diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ed9fd39db6..15d94ff1dc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,20 +35,51 @@ # @@-COPYRIGHT-END-@@ # #============================================================================= -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.18) +# 3.17+ required for correct finding of python within conda env +# 3.18 for CUDA_ARCHITECTURES +# C++14 for CUB +# TODO this code doesn't work with cuda 11 project(aimet) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O0 -ggdb -fPIC -D_GLIBCXX_USE_CXX11_ABI=0") +# # Fix behavior of CMAKE_CXX_STANDARD when targeting macOS. +# if (POLICY CMP0025) +# cmake_policy(SET CMP0025 NEW) +# endif () + set(AIMET_INSTALL_DIR ${CMAKE_BINARY_DIR}/staging/universal) +set(CMAKE_BUILD_TYPE Release) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_VERBOSE_MAKEFILE ON) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O0 -ggdb -fPIC -DGLIBCXX_USE_CXX11_ABI=0") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -march=native -Wno-narrowing -fPIC -DGLIBCXX_USE_CXX11_ABI=0") +list(APPEND CMAKE_PREFIX_PATH $ENV{CONDA_PREFIX}) +include_directories($ENV{CONDA_PREFIX}/include) +link_directories($ENV{CONDA_PREFIX}/lib) + +if(APPLE) + set(LIB_EXT "dylib") + list(APPEND CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") +else() + set(LIB_EXT "so") +endif() + if(NOT DEFINED AIMET_PYTHONPATH) set(AIMET_PYTHONPATH "PYTHONPATH=${CMAKE_BINARY_DIR}/artifacts" CACHE STRING "python path") endif() set(AIMET_PYTHONPATH "${AIMET_PYTHONPATH}:${CMAKE_CURRENT_SOURCE_DIR}/TrainingExtensions/common/src/python") if(NOT DEFINED AIMET_LD_LIBRARY_PATH) - set(AIMET_LD_LIBRARY_PATH "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/artifacts:$ENV{LD_LIBRARY_PATH}") + if(APPLE) + # set(AIMET_LD_LIBRARY_PATH "DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/artifacts:$ENV{CONDA_PREFIX}/lib:$ENV{DYLD_LIBRARY_PATH}") + set(AIMET_LD_LIBRARY_PATH "DYLD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/artifacts:$ENV{CONDA_PREFIX}/lib") + else() + set(AIMET_LD_LIBRARY_PATH "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/artifacts:$ENV{LD_LIBRARY_PATH}") + endif() message(STATUS "Set ${AIMET_LD_LIBRARY_PATH} in ${CMAKE_CURRENT_SOURCE_DIR}") endif() @@ -60,7 +91,7 @@ else() message(STATUS "SW_VERSION already set to ${SW_VERSION}.") endif() -find_package(Threads) +# find_package(Threads) # Disabling openMP since statically linked OpenMP in PyTorch (v1.5.0) seems to conflict with aimet usage #find_package(OpenMP REQUIRED) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") @@ -78,10 +109,14 @@ if (ENABLE_CUDA) message("Compiling with CUDA enabled") find_package(CUDAToolkit) + if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) - set(CMAKE_CUDA_ARCHITECTURES 52 60 61 70 72) + # set(CMAKE_CUDA_ARCHITECTURES 52 60 61 70 72 75 80 86) + set(CMAKE_CUDA_ARCHITECTURES 75) # TODO should be configurable endif() + list(APPEND CUDA_NVCC_FLAGS -O3 -std=c++14 -x cu -Xcompiler -fPIC -DGLIBCXX_USE_CXX11_ABI=0) + enable_language(CUDA) else(ENABLE_CUDA) @@ -89,52 +124,65 @@ else(ENABLE_CUDA) endif(ENABLE_CUDA) -set(LAPACKE_LIBRARY "/usr/lib/x86_64-linux-gnu/liblapacke.so.3.7.1") +# find LAPACK +if(APPLE) + set(BLA_VENDOR Apple) +else() + set(BLA_VENDOR Intel10_64lp) # all works, MKL is faster + # set(BLA_VENDOR OpenBLAS) +endif() +find_package(LAPACK REQUIRED) find_package(OpenCV REQUIRED) -set(OPENCV_LIBRARY /usr/local/lib/libopencv_core.a) +include_directories(${OpenCV_INCLUDE_DIRS}) +link_directories(${OpenCV_LIBRARY_DIRS}) +add_definitions(${OpenCV_DEFINITIONS}) +find_library(OPENCV_LIBRARY opencv_core) # ---------------------------------- # Conditional build for PyTorch # ---------------------------------- - find_package(Python COMPONENTS Interpreter Development) -set(PYTHON_DIST_PACKAGES "/usr/local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/dist-packages/") - +#set(PYTHON_DIST_PACKAGES "/usr/local/lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/dist-packages/") +find_package(pybind11 REQUIRED CONFIG) +# set(PYBIND11_CPP_STANDARD -std=c++11) +set(PYBIND11_PYTHON_VERSION ${Python_VERSION}) +set(PYBIND11_INC_DIRS "${pybind11_INCLUDE_DIRS}") +message("pybind11_INCLUDE_DIRS ${pybind11_INCLUDE_DIRS}") if (NOT (DEFINED ENABLE_TORCH)) message("AIMET Torch build not explicitly disabled. Enabling implicitly") set(ENABLE_TORCH ON CACHE BOOL "") - -endif(NOT (DEFINED ENABLE_TORCH)) +endif() if (ENABLE_TORCH) set(AIMET_PYTHONPATH "${AIMET_PYTHONPATH}:${CMAKE_CURRENT_SOURCE_DIR}/TrainingExtensions/torch/src/python") # If we enable PyTorch builds then use the pybind11 headers that are part of the torch pip install # So we don't have a version mismatch - between PyTorch custom C++ op code and PyMO - list(APPEND PYBIND11_INC_DIRS "${PYTHON_DIST_PACKAGES}/torch/include") +# list(APPEND PYBIND11_INC_DIRS "${PYTHON_DIST_PACKAGES}/torch/include") + set(PYBIND11_INC_DIRS "$ENV{CONDA_PREFIX}/lib/python3.7/site-packages/torch/include") list(APPEND PYBIND11_INC_DIRS ${Python_INCLUDE_DIRS}) -else (ENABLE_TORCH) - list(APPEND PYBIND11_INC_DIRS "${PYTHON_DIST_PACKAGES}/pybind11/include/") - list(APPEND PYBIND11_INC_DIRS ${Python_INCLUDE_DIRS}) +else() +# list(APPEND PYBIND11_INC_DIRS "${PYTHON_DIST_PACKAGES}/pybind11/include/") +# list(APPEND PYBIND11_INC_DIRS ${Python_INCLUDE_DIRS}) message("AIMET Torch build disabled") -endif (ENABLE_TORCH) +endif() # ---------------------------------- # PyBind11 # ---------------------------------- add_library(PYBIND11 SHARED IMPORTED) - set_target_properties(PYBIND11 PROPERTIES - IMPORTED_LOCATION ${Python_LIBRARIES} - INTERFACE_INCLUDE_DIRECTORIES "${PYBIND11_INC_DIRS}" - ) + IMPORTED_LOCATION "$ENV{CONDA_PREFIX}/lib/libpython3.7m.${LIB_EXT}" + INTERFACE_INCLUDE_DIRECTORIES "${PYBIND11_INC_DIRS}" + LINK_FLAGS "${pybind11_LIBRARIES}" +) # ---------------------------------- # Conditional build for TensorFlow @@ -147,14 +195,16 @@ if (NOT (DEFINED ENABLE_TENSORFLOW)) endif(NOT (DEFINED ENABLE_TENSORFLOW)) if (ENABLE_TENSORFLOW) + # TODO check TF version + set(TF_DIR "$ENV{CONDA_PREFIX}/lib/python3.7/site-packages/tensorflow_core") # for TF<=2.0 + #set(TF_DIR "$ENV{CONDA_PREFIX}/lib/python3.7/site-packages/tensorflow") # for TF>2.0 + set(AIMET_PYTHONPATH "${AIMET_PYTHONPATH}:${CMAKE_CURRENT_SOURCE_DIR}/TrainingExtensions/tensorflow/src/python") - list(APPEND TENSORFLOW_INCLUDES "${PYTHON_DIST_PACKAGES}/tensorflow_core/include") + list(APPEND TENSORFLOW_INCLUDES "${TF_DIR}/include") add_library(TensorFlow SHARED IMPORTED) set_target_properties(TensorFlow PROPERTIES - IMPORTED_LOCATION - "${PYTHON_DIST_PACKAGES}/tensorflow_core/libtensorflow_framework.so.1" - INTERFACE_INCLUDE_DIRECTORIES - "${TENSORFLOW_INCLUDES}" + IMPORTED_LOCATION "${TF_DIR}/libtensorflow_framework.so.1" + INTERFACE_INCLUDE_DIRECTORIES "${TENSORFLOW_INCLUDES}" ) else (ENABLE_TENSORFLOW) @@ -204,10 +254,11 @@ endif() enable_testing() +add_subdirectory(ThirdParty) +include_directories(${gtest_SOURCE_DIR}/include) add_subdirectory(ModelOptimizations) add_subdirectory(TrainingExtensions) add_subdirectory(NightlyTests) -add_subdirectory(ThirdParty) add_subdirectory(Docs) add_subdirectory(Examples) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Benchmarks") diff --git a/Jenkins/Dockerfile b/Jenkins/Dockerfile deleted file mode 120000 index d0bcf8eac9e..00000000000 --- a/Jenkins/Dockerfile +++ /dev/null @@ -1 +0,0 @@ -Dockerfile.tf-torch-cpu \ No newline at end of file diff --git a/Jenkins/Dockerfile b/Jenkins/Dockerfile new file mode 100755 index 00000000000..c06b66ad0a7 --- /dev/null +++ b/Jenkins/Dockerfile @@ -0,0 +1,243 @@ +# ============================================================================= +# @@-COPYRIGHT-START-@@ +# +# Copyright (c) 2020, Qualcomm Innovation Center, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# @@-COPYRIGHT-END-@@ +# ============================================================================= + +# Docker image file to build and test AIMET in a GPU environment + +FROM tensorflow/tensorflow:2.2.0-gpu-jupyter + +ENTRYPOINT ["/bin/bash"] + +RUN apt-get update > /dev/null && \ + apt-get install --no-install-recommends -y \ + # Bare minimum Packages + ca-certificates \ + git \ + ssh \ + sudo \ + wget \ + xterm \ + xauth > /dev/null && \ + rm -rf /var/lib/apt/lists/* + +# Install certificates +RUN sudo update-ca-certificates + +# Modified version of bash.bashrc that adjusts the prompt +### COPY bash.bashrc /etc/ +### RUN chmod 644 /etc/bash.bashrc + +### COPY profile.global /usr/local/etc/ +### RUN chmod 555 /usr/local/etc/profile.global + +# Add sudo support +RUN echo "%users ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers + +RUN apt-get update > /dev/null && \ + apt-get install --no-install-recommends -y \ + + # Python + python \ + python3-dev \ + python3-pip \ + + # Nvidia cuda profiler + libcupti-dev \ + + # lmdb depenedency + libffi-dev && \ + rm -rf /var/lib/apt/lists/* + +# Python 2 pip installation +RUN apt-get update && apt-get install -y python-pip && rm -rf /var/lib/apt/lists/* && \ + python2.7 -m pip --no-cache-dir install --upgrade \ + pip==20.3.4 \ + restkit==4.2.2 + +# Upgrade Python3 pip and install some more packages +RUN pip3 --no-cache-dir install --upgrade \ + setuptools==41.0.1 \ + pip==20.2.4 \ + numpy==1.16.4 \ + wheel==0.33.4 + +# nvidia-docker hooks +LABEL com.nvidia.volumes.needed="nvidia_driver" + +# Update all paths +ENV PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:$PATH +ENV LD_LIBRARY_PATH=/usr/local/cuda-10.0/lib64/:/usr/local/cuda-10.0/extras/CUPTI/lib64:$LD_LIBRARY_PATH +ENV CUDA_TOOLKIT_PATH=/usr/local/cuda +ENV CUDNN_INSTALL_PATH=/usr/local/cuda +ENV CUDA_HOME=/usr/local/cuda + +ENV DEBIAN_FRONTEND=noninteractive + +# Ubuntu packages for tensorflow and pytorch aimet +RUN dpkg --add-architecture i386 +RUN apt-get update > /dev/null && \ + apt-get install --no-install-recommends -y \ + build-essential \ + emacs \ + environment-modules \ + less \ + libavcodec-dev \ + libavformat-dev \ + libgtest-dev \ + libgtk2.0-dev \ + libsox-dev \ + libsox-fmt-all \ + libstdc++6:i386 \ + libswscale-dev \ + libxtst6 \ + lsb-release \ + meld \ + nano \ + pkg-config \ + python3-tk \ + sox \ + tree \ + vim && \ + rm -rf /var/lib/apt/lists/* + +# Python3 Packages +RUN pip3 --no-cache-dir install \ + attrs==19.1.0 \ + behave==1.2.6 \ + blosc==1.8.1 \ + cffi==1.12.3 \ + click==7.0 \ + cython==0.29.10 \ + docutils==0.16 \ + h5py==2.9.0 \ + ipykernel==4.8.2 \ + keras==2.2.4 \ + lmdb==0.95 \ + opencv-python==4.1.0.25 \ + pillow==6.2.1 \ + pluggy==0.12.0 \ + protobuf==3.7.1 \ + psutil==5.8.0 \ + ptflops==0.6.4 \ + pybind11==2.6.1 \ + pyDOE2==1.3.0 \ + pylint==2.3.1 \ + pymoo==0.4.1 \ + pytest==4.6.5 \ + pytest-cov==2.6.1 \ + pytorch-ignite==0.1.2 \ + https://download.pytorch.org/whl/cu100/torchvision-0.5.0%2Bcu100-cp36-cp36m-linux_x86_64.whl \ + https://download.pytorch.org/whl/cu100/torch-1.4.0%2Bcu100-cp36-cp36m-linux_x86_64.whl \ + scikit-image==0.15.0 \ + scikit-learn==0.21.0 \ + scipy==1.2.1 \ + sphinx==2.1.1 \ + sphinx-autodoc-typehints==1.6.0 \ + tensorboard==1.15 \ + tensorboardX==1.7 \ + tensorlayer==2.1.0 \ + timm==0.3.1 \ + torchtext==0.3.1 \ + tqdm==4.32.2 \ + wget==3.2 && \ + python3 -m ipykernel.kernelspec + +RUN cd /tmp && \ + wget https://github.com/Kitware/CMake/releases/download/v3.19.3/cmake-3.19.3-Linux-x86_64.sh && \ + mkdir /opt/cmake && \ + sh cmake-3.19.3-Linux-x86_64.sh --prefix=/opt/cmake --skip-license + +RUN ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake +RUN ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest +RUN ln -s /opt/cmake/bin/cpack /usr/local/bin/cpack +ENV PATH=/usr/local/bin:$PATH + +# Opencv +# Ref: https://docs.opencv.org/3.1.0/d7/d9f/tutorial_linux_install.html +RUN wget -q https://github.com/Itseez/opencv/archive/3.1.0.tar.gz -O /tmp/3.1.0.tar.gz > /dev/null && \ + tar -C /tmp -xvf /tmp/3.1.0.tar.gz > /dev/null && \ + cd /tmp/opencv-3.1.0 && mkdir release && cd release && \ + cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=release -DWITH_FFMPEG=OFF -DBUILD_TESTS=OFF -DWITH_CUDA=OFF -DBUILD_PERF_TESTS=OFF -DWITH_IPP=OFF -DENABLE_PRECOMPILED_HEADERS=OFF .. > /dev/null && \ + make -j16 > /dev/null && \ + make -j16 install > /dev/null && \ + rm -rf /tmp/opencv-3.1.0* + +EXPOSE 25000 +RUN apt-get update && apt-get install -y openssh-server && rm -rf /var/lib/apt/lists/* +RUN mkdir /var/run/sshd + +RUN apt-get update && apt-get install -y liblapacke liblapacke-dev && rm -rf /var/lib/apt/lists/* + +# install pillow-simd. +RUN apt-get update && apt-get install -y libjpeg8-dev \ + cuda-cusparse-dev-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-curand-dev-10-0 && \ + rm -rf /var/lib/apt/lists/* + +# Set up symlink to point to the correct python version +RUN ln -sf /usr/bin/python3.6 /usr/bin/python +RUN ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib +RUN pip3 uninstall -y Pillow && pip3 install Pillow-SIMD==6.0.0.post0 + +RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \ + sed -i 's/Port 22/Port 25000/' /etc/ssh/sshd_config + +# SSH login fix. Otherwise user is kicked off after login +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd + +# Clone the tensorflow repo to enable development +RUN cd / && git clone --depth 1 --single-branch --branch v1.15.0 https://github.com/tensorflow/tensorflow.git + +RUN pip install git-pylint-commit-hook osqp==0.6.1 onnx==1.8.1 + +# bokeh requires Pillow while Pillow-SIMD is being used everywhere in Morpheus. +# To install bokeh, we are installing all dependencies of bokeh other than Pillow. +RUN mkdir /var/tmp/bokeh_hvplot_download +RUN pip download bokeh==1.2.0 hvplot==0.4.0 -d /var/tmp/bokeh_hvplot_download | grep Collecting | cut -d' ' -f2 | grep -Ev "pillow(~|=|\!|>|<|$)" | grep -Ev "bokeh(~|=|\!|>|<|$)" | grep -Ev "hvplot(~|=|\!|>|<|$)" > /var/tmp/bokeh_hvplot_requirements.txt +RUN pip install --no-deps -r /var/tmp/bokeh_hvplot_requirements.txt +# NOTE: We need to pin the holoviews version to this since the latest version has a circular dependency on bokeh 2.0.0 through the panel package +RUN pip install holoviews==1.12.7 \ + netron==4.8.4 + +RUN pip install --no-deps bokeh==1.2.0 hvplot==0.4.0 +RUN rm -rf /var/tmp/bokeh_hvplot_download /var/tmp/bokeh_hvplot_requirements.txt + +RUN wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add - && echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" >> /etc/apt/sources.list +RUN apt-get update && apt-get install -y clang-11 clang-format clang-tidy-11 cuda-cublas-dev-10-0 cuda-curand-dev-10-0 && \ + rm -rf /var/lib/apt/lists/* + +# Create a version-less symbolic link for clang-tidy +RUN ln -s /usr/bin/run-clang-tidy-11.py /usr/bin/run-clang-tidy.py diff --git a/ModelOptimizations/DlCompression/test/CMakeLists.txt b/ModelOptimizations/DlCompression/test/CMakeLists.txt index 46c5771d175..6ed15ffaabf 100644 --- a/ModelOptimizations/DlCompression/test/CMakeLists.txt +++ b/ModelOptimizations/DlCompression/test/CMakeLists.txt @@ -50,17 +50,13 @@ target_compile_options(MoDlCompressionTest ) target_link_libraries(MoDlCompressionTest - ${OPENCV_LIBRARY} - Threads::Threads MoDlCompression - stdc++ - ${LAPACKE_LIBRARY} - z - dl - gtest gtest_main + ${OPENCV_LIBRARY} + LAPACK::LAPACK + z + gtest_main ) - add_test(MoDlCompressionTest MoDlCompressionTest --gtest_output=xml:cpp_test_output.xml ) diff --git a/ModelOptimizations/DlEqualization/test/CMakeLists.txt b/ModelOptimizations/DlEqualization/test/CMakeLists.txt index 5b2103e3300..2df94c82fff 100644 --- a/ModelOptimizations/DlEqualization/test/CMakeLists.txt +++ b/ModelOptimizations/DlEqualization/test/CMakeLists.txt @@ -35,7 +35,6 @@ #============================================================================== cmake_minimum_required(VERSION 3.5) - include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${gtest_SOURCE_DIR}/include @@ -53,12 +52,10 @@ target_compile_options(MoDlEqualizationTest target_link_libraries(MoDlEqualizationTest MoDlEqualization - stdc++ - PYBIND11 + pybind11::embed ${LAPACKE_LIBRARY} ${OPENCV_LIBRARY} z - dl gtest gtest_main ) @@ -72,4 +69,4 @@ add_test(MoDlEqualizationPythonTest set_property(TEST MoDlEqualizationPythonTest APPEND PROPERTY ENVIRONMENT "${AIMET_LD_LIBRARY_PATH}") set_property(TEST MoDlEqualizationPythonTest - APPEND PROPERTY ENVIRONMENT "${AIMET_PYTHONPATH}") \ No newline at end of file + APPEND PROPERTY ENVIRONMENT "${AIMET_PYTHONPATH}") diff --git a/ModelOptimizations/DlEqualization/test/TestDLEqualization.cpp b/ModelOptimizations/DlEqualization/test/TestDLEqualization.cpp index d43c1e7dcde..01db987eded 100644 --- a/ModelOptimizations/DlEqualization/test/TestDLEqualization.cpp +++ b/ModelOptimizations/DlEqualization/test/TestDLEqualization.cpp @@ -288,4 +288,4 @@ TEST(TestBiasCorrection, CorrectBiasBNParamsNoActivation) EXPECT_FLOAT_EQ(biasTensor.data[1], 8); EXPECT_FLOAT_EQ(biasTensor.data[2], 8); -} \ No newline at end of file +} diff --git a/ModelOptimizations/DlQuantization/include/DlQuantization/QuantizerFactory.hpp b/ModelOptimizations/DlQuantization/include/DlQuantization/QuantizerFactory.hpp index 76f03c4894f..50b7ca894c2 100644 --- a/ModelOptimizations/DlQuantization/include/DlQuantization/QuantizerFactory.hpp +++ b/ModelOptimizations/DlQuantization/include/DlQuantization/QuantizerFactory.hpp @@ -40,9 +40,9 @@ #ifndef QUANTIZER_FACTORY_HPP #define QUANTIZER_FACTORY_HPP -#include #include #include +#include #include "DlQuantization/IQuantizationEncodingAnalyzer.hpp" #include "DlQuantization/IQuantizer.hpp" diff --git a/ModelOptimizations/DlQuantization/src/TfEncodingAnalyzer.cpp b/ModelOptimizations/DlQuantization/src/TfEncodingAnalyzer.cpp index 35bd09d73bb..4aed8bc7d71 100644 --- a/ModelOptimizations/DlQuantization/src/TfEncodingAnalyzer.cpp +++ b/ModelOptimizations/DlQuantization/src/TfEncodingAnalyzer.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "DlQuantization/Quantization.hpp" #include "math_functions.hpp" diff --git a/ModelOptimizations/DlQuantization/test/CMakeLists.txt b/ModelOptimizations/DlQuantization/test/CMakeLists.txt index d80fa1a458e..d99af21ad21 100644 --- a/ModelOptimizations/DlQuantization/test/CMakeLists.txt +++ b/ModelOptimizations/DlQuantization/test/CMakeLists.txt @@ -35,8 +35,6 @@ #============================================================================== cmake_minimum_required(VERSION 3.5) -find_package(GTest) - include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/../src ${gtest_SOURCE_DIR}/include @@ -62,7 +60,7 @@ if (ENABLE_CUDA) target_link_libraries(MoDlQuantizationTest MoDlQuantization MoDlQuantizationCuda - CUDA::cublas + CUDA::cublas gtest gtest_main ) @@ -74,7 +72,6 @@ else (ENABLE_CUDA) endif (ENABLE_CUDA) - add_test(MoDlQuantizationTest MoDlQuantizationTest --gtest_output=xml:cpp_test_output.xml) @@ -88,4 +85,4 @@ set_property(TEST MoDlQuantizationPythonTest APPEND PROPERTY ENVIRONMENT "${AIMET_PYTHONPATH}") -#GTEST_ADD_TESTS(MoDlQuantizationTest "--gtest_output=xml:cpp_test_output.xml" AUTO) \ No newline at end of file +#GTEST_ADD_TESTS(MoDlQuantizationTest "--gtest_output=xml:cpp_test_output.xml" AUTO) diff --git a/ModelOptimizations/DlQuantization/test/test_quantization_lib_tf_enhanced_cpugpu.cpp b/ModelOptimizations/DlQuantization/test/test_quantization_lib_tf_enhanced_cpugpu.cpp index f8d6888da8f..05a17d4be1f 100644 --- a/ModelOptimizations/DlQuantization/test/test_quantization_lib_tf_enhanced_cpugpu.cpp +++ b/ModelOptimizations/DlQuantization/test/test_quantization_lib_tf_enhanced_cpugpu.cpp @@ -45,7 +45,7 @@ #include #include -#include "gtest/gtest.h" +//#include #include "DlQuantization/QuantizerFactory.hpp" #include "test_quantization_lib.hpp" diff --git a/ModelOptimizations/PyModelOptimizations/CMakeLists.txt b/ModelOptimizations/PyModelOptimizations/CMakeLists.txt index e6e3575b117..71087a976df 100644 --- a/ModelOptimizations/PyModelOptimizations/CMakeLists.txt +++ b/ModelOptimizations/PyModelOptimizations/CMakeLists.txt @@ -37,30 +37,27 @@ #============================================================================= cmake_minimum_required(VERSION 3.5) -add_library(PyModelOptimizations SHARED - PyModelOptimizations.cpp) +add_library(PyModelOptimizations MODULE PyModelOptimizations.cpp) if (ENABLE_CUDA) target_link_libraries(PyModelOptimizations - PUBLIC - stdc++ - PYBIND11 - MoDlQuantization - MoDlEqualization - MoDlQuantizationCuda - MoDlCompression + PUBLIC + pybind11::module + MoDlQuantization + MoDlEqualization + MoDlQuantizationCuda + MoDlCompression CUDA::cublas - ${LAPACKE_LIBRARY} - ${OPENCV_LIBRARY} - z - ) + ${LAPACKE_LIBRARY} + ${OPENCV_LIBRARY} + z + ) -else (ENABLE_CUDA) +else() target_link_libraries(PyModelOptimizations - PUBLIC - stdc++ - PYBIND11 + PUBLIC + pybind11::module MoDlQuantization MoDlEqualization MoDlCompression @@ -68,16 +65,17 @@ else (ENABLE_CUDA) ${LAPACKE_LIBRARY} ${OPENCV_LIBRARY} z - ) + ) -endif (ENABLE_CUDA) +endif() set_target_properties(PyModelOptimizations - PROPERTIES - OUTPUT_NAME "pymo" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts" - ) + PROPERTIES + SUFFIX ".so" + OUTPUT_NAME "pymo" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts" + ) install(FILES $ - DESTINATION ${AIMET_INSTALL_DIR}/lib/x86_64-linux-gnu - ) \ No newline at end of file + DESTINATION ${AIMET_INSTALL_DIR}/lib/x86_64-linux-gnu + ) diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index e4eecc2a7b0..cd29c55e8fc 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -37,5 +37,4 @@ #============================================================================= cmake_minimum_required(VERSION 3.5) -add_subdirectory(googletest/googletest-release-1.8.0/ - EXCLUDE_FROM_ALL) \ No newline at end of file +add_subdirectory(googletest/googletest-release-1.10.0/ EXCLUDE_FROM_ALL) diff --git a/TrainingExtensions/common/CMakeLists.txt b/TrainingExtensions/common/CMakeLists.txt index 6ec61676c37..ef30da1c195 100644 --- a/TrainingExtensions/common/CMakeLists.txt +++ b/TrainingExtensions/common/CMakeLists.txt @@ -35,5 +35,39 @@ #============================================================================== cmake_minimum_required(VERSION 3.5) +<<<<<<< HEAD +add_library(PyTrainingExtensions MODULE + PyTrainingExtensions/PyTrainingExtensions.cpp) + +target_include_directories(PyTrainingExtensions PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +if (ENABLE_CUDA) + target_link_libraries(PyTrainingExtensions PUBLIC + pybind11::module + MoDlQuantization + MoDlQuantizationCuda + CUDA::cublas + ) + +else (ENABLE_CUDA) + target_link_libraries(PyTrainingExtensions PUBLIC + pybind11::module + MoDlQuantization + ) + +endif (ENABLE_CUDA) + +set_target_properties(PyTrainingExtensions PROPERTIES + SUFFIX ".so" + OUTPUT_NAME "pytrext" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts") + +install(FILES $ + DESTINATION ${AIMET_INSTALL_DIR}/lib/x86_64-linux-gnu + ) + +======= +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e add_subdirectory(src/python) add_subdirectory(test) diff --git a/TrainingExtensions/tensorflow/CMakeLists.txt b/TrainingExtensions/tensorflow/CMakeLists.txt index 09088bc2d7c..1b17c629bb1 100644 --- a/TrainingExtensions/tensorflow/CMakeLists.txt +++ b/TrainingExtensions/tensorflow/CMakeLists.txt @@ -47,10 +47,9 @@ add_library(TrainingExtensionsTf SHARED ) target_include_directories(TrainingExtensionsTf PRIVATE - ${PYTHON_DIST_PACKAGES}/tensorflow_core/include - ${PYTHON_DIST_PACKAGES}/tensorflow_core/include/external/nsync/public - ${CMAKE_CURRENT_SOURCE_DIR}/../common/include - ) + ${TENSORFLOW_INCLUDES} + ${CMAKE_CURRENT_SOURCE_DIR}/../common/include + ) if (ENABLE_CUDA) target_compile_options(TrainingExtensionsTf @@ -59,29 +58,35 @@ if (ENABLE_CUDA) ) target_link_libraries(TrainingExtensionsTf PUBLIC - MoDlCompression - MoDlQuantization - MoDlQuantizationCuda - TensorFlow - TrainingExtensionsTfCuda - ) - -else(ENABLE_CUDA) + MoDlCompression + MoDlQuantization + MoDlQuantizationCuda + TensorFlow + TrainingExtensionsTfCuda + CUDA::cublas + ) + +else() target_link_libraries(TrainingExtensionsTf PUBLIC MoDlCompression MoDlQuantization TensorFlow - ) + ) -endif(ENABLE_CUDA) +endif() -set_target_properties(TrainingExtensionsTf PROPERTIES - OUTPUT_NAME "aimet_tf_ops" - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts" - ) +set_target_properties(TrainingExtensionsTf + PROPERTIES + SUFFIX ".so" + OUTPUT_NAME "aimet_tf_ops" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/artifacts" + ) get_target_property(dirs MoDlQuantization INCLUDE_DIRECTORIES) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common/include ${PYTHON_DIST_PACKAGES}/tensorflow_core/include/external/nsync/public ${dirs}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common/include ${TENSORFLOW_INCLUDES}/external/nsync/public ${dirs}) + +get_target_property(dirs MoDlQuantization INCLUDE_DIRECTORIES) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common/include ${dirs}) if (ENABLE_CUDA) add_library(TrainingExtensionsTfCuda @@ -90,14 +95,13 @@ if (ENABLE_CUDA) target_compile_options(TrainingExtensionsTfCuda PRIVATE - -Xcompiler -fPIC -D_GLIBCXX_USE_CXX11_ABI=0 + -Xcompiler -fPIC -DGLIBCXX_USE_CXX11_ABI=0 -DGOOGLE_CUDA=1 - -I ${PYTHON_DIST_PACKAGES}/tensorflow_core/include --expt-relaxed-constexpr + -I ${TENSORFLOW_INCLUDES} --expt-relaxed-constexpr ) endif(ENABLE_CUDA) - install(FILES $ DESTINATION ${AIMET_INSTALL_DIR}/lib/x86_64-linux-gnu ) diff --git a/TrainingExtensions/tensorflow/src/AimetOpUtils.h b/TrainingExtensions/tensorflow/src/AimetOpUtils.h index e9b64150fa8..c60c6b6054d 100644 --- a/TrainingExtensions/tensorflow/src/AimetOpUtils.h +++ b/TrainingExtensions/tensorflow/src/AimetOpUtils.h @@ -40,6 +40,8 @@ #define AIMET_OP_UTILS_H #include "tensorflow/core/framework/op.h" +#include "absl/base/config.h" +#undef ABSL_HAVE_STD_STRING_VIEW #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" #include "tensorflow/core/platform/logging.h" diff --git a/TrainingExtensions/tensorflow/src/QcQuantizeOp.hpp b/TrainingExtensions/tensorflow/src/QcQuantizeOp.hpp index ef5c3c02922..da933e3cc7a 100644 --- a/TrainingExtensions/tensorflow/src/QcQuantizeOp.hpp +++ b/TrainingExtensions/tensorflow/src/QcQuantizeOp.hpp @@ -40,6 +40,8 @@ #define QC_QUANTIZE_OP_HPP #include "tensorflow/core/framework/op.h" +#include "absl/base/config.h" +#undef ABSL_HAVE_STD_STRING_VIEW #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" #include "tensorflow/core/platform/logging.h" diff --git a/TrainingExtensions/tensorflow/src/QcQuantizeOpDeprecated.hpp b/TrainingExtensions/tensorflow/src/QcQuantizeOpDeprecated.hpp new file mode 100644 index 00000000000..9d59c5c793c --- /dev/null +++ b/TrainingExtensions/tensorflow/src/QcQuantizeOpDeprecated.hpp @@ -0,0 +1,72 @@ +//============================================================================== +// +// @@-COPYRIGHT-START-@@ +// +// Copyright (c) 2017-2018, Qualcomm Innovation Center, Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// SPDX-License-Identifier: BSD-3-Clause +// +// @@-COPYRIGHT-END-@@ +// +//============================================================================== + +#ifndef QC_QUANTIZE_OP_DEPRECATED_HPP +#define QC_QUANTIZE_OP_DEPRECATED_HPP + +#include "tensorflow/core/framework/op.h" +#include "absl/base/config.h" +#undef ABSL_HAVE_STD_STRING_VIEW +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/shape_inference.h" +#include "tensorflow/core/platform/logging.h" + +#include "DlQuantization/IQuantizer.hpp" +#include "DlQuantization/QuantizerFactory.hpp" +#include "QcOp/qc_quantizer.hpp" + +// Declarations of the functors to be used +// to invoke Quantization APIs. +using namespace tensorflow; + +template +struct QcQuantizeDeprecatedFunctor +{ + void operator()(const Device& d, QcOp::OP_CONFIG_TYPE config, const std::vector& in_tensors, + const std::vector& in_tensor_counts, std::vector out_tensors, + DlQuantization::TfEncodingLayer& in_encoding, DlQuantization::TfEncodingLayer& out_encoding, + T* output_min_tensor, T* output_max_tensor, QcOp::QC_Quantizer& quantizer); + + void operator()(const Device& d, QcOp::OP_CONFIG_TYPE config, std::vector& in_tensors, + const std::vector& in_tensor_counts, const bool* training_in_progress, + std::vector out_tensors, DlQuantization::TfEncodingLayer& in_encoding, + DlQuantization::TfEncodingLayer& out_encoding, T* output_min_tensor, T* output_max_tensor, + QcOp::QC_Quantizer& quantizer); +}; + +#endif // QC_QUANTIZE_OP_DEPRECATED_HPP diff --git a/TrainingExtensions/tensorflow/src/QcQuantizeRecurrentParamOp.hpp b/TrainingExtensions/tensorflow/src/QcQuantizeRecurrentParamOp.hpp index c487f77068e..abd1d8a6ae6 100644 --- a/TrainingExtensions/tensorflow/src/QcQuantizeRecurrentParamOp.hpp +++ b/TrainingExtensions/tensorflow/src/QcQuantizeRecurrentParamOp.hpp @@ -40,6 +40,8 @@ #define QC_QUANTIZE_RECURRENT_PARAM_OP_HPP #include "tensorflow/core/framework/op.h" +#include "absl/base/config.h" +#undef ABSL_HAVE_STD_STRING_VIEW #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" #include "tensorflow/core/platform/logging.h" diff --git a/TrainingExtensions/tensorflow/src/QcQuantizeStaticOp.hpp b/TrainingExtensions/tensorflow/src/QcQuantizeStaticOp.hpp index 22ddd264b75..ff525bd6913 100644 --- a/TrainingExtensions/tensorflow/src/QcQuantizeStaticOp.hpp +++ b/TrainingExtensions/tensorflow/src/QcQuantizeStaticOp.hpp @@ -40,6 +40,8 @@ #define QC_QUANTIZE_STATIC_OP_HPP #include "tensorflow/core/framework/op.h" +#include "absl/base/config.h" +#undef ABSL_HAVE_STD_STRING_VIEW #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" #include "tensorflow/core/platform/logging.h" diff --git a/TrainingExtensions/tensorflow/test/python/test_batch_norm_fold.py b/TrainingExtensions/tensorflow/test/python/test_batch_norm_fold.py index 64de09e02d6..0640de0aac4 100644 --- a/TrainingExtensions/tensorflow/test/python/test_batch_norm_fold.py +++ b/TrainingExtensions/tensorflow/test/python/test_batch_norm_fold.py @@ -41,7 +41,7 @@ import unittest import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import numpy as np diff --git a/TrainingExtensions/tensorflow/test/python/test_bias_correction.py b/TrainingExtensions/tensorflow/test/python/test_bias_correction.py index 91a6c2bd4fc..772b0f70578 100644 --- a/TrainingExtensions/tensorflow/test/python/test_bias_correction.py +++ b/TrainingExtensions/tensorflow/test/python/test_bias_correction.py @@ -41,7 +41,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import numpy as np diff --git a/TrainingExtensions/tensorflow/test/python/test_comp_ratio_select.py b/TrainingExtensions/tensorflow/test/python/test_comp_ratio_select.py index 0593feda0d2..649f70ebd50 100644 --- a/TrainingExtensions/tensorflow/test/python/test_comp_ratio_select.py +++ b/TrainingExtensions/tensorflow/test/python/test_comp_ratio_select.py @@ -44,7 +44,7 @@ import signal import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.defs import CostMetric, LayerCompRatioPair diff --git a/TrainingExtensions/tensorflow/test/python/test_compression_factory.py b/TrainingExtensions/tensorflow/test/python/test_compression_factory.py index d33e3cdb442..640bdcb13b6 100644 --- a/TrainingExtensions/tensorflow/test/python/test_compression_factory.py +++ b/TrainingExtensions/tensorflow/test/python/test_compression_factory.py @@ -43,7 +43,7 @@ from unittest.mock import MagicMock import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.utils import start_bokeh_server_session diff --git a/TrainingExtensions/tensorflow/test/python/test_cost_calculator.py b/TrainingExtensions/tensorflow/test/python/test_cost_calculator.py index 4aa4a1d25b0..e36ed80599f 100644 --- a/TrainingExtensions/tensorflow/test/python/test_cost_calculator.py +++ b/TrainingExtensions/tensorflow/test/python/test_cost_calculator.py @@ -44,7 +44,7 @@ from decimal import Decimal import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from tensorflow.python.keras.models import Sequential diff --git a/TrainingExtensions/tensorflow/test/python/test_cross_layer_equalization.py b/TrainingExtensions/tensorflow/test/python/test_cross_layer_equalization.py index e2b961a46a3..36be8641a39 100644 --- a/TrainingExtensions/tensorflow/test/python/test_cross_layer_equalization.py +++ b/TrainingExtensions/tensorflow/test/python/test_cross_layer_equalization.py @@ -43,7 +43,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import aimet_tensorflow.utils.graph_saver diff --git a/TrainingExtensions/tensorflow/test/python/test_layer_database.py b/TrainingExtensions/tensorflow/test/python/test_layer_database.py index 9c1dc58a7cd..5fcf9e2502e 100644 --- a/TrainingExtensions/tensorflow/test/python/test_layer_database.py +++ b/TrainingExtensions/tensorflow/test/python/test_layer_database.py @@ -43,7 +43,7 @@ import numpy as np import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.utils import AimetLogger diff --git a/TrainingExtensions/tensorflow/test/python/test_layer_selector.py b/TrainingExtensions/tensorflow/test/python/test_layer_selector.py index ba3bc931527..391f63129ca 100644 --- a/TrainingExtensions/tensorflow/test/python/test_layer_selector.py +++ b/TrainingExtensions/tensorflow/test/python/test_layer_selector.py @@ -41,7 +41,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_tensorflow.layer_database import Layer diff --git a/TrainingExtensions/tensorflow/test/python/test_module_identifier.py b/TrainingExtensions/tensorflow/test/python/test_module_identifier.py index 2a1e3ae4aaf..03d4adc28bc 100644 --- a/TrainingExtensions/tensorflow/test/python/test_module_identifier.py +++ b/TrainingExtensions/tensorflow/test/python/test_module_identifier.py @@ -40,6 +40,7 @@ import unittest import logging import tensorflow as tf +tf.logging.set_verbosity(tf.logging.WARN) from aimet_common.utils import AimetLogger from aimet_tensorflow.common.module_identifier import StructureModuleIdentifier diff --git a/TrainingExtensions/tensorflow/test/python/test_module_reducer.py b/TrainingExtensions/tensorflow/test/python/test_module_reducer.py index 54a6d9c67f7..8fde0b82304 100644 --- a/TrainingExtensions/tensorflow/test/python/test_module_reducer.py +++ b/TrainingExtensions/tensorflow/test/python/test_module_reducer.py @@ -42,7 +42,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.connected_graph.connectedgraph import get_ordered_ops diff --git a/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op.py b/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op.py index de59ea9c911..75f7afecb83 100644 --- a/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op.py +++ b/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op.py @@ -39,7 +39,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import libpymo diff --git a/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op_deprecated.py b/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op_deprecated.py new file mode 100644 index 00000000000..7d5ae1c3bfc --- /dev/null +++ b/TrainingExtensions/tensorflow/test/python/test_qc_quantize_op_deprecated.py @@ -0,0 +1,542 @@ +# -*- mode: python -*- +# ============================================================================= +# @@-COPYRIGHT-START-@@ +# +# Copyright (c) 2017-2018, Qualcomm Innovation Center, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# @@-COPYRIGHT-END-@@ +# ============================================================================= + +import os +import pytest + +import tensorflow as tf +tf.logging.set_verbosity(tf.logging.WARN) +os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" + +import numpy as np +from tensorflow.python.framework import ops +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import errors_impl +# Import Quantizer class to initialize the DlQuantization static object +import libpytrext +# Import the python bindings for the DlQuantization library +import libpymo +from aimet_common.utils import AimetLogger + +_log = AimetLogger.get_area_logger(AimetLogger.LogAreas.Utils) + +# import inspect # For inspecting function signature for python wrappers + + +class QCQuantizeTest(tf.test.TestCase): + + qc_quantize_module = tf.load_op_library('libaimet_tf_ops.so') + + def _compute_encodings (self, min, max, bw): + steps = (2**bw) - 1 + delta = (max - min) / steps + offset = np.round(min / delta) + new_min = delta * offset + new_max = delta * steps + new_min + tf_encoding = np.array(list([new_min, new_max, delta, offset])) + return tf_encoding + + @pytest.mark.cuda + def testQCQuantize_Params(self): + _log.info('running testQCQuantize_Params') + for use_gpu in [False, True]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + PARAM_MIN = -50.0 + PARAM_MAX = 80.0 + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + # Instantiate DlQuantization object + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + weights = constant_op.constant([-40.0, -1.0, 0.0, 1.0, 2.0, -50.0, 80.0]) + + # Quantize and de-quantize params + test_output = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config=int(libpytrext.config_type.CONFIG_TYPE_Q_DQ_PARAMS), + bitwidth=bw, in_tensors=[weights], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=1) + quantized_weights = ops.convert_to_tensor(test_output[0]).eval() + self.assertAllClose(quantized_weights[0], weights.eval(), 1.0) + + # Examine encodings of quantized params + out_enc_min = ops.convert_to_tensor(test_output[1]).eval() + out_enc_max = ops.convert_to_tensor(test_output[2]).eval() + true_encodings = self._compute_encodings(out_enc_min[0], out_enc_max[0], bw) + expected_encodings = self._compute_encodings(PARAM_MIN, PARAM_MAX, bw) + error_margin = 10 # Use better heuristics; ideally there should be 0 error margin + self.assertArrayNear(true_encodings, expected_encodings, error_margin) + + # Repeat test with training_in_progress == true + test_output = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=True, + config=int(libpytrext.config_type.CONFIG_TYPE_Q_DQ_PARAMS), + bitwidth=bw, in_tensors=[weights], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=1) + quantized_weights = ops.convert_to_tensor(test_output[0]).eval() + self.assertAllClose(quantized_weights[0], weights.eval(), 1.0) + + # Examine encodings of quantized params + out_enc_min = ops.convert_to_tensor(test_output[1]).eval() + out_enc_max = ops.convert_to_tensor(test_output[2]).eval() + true_encodings = self._compute_encodings(out_enc_min[0], out_enc_max[0], bw) + expected_encodings = self._compute_encodings(PARAM_MIN, PARAM_MAX, bw) + error_margin = 10 # Use better heuristics; ideally there should be 0 error margin + self.assertArrayNear(true_encodings, expected_encodings, error_margin) + + libpytrext.ResetQuantizer() + + @pytest.mark.cuda + def testQCQuantize_SingleActivation(self): + _log.info('running testQCQuantize_SingleActivation') + for use_gpu in [False, True]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + actvn_0 = constant_op.constant(np.arange(0, 20).astype(np.float32)) + actvn_1 = constant_op.constant(np.arange(0, 50).astype(np.float32)) + actvn_2 = constant_op.constant(np.arange(0, 100).astype(np.float32)) + # Update quantization stats + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, in_tensors=[actvn_0], + fixed_enc_mins=[], fixed_enc_maxs=[]) + ops.convert_to_tensor(output_0[0]).eval() + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, in_tensors=[actvn_1], + fixed_enc_mins=[], fixed_enc_maxs=[]) + ops.convert_to_tensor(output_1[0]).eval() + output_2 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, in_tensors=[actvn_2], + fixed_enc_mins=[], fixed_enc_maxs=[]) + ops.convert_to_tensor(output_2[0]).eval() + + ACT_MIN = 0.0 + ACT_MAX = 16.0 + test_actvn = constant_op.constant([ACT_MAX]) # Single input activation + # Quantize and de-quantize activations + test_output = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config=int(libpytrext.config_type.CONFIG_TYPE_Q_DQ_ACTIVATIONS), + bitwidth=bw, in_tensors=[test_actvn], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=1) + quantized_acts = ops.convert_to_tensor(test_output[0]).eval() + # Test output activations + self.assertAllClose (quantized_acts[0], test_actvn.eval(), 1.0) + + # Test output encodings from quantizing activations. + enc_min = ops.convert_to_tensor(test_output[1]).eval() + enc_max = ops.convert_to_tensor(test_output[2]).eval() + + true_encodings = self._compute_encodings(enc_min[0], enc_max[0], bw) + # Compare against encodings obtained from get_encoding() + get_enc_tensor = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=1) + + exp_enc_min = ops.convert_to_tensor(get_enc_tensor[1]).eval() + exp_enc_max = ops.convert_to_tensor(get_enc_tensor[2]).eval() + expected_encodings = self._compute_encodings(exp_enc_min[0], exp_enc_max[0], bw) + self.assertAllEqual (true_encodings, expected_encodings) + + libpytrext.ResetQuantizer() + + def testQCQuantize_MultipleActivations(self): + + _log.info('running testQCQuantize_MultipleActivations') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + actvn_stats_0 = actvn_stats_1 = actvn_stats_2 = actvn_stats_3 = constant_op.constant( + np.arange(0, 100).astype(np.float32)) + + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Update quantization stats + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, + in_tensors=[actvn_stats_0, actvn_stats_1, + actvn_stats_2, actvn_stats_3], + fixed_enc_mins=[], fixed_enc_maxs=[]) + + ops.convert_to_tensor(output_0[0]).eval() + + actvn_0 = constant_op.constant(np.arange(0, 10).astype(np.float32)) + actvn_1 = constant_op.constant(np.arange(10, 20).astype(np.float32)) + actvn_2 = constant_op.constant(np.arange(20, 30).astype(np.float32)) + actvn_3 = constant_op.constant(np.arange(30, 40).astype(np.float32)) + test_actvn = [actvn_0, actvn_1, actvn_2, actvn_3] + # Quantize and de-quantize activations + + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_Q_DQ_ACTIVATIONS), + bitwidth=bw, + in_tensors=[actvn_0, actvn_1, actvn_2, actvn_3], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=4) + quantized_acts = ops.convert_to_tensor(output_1[0]).eval() + + quantization_error_margin = 1.0 + for index in np.arange(0, len(quantized_acts)): + self.assertArrayNear(ops.convert_to_tensor(test_actvn[index]).eval(), quantized_acts[index], + quantization_error_margin) + + # Test output encodings + enc_min = ops.convert_to_tensor(output_1[1]).eval() + enc_max = ops.convert_to_tensor(output_1[2]).eval() + + # Compare against encodings obtained from get_encoding() + get_enc_tensor = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], + num_tensors=4) + + exp_enc_min = ops.convert_to_tensor(get_enc_tensor[1]).eval() + exp_enc_max = ops.convert_to_tensor(get_enc_tensor[2]).eval() + + for index in np.arange(0, len(quantized_acts)): + true_encodings = self._compute_encodings(enc_min[index], enc_max[index], bw) + expected_encodings = self._compute_encodings(exp_enc_min[index], exp_enc_max[index], bw) + error_margin = 1.0 # Not a fair test to compare TF with TF_ENHANCED, but works for now + self.assertAllEqual(true_encodings, expected_encodings) + + libpytrext.ResetQuantizer() + + def testQCQuantize_GetEncodings(self): + _log.info('running testQCQuantize_GetEncodings') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Prepare activation tensors + ACT_MIN = -20.0 + ACT_MAX = 25.0 + actvn_1 = constant_op.constant([-10.0, -20.0, 25.0]) + actvn_2 = constant_op.constant([8.0, -19.0, 30.0]) + actvn_3 = constant_op.constant([12.0, -31.0, 35.4]) + + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF) + + # Update stats + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, in_tensors=[actvn_1, actvn_2, actvn_3], + fixed_enc_mins=[], fixed_enc_maxs=[]) + ops.convert_to_tensor(output_0[0]).eval() + + # Get encodings + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=3) + + enc_min = ops.convert_to_tensor(output_1[1]).eval() + enc_max = ops.convert_to_tensor(output_1[2]).eval() + + true_encodings = self._compute_encodings(enc_min[0], enc_max[0], bw) + expected_encodings = self._compute_encodings(ACT_MIN, ACT_MAX, bw) + error_margin = 1e-5 # Use better heuristics + self.assertArrayNear (true_encodings, expected_encodings, error_margin) + + libpytrext.ResetQuantizer() + + @pytest.mark.cuda + def testQCQuantize_GetEncodingsGpu(self): + _log.info('running testQCQuantize_GetEncodings') + for use_gpu in [True]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Prepare activation tensors + ACT_MIN = -20.0 + ACT_MAX = 25.0 + actvn_1 = constant_op.constant([-10.0, -20.0, 25.0]) + actvn_2 = constant_op.constant([8.0, -19.0, 30.0]) + actvn_3 = constant_op.constant([12.0, -31.0, 35.4]) + + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF) + + # Update stats + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_UPDATE_STATS), + bitwidth=bw, in_tensors=[actvn_1, actvn_2, actvn_3], + fixed_enc_mins=[], fixed_enc_maxs=[]) + ops.convert_to_tensor(output_0[0]).eval() + + # Get encodings + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=3) + + enc_min = ops.convert_to_tensor(output_1[1]).eval() + enc_max = ops.convert_to_tensor(output_1[2]).eval() + + true_encodings = self._compute_encodings(enc_min[0], enc_max[0], bw) + expected_encodings = self._compute_encodings(ACT_MIN, ACT_MAX, bw) + error_margin = 1e-5 # Use better heuristics + self.assertArrayNear (true_encodings, expected_encodings, error_margin) + + libpytrext.ResetQuantizer() + + def testQCQuantize_SetEncodings(self): + _log.info('running testQCQuantize_SetEncodings') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_CPU if use_gpu else libpymo.ComputationMode.COMP_MODE_GPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Set encodings + # For the purpose of exact matches with expected encodings + # we choose to avoid ranges excluding 0 as the TF algorithm + # forces the encoding to include 0, thus differing from expected results. + enc_min_list = [-10.0, -0.5, 0] + enc_max_list = [100.0, 200.0, 160.0] + + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_SET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=enc_min_list, + fixed_enc_maxs=enc_max_list) + ops.convert_to_tensor(output_0[0]).eval() + + # Retrieve encodings from op and validate + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config=int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=3) + + get_enc_min = ops.convert_to_tensor(output_1[1]).eval() + get_enc_max = ops.convert_to_tensor(output_1[2]).eval() + for index in np.arange(0, len(enc_min_list)): + actual_encodings = self._compute_encodings(get_enc_min[index], get_enc_max[index], bw) + expected_encodings = self._compute_encodings(enc_min_list[index], enc_max_list[index], bw) + self.assertAllEqual(actual_encodings, expected_encodings) + + libpytrext.ResetQuantizer() + +# @testQCQuantize_CheckZeroRepresentation: +# Test examines if the TF quantization algorithm ensures the representation +# of 0 in the quantized space even when the original sample space is +# entirely positive or entirely negative. + + def testQCQuantize_CheckZeroRepresentation(self): + _log.info('running testQCQuantize_CheckZeroRepresentation') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Test all negative ranges + act_min = -8.0 + act_max = -5.0 + + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Set encodings + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config=int(libpytrext.config_type.CONFIG_TYPE_SET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[act_min], fixed_enc_maxs=[act_max]) + ops.convert_to_tensor(output_0[0]).eval() + + # Get encodings from op and validate + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=1) + + enc_max = ops.convert_to_tensor(output_1[2]).eval() + self.assertEqual(enc_max[0], 0.0) + + # Test all positive ranges + act_min = 20.0 + act_max = 100.0 + + # Set encodings + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_SET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[act_min], fixed_enc_maxs=[act_max]) + ops.convert_to_tensor(output_0[0]).eval() + + # Get encodings from op and validate + output_1 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_GET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=1) + + enc_min = ops.convert_to_tensor(output_1[1]).eval() + self.assertEqual(enc_min[0], 0.0) + + libpytrext.ResetQuantizer() + + def test_QCQuantize_CheckInvalidConfig(self): + _log.info('running test_QCQuantize_CheckInvalidConfig') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu = use_gpu): + bw = 8 + + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Feed an invalid configuration number + output_0 = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config=10, + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=[], fixed_enc_maxs=[], num_tensors=1) + with self.assertRaises(errors_impl.InvalidArgumentError): + ops.convert_to_tensor(output_0[0]).eval() + + libpytrext.ResetQuantizer() + + def testQCQuantize_CheckInvalidEncodingsCpu(self): + _log.info('running testQCQuantize_CheckInvalidEncodings') + for use_gpu in [False]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Set encodings + enc_min_list = [-10.0, 0.5, 20] + enc_max_list = [100.0, 150.0, 200.0, 255.0] + + output = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_SET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=enc_min_list, + fixed_enc_maxs=enc_max_list) + with self.assertRaises(errors_impl.InvalidArgumentError): + ops.convert_to_tensor(output[0]).eval() + + libpytrext.ResetQuantizer() + + @pytest.mark.cuda + def testQCQuantize_CheckInvalidEncodingsGpu(self): + _log.info('running testQCQuantize_CheckInvalidEncodings') + for use_gpu in [True]: + _log.info('GPU mode is selected') if use_gpu else _log.info('CPU mode is selected') + with self.session(use_gpu=use_gpu): + bw = 8 + # Instantiate DlQuantization object + comp_mode = libpymo.ComputationMode.COMP_MODE_GPU if use_gpu else libpymo.ComputationMode.COMP_MODE_CPU + libpytrext.InitQuantizer(["conv1"], comp_mode, [], libpymo.QuantizationMode.QUANTIZATION_TF_ENHANCED) + + # Set encodings + enc_min_list = [-10.0, 0.5, 20] + enc_max_list = [100.0, 150.0, 200.0, 255.0] + + output = self.qc_quantize_module.qc_quantize_deprecated(op_name='conv1', + training_in_progress=False, + config= + int(libpytrext.config_type.CONFIG_TYPE_SET_ENCODING), + bitwidth=bw, in_tensors=[[]], + fixed_enc_mins=enc_min_list, + fixed_enc_maxs=enc_max_list) + with self.assertRaises(errors_impl.InvalidArgumentError): + ops.convert_to_tensor(output[0]).eval() + + libpytrext.ResetQuantizer() + + +if __name__ == "__main__": + tf.test.main() diff --git a/TrainingExtensions/tensorflow/test/python/test_quantizer_info.py b/TrainingExtensions/tensorflow/test/python/test_quantizer_info.py index 85699eccfde..e17ddd4861a 100644 --- a/TrainingExtensions/tensorflow/test/python/test_quantizer_info.py +++ b/TrainingExtensions/tensorflow/test/python/test_quantizer_info.py @@ -39,14 +39,14 @@ import shutil import os -import tensorflow as tf from aimet_tensorflow.examples.test_models import keras_model from aimet_tensorflow.quantsim import QuantizationSimModel from aimet_tensorflow.quantizer_info import QuantizeOpIndices import libpymo -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +import tensorflow as tf +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" diff --git a/TrainingExtensions/tensorflow/test/python/test_quantsim.py b/TrainingExtensions/tensorflow/test/python/test_quantsim.py index 42bae1949e1..fa67529a855 100644 --- a/TrainingExtensions/tensorflow/test/python/test_quantsim.py +++ b/TrainingExtensions/tensorflow/test/python/test_quantsim.py @@ -41,7 +41,7 @@ import json import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import numpy as np diff --git a/TrainingExtensions/tensorflow/test/python/test_quantsim_config.py b/TrainingExtensions/tensorflow/test/python/test_quantsim_config.py index 4f7a9d7f889..a93f4d233a3 100644 --- a/TrainingExtensions/tensorflow/test/python/test_quantsim_config.py +++ b/TrainingExtensions/tensorflow/test/python/test_quantsim_config.py @@ -42,7 +42,7 @@ import unittest import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.connected_graph.connectedgraph_utils import get_all_input_ops diff --git a/TrainingExtensions/tensorflow/test/python/test_quantsim_rnn.py b/TrainingExtensions/tensorflow/test/python/test_quantsim_rnn.py index 4e617312374..aecf32d8c6f 100644 --- a/TrainingExtensions/tensorflow/test/python/test_quantsim_rnn.py +++ b/TrainingExtensions/tensorflow/test/python/test_quantsim_rnn.py @@ -40,7 +40,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import numpy as np diff --git a/TrainingExtensions/tensorflow/test/python/test_spatial_svd.py b/TrainingExtensions/tensorflow/test/python/test_spatial_svd.py index f79fca4c23a..8a11045b841 100644 --- a/TrainingExtensions/tensorflow/test/python/test_spatial_svd.py +++ b/TrainingExtensions/tensorflow/test/python/test_spatial_svd.py @@ -45,7 +45,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.utils import AimetLogger diff --git a/TrainingExtensions/tensorflow/test/python/test_svd.py b/TrainingExtensions/tensorflow/test/python/test_svd.py index 76ee0d8e48c..aee65e77dd7 100644 --- a/TrainingExtensions/tensorflow/test/python/test_svd.py +++ b/TrainingExtensions/tensorflow/test/python/test_svd.py @@ -41,7 +41,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_tensorflow import svd as s diff --git a/TrainingExtensions/tensorflow/test/python/test_tf_connected_graph_with_subgraph_matcher.py b/TrainingExtensions/tensorflow/test/python/test_tf_connected_graph_with_subgraph_matcher.py index a1985b7bad0..535fa9a9c0b 100644 --- a/TrainingExtensions/tensorflow/test/python/test_tf_connected_graph_with_subgraph_matcher.py +++ b/TrainingExtensions/tensorflow/test/python/test_tf_connected_graph_with_subgraph_matcher.py @@ -44,7 +44,7 @@ import logging import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from tensorflow.contrib.graph_editor.edit import detach_inputs diff --git a/TrainingExtensions/tensorflow/test/python/test_utils.py b/TrainingExtensions/tensorflow/test/python/test_utils.py index c071006aae6..1a8c0a80654 100644 --- a/TrainingExtensions/tensorflow/test/python/test_utils.py +++ b/TrainingExtensions/tensorflow/test/python/test_utils.py @@ -42,7 +42,7 @@ import os import tensorflow as tf -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import tensorflow.contrib.slim as slim diff --git a/TrainingExtensions/tensorflow/test/python/test_visualization_per_layer.py b/TrainingExtensions/tensorflow/test/python/test_visualization_per_layer.py index 880c1830b07..471d061ea05 100644 --- a/TrainingExtensions/tensorflow/test/python/test_visualization_per_layer.py +++ b/TrainingExtensions/tensorflow/test/python/test_visualization_per_layer.py @@ -40,8 +40,7 @@ import signal import unittest import tensorflow as tf - -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" import numpy as np diff --git a/TrainingExtensions/tensorflow/test/python/test_winnow.py b/TrainingExtensions/tensorflow/test/python/test_winnow.py index faa670cc730..9f287866025 100644 --- a/TrainingExtensions/tensorflow/test/python/test_winnow.py +++ b/TrainingExtensions/tensorflow/test/python/test_winnow.py @@ -45,8 +45,7 @@ import os import numpy as np import tensorflow as tf - -tf.compat.v1.logging.set_verbosity(tf.logging.WARN) +tf.logging.set_verbosity(tf.logging.WARN) os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" from aimet_common.winnow.mask import Mask diff --git a/TrainingExtensions/torch/CMakeLists.txt b/TrainingExtensions/torch/CMakeLists.txt index 155d444b9e7..e661f4ee8ac 100644 --- a/TrainingExtensions/torch/CMakeLists.txt +++ b/TrainingExtensions/torch/CMakeLists.txt @@ -37,6 +37,11 @@ cmake_minimum_required(VERSION 3.5) find_program(PYTHON "python3") +if(APPLE) + set(LIB_EXT "dylib") +else() + set(LIB_EXT "so") +endif() set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/src/setup.py") set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp" @@ -51,6 +56,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/setup.py.in ${SETUP_PY}) add_custom_command(OUTPUT ${OUTPUT} COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH="${CMAKE_BINARY_DIR}/artifacts" + CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ${PYTHON} ${SETUP_PY} install --install-base=${CMAKE_BINARY_DIR}/artifacts --install-purelib=${CMAKE_BINARY_DIR}/artifacts --install-platlib=${CMAKE_BINARY_DIR}/artifacts @@ -74,7 +80,7 @@ add_subdirectory(test) install(DIRECTORY ${CMAKE_BINARY_DIR}/artifacts/ DESTINATION ${AIMET_INSTALL_DIR}/lib/x86_64-linux-gnu - FILES_MATCHING PATTERN "AimetTensorQuantizer*.so" + FILES_MATCHING PATTERN "AimetTensorQuantizer*.${LIB_EXT}" PATTERN "__pycache*" EXCLUDE ) diff --git a/TrainingExtensions/torch/src/python/aimet_torch/onnx_utils.py b/TrainingExtensions/torch/src/python/aimet_torch/onnx_utils.py index 85f37c68035..06cc4b2f69e 100644 --- a/TrainingExtensions/torch/src/python/aimet_torch/onnx_utils.py +++ b/TrainingExtensions/torch/src/python/aimet_torch/onnx_utils.py @@ -130,6 +130,8 @@ def __init__(self, opset_version: int = None, input_names: List[str] = None, out self.opset_version = opset_version self.input_names = input_names self.output_names = output_names +<<<<<<< HEAD +======= @property def kwargs(self): @@ -139,7 +141,16 @@ def kwargs(self): return {'opset_version': self.opset_version, 'input_names': self.input_names, 'output_names': self.output_names} +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e + @property + def kwargs(self): + """ + formats all override options into kwarg format to appended to onnx export call + """ + return {'opset_version': self.opset_version, + 'input_names': self.input_names, + 'output_names': self.output_names} class OnnxSaver: """ @@ -289,6 +300,260 @@ def _map_onnx_nodes_to_pytorch_modules(cls, pt_model, dummy_input, onnx_model_pa # Find all marker nodes end_marker_map, start_marker_map = cls._create_map_of_marker_nodes(onnx_model) +<<<<<<< HEAD + + # Set names + cls._set_onnx_node_names(map_input_tensor_to_node, start_marker_map) + + # Remove markers + for markers in start_marker_map.values(): + for marker in markers: + cls._detach_start_marker_node(map_input_tensor_to_node, map_output_tensor_to_node, marker) + + for markers in end_marker_map.values(): + for marker in markers: + cls._detach_end_marker_node(onnx_model, map_input_tensor_to_node, map_output_tensor_to_node, marker) + + # Make sure we rename the model outputs to original names + cls._set_output_names(onnx_model, model_output_names, map_output_tensor_to_node, map_input_tensor_to_node) + + # Clean up the detached nodes + onnx_model = cls._remove_detached_nodes_from_onnx_graph(onnx_model) + + cls._fix_param_names(onnx_model) + + return onnx_model + + @classmethod + def _fix_param_names(cls, onnx_model): + """ + Parameter names have an additional level due to the name of the Marker module itself. This method removes that. + :param onnx_model: Onnx Model + """ + # Rename initializers + for ini in onnx_model.graph.initializer: + if 'marked_module' in ini.name: + name = ini.name + name = name.replace('marked_module.', '') + ini.name = name + + # Change the references to initializers in each node + for node in onnx_model.graph.node: + indices_to_replace = [] + for index, inp_tensor in enumerate(node.input): + if 'marked_module' in inp_tensor: + indices_to_replace.append(index) + + for index in indices_to_replace: + param_name = node.input[index] + node.input.remove(param_name) + node.input.insert(index, param_name.replace('marked_module.', '')) + + + @classmethod + def _remove_detached_nodes_from_onnx_graph(cls, onnx_model): + """ + Given a ONNX model removes any detached nodes from the graph + :return: Updated onnx model + """ + e = onnx.utils.Extractor(onnx_model) + model_inputs = [inp.name for inp in onnx_model.graph.input] + model_outputs = [output.name for output in onnx_model.graph.output] + onnx_model = e.extract_model(model_inputs, model_outputs) + return onnx_model + + @classmethod + def _set_onnx_node_names(cls, map_input_tensor_to_node, start_marker_map): + """ + Set names of the ONNX nodes using the identifier fields in the marker layers + :param map_input_tensor_to_node: Map of tensor to node consuming that tensor + :param start_marker_map: Map of start marker nodes in the ONNX graph + :return: + """ + def set_name_for_downstream_nodes(starting_nodes, name, depth): + for node in starting_nodes: + + if node.op_type == 'CustomMarker': # Recursion end condition + return + + if depth == 0: + node.name = name + else: + node.name = name + "#" + str(depth) + + for tensor in node.output: + downstream_nodes = map_input_tensor_to_node.get(tensor, []) + set_name_for_downstream_nodes(downstream_nodes, name, depth + 1) + + for node_name, markers in start_marker_map.items(): + for marker in markers: + out_tensor = marker.output[0] + downstream_nodes = map_input_tensor_to_node.get(out_tensor, []) + set_name_for_downstream_nodes(downstream_nodes, node_name, 0) + + @classmethod + def _create_map_of_marker_nodes(cls, onnx_model): + """ + Creates and returns maps of start and end marker nodes + :param onnx_model: Onnx model + :return: Map of end marker node, Map of start marker nodes + """ + start_marker_map = defaultdict(list) + end_marker_map = defaultdict(list) + for node in onnx_model.graph.node: + if node.op_type == 'CustomMarker': + identifier = node.attribute[0].s.decode() + is_start_marker = node.attribute[1].s.decode() + + if is_start_marker == 'True': + start_marker_map[identifier].append(node) + else: + end_marker_map[identifier].append(node) + print(start_marker_map.keys()) + print(end_marker_map.keys()) + return end_marker_map, start_marker_map + + @classmethod + def _create_onnx_model_with_markers(cls, dummy_input, pt_model, working_dir, onnx_export_args) -> onnx.ModelProto: + """ + Exports an onnx model with marker nodes inserted + :param dummy_input: Dummy input + :param pt_model: PyTorch model + :param working_dir: Working directory for storing the exported onnx model + :param onnx_export_args: override options for torch.onnx.export call + :return: Onnx model with marker layers + """ + model = copy.deepcopy(pt_model).cpu() + module_name_map = {} + for module_name, module_ref in model.named_modules(): + if aimet_torch.utils.is_leaf_module(module_ref): + module_name_map[module_ref] = module_name + cls._add_markers(model, module_name_map) + temp_file = os.path.join(working_dir, 'temp_onnx_model_with_markers.onnx') + torch.onnx.export(model, dummy_input, temp_file, enable_onnx_checker=False, **onnx_export_args.kwargs) + onnx_model = onnx.load(temp_file) + return onnx_model + + @classmethod + def _detach_start_marker_node(cls, map_input_tensor_to_node, map_output_tensor_to_node, start_marker): + """ + Given a ONNX start_marker node, detach it from the graph + :param map_input_tensor_to_node: Map of tensor to node consuming the tensor + :param map_output_tensor_to_node: Map of tensor to node producing the tensor + :param start_marker: Reference to the ONNX node to detach + """ + assert len(start_marker.input) == 1 + assert len(start_marker.output) == 1 + + input_tensor = start_marker.input[0] + output_tensor = start_marker.output[0] + + for next_node in map_input_tensor_to_node[output_tensor]: + + index = list(next_node.input).index(output_tensor) + next_node.input.remove(output_tensor) + next_node.input.insert(index, input_tensor) + map_input_tensor_to_node[input_tensor].append(next_node) + + map_input_tensor_to_node[input_tensor].remove(start_marker) + del map_output_tensor_to_node[output_tensor] # No node should produce output tensor anymore + del map_input_tensor_to_node[output_tensor] # No node should consume output tensor anymore + + start_marker.input.pop() + start_marker.output.pop() + + @classmethod + def _detach_end_marker_node(cls, onnx_model, map_input_tensor_to_node, map_output_tensor_to_node, end_marker): + """ + Given a ONNX end_marker node, detach it from the graph + :param onnx_model: ONNX model instance + :param map_input_tensor_to_node: Map of tensor to node consuming the tensor + :param map_output_tensor_to_node: Map of tensor to node producing the tensor + :param end_marker: Reference to the ONNX node to detach + """ + assert len(end_marker.input) == 1 + assert len(end_marker.output) == 1 + + input_tensor = end_marker.input[0] + output_tensor = end_marker.output[0] + + model_outputs = [output.name for output in onnx_model.graph.output] + + if output_tensor in model_outputs: + + # Degenerate case: somebody did a "return y, y" at the end of the model or something similar + for index, model_output in enumerate(model_outputs): + if model_output == output_tensor: + onnx_model.graph.output[index].name = input_tensor + else: + for next_node in map_input_tensor_to_node[output_tensor]: + index = list(next_node.input).index(output_tensor) + next_node.input.remove(output_tensor) + next_node.input.insert(index, input_tensor) + map_input_tensor_to_node[input_tensor].append(next_node) + + map_input_tensor_to_node[input_tensor].remove(end_marker) + if not map_input_tensor_to_node[input_tensor]: + del map_input_tensor_to_node[input_tensor] + + del map_output_tensor_to_node[output_tensor] # No node should produce output tensor anymore + if output_tensor in map_input_tensor_to_node: + del map_input_tensor_to_node[output_tensor] # No node should consume output tensor anymore + + end_marker.input.pop() + end_marker.output.pop() + + @staticmethod + def _set_output_names(onnx_model: onnx.ModelProto, desired_model_output_names, + map_output_tensor_to_node, map_input_tensor_to_node): + + # Find duplicates in model outputs + duplicates = [] + actual_model_output_names = [output.name for output in onnx_model.graph.output] + for output in actual_model_output_names: + if actual_model_output_names.count(output) > 1: + duplicates.append(output) + + # Iterate over the model outputs + for index, output in enumerate(onnx_model.graph.output): + new_tensor = desired_model_output_names[index] + old_tensor = output.name + + if old_tensor == new_tensor: # Nothing to do + continue + + if old_tensor in map_input_tensor_to_node: + # Degenerate case: model output tensor also is an intermediate tensor that inputs into other nodes + for consumer in map_input_tensor_to_node[old_tensor]: + index = list(consumer.input).index(old_tensor) + consumer.input.remove(old_tensor) + consumer.input.insert(index, new_tensor) + if new_tensor not in map_input_tensor_to_node: + map_input_tensor_to_node[new_tensor] = [] + map_input_tensor_to_node[new_tensor].append(consumer) + + del map_input_tensor_to_node[old_tensor] # No node should consume old tensor anymore + + + producer = map_output_tensor_to_node[old_tensor] + + output.name = new_tensor + index = list(producer.output).index(old_tensor) + producer.output.remove(old_tensor) + producer.output.insert(index, new_tensor) + + del map_output_tensor_to_node[old_tensor] + map_output_tensor_to_node[new_tensor] = producer + + # If there were duplicate outputs with the same name, they need to be updated + for output_node in onnx_model.graph.output: + # Ugly double loop - cannot avoid + if output_node.name == old_tensor: + output_node.name = new_tensor + + + @staticmethod +======= # Set names cls._set_onnx_node_names(map_input_tensor_to_node, start_marker_map) @@ -582,6 +847,7 @@ def _set_output_names(onnx_model: onnx.ModelProto, desired_model_output_names, @staticmethod +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e def _collate_io_tensors_for_multi_layer_recurrent_nodes(onnx_model: onnx.NodeProto, node_to_io_tensor_name_map: Dict): """ diff --git a/TrainingExtensions/torch/src/python/aimet_torch/torchscript_utils.py b/TrainingExtensions/torch/src/python/aimet_torch/torchscript_utils.py index 164033dd319..f9dbaee0a03 100644 --- a/TrainingExtensions/torch/src/python/aimet_torch/torchscript_utils.py +++ b/TrainingExtensions/torch/src/python/aimet_torch/torchscript_utils.py @@ -230,7 +230,11 @@ def forward_hook(curr_module: torch.nn.Module, *_): if isinstance(curr_module, (torch.nn.RNN, torch.nn.LSTM, torch.nn.GRU)): raise NotImplementedError('exporting encoding for RNN module via torchscript not supported') +<<<<<<< HEAD + if not isinstance(curr_module, PassThroughOp): +======= if not isinstance(curr_module, torch.nn.Identity): +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e modules.append(curr_module) run_hook_for_layers_with_given_input(model, inputs, forward_hook) diff --git a/TrainingExtensions/torch/src/setup.py.in b/TrainingExtensions/torch/src/setup.py.in index eb9dea15315..823948dcefb 100644 --- a/TrainingExtensions/torch/src/setup.py.in +++ b/TrainingExtensions/torch/src/setup.py.in @@ -47,7 +47,8 @@ setup( sources=['${CMAKE_CURRENT_SOURCE_DIR}/src/AimetTensorQuantizer.cpp'], include_dirs=['${DlQuantizationIncludePaths}'], extra_objects=['${CMAKE_BINARY_DIR}/artifacts/libpymo.so'], - extra_compile_args=['-D_GLIBCXX_USE_CXX11_ABI=0']), + extra_compile_args=['-DGLIBCXX_USE_CXX11_ABI=0'], + ), ], cmdclass={ 'build_ext': BuildExtension diff --git a/TrainingExtensions/torch/test/python/test_onnx_utils.py b/TrainingExtensions/torch/test/python/test_onnx_utils.py index 69404500447..fb9a2dfd604 100644 --- a/TrainingExtensions/torch/test/python/test_onnx_utils.py +++ b/TrainingExtensions/torch/test/python/test_onnx_utils.py @@ -324,6 +324,165 @@ def forward(self, x): expected_nodes = ['conv0', 'conv2'] actual_nodes = [node.name for node in onnx_model.graph.node] assert len(actual_nodes) == len(expected_nodes) +<<<<<<< HEAD + + for name in expected_nodes: + assert name in actual_nodes + + def test_onnx_export_model_output_empty_layer(self): + + class MyModel(torch.nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.conv0 = torch.nn.Conv2d(10, 20, 3) + self.conv2 = torch.nn.Conv2d(20, 20, 3) + self.drop0 = torch.nn.Dropout2d() + self.drop1 = torch.nn.Dropout2d() + + def forward(self, x): + x = self.conv0(x) + x = self.conv2(x) + x = self.drop0(x) + x = self.drop1(x) + + return x + + model = MyModel() + + onnx_utils.OnnxSaver.set_node_names('./data/MyModel.onnx', model, dummy_input=torch.rand(1, 10, 24, 24)) + onnx_model = onnx.load('./data/MyModel.onnx') + + expected_nodes = ['conv0', 'conv2'] + actual_nodes = [node.name for node in onnx_model.graph.node] + assert len(actual_nodes) == len(expected_nodes) + + for name in expected_nodes: + assert name in actual_nodes + + def test_onnx_export_model_empty_layer_consumed_by_multiple_nodes(self): + + class MyModel(torch.nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.conv0 = torch.nn.Conv2d(10, 20, 3) + self.drop0 = torch.nn.Dropout2d() + self.drop1 = torch.nn.Dropout2d() + self.conv1 = torch.nn.Conv2d(20, 20, 3) + self.conv2 = torch.nn.Conv2d(20, 20, 3) + + def forward(self, x): + x = self.conv0(x) + x = self.drop0(x) + x = self.drop1(x) + y1 = self.conv1(x) + y2 = self.conv2(x) + + return y1, y2 + + model = MyModel() + + onnx_utils.OnnxSaver.set_node_names('./data/MyModel.onnx', model, dummy_input=torch.rand(1, 10, 24, 24)) + onnx_model = onnx.load('./data/MyModel.onnx') + + expected_nodes = ['conv0', 'conv1', 'conv2'] + actual_nodes = [node.name for node in onnx_model.graph.node] + assert len(actual_nodes) == len(expected_nodes) + + for name in expected_nodes: + assert name in actual_nodes + + def test_onnx_export_model_input_empty_layer_consumed_by_multiple_nodes(self): + + class MyModel(torch.nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.drop0 = torch.nn.Dropout2d() + self.drop1 = torch.nn.Dropout2d() + self.conv1 = torch.nn.Conv2d(10, 20, 3) + self.conv2 = torch.nn.Conv2d(10, 20, 3) + + def forward(self, x): + x = self.drop0(x) + x = self.drop1(x) + y1 = self.conv1(x) + y2 = self.conv2(x) + + return y1, y2 + + model = MyModel() + + onnx_utils.OnnxSaver.set_node_names('./data/MyModel.onnx', model, dummy_input=torch.rand(1, 10, 24, 24)) + onnx_model = onnx.load('./data/MyModel.onnx') + + expected_nodes = ['conv1', 'conv2'] + actual_nodes = [node.name for node in onnx_model.graph.node] + assert len(actual_nodes) == len(expected_nodes) + + for name in expected_nodes: + assert name in actual_nodes + + def test_onnx_export_intermediate_tensor_also_model_output(self): + + class MyModel(torch.nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.conv1 = torch.nn.Conv2d(10, 20, 3) + self.conv2 = torch.nn.Conv2d(20, 20, 3) + self.conv3 = torch.nn.Conv2d(20, 20, 3) + + def forward(self, x): + x = self.conv1(x) + + y1 = self.conv2(x) + y2 = self.conv3(x) + + return y1, y2, x + + model = MyModel() + + onnx_utils.OnnxSaver.set_node_names('./data/MyModel.onnx', model, dummy_input=torch.rand(1, 10, 24, 24)) + onnx_model = onnx.load('./data/MyModel.onnx') + + expected_nodes = ['conv1', 'conv2', 'conv3'] + actual_nodes = [node.name for node in onnx_model.graph.node] + assert len(actual_nodes) == len(expected_nodes) + + for name in expected_nodes: + assert name in actual_nodes + + def test_onnx_export_intermediate_tensor_also_model_output_via_empty_marker(self): + + class MyModel(torch.nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.conv1 = torch.nn.Conv2d(10, 20, 3) + self.conv2 = torch.nn.Conv2d(20, 20, 3) + self.conv3 = torch.nn.Conv2d(20, 20, 3) + self.drop1 = torch.nn.Dropout2d() + self.drop2 = torch.nn.Dropout2d() + + def forward(self, x): + x = self.conv1(x) + + y1 = self.conv2(x) + y2 = self.conv3(x) + y3 = self.drop1(x) + y4 = self.drop2(x) + + return y1, y2, y3, y4 + + model = MyModel() + + onnx_utils.OnnxSaver.set_node_names('./data/MyModel.onnx', model, dummy_input=torch.rand(1, 10, 24, 24)) + onnx_model = onnx.load('./data/MyModel.onnx') + + expected_nodes = ['conv1', 'conv2', 'conv3'] + actual_nodes = [node.name for node in onnx_model.graph.node] + assert len(actual_nodes) == len(expected_nodes) + + for name in expected_nodes: + assert name in actual_nodes +======= for name in expected_nodes: assert name in actual_nodes @@ -558,3 +717,4 @@ def forward(self, inp): if os.path.exists('./data/MyModel.onnx'): os.remove('./data/MyModel.onnx') +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e diff --git a/TrainingExtensions/torch/test/python/test_quantizer.py b/TrainingExtensions/torch/test/python/test_quantizer.py index 4d3b38a3be7..c61574a305d 100644 --- a/TrainingExtensions/torch/test/python/test_quantizer.py +++ b/TrainingExtensions/torch/test/python/test_quantizer.py @@ -739,8 +739,13 @@ def forward_pass(model, args): print(encoding_data) activation_keys = list(encoding_data["activation_encodings"].keys()) +<<<<<<< HEAD + self.assertTrue(activation_keys[0] == "124") + self.assertTrue(isinstance(encoding_data["activation_encodings"]["124"], list)) +======= assert activation_keys[0] == "124" assert isinstance(encoding_data["activation_encodings"]["124"], list) +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e param_keys = list(encoding_data["param_encodings"].keys()) assert param_keys[2] == "conv1.weight" @@ -1262,7 +1267,11 @@ def test_with_standalone_ops(self): with open('./data/encodings_with_standalone_ops.encodings') as json_file: encoding_data = json.load(json_file) # in onnx definition tensor 16 is output of Reshape, to be ignored +<<<<<<< HEAD + self.assertTrue("32" not in encoding_data["activation_encodings"].keys()) +======= assert "32" not in encoding_data["activation_encodings"].keys() +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e # ------------------------------------------------------------------------------- def test_layers_to_ignore(self): @@ -1582,8 +1591,13 @@ def test_compute_encoding_with_given_bitwidth(self): encoding_dict = QuantizationSimModel.generate_symmetric_encoding_dict( torch.as_tensor(np.array([0.7796169519533523, -0.9791506528745285], dtype='float32')), bitwidth=8) +<<<<<<< HEAD + self.assertEqual(-128, encoding_dict['offset']) + self.assertAlmostEqual(encoding_dict['scale'], 0.0077098476, places=7) +======= assert -128 == encoding_dict['offset'] assert round(encoding_dict['scale'], 7) == 0.0077098 +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e def test_export_dict_input_output(self): """ test export functionality on dictionary input and output """ @@ -1618,8 +1632,16 @@ def forward_pass(model, args): onnx_model = onnx.load('./data/dict_input_output_model.onnx') for inp in onnx_model.graph.input: +<<<<<<< HEAD + self.assertIn(inp.name, ['a', 'b', 'c']) + for exp, act in zip(o_names, onnx_model.graph.output): + self.assertEqual(exp, act.name) + for tensor_name in encoding_data["activation_encodings"].keys(): + self.assertIn(tensor_name, o_names) +======= assert inp.name in ['a', 'b', 'c'] for exp, act in zip(o_names, onnx_model.graph.output): assert exp == act.name for tensor_name in encoding_data["activation_encodings"].keys(): assert tensor_name in o_names +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e diff --git a/buildntest.sh b/buildntest.sh index f0e63eb27dd..dd5a8b266fe 100755 --- a/buildntest.sh +++ b/buildntest.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # ============================================================================= # @@-COPYRIGHT-START-@@ # diff --git a/dobuildntest.sh b/dobuildntest.sh index 2461c66edc0..74d887c1a5b 100755 --- a/dobuildntest.sh +++ b/dobuildntest.sh @@ -190,14 +190,23 @@ if [ $run_clean -eq 0 ] && [ $run_acceptance_tests -eq 0 ] && [ $run_build -eq 0 [ $run_package_gen -eq 0 ] && [ $run_unit_tests -eq 0 ] && [ $run_code_violation -eq 0 ] && \ [ $run_code_coverage -eq 0 ] && [ $run_static_analysis -eq 0 ]; then run_prep=1 - run_clean=1 +# run_clean=1 run_build=1 +<<<<<<< HEAD +# run_package_gen=0 +# run_unit_tests=1 +# run_code_violation=1 +# run_code_coverage=1 +# run_static_analysis=1 +# run_acceptance_tests=0 +======= run_package_gen=1 run_unit_tests=1 run_code_violation=1 run_code_coverage=1 run_static_analysis=1 run_acceptance_tests=0 +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e fi if [[ -z "${workspaceFolder}" ]]; then @@ -255,11 +264,11 @@ if [ $run_prep -eq 1 ]; then ## wget -N https://download.pytorch.org/models/inception_v3_google-1a9a5a14.pth -P ${AIMET_TORCH_HOME}/checkpoints # Clone the google test repo if not already present - google_test_path="${workspaceFolder}/ThirdParty/googletest/googletest-release-1.8.0" + google_test_path="${workspaceFolder}/ThirdParty/googletest/googletest-release-1.10.0" if [ ! -e ${google_test_path} ]; then mkdir -p $workspaceFolder/ThirdParty/googletest pushd $workspaceFolder/ThirdParty/googletest - git clone https://github.com/google/googletest.git -b release-1.8.0 googletest-release-1.8.0 + git clone https://github.com/google/googletest.git -b release-1.10.0 googletest-release-1.10.0 popd check_stage $? "Preparation" "true" fi @@ -312,8 +321,8 @@ if [ $run_prep -eq 1 ]; then pycov_src_path_ending=${pycov_dir_ending%%:*} pycov_test_path_ending=${pycov_dir_ending#*:} # Find all absolute src and test folders ending in the endings of interest - PYCOV_SRC_PATHS+=($(find . -path "*$pycov_src_path_ending" -exec readlink -f {} \; | grep -v build)) - PYCOV_TEST_PATHS+=($(find . -path "*$pycov_test_path_ending" -exec readlink -f {} \; | grep -v build)) + PYCOV_SRC_PATHS+=($(find . -path "*$pycov_src_path_ending" -exec readlink -f {} \; | grep -v build || true)) + PYCOV_TEST_PATHS+=($(find . -path "*$pycov_test_path_ending" -exec readlink -f {} \; | grep -v build || true)) done # Just display all the code coverage paths for debugging purposes @@ -359,12 +368,12 @@ if [ $run_build -eq 1 ]; then set +e cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ${extra_opts} .. - make -j 8 + make -j `nproc` check_stage $? "Build" "true" - echo -e "\n********** Stage 2a: Generate Docs **********\n" - make doc - check_stage $? "Generate Doc" "true" + #echo -e "\n********** Stage 2a: Generate Docs **********\n" + #make doc + #check_stage $? "Generate Doc" "true" fi if [ $run_package_gen -eq 1 ]; then diff --git a/packaging/dependencies/reqs_pip_common.txt b/packaging/dependencies/reqs_pip_common.txt index 8f86c4cac5d..32703ca6fb9 100644 --- a/packaging/dependencies/reqs_pip_common.txt +++ b/packaging/dependencies/reqs_pip_common.txt @@ -12,5 +12,9 @@ holoviews==1.12.7 hvplot==0.4.0 jsonschema==3.1.1 osqp==0.6.1 +<<<<<<< HEAD:packaging/requirements.txt onnx==1.8.1 -timm==0.3.1 \ No newline at end of file +======= +onnx==1.8.1 +timm==0.3.1 +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e:packaging/dependencies/reqs_pip_common.txt diff --git a/packaging/environment.yml b/packaging/environment.yml new file mode 100644 index 00000000000..b9621018083 --- /dev/null +++ b/packaging/environment.yml @@ -0,0 +1,25 @@ +name: aimet +channels: + - pytorch +dependencies: + - cmake + - make + - pyyaml + - jsonschema + - matplotlib + - osqp + - opencv + - numpy=1.19.5 + - scipy + - pandas + - bokeh=1 + - holoviews + - hvplot + - pybind11 + - pytest + - tensorflow=1.15 + - libprotobuf=3.8 + - mkl-devel + - pytorch + - torchvision + - cudatoolkit>=10 diff --git a/packaging/version.txt b/packaging/version.txt index 84cc529467b..61e0160263b 100644 --- a/packaging/version.txt +++ b/packaging/version.txt @@ -1 +1,5 @@ +<<<<<<< HEAD +1.17.0 +======= 1.18.0 +>>>>>>> 05b58a69321e7c93e158debe8c6541924edb5b0e diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000000..d3d6bae2124 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +markers = + cuda: mark a test for CUDA.