diff --git a/.gitmodules b/.gitmodules index 836d824a6f5a..e0ffec11bfd0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -20,12 +20,12 @@ path = 3rdparty/mkldnn url = https://github.com/intel/mkl-dnn.git branch = master -[submodule "3rdparty/cub"] - path = 3rdparty/cub - url = https://github.com/dmlc/cub [submodule "3rdparty/tvm"] path = 3rdparty/tvm url = https://github.com/dmlc/tvm [submodule "3rdparty/onnx-tensorrt"] path = 3rdparty/onnx-tensorrt url = https://github.com/onnx/onnx-tensorrt.git +[submodule "3rdparty/nvidia_cub"] + path = 3rdparty/nvidia_cub + url = https://github.com/NVlabs/cub.git diff --git a/3rdparty/cub b/3rdparty/cub deleted file mode 160000 index 05eb57faa0a4..000000000000 --- a/3rdparty/cub +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 05eb57faa0a4cac37c2a86fdf4b4dc865a95a1a3 diff --git a/3rdparty/dmlc-core b/3rdparty/dmlc-core index 55f3c7bc1d87..3ffea8694adf 160000 --- a/3rdparty/dmlc-core +++ b/3rdparty/dmlc-core @@ -1 +1 @@ -Subproject commit 55f3c7bc1d875fbc7d34fc26651bb8c6818c8355 +Subproject commit 3ffea8694adf9c0363f9abbf162dc0e4a45b22c5 diff --git a/3rdparty/mkldnn b/3rdparty/mkldnn index 722901c9aaef..7de7e5d02bf6 160000 --- a/3rdparty/mkldnn +++ b/3rdparty/mkldnn @@ -1 +1 @@ -Subproject commit 722901c9aaefa579698df778d061d4848ab8c3e3 +Subproject commit 7de7e5d02bf687f971e7668963649728356e0c20 diff --git a/3rdparty/nvidia_cub b/3rdparty/nvidia_cub new file mode 160000 index 000000000000..c3cceac115c0 --- /dev/null +++ b/3rdparty/nvidia_cub @@ -0,0 +1 @@ +Subproject commit c3cceac115c072fb63df1836ff46d8c60d9eb304 diff --git a/3rdparty/sparse-matrix/Makefile b/3rdparty/sparse-matrix/Makefile new file mode 100644 index 000000000000..214312f6586c --- /dev/null +++ b/3rdparty/sparse-matrix/Makefile @@ -0,0 +1,21 @@ +CC = g++ +C = gcc +MKLROOT = /opt/intel/mkl + +ifneq ($(USE_INTEL_PATH),) + MKLROOT = $(USE_INTEL_PATH)/mkl +endif + +CFLAGS = -fpic -O2 -I/opt/intel/mkl/include -c -Wall -Werror -DMKL_ILP64 -m64 -std=c++11 +LDFLAGS = -Wl,--start-group -L${MKLROOT}/../compiler/lib/intel64 ${MKLROOT}/lib/intel64/libmkl_intel_ilp64.a ${MKLROOT}/lib/intel64/libmkl_intel_thread.a ${MKLROOT}/lib/intel64/libmkl_core.a -Wl,--end-group -liomp5 -lpthread -lm -ldl + +default: libsparse_matrix.so + +libsparse_matrix.so: sparse_matrix.o + $(CC) -shared -o libsparse_matrix.so sparse_matrix.o $(LDFLAGS) + +sparse_matrix.o: sparse_matrix.cc sparse_matrix.h + $(CC) $(CFLAGS) sparse_matrix.cc + +clean: + $(RM) libsparse_matrix.so *.o *~ diff --git a/3rdparty/sparse-matrix/sparse_matrix.cc b/3rdparty/sparse-matrix/sparse_matrix.cc new file mode 100644 index 000000000000..fa362f0f8a18 --- /dev/null +++ b/3rdparty/sparse-matrix/sparse_matrix.cc @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include "sparse_matrix.h" + + + +bool mkl_DotCsrDnsDns(SP_INT64* rows_start, SP_INT64* col_indx, + float* values, float* X, float* y, + int rows, int cols, int X_columns) +{ + + sparse_index_base_t indexing = SPARSE_INDEX_BASE_ZERO; + sparse_status_t status; + sparse_matrix_t A = NULL; + sparse_layout_t layout = SPARSE_LAYOUT_ROW_MAJOR; + float one, zero; + one = (float)1.0; + zero = (float)0.0; + + MKL_INT* rows_end = rows_start + 1; + status = mkl_sparse_s_create_csr(&A, indexing, rows, cols, rows_start, rows_end, col_indx, values); + + if (status != SPARSE_STATUS_SUCCESS) + { + std::cout << "mkl_sparse_s_create_csr status :" << status << std::endl; + return false; + } + sparse_operation_t operation = SPARSE_OPERATION_NON_TRANSPOSE; + struct matrix_descr descrA; + descrA.type = SPARSE_MATRIX_TYPE_GENERAL; + + status = mkl_sparse_s_mm(operation, one, A, descrA, layout, X, X_columns, X_columns, zero, y, X_columns); + if (status != SPARSE_STATUS_SUCCESS) + { + std::cout << "mkl_sparse_s_create_csr status :" << status << std::endl; + return false; + } + + mkl_sparse_destroy(A); + + return true; + +} diff --git a/3rdparty/sparse-matrix/sparse_matrix.h b/3rdparty/sparse-matrix/sparse_matrix.h new file mode 100644 index 000000000000..93054a80b374 --- /dev/null +++ b/3rdparty/sparse-matrix/sparse_matrix.h @@ -0,0 +1,48 @@ +#ifndef MXNET_OPERATOR_SPARSE_MATRIX_INL_H_ +#define MXNET_OPERATOR_SPARSE_MATRIX_INL_H_ + + +#if (!defined(__INTEL_COMPILER)) & defined(_MSC_VER) +#define SP_INT64 __int64 +#define SP_UINT64 unsigned __int64 +#else +#define SP_INT64 long long int +#define SP_UINT64 unsigned long long int +#endif + + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef BUILDING_DLL + #ifdef __GNUC__ + #define SPM_API_PUBLIC __attribute__ ((dllexport)) + #else + #define SPM_API_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. + #endif + #else + #ifdef __GNUC__ + #define SPM_API_PUBLIC __attribute__ ((dllimport)) + #else + #define SPM_API_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. + #endif + #endif + #define SPM_API_LOCAL +#else + #if __GNUC__ >= 4 + #define SPM_API_PUBLIC __attribute__ ((visibility ("default"))) + #define SPM_API_LOCAL __attribute__ ((visibility ("hidden"))) + #else + #define SPM_API_PUBLIC + #define SPM_API_LOCAL + #endif +#endif + + + +extern "C" +{ + extern SPM_API_PUBLIC bool mkl_DotCsrDnsDns(SP_INT64* rows_start, SP_INT64* col_indx, + float* values, float* X, float* y, int rows, int cols, int X_columns); + +} + +#endif //MXNET_OPERATOR_SPARSE_MATRIX_INL_H_ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d8ef524bb389..9cd68e14093c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ mxnet_option(USE_MKL_IF_AVAILABLE "Use MKL if found" ON) mxnet_option(USE_MKLML_MKL "Use MKLDNN variant of MKL (if MKL found)" ON IF USE_MKL_IF_AVAILABLE AND (NOT APPLE)) mxnet_option(USE_MKLDNN "Use MKLDNN variant of MKL (if MKL found)" ON IF USE_MKL_IF_AVAILABLE AND (NOT APPLE) AND (NOT MSVC) AND (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") AND (NOT CMAKE_CROSSCOMPILING)) mxnet_option(USE_OPERATOR_TUNING "Enable auto-tuning of operators" ON IF NOT MSVC) -mxnet_option(USE_GPERFTOOLS "Build with GPerfTools support (if found)" ON) +mxnet_option(USE_GPERFTOOLS "Build with GPerfTools support" OFF) mxnet_option(USE_JEMALLOC "Build with Jemalloc support" ON) mxnet_option(USE_PROFILER "Build with Profiler support" ON) mxnet_option(USE_DIST_KVSTORE "Build with DIST_KVSTORE support" OFF) @@ -255,6 +255,7 @@ if(USE_MKLDNN) add_subdirectory(3rdparty/mkldnn) include_directories(3rdparty/mkldnn/include) + include_directories(${PROJECT_BINARY_DIR}/3rdparty/mkldnn/include) add_definitions(-DUSE_MKL=1) add_definitions(-DCUB_MKL=1) add_definitions(-DMXNET_USE_MKLDNN=1) @@ -327,7 +328,7 @@ endforeach() include_directories("include") include_directories("3rdparty/mshadow") -include_directories("3rdparty/cub") +include_directories("3rdparty/nvidia_cub") include_directories("3rdparty/tvm/nnvm/include") include_directories("3rdparty/tvm/include") include_directories("3rdparty/dmlc-core/include") @@ -511,7 +512,7 @@ list(APPEND CUDA ${MSHADOW_CUDASOURCE}) FILE(GLOB_RECURSE GROUP_SOURCE "src/*.cc" "3rdparty/tvm/nnvm/*.cc" "plugin/*.cc") FILE(GLOB_RECURSE GROUP_Include "src/*.h" "3rdparty/tvm/nnvm/*.h" "3rdparty/mshadow/mshadow/*.h" "plugin/*.h") FILE(GLOB_RECURSE GROUP_CUDA "src/*.cu" "src/*.cuh" "3rdparty/mshadow/mshadow/*.cuh" "plugin/*.cu" - "plugin/*.cuh" "3rdparty/cub/cub/*.cuh") + "plugin/*.cuh" "3rdparty/nvidia_cub/cub/*.cuh") assign_source_group("Source" ${GROUP_SOURCE}) assign_source_group("Include" ${GROUP_Include}) assign_source_group("CUDA" ${GROUP_CUDA}) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index caf61e8be6c2..5c5c217b47eb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -65,6 +65,10 @@ The committers are the granted write access to the project. - Marco is the creator of the current MXNet CI. * [Carin Meier](https://github.com/gigasquid) - Carin created and is the current maintainer for the Clojure interface. +* [Patric Zhao](https://github.com/pengzhao-intel) + - Patric is a parallel computing expert and a major contributor to the MXNet MKL-DNN backend. +* [Tao Lv](https://github.com/TaoLv) + - Tao is a major contributor to the MXNet MKL-DNN backend and performance on CPU. ### Become a Committer @@ -211,10 +215,27 @@ List of Contributors * [Harsh Patel](https://github.com/harshp8l) * [Xiao Wang](https://github.com/BeyonderXX) * [Piyush Ghai](https://github.com/piyushghai) +* [Dang Trung Kien](https://github.com/kiendang) * [Zach Boldyga](https://github.com/zboldyga) * [Gordon Reid](https://github.com/gordon1992) - * [Ming Yang](http://ufoym.com) +* [Satya Krishna Gorti](https://github.com/satyakrishnagorti) +* [Neo Chien](https://github.com/cchung100m) +* [Wujie Zhou](https://github.com/eureka7mt) +* [Ciyong Chen](https://github.com/ciyongch) +* [Hao Li](https://github.com/lihaofd) +* [Jin Huang](https://github.com/jinhuang415) +* [Luobao Zou](https://github.com/luobao-intel) +* [Pengxin Yuan](https://github.com/pengxin99) +* [Rong Zhang](https://github.com/rongzha1/) +* [Shu Zhang](https://github.com/Sherry-Zhang) +* [Shufan Wu](https://github.com/juliusshufan) +* [Wenting Jiang](https://github.com/wentingj) +* [Xiaotao Chen](https://github.com/XiaotaoChen) +* [Xinyu Chen](https://github.com/xinyu-intel) +* [Zhennan Qin](https://github.com/ZhennanQin) +* [Zhiyuan Huang](https://github.com/huangzhiyuan) +* [Zak Jost](https://github.com/zjost) Label Bot --------- diff --git a/LICENSE b/LICENSE index b73ba3740c3a..72fe08f4e316 100644 --- a/LICENSE +++ b/LICENSE @@ -315,10 +315,10 @@ Copyright 2005-2008, Google Inc. 3. Moderngpu - For details, see, 3rdparty/ctc_include/contrib/moderngpu/LICENSE Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. - 4. CUB Library - For details, see, 3rdparty/cub/LICENSE.TXT + 4. CUB Library - For details, see, 3rdparty/nvidia_cub/LICENSE.TXT Copyright (c) 2010-2011, Duane Merrill. All rights reserved. Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. - 5. CUB mersenne.h - For details, see 3rdparty/cub/test/mersenne.h + 5. CUB mersenne.h - For details, see 3rdparty/nvidia_cub/test/mersenne.h Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 6. Googlemock - For details, see, 3rdparty/googletest/googlemock/LICENSE Copyright 2006-2015, Google Inc. diff --git a/MKLDNN_README.md b/MKLDNN_README.md index 214fc83985fb..34790c9c513d 100644 --- a/MKLDNN_README.md +++ b/MKLDNN_README.md @@ -15,316 +15,4 @@ -# Build/Install MXNet with MKL-DNN - -A better training and inference performance is expected to be achieved on Intel-Architecture CPUs with MXNet built with [Intel MKL-DNN](https://github.com/intel/mkl-dnn) on multiple operating system, including Linux, Windows and MacOS. -In the following sections, you will find build instructions for MXNet with Intel MKL-DNN on Linux, MacOS and Windows. - -The detailed performance data collected on Intel Xeon CPU with MXNet built with Intel MKL-DNN can be found [here](https://mxnet.incubator.apache.org/faq/perf.html#intel-cpu). - - -

Contents

- -* [1. Linux](#1) -* [2. MacOS](#2) -* [3. Windows](#3) -* [4. Verify MXNet with python](#4) -* [5. Enable MKL BLAS](#5) -* [6. Enable graph optimization](#6) -* [7. Quantization](#7) -* [8. Support](#8) - -

Linux

- -### Prerequisites - -``` -sudo apt-get update -sudo apt-get install -y build-essential git -sudo apt-get install -y libopenblas-dev liblapack-dev -sudo apt-get install -y libopencv-dev -sudo apt-get install -y graphviz -``` - -### Clone MXNet sources - -``` -git clone --recursive https://github.com/apache/incubator-mxnet.git -cd incubator-mxnet -``` - -### Build MXNet with MKL-DNN - -``` -make -j $(nproc) USE_OPENCV=1 USE_MKLDNN=1 USE_BLAS=mkl USE_INTEL_PATH=/opt/intel -``` - -If you don't have the full [MKL](https://software.intel.com/en-us/intel-mkl) library installation, you might use OpenBLAS as the blas library, by setting USE_BLAS=openblas. - -

MacOS

- -### Prerequisites - -Install the dependencies, required for MXNet, with the following commands: - -- [Homebrew](https://brew.sh/) -- llvm (clang in macOS does not support OpenMP) -- OpenCV (for computer vision operations) - -``` -# Paste this command in Mac terminal to install Homebrew -/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" - -# install dependency -brew update -brew install pkg-config -brew install graphviz -brew tap homebrew/core -brew install opencv -brew tap homebrew/versions -brew install llvm -``` - -### Clone MXNet sources - -``` -git clone --recursive https://github.com/apache/incubator-mxnet.git -cd incubator-mxnet -``` - -### Build MXNet with MKL-DNN - -``` -LIBRARY_PATH=$(brew --prefix llvm)/lib/ make -j $(sysctl -n hw.ncpu) CC=$(brew --prefix llvm)/bin/clang CXX=$(brew --prefix llvm)/bin/clang++ USE_OPENCV=1 USE_OPENMP=1 USE_MKLDNN=1 USE_BLAS=apple USE_PROFILER=1 -``` - -

Windows

- -On Windows, you can use [Micrsoft Visual Studio 2015](https://www.visualstudio.com/vs/older-downloads/) and [Microsoft Visual Studio 2017](https://www.visualstudio.com/downloads/) to compile MXNet with Intel MKL-DNN. -[Micrsoft Visual Studio 2015](https://www.visualstudio.com/vs/older-downloads/) is recommended. - -**Visual Studio 2015** - -To build and install MXNet yourself, you need the following dependencies. Install the required dependencies: - -1. If [Microsoft Visual Studio 2015](https://www.visualstudio.com/vs/older-downloads/) is not already installed, download and install it. You can download and install the free community edition. -2. Download and Install [CMake 3](https://cmake.org/) if it is not already installed. -3. Download and install [OpenCV 3](http://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.0.0/opencv-3.0.0.exe/download). -4. Unzip the OpenCV package. -5. Set the environment variable ```OpenCV_DIR``` to point to the ```OpenCV build directory``` (```C:\opencv\build\x64\vc14``` for example). Also, you need to add the OpenCV bin directory (```C:\opencv\build\x64\vc14\bin``` for example) to the ``PATH`` variable. -6. If you have Intel Math Kernel Library (MKL) installed, set ```MKL_ROOT``` to point to ```MKL``` directory that contains the ```include``` and ```lib```. If you want to use MKL blas, you should set ```-DUSE_BLAS=mkl``` when cmake. Typically, you can find the directory in -```C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2018\windows\mkl```. -7. If you don't have the Intel Math Kernel Library (MKL) installed, download and install [OpenBLAS](http://sourceforge.net/projects/openblas/files/v0.2.14/). Note that you should also download ```mingw64.dll.zip`` along with openBLAS and add them to PATH. -8. Set the environment variable ```OpenBLAS_HOME``` to point to the ```OpenBLAS``` directory that contains the ```include``` and ```lib``` directories. Typically, you can find the directory in ```C:\Program files (x86)\OpenBLAS\```. - -After you have installed all of the required dependencies, build the MXNet source code: - -1. Download the MXNet source code from [GitHub](https://github.com/apache/incubator-mxnet). Don't forget to pull the submodules: -``` -git clone --recursive https://github.com/apache/incubator-mxnet.git -``` - -2. Copy file `3rdparty/mkldnn/config_template.vcxproj` to incubator-mxnet root. - -3. Start a Visual Studio command prompt. - -4. Use [CMake 3](https://cmake.org/) to create a Visual Studio solution in ```./build``` or some other directory. Make sure to specify the architecture in the -[CMake 3](https://cmake.org/) command: -``` -mkdir build -cd build -cmake -G "Visual Studio 14 Win64" .. -DUSE_CUDA=0 -DUSE_CUDNN=0 -DUSE_NVRTC=0 -DUSE_OPENCV=1 -DUSE_OPENMP=1 -DUSE_PROFILER=1 -DUSE_BLAS=open -DUSE_LAPACK=1 -DUSE_DIST_KVSTORE=0 -DCUDA_ARCH_NAME=All -DUSE_MKLDNN=1 -DCMAKE_BUILD_TYPE=Release -``` - -5. In Visual Studio, open the solution file,```.sln```, and compile it. -These commands produce a library called ```libmxnet.dll``` in the ```./build/Release/``` or ```./build/Debug``` folder. -Also ```libmkldnn.dll``` with be in the ```./build/3rdparty/mkldnn/src/Release/``` - -6. Make sure that all the dll files used above(such as `libmkldnn.dll`, `libmklml.dll`, `libiomp5.dll`, `libopenblas.dll`, etc) are added to the system PATH. For convinence, you can put all of them to ```\windows\system32```. Or you will come across `Not Found Dependencies` when loading MXNet. - -**Visual Studio 2017** - -To build and install MXNet yourself using [Microsoft Visual Studio 2017](https://www.visualstudio.com/downloads/), you need the following dependencies. Install the required dependencies: - -1. If [Microsoft Visual Studio 2017](https://www.visualstudio.com/downloads/) is not already installed, download and install it. You can download and install the free community edition. -2. Download and install [CMake 3](https://cmake.org/files/v3.11/cmake-3.11.0-rc4-win64-x64.msi) if it is not already installed. -3. Download and install [OpenCV](https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.4.1/opencv-3.4.1-vc14_vc15.exe/download). -4. Unzip the OpenCV package. -5. Set the environment variable ```OpenCV_DIR``` to point to the ```OpenCV build directory``` (e.g., ```OpenCV_DIR = C:\utils\opencv\build```). -6. If you don’t have the Intel Math Kernel Library (MKL) installed, download and install [OpenBlas](https://sourceforge.net/projects/openblas/files/v0.2.20/OpenBLAS%200.2.20%20version.zip/download). -7. Set the environment variable ```OpenBLAS_HOME``` to point to the ```OpenBLAS``` directory that contains the ```include``` and ```lib``` directories (e.g., ```OpenBLAS_HOME = C:\utils\OpenBLAS```). - -After you have installed all of the required dependencies, build the MXNet source code: - -1. Start ```cmd``` in windows. - -2. Download the MXNet source code from GitHub by using following command: - -```r -cd C:\ -git clone --recursive https://github.com/apache/incubator-mxnet.git -``` - -3. Copy file `3rdparty/mkldnn/config_template.vcxproj` to incubator-mxnet root. - -4. Follow [this link](https://docs.microsoft.com/en-us/visualstudio/install/modify-visual-studio) to modify ```Individual components```, and check ```VC++ 2017 version 15.4 v14.11 toolset```, and click ```Modify```. - -5. Change the version of the Visual studio 2017 to v14.11 using the following command (by default the VS2017 is installed in the following path): - -```r -"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" -vcvars_ver=14.11 -``` - -6. Create a build dir using the following command and go to the directory, for example: - -```r -mkdir C:\build -cd C:\build -``` - -7. CMake the MXNet source code by using following command: - -```r -cmake -G "Visual Studio 15 2017 Win64" .. -T host=x64 -DUSE_CUDA=0 -DUSE_CUDNN=0 -DUSE_NVRTC=0 -DUSE_OPENCV=1 -DUSE_OPENMP=1 -DUSE_PROFILER=1 -DUSE_BLAS=open -DUSE_LAPACK=1 -DUSE_DIST_KVSTORE=0 -DCUDA_ARCH_NAME=All -DUSE_MKLDNN=1 -DCMAKE_BUILD_TYPE=Release -``` - -8. After the CMake successfully completed, compile the the MXNet source code by using following command: - -```r -msbuild mxnet.sln /p:Configuration=Release;Platform=x64 /maxcpucount -``` - -9. Make sure that all the dll files used above(such as `libmkldnn.dll`, `libmklml.dll`, `libiomp5.dll`, `libopenblas.dll`, etc) are added to the system PATH. For convinence, you can put all of them to ```\windows\system32```. Or you will come across `Not Found Dependencies` when loading MXNet. - -

Verify MXNet with python

- -``` -cd python -sudo python setup.py install -python -c "import mxnet as mx;print((mx.nd.ones((2, 3))*2).asnumpy());" - -Expected Output: - -[[ 2. 2. 2.] - [ 2. 2. 2.]] -``` - -### Verify whether MKL-DNN works - -After MXNet is installed, you can verify if MKL-DNN backend works well with a single Convolution layer. - -``` -import mxnet as mx -import numpy as np - -num_filter = 32 -kernel = (3, 3) -pad = (1, 1) -shape = (32, 32, 256, 256) - -x = mx.sym.Variable('x') -w = mx.sym.Variable('w') -y = mx.sym.Convolution(data=x, weight=w, num_filter=num_filter, kernel=kernel, no_bias=True, pad=pad) -exe = y.simple_bind(mx.cpu(), x=shape) - -exe.arg_arrays[0][:] = np.random.normal(size=exe.arg_arrays[0].shape) -exe.arg_arrays[1][:] = np.random.normal(size=exe.arg_arrays[1].shape) - -exe.forward(is_train=False) -o = exe.outputs[0] -t = o.asnumpy() -``` - -More detailed debugging and profiling information can be logged by setting the environment variable 'MKLDNN_VERBOSE': -``` -export MKLDNN_VERBOSE=1 -``` -For example, by running above code snippet, the following debugging logs providing more insights on MKL-DNN primitives `convolution` and `reorder`. That includes: Memory layout, infer shape and the time cost of primitive execution. -``` -mkldnn_verbose,exec,reorder,jit:uni,undef,in:f32_nchw out:f32_nChw16c,num:1,32x32x256x256,6.47681 -mkldnn_verbose,exec,reorder,jit:uni,undef,in:f32_oihw out:f32_OIhw16i16o,num:1,32x32x3x3,0.0429688 -mkldnn_verbose,exec,convolution,jit:avx512_common,forward_inference,fsrc:nChw16c fwei:OIhw16i16o fbia:undef fdst:nChw16c,alg:convolution_direct,mb32_g1ic32oc32_ih256oh256kh3sh1dh0ph1_iw256ow256kw3sw1dw0pw1,9.98193 -mkldnn_verbose,exec,reorder,jit:uni,undef,in:f32_oihw out:f32_OIhw16i16o,num:1,32x32x3x3,0.0510254 -mkldnn_verbose,exec,reorder,jit:uni,undef,in:f32_nChw16c out:f32_nchw,num:1,32x32x256x256,20.4819 -``` - -

Enable MKL BLAS

- -With MKL BLAS, the performace is expected to furtherly improved with variable range depending on the computation load of the models. -You can redistribute not only dynamic libraries but also headers, examples and static libraries on accepting the license [Intel® Simplified license](https://software.intel.com/en-us/license/intel-simplified-software-license). -Installing the full MKL installation enables MKL support for all operators under the linalg namespace. - - 1. Download and install the latest full MKL version following instructions on the [intel website.](https://software.intel.com/en-us/mkl) - - 2. Run `make -j ${nproc} USE_BLAS=mkl` - - 3. Navigate into the python directory - - 4. Run `sudo python setup.py install` - -### Verify whether MKL works - -After MXNet is installed, you can verify if MKL BLAS works well with a single dot layer. - -``` -import mxnet as mx -import numpy as np - -shape_x = (1, 10, 8) -shape_w = (1, 12, 8) - -x_npy = np.random.normal(0, 1, shape_x) -w_npy = np.random.normal(0, 1, shape_w) - -x = mx.sym.Variable('x') -w = mx.sym.Variable('w') -y = mx.sym.batch_dot(x, w, transpose_b=True) -exe = y.simple_bind(mx.cpu(), x=x_npy.shape, w=w_npy.shape) - -exe.forward(is_train=False) -o = exe.outputs[0] -t = o.asnumpy() -``` - -You can open the `MKL_VERBOSE` flag by setting environment variable: -``` -export MKL_VERBOSE=1 -``` -Then by running above code snippet, you probably will get the following output message which means `SGEMM` primitive from MKL are called. Layout information and primitive execution performance are also demonstrated in the log message. -``` -Numpy + Intel(R) MKL: THREADING LAYER: (null) -Numpy + Intel(R) MKL: setting Intel(R) MKL to use INTEL OpenMP runtime -Numpy + Intel(R) MKL: preloading libiomp5.so runtime -MKL_VERBOSE Intel(R) MKL 2018.0 Update 1 Product build 20171007 for Intel(R) 64 architecture Intel(R) Advanced Vector Extensions 512 (Intel(R) AVX-512) enabled processors, Lnx 2.40GHz lp64 intel_thread NMICDev:0 -MKL_VERBOSE SGEMM(T,N,12,10,8,0x7f7f927b1378,0x1bc2140,8,0x1ba8040,8,0x7f7f927b1380,0x7f7f7400a280,12) 8.93ms CNR:OFF Dyn:1 FastMM:1 TID:0 NThr:40 WDiv:HOST:+0.000 -``` - -

Enable graph optimization

- -Graph optimization by subgraph feature are available in master branch. You can build from source and then use below command to enable this *experimental* feature for better performance: - -``` -export MXNET_SUBGRAPH_BACKEND=MKLDNN -``` - -This limitations of this experimental feature are: - -- Use this feature only for inference. When training, be sure to turn the feature off by unsetting the `MXNET_SUBGRAPH_BACKEND` environment variable. - -- This feature will only run on the CPU, even if you're using a GPU-enabled build of MXNet. - -- [MXNet Graph Optimization and Quantization Technical Information and Performance Details](https://cwiki.apache.org/confluence/display/MXNET/MXNet+Graph+Optimization+and+Quantization+based+on+subgraph+and+MKL-DNN). - -

Quantization and Inference with INT8

- -Benefiting from Intel® MKL-DNN, MXNet built with Intel® MKL-DNN brings outstanding performance improvement on quantization and inference with INT8 Intel® CPU Platform on Intel® Xeon® Scalable Platform. - -- [CNN Quantization Examples](https://github.com/apache/incubator-mxnet/tree/master/example/quantization). - -

Next Steps and Support

- -- For questions or support specific to MKL, visit the [Intel MKL](https://software.intel.com/en-us/mkl) website. - -- For questions or support specific to MKL, visit the [Intel MKLDNN](https://github.com/intel/mkl-dnn) website. - -- If you find bugs, please open an issue on GitHub for [MXNet with MKL](https://github.com/apache/incubator-mxnet/labels/MKL) or [MXNet with MKLDNN](https://github.com/apache/incubator-mxnet/labels/MKLDNN). +File is moved to [docs/tutorials/mkldnn/MKLDNN_README.md](docs/tutorials/mkldnn/MKLDNN_README.md). diff --git a/Makefile b/Makefile index cd6610581893..53998ac31919 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ ifdef CAFFE_PATH endif ifndef LINT_LANG - LINT_LANG="all" + LINT_LANG = "all" endif ifeq ($(USE_MKLDNN), 1) @@ -144,13 +144,36 @@ ifeq ($(USE_MKLDNN), 1) LDFLAGS += -L$(MKLDNNROOT)/lib -lmkldnn -Wl,-rpath,'$${ORIGIN}' endif + # setup opencv ifeq ($(USE_OPENCV), 1) - CFLAGS += -DMXNET_USE_OPENCV=1 $(shell pkg-config --cflags opencv) - LDFLAGS += $(filter-out -lopencv_ts, $(shell pkg-config --libs opencv)) + CFLAGS += -DMXNET_USE_OPENCV=1 + ifneq ($(filter-out NONE, $(USE_OPENCV_INC_PATH)),) + CFLAGS += -I$(USE_OPENCV_INC_PATH)/include + ifeq ($(filter-out NONE, $(USE_OPENCV_LIB_PATH)),) +$(error Please add the path of OpenCV shared library path into `USE_OPENCV_LIB_PATH`, when `USE_OPENCV_INC_PATH` is not NONE) + endif + LDFLAGS += -L$(USE_OPENCV_LIB_PATH) + ifneq ($(wildcard $(USE_OPENCV_LIB_PATH)/libopencv_imgcodecs.*),) + LDFLAGS += -lopencv_imgcodecs + endif + ifneq ($(wildcard $(USE_OPENCV_LIB_PATH)/libopencv_highgui.*),) + LDFLAGS += -lopencv_highgui + endif + else + ifeq ("$(shell pkg-config --exists opencv4; echo $$?)", "0") + OPENCV_LIB = opencv4 + else + OPENCV_LIB = opencv + endif + CFLAGS += $(shell pkg-config --cflags $(OPENCV_LIB)) + LDFLAGS += $(shell pkg-config --libs-only-L $(OPENCV_LIB)) + LDFLAGS += $(filter -lopencv_imgcodecs -lopencv_highgui, $(shell pkg-config --libs-only-l $(OPENCV_LIB))) + endif + LDFLAGS += -lopencv_imgproc -lopencv_core BIN += bin/im2rec else - CFLAGS+= -DMXNET_USE_OPENCV=0 + CFLAGS += -DMXNET_USE_OPENCV=0 endif ifeq ($(USE_OPENMP), 1) @@ -388,6 +411,14 @@ ifeq ($(USE_DIST_KVSTORE), 1) LDFLAGS += $(PS_LDFLAGS_A) endif +#sparse-matrix +ifeq ($(USE_BLAS), mkl) + SPARSE_MATRIX_DIR = $(ROOTDIR)/3rdparty/sparse-matrix + LIB_DEP += $(SPARSE_MATRIX_DIR)/libsparse_matrix.so + CFLAGS += -I$(SPARSE_MATRIX_DIR) + LDFLAGS += -L$(SPARSE_MATRIX_DIR) -lsparse_matrix -Wl,-rpath,'$${ORIGIN}' +endif + .PHONY: clean all extra-packages test lint docs clean_all rcpplint rcppexport roxygen\ cython2 cython3 cython cyclean @@ -431,7 +462,7 @@ LIB_DEP += $(DMLC_CORE)/libdmlc.a $(NNVM_PATH)/lib/libnnvm.a ALL_DEP = $(OBJ) $(EXTRA_OBJ) $(PLUGIN_OBJ) $(LIB_DEP) ifeq ($(USE_CUDA), 1) - CFLAGS += -I$(ROOTDIR)/3rdparty/cub + CFLAGS += -I$(ROOTDIR)/3rdparty/nvidia_cub ALL_DEP += $(CUOBJ) $(EXTRA_CUOBJ) $(PLUGIN_CUOBJ) LDFLAGS += -lcufft ifeq ($(ENABLE_CUDA_RTC), 1) @@ -525,11 +556,30 @@ ifeq ($(UNAME_S), Darwin) endif endif +ifeq ($(USE_BLAS), mkl) +ifeq ($(UNAME_S), Darwin) + install_name_tool -change '@rpath/libsparse_matrix.dylib' '@loader_path/libsparse_matrix.dylib' $@ +endif +endif + $(PS_PATH)/build/libps.a: PSLITE PSLITE: $(MAKE) CXX="$(CXX)" DEPS_PATH="$(DEPS_PATH)" -C $(PS_PATH) ps +ifeq ($(USE_BLAS), mkl) +$(SPARSE_MATRIX_DIR)/libsparse_matrix.so: SPARSE_MATRIX + +SPARSE_MATRIX: +ifeq ($(USE_INTEL_PATH), NONE) + $(MAKE) -C $(SPARSE_MATRIX_DIR) +else + $(MAKE) -C $(SPARSE_MATRIX_DIR) USE_INTEL_PATH=$(USE_INTEL_PATH) +endif + mkdir -p $(ROOTDIR)/lib + cp $(SPARSE_MATRIX_DIR)/libsparse_matrix.so $(ROOTDIR)/lib/ +endif + $(DMLC_CORE)/libdmlc.a: DMLCCORE DMLCCORE: @@ -606,6 +656,10 @@ rpkg: cp -rf lib/libmklml_intel.so R-package/inst/libs; \ fi + if [ -e "lib/libsparse_matrix.so" ]; then \ + cp -rf lib/libsparse_matrix.so R-package/inst/libs; \ + fi + mkdir -p R-package/inst/include cp -rl include/* R-package/inst/include Rscript -e "if(!require(devtools)){install.packages('devtools', repo = 'https://cloud.r-project.org/')}" @@ -648,8 +702,10 @@ rclean: ifneq ($(EXTRA_OPERATORS),) clean: rclean cyclean $(EXTRA_PACKAGES_CLEAN) $(RM) -r build lib bin deps *~ */*~ */*/*~ */*/*/*~ + (cd scala-package && mvn clean) || true cd $(DMLC_CORE); $(MAKE) clean; cd - cd $(PS_PATH); $(MAKE) clean; cd - + cd $(SPARSE_MATRIX_DIR); $(MAKE) clean; cd - cd $(NNVM_PATH); $(MAKE) clean; cd - cd $(AMALGAMATION_PATH); $(MAKE) clean; cd - $(RM) -r $(patsubst %, %/*.d, $(EXTRA_OPERATORS)) $(patsubst %, %/*/*.d, $(EXTRA_OPERATORS)) @@ -657,8 +713,10 @@ clean: rclean cyclean $(EXTRA_PACKAGES_CLEAN) else clean: rclean mkldnn_clean cyclean testclean $(EXTRA_PACKAGES_CLEAN) $(RM) -r build lib bin *~ */*~ */*/*~ */*/*/*~ + (cd scala-package && mvn clean) || true cd $(DMLC_CORE); $(MAKE) clean; cd - cd $(PS_PATH); $(MAKE) clean; cd - + cd $(SPARSE_MATRIX_DIR); $(MAKE) clean; cd - cd $(NNVM_PATH); $(MAKE) clean; cd - cd $(AMALGAMATION_PATH); $(MAKE) clean; cd - endif diff --git a/NEWS.md b/NEWS.md index 1af4b138e600..ad842ac84786 100644 --- a/NEWS.md +++ b/NEWS.md @@ -164,7 +164,7 @@ MKLDNN backend takes advantage of MXNet subgraph to implement the most of possib ##### Quantization Performance of reduced-precision (INT8) computation is also dramatically improved after the graph optimization feature is applied on CPU Platforms. Various models are supported and can benefit from reduced-precision computation, including symbolic models, Gluon models and even custom models. Users can run most of the pre-trained models with only a few lines of commands and a new quantization script imagenet_gen_qsym_mkldnn.py. The observed accuracy loss is less than 0.5% for popular CNN networks, like ResNet-50, Inception-BN, MobileNet, etc. -Please find detailed information and performance/accuracy numbers here: [MKLDNN README](https://github.com/apache/incubator-mxnet/blob/master/MKLDNN_README.md), [quantization README](https://github.com/apache/incubator-mxnet/tree/master/example/quantization#1) and [design proposal](https://cwiki.apache.org/confluence/display/MXNET/MXNet+Graph+Optimization+and+Quantization+based+on+subgraph+and+MKL-DNN) +Please find detailed information and performance/accuracy numbers here: [MKLDNN README](https://github.com/apache/incubator-mxnet/blob/master/docs/tutorials/mkldnn/MKLDNN_README.md), [quantization README](https://github.com/apache/incubator-mxnet/tree/master/example/quantization#1) and [design proposal](https://cwiki.apache.org/confluence/display/MXNET/MXNet+Graph+Optimization+and+Quantization+based+on+subgraph+and+MKL-DNN) ### New Operators @@ -584,7 +584,7 @@ Submodule@commit ID::Last updated by MXNet:: Last update in submodule * dlpack@10892ac:: Oct 30, 2017 :: Aug 23, 2018 * dmlc-core@0a0e8ad:: Aug 15, 2018 :: Nov 15, 2018 * googletest@ec44c6c:: July 14, 2016 :: July 14, 2016 -* mkldnn@a7c5f53:: Nov 7, 2018 :: Nov 5, 2018 +* mkldnn@722901c:: Feb 13, 2019 :: Feb 12, 2019 * mshadow@696803b:: Sep 28, 2018 :: Nov 7, 2018 * onnx-tensorrt@3d8ee04:: Aug 22, 2018 :: Nov 10, 2018 * openmp@37c7212: Nov 22, 2017 :: Nov 13, 2018 diff --git a/R-package/src/ndarray.cc b/R-package/src/ndarray.cc index 94d24f3fb46b..0409d3ba8887 100644 --- a/R-package/src/ndarray.cc +++ b/R-package/src/ndarray.cc @@ -179,9 +179,9 @@ Rcpp::RObject NDArrayPacker::CreateNDArrayPacker() { } Rcpp::Dimension NDArray::dim() const { - mx_uint ndim; - const mx_uint *pshape; - MX_CALL(MXNDArrayGetShape( + int ndim; + const int *pshape; + MX_CALL(MXNDArrayGetShapeEx( ptr_->handle, &ndim, &pshape)); Rcpp::IntegerVector dat(pshape, pshape + ndim); std::reverse(dat.begin(), dat.end()); diff --git a/R-package/src/symbol.cc b/R-package/src/symbol.cc index 031c9a254019..317e82568012 100644 --- a/R-package/src/symbol.cc +++ b/R-package/src/symbol.cc @@ -167,8 +167,8 @@ Symbol::RObjectType Symbol::GetOutput(mx_uint index) const { // helper function to convert shape into Rcpp vector inline Rcpp::List BuildShapeData(mx_uint shape_size, - const mx_uint *shape_ndim, - const mx_uint **shape_data, + const int *shape_ndim, + const int **shape_data, const std::vector &names) { Rcpp::List ret(shape_size); for (mx_uint i = 0; i < shape_size; ++i) { @@ -185,7 +185,7 @@ SEXP Symbol::InferShape(const Rcpp::List& kwargs) const { << "Need to pass parameters in key=value style.\n"; std::vector keys = kwargs.names(); std::vector arg_ind_ptr(1, 0); - std::vector arg_shape_data; + std::vector arg_shape_data; for (size_t i = 0; i < kwargs.size(); ++i) { RCHECK(keys[i].length() != 0) @@ -197,17 +197,17 @@ SEXP Symbol::InferShape(const Rcpp::List& kwargs) const { std::vector c_keys = CKeys(keys); mx_uint in_shape_size; - const mx_uint *in_shape_ndim; - const mx_uint **in_shape_data; + const int *in_shape_ndim; + const int **in_shape_data; mx_uint out_shape_size; - const mx_uint *out_shape_ndim; - const mx_uint **out_shape_data; + const int *out_shape_ndim; + const int **out_shape_data; mx_uint aux_shape_size; - const mx_uint *aux_shape_ndim; - const mx_uint **aux_shape_data; + const int *aux_shape_ndim; + const int **aux_shape_data; int complete; - MX_CALL(MXSymbolInferShape( + MX_CALL(MXSymbolInferShapeEx( handle_, static_cast(kwargs.size()), dmlc::BeginPtr(c_keys), dmlc::BeginPtr(arg_ind_ptr), dmlc::BeginPtr(arg_shape_data), &in_shape_size, &in_shape_ndim, &in_shape_data, diff --git a/R-package/tests/testthat/get_data.R b/R-package/tests/testthat/get_data.R index 691131c11739..9bcacdb46ac8 100644 --- a/R-package/tests/testthat/get_data.R +++ b/R-package/tests/testthat/get_data.R @@ -55,13 +55,16 @@ GetInception <- function() { if (!dir.exists("model")) { dir.create("model/") } + if (!file.exists("model/Inception-BN-0126.params")) { - download.file("http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-0126.params", - destfile = "model/Inception-BN-0126.params") + download.file( + "http://data.mxnet.io/mxnet/models/imagenet/inception-bn/Inception-BN-0126.params?raw=true", + destfile = "model/Inception-BN-0126.params") } if (!file.exists("model/Inception-BN-symbol.json")) { - download.file("http://data.dmlc.ml/models/imagenet/inception-bn/Inception-BN-symbol.json", - destfile = "model/Inception-BN-symbol.json") + download.file( + "http://data.mxnet.io/mxnet/models/imagenet/inception-bn/Inception-BN-symbol.json", + destfile = "model/Inception-BN-symbol.json") } } diff --git a/R-package/vignettes/CatsDogsFinetune.Rmd b/R-package/vignettes/CatsDogsFinetune.Rmd index 680b5a302498..726bb1a43c77 100644 --- a/R-package/vignettes/CatsDogsFinetune.Rmd +++ b/R-package/vignettes/CatsDogsFinetune.Rmd @@ -162,13 +162,13 @@ val <- data$val ## Load pretrained model -Here we use the pretrained model from http://data.dmlc.ml/models/imagenet/. +Here we use the pretrained model from http://data.mxnet.io/mxnet/data/. There are 1000 classes in imagenet, and we need to replace the last fully connected layer with a new layer for 2 classes. ```{r} -download.file('http://data.dmlc.ml/data/Inception.zip', destfile = 'Inception.zip') +download.file('http://data.mxnet.io/mxnet/data/Inception.zip', destfile = 'Inception.zip') unzip("Inception.zip") inception_bn <- mx.model.load("./Inception-BN", iteration = 126) diff --git a/R-package/vignettes/classifyRealImageWithPretrainedModel.Rmd b/R-package/vignettes/classifyRealImageWithPretrainedModel.Rmd index ff631e0f5ce9..9cfdd5a5473f 100644 --- a/R-package/vignettes/classifyRealImageWithPretrainedModel.Rmd +++ b/R-package/vignettes/classifyRealImageWithPretrainedModel.Rmd @@ -31,7 +31,7 @@ Make sure you unzip the pre-trained model in current folder. And we can use the loading function to load the model into R. ```{r} -download.file('http://data.dmlc.ml/data/Inception.zip', destfile = 'Inception.zip') +download.file('http://data.mxnet.io/mxnet/data/Inception.zip', destfile = 'Inception.zip') unzip("Inception.zip") model <- mx.model.load("Inception/Inception_BN", iteration = 39) ``` diff --git a/README.md b/README.md index 6a8ecdd99bd0..cab2c6deebc7 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ At its core, MXNet contains a dynamic dependency scheduler that automatically pa A graph optimization layer on top of that makes symbolic execution fast and memory efficient. MXNet is portable and lightweight, scaling effectively to multiple GPUs and multiple machines. -MXNet is also more than a deep learning project. It is also a collection of +MXNet is more than a deep learning project. It is a collection of [blue prints and guidelines](https://mxnet.incubator.apache.org/architecture/index.html#deep-learning-system-design-concepts) for building deep learning systems, and interesting insights of DL systems for hackers. @@ -50,6 +50,7 @@ How to Contribute What's New ---------- +* [Version 1.4.0 Release](https://github.com/apache/incubator-mxnet/releases/tag/1.4.0) - MXNet 1.4.0 Release. * [Version 1.3.1 Release](https://github.com/apache/incubator-mxnet/releases/tag/1.3.1) - MXNet 1.3.1 Patch Release. * [Version 1.3.0 Release](https://github.com/apache/incubator-mxnet/releases/tag/1.3.0) - MXNet 1.3.0 Release. * [Version 1.2.0 Release](https://github.com/apache/incubator-mxnet/releases/tag/1.2.0) - MXNet 1.2.0 Release. @@ -64,8 +65,8 @@ What's New * [Version 0.9.1 Release (NNVM refactor)](./docs/architecture/release_note_0_9.md) - NNVM branch is merged into master now. An official release will be made soon. * [Version 0.8.0 Release](https://github.com/dmlc/mxnet/releases/tag/v0.8.0) * [Updated Image Classification with new Pre-trained Models](./example/image-classification) -* [Notebooks How to Use MXNet](https://github.com/zackchase/mxnet-the-straight-dope) -* [MKLDNN for Faster CPU Performance](./MKLDNN_README.md) +* [Notebooks How to Use MXNet](https://github.com/d2l-ai/d2l-en) +* [MKLDNN for Faster CPU Performance](./docs/tutorials/mkldnn/MKLDNN_README.md) * [MXNet Memory Monger, Training Deeper Nets with Sublinear Memory Cost](https://github.com/dmlc/mxnet-memonger) * [Tutorial for NVidia GTC 2016](https://github.com/dmlc/mxnet-gtc-tutorial) * [Embedding Torch layers and functions in MXNet](https://mxnet.incubator.apache.org/faq/torch.html) diff --git a/amalgamation/amalgamation.py b/amalgamation/amalgamation.py index 0a4be02b8ff9..e47ab6b0e22e 100644 --- a/amalgamation/amalgamation.py +++ b/amalgamation/amalgamation.py @@ -46,6 +46,14 @@ if platform.system() != 'Windows': blacklist.append('windows.h') blacklist.append('process.h') + blacklist.append('Shlwapi.h') + +if platform.system() == 'Windows': + blacklist.append('unistd.h') + +if 'freebsd' not in sys.platform: + blacklist.append('sys/endian.h') + def get_sources(def_file): @@ -94,6 +102,7 @@ def find_source(name, start, stage): re1 = re.compile('<([./a-zA-Z0-9_-]*)>') re2 = re.compile('"([./a-zA-Z0-9_-]*)"') +re3 = re.compile('DMLC_EXECINFO_H') sysheaders = [] history = set([]) @@ -129,6 +138,9 @@ def expand(x, pending, stage): with open(x, 'rb') as x_h: for line in x_h.readlines(): uline = line.decode('utf-8') + if '#define DMLC_LOG_STACK_TRACE 1' in uline.strip(): + # Do not enable stacktrace logging + continue if uline.find('#include') < 0: out.write(line) continue @@ -138,10 +150,15 @@ def expand(x, pending, stage): m = re1.search(uline) if not m: m = re2.search(uline) - if not m: - print(uline + ' not found') - continue - path = m.groups()[0] + if m: + path = m.groups()[0] + else: + m = re3.search(uline) + if m: + path = 'execinfo.h' + else: + print(uline + ' not found') + continue h = path.strip('./') if "../3rdparty/" not in path else path if h.endswith('complex.h') and x.endswith('openblas_config.h'): source = '' diff --git a/benchmark/python/control_flow/rnn.py b/benchmark/python/control_flow/rnn.py index 08498724b1b4..24e326c9afd1 100644 --- a/benchmark/python/control_flow/rnn.py +++ b/benchmark/python/control_flow/rnn.py @@ -79,12 +79,7 @@ def _array(shape, ctx): def _get_gpus(): - try: - re = subprocess.check_output(["nvidia-smi", "-L"], universal_newlines=True) - except OSError: - return [] - return range(len([i for i in re.split('\n') if 'GPU' in i])) - + return range(mx.util.get_gpu_count()) def run_benchmark(cell_type, ctx, seq_len, batch_size, hidden_dim): obj = {"foreach": ForeachRNN, "while_loop": WhileRNN}[args.benchmark] diff --git a/ci/Jenkinsfile_utils.groovy b/ci/Jenkinsfile_utils.groovy index 054deb5f87d2..38cc0b927c43 100644 --- a/ci/Jenkinsfile_utils.groovy +++ b/ci/Jenkinsfile_utils.groovy @@ -186,18 +186,27 @@ def update_github_commit_status(state, message) { context = get_github_context() echo "context=${context}" - step([ - $class: 'GitHubCommitStatusSetter', - reposSource: [$class: "ManuallyEnteredRepositorySource", url: repoUrl], - contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context], - commitShaSource: [$class: "ManuallyEnteredShaSource", sha: commitSha], - statusBackrefSource: [$class: "ManuallyEnteredBackrefSource", backref: "${env.RUN_DISPLAY_URL}"], - errorHandlers: [[$class: 'ShallowAnyErrorHandler']], - statusResultSource: [ - $class: 'ConditionalStatusResultSource', - results: [[$class: "AnyBuildResult", message: message, state: state]] - ] - ]) + // a few attempts need to be made: https://github.com/apache/incubator-mxnet/issues/11654 + for (int attempt = 1; attempt <= 3; attempt++) { + echo "Sending GitHub status attempt ${attempt}..." + + step([ + $class: 'GitHubCommitStatusSetter', + reposSource: [$class: "ManuallyEnteredRepositorySource", url: repoUrl], + contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context], + commitShaSource: [$class: "ManuallyEnteredShaSource", sha: commitSha], + statusBackrefSource: [$class: "ManuallyEnteredBackrefSource", backref: "${env.RUN_DISPLAY_URL}"], + errorHandlers: [[$class: 'ShallowAnyErrorHandler']], + statusResultSource: [ + $class: 'ConditionalStatusResultSource', + results: [[$class: "AnyBuildResult", message: message, state: state]] + ] + ]) + + if (attempt <= 2) { + sleep 1 + } + } echo "Publishing commit status done." diff --git a/ci/build_windows.py b/ci/build_windows.py index b7d47fb1fde1..e8658995b68e 100755 --- a/ci/build_windows.py +++ b/ci/build_windows.py @@ -213,11 +213,11 @@ def main(): if system == 'Windows': logging.info("Detected Windows platform") if 'OpenBLAS_HOME' not in os.environ: - os.environ["OpenBLAS_HOME"] = "C:\\mxnet\\openblas" + os.environ["OpenBLAS_HOME"] = "C:\\Program Files\\OpenBLAS-v0.2.19" if 'OpenCV_DIR' not in os.environ: - os.environ["OpenCV_DIR"] = "C:\\mxnet\\opencv_vc14" + os.environ["OpenCV_DIR"] = "C:\\Program Files\\OpenCV-v3.4.1\\build" if 'CUDA_PATH' not in os.environ: - os.environ["CUDA_PATH"] = "C:\\CUDA\\v8.0" + os.environ["CUDA_PATH"] = "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v9.2" windows_build(args) elif system == 'Linux' or system == 'Darwin': diff --git a/ci/docker/install/ubuntu_core.sh b/ci/docker/install/ubuntu_core.sh index 61a4637830da..3cb806e0aadd 100755 --- a/ci/docker/install/ubuntu_core.sh +++ b/ci/docker/install/ubuntu_core.sh @@ -45,6 +45,7 @@ apt-get install -y \ software-properties-common \ sudo \ unzip \ + vim-nox \ wget # Use libturbojpeg package as it is correctly compiled with -fPIC flag diff --git a/ci/docker/install/ubuntu_mklml.sh b/ci/docker/install/ubuntu_mklml.sh index 862e2846403a..e50b6d273b8c 100755 --- a/ci/docker/install/ubuntu_mklml.sh +++ b/ci/docker/install/ubuntu_mklml.sh @@ -21,5 +21,5 @@ # the whole docker cache for the image set -ex -wget -q --no-check-certificate -O /tmp/mklml.tgz https://github.com/intel/mkl-dnn/releases/download/v0.17-rc/mklml_lnx_2019.0.1.20180928.tgz +wget -q --no-check-certificate -O /tmp/mklml.tgz https://github.com/intel/mkl-dnn/releases/download/v0.18/mklml_lnx_2019.0.3.20190220.tgz tar -zxf /tmp/mklml.tgz && cp -rf mklml_*/* /usr/local/ && rm -rf mklml_* diff --git a/ci/docker/qemu/runtime_functions.py b/ci/docker/qemu/runtime_functions.py index 8b8e5acb503c..5a57cb8dae6a 100755 --- a/ci/docker/qemu/runtime_functions.py +++ b/ci/docker/qemu/runtime_functions.py @@ -77,8 +77,8 @@ def run_ut_python3_qemu_internal(): logging.info("=== NOW Running inside QEMU ===") logging.info("PIP Installing %s", pkg) check_call(['sudo', 'pip3', 'install', pkg]) - logging.info("PIP Installing mxnet/tests/requirements.txt") - check_call(['sudo', 'pip3', 'install', '-r', 'mxnet/tests/requirements.txt']) + logging.info("PIP Installing mxnet/test_requirements.txt") + check_call(['sudo', 'pip3', 'install', '-r', 'mxnet/test_requirements.txt']) logging.info("Running tests in mxnet/tests/python/unittest/") check_call(['nosetests', '--with-timer', '--with-xunit', '--xunit-file', 'nosetests_unittest.xml', '--verbose', 'mxnet/tests/python/unittest/test_engine.py']) # Example to run a single unit test: diff --git a/ci/docker/qemu/vmcontrol.py b/ci/docker/qemu/vmcontrol.py index d80e22b1db85..31ef4d2550c3 100644 --- a/ci/docker/qemu/vmcontrol.py +++ b/ci/docker/qemu/vmcontrol.py @@ -229,6 +229,7 @@ def qemu_provision(ssh_port=QEMU_SSH_PORT): qemu_rsync(ssh_port, '/work/runtime_functions.py','') qemu_rsync(ssh_port, '/work/vmcontrol.py','') qemu_rsync(ssh_port, 'mxnet/tests', 'mxnet') + qemu_rsync(ssh_port, 'mxnet/ci/qemu/test_requirements.txt', 'mxnet/test_requirements.txt') logging.info("Provisioning completed successfully.") diff --git a/ci/docker/runtime_functions.sh b/ci/docker/runtime_functions.sh index de1b7795ce69..a89c51de0d8e 100755 --- a/ci/docker/runtime_functions.sh +++ b/ci/docker/runtime_functions.sh @@ -803,7 +803,7 @@ unittest_ubuntu_python2_gpu() { export PYTHONPATH=./python/ export MXNET_MKLDNN_DEBUG=1 # Ignored if not present export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} nosetests-2.7 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu } @@ -812,7 +812,7 @@ unittest_ubuntu_python3_gpu() { export PYTHONPATH=./python/ export MXNET_MKLDNN_DEBUG=1 # Ignored if not present export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu } @@ -829,7 +829,7 @@ unittest_ubuntu_tensorrt_gpu() { export PYTHONPATH=./python/ export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 export LD_LIBRARY_PATH=/work/mxnet/lib:$LD_LIBRARY_PATH - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} python tests/python/tensorrt/lenet5_train.py nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_trt_gpu.xml --verbose --nocapture tests/python/tensorrt/ } @@ -841,7 +841,7 @@ unittest_ubuntu_python2_quantization_gpu() { export PYTHONPATH=./python/ export MXNET_MKLDNN_DEBUG=1 # Ignored if not present export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} nosetests-2.7 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_quantization_gpu.xml --verbose tests/python/quantization_gpu } @@ -852,7 +852,7 @@ unittest_ubuntu_python3_quantization_gpu() { export PYTHONPATH=./python/ export MXNET_MKLDNN_DEBUG=1 # Ignored if not present export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} nosetests-3.4 $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_quantization_gpu.xml --verbose tests/python/quantization_gpu } @@ -1003,7 +1003,7 @@ unittest_centos7_cpu() { unittest_centos7_gpu() { set -ex cd /work/mxnet - export CUDNN_VERSION=7.0.3 + export CUDNN_VERSION=${CUDNN_VERSION:-7.0.3} python3.6 -m "nose" $NOSE_COVERAGE_ARGUMENTS $NOSE_TIMER_ARGUMENTS --with-xunit --xunit-file nosetests_gpu.xml --verbose tests/python/gpu } diff --git a/ci/jenkins/Jenkins_steps.groovy b/ci/jenkins/Jenkins_steps.groovy index cfbf484756e5..5b9ad47f6afb 100644 --- a/ci/jenkins/Jenkins_steps.groovy +++ b/ci/jenkins/Jenkins_steps.groovy @@ -33,7 +33,7 @@ mx_cmake_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/li // mxnet cmake libraries, in cmake builds we do not produce a libnvvm static library by default. mx_cmake_lib_debug = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests' mx_cmake_mkldnn_lib = 'build/libmxnet.so, build/libmxnet.a, build/3rdparty/dmlc-core/libdmlc.a, build/tests/mxnet_unit_tests, build/3rdparty/openmp/runtime/src/libomp.so, build/3rdparty/mkldnn/src/libmkldnn.so.0' -mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' +mx_mkldnn_lib = 'lib/libmxnet.so, lib/libmxnet.a, lib/libiomp5.so, lib/libmkldnn.so.0, lib/libmklml_intel.so, lib/libsparse_matrix.so, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a' mx_tensorrt_lib = 'build/libmxnet.so, lib/libnvonnxparser_runtime.so.0, lib/libnvonnxparser.so.0, lib/libonnx_proto.so, lib/libonnx.so' mx_lib_cpp_examples = 'lib/libmxnet.so, lib/libmxnet.a, 3rdparty/dmlc-core/libdmlc.a, 3rdparty/tvm/nnvm/lib/libnnvm.a, 3rdparty/ps-lite/build/libps.a, deps/lib/libprotobuf-lite.a, deps/lib/libzmq.a, build/cpp-package/example/*' mx_lib_cpp_examples_cpu = 'build/libmxnet.so, build/cpp-package/example/*' @@ -476,13 +476,11 @@ def compile_unix_amalgamation() { def compile_windows_cpu() { return ['Build CPU windows':{ node(NODE_WINDOWS_CPU) { - timeout(time: max_time, unit: 'MINUTES') { - ws('workspace/build-cpu') { - withEnv(['OpenBLAS_HOME=C:\\mxnet\\openblas', 'OpenCV_DIR=C:\\mxnet\\opencv_vc14', 'CUDA_PATH=C:\\CUDA\\v8.0']) { - utils.init_git_win() - powershell 'python ci/build_windows.py -f WIN_CPU' - stash includes: 'windows_package.7z', name: 'windows_package_cpu' - } + ws('workspace/build-cpu') { + timeout(time: max_time, unit: 'MINUTES') { + utils.init_git_win() + powershell 'py -3 ci/build_windows.py -f WIN_CPU' + stash includes: 'windows_package.7z', name: 'windows_package_cpu' } } } @@ -492,13 +490,11 @@ def compile_windows_cpu() { def compile_windows_gpu() { return ['Build GPU windows':{ node(NODE_WINDOWS_CPU) { - timeout(time: max_time, unit: 'MINUTES') { - ws('workspace/build-gpu') { - withEnv(['OpenBLAS_HOME=C:\\mxnet\\openblas', 'OpenCV_DIR=C:\\mxnet\\opencv_vc14', 'CUDA_PATH=C:\\CUDA\\v8.0']) { + ws('workspace/build-gpu') { + timeout(time: max_time, unit: 'MINUTES') { utils.init_git_win() - powershell 'python ci/build_windows.py -f WIN_GPU' + powershell 'py -3 ci/build_windows.py -f WIN_GPU' stash includes: 'windows_package.7z', name: 'windows_package_gpu' - } } } } @@ -508,13 +504,11 @@ def compile_windows_gpu() { def compile_windows_gpu_mkldnn() { return ['Build GPU MKLDNN windows':{ node(NODE_WINDOWS_CPU) { - timeout(time: max_time, unit: 'MINUTES') { - ws('workspace/build-gpu') { - withEnv(['OpenBLAS_HOME=C:\\mxnet\\openblas', 'OpenCV_DIR=C:\\mxnet\\opencv_vc14', 'CUDA_PATH=C:\\CUDA\\v8.0','BUILD_NAME=vc14_gpu_mkldnn']) { - utils.init_git_win() - powershell 'python ci/build_windows.py -f WIN_GPU_MKLDNN' - stash includes: 'windows_package.7z', name: 'windows_package_gpu_mkldnn' - } + ws('workspace/build-gpu') { + timeout(time: max_time, unit: 'MINUTES') { + utils.init_git_win() + powershell 'py -3 ci/build_windows.py -f WIN_GPU_MKLDNN' + stash includes: 'windows_package.7z', name: 'windows_package_gpu_mkldnn' } } } diff --git a/ci/jenkins/Jenkinsfile_unix_gpu b/ci/jenkins/Jenkinsfile_unix_gpu index 664e591abbd8..f6191deb7a68 100644 --- a/ci/jenkins/Jenkinsfile_unix_gpu +++ b/ci/jenkins/Jenkinsfile_unix_gpu @@ -50,7 +50,8 @@ core_logic: { custom_steps.test_unix_python2_mkldnn_gpu(), custom_steps.test_unix_python3_mkldnn_gpu(), custom_steps.test_unix_python3_mkldnn_nocudnn_gpu(), - custom_steps.test_unix_python3_tensorrt_gpu(), +// Disabled temporarily for https://github.com/apache/incubator-mxnet/issues/14626 +// custom_steps.test_unix_python3_tensorrt_gpu(), custom_steps.test_unix_perl_gpu(), custom_steps.test_unix_r_gpu(), custom_steps.test_unix_cpp_gpu(), diff --git a/ci/jenkins/Jenkinsfile_windows_cpu b/ci/jenkins/Jenkinsfile_windows_cpu index 01ca673433f6..5bc40d625930 100644 --- a/ci/jenkins/Jenkinsfile_windows_cpu +++ b/ci/jenkins/Jenkinsfile_windows_cpu @@ -34,14 +34,14 @@ utils.assign_node_labels(utility: 'utility', windows_cpu: 'mxnetwindows-cpu') utils.main_wrapper( core_logic: { utils.parallel_stage('Build', [ - // custom_steps.compile_windows_cpu() + custom_steps.compile_windows_cpu() ]) utils.parallel_stage('Tests', [ - // custom_steps.test_windows_python2_cpu(), - // custom_steps.test_windows_python3_cpu(), - // custom_steps.test_windows_julia07_cpu(), - // custom_steps.test_windows_julia10_cpu() + custom_steps.test_windows_python2_cpu(), + custom_steps.test_windows_python3_cpu(), + custom_steps.test_windows_julia07_cpu(), + custom_steps.test_windows_julia10_cpu() ]) } , diff --git a/ci/jenkins/Jenkinsfile_windows_gpu b/ci/jenkins/Jenkinsfile_windows_gpu index b3447b960b22..2319f25942de 100644 --- a/ci/jenkins/Jenkinsfile_windows_gpu +++ b/ci/jenkins/Jenkinsfile_windows_gpu @@ -34,14 +34,14 @@ utils.assign_node_labels(utility: 'utility', windows_cpu: 'mxnetwindows-cpu', wi utils.main_wrapper( core_logic: { utils.parallel_stage('Build', [ - // custom_steps.compile_windows_gpu(), - // custom_steps.compile_windows_gpu_mkldnn() + custom_steps.compile_windows_gpu(), + custom_steps.compile_windows_gpu_mkldnn() ]) utils.parallel_stage('Tests', [ - // custom_steps.test_windows_python2_gpu(), - // custom_steps.test_windows_python3_gpu(), - // custom_steps.test_windows_python3_gpu_mkldnn() + custom_steps.test_windows_python2_gpu(), + custom_steps.test_windows_python3_gpu(), + custom_steps.test_windows_python3_gpu_mkldnn() ]) } , diff --git a/ci/qemu/README.md b/ci/qemu/README.md index 498f8b7a8739..4beca4a03690 100644 --- a/ci/qemu/README.md +++ b/ci/qemu/README.md @@ -86,3 +86,7 @@ pip3 install -r mxnet_requirements.txt To access qemu control console from tmux: `ctrl-a a c` + +# CI and Testing + +Formally, [runtime_functions.py](https://github.com/apache/incubator-mxnet/blob/master/ci/docker/qemu/runtime_functions.py) would [run](https://github.com/apache/incubator-mxnet/blob/8beea18e3d9835f90b59d3f9de8f9945ac819423/ci/docker/qemu/runtime_functions.py#L81) *pip install -r [mxnet/tests/requirements.txt](https://github.com/apache/incubator-mxnet/blob/master/tests/requirements.txt)*. If the requirements change, there can be an unfortunate side-effect that there are no wheel files for Raspberry Pi for the new requirement. This would trigger a build from source on the emulator, which can take a long time and cause job timeouts. Therefore, we no longer install the `tests/requirements.txt` requirements, but rather rely on [test_requirements.txt](https://github.com/apache/incubator-mxnet/blob/master/ci/qemu/test_requirements.txt) to maintain the requirements for the qemu tests. Should any requirements changes lead to a job time out, it is incumbent on the submitter to update the image to include the requirement and unblock ci. diff --git a/ci/qemu/test_requirements.txt b/ci/qemu/test_requirements.txt new file mode 100644 index 000000000000..77037d89c673 --- /dev/null +++ b/ci/qemu/test_requirements.txt @@ -0,0 +1,3 @@ +mock +nose +nose-timer \ No newline at end of file diff --git a/ci/windows/test_py2_cpu.ps1 b/ci/windows/test_py2_cpu.ps1 index 46e49baeadbb..1c4a72682ae5 100644 --- a/ci/windows/test_py2_cpu.ps1 +++ b/ci/windows/test_py2_cpu.ps1 @@ -22,8 +22,8 @@ $env:PYTHONPATH=join-path $pwd.Path windows_package\python $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home') -c:\Anaconda3\envs\py2\Scripts\pip install -r tests\requirements.txt -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest +C:\Python27\Scripts\pip install -r tests\requirements.txt +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest if (! $?) { Throw ("Error running unittest") } -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train if (! $?) { Throw ("Error running train tests") } diff --git a/ci/windows/test_py2_gpu.ps1 b/ci/windows/test_py2_gpu.ps1 index d362c61da02b..8a6c8e9b44f9 100644 --- a/ci/windows/test_py2_gpu.ps1 +++ b/ci/windows/test_py2_gpu.ps1 @@ -22,12 +22,12 @@ $env:PYTHONPATH=join-path $pwd.Path windows_package\python $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home') -c:\Anaconda3\envs\py2\Scripts\pip install -r tests\requirements.txt -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest +C:\Python27\Scripts\pip install -r tests\requirements.txt +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest if (! $?) { Throw ("Error running unittest") } -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py if (! $?) { Throw ("Error running tests") } -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_forward.xml tests\python\gpu\test_forward.py +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_forward.xml tests\python\gpu\test_forward.py if (! $?) { Throw ("Error running tests") } -c:\Anaconda3\envs\py2\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error tests\python\train +C:\Python27\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error tests\python\train if (! $?) { Throw ("Error running tests") } diff --git a/ci/windows/test_py3_cpu.ps1 b/ci/windows/test_py3_cpu.ps1 index 32da4885fe0a..a7067f9f3f83 100644 --- a/ci/windows/test_py3_cpu.ps1 +++ b/ci/windows/test_py3_cpu.ps1 @@ -22,8 +22,8 @@ $env:PYTHONPATH=join-path $pwd.Path windows_package\python $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home') -c:\Anaconda3\envs\py3\Scripts\pip install -r tests\requirements.txt -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest +C:\Python37\Scripts\pip install -r tests\requirements.txt +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest if (! $?) { Throw ("Error running unittest") } -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train if (! $?) { Throw ("Error running train tests") } diff --git a/ci/windows/test_py3_gpu.ps1 b/ci/windows/test_py3_gpu.ps1 index b30b22ae90e4..5fbc9f2f8036 100644 --- a/ci/windows/test_py3_gpu.ps1 +++ b/ci/windows/test_py3_gpu.ps1 @@ -22,12 +22,12 @@ $env:PYTHONPATH=join-path $pwd.Path windows_package\python $env:MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0 $env:MXNET_HOME=[io.path]::combine($PSScriptRoot, 'mxnet_home') -c:\Anaconda3\envs\py3\Scripts\pip install -r tests\requirements.txt -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest +C:\Python37\Scripts\pip install -r tests\requirements.txt +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_unittest.xml tests\python\unittest if (! $?) { Throw ("Error running unittest") } -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_operator.xml tests\python\gpu\test_operator_gpu.py if (! $?) { Throw ("Error running tests") } -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_forward.xml tests\python\gpu\test_forward.py +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_forward.xml tests\python\gpu\test_forward.py if (! $?) { Throw ("Error running tests") } -c:\Anaconda3\envs\py3\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train +C:\Python37\python.exe -m nose -v --with-timer --timer-ok 1 --timer-warning 15 --timer-filter warning,error --with-xunit --xunit-file nosetests_train.xml tests\python\train if (! $?) { Throw ("Error running tests") } diff --git a/cmake/DownloadMKLML.cmake b/cmake/DownloadMKLML.cmake index eabf861a4b2a..7b0e5ecf7c9c 100644 --- a/cmake/DownloadMKLML.cmake +++ b/cmake/DownloadMKLML.cmake @@ -19,15 +19,19 @@ message(STATUS "Downloading MKLML...") -set(MKLDNN_RELEASE v0.17-rc) -set(MKLML_RELEASE_FILE_SUFFIX 2019.0.1.20180928) +set(MKLDNN_RELEASE v0.18) +set(MKLML_RELEASE_FILE_SUFFIX 2019.0.3.20190220) + +set(MKLML_LNX_MD5 76354b74325cd293aba593d7cbe36b3f) +set(MKLML_WIN_MD5 02286cb980f12af610c05e99dbd78755) +set(MKLML_MAC_MD5 3b28da686a25a4cf995ca4fc5e30e514) if(MSVC) set(MKL_NAME "mklml_win_${MKLML_RELEASE_FILE_SUFFIX}") file(DOWNLOAD "https://github.com/intel/mkl-dnn/releases/download/${MKLDNN_RELEASE}/${MKL_NAME}.zip" "${CMAKE_CURRENT_BINARY_DIR}/mklml/${MKL_NAME}.zip" - EXPECTED_MD5 "443e661bdfd32dbbc99b460b43afceee" SHOW_PROGRESS) + EXPECTED_MD5 "${MKLML_WIN_MD5}" SHOW_PROGRESS) file(DOWNLOAD "https://github.com/apache/incubator-mxnet/releases/download/utils/7z.exe" "${CMAKE_CURRENT_BINARY_DIR}/mklml/7z2.exe" EXPECTED_MD5 "E1CF766CF358F368EC97662D06EA5A4C" SHOW_PROGRESS) @@ -47,7 +51,7 @@ elseif(APPLE) file(DOWNLOAD "https://github.com/intel/mkl-dnn/releases/download/${MKLDNN_RELEASE}/${MKL_NAME}.tgz" "${CMAKE_CURRENT_BINARY_DIR}/mklml/${MKL_NAME}.tgz" - EXPECTED_MD5 "95f887af332205b1d15b392260003952" SHOW_PROGRESS) + EXPECTED_MD5 "${MKLML_MAC_MD5}" SHOW_PROGRESS) execute_process(COMMAND "tar" "-xzf" "${CMAKE_CURRENT_BINARY_DIR}/mklml/${MKL_NAME}.tgz" "-C" "${CMAKE_CURRENT_BINARY_DIR}/mklml/") @@ -61,7 +65,7 @@ elseif(UNIX) file(DOWNLOAD "https://github.com/intel/mkl-dnn/releases/download/${MKLDNN_RELEASE}/${MKL_NAME}.tgz" "${CMAKE_CURRENT_BINARY_DIR}/mklml/${MKL_NAME}.tgz" - EXPECTED_MD5 "a63abf155361322b9c03f8fc50f4f317" SHOW_PROGRESS) + EXPECTED_MD5 "${MKLML_LNX_MD5}" SHOW_PROGRESS) execute_process(COMMAND "tar" "-xzf" "${CMAKE_CURRENT_BINARY_DIR}/mklml/${MKL_NAME}.tgz" "-C" "${CMAKE_CURRENT_BINARY_DIR}/mklml/") diff --git a/contrib/clojure-package/.gitignore b/contrib/clojure-package/.gitignore index f5d81ddc7620..71d812e56ecd 100644 --- a/contrib/clojure-package/.gitignore +++ b/contrib/clojure-package/.gitignore @@ -39,6 +39,8 @@ examples/visualization/test-vis.pdf src/.DS_Store src/org/.DS_Store test/test-ndarray.clj +test/test-ndarray-api.clj test/test-symbol.clj +test/test-symbol-api.clj src/org/apache/clojure_mxnet/gen/* diff --git a/contrib/clojure-package/examples/bert-qa/.gitignore b/contrib/clojure-package/examples/bert-qa/.gitignore new file mode 100644 index 000000000000..d18f225992a9 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/.gitignore @@ -0,0 +1,12 @@ +/target +/classes +/checkouts +profiles.clj +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +.hgignore +.hg/ diff --git a/contrib/clojure-package/examples/bert-qa/README.md b/contrib/clojure-package/examples/bert-qa/README.md new file mode 100644 index 000000000000..9a21bcdfd66b --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/README.md @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + +# bert-qa + +**This example was based off of the Java API one. It shows how to do inference with a pre-trained BERT network that is trained on Questions and Answers using the [SQuAD Dataset](https://rajpurkar.github.io/SQuAD-explorer/)** + +The pretrained model was created using GluonNLP and then exported to the MXNet symbol format. You can find more information in the background section below. + +In this tutorial, we will walk through the BERT QA model trained by MXNet. +Users can provide a question with a paragraph contains answer to the model and +the model will be able to find the best answer from the answer paragraph. + +Example: + +``` +{:input-answer "Steam engines are external combustion engines, where the working fluid is separate from the combustion products. Non-combustion heat sources such as solar power, nuclear power or geothermal energy may be used. The ideal thermodynamic cycle used to analyze this process is called the Rankine cycle. In the cycle, water is heated and transforms into steam within a boiler operating at a high pressure. When expanded through pistons or turbines, mechanical work is done. The reduced-pressure steam is then condensed and pumped back into the boiler." + :input-question "Along with geothermal and nuclear, what is a notable non-combustion heat source?" + :ground-truth-answers ["solar" + "solar power" + "solar power, nuclear power or geothermal energysolar"]} +``` + +The prediction in this case would be `solar power` + +## Setup Guide + +### Step 1: Download the model + +For this tutorial, you can get the model and vocabulary by running following bash file. This script will use `wget` to download these artifacts from AWS S3. + +From the example directory: + +```bash +./get_bert_data.sh +``` + +Some sample questions and answers are provide in the `squad-sample.edn` file. Some are taken directly from the SQuAD dataset and one was just made up. Feel free to edit the file and add your own! + + +## To run + +* `lein install` in the root of the main project directory +* cd into this project directory and do `lein run`. This will execute the cpu version. + +`lein run :cpu` - to run with cpu +`lein run :gpu` - to run with gpu + +## Background + +To learn more about how BERT works in MXNet, please follow this [MXNet Gluon tutorial on NLP using BERT](https://medium.com/apache-mxnet/gluon-nlp-bert-6a489bdd3340). + +The model was extracted from MXNet GluonNLP with static length settings. + +[Download link for the script](https://gluon-nlp.mxnet.io/_downloads/bert.zip) + +The original description can be found in the [MXNet GluonNLP model zoo](https://gluon-nlp.mxnet.io/model_zoo/bert/index.html#bert-base-on-squad-1-1). +```bash +python static_finetune_squad.py --optimizer adam --accumulate 2 --batch_size 6 --lr 3e-5 --epochs 2 --gpu 0 --export + +``` +This script will generate `json` and `param` fles that are the standard MXNet model files. +By default, this model are using `bert_12_768_12` model with extra layers for QA jobs. + +After that, to be able to use it in Java, we need to export the dictionary from the script to parse the text +to actual indexes. Please add the following lines after [this line](https://github.com/dmlc/gluon-nlp/blob/master/scripts/bert/staticbert/static_finetune_squad.py#L262). +```python +import json +json_str = vocab.to_json() +f = open("vocab.json", "w") +f.write(json_str) +f.close() +``` +This would export the token vocabulary in json format. +Once you have these three files, you will be able to run this example without problems. + diff --git a/contrib/clojure-package/examples/bert-qa/get_bert_data.sh b/contrib/clojure-package/examples/bert-qa/get_bert_data.sh new file mode 100755 index 000000000000..603194a03c05 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/get_bert_data.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e + +data_path=model + +if [ ! -d "$data_path" ]; then + mkdir -p "$data_path" + curl https://s3.us-east-2.amazonaws.com/mxnet-scala/scala-example-ci/BertQA/vocab.json -o $data_path/vocab.json + curl https://s3.us-east-2.amazonaws.com/mxnet-scala/scala-example-ci/BertQA/static_bert_qa-0002.params -o $data_path/static_bert_qa-0002.params + curl https://s3.us-east-2.amazonaws.com/mxnet-scala/scala-example-ci/BertQA/static_bert_qa-symbol.json -o $data_path/static_bert_qa-symbol.json +fi diff --git a/contrib/clojure-package/examples/bert-qa/project.clj b/contrib/clojure-package/examples/bert-qa/project.clj new file mode 100644 index 000000000000..d256d44d0798 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/project.clj @@ -0,0 +1,28 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + +(defproject bert-qa "0.1.0-SNAPSHOT" + :description "BERT QA Example" + :plugins [[lein-cljfmt "0.5.7"]] + :dependencies [[org.clojure/clojure "1.9.0"] + [org.apache.mxnet.contrib.clojure/clojure-mxnet "1.5.0-SNAPSHOT"] + [cheshire "5.8.1"]] + :pedantic? :skip + :java-source-paths ["src/java"] + :main bert-qa.infer + :repl-options {:init-ns bert-qa.infer}) diff --git a/contrib/clojure-package/examples/bert-qa/squad-samples.edn b/contrib/clojure-package/examples/bert-qa/squad-samples.edn new file mode 100644 index 000000000000..e99a181f7d17 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/squad-samples.edn @@ -0,0 +1,39 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + +[{:input-answer "Computational complexity theory is a branch of the theory of computation in theoretical computer science that focuses on classifying computational problems according to their inherent difficulty, and relating those classes to each other. A computational problem is understood to be a task that is in principle amenable to being solved by a computer, which is equivalent to stating that the problem may be solved by mechanical application of mathematical steps, such as an algorithm." + :input-question "By what main attribute are computational problems classified utilizing computational complexity theory?" + :ground-truth-answers ["Computational complexity theory" + "Computational complexity theory" + "complexity theory"]} + {:input-answer "Steam engines are external combustion engines, where the working fluid is separate from the combustion products. Non-combustion heat sources such as solar power, nuclear power or geothermal energy may be used. The ideal thermodynamic cycle used to analyze this process is called the Rankine cycle. In the cycle, water is heated and transforms into steam within a boiler operating at a high pressure. When expanded through pistons or turbines, mechanical work is done. The reduced-pressure steam is then condensed and pumped back into the boiler." + :input-question "Along with geothermal and nuclear, what is a notable non-combustion heat source?" + :ground-truth-answers ["solar" + "solar power" + "solar power, nuclear power or geothermal energysolar"]} + {:input-answer "In the 1960s, a series of discoveries, the most important of which was seafloor spreading, showed that the Earth's lithosphere, which includes the crust and rigid uppermost portion of the upper mantle, is separated into a number of tectonic plates that move across the plastically deforming, solid, upper mantle, which is called the asthenosphere. There is an intimate coupling between the movement of the plates on the surface and the convection of the mantle: oceanic plate motions and mantle convection currents always move in the same direction, because the oceanic lithosphere is the rigid upper thermal boundary layer of the convecting mantle. This coupling between rigid plates moving on the surface of the Earth and the convecting mantle is called plate tectonics." + :input-question "What was the most important discovery that led to the understanding that Earth's lithosphere is separated into tectonic plates?" + :ground-truth-answers ["seafloor spreading"]} + ;;; totally made up + {:input-answer "Susan had a cat named Sammy when she lived in the green house." + :input-question "What was Susan's cat named?" + :ground-truth-answers ["Sammy" "sammy"]} + ;;; more or less from wikipedia on clojure + {:input-answer "Rich Hickey is the creator of the Clojure language. Before Clojure, he developed dotLisp, a similar project based on the .NET platform, and three earlier attempts to provide interoperability between Lisp and Java: a Java foreign language interface for Common Lisp, A Foreign Object Interface for Lisp, and a Lisp-friendly interface to Java Servlets." + :input-question "Who created Clojure?" + :ground-truth-answers ["rich" "hickey"]}] diff --git a/contrib/clojure-package/examples/bert-qa/src/bert_qa/infer.clj b/contrib/clojure-package/examples/bert-qa/src/bert_qa/infer.clj new file mode 100644 index 000000000000..836684e04977 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/src/bert_qa/infer.clj @@ -0,0 +1,159 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + +(ns bert-qa.infer + (:require [clojure.string :as string] + [clojure.reflect :as r] + [cheshire.core :as json] + [clojure.java.io :as io] + [clojure.set :as set] + [org.apache.clojure-mxnet.dtype :as dtype] + [org.apache.clojure-mxnet.context :as context] + [org.apache.clojure-mxnet.layout :as layout] + [org.apache.clojure-mxnet.ndarray :as ndarray] + [org.apache.clojure-mxnet.infer :as infer] + [clojure.pprint :as pprint])) + +(def model-path-prefix "model/static_bert_qa") +;; epoch number of the model +(def epoch 2) +;; the vocabulary used in the model +(def model-vocab "model/vocab.json") +;; the input question +;; the maximum length of the sequence +(def seq-length 384) + +;;; data helpers + +(defn break-out-punctuation [s str-match] + (->> (string/split (str s "") (re-pattern (str "\\" str-match))) + (map #(string/replace % "" str-match)))) + +(defn break-out-punctuations [s] + (if-let [target-char (first (re-seq #"[.,?!]" s))] + (break-out-punctuation s target-char) + [s])) + +(defn tokenize [s] + (->> (string/split s #"\s+") + (mapcat break-out-punctuations) + (into []))) + +(defn pad [tokens pad-item num] + (if (>= (count tokens) num) + tokens + (into tokens (repeat (- num (count tokens)) pad-item)))) + +(defn get-vocab [] + (let [vocab (json/parse-stream (clojure.java.io/reader "model/vocab.json"))] + {:idx->token (get vocab "idx_to_token") + :token->idx (get vocab "token_to_idx")})) + +(defn tokens->idxs [token->idx tokens] + (let [unk-idx (get token->idx "[UNK]")] + (mapv #(get token->idx % unk-idx) tokens))) + +(defn idxs->tokens [idx->token idxs] + (mapv #(get idx->token %) idxs)) + +(defn post-processing [result tokens] + (let [output1 (ndarray/slice-axis result 2 0 1) + output2 (ndarray/slice-axis result 2 1 2) + ;;; get the formatted logits result + start-logits (ndarray/reshape output1 [0 -3]) + end-logits (ndarray/reshape output2 [0 -3]) + start-prob (ndarray/softmax start-logits) + end-prob (ndarray/softmax end-logits) + start-idx (-> (ndarray/argmax start-prob 1) + (ndarray/->vec) + (first)) + end-idx (-> (ndarray/argmax end-prob 1) + (ndarray/->vec) + (first))] + (if (> end-idx start-idx) + (subvec tokens start-idx (inc end-idx)) + (subvec tokens end-idx (inc end-idx))))) + +(defn make-predictor [ctx] + (let [input-descs [{:name "data0" + :shape [1 seq-length] + :dtype dtype/FLOAT32 + :layout layout/NT} + {:name "data1" + :shape [1 seq-length] + :dtype dtype/FLOAT32 + :layout layout/NT} + {:name "data2" + :shape [1] + :dtype dtype/FLOAT32 + :layout layout/N}] + factory (infer/model-factory model-path-prefix input-descs)] + (infer/create-predictor + factory + {:contexts [ctx] + :epoch 2}))) + +(defn pre-processing [ctx idx->token token->idx qa-map] + (let [{:keys [input-question input-answer ground-truth-answers]} qa-map + ;;; pre-processing tokenize sentence + token-q (tokenize (string/lower-case input-question)) + token-a (tokenize (string/lower-case input-answer)) + valid-length (+ (count token-q) (count token-a)) + ;;; generate token types [0000...1111...0000] + qa-embedded (into (pad [] 0 (count token-q)) + (pad [] 1 (count token-a))) + token-types (pad qa-embedded 0 seq-length) + ;;; make BERT pre-processing standard + token-a (conj token-a "[SEP]") + token-q (into [] (concat ["[CLS]"] token-q ["[SEP]"] token-a)) + tokens (pad token-q "[PAD]" seq-length) + ;;; pre-processing - token to index translation + + indexes (tokens->idxs token->idx tokens)] + {:input-batch [(ndarray/array indexes [1 seq-length] {:context ctx}) + (ndarray/array token-types [1 seq-length] {:context ctx}) + (ndarray/array [valid-length] [1] {:context ctx})] + :tokens tokens + :qa-map qa-map})) + +(defn infer [ctx] + (let [ctx (context/default-context) + predictor (make-predictor ctx) + {:keys [idx->token token->idx]} (get-vocab) + ;;; samples taken from https://rajpurkar.github.io/SQuAD-explorer/explore/v2.0/dev/ + question-answers (clojure.edn/read-string (slurp "squad-samples.edn"))] + (doseq [qa-map question-answers] + (let [{:keys [input-batch tokens qa-map]} (pre-processing ctx idx->token token->idx qa-map) + result (first (infer/predict-with-ndarray predictor input-batch)) + answer (post-processing result tokens)] + (println "===============================") + (println " Question Answer Data") + (pprint/pprint qa-map) + (println) + (println " Predicted Answer: " answer) + (println "==============================="))))) + +(defn -main [& args] + (let [[dev] args] + (if (= dev ":gpu") + (infer (context/gpu)) + (infer (context/cpu))))) + +(comment + + (infer :cpu)) diff --git a/contrib/clojure-package/examples/bert-qa/test/bert_qa/infer_test.clj b/contrib/clojure-package/examples/bert-qa/test/bert_qa/infer_test.clj new file mode 100644 index 000000000000..767fb089f284 --- /dev/null +++ b/contrib/clojure-package/examples/bert-qa/test/bert_qa/infer_test.clj @@ -0,0 +1,42 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + +(ns bert-qa.infer-test + (:require [bert-qa.infer :refer :all] + [clojure.java.io :as io] + [clojure.java.shell :refer [sh]] + [clojure.test :refer :all] + [org.apache.clojure-mxnet.context :as context] + [org.apache.clojure-mxnet.infer :as infer])) + +(def model-dir "model/") + +(when-not (.exists (io/file (str model-dir "static_bert_qa-0002.params"))) + (println "Downloading bert qa data") + (sh "./get_bert_data.sh")) + +(deftest infer-test + (let [ctx (context/default-context) + predictor (make-predictor ctx) + {:keys [idx->token token->idx]} (get-vocab) + ;;; samples taken from https://rajpurkar.github.io/SQuAD-explorer/explore/v2.0/dev/ + question-answers (clojure.edn/read-string (slurp "squad-samples.edn"))] + (let [qa-map (last question-answers) + {:keys [input-batch tokens qa-map]} (pre-processing ctx idx->token token->idx qa-map) + result (first (infer/predict-with-ndarray predictor input-batch))] + (is (= ["rich" "hickey"] (post-processing result tokens)))))) diff --git a/contrib/clojure-package/examples/infer/objectdetector/project.clj b/contrib/clojure-package/examples/infer/objectdetector/project.clj index cdd9a8991dc8..da01797f5a21 100644 --- a/contrib/clojure-package/examples/infer/objectdetector/project.clj +++ b/contrib/clojure-package/examples/infer/objectdetector/project.clj @@ -22,7 +22,6 @@ :aliases {"run-detector" ["run" "--" "-m" "models/resnet50_ssd/resnet50_ssd_model" "-i" "images/dog.jpg" "-d" "images/"]} :dependencies [[org.clojure/clojure "1.9.0"] [org.clojure/tools.cli "0.4.1"] - [origami "4.0.0-3"] [org.apache.mxnet.contrib.clojure/clojure-mxnet "1.5.0-SNAPSHOT"]] :main ^:skip-aot infer.objectdetector-example :profiles {:uberjar {:aot :all}}) diff --git a/contrib/clojure-package/examples/infer/objectdetector/src/infer/draw.clj b/contrib/clojure-package/examples/infer/objectdetector/src/infer/draw.clj deleted file mode 100644 index d29b34b5c22a..000000000000 --- a/contrib/clojure-package/examples/infer/objectdetector/src/infer/draw.clj +++ /dev/null @@ -1,44 +0,0 @@ -;; Licensed to the Apache Software Foundation (ASF) under one or more -;; contributor license agreements. See the NOTICE file distributed with -;; this work for additional information regarding copyright ownership. -;; The ASF licenses this file to You under the Apache License, Version 2.0 -;; (the "License"); you may not use this file except in compliance with -;; the License. You may obtain a copy of the License at -;; -;; http://www.apache.org/licenses/LICENSE-2.0 -;; -;; Unless required by applicable law or agreed to in writing, software -;; distributed under the License is distributed on an "AS IS" BASIS, -;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -;; See the License for the specific language governing permissions and -;; limitations under the License. -;; - -(ns infer.draw - (:require - [opencv4.colors.rgb :as rgb] - [opencv4.core :refer [FONT_HERSHEY_PLAIN imread imwrite new-point put-text! rectangle]])) - -(defn black-boxes! [img results] - (doseq [{confidence :confidence label :label top-left :top-left bottom-right :bottom-right} results] - (let [w (.width img) - h (.height img) - top-left-p (new-point (int (* w (first top-left))) (int (* h (second top-left)))) - bottom-right-p (new-point (int (* w (first bottom-right))) (int (* h (second bottom-right))))] - (if (< 15 confidence) - (do - (rectangle img top-left-p bottom-right-p rgb/white 1) - (put-text! img - (str label "[" confidence "% ]") - top-left-p - FONT_HERSHEY_PLAIN - 1.0 - rgb/white 1))))) - img) - -(defn draw-bounds [image results output-dir] - (let [out-file (str output-dir "/" (.getName (clojure.java.io/as-file image)))] - (-> image - (imread) - (black-boxes! results) - (imwrite out-file)))) \ No newline at end of file diff --git a/contrib/clojure-package/examples/infer/objectdetector/src/infer/objectdetector_example.clj b/contrib/clojure-package/examples/infer/objectdetector/src/infer/objectdetector_example.clj index 9331798b038c..65d822ff36aa 100644 --- a/contrib/clojure-package/examples/infer/objectdetector/src/infer/objectdetector_example.clj +++ b/contrib/clojure-package/examples/infer/objectdetector/src/infer/objectdetector_example.clj @@ -17,13 +17,15 @@ (ns infer.objectdetector-example (:require [org.apache.clojure-mxnet.context :as context] [org.apache.clojure-mxnet.dtype :as dtype] + [org.apache.clojure-mxnet.image :as image] [org.apache.clojure-mxnet.infer :as infer] [org.apache.clojure-mxnet.layout :as layout] [clojure.java.io :as io] - [infer.draw :as draw] - [clojure.string :refer [join]] + [clojure.string :as string] [clojure.tools.cli :refer [parse-opts]]) - (:gen-class)) + (:gen-class) + (:import (javax.imageio ImageIO) + (java.io File))) (defn check-valid-dir "Check that the input directory exists" @@ -54,27 +56,36 @@ :validate [check-valid-dir "Input directory not found"]] ["-h" "--help"]]) -(defn result->map [{:keys [class prob x-min y-min x-max y-max]}] - (hash-map - :label class - :confidence (int (* 100 prob)) - :top-left [x-min y-min] - :bottom-right [x-max y-max])) -(defn print-results [results] - (doseq [_r results] - (println (format "Class: %s Confidence=%s Coords=(%s, %s)" - (_r :label) - (_r :confidence) - (_r :top-left) - (_r :bottom-right))))) +(defn process-result! [output-dir image-path predictions] + (println "looking at image" image-path) + (println "predictions: " predictions) + (let [buf (ImageIO/read (new File image-path)) + width (.getWidth buf) + height (.getHeight buf) + names (mapv :class predictions) + coords (mapv (fn [prediction] + (-> prediction + (update :x-min #(* width %)) + (update :x-max #(* width %)) + (update :y-min #(* height %)) + (update :y-max #(* height %)))) + predictions) + new-img (-> (ImageIO/read (new File image-path)) + (image/draw-bounding-box! coords + {:stroke 2 + :names (mapv #(str (:class %) "-" (:prob %)) + predictions) + :transparency 0.5 + + :font-size-mult 1.0}))] + (->> (string/split image-path #"\/") + last + (io/file output-dir) + (ImageIO/write new-img "jpg")))) (defn process-results [images results output-dir] - (dotimes [i (count images)] - (let [image (nth images i) _results (map result->map (nth results i))] - (println "processing: " image) - (print-results _results) - (draw/draw-bounds image _results output-dir)))) + (doall (map (partial process-result! output-dir) images results))) (defn detect-single-image "Detect objects in a single image and print top-5 predictions" @@ -82,7 +93,7 @@ ([detector input-image output-dir] (.mkdir (io/file output-dir)) (let [image (infer/load-image-from-file input-image) - topk 5 + topk 3 res (infer/detect-objects detector image topk) ] (process-results @@ -109,7 +120,7 @@ (apply concat (for [image-files image-file-batches] (let [image-batch (infer/load-image-paths image-files) - topk 5 + topk 3 res (infer/detect-objects-batch detector image-batch topk) ] (process-results image-files @@ -143,5 +154,5 @@ (parse-opts args cli-options)] (cond (:help options) (println summary) - (some? errors) (println (join "\n" errors)) + (some? errors) (println (string/join "\n" errors)) :else (run-detector options)))) diff --git a/contrib/clojure-package/examples/infer/objectdetector/test/infer/objectdetector_example_test.clj b/contrib/clojure-package/examples/infer/objectdetector/test/infer/objectdetector_example_test.clj index 696d96b3ae3a..3d20c614918f 100644 --- a/contrib/clojure-package/examples/infer/objectdetector/test/infer/objectdetector_example_test.clj +++ b/contrib/clojure-package/examples/infer/objectdetector/test/infer/objectdetector_example_test.clj @@ -47,11 +47,11 @@ {:keys [class prob x-min x-max y-min y-max] :as pred} (first predictions)] (clojure.pprint/pprint predictions) (is (some? predictions)) - (is (= 5 (count predictions))) + (is (= 3 (count predictions))) (is (string? class)) (is (< 0.8 prob)) (is (every? #(< 0 % 1) [x-min x-max y-min y-max])) - (is (= #{"dog" "person" "bicycle" "car"} (set (mapv :class predictions)))))) + (is (= #{"dog" "bicycle" "car"} (set (mapv :class predictions)))))) (deftest test-batch-detection (let [detector (create-detector) @@ -60,7 +60,7 @@ predictions (first batch-predictions) {:keys [class prob x-min x-max y-min y-max] :as pred} (first predictions)] (is (some? batch-predictions)) - (is (= 5 (count predictions))) + (is (= 3 (count predictions))) (is (string? class)) (is (< 0.8 prob)) (println [x-min x-max y-min y-max]) diff --git a/contrib/clojure-package/integration-tests.sh b/contrib/clojure-package/integration-tests.sh index 3f80ea5fb29a..3df9ba9787b5 100755 --- a/contrib/clojure-package/integration-tests.sh +++ b/contrib/clojure-package/integration-tests.sh @@ -26,7 +26,7 @@ lein install # then run through the examples EXAMPLES_HOME=${MXNET_HOME}/contrib/clojure-package/examples # use AWK pattern for blacklisting -TEST_CASES=`find ${EXAMPLES_HOME} -name test | awk '!/dontselect1|cnn-text-classification/'` +TEST_CASES=`find ${EXAMPLES_HOME} -name test | awk '!/dontselect1|cnn-text-classification|gan|neural-style|pre-trained-models/'` for i in $TEST_CASES ; do cd ${i} && lein test done diff --git a/contrib/clojure-package/src/dev/generator.clj b/contrib/clojure-package/src/dev/generator.clj index ca93c3421d2a..34210bef63d0 100644 --- a/contrib/clojure-package/src/dev/generator.clj +++ b/contrib/clojure-package/src/dev/generator.clj @@ -17,10 +17,14 @@ (ns dev.generator (:require [t6.from-scala.core :as scala] + [t6.from-scala.core :refer [$ $$] :as $] [clojure.reflect :as r] - [org.apache.clojure-mxnet.util :as util] - [clojure.pprint]) - (:import (org.apache.mxnet NDArray Symbol)) + [clojure.pprint] + [org.apache.clojure-mxnet.util :as util]) + (:import (org.apache.mxnet NDArray NDArrayAPI + Symbol SymbolAPI + Base Base$RefInt Base$RefLong Base$RefFloat Base$RefString) + (scala.collection.mutable ListBuffer ArrayBuffer)) (:gen-class)) @@ -34,17 +38,17 @@ (clojure.string/replace #"\_" "-") (clojure.string/replace #"\/" "div"))) -(defn symbol-transform-param-name [parameter-types] +(defn transform-param-names [coerce-fn parameter-types] (->> parameter-types (map str) - (map (fn [x] (or (util/symbol-param-coerce x) x))) + (map (fn [x] (or (coerce-fn x) x))) (map (fn [x] (last (clojure.string/split x #"\.")))))) +(defn symbol-transform-param-name [parameter-types] + (transform-param-names util/symbol-param-coerce parameter-types)) + (defn ndarray-transform-param-name [parameter-types] - (->> parameter-types - (map str) - (map (fn [x] (or (util/ndarray-param-coerce x) x))) - (map (fn [x] (last (clojure.string/split x #"\.")))))) + (transform-param-names util/ndarray-param-coerce parameter-types)) (defn has-variadic? [params] (->> params @@ -56,37 +60,136 @@ (defn increment-param-name [pname] (if-let [num-str (re-find #"-\d" pname)] - (str (first (clojure.string/split pname #"-")) "-" (inc (Integer/parseInt (last (clojure.string/split num-str #"-"))))) + (str + (first (clojure.string/split pname #"-")) + "-" + (inc (Integer/parseInt (last (clojure.string/split num-str #"-"))))) (str pname "-" 1))) -(defn rename-duplicate-params [params] - (reduce (fn [known-names n] (conj known-names (if (contains? (set known-names) n) - (increment-param-name n) - n))) - [] - params)) - +(defn rename-duplicate-params [pnames] + (->> (reduce + (fn [pname-counts n] + (let [rn (if (pname-counts n) (str n "-" (pname-counts n)) n) + inc-pname-counts (update-in pname-counts [n] (fnil inc 0))] + (update-in inc-pname-counts [:params] conj rn))) + {:params []} + pnames) + :params)) + +(defn get-public-no-default-methods [obj] + (->> (r/reflect obj) + :members + (map #(into {} %)) + (filter #(-> % :flags :public)) + (remove #(re-find #"org\$apache\$mxnet" (str (:name %)))) + (remove #(re-find #"\$default" (str (:name %)))))) + +(defn get-public-to-gen-methods [public-to-hand-gen public-no-default] + (let [public-to-hand-gen-names + (into #{} (mapv (comp str :name) public-to-hand-gen))] + (remove #(-> % :name str public-to-hand-gen-names) public-no-default))) -;;;;;;; symbol +(defn public-by-name-and-param-count [public-reflect-info] + (->> public-reflect-info + (group-by :name) + (map (fn [[k v]] [k (group-by #(count (:parameter-types %)) v)])) + (into {}))) -(def symbol-reflect-info (->> (:members (r/reflect Symbol)) - (map #(into {} %)))) +(def license + (str + ";; Licensed to the Apache Software Foundation (ASF) under one or more\n" + ";; contributor license agreements. See the NOTICE file distributed with\n" + ";; this work for additional information regarding copyright ownership.\n" + ";; The ASF licenses this file to You under the Apache License, Version 2.0\n" + ";; (the \"License\"); you may not use this file except in compliance with\n" + ";; the License. You may obtain a copy of the License at\n" + ";;\n" + ";; http://www.apache.org/licenses/LICENSE-2.0\n" + ";;\n" + ";; Unless required by applicable law or agreed to in writing, software\n" + ";; distributed under the License is distributed on an \"AS IS\" BASIS,\n" + ";; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + ";; See the License for the specific language governing permissions and\n" + ";; limitations under the License.\n" + ";;\n")) -(def symbol-public (filter (fn [x] (-> x :flags :public)) symbol-reflect-info)) +(defn write-to-file [functions ns-gen fname] + (with-open [w (clojure.java.io/writer fname)] + (.write w ns-gen) + (.write w "\n\n") + (.write w ";; Do not edit - this is auto-generated") + (.write w "\n\n") + (.write w license) + (.write w "\n\n") + (.write w "\n\n") + (doseq [f functions] + (clojure.pprint/pprint f w) + (.write w "\n")))) -(def symbol-public-no-default (->> symbol-public - (filter #(not (re-find #"org\$apache\$mxnet" (str (:name %))))) - (filter #(not (re-find #"\$default" (str (:name %))))))) +;;;;;;; Common operations + +(def libinfo (Base/_LIB)) +(def op-names + (let [l ($ ListBuffer/empty)] + (do (.mxListAllOpNames libinfo l) + (remove #(or (= "Custom" %) + (re-matches #"^_.*" %)) + (util/buffer->vec l))))) + +(defn- parse-arg-type [s] + (let [[_ var-arg-type _ set-arg-type arg-spec _ type-req _ default-val] (re-find #"(([\w-\[\]\s]+)|\{([^}]+)\})\s*(\([^)]+\))?(,\s*(optional|required)(,\s*default=(.*))?)?" s)] + {:type (clojure.string/trim (or set-arg-type var-arg-type)) + :spec arg-spec + :optional? (or (= "optional" type-req) + (= "boolean" var-arg-type)) + :default default-val + :orig s})) + +(defn- get-op-handle [op-name] + (let [ref (new Base$RefLong 0)] + (do (.nnGetOpHandle libinfo op-name ref) + (.value ref)))) + +(defn gen-op-info [op-name] + (let [handle (get-op-handle op-name) + name (new Base$RefString nil) + desc (new Base$RefString nil) + key-var-num-args (new Base$RefString nil) + num-args (new Base$RefInt 0) + arg-names ($ ListBuffer/empty) + arg-types ($ ListBuffer/empty) + arg-descs ($ ListBuffer/empty)] + (do (.mxSymbolGetAtomicSymbolInfo libinfo + handle + name + desc + num-args + arg-names + arg-types + arg-descs + key-var-num-args) + {:fn-name (clojure-case (.value name)) + :fn-description (.value desc) + :args (mapv (fn [t n d] (assoc t :name n :description d)) + (mapv parse-arg-type (util/buffer->vec arg-types)) + (mapv clojure-case (util/buffer->vec arg-names)) + (util/buffer->vec arg-descs)) + :key-var-num-args (clojure-case (.value key-var-num-args))}))) + +;;;;;;; Symbol + +(def symbol-public-no-default + (get-public-no-default-methods Symbol)) (into #{} (mapcat :parameter-types symbol-public-no-default)) - ;#{java.lang.Object scala.collection.Seq scala.Option long double scala.collection.immutable.Map int ml.dmlc.mxnet.Executor float ml.dmlc.mxnet.Context java.lang.String scala.Enumeration$Value ml.dmlc.mxnet.Symbol int<> ml.dmlc.mxnet.Symbol<> ml.dmlc.mxnet.Shape java.lang.String<>} +;; #{java.lang.Object scala.collection.Seq scala.Option long double scala.collection.immutable.Map int ml.dmlc.mxnet.Executor float ml.dmlc.mxnet.Context java.lang.String scala.Enumeration$Value ml.dmlc.mxnet.Symbol int<> ml.dmlc.mxnet.Symbol<> ml.dmlc.mxnet.Shape java.lang.String<>} -(def symbol-hand-gen-set #{"scala.Option" - "int org.apache.mxnet.Executor" - "scala.Enumeration$Value" - "org.apache.mxnet.Context" - "scala.Tuple2" - "scala.collection.Traversable"} ) +(def symbol-hand-gen-set + #{"scala.Option" + "scala.Enumeration$Value" + "org.apache.mxnet.Context" + "scala.Tuple2" + "scala.collection.Traversable"}) ;;; min and max have a conflicting arity of 2 with the auto gen signatures (def symbol-filter-name-set #{"max" "min"}) @@ -102,34 +205,35 @@ count pos?))) -(def symbol-public-to-hand-gen (filter is-symbol-hand-gen? symbol-public-no-default)) -(def symbol-public-to-gen (->> (remove #(contains?(->> symbol-public-to-hand-gen - (mapv :name) - (mapv str) - (set)) (str (:name %))) symbol-public-no-default))) +(def symbol-public-to-hand-gen + (filter is-symbol-hand-gen? symbol-public-no-default)) +(def symbol-public-to-gen + (get-public-to-gen-methods symbol-public-to-hand-gen + symbol-public-no-default)) (count symbol-public-to-hand-gen) ;=> 35 mostly bind! (count symbol-public-to-gen) ;=> 307 -(into #{} (map :name symbol-public-to-hand-gen));=> #{arange bind ones zeros simpleBind Variable} +(into #{} (map :name symbol-public-to-hand-gen)) +;;=> #{arange bind ones zeros simpleBind Variable} -(defn public-by-name-and-param-count [public-reflect-info] - (->> public-reflect-info - (group-by :name) - (map (fn [[k v]] [k (group-by #(count (:parameter-types %)) v)])) - (into {}))) (defn symbol-vector-args [] - `(if (map? ~'kwargs-map-or-vec-or-sym) (~'util/empty-list) (~'util/coerce-param ~'kwargs-map-or-vec-or-sym #{"scala.collection.Seq"}))) + `(if (map? ~'kwargs-map-or-vec-or-sym) + (~'util/empty-list) + (~'util/coerce-param ~'kwargs-map-or-vec-or-sym #{"scala.collection.Seq"}))) (defn symbol-map-args [] - `(if (map? ~'kwargs-map-or-vec-or-sym) (util/convert-symbol-map ~'kwargs-map-or-vec-or-sym) nil)) + `(if (map? ~'kwargs-map-or-vec-or-sym) + (util/convert-symbol-map ~'kwargs-map-or-vec-or-sym) + nil)) (defn add-symbol-arities [params function-name] - (if (= ["sym-name" "kwargs-map" "symbol-list" "kwargs-map-1"] (mapv str params)) + (if (= ["sym-name" "kwargs-map" "symbol-list" "kwargs-map-1"] + (mapv str params)) [`([~'sym-name ~'attr-map ~'kwargs-map] (~function-name ~'sym-name (~'util/convert-symbol-map ~'attr-map) (~'util/empty-list) (~'util/convert-symbol-map ~'kwargs-map))) `([~'sym-name ~'kwargs-map-or-vec-or-sym] @@ -180,36 +284,7 @@ `(~'defn ~function-name ~@(remove nil? (gen-symbol-function-arity op-name op-values function-name)))))) -(def license - (str - ";; Licensed to the Apache Software Foundation (ASF) under one or more\n" - ";; contributor license agreements. See the NOTICE file distributed with\n" - ";; this work for additional information regarding copyright ownership.\n" - ";; The ASF licenses this file to You under the Apache License, Version 2.0\n" - ";; (the \"License\"); you may not use this file except in compliance with\n" - ";; the License. You may obtain a copy of the License at\n" - ";;\n" - ";; http://www.apache.org/licenses/LICENSE-2.0\n" - ";;\n" - ";; Unless required by applicable law or agreed to in writing, software\n" - ";; distributed under the License is distributed on an \"AS IS\" BASIS,\n" - ";; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" - ";; See the License for the specific language governing permissions and\n" - ";; limitations under the License.\n" - ";;\n")) -(defn write-to-file [functions ns-gen fname] - (with-open [w (clojure.java.io/writer fname)] - (.write w ns-gen) - (.write w "\n\n") - (.write w ";; Do not edit - this is auto-generated") - (.write w "\n\n") - (.write w license) - (.write w "\n\n") - (.write w "\n\n") - (doseq [f functions] - (clojure.pprint/pprint f w) - (.write w "\n")))) (def symbol-gen-ns "(ns org.apache.clojure-mxnet.symbol (:refer-clojure :exclude [* - + > >= < <= / cast concat identity flatten load max @@ -223,25 +298,18 @@ (println "Generating symbol file") (write-to-file all-symbol-functions symbol-gen-ns "src/org/apache/clojure_mxnet/gen/symbol.clj")) +;;;;;;; NDArray -;;;;;;;;NDARRAY - - -(def ndarray-reflect-info (->> (:members (r/reflect NDArray)) - (map #(into {} %)))) +(def ndarray-public-no-default + (get-public-no-default-methods NDArray)) -(def ndarray-public (filter (fn [x] (-> x :flags :public)) ndarray-reflect-info)) - -(def ndarray-public-no-default (->> ndarray-public - (filter #(not (re-find #"org\$apache\$mxnet" (str (:name %))))) - (filter #(not (re-find #"\$default" (str (:name %))))))) - -(def ndarray-hand-gen-set #{"org.apache.mxnet.NDArrayFuncReturn" - "org.apache.mxnet.Context" - "scala.Enumeration$Value" - "scala.Tuple2" - "scala.collection.Traversable"} ) +(def ndarray-hand-gen-set + #{"org.apache.mxnet.NDArrayFuncReturn" + "org.apache.mxnet.Context" + "scala.Enumeration$Value" + "scala.Tuple2" + "scala.collection.Traversable"}) (defn is-ndarray-hand-gen? [info] (->> (map str (:parameter-types info)) @@ -251,17 +319,17 @@ pos?)) -(def ndarray-public-to-hand-gen (filter is-ndarray-hand-gen? ndarray-public-no-default)) -(def ndarray-public-to-gen (->> (remove #(contains?(->> ndarray-public-to-hand-gen - (mapv :name) - (mapv str) - (set)) (str (:name %))) ndarray-public-no-default))) +(def ndarray-public-to-hand-gen + (filter is-ndarray-hand-gen? ndarray-public-no-default)) +(def ndarray-public-to-gen + (get-public-to-gen-methods ndarray-public-to-hand-gen + ndarray-public-no-default)) (count ndarray-public-to-hand-gen) ;=> 15 (count ndarray-public-to-gen) ;=> 486 -(map :name ndarray-public-to-hand-gen) +(->> ndarray-public-to-hand-gen (map :name) (into #{})) @@ -294,16 +362,19 @@ ))))) +(defn gen-ndarray-functions [public-to-gen-methods] + (for [operation (sort (public-by-name-and-param-count public-to-gen-methods))] + (let [[op-name op-values] operation + function-name (-> op-name + str + scala/decode-scala-symbol + clojure-case + symbol)] + `(~'defn ~function-name + ~@(remove nil? (gen-ndarray-function-arity op-name op-values)))))) + (def all-ndarray-functions - (for [operation (sort (public-by-name-and-param-count ndarray-public-to-gen))] - (let [[op-name op-values] operation - function-name (-> op-name - str - scala/decode-scala-symbol - clojure-case - symbol)] - `(~'defn ~function-name - ~@(remove nil? (gen-ndarray-function-arity op-name op-values)))))) + (gen-ndarray-functions ndarray-public-to-gen)) (def ndarray-gen-ns "(ns org.apache.clojure-mxnet.ndarray (:refer-clojure :exclude [* - + > >= < <= / cast concat flatten identity load max @@ -314,16 +385,191 @@ (defn generate-ndarray-file [] (println "Generating ndarray file") - (write-to-file all-ndarray-functions ndarray-gen-ns "src/org/apache/clojure_mxnet/gen/ndarray.clj")) + (write-to-file all-ndarray-functions + ndarray-gen-ns + "src/org/apache/clojure_mxnet/gen/ndarray.clj")) + +;;;;;;; SymbolAPI + +(defn symbol-api-coerce-param + [{:keys [name sym type optional?]}] + (let [coerced-param (case type + "Shape" `(when ~sym (~'mx-shape/->shape ~sym)) + "NDArray-or-Symbol[]" `(~'clojure.core/into-array ~sym) + "Map[String, String]" + `(when ~sym + (->> ~sym + (mapv (fn [[~'k ~'v]] [~'k (str ~'v)])) + (into {}) + ~'util/convert-map)) + sym) + nil-param-allowed? (#{"name" "attr"} name)] + (if (and optional? (not nil-param-allowed?)) + `(~'util/->option ~coerced-param) + coerced-param))) + +(defn gen-symbol-api-doc [fn-description params] + (let [param-descriptions (mapv (fn [{:keys [name description optional?]}] + (str "`" name "`: " + description + (when optional? " (optional)") + "\n")) + params)] + (str fn-description "\n\n" + (apply str param-descriptions)))) + +(defn gen-symbol-api-default-arity [op-name params] + (let [opt-params (filter :optional? params) + coerced-params (mapv symbol-api-coerce-param params) + default-args (array-map :keys (mapv :sym params) + :or (into {} + (mapv (fn [{:keys [sym]}] [sym nil]) + opt-params)) + :as 'opts)] + `([~default-args] + (~'util/coerce-return + (~(symbol (str "SymbolAPI/" op-name)) + ~@coerced-params))))) + +(defn gen-symbol-api-function [op-name] + (let [{:keys [fn-name fn-description args]} (gen-op-info op-name) + params (mapv (fn [{:keys [name type optional?] :as opts}] + (assoc opts + :sym (symbol name) + :optional? (or optional? + (= "NDArray-or-Symbol" type)))) + (conj args + {:name "name" + :type "String" + :optional? true + :description "Name of the symbol"} + {:name "attr" + :type "Map[String, String]" + :optional? true + :description "Attributes of the symbol"})) + doc (gen-symbol-api-doc fn-description params) + default-call (gen-symbol-api-default-arity op-name params)] + `(~'defn ~(symbol fn-name) + ~doc + ~@default-call))) + +(def all-symbol-api-functions + (mapv gen-symbol-api-function op-names)) + +(def symbol-api-gen-ns "(ns + ^{:doc \"Experimental\"} + org.apache.clojure-mxnet.symbol-api + (:refer-clojure :exclude [* - + > >= < <= / cast concat identity flatten load max + min repeat reverse set sort take to-array empty sin + get apply shuffle ref]) + (:require [org.apache.clojure-mxnet.util :as util] + [org.apache.clojure-mxnet.shape :as mx-shape]) + (:import (org.apache.mxnet SymbolAPI)))") + +(defn generate-symbol-api-file [] + (println "Generating symbol-api file") + (write-to-file all-symbol-api-functions symbol-api-gen-ns "src/org/apache/clojure_mxnet/gen/symbol_api.clj")) + +;;;;;;; NDArrayAPI + +(defn ndarray-api-coerce-param + [{:keys [sym type optional?]}] + (let [coerced-param (case type + "Shape" `(when ~sym (~'mx-shape/->shape ~sym)) + "NDArray-or-Symbol[]" `(~'clojure.core/into-array ~sym) + sym)] + (if optional? + `(~'util/->option ~coerced-param) + coerced-param))) + +(defn gen-ndarray-api-doc [fn-description params] + (let [param-descriptions (mapv (fn [{:keys [name description optional?]}] + (str "`" name "`: " + description + (when optional? " (optional)") + "\n")) + params)] + (str fn-description "\n\n" + (apply str param-descriptions)))) + +(defn gen-ndarray-api-default-arity [op-name params] + (let [opt-params (filter :optional? params) + coerced-params (mapv ndarray-api-coerce-param params) + default-args (array-map :keys (mapv :sym params) + :or (into {} + (mapv (fn [{:keys [sym]}] [sym nil]) + opt-params)) + :as 'opts)] + `([~default-args] + (~'util/coerce-return + (~(symbol (str "NDArrayAPI/" op-name)) + ~@coerced-params))))) + +(defn gen-ndarray-api-required-arity [fn-name req-params] + (let [req-args (->> req-params + (mapv (fn [{:keys [sym]}] [(keyword sym) sym])) + (into {}))] + `(~(mapv :sym req-params) + (~(symbol fn-name) ~req-args)))) + +(defn gen-ndarray-api-function [op-name] + (let [{:keys [fn-name fn-description args]} (gen-op-info op-name) + params (mapv (fn [{:keys [name] :as opts}] + (assoc opts :sym (symbol name))) + (conj args {:name "out" + :type "NDArray-or-Symbol" + :optional? true + :description "Output array."})) + doc (gen-ndarray-api-doc fn-description params) + opt-params (filter :optional? params) + req-params (remove :optional? params) + req-call (gen-ndarray-api-required-arity fn-name req-params) + default-call (gen-ndarray-api-default-arity op-name params)] + (if (= 1 (count req-params)) + `(~'defn ~(symbol fn-name) + ~doc + ~@default-call) + `(~'defn ~(symbol fn-name) + ~doc + ~req-call + ~default-call)))) + +(def all-ndarray-api-functions + (mapv gen-ndarray-api-function op-names)) + +(def ndarray-api-gen-ns "(ns + ^{:doc \"Experimental\"} + org.apache.clojure-mxnet.ndarray-api + (:refer-clojure :exclude [* - + > >= < <= / cast concat flatten identity load max + min repeat reverse set sort take to-array empty shuffle + ref]) + (:require [org.apache.clojure-mxnet.shape :as mx-shape] + [org.apache.clojure-mxnet.util :as util]) + (:import (org.apache.mxnet NDArrayAPI)))") + + +(defn generate-ndarray-api-file [] + (println "Generating ndarray-api file") + (write-to-file all-ndarray-api-functions + ndarray-api-gen-ns + "src/org/apache/clojure_mxnet/gen/ndarray_api.clj")) ;;; autogen the files (do (generate-ndarray-file) - (generate-symbol-file)) + (generate-ndarray-api-file) + (generate-symbol-file) + (generate-symbol-api-file)) (comment + (gen-op-info "ElementWiseSum") + + (gen-ndarray-api-function "Activation") + + (gen-symbol-api-function "Activation") + ;; This generates a file with the bulk of the nd-array functions (generate-ndarray-file) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/eval_metric.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/eval_metric.clj index 1946103a4a2d..f1fe2d18bd35 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/eval_metric.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/eval_metric.clj @@ -18,7 +18,7 @@ (ns org.apache.clojure-mxnet.eval-metric (:refer-clojure :exclude [get update]) (:require [org.apache.clojure-mxnet.util :as util]) - (:import (org.apache.mxnet Accuracy TopKAccuracy F1 Perplexity MAE MSE RMSE CustomMetric))) + (:import (org.apache.mxnet Accuracy TopKAccuracy F1 Perplexity MAE MSE RMSE CustomMetric CompositeEvalMetric))) (defn accuracy "Basic Accuracy Metric" @@ -74,11 +74,21 @@ [f-eval mname] `(new CustomMetric (util/scala-fn ~f-eval) ~mname)) +(defn comp-metric + "Create a metric instance composed out of several metrics" + [metrics] + (let [cm (CompositeEvalMetric.)] + (doseq [m metrics] (.add cm m)) + cm)) + (defn get - "Get the values of the metric in a vector form (name and value)" + "Get the values of the metric in as a map of {name value} pairs" [metric] - (let [[[mname] [mvalue]] (util/tuple->vec (.get metric))] - [mname mvalue])) + (let [m (apply zipmap (-> (.get metric) + util/tuple->vec))] + (if-not (instance? CompositeEvalMetric metric) + (first m) + m))) (defn reset "clear the internal statistics to an initial state" diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj index e2e87ed47e2f..f81a35803171 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj @@ -16,12 +16,14 @@ ;; (ns org.apache.clojure-mxnet.image + "Image API of Clojure package." (:require [t6.from-scala.core :refer [$ $$] :as $] [org.apache.clojure-mxnet.dtype :as dtype] [org.apache.clojure-mxnet.ndarray :as ndarray] [org.apache.clojure-mxnet.util :as util] [clojure.spec.alpha :as s]) (:import (org.apache.mxnet Image NDArray) + (java.awt.image BufferedImage) (java.io InputStream))) ;; Flags for conversion of images @@ -37,7 +39,18 @@ (s/keys :opt-un [::color-flag ::to-rgb ::output])) (defn decode-image - "Decodes an image from an input stream" + "Decodes an image from an input stream with OpenCV + `input-stream`: `InputStream` - Contains the binary encoded image + `color-flag`: 0 or 1 - Convert decoded image to grayscale (0) or color (1) + `to-rgb`: boolean - Whether to convert decoded image to mxnet's default RGB + format (instead of opencv's default BGR) + `output`: nil or `NDArray` + returns: `NDArray` with dtype uint8 + + Ex: + (decode-image input-stream) + (decode-image input-stream {:color-flag 1}) + (decode-image input-stream {:color-flag 0 :output nd})" ([input-stream {:keys [color-flag to-rgb output] :or {color-flag COLOR to-rgb true output nil} :as opts}] @@ -54,7 +67,19 @@ (s/or :none nil? :some ::to-rgb)) (defn read-image - "Reads an image file and returns an ndarray" + "Reads an image file and returns an ndarray with OpenCV. It returns image in + RGB by default instead of OpenCV's default BGR. + `filename`: string - Name of the image file to be loaded + `color-flag`: 0 or 1 - Convert decoded image to grayscale (0) or color (1) + `to-rgb`: boolean - Whether to convert decoded image to mxnet's default RGB + format (instead of opencv's default BGR) + `output`: nil or `NDArray` + returns: `NDArray` with dtype uint8 + + Ex: + (read-image \"cat.jpg\") + (read-image \"cat.jpg\" {:color-flag 0}) + (read-image \"cat.jpg\" {:color-flag 1 :output nd})" ([filename {:keys [color-flag to-rgb output] :or {color-flag nil to-rgb nil output nil} :as opts}] @@ -74,7 +99,17 @@ (s/def ::optional-int (s/or :none nil? :some int?)) (defn resize-image - "Resizes the image array to (width, height)" + "Resizes the image array to (width, height) + `input`: `NDArray` - source image in NDArray + `w`: int - Width of resized image + `h`: int - Height of resized image + `interpolation`: Interpolation method. Default is INTER_LINEAR + `ouput`: nil or `NDArray` + returns: `NDArray` + + Ex: + (resize-image nd-img 300 300) + (resize-image nd-img 28 28 {:output nd})" ([input w h {:keys [interpolation output] :or {interpolation nil output nil} :as opts}] @@ -88,7 +123,21 @@ (resize-image input w h {}))) (defn apply-border - "Pad image border" + "Pad image border with OpenCV. + `input`: `NDArray` - source image in NDArray + `top`: int - Top margin + `bottom`: int - Bottom margin + `left`: int - Left margin + `right`: int - Right margin + `fill-type`: nil or Filling type - Default BORDER_CONSTANT + `value`: nil or double - Deprecated, use `values` instead + `values`: Fill with value(RGB or gray), up to 4 channels + `output`: nil or `NDArray` + returns: `NDArray` + + Ex: + (apply-border img-nd 1 1 1 1) + (apply-border img-nd 3 3 0 0)" ([input top bottom left right {:keys [fill-type value values output] :or {fill-type nil value nil values nil output nil} @@ -109,7 +158,17 @@ (apply-border input top bottom left right {}))) (defn fixed-crop - "Return a fixed crop of the image" + "Return a fixed crop of the image. + `input`: `NDArray` - Source image in NDArray + `x0`: int - Starting x point + `y0`: int - Starting y point + `w`: int - Width of the image + `h`: int - Height of the image + returns: cropped `NDArray` + + Ex: + (fixed-crop nd-img 0 0 28 28) + (fixed-crop nd-img 10 0 100 300)" [input x0 y0 w h] (util/validate! ::ndarray input "Invalid input array") (util/validate! ::int x0 "Invalid starting x coordinate") @@ -119,7 +178,9 @@ (Image/fixedCrop input x0 y0 w h)) (defn rgb-array? - "Returns whether the ndarray is in the RGB format" + "Returns whether the ndarray is in the RGB format + `input`: `NDArray` - Source image in NDArray + returns: boolean" [input] (util/validate! ::ndarray input "Invalid input array") (let [shape (ndarray/shape-vec input)] @@ -133,7 +194,74 @@ (s/and ::ndarray ::all-bytes ::rgb-array)) (defn to-image - "Convert a NDArray image in RGB format to a real image" + "Convert a NDArray image in RGB format to a real image. + `input`: `NDArray` - Source image in NDArray + returns: `BufferedImage`" [input] (util/validate! ::to-image-ndarray input "Invalid input array") (Image/toImage input)) + +(s/def ::buffered-image #(instance? BufferedImage %)) +(s/def ::x-min number?) +(s/def ::x-max number?) +(s/def ::y-min number?) +(s/def ::y-max number?) +(s/def ::coordinate (s/keys :req-un [::x-min ::x-max ::y-min ::y-max])) +(s/def ::coordinates (s/coll-of ::coordinate)) +(s/def ::names (s/nilable (s/coll-of string?))) +(s/def ::stroke (s/and integer? pos?)) +(s/def ::font-size-mult (s/and float? pos?)) +(s/def ::transparency (s/and float? #(<= 0.0 % 1.0))) +(s/def ::coordinates-names + (fn [[coordinates names]] (= (count coordinates) (count names)))) + +(defn- convert-coordinate + "Convert bounding box coordinate to Scala correct types." + [{:keys [x-min x-max y-min y-max]}] + {:xmin (int x-min) + :xmax (int x-max) + :ymin (int y-min) + :ymax (int y-max)}) + +(defn draw-bounding-box! + "Draw bounding boxes on `buffered-image` and Mutate the input image. + `buffered-image`: BufferedImage + `coordinates`: collection of {:xmin int :xmax int :ymin int :ymax int} + `font-size-mult`: positive float - Font size multiplier + `names`: collection of strings - List of names for the bounding boxes + `stroke`: positive integer - thickness of the bounding box + `transparency`: float in (0.0, 1.0) - Transparency of the bounding box + returns: Modified `buffered-image` + Ex: + (draw-bounding-box! img [{:x-min 0 :x-max 100 :y-min 0 :y-max 100}]) + (draw-bounding-box! [{:x-min 190 :x-max 850 :y-min 50 :y-max 450} + {:x-min 200 :x-max 350 :y-min 440 :y-max 530}] + {:stroke 2 + :names [\"pug\" \"cookie\"] + :transparency 0.8 + :font-size-mult 2.0})" + ([buffered-image coordinates] + (draw-bounding-box! buffered-image coordinates {})) + ([buffered-image coordinates + {:keys [names stroke font-size-mult transparency] + :or {stroke 3 font-size-mult 1.0 transparency 1.0} + :as opts}] + (util/validate! ::buffered-image buffered-image "Invalid input image") + (util/validate! ::coordinates coordinates "Invalid input coordinates") + (util/validate! ::names names "Invalid input names") + (util/validate! ::stroke stroke "Invalid input stroke") + (util/validate! ::font-size-mult font-size-mult "Invalid input font-size-mult") + (util/validate! ::transparency transparency "Invalid input transparency") + (when (pos? (count names)) + (util/validate! ::coordinates-names [coordinates names] "Invalid number of names")) + (Image/drawBoundingBox + buffered-image + (->> coordinates + (map convert-coordinate) + (map util/convert-map) + (into-array)) + (util/->option (into-array names)) + (util/->option (int stroke)) + (util/->option (float font-size-mult)) + (util/->option (float transparency))) + buffered-image)) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/module.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/module.clj index aa5ce39f7a80..09f17e5d81f4 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/module.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/module.clj @@ -16,6 +16,7 @@ ;; (ns org.apache.clojure-mxnet.module + "Module API for Clojure package." (:refer-clojure :exclude [update symbol]) (:require [org.apache.clojure-mxnet.callback :as callback] [org.apache.clojure-mxnet.context :as context] @@ -31,18 +32,29 @@ (:import (org.apache.mxnet.module Module FitParams BaseModule) (org.apache.mxnet.io MXDataIter NDArrayIter) (org.apache.mxnet Initializer Optimizer NDArray DataBatch - Context EvalMetric Monitor Callback$Speedometer DataDesc))) + Context EvalMetric Monitor Callback$Speedometer + DataDesc))) (defn module - "Module is a basic module that wrap a symbol. - sym : Symbol definition. - map of options - :data-names - Input data names. - :label-names - Input label names - :contexts - Default is cpu(). - :workload-list - Default nil, indicating uniform workload. - :fixed-param-names Default nil, indicating no network parameters are fixed." - ([sym {:keys [data-names label-names contexts workload-list fixed-param-names] :as opts + "Module is a basic module that wrap a `symbol`. + `sym`: Symbol definition. + `opts-map` { + `data-names`: vector of strings - Default is [\"data\"] + Input data names + `label-names`: vector of strings - Default is [\"softmax_label\"] + Input label names + `contexts`: Context - Default is `context/cpu`. + `workload-list`: Default nil + Indicating uniform workload. + `fixed-param-names`: Default nil + Indicating no network parameters are fixed. + } + Ex: + (module sym) + (module sym {:data-names [\"data\"] + :label-names [\"linear_regression_label\"]}" + ([sym {:keys [data-names label-names contexts + workload-list fixed-param-names] :as opts :or {data-names ["data"] label-names ["softmax_label"] contexts [(context/default-context)]}}] @@ -80,31 +92,41 @@ (s/def ::force-rebind boolean?) (s/def ::shared-module #(instance? Module)) (s/def ::grad-req string?) -(s/def ::bind-opts (s/keys :req-un [::data-shapes] :opt-un [::label-shapes ::for-training ::inputs-need-grad - ::force-rebind ::shared-module ::grad-req])) +(s/def ::bind-opts + (s/keys :req-un [::data-shapes] + :opt-un [::label-shapes ::for-training ::inputs-need-grad + ::force-rebind ::shared-module ::grad-req])) (defn bind "Bind the symbols to construct executors. This is necessary before one can perform computation with the module. - mod : module - map of opts: - :data-shapes Typically is (provide-data-desc data-iter). Data shape must be in the form of io/data-desc with is a map of :name :shape :dtype and :layout - :label-shapes Typically is (provide-label-desc data-iter). map of :name :shape :dtype and :layout - :for-training Default is `true`. Whether the executors should be bind for training. - :inputs-need-grad Default is `false`. - Whether the gradients to the input data need to be computed. - Typically this is not needed. - But this might be needed when implementing composition of modules. - :force-rebind Default is `false`. - This function does nothing if the executors are already binded. - But with this `true`, the executors will be forced to rebind. - :shared-module Default is nil. This is used in bucketing. - When not `None`, the shared module essentially corresponds to - a different bucket -- a module with different symbol - but with the same sets of parameters - (e.g. unrolled RNNs with different lengths). " - [mod {:keys [data-shapes label-shapes for-training inputs-need-grad force-rebind - shared-module grad-req] :as opts + `mod`: module + `opts-map` { + `data-shapes`: map of `:name`, `:shape`, `:dtype`, and `:layout` + Typically is `(provide-data-desc data-iter)`.Data shape must be in the + form of `io/data-desc` + `label-shapes`: map of `:name` `:shape` `:dtype` and `:layout` + Typically is `(provide-label-desc data-iter)`. + `for-training`: boolean - Default is `true` + Whether the executors should be bind for training. + `inputs-need-grad`: boolean - Default is `false`. + Whether the gradients to the input data need to be computed. + Typically this is not needed. But this might be needed when + implementing composition of modules. + `force-rebind`: boolean - Default is `false`. + This function does nothing if the executors are already binded. But + with this `true`, the executors will be forced to rebind. + `shared-module`: Default is nil. + This is used in bucketing. When not `nil`, the shared module + essentially corresponds to a different bucket -- a module with + different symbol but with the same sets of parameters (e.g. unrolled + RNNs with different lengths). + } + Ex: + (bind {:data-shapes (mx-io/provide-data train-iter) + :label-shapes (mx-io/provide-label test-iter)})) " + [mod {:keys [data-shapes label-shapes for-training inputs-need-grad + force-rebind shared-module grad-req] :as opts :or {for-training true inputs-need-grad false force-rebind false @@ -129,24 +151,36 @@ (s/def ::aux-params map?) (s/def ::force-init boolean?) (s/def ::allow-extra boolean?) -(s/def ::init-params-opts (s/keys :opt-un [::initializer ::arg-params ::aux-params - ::force-init ::allow-extra])) +(s/def ::init-params-opts + (s/keys :opt-un [::initializer ::arg-params ::aux-params + ::force-init ::allow-extra])) (defn init-params - " Initialize the parameters and auxiliary states. - options map - :initializer - Called to initialize parameters if needed. - :arg-params - If not nil, should be a map of existing arg-params. - Initialization will be copied from that. - :auxParams - If not nil, should be a map of existing aux-params. - Initialization will be copied from that. - :allow-missing - If true, params could contain missing values, - and the initializer will be called to fill those missing params. - :force-init - If true, will force re-initialize even if already initialized. - :allow-extra - Whether allow extra parameters that are not needed by symbol. - If this is True, no error will be thrown when argParams or auxParams - contain extra parameters that is not needed by the executor." - ([mod {:keys [initializer arg-params aux-params allow-missing force-init allow-extra] :as opts + "Initialize the parameters and auxiliary states. + `opts-map` { + `initializer`: Initializer - Default is `uniform` + Called to initialize parameters if needed. + `arg-params`: map + If not nil, should be a map of existing arg-params. Initialization + will be copied from that. + `aux-params`: map + If not nil, should be a map of existing aux-params. Initialization + will be copied from that. + `allow-missing`: boolean - Default is `false` + If true, params could contain missing values, and the initializer will + be called to fill those missing params. + `force-init` boolean - Default is `false` + If true, will force re-initialize even if already initialized. + `allow-extra`: boolean - Default is `false` + Whether allow extra parameters that are not needed by symbol. + If this is `true`, no error will be thrown when `arg-params` or + `aux-params` contain extra parameters that is not needed by the + executor. + Ex: + (init-params {:initializer (initializer/xavier)}) + (init-params {:force-init true :allow-extra true})" + ([mod {:keys [initializer arg-params aux-params allow-missing force-init + allow-extra] :as opts :or {initializer (initializer/uniform 0.01) allow-missing false force-init false @@ -167,17 +201,23 @@ (s/def ::kvstore string?) (s/def ::reset-optimizer boolean?) (s/def ::force-init boolean?) -(s/def ::init-optimizer-opts (s/keys :opt-un [::optimizer ::kvstore ::reset-optimizer ::force-init])) +(s/def ::init-optimizer-opts + (s/keys :opt-un [::optimizer ::kvstore ::reset-optimizer ::force-init])) (defn init-optimizer - " Install and initialize optimizers. - - mod Module - - options map of - - kvstore - - reset-optimizer Default `True`, indicating whether we should set - `rescaleGrad` & `idx2name` for optimizer according to executorGroup - - force-init Default `False`, indicating whether we should force - re-initializing the optimizer in the case an optimizer is already installed." + "Install and initialize optimizers. + `mod`: Module + `opts-map` { + `kvstore`: string - Default is \"local\" + `optimizer`: Optimizer - Default is `sgd` + `reset-optimizer`: boolean - Default is `true` + Indicating whether we should set `rescaleGrad` & `idx2name` for + optimizer according to executorGroup. + `force-init`: boolean - Default is `false` + Indicating whether we should force re-initializing the optimizer + in the case an optimizer is already installed. + Ex: + (init-optimizer {:optimizer (optimizer/sgd {:learning-rate 0.1})})" ([mod {:keys [kvstore optimizer reset-optimizer force-init] :as opts :or {kvstore "local" optimizer (optimizer/sgd) @@ -191,8 +231,10 @@ (defn forward "Forward computation. - data-batch - input data of form io/data-batch either map or DataBatch - is-train - Default is nil, which means `is_train` takes the value of `for_training`." + `data-batch`: Either map or DataBatch + Input data of form `io/data-batch`. + `is-train`: Default is nil + Which means `is_train` takes the value of `for_training`." ([mod data-batch is-train] (util/validate! ::mx-io/data-batch data-batch "Invalid data batch") (doto mod @@ -209,9 +251,9 @@ (defn backward "Backward computation. - out-grads - Gradient on the outputs to be propagated back. - This parameter is only needed when bind is called - on outputs that are not a loss function." + `out-grads`: collection of NDArrays + Gradient on the outputs to be propagated back. This parameter is only + needed when bind is called on outputs that are not a loss function." ([mod out-grads] (util/validate! ::out-grads out-grads "Invalid out-grads") (doto mod @@ -227,50 +269,48 @@ (.forwardBackward data-batch))) (defn outputs - " Get outputs of the previous forward computation. - In the case when data-parallelism is used, - the outputs will be collected from multiple devices. - The results will look like `[[out1_dev1, out1_dev2], [out2_dev1, out2_dev2]]`, - those `NDArray` might live on different devices." + "Get outputs of the previous forward computation. + In the case when data-parallelism is used, the outputs will be collected from + multiple devices. The results will look like + `[[out1_dev1, out1_dev2], [out2_dev1, out2_dev2]]`. + Those `NDArray`s might live on different devices." [mod] (->> (.getOutputs mod) (util/scala-vector->vec) (mapv util/scala-vector->vec))) (defn update - "Update parameters according to the installed optimizer and the gradients computed - in the previous forward-backward batch." + "Update parameters according to the installed optimizer and the gradients + computed in the previous forward-backward batch." [mod] (doto mod (.update))) (defn outputs-merged - " Get outputs of the previous forward computation. - return In the case when data-parallelism is used, - the outputs will be merged from multiple devices, - as they look like from a single executor. - The results will look like `[out1, out2]`" + "Get outputs of the previous forward computation. + In the case when data-parallelism is used, the outputs will be merged from + multiple devices, as they look like from a single executor. + The results will look like `[out1, out2]`." [mod] (->> (.getOutputsMerged mod) (util/scala-vector->vec))) (defn input-grads - " Get the gradients to the inputs, computed in the previous backward computation. - In the case when data-parallelism is used, - the outputs will be collected from multiple devices. - The results will look like `[[grad1_dev1, grad1_dev2], [grad2_dev1, grad2_dev2]]` - those `NDArray` might live on different devices." + "Get the gradients to the inputs, computed in the previous backward computation. + In the case when data-parallelism is used, the outputs will be collected from + multiple devices. The results will look like + `[[grad1_dev1, grad1_dev2], [grad2_dev1, grad2_dev2]]`. + Those `NDArray`s might live on different devices." [mod] (->> (.getInputGrads mod) (util/scala-vector->vec) (mapv util/scala-vector->vec))) (defn input-grads-merged - " Get the gradients to the inputs, computed in the previous backward computation. - return In the case when data-parallelism is used, - the outputs will be merged from multiple devices, - as they look like from a single executor. - The results will look like `[grad1, grad2]`" + "Get the gradients to the inputs, computed in the previous backward computation. + In the case when data-parallelism is used, the outputs will be merged from + multiple devices, as they look like from a single executor. + The results will look like `[grad1, grad2]`." [mod] (->> (.getInputGradsMerged mod) (util/scala-vector->vec))) @@ -278,16 +318,25 @@ (s/def ::prefix string?) (s/def ::epoch int?) (s/def ::save-opt-states boolean?) -(s/def ::save-checkpoint-opts (s/keys :req-un [::prefix ::epoch] :opt-un [::save-opt-states ::save-checkpoint])) +(s/def ::save-checkpoint-opts + (s/keys :req-un [::prefix ::epoch] + :opt-un [::save-opt-states ::save-checkpoint])) (defn save-checkpoint - " Save current progress to checkpoint. - Use mx.callback.module_checkpoint as epoch_end_callback to save during training. - - mod Module - - opt-map with - :prefix The file prefix to checkpoint to - :epoch The current epoch number - :save-opt-states Whether to save optimizer states for continue training " + "Save current progress to checkpoint. + Use mx.callback.module_checkpoint as epoch_end_callback to save during + training. + `mod`: Module + `opts-map` { + `prefix`: string + The file prefix to checkpoint to + `epoch`: int + The current epoch number + `save-opt-states`: boolean - Default is `false` + Whether to save optimizer states for continue training + } + Ex: + (save-checkpoint {:prefix \"saved_model\" :epoch 0 :save-opt-states true})" ([mod {:keys [prefix epoch save-opt-states] :as opts :or {save-opt-states false}}] (util/validate! ::save-checkpoint-opts opts "Invalid save checkpoint opts") @@ -303,24 +352,34 @@ (s/def ::contexts (s/coll-of ::context :kind vector?)) (s/def ::workload-list (s/coll-of number? :kind vector?)) (s/def ::fixed-params-names (s/coll-of string? :kind vector?)) -(s/def ::load-checkpoint-opts (s/keys :req-un [::prefix ::epoch] - :opt-un [::load-optimizer-states ::data-names ::label-names - ::contexts ::workload-list ::fixed-param-names])) +(s/def ::load-checkpoint-opts + (s/keys :req-un [::prefix ::epoch] + :opt-un [::load-optimizer-states ::data-names ::label-names + ::contexts ::workload-list ::fixed-param-names])) (defn load-checkpoint "Create a model from previously saved checkpoint. - - opts map of - - prefix Path prefix of saved model files. You should have prefix-symbol.json, - prefix-xxxx.params, and optionally prefix-xxxx.states, - where xxxx is the epoch number. - - epoch Epoch to load. - - load-optimizer-states Whether to load optimizer states. - Checkpoint needs to have been made with save-optimizer-states=True - - dataNames Input data names. - - labelNames Input label names - - contexts Default is cpu(). - - workload-list Default nil, indicating uniform workload. - - fixed-param-names Default nil, indicating no network parameters are fixed." + `opts-map` { + `prefix`: string + Path prefix of saved model files. You should have prefix-symbol.json, + prefix-xxxx.params, and optionally prefix-xxxx.states, where xxxx is + the epoch number. + `epoch`: int + Epoch to load. + `load-optimizer-states`: boolean - Default is false + Whether to load optimizer states. Checkpoint needs to have been made + with `save-optimizer-states` = `true`. + `data-names`: vector of strings - Default is [\"data\"] + Input data names. + `label-names`: vector of strings - Default is [\"softmax_label\"] + Input label names. + `contexts`: Context - Default is `context/cpu` + `workload-list`: Default nil + Indicating uniform workload. + `fixed-param-names`: Default nil + Indicating no network parameters are fixed. + Ex: + (load-checkpoint {:prefix \"my-model\" :epoch 1 :load-optimizer-states true}" ([{:keys [prefix epoch load-optimizer-states data-names label-names contexts workload-list fixed-param-names] :as opts :or {load-optimizer-states false @@ -358,10 +417,10 @@ (util/scala-map->map (.auxParams mod))) (defn reshape - " Reshapes the module for new input shapes. - - mod module - - data-shapes Typically is `(provide-data data-iter) - - param label-shapes Typically is `(provide-label data-tier)`. " + "Reshapes the module for new input shapes. + `mod`: Module + `data-shapes`: Typically is `(provide-data data-iter)` + `label-shapes`: Typically is `(provide-label data-tier)`" ([mod data-shapes label-shapes] (util/validate! ::data-shapes data-shapes "Invalid data-shapes") (util/validate! (s/nilable ::label-shapes) label-shapes "Invalid label-shapes") @@ -376,28 +435,35 @@ ([mod data-shapes] (reshape mod data-shapes nil))) -(s/def ::set-param-opts (s/keys :opt-un [::arg-params ::aux-params ::allow-missing ::force-init ::allow-extra])) +(s/def ::set-param-opts + (s/keys :opt-un [::arg-params ::aux-params ::allow-missing + ::force-init ::allow-extra])) (defn get-params [mod] (.getParams mod)) (defn set-params - " Assign parameter and aux state values. - - mod module - - arg-params : map - map of name to value (`NDArray`) mapping. - - aux-params : map - map of name to value (`NDArray`) mapping. - - allow-missing : bool - If true, params could contain missing values, and the initializer will be - called to fill those missing params. - - force-init : bool - If true, will force re-initialize even if already initialized. - - allow-extra : bool - Whether allow extra parameters that are not needed by symbol. - If this is True, no error will be thrown when arg-params or aux-params - contain extra parameters that is not needed by the executor." - [mod {:keys [arg-params aux-params allow-missing force-init allow-extra] :as opts + "Assign parameters and aux state values. + `mod`: Module + `opts-map` { + `arg-params`: map - map of name to value (`NDArray`) mapping. + `aux-params`: map - map of name to value (`NDArray`) mapping. + `allow-missing`: boolean + If true, params could contain missing values, and the initializer will + be called to fill those missing params. + `force-init`: boolean - Default is `false` + If true, will force re-initialize even if already initialized. + `allow-extra`: boolean - Default is `false` + Whether allow extra parameters that are not needed by symbol. If this + is `true`, no error will be thrown when arg-params or aux-params + contain extra parameters that is not needed by the executor. + } + Ex: + (set-params mod + {:arg-params {\"fc_0_weight\" (ndarray/array [0.15 0.2 0.25 0.3] [2 2]) + :allow-missing true})" + [mod {:keys [arg-params aux-params allow-missing force-init + allow-extra] :as opts :or {allow-missing false force-init true allow-extra false}}] (util/validate! ::set-param-opts opts "Invalid set-params") (doto mod @@ -409,33 +475,32 @@ allow-extra))) (defn install-monitor - "Install monitor on all executors" + "Install monitor on all executors." [mod monitor] (doto mod (.installMonitor monitor))) (defn borrow-optimizer - "Borrow optimizer from a shared module. Used in bucketing, where exactly the same - optimizer (esp. kvstore) is used. - - mod module - - shared-module" + "Borrow optimizer from a shared module. Used in bucketing, where exactly the + same optimizer (esp. kvstore) is used. + `mod`: Module + `shared-module`" [mod shared-module] (doto mod (.borrowOptimizer shared-module))) (defn save-optimizer-states - "Save optimizer (updater) state to file - - mod module - - fname Path to output states file." + "Save optimizer (updater) state to file. + `mod`: Module + `fname`: string - Path to output states file." [mod fname] (doto mod (.saveOptimizerStates mod fname))) (defn load-optimizer-states - "Load optimizer (updater) state from file - - mod module - - fname Path to input states file. - " + "Load optimizer (updater) state from file. + `mod`: Module + `fname`: string - Path to input states file." [mod fname] (doto mod (.loadOptimzerStates fname))) @@ -444,10 +509,13 @@ (s/def ::labels (s/coll-of ::ndarray :kind vector?)) (defn update-metric - "Evaluate and accumulate evaluation metric on outputs of the last forward computation. - - mod module - - eval-metric - - labels" + "Evaluate and accumulate evaluation metric on outputs of the last forward + computation. + `mod`: module + `eval-metric`: EvalMetric + `labels`: collection of NDArrays + Ex: + (update-metric mod (eval-metric/mse) labels)" [mod eval-metric labels] (util/validate! ::eval-metric eval-metric "Invalid eval metric") (util/validate! ::labels labels "Invalid labels") @@ -458,18 +526,48 @@ (s/def ::validation-metric ::eval-metric) (s/def ::monitor #(instance? Monitor %)) (s/def ::batch-end-callback #(instance? Callback$Speedometer %)) -(s/def ::fit-params-opts (s/keys :opt-un [::eval-metric ::kvstore ::optimizer ::initializer - ::arg-params ::aux-params ::allow-missing ::force-rebind - ::force-init ::begin-epoch ::validation-metric ::monitor - ::batch-end-callback])) +(s/def ::fit-params-opts + (s/keys :opt-un [::eval-metric ::kvstore ::optimizer ::initializer + ::arg-params ::aux-params ::allow-missing ::force-rebind + ::force-init ::begin-epoch ::validation-metric ::monitor + ::batch-end-callback])) ;; callbacks are not supported for now (defn fit-params - "Fit Params" + "Initialize FitParams with provided parameters. + `eval-metric`: EvalMetric - Default is `accuracy` + `kvstore`: String - Default is \"local\" + `optimizer`: Optimizer - Default is `sgd` + `initializer`: Initializer - Default is `uniform` + Called to initialize parameters if needed. + `arg-params`: map + If not nil, should be a map of existing `arg-params`. Initialization + will be copied from that. + `aux-params`: map - + If not nil, should be a map of existing `aux-params`. Initialization + will be copied from that. + `allow-missing`: boolean - Default is `false` + If `true`, params could contain missing values, and the initializer will + be called to fill those missing params. + `force-rebind`: boolean - Default is `false` + This function does nothing if the executors are already binded. But with + this `true`, the executors will be forced to rebind. + `force-init`: boolean - Default is `false` + If `true`, will force re-initialize even if already initialized. + `begin-epoch`: int - Default is 0 + `validation-metric`: EvalMetric + `monitor`: Monitor + Ex: + (fit-params {:force-init true :force-rebind true :allow-missing true}) + (fit-params + {:batch-end-callback (callback/speedometer batch-size 100) + :initializer (initializer/xavier) + :optimizer (optimizer/sgd {:learning-rate 0.01}) + :eval-metric (eval-metric/mse)})" ([{:keys [eval-metric kvstore optimizer initializer arg-params aux-params - allow-missing force-rebind force-init begin-epoch validation-metric monitor - batch-end-callback] :as opts + allow-missing force-rebind force-init begin-epoch + validation-metric monitor batch-end-callback] :as opts :or {eval-metric (eval-metric/accuracy) kvstore "local" optimizer (optimizer/sgd) @@ -500,25 +598,36 @@ (s/def ::ndarray-iter #(instance? NDArrayIter %)) (s/def ::train-data (s/or :mx-iter ::mx-data-iter :ndarry-iter ::ndarray-iter)) (s/def ::eval-data ::train-data) -(s/def ::num-epoch int?) +(s/def ::num-epoch (s/and int? pos?)) (s/def ::fit-params #(instance? FitParams %)) -(s/def ::fit-options (s/keys :req-un [::train-data] :opt-un [::eval-data ::num-epoch ::fit-params])) +(s/def ::fit-options + (s/keys :req-un [::train-data] + :opt-un [::eval-data ::num-epoch ::fit-params])) ;;; High Level API (defn score - " Run prediction on `eval-data` and evaluate the performance according to `eval-metric`. - - mod module - - option map with - :eval-data : DataIter - :eval-metric : EvalMetric - :num-batch Number of batches to run. Default is `Integer.MAX_VALUE`, - indicating run until the `DataIter` finishes. - :batch-end-callback -not supported yet - :reset Default `True`, - indicating whether we should reset `eval-data` before starting evaluating. - :epoch Default 0. For compatibility, this will be passed to callbacks (if any). - During training, this will correspond to the training epoch number." + "Run prediction on `eval-data` and evaluate the performance according to + `eval-metric`. + `mod`: module + `opts-map` { + `eval-data`: DataIter + `eval-metric`: EvalMetric + `num-batch`: int - Default is `Integer.MAX_VALUE` + Number of batches to run. Indicating run until the `DataIter` + finishes. + `batch-end-callback`: not supported yet. + `reset`: boolean - Default is `true`, + Indicating whether we should reset `eval-data` before starting + evaluating. + `epoch`: int - Default is 0 + For compatibility, this will be passed to callbacks (if any). During + training, this will correspond to the training epoch number. + } + Ex: + (score mod {:eval-data data-iter :eval-metric (eval-metric/accuracy)}) + (score mod {:eval-data data-iter + :eval-metric (eval-metric/mse) :num-batch 10})" [mod {:keys [eval-data eval-metric num-batch reset epoch] :as opts :or {num-batch Integer/MAX_VALUE reset true @@ -537,15 +646,30 @@ (defn fit "Train the module parameters. - - mod module - - train-data (data-iterator) - - eval-data (data-iterator)If not nil, will be used as validation set and evaluate - the performance after each epoch. - - num-epoch Number of epochs to run training. - - f-params Extra parameters for training (See fit-params)." + `mod`: Module + `opts-map` { + `train-data`: DataIter + `eval-data`: DataIter + If not nil, will be used as validation set and evaluate the + performance after each epoch. + `num-epoch`: int + Number of epochs to run training. + `fit-params`: FitParams + Extra parameters for training (see fit-params). + } + Ex: + (fit {:train-data train-iter :eval-data test-iter :num-epoch 100) + (fit {:train-data train-iter + :eval-data test-iter + :num-epoch 5 + :fit-params + (fit-params {:batch-end-callback (callback/speedometer 128 100) + :initializer (initializer/xavier) + :optimizer (optimizer/sgd {:learning-rate 0.01}) + :eval-metric (eval-metric/mse)}))" [mod {:keys [train-data eval-data num-epoch fit-params] :as opts - `:or {num-epoch 1 - fit-params (new FitParams)}}] + :or {num-epoch 1 + fit-params (new FitParams)}}] (util/validate! ::fit-options opts "Invalid options for fit") (doto mod (.fit @@ -557,12 +681,13 @@ (s/def ::eval-data ::train-data) (s/def ::num-batch integer?) (s/def ::reset boolean?) -(s/def ::predict-opts (s/keys :req-un [::eval-data] :opt-un [::num-batch ::reset])) +(s/def ::predict-opts + (s/keys :req-un [::eval-data] :opt-un [::num-batch ::reset])) (defn predict-batch - "Run the predication on a data batch - - mod module - - data-batch data-batch" + "Run the predication on a data batch. + `mod`: Module + `data-batch`: data-batch" [mod data-batch] (util/validate! ::mx-io/data-batch data-batch "Invalid data batch") (util/coerce-return (.predict mod (if (map? data-batch) @@ -571,41 +696,60 @@ (defn predict "Run prediction and collect the outputs. - - mod module - - option map with - - :eval-data - - :num-batch Default is -1, indicating running all the batches in the data iterator. - - :reset Default is `True`, indicating whether we should reset the data iter before start - doing prediction. - The return value will be a vector of NDArrays `[out1, out2, out3]`. - Where each element is concatenation of the outputs for all the mini-batches." + `mod`: Module + `opts-map` { + `eval-data`: DataIter + `num-batch` int - Default is `-1` + Indicating running all the batches in the data iterator. + `reset`: boolean - Default is `true` + Indicating whether we should reset the data iter before start doing + prediction. + } + returns: vector of NDArrays `[out1, out2, out3]` where each element is the + concatenation of the outputs for all the mini-batches. + Ex: + (predict mod {:eval-data test-iter}) + (predict mod {:eval-data test-iter :num-batch 10 :reset false})" [mod {:keys [eval-data num-batch reset] :as opts :or {num-batch -1 reset true}}] (util/validate! ::predict-opts opts "Invalid opts for predict") (util/scala-vector->vec (.predict mod eval-data (int num-batch) reset))) -(s/def ::predict-every-batch-opts (s/keys :req-un [::eval-data] :opt-un [::num-batch ::reset])) +(s/def ::predict-every-batch-opts + (s/keys :req-un [::eval-data] :opt-un [::num-batch ::reset])) (defn predict-every-batch - " Run prediction and collect the outputs. - - module - - option map with - :eval-data - :num-batch Default is -1, indicating running all the batches in the data iterator. - :reset Default is `True`, indicating whether we should reset the data iter before start - doing prediction. - The return value will be a nested list like - [[out1_batch1, out2_batch1, ...], [out1_batch2, out2_batch2, ...]]` - This mode is useful because in some cases (e.g. bucketing), - the module does not necessarily produce the same number of outputs." + "Run prediction and collect the outputs. + `mod`: Module + `opts-map` { + `eval-data`: DataIter + `num-batch` int - Default is `-1` + Indicating running all the batches in the data iterator. + `reset` boolean - Default is `true` + Indicating whether we should reset the data iter before start doing + prediction. + } + returns: nested list like this + `[[out1_batch1, out2_batch1, ...], [out1_batch2, out2_batch2, ...]]` + + Note: This mode is useful because in some cases (e.g. bucketing), the module + does not necessarily produce the same number of outputs. + Ex: + (predict-every-batch mod {:eval-data test-iter})" [mod {:keys [eval-data num-batch reset] :as opts :or {num-batch -1 reset true}}] - (util/validate! ::predict-every-batch-opts opts "Invalid opts for predict-every-batch") - (mapv util/scala-vector->vec (util/scala-vector->vec (.predictEveryBatch mod eval-data (int num-batch) reset)))) - -(s/def ::score-opts (s/keys :req-un [::eval-data ::eval-metric] :opt-un [::num-batch ::reset ::epoch])) + (util/validate! ::predict-every-batch-opts + opts + "Invalid opts for predict-every-batch") + (mapv util/scala-vector->vec + (util/scala-vector->vec + (.predictEveryBatch mod eval-data (int num-batch) reset)))) + +(s/def ::score-opts + (s/keys :req-un [::eval-data ::eval-metric] + :opt-un [::num-batch ::reset ::epoch])) (defn exec-group [mod] (.execGroup mod)) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray.clj index 651bdcb3f315..9caa00d49010 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray.clj @@ -16,15 +16,18 @@ ;; (ns org.apache.clojure-mxnet.ndarray + "NDArray API for Clojure package." (:refer-clojure :exclude [* - + > >= < <= / cast concat flatten identity load max min repeat reverse set sort take to-array empty shuffle ref]) - (:require [org.apache.clojure-mxnet.base :as base] - [org.apache.clojure-mxnet.context :as mx-context] - [org.apache.clojure-mxnet.shape :as mx-shape] - [org.apache.clojure-mxnet.util :as util] - [clojure.reflect :as r] - [t6.from-scala.core :refer [$] :as $]) + (:require + [clojure.spec.alpha :as s] + + [org.apache.clojure-mxnet.base :as base] + [org.apache.clojure-mxnet.context :as mx-context] + [org.apache.clojure-mxnet.shape :as mx-shape] + [org.apache.clojure-mxnet.util :as util] + [t6.from-scala.core :refer [$] :as $]) (:import (org.apache.mxnet NDArray))) ;; loads the generated functions into the namespace @@ -91,6 +94,27 @@ ([start stop] (arange start stop {}))) +(defn ->ndarray + "Creates a new NDArray based on the given n-dimenstional vector + of numbers. + `nd-vec`: n-dimensional vector with numbers. + `opts-map` { + `ctx`: Context of the output ndarray, will use default context if unspecified. + } + returns: `ndarray` with the given values and matching the shape of the input vector. + Ex: + (->ndarray [5.0 -4.0]) + (->ndarray [5 -4] {:ctx (context/cpu)}) + (->ndarray [[1 2 3] [4 5 6]]) + (->ndarray [[[1.0] [2.0]]]" + ([nd-vec {:keys [ctx] + :or {ctx (mx-context/default-context)} + :as opts}] + (array (vec (clojure.core/flatten nd-vec)) + (util/nd-seq-shape nd-vec) + {:ctx ctx})) + ([nd-vec] (->ndarray nd-vec {}))) + (defn slice "Return a sliced NDArray that shares memory with current one." ([ndarray i] @@ -167,3 +191,46 @@ (defn shape-vec [ndarray] (mx-shape/->vec (shape ndarray))) + +(s/def ::ndarray #(instance? NDArray %)) +(s/def ::vector vector?) +(s/def ::sequential sequential?) +(s/def ::shape-vec-match-vec + (fn [[v vec-shape]] (= (count v) (reduce clojure.core/* 1 vec-shape)))) + +(s/fdef vec->nd-vec + :args (s/cat :v ::sequential :shape-vec ::sequential) + :ret ::vector) + +(defn- vec->nd-vec + "Convert a vector `v` into a n-dimensional vector given the `shape-vec` + Ex: + (vec->nd-vec [1 2 3] [1 1 3]) ;[[[1 2 3]]] + (vec->nd-vec [1 2 3 4 5 6] [2 3 1]) ;[[[1] [2] [3]] [[4] [5] [6]]] + (vec->nd-vec [1 2 3 4 5 6] [1 2 3]) ;[[[1 2 3]] [4 5 6]]] + (vec->nd-vec [1 2 3 4 5 6] [3 1 2]) ;[[[1 2]] [[3 4]] [[5 6]]] + (vec->nd-vec [1 2 3 4 5 6] [3 2]) ;[[1 2] [3 4] [5 6]]" + [v [s1 & ss :as shape-vec]] + (util/validate! ::sequential v "Invalid input vector `v`") + (util/validate! ::sequential shape-vec "Invalid input vector `shape-vec`") + (util/validate! ::shape-vec-match-vec + [v shape-vec] + "Mismatch between vector `v` and vector `shape-vec`") + (if-not (seq ss) + (vec v) + (->> v + (partition (clojure.core// (count v) s1)) + vec + (mapv #(vec->nd-vec % ss))))) + +(s/fdef ->nd-vec :args (s/cat :ndarray ::ndarray) :ret ::vector) + +(defn ->nd-vec + "Convert an ndarray `ndarray` into a n-dimensional Clojure vector. + Ex: + (->nd-vec (array [1] [1 1 1])) ;[[[1.0]]] + (->nd-vec (array [1 2 3] [3 1 1])) ;[[[1.0]] [[2.0]] [[3.0]]] + (->nd-vec (array [1 2 3 4 5 6]) [3 1 2]) ;[[[1.0 2.0]] [[3.0 4.0]] [[5.0 6.0]]]" + [ndarray] + (util/validate! ::ndarray ndarray "Invalid input array") + (vec->nd-vec (->vec ndarray) (shape-vec ndarray))) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray_api.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray_api.clj new file mode 100644 index 000000000000..70359a6ef9b7 --- /dev/null +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/ndarray_api.clj @@ -0,0 +1,32 @@ +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +(ns org.apache.clojure-mxnet.ndarray-api + "Experimental NDArray API" + (:refer-clojure :exclude [* - + > >= < <= / cast concat flatten identity load max + min repeat reverse set sort take to-array empty shuffle + ref]) + + (:require [org.apache.clojure-mxnet.base :as base] + [org.apache.clojure-mxnet.context :as mx-context] + [org.apache.clojure-mxnet.shape :as mx-shape] + [org.apache.clojure-mxnet.util :as util] + [clojure.reflect :as r] + [t6.from-scala.core :refer [$] :as $]) + (:import (org.apache.mxnet NDArrayAPI))) + +;; loads the generated functions into the namespace +(do (clojure.core/load "gen/ndarray_api")) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/random.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/random.clj index 0ec2039ba79b..1261e659e6dc 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/random.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/random.clj @@ -16,70 +16,84 @@ ;; (ns org.apache.clojure-mxnet.random + "Random Number interface of mxnet." (:require - [org.apache.clojure-mxnet.shape :as mx-shape] - [org.apache.clojure-mxnet.context :as context] [clojure.spec.alpha :as s] + [org.apache.clojure-mxnet.context :as context] + [org.apache.clojure-mxnet.shape :as mx-shape] [org.apache.clojure-mxnet.util :as util]) (:import (org.apache.mxnet Context Random))) (s/def ::low number?) (s/def ::high number?) +(s/def ::low-high (fn [[low high]] (<= low high))) (s/def ::shape-vec (s/coll-of pos-int? :kind vector?)) (s/def ::ctx #(instance? Context %)) (s/def ::uniform-opts (s/keys :opt-un [::ctx])) (defn uniform - "Generate uniform distribution in [low, high) with shape. - low: The lower bound of distribution. - high: The upper bound of distribution. - shape-vec: vector shape of the ndarray generated. - opts-map { - ctx: Context of output ndarray, will use default context if not specified. - out: Output place holder} - returns: The result ndarray with generated result./" + "Generate uniform distribution in [`low`, `high`) with shape. + `low`: The lower bound of distribution. + `high`: The upper bound of distribution. + `shape-vec`: vector shape of the ndarray generated. + `opts-map` { + `ctx`: Context of output ndarray, will use default context if not specified. + `out`: Output place holder} + returns: The result ndarray with generated result. + Ex: + (uniform 0 1 [1 10]) + (uniform -10 10 [100 100])" ([low high shape-vec {:keys [ctx out] :as opts}] - (util/validate! ::uniform-opts opts "Incorrect random uniform parameters") + (util/validate! ::uniform-opts opts "Incorrect random uniform parameters") (util/validate! ::low low "Incorrect random uniform parameter") (util/validate! ::high high "Incorrect random uniform parameters") + (util/validate! ::low-high [low high] "Incorrect random uniform parameters") (util/validate! ::shape-vec shape-vec "Incorrect random uniform parameters") (Random/uniform (float low) (float high) (mx-shape/->shape shape-vec) ctx out)) ([low high shape-vec] (uniform low high shape-vec {}))) (s/def ::loc number?) -(s/def ::scale number?) +(s/def ::scale (s/and number? pos?)) (s/def ::normal-opts (s/keys :opt-un [::ctx])) (defn normal - "Generate normal(Gaussian) distribution N(mean, stdvar^^2) with shape. - loc: The standard deviation of the normal distribution - scale: The upper bound of distribution. - shape-vec: vector shape of the ndarray generated. - opts-map { - ctx: Context of output ndarray, will use default context if not specified. - out: Output place holder} - returns: The result ndarray with generated result./" + "Generate normal (Gaussian) distribution N(mean, stdvar^^2) with shape. + `loc`: Mean (centre) of the distribution. + `scale`: Standard deviation (spread or width) of the distribution. + `shape-vec`: vector shape of the ndarray generated. + `opts-map` { + `ctx`: Context of output ndarray, will use default context if not specified. + `out`: Output place holder} + returns: The result ndarray with generated result. + Ex: + (normal 0 1 [10 10]) + (normal -5 4 [2 3])" ([loc scale shape-vec {:keys [ctx out] :as opts}] (util/validate! ::normal-opts opts "Incorrect random normal parameters") (util/validate! ::loc loc "Incorrect random normal parameters") (util/validate! ::scale scale "Incorrect random normal parameters") (util/validate! ::shape-vec shape-vec "Incorrect random uniform parameters") - (Random/normal (float loc) (float scale) (mx-shape/->shape shape-vec) ctx out)) + (Random/normal (float loc) + (float scale) + (mx-shape/->shape shape-vec) ctx out)) ([loc scale shape-vec] (normal loc scale shape-vec {}))) (s/def ::seed-state number?) (defn seed - " Seed the random number generators in mxnet. - This seed will affect behavior of functions in this module, - as well as results from executors that contains Random number - such as Dropout operators. + "Seed the random number generators in mxnet. + This seed will affect behavior of functions in this module, + as well as results from executors that contains Random number + such as Dropout operators. - seed-state: The random number seed to set to all devices. + `seed-state`: The random number seed to set to all devices. note: The random number generator of mxnet is by default device specific. This means if you set the same seed, the random number sequence - generated from GPU0 can be different from CPU." + generated from GPU0 can be different from CPU. + Ex: + (seed-state 42) + (seed-state 42.0)" [seed-state] (util/validate! ::seed-state seed-state "Incorrect seed parameters") - (Random/seed (int seed-state))) \ No newline at end of file + (Random/seed (int seed-state))) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/symbol_api.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/symbol_api.clj new file mode 100644 index 000000000000..69cc8136d500 --- /dev/null +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/symbol_api.clj @@ -0,0 +1,32 @@ +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +(ns org.apache.clojure-mxnet.symbol-api + "Experimental Symbol API" + (:refer-clojure :exclude [* - + > >= < <= / cast concat identity flatten load max + min repeat reverse set sort take to-array empty sin + get apply shuffle ref]) + (:require [org.apache.clojure-mxnet.base :as base] + [org.apache.clojure-mxnet.context :as mx-context] + [org.apache.clojure-mxnet.executor :as ex] + [org.apache.clojure-mxnet.shape :as mx-shape] + [org.apache.clojure-mxnet.util :as util] + [t6.from-scala.core :refer [$] :as $] + [org.apache.clojure-mxnet.ndarray :as ndarray]) + (:import (org.apache.mxnet SymbolAPI))) + +;; loads the generated functions into the namespace +(do (clojure.core/load "gen/symbol_api")) diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/util.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/util.clj index 6b5f50792ead..9dc6c8f88ddd 100644 --- a/contrib/clojure-package/src/org/apache/clojure_mxnet/util.clj +++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/util.clj @@ -35,7 +35,6 @@ "int<>" "vec-of-ints" "float<>" "vec-of-floats" "byte<>" "byte-array" - "java.lang.String<>" "vec-or-strings" "org.apache.mxnet.NDArray" "ndarray" "org.apache.mxnet.Symbol" "sym" "org.apache.mxnet.MX_PRIMITIVES$MX_PRIMITIVE_TYPE" "double-or-float"}) @@ -49,7 +48,7 @@ "int<>" "vec-of-ints" "float<>" "vec-of-floats" "byte<>" "byte-array" - "java.lang.String<>" "vec-or-strings" + "java.lang.String<>" "vec-of-strings" "org.apache.mxnet.Symbol" "sym" "java.lang.Object" "object"}) @@ -74,8 +73,17 @@ (defn option->value [opt] ($/view opt)) -(defn keyword->snake-case [vals] - (mapv (fn [v] (if (keyword? v) (string/replace (name v) "-" "_") v)) vals)) +(defn keyword->snake-case + "Transforms a keyword `kw` into a snake-case string. + `kw`: keyword + returns: string + Ex: + (keyword->snake-case :foo-bar) ;\"foo_bar\" + (keyword->snake-case :foo) ;\"foo\"" + [kw] + (if (keyword? kw) + (string/replace (name kw) "-" "_") + kw)) (defn convert-tuple [param] (apply $/tuple param)) @@ -111,8 +119,8 @@ (empty-map) (apply $/immutable-map (->> param (into []) - flatten - keyword->snake-case)))) + (flatten) + (mapv keyword->snake-case))))) (defn convert-symbol-map [param] (convert-map (tuple-convert-by-param-name param))) @@ -143,9 +151,12 @@ (and (get targets "scala.collection.Seq") (instance? org.apache.mxnet.Symbol param)) ($/immutable-list param) (and (get targets "scala.collection.Seq") (and (or (vector? param) (seq? param)) (empty? param))) (empty-list) (and (get targets "scala.collection.Seq") (or (vector? param) (seq? param))) (apply $/immutable-list param) + (and (get targets "org.apache.mxnet.Shape") (or (vector? param) (seq? param) (empty? param))) (mx-shape/->shape param) (and (get targets "int<>") (vector? param)) (int-array param) (and (get targets "float<>") (vector? param)) (float-array param) (and (get targets "java.lang.String<>") (vector? param)) (into-array param) + (and (get targets "org.apache.mxnet.NDArray<>") (vector? param)) (into-array param) + (and (get targets "org.apache.mxnet.Symbol<>") (vector? param)) (into-array param) (and (get targets "org.apache.mxnet.MX_PRIMITIVES$MX_PRIMITIVE_TYPE") (instance? Float param)) (primitives/mx-float param) (and (get targets "org.apache.mxnet.MX_PRIMITIVES$MX_PRIMITIVE_TYPE") (number? param)) (primitives/mx-double param) :else param)) @@ -218,8 +229,28 @@ (throw (ex-info error-msg (s/explain-data spec value))))) +(s/def ::non-empty-seq (s/and sequential? not-empty)) +(defn to-array-nd + "Converts any N-D sequential structure to an array + with the same dimensions." + [nd-seq] + (validate! ::non-empty-seq nd-seq "Invalid N-D sequence") + (if (sequential? (first nd-seq)) + (to-array (mapv to-array-nd nd-seq)) + (to-array nd-seq))) + +(defn nd-seq-shape + "Computes the shape of a n-dimensional sequential structure" + [nd-seq] + (validate! ::non-empty-seq nd-seq "Invalid N-D sequence") + (loop [s nd-seq + shape [(count s)]] + (if (sequential? (first s)) + (recur (first s) (conj shape (count (first s)))) + shape))) + (defn map->scala-tuple-seq - "* Convert a map to a scala-Seq of scala-Tubple. + "* Convert a map to a scala-Seq of scala-Tuple. * Should also work if a seq of seq of 2 things passed. * Otherwise passed through unchanged." [map-or-tuple-seq] diff --git a/contrib/clojure-package/test/dev/generator_test.clj b/contrib/clojure-package/test/dev/generator_test.clj index a3ec338921ba..cf28241c59e8 100644 --- a/contrib/clojure-package/test/dev/generator_test.clj +++ b/contrib/clojure-package/test/dev/generator_test.clj @@ -50,6 +50,127 @@ (is (= transformed-params (gen/symbol-transform-param-name (:parameter-types (symbol-reflect-info "floor"))))))) +(deftest test-gen-op-info + (testing "activation" + (let [activation-info (gen/gen-op-info "Activation")] + (is (= "activation" (:fn-name activation-info))) + (is (string? (:fn-description activation-info))) + (is (= 2 (-> activation-info :args count))) + (is (= "" (:key-var-num-args activation-info))) + + (is (= "data" (-> activation-info :args first :name))) + (is (= "NDArray-or-Symbol" (-> activation-info :args first :type))) + (is (false? (-> activation-info :args first :optional?))) + (is (nil? (-> activation-info :args first :default))) + (is (string? (-> activation-info :args first :description))) + + (is (= "act-type" (-> activation-info :args second :name))) + (is (= "'relu', 'sigmoid', 'softrelu', 'softsign', 'tanh'" (-> activation-info :args second :type))) + (is (false? (-> activation-info :args second :optional?))) + (is (nil? (-> activation-info :args second :default))) + (is (string? (-> activation-info :args second :description))))) + + (testing "argmin" + (let [argmin-info (gen/gen-op-info "argmin")] + (is (= "argmin" (:fn-name argmin-info))) + (is (= 3 (-> argmin-info :args count))) + + (is (= "data" (-> argmin-info :args (nth 0) :name))) + (is (= "NDArray-or-Symbol" (-> argmin-info :args (nth 0) :type))) + (is (false? (-> argmin-info :args (nth 0) :optional?))) + + (is (= "axis" (-> argmin-info :args (nth 1) :name))) + (is (= "int or None" (-> argmin-info :args (nth 1) :type))) + (is (= "'None'" (-> argmin-info :args (nth 1) :default))) + (is (true? (-> argmin-info :args (nth 1) :optional?))) + + (is (= "keepdims" (-> argmin-info :args (nth 2) :name))) + (is (= "boolean" (-> argmin-info :args (nth 2) :type))) + (is (= "0" (-> argmin-info :args (nth 2) :default))) + (is (true? (-> argmin-info :args (nth 2) :optional?))))) + + (testing "concat" + (let [concat-info (gen/gen-op-info "Concat")] + (is (= "concat" (:fn-name concat-info))) + (is (= 3 (-> concat-info :args count))) + (is (= "num-args" (:key-var-num-args concat-info))) + + (is (= "data" (-> concat-info :args (nth 0) :name))) + (is (= "NDArray-or-Symbol[]" (-> concat-info :args (nth 0) :type))) + (is (false? (-> concat-info :args (nth 0) :optional?))) + + (is (= "num-args" (-> concat-info :args (nth 1) :name))) + (is (= "int" (-> concat-info :args (nth 1) :type))) + (is (false? (-> concat-info :args (nth 1) :optional?))) + + (is (= "dim" (-> concat-info :args (nth 2) :name))) + (is (= "int" (-> concat-info :args (nth 2) :type))) + (is (= "'1'" (-> concat-info :args (nth 2) :default))) + (is (true? (-> concat-info :args (nth 2) :optional?))))) + + (testing "convolution" + (let [convolution-info (gen/gen-op-info "Convolution")] + + (is (= "convolution" (:fn-name convolution-info))) + (is (= 14 (-> convolution-info :args count))) + (is (= "" (:key-var-num-args convolution-info))) + + (is (= "data" (-> convolution-info :args (nth 0) :name))) + (is (= "NDArray-or-Symbol" (-> convolution-info :args (nth 0) :type))) + (is (false? (-> convolution-info :args (nth 0) :optional?))) + + (is (= "weight" (-> convolution-info :args (nth 1) :name))) + (is (= "NDArray-or-Symbol" (-> convolution-info :args (nth 1) :type))) + (is (false? (-> convolution-info :args (nth 1) :optional?))) + + (is (= "kernel" (-> convolution-info :args (nth 3) :name))) + (is (= "Shape" (-> convolution-info :args (nth 3) :type))) + (is (= "(tuple)" (-> convolution-info :args (nth 3) :spec))) + (is (false? (-> convolution-info :args (nth 3) :optional?))) + + (is (= "stride" (-> convolution-info :args (nth 4) :name))) + (is (= "Shape" (-> convolution-info :args (nth 4) :type))) + (is (= "(tuple)" (-> convolution-info :args (nth 4) :spec))) + (is (= "[]" (-> convolution-info :args (nth 4) :default))) + (is (true? (-> convolution-info :args (nth 4) :optional?))) + + (is (= "num-filter" (-> convolution-info :args (nth 7) :name))) + (is (= "int" (-> convolution-info :args (nth 7) :type))) + (is (= "(non-negative)" (-> convolution-info :args (nth 7) :spec))) + (is (false? (-> convolution-info :args (nth 7) :optional?))) + + (is (= "num-group" (-> convolution-info :args (nth 8) :name))) + (is (= "int" (-> convolution-info :args (nth 8) :type))) + (is (= "(non-negative)" (-> convolution-info :args (nth 8) :spec))) + (is (= "1" (-> convolution-info :args (nth 8) :default))) + (is (true? (-> convolution-info :args (nth 8) :optional?))) + + (is (= "workspace" (-> convolution-info :args (nth 9) :name))) + (is (= "long" (-> convolution-info :args (nth 9) :type))) + (is (= "(non-negative)" (-> convolution-info :args (nth 9) :spec))) + (is (= "1024" (-> convolution-info :args (nth 9) :default))) + (is (true? (-> convolution-info :args (nth 9) :optional?))) + + (is (= "no-bias" (-> convolution-info :args (nth 10) :name))) + (is (= "boolean" (-> convolution-info :args (nth 10) :type))) + (is (= "0" (-> convolution-info :args (nth 10) :default))) + (is (true? (-> convolution-info :args (nth 10) :optional?))) + + (is (= "layout" (-> convolution-info :args (nth 13) :name))) + (is (= "None, 'NCDHW', 'NCHW', 'NCW', 'NDHWC', 'NHWC'" (-> convolution-info :args (nth 13) :type))) + (is (= "'None'" (-> convolution-info :args (nth 13) :default))) + (is (true? (-> convolution-info :args (nth 13) :optional?))))) + + (testing "element wise sum" + (let [element-wise-sum-info (gen/gen-op-info "ElementWiseSum")] + (is (= "add-n" (:fn-name element-wise-sum-info))) + (is (= 1 (-> element-wise-sum-info :args count))) + (is (= "num-args" (:key-var-num-args element-wise-sum-info))) + + (is (= "args" (-> element-wise-sum-info :args (nth 0) :name))) + (is (= "NDArray-or-Symbol[]" (-> element-wise-sum-info :args (nth 0) :type))) + (is (false? (-> element-wise-sum-info :args (nth 0) :optional?)))))) + (deftest test-ndarray-transform-param-name (let [params ["scala.collection.immutable.Map" "scala.collection.Seq"] @@ -68,7 +189,10 @@ (deftest test-rename-duplicate-params (is (= ["foo" "bar" "baz"] (gen/rename-duplicate-params ["foo" "bar" "baz"]))) - (is (= ["foo" "bar" "bar-1"] (gen/rename-duplicate-params ["foo" "bar" "bar"])))) + (is (= ["foo" "bar" "bar-1"] (gen/rename-duplicate-params ["foo" "bar" "bar"]))) + (is (= ["foo" "bar" "bar-1" "foo-1"] (gen/rename-duplicate-params ["foo" "bar" "bar" "foo"]))) + (is (= ["foo" "bar" "bar-1" "bar-2"] (gen/rename-duplicate-params ["foo" "bar" "bar" "bar"]))) + (is (= ["foo" "bar" "bar-1" "bar-2" "foo-1" "baz"] (gen/rename-duplicate-params ["foo" "bar" "bar" "bar" "foo" "baz"])))) (deftest test-is-symbol-hand-gen? (is (not (false? (gen/is-symbol-hand-gen? (symbol-reflect-info "max"))))) @@ -86,18 +210,19 @@ (is (= "LRN" (-> lrn-info vals ffirst :name str))))) (deftest test-symbol-vector-args - (is (= `(if (clojure.core/map? kwargs-map-or-vec-or-sym) + (is (= '(if (clojure.core/map? kwargs-map-or-vec-or-sym) (util/empty-list) (util/coerce-param kwargs-map-or-vec-or-sym - #{"scala.collection.Seq"}))) (gen/symbol-vector-args))) + #{"scala.collection.Seq"})) + (gen/symbol-vector-args)))) (deftest test-symbol-map-args - (is (= `(if (clojure.core/map? kwargs-map-or-vec-or-sym) + (is (= '(if (clojure.core/map? kwargs-map-or-vec-or-sym) (org.apache.clojure-mxnet.util/convert-symbol-map kwargs-map-or-vec-or-sym) - nil)) - (gen/symbol-map-args))) + nil) + (gen/symbol-map-args)))) (deftest test-add-symbol-arities (let [params (map symbol ["sym-name" "kwargs-map" "symbol-list" "kwargs-map-1"]) @@ -112,36 +237,36 @@ ar1)) (is (= '([sym-name kwargs-map-or-vec-or-sym] (foo - sym-name - nil - (if - (clojure.core/map? kwargs-map-or-vec-or-sym) - (util/empty-list) - (util/coerce-param - kwargs-map-or-vec-or-sym - #{"scala.collection.Seq"})) - (if - (clojure.core/map? kwargs-map-or-vec-or-sym) - (org.apache.clojure-mxnet.util/convert-symbol-map - kwargs-map-or-vec-or-sym) - nil)))) - ar2) + sym-name + nil + (if + (clojure.core/map? kwargs-map-or-vec-or-sym) + (util/empty-list) + (util/coerce-param + kwargs-map-or-vec-or-sym + #{"scala.collection.Seq"})) + (if + (clojure.core/map? kwargs-map-or-vec-or-sym) + (org.apache.clojure-mxnet.util/convert-symbol-map + kwargs-map-or-vec-or-sym) + nil))) + ar2)) (is (= '([kwargs-map-or-vec-or-sym] (foo - nil - nil - (if - (clojure.core/map? kwargs-map-or-vec-or-sym) - (util/empty-list) - (util/coerce-param - kwargs-map-or-vec-or-sym - #{"scala.collection.Seq"})) - (if - (clojure.core/map? kwargs-map-or-vec-or-sym) - (org.apache.clojure-mxnet.util/convert-symbol-map - kwargs-map-or-vec-or-sym) - nil)))) - ar3))) + nil + nil + (if + (clojure.core/map? kwargs-map-or-vec-or-sym) + (util/empty-list) + (util/coerce-param + kwargs-map-or-vec-or-sym + #{"scala.collection.Seq"})) + (if + (clojure.core/map? kwargs-map-or-vec-or-sym) + (org.apache.clojure-mxnet.util/convert-symbol-map + kwargs-map-or-vec-or-sym) + nil))) + ar3)))) (deftest test-gen-symbol-function-arity (let [op-name (symbol "$div") @@ -157,14 +282,14 @@ :exception-types [], :flags #{:public}}]} function-name (symbol "div")] - (is (= '(([sym sym-or-Object] + (is (= '(([sym sym-or-object] (util/coerce-return (.$div sym (util/nil-or-coerce-param - sym-or-Object - #{"org.apache.mxnet.Symbol" "java.lang.Object"})))))) - (gen/gen-symbol-function-arity op-name op-values function-name)))) + sym-or-object + #{"org.apache.mxnet.Symbol" "java.lang.Object"}))))) + (gen/gen-symbol-function-arity op-name op-values function-name))))) (deftest test-gen-ndarray-function-arity (let [op-name (symbol "$div") @@ -182,15 +307,25 @@ :flags #{:public}}]}] (is (= '(([ndarray num-or-ndarray] (util/coerce-return - (.$div - ndarray - (util/coerce-param - num-or-ndarray - #{"float" "org.apache.mxnet.NDArray"})))))) - (gen/gen-ndarray-function-arity op-name op-values)))) + (.$div + ndarray + (util/coerce-param + num-or-ndarray + #{"float" "org.apache.mxnet.NDArray"}))))) + (gen/gen-ndarray-function-arity op-name op-values))))) (deftest test-write-to-file - (testing "symbol" + (testing "symbol-api" + (let [fname "test/test-symbol-api.clj" + _ (gen/write-to-file [(first gen/all-symbol-api-functions) + (second gen/all-symbol-api-functions)] + gen/symbol-api-gen-ns + fname) + good-contents (slurp "test/good-test-symbol-api.clj") + contents (slurp fname)] + (is (= good-contents contents)))) + + (testing "symbol" (let [fname "test/test-symbol.clj" _ (gen/write-to-file [(first gen/all-symbol-functions)] gen/symbol-gen-ns @@ -199,6 +334,16 @@ contents (slurp fname)] (is (= good-contents contents)))) + (testing "ndarray-api" + (let [fname "test/test-ndarray-api.clj" + _ (gen/write-to-file [(first gen/all-ndarray-api-functions) + (second gen/all-ndarray-api-functions)] + gen/ndarray-api-gen-ns + fname) + good-contents (slurp "test/good-test-ndarray-api.clj") + contents (slurp fname)] + (is (= good-contents contents)))) + (testing "ndarray" (let [fname "test/test-ndarray.clj" _ (gen/write-to-file [(first gen/all-ndarray-functions)] diff --git a/contrib/clojure-package/test/good-test-ndarray-api.clj b/contrib/clojure-package/test/good-test-ndarray-api.clj new file mode 100644 index 000000000000..1b83a7beb7bc --- /dev/null +++ b/contrib/clojure-package/test/good-test-ndarray-api.clj @@ -0,0 +1,89 @@ +(ns + ^{:doc "Experimental"} + org.apache.clojure-mxnet.ndarray-api + (:refer-clojure :exclude [* - + > >= < <= / cast concat flatten identity load max + min repeat reverse set sort take to-array empty shuffle + ref]) + (:require [org.apache.clojure-mxnet.shape :as mx-shape] + [org.apache.clojure-mxnet.util :as util]) + (:import (org.apache.mxnet NDArrayAPI))) + +;; Do not edit - this is auto-generated + +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + + + +(defn + activation + "Applies an activation function element-wise to the input.\n\nThe following activation functions are supported:\n\n- `relu`: Rectified Linear Unit, :math:`y = max(x, 0)`\n- `sigmoid`: :math:`y = \\frac{1}{1 + exp(-x)}`\n- `tanh`: Hyperbolic tangent, :math:`y = \\frac{exp(x) - exp(-x)}{exp(x) + exp(-x)}`\n- `softrelu`: Soft ReLU, or SoftPlus, :math:`y = log(1 + exp(x))`\n- `softsign`: :math:`y = \\frac{x}{1 + abs(x)}`\n\n\n\nDefined in src/operator/nn/activation.cc:L167\n\n`data`: The input array.\n`act-type`: Activation function to be applied.\n`out`: Output array. (optional)\n" + ([data act-type] (activation {:data data, :act-type act-type})) + ([{:keys [data act-type out], :or {out nil}, :as opts}] + (util/coerce-return + (NDArrayAPI/Activation data act-type (util/->option out))))) + +(defn + batch-norm + "Batch normalization.\n\nNormalizes a data batch by mean and variance, and applies a scale ``gamma`` as\nwell as offset ``beta``.\n\nAssume the input has more than one dimension and we normalize along axis 1.\nWe first compute the mean and variance along this axis:\n\n.. math::\n\n data\\_mean[i] = mean(data[:,i,:,...]) \\\\\n data\\_var[i] = var(data[:,i,:,...])\n\nThen compute the normalized output, which has the same shape as input, as following:\n\n.. math::\n\n out[:,i,:,...] = \\frac{data[:,i,:,...] - data\\_mean[i]}{\\sqrt{data\\_var[i]+\\epsilon}} * gamma[i] + beta[i]\n\nBoth *mean* and *var* returns a scalar by treating the input as a vector.\n\nAssume the input has size *k* on axis 1, then both ``gamma`` and ``beta``\nhave shape *(k,)*. If ``output_mean_var`` is set to be true, then outputs both ``data_mean`` and\nthe inverse of ``data_var``, which are needed for the backward pass. Note that gradient of these\ntwo outputs are blocked.\n\nBesides the inputs and the outputs, this operator accepts two auxiliary\nstates, ``moving_mean`` and ``moving_var``, which are *k*-length\nvectors. They are global statistics for the whole dataset, which are updated\nby::\n\n moving_mean = moving_mean * momentum + data_mean * (1 - momentum)\n moving_var = moving_var * momentum + data_var * (1 - momentum)\n\nIf ``use_global_stats`` is set to be true, then ``moving_mean`` and\n``moving_var`` are used instead of ``data_mean`` and ``data_var`` to compute\nthe output. It is often used during inference.\n\nThe parameter ``axis`` specifies which axis of the input shape denotes\nthe 'channel' (separately normalized groups). The default is 1. Specifying -1 sets the channel\naxis to be the last item in the input shape.\n\nBoth ``gamma`` and ``beta`` are learnable parameters. But if ``fix_gamma`` is true,\nthen set ``gamma`` to 1 and its gradient to 0.\n\n.. Note::\n When ``fix_gamma`` is set to True, no sparse support is provided. If ``fix_gamma is`` set to False,\n the sparse tensors will fallback.\n\n\n\nDefined in src/operator/nn/batch_norm.cc:L574\n\n`data`: Input data to batch normalization\n`gamma`: gamma array\n`beta`: beta array\n`moving-mean`: running mean of input\n`moving-var`: running variance of input\n`eps`: Epsilon to prevent div 0. Must be no less than CUDNN_BN_MIN_EPSILON defined in cudnn.h when using cudnn (usually 1e-5) (optional)\n`momentum`: Momentum for moving average (optional)\n`fix-gamma`: Fix gamma while training (optional)\n`use-global-stats`: Whether use global moving statistics instead of local batch-norm. This will force change batch-norm into a scale shift operator. (optional)\n`output-mean-var`: Output the mean and inverse std (optional)\n`axis`: Specify which shape axis the channel is specified (optional)\n`cudnn-off`: Do not select CUDNN operator, if available (optional)\n`out`: Output array. (optional)\n" + ([data gamma beta moving-mean moving-var] + (batch-norm + {:data data, + :gamma gamma, + :beta beta, + :moving-mean moving-mean, + :moving-var moving-var})) + ([{:keys + [data + gamma + beta + moving-mean + moving-var + eps + momentum + fix-gamma + use-global-stats + output-mean-var + axis + cudnn-off + out], + :or + {eps nil, + momentum nil, + fix-gamma nil, + use-global-stats nil, + output-mean-var nil, + axis nil, + cudnn-off nil, + out nil}, + :as opts}] + (util/coerce-return + (NDArrayAPI/BatchNorm + data + gamma + beta + moving-mean + moving-var + (util/->option eps) + (util/->option momentum) + (util/->option fix-gamma) + (util/->option use-global-stats) + (util/->option output-mean-var) + (util/->option axis) + (util/->option cudnn-off) + (util/->option out))))) + diff --git a/contrib/clojure-package/test/good-test-symbol-api.clj b/contrib/clojure-package/test/good-test-symbol-api.clj new file mode 100644 index 000000000000..a03088486ee8 --- /dev/null +++ b/contrib/clojure-package/test/good-test-symbol-api.clj @@ -0,0 +1,109 @@ +(ns + ^{:doc "Experimental"} + org.apache.clojure-mxnet.symbol-api + (:refer-clojure :exclude [* - + > >= < <= / cast concat identity flatten load max + min repeat reverse set sort take to-array empty sin + get apply shuffle ref]) + (:require [org.apache.clojure-mxnet.util :as util] + [org.apache.clojure-mxnet.shape :as mx-shape]) + (:import (org.apache.mxnet SymbolAPI))) + +;; Do not edit - this is auto-generated + +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + + + + +(defn + activation + "Applies an activation function element-wise to the input.\n\nThe following activation functions are supported:\n\n- `relu`: Rectified Linear Unit, :math:`y = max(x, 0)`\n- `sigmoid`: :math:`y = \\frac{1}{1 + exp(-x)}`\n- `tanh`: Hyperbolic tangent, :math:`y = \\frac{exp(x) - exp(-x)}{exp(x) + exp(-x)}`\n- `softrelu`: Soft ReLU, or SoftPlus, :math:`y = log(1 + exp(x))`\n- `softsign`: :math:`y = \\frac{x}{1 + abs(x)}`\n\n\n\nDefined in src/operator/nn/activation.cc:L167\n\n`data`: The input array. (optional)\n`act-type`: Activation function to be applied.\n`name`: Name of the symbol (optional)\n`attr`: Attributes of the symbol (optional)\n" + [{:keys [data act-type name attr], + :or {data nil, name nil, attr nil}, + :as opts}] + (util/coerce-return + (SymbolAPI/Activation + (util/->option data) + act-type + name + (clojure.core/when + attr + (clojure.core/->> + attr + (clojure.core/mapv + (clojure.core/fn [[k v]] [k (clojure.core/str v)])) + (clojure.core/into {}) + util/convert-map))))) + +(defn + batch-norm + "Batch normalization.\n\nNormalizes a data batch by mean and variance, and applies a scale ``gamma`` as\nwell as offset ``beta``.\n\nAssume the input has more than one dimension and we normalize along axis 1.\nWe first compute the mean and variance along this axis:\n\n.. math::\n\n data\\_mean[i] = mean(data[:,i,:,...]) \\\\\n data\\_var[i] = var(data[:,i,:,...])\n\nThen compute the normalized output, which has the same shape as input, as following:\n\n.. math::\n\n out[:,i,:,...] = \\frac{data[:,i,:,...] - data\\_mean[i]}{\\sqrt{data\\_var[i]+\\epsilon}} * gamma[i] + beta[i]\n\nBoth *mean* and *var* returns a scalar by treating the input as a vector.\n\nAssume the input has size *k* on axis 1, then both ``gamma`` and ``beta``\nhave shape *(k,)*. If ``output_mean_var`` is set to be true, then outputs both ``data_mean`` and\nthe inverse of ``data_var``, which are needed for the backward pass. Note that gradient of these\ntwo outputs are blocked.\n\nBesides the inputs and the outputs, this operator accepts two auxiliary\nstates, ``moving_mean`` and ``moving_var``, which are *k*-length\nvectors. They are global statistics for the whole dataset, which are updated\nby::\n\n moving_mean = moving_mean * momentum + data_mean * (1 - momentum)\n moving_var = moving_var * momentum + data_var * (1 - momentum)\n\nIf ``use_global_stats`` is set to be true, then ``moving_mean`` and\n``moving_var`` are used instead of ``data_mean`` and ``data_var`` to compute\nthe output. It is often used during inference.\n\nThe parameter ``axis`` specifies which axis of the input shape denotes\nthe 'channel' (separately normalized groups). The default is 1. Specifying -1 sets the channel\naxis to be the last item in the input shape.\n\nBoth ``gamma`` and ``beta`` are learnable parameters. But if ``fix_gamma`` is true,\nthen set ``gamma`` to 1 and its gradient to 0.\n\n.. Note::\n When ``fix_gamma`` is set to True, no sparse support is provided. If ``fix_gamma is`` set to False,\n the sparse tensors will fallback.\n\n\n\nDefined in src/operator/nn/batch_norm.cc:L574\n\n`data`: Input data to batch normalization (optional)\n`gamma`: gamma array (optional)\n`beta`: beta array (optional)\n`moving-mean`: running mean of input (optional)\n`moving-var`: running variance of input (optional)\n`eps`: Epsilon to prevent div 0. Must be no less than CUDNN_BN_MIN_EPSILON defined in cudnn.h when using cudnn (usually 1e-5) (optional)\n`momentum`: Momentum for moving average (optional)\n`fix-gamma`: Fix gamma while training (optional)\n`use-global-stats`: Whether use global moving statistics instead of local batch-norm. This will force change batch-norm into a scale shift operator. (optional)\n`output-mean-var`: Output the mean and inverse std (optional)\n`axis`: Specify which shape axis the channel is specified (optional)\n`cudnn-off`: Do not select CUDNN operator, if available (optional)\n`name`: Name of the symbol (optional)\n`attr`: Attributes of the symbol (optional)\n" + [{:keys + [data + gamma + beta + moving-mean + moving-var + eps + momentum + fix-gamma + use-global-stats + output-mean-var + axis + cudnn-off + name + attr], + :or + {output-mean-var nil, + axis nil, + cudnn-off nil, + fix-gamma nil, + eps nil, + data nil, + attr nil, + beta nil, + name nil, + use-global-stats nil, + moving-mean nil, + moving-var nil, + momentum nil, + gamma nil}, + :as opts}] + (util/coerce-return + (SymbolAPI/BatchNorm + (util/->option data) + (util/->option gamma) + (util/->option beta) + (util/->option moving-mean) + (util/->option moving-var) + (util/->option eps) + (util/->option momentum) + (util/->option fix-gamma) + (util/->option use-global-stats) + (util/->option output-mean-var) + (util/->option axis) + (util/->option cudnn-off) + name + (clojure.core/when + attr + (clojure.core/->> + attr + (clojure.core/mapv + (clojure.core/fn [[k v]] [k (clojure.core/str v)])) + (clojure.core/into {}) + util/convert-map))))) + diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/conv_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/conv_test.clj index feda45b9d027..ca9d4bc93986 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/conv_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/conv_test.clj @@ -24,6 +24,8 @@ [org.apache.clojure-mxnet.module :as m] [org.apache.clojure-mxnet.optimizer :as optimizer] [org.apache.clojure-mxnet.symbol :as sym] + [org.apache.clojure-mxnet.symbol-api :as sym-api] + [org.apache.clojure-mxnet.util :as util] [clojure.reflect :as r])) (def data-dir "data/") @@ -54,17 +56,19 @@ (defn get-symbol [] (as-> (sym/variable "data") data - (sym/convolution "conv1" {:data data :kernel [3 3] :num-filter 32 :stride [2 2]}) - (sym/batch-norm "bn1" {:data data}) - (sym/activation "relu1" {:data data :act-type "relu"}) - (sym/pooling "mp1" {:data data :kernel [2 2] :pool-type "max" :stride [2 2]}) (sym/convolution "conv2" {:data data :kernel [3 3] :num-filter 32 :stride [2 2]}) - (sym/batch-norm "bn2" {:data data}) - (sym/activation "relu2" {:data data :act-type "relu"}) - (sym/pooling "mp2" {:data data :kernel [2 2] :pool-type "max" :stride [2 2]}) + (sym-api/convolution {:name "conv1" :data data :kernel [3 3] :num-filter 32 :stride [2 2]}) + (sym-api/batch-norm {:name "bn1" :data data}) + (sym-api/activation {:name "relu1" :data data :act-type "relu"}) + (sym-api/pooling {:name "mp1" :data data :kernel [2 2] :pool-type "max" :stride [2 2]}) - (sym/flatten "fl" {:data data}) - (sym/fully-connected "fc2" {:data data :num-hidden 10}) - (sym/softmax-output "softmax" {:data data}))) + (sym-api/convolution {:name "conv2" :data data :kernel [3 3] :num-filter 32 :stride [2 2]}) + (sym-api/batch-norm {:name "bn2" :data data}) + (sym-api/activation {:name "relu2" :data data :act-type "relu"}) + (sym-api/pooling {:name "mp2" :data data :kernel [2 2] :pool-type "max" :stride [2 2]}) + + (sym-api/flatten {:name "fl" :data data}) + (sym-api/fully-connected {:name "fc2" :data data :num-hidden 10}) + (sym-api/softmax-output {:name "softmax" :data data}))) (deftest test-conv [] (let [mod (m/module (get-symbol))] diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/eval_metric_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/eval_metric_test.clj index d6da2ec9ee58..1f4dba35fa7a 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/eval_metric_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/eval_metric_test.clj @@ -57,3 +57,12 @@ "my-metric")] (eval-metric/update metric [(ndarray/ones [2])] [(ndarray/ones [2])]) (is (= ["my-metric" 0.0] (eval-metric/get metric))))) + +(deftest test-comp-metric + (let [metric (eval-metric/comp-metric [(eval-metric/accuracy) + (eval-metric/f1) + (eval-metric/top-k-accuracy 2)])] + (eval-metric/update metric [(ndarray/ones [2])] [(ndarray/ones [2 3])]) + (is (= {"accuracy" 0.0 + "f1" 0.0 + "top_k_accuracy" 1.0} (eval-metric/get metric))))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/executor_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/executor_test.clj index fb73f0091562..ebd1a9d061a4 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/executor_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/executor_test.clj @@ -65,10 +65,10 @@ (map ndarray/->vec) first))) ;; test shared memory - (is (= [4.0 4.0 4.0]) (->> (executor/outputs exec) - (map ndarray/->vec) - first - (take 3))) + (is (= [4.0 4.0 4.0] (->> (executor/outputs exec) + (map ndarray/->vec) + first + (take 3)))) ;; test base exec forward (executor/forward exec) (is (every? #(= 4.0 %) (->> (executor/outputs exec) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj index 38ab11c86012..23b88d07e896 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj @@ -20,7 +20,8 @@ [org.apache.clojure-mxnet.ndarray :as ndarray] [clojure.java.io :as io] [clojure.test :refer :all]) - (:import (javax.imageio ImageIO))) + (:import (javax.imageio ImageIO) + (java.io File))) (def tmp-dir (System/getProperty "java.io.tmpdir")) (def image-path (.getAbsolutePath (io/file tmp-dir "Pug-Cookie.jpg"))) @@ -76,4 +77,15 @@ (let [img-arr (image/read-image image-path) resized-arr (image/resize-image img-arr 224 224) new-img (image/to-image resized-arr)] - (is (= true (ImageIO/write new-img "png" (io/file tmp-dir "out.png")))))) + (is (ImageIO/write new-img "png" (io/file tmp-dir "out.png"))))) + +(deftest test-draw-bounding-box! + (let [orig-img (ImageIO/read (new File image-path)) + new-img (-> orig-img + (image/draw-bounding-box! [{:x-min 190 :x-max 850 :y-min 50 :y-max 450} + {:x-min 200 :x-max 350 :y-min 440 :y-max 530}] + {:stroke 2 + :names ["pug" "cookie"] + :transparency 0.8 + :font-size-mult 2.0}))] + (is (ImageIO/write new-img "png" (io/file tmp-dir "out.png"))))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/infer/imageclassifier_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/infer/imageclassifier_test.clj index e3935c31e342..b7f468f341cd 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/infer/imageclassifier_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/infer/imageclassifier_test.clj @@ -48,7 +48,7 @@ (is (= 10 (count predictions-with-default-dtype))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) (deftest test-batch-classification (let [classifier (create-classifier) @@ -61,7 +61,7 @@ (is (= 10 (count batch-predictions-with-default-dtype))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) (deftest test-single-classification-with-ndarray (let [classifier (create-classifier) @@ -74,7 +74,7 @@ (is (= 1000 (count predictions-all))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) (deftest test-single-classify (let [classifier (create-classifier) @@ -87,7 +87,7 @@ (is (= 1000 (count predictions-all))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) (deftest test-base-classification-with-ndarray (let [descriptors [{:name "data" @@ -105,7 +105,7 @@ (is (= 1000 (count predictions-all))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) (deftest test-base-single-classify (let [descriptors [{:name "data" @@ -123,6 +123,6 @@ (is (= 1000 (count predictions-all))) (is (= 5 (count predictions))) (is (= "n02123159 tiger cat" (:class (first predictions)))) - (is (= (< 0 (:prob (first predictions)) 1))))) + (is (< 0 (:prob (first predictions)) 1)))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/module_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/module_test.clj index d53af2ec249d..e03c43848332 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/module_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/module_test.clj @@ -246,6 +246,7 @@ d-shape1 [10 3 64 64] d-shape2 [10 3 32 32] l-shape [10] + mod (m/module my-sym {:data-names ["data1" "data2"]}) data-batch {:data [(ndarray/random-uniform 0 9 (str (mx-shape/->shape d-shape1))) (ndarray/random-uniform 5 15 (str (mx-shape/->shape d-shape2)))] @@ -261,7 +262,12 @@ (m/init-params) (m/init-optimizer {:optimizer (optimizer/sgd {:learning-rate 0.1})}) (m/forward data-batch)) - (is (= [(first l-shape) num-class]) (-> (m/outputs-merged mod) first (ndarray/shape) (mx-shape/->vec))) + (is (= [(first l-shape) num-class] + (-> mod + (m/outputs-merged) + (first) + (ndarray/shape) + (mx-shape/->vec)))) (-> mod (m/backward) (m/update)) @@ -275,8 +281,13 @@ :index nil :pad 0}] (-> mod - (m/forward data-batch)) - (is (= [(first l-shape) num-class]) (-> (m/outputs-merged mod) first (ndarray/shape) (mx-shape/->vec))) + (m/forward data-batch-2)) + (is (= [(first l-shape) num-class] + (-> mod + (m/outputs-merged) + (first) + (ndarray/shape) + (mx-shape/->vec)))) (-> mod (m/backward) (m/update))) @@ -290,8 +301,13 @@ :index nil :pad 0}] (-> mod - (m/forward data-batch)) - (is (= [(first l-shape) num-class]) (-> (m/outputs-merged mod) first (ndarray/shape) (mx-shape/->vec))) + (m/forward data-batch-2)) + (is (= [(first l-shape) num-class] + (-> mod + (m/outputs-merged) + (first) + (ndarray/shape) + (mx-shape/->vec)))) (-> mod (m/backward) (m/update))) @@ -307,7 +323,11 @@ :pad 0}] (-> mod (m/forward data-batch)) - (is (= [(first l-shape) num-class]) (-> (m/outputs-merged mod) first (ndarray/shape) (mx-shape/->vec))) + (is (= [(first l-shape) num-class] + (-> (m/outputs-merged mod) + first + (ndarray/shape) + (mx-shape/->vec)))) (-> mod (m/backward) (m/update))) @@ -321,7 +341,11 @@ :pad 0}] (-> mod (m/forward data-batch)) - (is (= [(first l-shape) num-class]) (-> (m/outputs-merged mod) first (ndarray/shape) (mx-shape/->vec))) + (is (= [(first l-shape) num-class] + (-> (m/outputs-merged mod) + first + (ndarray/shape) + (mx-shape/->vec)))) (-> mod (m/backward) (m/update))))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_api_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_api_test.clj new file mode 100644 index 000000000000..18b8b78f19d1 --- /dev/null +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_api_test.clj @@ -0,0 +1,415 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +(ns org.apache.clojure-mxnet.ndarray-api-test + (:require [org.apache.clojure-mxnet.base :as base] + [org.apache.clojure-mxnet.context :as ctx] + [org.apache.clojure-mxnet.dtype :as dtype] + [org.apache.clojure-mxnet.ndarray :as ndarray :refer [->vec zeros ones += -= *= full shape shape-vec]] + [org.apache.clojure-mxnet.ndarray-api :as ndarray-api] + [org.apache.clojure-mxnet.shape :as mx-shape :refer [->shape]] + [org.apache.clojure-mxnet.test-util :as test-util :refer [approx=]] + [org.apache.clojure-mxnet.util :as util :refer [->option]] + [clojure.test :refer :all])) + +(deftest test-activation + (let [data (ndarray/array [2 1 0 -1 -2] [1 5]) + relu (ndarray-api/activation data "relu") + sigmoid (ndarray-api/activation data "sigmoid") + softsign (ndarray-api/activation data "softsign") + out (ndarray/zeros [1 5]) + _ (ndarray-api/activation {:data data :act-type "relu" :out out})] + (is (= [2.0 1.0 0.0 0.0 0.0] (->vec relu))) + (is (approx= 1e-3 [0.881 0.731 0.5 0.269 0.119] (->vec sigmoid))) + (is (approx= 1e-3 [0.666 0.5 0.0 -0.5 -0.666] (->vec softsign))) + (is (= [2.0 1.0 0.0 0.0 0.0] (->vec out))))) + +(deftest test-bilinear-sampler + (let [data (ndarray/array [1 4 3 6 + 1 8 8 9 + 0 4 1 5 + 1 0 1 3] + [1 1 4 4]) + affine (ndarray/array [2 0 0 + 0 2 0] + [1 6]) + grid (ndarray-api/grid-generator {:data affine :transform-type "affine" :target-shape [4 4]}) + out (ndarray-api/bilinear-sampler data grid)] + (is (approx= 1e-3 + [0.0 0.0 0.0 0.0 + 0.0 3.5 6.5 0.0 + 0.0 1.25 2.5 0.0 + 0.0 0.0 0.0 0.0] + (->vec out))))) + +(deftest test-cast + (let [nda1 (ndarray/array [0.9 1.3] [2]) + nda2 (ndarray/array [1e20 11.1] [2]) + nda3 (ndarray/array [300 11.1 10.9 -1 -3] [5]) + out (ndarray/zeros [2] {:dtype dtype/INT32}) + _ (ndarray-api/cast {:data nda1 :dtype (str dtype/INT32) :out out})] + (is (= [0.0 1.0] (->vec (ndarray-api/cast nda1 (str dtype/INT32))))) + (is (= [(float 1e20) (float 11.1)] (->vec (ndarray-api/cast nda2 (str dtype/FLOAT32))))) + ;; uint8 gets converted to native types after ->vec + (is (= [44.0 11.0 10.0 -1.0 -3.0] (->vec (ndarray-api/cast nda3 "uint8")))))) + +(deftest test-concat + (let [nda1 (ndarray/zeros [1 2]) + nda2 (ndarray/ones [1 2]) + out (ndarray/zeros [1 4]) + res1 (ndarray-api/concat [nda1 nda2] 2) ;; num_args=2, dim=1 (default) + res2 (ndarray-api/concat {:data [nda1 nda2] :num-args 2 :dim 0}) ;; num_args=2, dim=0 + res3 (ndarray-api/concat {:data [nda1 nda2 nda1] :num-args 3 :dim 1}) ;; num_args=3, dim=1 + _ (ndarray-api/concat {:data [nda1 nda2] :num-args 2 :dim 1 :out out}) ;; store result in out + ] + (is (= [0.0 0.0 1.0 1.0] (->vec res1))) + (is (= [1 4] (shape-vec res1))) + (is (= [0.0 0.0 1.0 1.0] (->vec res2))) + (is (= [2 2] (shape-vec res2))) + (is (= [0.0 0.0 1.0 1.0 0.0 0.0] (->vec res3))) + (is (= [1 6] (shape-vec res3))) + (is (= [0.0 0.0 1.0 1.0] (->vec out))) + (is (= [1 4] (shape-vec out))))) + +(deftest test-embedding + (let [input-dim 4 + output-dim 5 + w (ndarray/array [0. 1. 2. 3. 4. + 5. 6. 7. 8. 9. + 10. 11. 12. 13. 14. + 15. 16. 17. 18. 19.] + [4 5]) + x (ndarray/array [1. 3. + 0. 2.] + [2 2]) + out (ndarray-api/embedding x w input-dim output-dim)] + (is (= [5. 6. 7. 8. 9. + 15. 16. 17. 18. 19. + 0. 1. 2. 3. 4. + 10. 11. 12. 13. 14.] + (->vec out))) + (is (= [2 2 5] (shape-vec out))))) + +(deftest test-flatten + (let [nda (ndarray/array [1 2 3 + 4 5 6 + 7 8 9 + 1 2 3 + 4 5 6 + 7 8 9] + [2 3 3]) + out (ndarray/zeros [2 9]) + res (ndarray-api/flatten {:data nda}) + _ (ndarray-api/flatten {:data nda :out out})] + (is (= [1. 2. 3. 4. 5. 6. 7. 8. 9. + 1. 2. 3. 4. 5. 6. 7. 8. 9.] (->vec res))) + (is (= [2 9] (shape-vec res))) + (is (= [1. 2. 3. 4. 5. 6. 7. 8. 9. + 1. 2. 3. 4. 5. 6. 7. 8. 9.] (->vec out))) + (is (= [2 9] (shape-vec out))))) + +(deftest test-instance-norm + (let [x (ndarray/array [1.1 2.2 3.3 4.4] [2 1 2]) + gamma (ndarray/array [1.5] [1]) + beta (ndarray/array [0.5] [1]) + res (ndarray-api/instance-norm x gamma beta)] + (is (approx= 1e-4 [-0.9975 1.9975 + -0.9975 1.9975] (->vec res))) + (is (= [2 1 2] (shape-vec res))))) + +(deftest test-l2-normalization + (let [x (ndarray/array [1 2 3 4 2 2 5 6] [2 2 2]) + res1 (ndarray-api/l2-normalization {:data x}) ;; instance-wise + res2 (ndarray-api/l2-normalization {:data x :mode "instance"}) + res3 (ndarray-api/l2-normalization {:data x :mode "channel"}) + res4 (ndarray-api/l2-normalization {:data x :mode "spatial"})] + (is (approx= 1e-4 [0.1825 0.3651 + 0.5477 0.7303 + 0.2407 0.2407 + 0.6019 0.7223] (->vec res1))) + (is (approx= 1e-4 [0.1825 0.3651 + 0.5477 0.7303 + 0.2407 0.2407 + 0.6019 0.7223] (->vec res2))) + (is (approx= 1e-4 [0.3162 0.4472 + 0.9486 0.8944 + 0.3714 0.3162 + 0.9284 0.9486] (->vec res3))) + (is (approx= 1e-4 [0.4472 0.8944 + 0.6 0.8 + 0.7071 0.7071 + 0.6402 0.7682] (->vec res4))))) + +(deftest test-pad + (let [x (ndarray/array [1 2 3 + 4 5 6 + 7 8 9 + 10 11 12 + 11 12 13 + 14 15 16 + 17 18 19 + 20 21 22] + [2 2 2 3]) + res1 (ndarray-api/pad x "edge" [0,0,0,0,1,1,1,1]) + res2 (ndarray-api/pad {:data x :mode "constant" :pad-width [0,0,0,0,1,1,1,1] :constant-value 0})] + (is (= [1. 1. 2. 3. 3. + 1. 1. 2. 3. 3. + 4. 4. 5. 6. 6. + 4. 4. 5. 6. 6. + 7. 7. 8. 9. 9. + 7. 7. 8. 9. 9. + 10. 10. 11. 12. 12. + 10. 10. 11. 12. 12. + 11. 11. 12. 13. 13. + 11. 11. 12. 13. 13. + 14. 14. 15. 16. 16. + 14. 14. 15. 16. 16. + 17. 17. 18. 19. 19. + 17. 17. 18. 19. 19. + 20. 20. 21. 22. 22. + 20. 20. 21. 22. 22.] (->vec res1))) + (is (= [2 2 4 5] (shape-vec res1))) + (is (= [0. 0. 0. 0. 0. + 0. 1. 2. 3. 0. + 0. 4. 5. 6. 0. + 0. 0. 0. 0. 0. + + 0. 0. 0. 0. 0. + 0. 7. 8. 9. 0. + 0. 10. 11. 12. 0. + 0. 0. 0. 0. 0. + + 0. 0. 0. 0. 0. + 0. 11. 12. 13. 0. + 0. 14. 15. 16. 0. + 0. 0. 0. 0. 0. + + 0. 0. 0. 0. 0. + 0. 17. 18. 19. 0. + 0. 20. 21. 22. 0. + 0. 0. 0. 0. 0.] (->vec res2))) + (is (= [2 2 4 5] (shape-vec res2))))) + +(deftest test-roi-pooling + (let [xi [[[[ 0., 1., 2., 3., 4., 5.], + [ 6., 7., 8., 9., 10., 11.], + [ 12., 13., 14., 15., 16., 17.], + [ 18., 19., 20., 21., 22., 23.], + [ 24., 25., 26., 27., 28., 29.], + [ 30., 31., 32., 33., 34., 35.], + [ 36., 37., 38., 39., 40., 41.], + [ 42., 43., 44., 45., 46., 47.]]]] + x (ndarray/array (-> xi flatten vec) [1 1 8 6]) + y (ndarray/array [0 0 0 4 4] [1 5]) + res1 (ndarray-api/roi-pooling x y [2 2] 1.0) + res2 (ndarray-api/roi-pooling x y [2 2] 0.7)] + (is (= [14. 16. 26. 28.] (->vec res1))) + (is (= [1 1 2 2] (shape-vec res1))) + (is (= [7. 9. 19. 21.] (->vec res2))) + (is (= [1 1 2 2] (shape-vec res2))))) + +(deftest test-reshape + (let [x (ndarray/array (vec (range 4)) [4]) + y (ndarray/array (vec (range 24)) [2 3 4]) + z (ndarray/array (vec (range 120)) [2 3 4 5]) + res1 (ndarray-api/reshape {:data x :shape [2 2]})] + (is (= [0. 1. 2. 3.] (->vec res1))) + (is (= [2 2] (shape-vec res1))) + (is (= (map float (range 24)) (->vec (ndarray-api/reshape {:data y :shape [4 0 2]})))) + (is (= [4 3 2] (shape-vec (ndarray-api/reshape {:data y :shape [4 0 2]})))) + (is (= [2 3 4] (shape-vec (ndarray-api/reshape {:data y :shape [2 0 0]})))) + (is (= [6 1 4] (shape-vec (ndarray-api/reshape {:data y :shape [6 1 -1]})))) + (is (= [3 1 8] (shape-vec (ndarray-api/reshape {:data y :shape [3 -1 8]})))) + (is (= [24] (shape-vec (ndarray-api/reshape {:data y :shape [-1]})))) + (is (= [2 3 4] (shape-vec (ndarray-api/reshape {:data y :shape [-2]})))) + (is (= [2 3 4] (shape-vec (ndarray-api/reshape {:data y :shape [2 -2]})))) + (is (= [2 3 4 1 1] (shape-vec (ndarray-api/reshape {:data y :shape [-2 1 1]})))) + (is (= [6 4] (shape-vec (ndarray-api/reshape {:data y :shape [-3 4]})))) + (is (= [6 20] (shape-vec (ndarray-api/reshape {:data z :shape [-3 -3]})))) + (is (= [2 12] (shape-vec (ndarray-api/reshape {:data y :shape [0 -3]})))) + (is (= [6 4] (shape-vec (ndarray-api/reshape {:data y :shape [-3 -2]})))) + (is (= [1 2 3 4] (shape-vec (ndarray-api/reshape {:data y :shape [-4 1 2 -2]})))) + (is (= [2 1 3 4] (shape-vec (ndarray-api/reshape {:data y :shape [2 -4 -1 3 -2]})))))) + +(deftest test-sequence-last + (let [xi [[[ 1., 2., 3.], + [ 4., 5., 6.], + [ 7., 8., 9.]], + + [[ 10., 11., 12.], + [ 13., 14., 15.], + [ 16., 17., 18.]], + + [[ 19., 20., 21.], + [ 22., 23., 24.], + [ 25., 26., 27.]]] + x (ndarray/array (-> xi flatten vec) [3 3 3]) + seq-len1 (ndarray/array [1 1 1] [3]) + seq-len2 (ndarray/array [1 2 3] [3]) + ;; This test is failing with an exception + ;; (most likely a scala generation issue) + ;; res1 (ndarray-api/sequence-last x nil) + ] + ;; (is (= [] (->vec res1))) +)) + +(deftest test-sequence-mask + (let [xi [[[ 1., 2., 3.], + [ 4., 5., 6.]], + + [[ 7., 8., 9.], + [ 10., 11., 12.]], + + [[ 13., 14., 15.], + [ 16., 17., 18.]]] + x (ndarray/array (-> xi flatten vec) [3 2 3]) + seq-len1 (ndarray/array [1 1] [2]) + seq-len2 (ndarray/array [2 3] [2]) + ;; Same issue as previous test + ;; res1 (ndarray-api/sequence-mask x seq-len1) + ] + ;; (is (= [] (->vec res1))) +)) + +(deftest test-slice-channel + (let [xi [[[ 1.] [ 2.]] + [[ 3.] [ 4.]] + [[ 5.] [ 6.]]] + x (ndarray/array (-> xi flatten vec) [3 2 1]) + res1 (ndarray-api/slice-channel {:data x :num-outputs 2 :axis 1}) + res2 (ndarray-api/slice-channel {:data x :num-outputs 3 :axis 0}) + res3 (ndarray-api/slice-channel {:data x :num-outputs 3 :axis 0 :squeeze-axis 1})] + (is (= [1. 3. 5.] (->vec res1))) + (is (= [3 1 1] (shape-vec res1))) + (is (= [1. 2.] (->vec res2))) + (is (= [1 2 1] (shape-vec res2))) + (is (= [1. 2.] (->vec res3))) + (is (= [2 1] (shape-vec res3))))) + +(deftest test-softmax-activation + (let [x (ndarray/array [1 1 1 1 1 1] [2 3]) + res1 (ndarray-api/softmax-activation {:data x :mode "instance"})] + (is (approx= 1e-3 [0.333 0.333 0.333 + 0.333 0.333 0.333] (->vec res1))) + (is (= [2 3] (shape-vec res1))))) + +(deftest test-softmax-output + (let [datai [[1,2,3,4],[2,2,2,2],[3,3,3,3],[4,4,4,4]] + data (ndarray/array (-> datai flatten vec) [4 4]) + label (ndarray/array [1,0,2,3] [4]) + res1 (ndarray-api/softmax-output data label)] + (is (approx= 1e-4 [0.0321 0.0871 0.2369 0.6439 + 0.25 0.25 0.25 0.25 + 0.25 0.25 0.25 0.25 + 0.25 0.25 0.25 0.25] (->vec res1))) + (is (= [4 4] (shape-vec res1))))) + +(deftest test-swap-axis + (let [x (ndarray/array (range 3) [1 3]) + y (ndarray/array (range 8) [2 2 2]) + res1 (ndarray-api/swap-axis {:data x :dim1 0 :dim2 1}) + res2 (ndarray-api/swap-axis {:data y :dim1 0 :dim2 2})] + (is (= [0. 1. 2.] (->vec res1))) + (is (= [3 1] (shape-vec res1))) + (is (= [0. 4. 2. 6. 1. 5. 3. 7.] (->vec res2))) + (is (= [2 2 2] (shape-vec res2))))) + +(deftest test-abs + (let [x (ndarray/array [-2 0 3] [3]) + res1 (ndarray-api/abs {:data x})] + (is (= [2. 0. 3.] (->vec res1))) + (is (= [3] (shape-vec res1))))) + +(deftest test-arccos + (let [x (ndarray/array [-1 -0.707 0 0.707 1] [5]) + pi Math/PI + res1 (ndarray-api/arccos {:data x})] + (is (approx= 1e-3 [pi (* 0.75 pi) (* 0.5 pi) (* 0.25 pi) 0.] (->vec res1))))) + +(deftest test-arcsin + (let [x (ndarray/array [-1 -0.707 0 0.707 1] [5]) + pi Math/PI + res1 (ndarray-api/arcsin {:data x})] + (is (approx= 1e-3 [(- (* 0.5 pi)) (- (* 0.25 pi)) 0 (* 0.25 pi) (* 0.5 pi)] (->vec res1))))) + +(deftest test-argmax + (let [x (ndarray/array (range 6) [2 3]) + res1 (ndarray-api/argmax {:data x :axis 0}) + res2 (ndarray-api/argmax {:data x :axis 1}) + res3 (ndarray-api/argmax {:data x :axis 0 :keepdims true}) + res4 (ndarray-api/argmax {:data x :axis 1 :keepdims true})] + (is (= [1. 1. 1.] (->vec res1))) + (is (= [3] (shape-vec res1))) + (is (= [2. 2.] (->vec res2))) + (is (= [2] (shape-vec res2))) + (is (= [1. 1. 1.] (->vec res3))) + (is (= [1 3] (shape-vec res3))) + (is (= [2. 2.] (->vec res4))) + (is (= [2 1] (shape-vec res4))))) + +(deftest test-argmax-channel + (let [x (ndarray/array (range 6) [2 3]) + res1 (ndarray-api/argmax-channel {:data x})] + (is (= [2. 2.] (->vec res1))) + (is (= [2] (shape-vec res1))))) + +(deftest test-argmin + (let [x (ndarray/array (reverse (range 6)) [2 3]) + res1 (ndarray-api/argmin {:data x :axis 0}) + res2 (ndarray-api/argmin {:data x :axis 1}) + res3 (ndarray-api/argmin {:data x :axis 0 :keepdims true}) + res4 (ndarray-api/argmin {:data x :axis 1 :keepdims true})] + (is (= [1. 1. 1.] (->vec res1))) + (is (= [3] (shape-vec res1))) + (is (= [2. 2.] (->vec res2))) + (is (= [2] (shape-vec res2))) + (is (= [1. 1. 1.] (->vec res3))) + (is (= [1 3] (shape-vec res3))) + (is (= [2. 2.] (->vec res4))) + (is (= [2 1] (shape-vec res4))))) + +(deftest test-argsort + (let [x (ndarray/array [0.3 0.2 0.4 + 0.1 0.3 0.2] + [2 3]) + y (ndarray/array [0.3 0.2 0.4 0.1 0.3 0.2] [6]) + res1 (ndarray-api/argsort {:data x}) + res2 (ndarray-api/argsort {:data x :axis 0}) + res3 (ndarray-api/argsort {:data y})] + (is (= [1. 0. 2. + 0. 2. 1.] + (->vec res1))) + (is (= [2 3] (shape-vec res1))) + (is (= [1. 0. 1. + 0. 1. 0.] + (->vec res2))) + (is (= [2 3] (shape-vec res1))) + (is (= [3. 1. 5. 0. 4. 2.] (->vec res3))) + (is (= [6] (shape-vec res3))))) + +(deftest test-batch-take + (let [x (ndarray/array (range 6) [3 2]) + i (ndarray/as-type (ndarray/array [0 1 0] [3]) dtype/INT32) + res1 (ndarray-api/batch-take x i) ] + (is (= [0. 3. 4.] (->vec res1))))) + +(deftest test-broadcast-add + (let [x (ndarray/ones [2 3]) + y (ndarray/array (range 2) [2 1]) + res1 (ndarray-api/broadcast-add x y)] + (is (= [1. 1. 1. 2. 2. 2.] (->vec res1))) + (is (= [2 3] (shape-vec res1))))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_test.clj index 9ffd3abed2f9..13209e609a1d 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/ndarray_test.clj @@ -28,7 +28,7 @@ (is (= [0.0 0.0 0.0 0.0] (->vec (zeros [2 2]))))) (deftest test-to-array - (is (= [0.0 0.0 0.0 0.0]) (vec (ndarray/to-array (zeros [2 2]))))) + (is (= [0.0 0.0 0.0 0.0] (vec (ndarray/to-array (zeros [2 2])))))) (deftest test-to-scalar (is (= 0.0 (ndarray/to-scalar (zeros [1])))) @@ -61,8 +61,8 @@ (is (= [2.0 2.0] (->vec (ndarray/+ ndones 1)))) (is (= [1.0 1.0] (->vec ndones))) ;;; += mutuates - (is (= [2.0 2.0]) (->vec (+= ndones 1))) - (is (= [2.0 2.0]) (->vec ndones)))) + (is (= [2.0 2.0] (->vec (+= ndones 1)))) + (is (= [2.0 2.0] (->vec ndones))))) (deftest test-minus (let [ndones (ones [2 1]) @@ -71,8 +71,8 @@ (is (= [-1.0 -1.0] (->vec (ndarray/- ndzeros 1)))) (is (= [0.0 0.0] (->vec ndzeros))) ;;; += mutuates - (is (= [-1.0 -1.0]) (->vec (-= ndzeros 1))) - (is (= [-1.0 -1.0]) (->vec ndzeros)))) + (is (= [-1.0 -1.0] (->vec (-= ndzeros 1)))) + (is (= [-1.0 -1.0] (->vec ndzeros))))) (deftest test-multiplication (let [ndones (ones [2 1]) @@ -146,6 +146,18 @@ (is (= [0.0 0.0 0.5 0.5 1.0 1.0 1.5 1.5 2.0 2.0 2.5 2.5 3.0 3.0 3.5 3.5 4.0 4.0 4.5 4.5] (->vec (ndarray/arange start stop {:step step :repeat repeat})))))) +(deftest test->ndarray + (let [nda1 (ndarray/->ndarray [5.0 -4.0]) + nda2 (ndarray/->ndarray [[1 2 3] + [4 5 6]]) + nda3 (ndarray/->ndarray [[[7.0] [8.0]]])] + (is (= [5.0 -4.0] (->vec nda1))) + (is (= [2] (mx-shape/->vec (shape nda1)))) + (is (= [1.0 2.0 3.0 4.0 5.0 6.0] (->vec nda2))) + (is (= [2 3] (mx-shape/->vec (shape nda2)))) + (is (= [7.0 8.0] (->vec nda3))) + (is (= [1 2 1] (mx-shape/->vec (shape nda3)))))) + (deftest test-power (let [nda (ndarray/array [3 5] [2 1])] @@ -396,7 +408,7 @@ (let [nda (ndarray/array [1 2 3 4 5 6] [3 2]) res (ndarray/at nda 1)] (is (= [2] (-> res shape mx-shape/->vec))) - (is (= [3 4])))) + (is (= [3 4] (-> res ndarray/->int-vec))))) (deftest test-reshape (let [nda (ndarray/array [1 2 3 4 5 6] [3 2]) @@ -473,3 +485,15 @@ (is (= [2 2] (ndarray/->int-vec nda))) (is (= [2.0 2.0] (ndarray/->double-vec nda))) (is (= [(byte 2) (byte 2)] (ndarray/->byte-vec nda))))) + +(deftest test->nd-vec + (is (= [[[1.0]]] + (ndarray/->nd-vec (ndarray/array [1] [1 1 1])))) + (is (= [[[1.0]] [[2.0]] [[3.0]]] + (ndarray/->nd-vec (ndarray/array [1 2 3] [3 1 1])))) + (is (= [[[1.0 2.0]] [[3.0 4.0]] [[5.0 6.0]]] + (ndarray/->nd-vec (ndarray/array [1 2 3 4 5 6] [3 1 2])))) + (is (= [[[1.0] [2.0]] [[3.0] [4.0]] [[5.0] [6.0]]] + (ndarray/->nd-vec (ndarray/array [1 2 3 4 5 6] [3 2 1])))) + (is (thrown-with-msg? Exception #"Invalid input array" + (ndarray/->nd-vec [1 2 3 4 5])))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/operator_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/operator_test.clj index 3b97190854b4..5e1b127d18bd 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/operator_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/operator_test.clj @@ -264,9 +264,9 @@ _ (executor/set-arg exec "datas" data-vec) output (-> (executor/forward exec) (executor/outputs) first)] (is (approx= 1e-5 expected output)) - (is (= [0 0 0 0]) (-> (executor/backward exec (ndarray/ones shape-vec)) + (is (= [0 0 0 0] (-> (executor/backward exec (ndarray/ones shape-vec)) (executor/get-grad "datas") - (ndarray/->vec))))) + (ndarray/->int-vec)))))) (defn check-symbol-operation [operator data-vec-1 data-vec-2 expected] @@ -280,8 +280,8 @@ output (-> (executor/forward exec) (executor/outputs) first)] (is (approx= 1e-5 expected output)) _ (executor/backward exec (ndarray/ones shape-vec)) - (is (= [0 0 0 0]) (-> (executor/get-grad exec "datas") (ndarray/->vec))) - (is (= [0 0 0 0]) (-> (executor/get-grad exec "datas2") (ndarray/->vec))))) + (is (= [0 0 0 0] (-> (executor/get-grad exec "datas") (ndarray/->int-vec)))) + (is (= [0 0 0 0] (-> (executor/get-grad exec "datas2") (ndarray/->int-vec)))))) (defn check-scalar-2-operation [operator data-vec expected] @@ -292,9 +292,9 @@ _ (executor/set-arg exec "datas" data-vec) output (-> (executor/forward exec) (executor/outputs) first)] (is (approx= 1e-5 expected output)) - (is (= [0 0 0 0]) (-> (executor/backward exec (ndarray/ones shape-vec)) + (is (= [0 0 0 0] (-> (executor/backward exec (ndarray/ones shape-vec)) (executor/get-grad "datas") - (ndarray/->vec))))) + (ndarray/->int-vec)))))) (deftest test-scalar-equal (check-scalar-operation sym/equal [1 2 3 4] 2 [0 1 0 0])) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/random_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/random_test.clj index 6952335c1390..ca1dcc9430dc 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/random_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/random_test.clj @@ -56,6 +56,8 @@ (is (thrown? Exception (fn_ 'a 2 []))) (is (thrown? Exception (fn_ 1 'b []))) (is (thrown? Exception (fn_ 1 2 [-1]))) + (is (thrown? Exception (fn_ 1 0 [1 2]))) + (is (thrown? Exception (fn_ 1 -1 [1 2]))) (is (thrown? Exception (fn_ 1 2 [2 3 0]))) (is (thrown? Exception (fn_ 1 2 [10 10] {:ctx "a"}))) (let [ctx (context/default-context)] @@ -64,4 +66,4 @@ (deftest test-random-parameters-specs (random-or-normal random/normal) (random-or-normal random/uniform) - (is (thrown? Exception (random/seed "a")))) \ No newline at end of file + (is (thrown? Exception (random/seed "a")))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_api_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_api_test.clj new file mode 100644 index 000000000000..b642ad75d1d0 --- /dev/null +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_api_test.clj @@ -0,0 +1,61 @@ +;; +;; Licensed to the Apache Software Foundation (ASF) under one or more +;; contributor license agreements. See the NOTICE file distributed with +;; this work for additional information regarding copyright ownership. +;; The ASF licenses this file to You under the Apache License, Version 2.0 +;; (the "License"); you may not use this file except in compliance with +;; the License. You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; + +(ns org.apache.clojure-mxnet.symbol-api-test + (:require [org.apache.clojure-mxnet.dtype :as dtype] + [org.apache.clojure-mxnet.executor :as executor] + [org.apache.clojure-mxnet.ndarray :as ndarray] + [org.apache.clojure-mxnet.symbol :as sym] + [org.apache.clojure-mxnet.symbol-api :as sym-api] + [org.apache.clojure-mxnet.util :as util] + [clojure.test :refer :all] + [org.apache.clojure-mxnet.context :as context])) + +(deftest test-compose + (let [data (sym/variable "data") + net1 (sym-api/fully-connected {:data data :num-hidden 10 :name "fc1"}) + net1 (sym-api/fully-connected {:data net1 :num-hidden 100 :name "fc2"} ) + + net2 (sym-api/fully-connected {:num-hidden 10 :name "fc3"}) + net2 (sym-api/activation {:data net2 :act-type "relu"}) + net2 (sym-api/fully-connected {:data net2 :num-hidden 20 :name "fc4"}) + + composed (sym/apply net2 "composed" {"fc3_data" net1}) + + multi-out (sym/group [composed net1])] + + (is (= ["data" "fc1_weight" "fc1_bias" "fc2_weight" "fc2_bias"] (sym/list-arguments net1))) + (is (= 2 (count (sym/list-outputs multi-out)))))) + +(deftest test-symbol-internal + (let [data (sym/variable "data") + oldfc (sym-api/fully-connected {:data data :num-hidden 10 :name"fc1"}) + net1 (sym-api/fully-connected {:data oldfc :num-hidden 100 :name"fc2"})] + (is (= ["data" "fc1_weight" "fc1_bias" "fc2_weight" "fc2_bias"] (sym/list-arguments net1))) + (= (sym/list-arguments oldfc) (-> (sym/get-internals net1) + (sym/get "fc1_output") + (sym/list-arguments))))) + +(deftest test-infer-type + (let [data (sym/variable "data") + f32data (sym-api/cast {:data data :dtype "float32"}) + fc1 (sym-api/fully-connected {:data f32data :num-hidden 128 :name"fc1"}) + mlp (sym-api/softmax-output {:data fc1 :name"softmax"}) + [arg out aux] (sym/infer-type mlp {:data dtype/FLOAT64})] + (is (= [dtype/FLOAT64 dtype/FLOAT32 dtype/FLOAT32 dtype/FLOAT32] (util/buffer->vec arg))) + (is (= [dtype/FLOAT32] (util/buffer->vec out))) + (is (= [] (util/buffer->vec aux))))) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_test.clj index 89b51237d3a5..4d1b493ab2b6 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/symbol_test.clj @@ -57,7 +57,7 @@ mlp (sym/softmax-output "softmax" {:data fc1}) [arg out aux] (sym/infer-type mlp {:data dtype/FLOAT64})] (is (= [dtype/FLOAT64 dtype/FLOAT32 dtype/FLOAT32 dtype/FLOAT32] (util/buffer->vec arg))) - (is (= [dtype/FLOAT32 (util/buffer->vec out)])) + (is (= [dtype/FLOAT32] (util/buffer->vec out))) (is (= [] (util/buffer->vec aux))))) (deftest test-copy @@ -70,10 +70,10 @@ b (sym/variable "b") c (sym/+ a b) ex (sym/bind c {"a" (ndarray/ones [2 2]) "b" (ndarray/ones [2 2])})] - (is (= [2.0 2.0 2.0 2.0]) (-> (executor/forward ex) - (executor/outputs) - (first) - (ndarray/->vec))))) + (is (= [2.0 2.0 2.0 2.0] (-> (executor/forward ex) + (executor/outputs) + (first) + (ndarray/->vec)))))) (deftest test-simple-bind (let [a (sym/ones [3]) b (sym/ones [3]) diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/util_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/util_test.clj index 4ed7d38e690a..6652b68a4830 100644 --- a/contrib/clojure-package/test/org/apache/clojure_mxnet/util_test.clj +++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/util_test.clj @@ -70,8 +70,8 @@ (util/option->value))))) (deftest test-keyword->snake-case - (is (= [:foo-bar :foo2 :bar-bar]) - (util/keyword->snake-case [:foo_bar :foo2 :bar-bar]))) + (is (= ["foo_bar" "foo2" "bar_bar"] + (mapv util/keyword->snake-case [:foo_bar :foo2 :bar-bar])))) (deftest test-convert-tuple (is (instance? Tuple1 (util/convert-tuple [1]))) @@ -163,6 +163,33 @@ (is (= [1 2] (-> (util/convert-tuple [1 2]) (util/tuple->vec))))) +(deftest test-to-array-nd + (let [a1 (util/to-array-nd '(1)) + a2 (util/to-array-nd [1.0 2.0]) + a3 (util/to-array-nd [[3.0] [4.0]]) + a4 (util/to-array-nd [[[5 -5]]])] + (is (= 1 (alength a1))) + (is (= [1] (->> a1 vec))) + (is (= 2 (alength a2))) + (is (= 2.0 (aget a2 1))) + (is (= [1.0 2.0] (->> a2 vec))) + (is (= 2 (alength a3))) + (is (= 1 (alength (aget a3 0)))) + (is (= 4.0 (aget a3 1 0))) + (is (= [[3.0] [4.0]] (->> a3 vec (mapv vec)))) + (is (= 1 (alength a4))) + (is (= 1 (alength (aget a4 0)))) + (is (= 2 (alength (aget a4 0 0)))) + (is (= 5 (aget a4 0 0 0))) + (is (= [[[5 -5]]] (->> a4 vec (mapv vec) (mapv #(mapv vec %))))))) + +(deftest test-nd-seq-shape + (is (= [1] (util/nd-seq-shape '(5)))) + (is (= [2] (util/nd-seq-shape [1.0 2.0]))) + (is (= [3] (util/nd-seq-shape [1 1 1]))) + (is (= [2 1] (util/nd-seq-shape [[3.0] [4.0]]))) + (is (= [1 3 2] (util/nd-seq-shape [[[5 -5] [5 -5] [5 -5]]])))) + (deftest test-coerce-return (is (= [] (util/coerce-return (ArrayBuffer.)))) (is (= [1 2 3] (util/coerce-return (util/vec->indexed-seq [1 2 3])))) diff --git a/cpp-package/CMakeLists.txt b/cpp-package/CMakeLists.txt index 5d2977279d74..fec86e78e6b8 100644 --- a/cpp-package/CMakeLists.txt +++ b/cpp-package/CMakeLists.txt @@ -16,7 +16,7 @@ if(USE_CPP_PACKAGE) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scripts ) - if(NOT DO_NOT_BUILD_EXAMPLES) + if(BUILD_CPP_EXAMPLES) add_subdirectory(example) endif() diff --git a/cpp-package/example/alexnet.cpp b/cpp-package/example/alexnet.cpp index e2083a0dfa0a..21029da4678b 100644 --- a/cpp-package/example/alexnet.cpp +++ b/cpp-package/example/alexnet.cpp @@ -196,19 +196,40 @@ Symbol AlexnetSymbol(int num_classes) { return softmax; } +NDArray ResizeInput(NDArray data, const Shape new_shape) { + NDArray pic = data.Reshape(Shape(0, 1, 28, 28)); + NDArray pic_1channel; + Operator("_contrib_BilinearResize2D") + .SetParam("height", new_shape[2]) + .SetParam("width", new_shape[3]) + (pic).Invoke(pic_1channel); + NDArray output; + Operator("tile") + .SetParam("reps", Shape(1, 3, 1, 1)) + (pic_1channel).Invoke(output); + return output; +} + int main(int argc, char const *argv[]) { /*basic config*/ - int batch_size = 256; int max_epo = argc > 1 ? strtol(argv[1], NULL, 10) : 100; float learning_rate = 1e-4; float weight_decay = 1e-4; - /*context and net symbol*/ - auto ctx = Context::gpu(); -#if MXNET_USE_CPU - ctx = Context::cpu(); + /*context*/ + auto ctx = Context::cpu(); + int num_gpu; + MXGetGPUCount(&num_gpu); + int batch_size = 32; +#if !MXNET_USE_CPU + if (num_gpu > 0) { + ctx = Context::gpu(); + batch_size = 256; + } #endif + TRY + /*net symbol*/ auto Net = AlexnetSymbol(10); /*args_map and aux_map is used for parameters' saving*/ @@ -216,8 +237,10 @@ int main(int argc, char const *argv[]) { std::map aux_map; /*we should tell mxnet the shape of data and label*/ - args_map["data"] = NDArray(Shape(batch_size, 3, 256, 256), ctx); - args_map["label"] = NDArray(Shape(batch_size), ctx); + const Shape data_shape = Shape(batch_size, 3, 256, 256), + label_shape = Shape(batch_size); + args_map["data"] = NDArray(data_shape, ctx); + args_map["label"] = NDArray(label_shape, ctx); /*with data and label, executor can be generated automatically*/ auto *exec = Net.SimpleBind(ctx, args_map); @@ -261,17 +284,18 @@ int main(int argc, char const *argv[]) { ->SetParam("wd", weight_decay); Accuracy acu_train, acu_val; - LogLoss logloss_val; - for (int iter = 0; iter < max_epo; ++iter) { - LG << "Train Epoch: " << iter; + LogLoss logloss_train, logloss_val; + for (int epoch = 0; epoch < max_epo; ++epoch) { + LG << "Train Epoch: " << epoch; /*reset the metric every epoch*/ acu_train.Reset(); /*reset the data iter every epoch*/ train_iter.Reset(); + int iter = 0; while (train_iter.Next()) { auto batch = train_iter.GetDataBatch(); /*use copyto to feed new data and label to the executor*/ - batch.data.CopyTo(&args_map["data"]); + ResizeInput(batch.data, data_shape).CopyTo(&args_map["data"]); batch.label.CopyTo(&args_map["label"]); exec->Forward(true); exec->Backward(); @@ -282,44 +306,53 @@ int main(int argc, char const *argv[]) { NDArray::WaitAll(); acu_train.Update(batch.label, exec->outputs[0]); + logloss_train.Reset(); + logloss_train.Update(batch.label, exec->outputs[0]); + ++iter; + LG << "EPOCH: " << epoch << " ITER: " << iter + << " Train Accuracy: " << acu_train.Get() + << " Train Loss: " << logloss_train.Get(); } - LG << "ITER: " << iter << " Train Accuracy: " << acu_train.Get(); + LG << "EPOCH: " << epoch << " Train Accuracy: " << acu_train.Get(); - LG << "Val Epoch: " << iter; + LG << "Val Epoch: " << epoch; acu_val.Reset(); val_iter.Reset(); logloss_val.Reset(); + iter = 0; while (val_iter.Next()) { auto batch = val_iter.GetDataBatch(); - LG << val_iter.GetDataBatch().index.size(); - batch.data.CopyTo(&args_map["data"]); + ResizeInput(batch.data, data_shape).CopyTo(&args_map["data"]); batch.label.CopyTo(&args_map["label"]); exec->Forward(false); NDArray::WaitAll(); acu_val.Update(batch.label, exec->outputs[0]); logloss_val.Update(batch.label, exec->outputs[0]); + LG << "EPOCH: " << epoch << " ITER: " << iter << " Val Accuracy: " << acu_val.Get(); + ++iter; } - LG << "ITER: " << iter << " Val Accuracy: " << acu_val.Get(); - LG << "ITER: " << iter << " Val LogLoss: " << logloss_val.Get(); + LG << "EPOCH: " << epoch << " Val Accuracy: " << acu_val.Get(); + LG << "EPOCH: " << epoch << " Val LogLoss: " << logloss_val.Get(); /*save the parameters*/ std::stringstream ss; - ss << iter; - std::string iter_str; - ss >> iter_str; - std::string save_path_param = "alex_param_" + iter_str; + ss << epoch; + std::string epoch_str; + ss >> epoch_str; + std::string save_path_param = "alex_param_" + epoch_str; auto save_args = args_map; /*we do not want to save the data and label*/ save_args.erase(save_args.find("data")); save_args.erase(save_args.find("label")); /*the alexnet does not get any aux array, so we do not need to save * aux_map*/ - LG << "ITER: " << iter << " Saving to..." << save_path_param; + LG << "EPOCH: " << epoch << " Saving to..." << save_path_param; NDArray::Save(save_path_param, save_args); } /*don't foget to release the executor*/ delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/charRNN.cpp b/cpp-package/example/charRNN.cpp index 8951580067a8..ac5faa47b58c 100644 --- a/cpp-package/example/charRNN.cpp +++ b/cpp-package/example/charRNN.cpp @@ -42,6 +42,7 @@ #include #include #include "mxnet-cpp/MxNetCpp.h" +#include "utils.h" using namespace mxnet::cpp; @@ -721,6 +722,7 @@ int main(int argc, char** argv) { TIME_MAJOR = task.find("TimeMajor") != std::string::npos; std::cout << "use BuiltIn cuDNN RNN: " << builtIn << std::endl << "use data as TimeMajor: " << TIME_MAJOR << std::endl; + TRY if (task.find("train") == 0) { std::cout << "train batch size: " << argv[3] << std::endl << "train max epoch: " << argv[4] << std::endl; @@ -746,5 +748,6 @@ int main(int argc, char** argv) { } MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/feature_extract/run.sh b/cpp-package/example/feature_extract/run.sh index 616445dbd671..b98ddb9eb81e 100755 --- a/cpp-package/example/feature_extract/run.sh +++ b/cpp-package/example/feature_extract/run.sh @@ -17,7 +17,12 @@ # Downloading the data and model mkdir -p model -wget -nc http://data.dmlc.ml/mxnet/models/imagenet/inception-bn.tar.gz +wget -nc -O model/Inception-BN-symbol.json \ + http://data.mxnet.io/mxnet/models/imagenet/inception-bn/Inception-BN-symbol.json +wget -nc -O model/synset.txt \ + http://data.mxnet.io/mxnet/models/imagenet/synset.txt +wget -nc -O model/Inception-BN-0126.params \ + http://data.mxnet.io/mxnet/models/imagenet/inception-bn/Inception-BN-0126.params?raw=true wget -nc -O cat.jpg https://github.com/dmlc/web-data/blob/master/mxnet/doc/tutorials/python/predict_image/cat.jpg?raw=true wget -nc -O dog.jpg https://github.com/dmlc/web-data/blob/master/mxnet/doc/tutorials/python/predict_image/dog.jpg?raw=true wget -nc -O model/mean_224.nd https://github.com/dmlc/web-data/raw/master/mxnet/example/feature_extract/mean_224.nd diff --git a/cpp-package/example/get_data.sh b/cpp-package/example/get_data.sh index 7f975222d0be..b0913bdb684d 100755 --- a/cpp-package/example/get_data.sh +++ b/cpp-package/example/get_data.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information @@ -14,29 +16,48 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -unameOut="$(uname -s)" -case "${unameOut}" in - Linux*) CMD='wget';; - Darwin*) CMD='curl -o';; - CYGWIN*) CMD='wget';; - MINGW*) CMD='wget';; - *) CMD="" -esac -if [ ! -d "./data" ]; then - mkdir data -fi +set -e + +mkdir -p data/mnist_data +cd data/mnist_data + +download () { + local URL=$1 + local GZ_FILE_NAME="${URL##*/}" + + local FILE_NAME="${GZ_FILE_NAME%.*}" + if [[ -f "${FILE_NAME}" ]]; then + echo "File ${FILE_NAME} already downloaded." + return 0 + fi -if [ ! -d "./data/mnist_data" ]; then - mkdir ./data/mnist_data + echo "Downloading ${URL} ..." + local CURL_OPTIONS="--connect-timeout 10 \ + --max-time 300 \ + --retry-delay 10 \ + --retry 3 \ + --retry-delay 0 \ + --location \ + --silent" + curl ${CURL_OPTIONS} ${URL} -o ${GZ_FILE_NAME} - (cd data/mnist_data; $CMD train-images-idx3-ubyte.gz https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/train-images-idx3-ubyte.gz) - (cd data/mnist_data; $CMD train-labels-idx1-ubyte.gz https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/train-labels-idx1-ubyte.gz) - (cd data/mnist_data; $CMD t10k-images-idx3-ubyte.gz https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/t10k-images-idx3-ubyte.gz) - (cd data/mnist_data; $CMD t10k-labels-idx1-ubyte.gz https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/t10k-labels-idx1-ubyte.gz) - (cd data/mnist_data; $CMD mnist_train.csv.gz http://data.mxnet.io/data/mnist_train.csv.gz) - (cd data/mnist_data; gzip -d *.gz) -fi + if [[ ! -f "${GZ_FILE_NAME}" ]]; then + echo "File ${URL} couldn't be downloaded!" + exit 1 + fi + gzip -d ${GZ_FILE_NAME} + (($? != 0)) && exit 1 || return 0 +} +FILES=( + "https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/train-images-idx3-ubyte.gz" + "https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/train-labels-idx1-ubyte.gz" + "https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/t10k-images-idx3-ubyte.gz" + "https://apache-mxnet.s3-accelerate.dualstack.amazonaws.com/gluon/dataset/mnist/t10k-labels-idx1-ubyte.gz" + "http://data.mxnet.io/data/mnist_train.csv.gz") +for FILE in ${FILES[@]}; do + download ${FILE} +done diff --git a/cpp-package/example/googlenet.cpp b/cpp-package/example/googlenet.cpp index 26ba51027db6..9cf1834cf22c 100644 --- a/cpp-package/example/googlenet.cpp +++ b/cpp-package/example/googlenet.cpp @@ -124,6 +124,7 @@ int main(int argc, char const *argv[]) { ctx = Context::cpu();; #endif + TRY auto googlenet = GoogleNetSymbol(10); std::map args_map; std::map aux_map; @@ -192,5 +193,6 @@ int main(int argc, char const *argv[]) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/inception_bn.cpp b/cpp-package/example/inception_bn.cpp index 2073ebe47fbc..caf858a64177 100644 --- a/cpp-package/example/inception_bn.cpp +++ b/cpp-package/example/inception_bn.cpp @@ -142,23 +142,45 @@ Symbol InceptionSymbol(int num_classes) { return SoftmaxOutput("softmax", fc1, data_label); } +NDArray ResizeInput(NDArray data, const Shape new_shape) { + NDArray pic = data.Reshape(Shape(0, 1, 28, 28)); + NDArray pic_1channel; + Operator("_contrib_BilinearResize2D") + .SetParam("height", new_shape[2]) + .SetParam("width", new_shape[3]) + (pic).Invoke(pic_1channel); + NDArray output; + Operator("tile") + .SetParam("reps", Shape(1, 3, 1, 1)) + (pic_1channel).Invoke(output); + return output; +} + int main(int argc, char const *argv[]) { int batch_size = 40; int max_epoch = argc > 1 ? strtol(argv[1], NULL, 10) : 100; float learning_rate = 1e-2; float weight_decay = 1e-4; - auto ctx = Context::gpu(); -#if MXNET_USE_CPU - ctx = Context::cpu(); + /*context*/ + auto ctx = Context::cpu(); + int num_gpu; + MXGetGPUCount(&num_gpu); +#if !MXNET_USE_CPU + if (num_gpu > 0) { + ctx = Context::gpu(); + } #endif + TRY auto inception_bn_net = InceptionSymbol(10); std::map args_map; std::map aux_map; - args_map["data"] = NDArray(Shape(batch_size, 3, 224, 224), ctx); - args_map["data_label"] = NDArray(Shape(batch_size), ctx); + const Shape data_shape = Shape(batch_size, 3, 224, 224), + label_shape = Shape(batch_size); + args_map["data"] = NDArray(data_shape, ctx); + args_map["data_label"] = NDArray(label_shape, ctx); inception_bn_net.InferArgsMap(ctx, &args_map, args_map); std::vector data_files = { "./data/mnist_data/train-images-idx3-ubyte", @@ -201,7 +223,7 @@ int main(int argc, char const *argv[]) { train_acc.Reset(); while (train_iter.Next()) { auto data_batch = train_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); @@ -221,7 +243,7 @@ int main(int argc, char const *argv[]) { val_acc.Reset(); while (val_iter.Next()) { auto data_batch = val_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); exec->Forward(false); @@ -234,5 +256,6 @@ int main(int argc, char const *argv[]) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/inference/inception_inference.cpp b/cpp-package/example/inference/inception_inference.cpp index 78487e6ee0cd..fa5600190f95 100644 --- a/cpp-package/example/inference/inception_inference.cpp +++ b/cpp-package/example/inference/inception_inference.cpp @@ -301,7 +301,7 @@ void Predictor::PredictImage(const std::string& image_file) { executor->Forward(false); // The output is available in executor->outputs. - auto array = executor->outputs[0].Copy(global_ctx); + auto array = executor->outputs[0].Copy(Context::cpu()); /* * Find out the maximum accuracy and the index associated with that accuracy. diff --git a/cpp-package/example/inference/unit_test_inception_inference.sh b/cpp-package/example/inference/unit_test_inception_inference.sh index f33b8f19be6d..c3c4630f6e4a 100755 --- a/cpp-package/example/inference/unit_test_inception_inference.sh +++ b/cpp-package/example/inference/unit_test_inception_inference.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information @@ -17,7 +19,7 @@ # Downloading the data and model mkdir -p model -wget -nc http://data.dmlc.ml/mxnet/models/imagenet/inception-bn.tar.gz +wget -nc http://data.mxnet.io/models/imagenet/inception-bn.tar.gz wget -nc -O model/dog.jpg https://github.com/dmlc/web-data/blob/master/mxnet/doc/tutorials/python/predict_image/dog.jpg?raw=true wget -nc -O model/mean_224.nd https://github.com/dmlc/web-data/raw/master/mxnet/example/feature_extract/mean_224.nd tar -xvzf inception-bn.tar.gz -C model diff --git a/cpp-package/example/lenet.cpp b/cpp-package/example/lenet.cpp index 42594548130a..a52efd8fed40 100644 --- a/cpp-package/example/lenet.cpp +++ b/cpp-package/example/lenet.cpp @@ -25,6 +25,7 @@ #include #include #include "mxnet-cpp/MxNetCpp.h" +#include "utils.h" using namespace mxnet::cpp; @@ -257,8 +258,10 @@ class Lenet { }; int main(int argc, char const *argv[]) { + TRY Lenet lenet; lenet.Run(argc > 1 ? strtol(argv[1], NULL, 10) : 100000); MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/lenet_with_mxdataiter.cpp b/cpp-package/example/lenet_with_mxdataiter.cpp index 33110fee3a88..69067d549380 100644 --- a/cpp-package/example/lenet_with_mxdataiter.cpp +++ b/cpp-package/example/lenet_with_mxdataiter.cpp @@ -66,6 +66,16 @@ Symbol LenetSymbol() { return lenet; } +NDArray ResizeInput(NDArray data, const Shape new_shape) { + NDArray pic = data.Reshape(Shape(0, 1, 28, 28)); + NDArray output; + Operator("_contrib_BilinearResize2D") + .SetParam("height", new_shape[2]) + .SetParam("width", new_shape[3]) + (pic).Invoke(output); + return output; +} + int main(int argc, char const *argv[]) { /*setup basic configs*/ int W = 28; @@ -74,15 +84,24 @@ int main(int argc, char const *argv[]) { int max_epoch = argc > 1 ? strtol(argv[1], NULL, 10) : 100; float learning_rate = 1e-4; float weight_decay = 1e-4; - auto dev_ctx = Context::gpu(); -#if MXNET_USE_CPU - dev_ctx = Context::cpu(); + + auto dev_ctx = Context::cpu(); + int num_gpu; + MXGetGPUCount(&num_gpu); +#if !MXNET_USE_CPU + if (num_gpu > 0) { + dev_ctx = Context::gpu(); + } #endif + + TRY auto lenet = LenetSymbol(); std::map args_map; - args_map["data"] = NDArray(Shape(batch_size, 1, W, H), dev_ctx); - args_map["data_label"] = NDArray(Shape(batch_size), dev_ctx); + const Shape data_shape = Shape(batch_size, 1, H, W), + label_shape = Shape(batch_size); + args_map["data"] = NDArray(data_shape, dev_ctx); + args_map["data_label"] = NDArray(label_shape, dev_ctx); lenet.InferArgsMap(dev_ctx, &args_map, args_map); args_map["fc1_w"] = NDArray(Shape(500, 4 * 4 * 50), dev_ctx); @@ -131,7 +150,7 @@ int main(int argc, char const *argv[]) { samples += batch_size; auto data_batch = train_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); @@ -163,7 +182,7 @@ int main(int argc, char const *argv[]) { val_iter.Reset(); while (val_iter.Next()) { auto data_batch = val_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); @@ -179,5 +198,6 @@ int main(int argc, char const *argv[]) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/mlp.cpp b/cpp-package/example/mlp.cpp index c3760fd3c846..ee09bf8da3f8 100644 --- a/cpp-package/example/mlp.cpp +++ b/cpp-package/example/mlp.cpp @@ -24,6 +24,7 @@ #include #include #include "mxnet-cpp/MxNetCpp.h" +#include "utils.h" using namespace mxnet::cpp; @@ -173,7 +174,9 @@ void MLP(int max_epoch) { int main(int argc, char** argv) { int max_epoch = argc > 1 ? strtol(argv[1], NULL, 10) : 15000; + TRY MLP(max_epoch); MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/mlp_cpu.cpp b/cpp-package/example/mlp_cpu.cpp index 5d46d40e421f..7ea6946dd8c2 100644 --- a/cpp-package/example/mlp_cpu.cpp +++ b/cpp-package/example/mlp_cpu.cpp @@ -72,6 +72,7 @@ int main(int argc, char** argv) { return 1; } + TRY auto net = mlp(layers); Context ctx = Context::cpu(); // Use CPU for training @@ -141,5 +142,6 @@ int main(int argc, char** argv) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/mlp_csv.cpp b/cpp-package/example/mlp_csv.cpp index f12b7c17133d..0d9103783a79 100644 --- a/cpp-package/example/mlp_csv.cpp +++ b/cpp-package/example/mlp_csv.cpp @@ -156,6 +156,7 @@ int main(int argc, char** argv) { .SetParam("shuffle", 0) .CreateDataIter(); + TRY auto net = mlp(hidden_units); Context ctx = Context::cpu(); @@ -269,5 +270,6 @@ int main(int argc, char** argv) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/mlp_gpu.cpp b/cpp-package/example/mlp_gpu.cpp index f6060209a51e..5265de79d976 100644 --- a/cpp-package/example/mlp_gpu.cpp +++ b/cpp-package/example/mlp_gpu.cpp @@ -72,6 +72,7 @@ int main(int argc, char** argv) { return 1; } + TRY auto net = mlp(layers); Context ctx = Context::gpu(); // Use GPU for training @@ -157,5 +158,6 @@ int main(int argc, char** argv) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/resnet.cpp b/cpp-package/example/resnet.cpp index 7200bd42d2de..8f8fd12e32ce 100644 --- a/cpp-package/example/resnet.cpp +++ b/cpp-package/example/resnet.cpp @@ -153,23 +153,46 @@ Symbol ResNetSymbol(int num_class, int num_level = 3, int num_block = 9, return SoftmaxOutput("softmax", fc, data_label); } +NDArray ResizeInput(NDArray data, const Shape new_shape) { + NDArray pic = data.Reshape(Shape(0, 1, 28, 28)); + NDArray pic_1channel; + Operator("_contrib_BilinearResize2D") + .SetParam("height", new_shape[2]) + .SetParam("width", new_shape[3]) + (pic).Invoke(pic_1channel); + NDArray output; + Operator("tile") + .SetParam("reps", Shape(1, 3, 1, 1)) + (pic_1channel).Invoke(output); + return output; +} + int main(int argc, char const *argv[]) { - int batch_size = 50; int max_epoch = argc > 1 ? strtol(argv[1], NULL, 10) : 100; float learning_rate = 1e-4; float weight_decay = 1e-4; + TRY auto resnet = ResNetSymbol(10); std::map args_map; std::map aux_map; - auto ctx = Context::gpu(); -#if MXNET_USE_CPU - ctx = Context::cpu();; + /*context*/ + auto ctx = Context::cpu(); + int num_gpu; + MXGetGPUCount(&num_gpu); + int batch_size = 8; +#if !MXNET_USE_CPU + if (num_gpu > 0) { + ctx = Context::gpu(); + batch_size = 32; + } #endif - args_map["data"] = NDArray(Shape(batch_size, 3, 256, 256), ctx); - args_map["data_label"] = NDArray(Shape(batch_size), ctx); + const Shape data_shape = Shape(batch_size, 3, 224, 224), + label_shape = Shape(batch_size); + args_map["data"] = NDArray(data_shape, ctx); + args_map["data_label"] = NDArray(label_shape, ctx); resnet.InferArgsMap(ctx, &args_map, args_map); std::vector data_files = { "./data/mnist_data/train-images-idx3-ubyte", @@ -206,13 +229,15 @@ int main(int argc, char const *argv[]) { // Create metrics Accuracy train_acc, val_acc; - for (int iter = 0; iter < max_epoch; ++iter) { - LG << "Epoch: " << iter; + LogLoss logloss_train, logloss_val; + for (int epoch = 0; epoch < max_epoch; ++epoch) { + LG << "Epoch: " << epoch; train_iter.Reset(); train_acc.Reset(); + int iter = 0; while (train_iter.Next()) { auto data_batch = train_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); @@ -225,24 +250,34 @@ int main(int argc, char const *argv[]) { } NDArray::WaitAll(); train_acc.Update(data_batch.label, exec->outputs[0]); + logloss_train.Reset(); + logloss_train.Update(data_batch.label, exec->outputs[0]); + ++iter; + LG << "EPOCH: " << epoch << " ITER: " << iter + << " Train Accuracy: " << train_acc.Get() + << " Train Loss: " << logloss_train.Get(); } + LG << "EPOCH: " << epoch << " Train Accuracy: " << train_acc.Get(); val_iter.Reset(); val_acc.Reset(); + iter = 0; while (val_iter.Next()) { auto data_batch = val_iter.GetDataBatch(); - data_batch.data.CopyTo(&args_map["data"]); + ResizeInput(data_batch.data, data_shape).CopyTo(&args_map["data"]); data_batch.label.CopyTo(&args_map["data_label"]); NDArray::WaitAll(); exec->Forward(false); NDArray::WaitAll(); val_acc.Update(data_batch.label, exec->outputs[0]); + LG << "EPOCH: " << epoch << " ITER: " << iter << " Val Accuracy: " << val_acc.Get(); + ++iter; } - LG << "Train Accuracy: " << train_acc.Get(); LG << "Validation Accuracy: " << val_acc.Get(); } delete exec; delete opt; MXNotifyShutdown(); + CATCH return 0; } diff --git a/cpp-package/example/test_kvstore.cpp b/cpp-package/example/test_kvstore.cpp new file mode 100644 index 000000000000..d9e0400a5ac8 --- /dev/null +++ b/cpp-package/example/test_kvstore.cpp @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +#include "mxnet/c_api.h" // MXGetGPUCount() +#include "mxnet-cpp/MxNetCpp.h" + +using namespace mxnet::cpp; + +static bool test_single_key(const Context &context, const std::string &context_str) { + std::string key = "singlekeytest-" + context_str; + + NDArray result(Shape(4), context); + NDArray result_cpu; + + // initialize data + NDArray data_cpu({0.f, 233.f, -0.12f, 9.f}, Shape(4), Context::cpu()); + NDArray data = data_cpu.Copy(context); + NDArray::WaitAll(); + + KVStore::Init(key, data); + NDArray::WaitAll(); + + // retrieve result + KVStore::Pull(key, &result); + NDArray::WaitAll(); + + result_cpu = result.Copy(Context::cpu()); + NDArray::WaitAll(); + + // compare + for (size_t j=0; j < result_cpu.Size(); j++) { + if (result_cpu.GetData()[j] != data_cpu.GetData()[j]) { + LG << "Error: wrong initialized data in singlekeytest-" << context_str + << ", expect " << data_cpu.GetData()[j] + << " got " << result_cpu.GetData()[j]; + return false; + } + } + + // push gradient + NDArray grad_cpu({0.1f, -2.f, -4.4f, 0.f}, Shape(4), Context::cpu()); + NDArray grad = grad_cpu.Copy(context); + NDArray::WaitAll(); + + KVStore::Push(key, grad); + NDArray::WaitAll(); + + // retrieve result + KVStore::Pull(key, &result); + NDArray::WaitAll(); + + result_cpu = result.Copy(Context::cpu()); + NDArray::WaitAll(); + + // compare + for (size_t j=0; j < result_cpu.Size(); j++) { + if (result_cpu.GetData()[j] != grad_cpu.GetData()[j]) { + LG << "Error: wrong gradient data in singlekeytest-" << context_str + << ", expect " << grad_cpu.GetData()[j] + << " got " << result_cpu.GetData()[j]; + return false; + } + } + + return true; +} + +static bool test_multiple_key(const Context &context, const std::string &context_str) { + std::vector keys(2); + keys[0] = "multikeytest-0-" + context_str; + keys[1] = "multikeytest-1-" + context_str; + + std::vector results(2); + results[0] = NDArray(Shape(4), context); + results[1] = NDArray(Shape(4), context); + std::vector results_cpu(2); + + // initialize data + std::vector data_cpu(2); + data_cpu[0] = NDArray({0.f, 2.f, -3.12f, 4.f}, Shape(4), Context::cpu()); + data_cpu[1] = NDArray({0.8f, -2.f, 6.6f, 77.f}, Shape(4), Context::cpu()); + std::vector data(2); + data[0] = data_cpu[0].Copy(context); + data[1] = data_cpu[1].Copy(context); + NDArray::WaitAll(); + + KVStore::Init(keys, data); + NDArray::WaitAll(); + + // retrieve result + KVStore::Pull(keys, &results); + NDArray::WaitAll(); + + results_cpu[0] = results[0].Copy(Context::cpu()); + results_cpu[1] = results[1].Copy(Context::cpu()); + NDArray::WaitAll(); + + // compare + for (size_t i=0; i < results_cpu.size(); i++) { + for (size_t j=0; j < results_cpu[i].Size(); j++) { + if (results_cpu[i].GetData()[j] != data_cpu[i].GetData()[j]) { + LG << "Error: wrong initialized data in multikeytest-" << context_str + << ", expect " << data_cpu[i].GetData()[j] + << " got " << results_cpu[i].GetData()[j]; + return false; + } + } + } + + // push gradient, reduce for the second + std::vector push_keys(3); + push_keys[0] = "multikeytest-0-" + context_str; + push_keys[1] = "multikeytest-1-" + context_str; + push_keys[2] = "multikeytest-1-" + context_str; + + std::vector grads_cpu(3); + grads_cpu[0] = NDArray({0.2f, -0.3f, -1.1f, 0.0f}, Shape(4), Context::cpu()); + grads_cpu[1] = NDArray({2.f, 4.f, -4.f, -5.f}, Shape(4), Context::cpu()); + grads_cpu[2] = NDArray({-3.f, -0.2f, 12.f, -9.f}, Shape(4), Context::cpu()); + std::vector grads(3); + grads[0] = grads_cpu[0].Copy(context); + grads[1] = grads_cpu[1].Copy(context); + grads[2] = grads_cpu[2].Copy(context); + NDArray::WaitAll(); + + KVStore::Push(push_keys, grads); + NDArray::WaitAll(); + + // retrieve result + KVStore::Pull(keys, &results); + NDArray::WaitAll(); + + results_cpu[0] = results[0].Copy(Context::cpu()); + results_cpu[1] = results[1].Copy(Context::cpu()); + NDArray::WaitAll(); + + // compare the first + for (size_t j=0; j < results_cpu[0].Size(); j++) { + if (results_cpu[0].GetData()[j] != grads_cpu[0].GetData()[j]) { + LG << "Error: wrong gradient data in multikeytest-" << context_str + << ", expect " << grads_cpu[0].GetData()[j] + << " got " << results_cpu[0].GetData()[j]; + return false; + } + } + + // compare the second + for (size_t j=0; j < results_cpu[1].Size(); j++) { + if (results_cpu[1].GetData()[j] != (grads_cpu[1].GetData()[j] + grads_cpu[2].GetData()[j])) { + LG << "Error: wrong reduced gradient data in multikeytest-" << context_str + << ", expect " << (grads_cpu[1].GetData()[j] + grads_cpu[2].GetData()[j]) + << " got " << results_cpu[1].GetData()[j]; + return false; + } + } + + return true; +} + +int main(int argc, char** argv) { + KVStore::SetType("local"); + + bool success1 = test_single_key(Context::cpu(), "cpu"); + bool success2 = test_multiple_key(Context::cpu(), "cpu"); + + bool success3 = true; + bool success4 = true; + + int gpu_count = 0; + if (MXGetGPUCount(&gpu_count) != 0) { + LG << "Error: MXGetGPUCount"; + + MXNotifyShutdown(); + return 1; + } + + if (gpu_count > 0) { + success3 = test_single_key(Context::gpu(), "gpu"); + success4 = test_multiple_key(Context::gpu(), "gpu"); + } + + int ret = (success1 && success2 && success3 && success4) ? 0 : 1; + + MXNotifyShutdown(); + return ret; +} diff --git a/cpp-package/example/test_score.cpp b/cpp-package/example/test_score.cpp index 687683f487f8..0ccdf65b3b19 100644 --- a/cpp-package/example/test_score.cpp +++ b/cpp-package/example/test_score.cpp @@ -62,6 +62,7 @@ int main(int argc, char** argv) { const int max_epoch = 10; const float learning_rate = 0.1; const float weight_decay = 1e-2; + float score = 0; std::vector data_files = { "./data/mnist_data/train-images-idx3-ubyte", "./data/mnist_data/train-labels-idx1-ubyte", @@ -79,6 +80,7 @@ int main(int argc, char** argv) { return 1; } + TRY auto net = mlp(layers); Context ctx = Context::gpu(); // Use GPU for training @@ -111,7 +113,6 @@ int main(int argc, char** argv) { auto *exec = net.SimpleBind(ctx, args); auto arg_names = net.ListArguments(); - float score = 0; // Start training for (int iter = 0; iter < max_epoch; ++iter) { int samples = 0; @@ -158,5 +159,6 @@ int main(int argc, char** argv) { delete exec; delete opt; MXNotifyShutdown(); + CATCH return score >= MIN_SCORE ? 0 : 1; } diff --git a/cpp-package/example/utils.h b/cpp-package/example/utils.h index 020d1ec5804e..87847701ce6e 100644 --- a/cpp-package/example/utils.h +++ b/cpp-package/example/utils.h @@ -27,6 +27,15 @@ using namespace mxnet::cpp; +#define TRY \ + try { +#define CATCH \ + } catch(dmlc::Error &err) { \ + LG << "Status: FAIL";\ + LG << "With Error: " << MXGetLastError(); \ + return 1; \ + } + bool isFileExists(const std::string &filename) { std::ifstream fhandle(filename.c_str()); return fhandle.good(); diff --git a/cpp-package/include/mxnet-cpp/kvstore.h b/cpp-package/include/mxnet-cpp/kvstore.h index d5aa1509a8f0..67f984fce0ee 100644 --- a/cpp-package/include/mxnet-cpp/kvstore.h +++ b/cpp-package/include/mxnet-cpp/kvstore.h @@ -39,12 +39,21 @@ class KVStore { static void SetType(const std::string& type); static void RunServer(); static void Init(int key, const NDArray& val); + static void Init(const std::string& key, const NDArray& val); static void Init(const std::vector& keys, const std::vector& vals); + static void Init(const std::vector& keys, const std::vector& vals); static void Push(int key, const NDArray& val, int priority = 0); + static void Push(const std::string& key, const NDArray& val, int priority = 0); static void Push(const std::vector& keys, - const std::vector& vals, int priority = 0); + const std::vector& vals, int priority = 0); + static void Push(const std::vector& keys, + const std::vector& vals, int priority = 0); static void Pull(int key, NDArray* out, int priority = 0); - static void Pull(const std::vector& keys, std::vector* outs, int priority = 0); + static void Pull(const std::string& key, NDArray* out, int priority = 0); + static void Pull(const std::vector& keys, + std::vector* outs, int priority = 0); + static void Pull(const std::vector& keys, + std::vector* outs, int priority = 0); // TODO(lx): put lr in optimizer or not? static void SetOptimizer(std::unique_ptr optimizer, bool local = false); static std::string GetType(); diff --git a/cpp-package/include/mxnet-cpp/kvstore.hpp b/cpp-package/include/mxnet-cpp/kvstore.hpp index f2b5e74990ce..6cd405b91dd4 100644 --- a/cpp-package/include/mxnet-cpp/kvstore.hpp +++ b/cpp-package/include/mxnet-cpp/kvstore.hpp @@ -87,6 +87,12 @@ inline void KVStore::Init(int key, const NDArray& val) { CHECK_EQ(MXKVStoreInit(get_kvstore()->get_handle(), 1, &key, &val_handle), 0); } +inline void KVStore::Init(const std::string& key, const NDArray& val) { + const char* key_handle = key.c_str(); + NDArrayHandle val_handle = val.GetHandle(); + CHECK_EQ(MXKVStoreInitEx(get_kvstore()->get_handle(), 1, &key_handle, &val_handle), 0); +} + inline void KVStore::Init(const std::vector& keys, const std::vector& vals) { CHECK_EQ(keys.size(), vals.size()); std::vector val_handles(vals.size()); @@ -99,14 +105,36 @@ inline void KVStore::Init(const std::vector& keys, const std::vector& keys, const std::vector& vals) { + CHECK_EQ(keys.size(), vals.size()); + std::vector key_handles(keys.size()); + std::transform(keys.cbegin(), keys.cend(), key_handles.begin(), + [](const std::string& key) { + return key.c_str(); + }); + std::vector val_handles(vals.size()); + std::transform(vals.cbegin(), vals.cend(), val_handles.begin(), + [](const NDArray& val) { + return val.GetHandle(); + }); + + CHECK_EQ(MXKVStoreInitEx(get_kvstore()->get_handle(), key_handles.size(), key_handles.data(), + val_handles.data()), 0); +} + inline void KVStore::Push(int key, const NDArray& val, int priority) { NDArrayHandle val_handle = val.GetHandle(); CHECK_EQ(MXKVStorePush(get_kvstore()->get_handle(), 1, &key, &val_handle, priority), 0); } +inline void KVStore::Push(const std::string& key, const NDArray& val, int priority) { + const char* key_handle = key.c_str(); + NDArrayHandle val_handle = val.GetHandle(); + CHECK_EQ(MXKVStorePushEx(get_kvstore()->get_handle(), 1, &key_handle, &val_handle, priority), 0); +} + inline void KVStore::Push(const std::vector& keys, - const std::vector& vals, - int priority) { + const std::vector& vals, int priority) { CHECK_EQ(keys.size(), vals.size()); std::vector val_handles(vals.size()); std::transform(vals.cbegin(), vals.cend(), val_handles.begin(), @@ -118,12 +146,37 @@ inline void KVStore::Push(const std::vector& keys, val_handles.data(), priority), 0); } +inline void KVStore::Push(const std::vector& keys, + const std::vector& vals, int priority) { + CHECK_EQ(keys.size(), vals.size()); + std::vector key_handles(keys.size()); + std::transform(keys.cbegin(), keys.cend(), key_handles.begin(), + [](const std::string& key) { + return key.c_str(); + }); + std::vector val_handles(vals.size()); + std::transform(vals.cbegin(), vals.cend(), val_handles.begin(), + [](const NDArray& val) { + return val.GetHandle(); + }); + + CHECK_EQ(MXKVStorePushEx(get_kvstore()->get_handle(), key_handles.size(), key_handles.data(), + val_handles.data(), priority), 0); +} + inline void KVStore::Pull(int key, NDArray* out, int priority) { NDArrayHandle out_handle = out->GetHandle(); CHECK_EQ(MXKVStorePull(get_kvstore()->get_handle(), 1, &key, &out_handle, priority), 0); } -inline void KVStore::Pull(const std::vector& keys, std::vector* outs, int priority) { +inline void KVStore::Pull(const std::string& key, NDArray* out, int priority) { + const char* key_handle = key.c_str(); + NDArrayHandle out_handle = out->GetHandle(); + CHECK_EQ(MXKVStorePullEx(get_kvstore()->get_handle(), 1, &key_handle, &out_handle, priority), 0); +} + +inline void KVStore::Pull(const std::vector& keys, + std::vector* outs, int priority) { CHECK_EQ(keys.size(), outs->size()); std::vector out_handles(keys.size()); @@ -136,6 +189,25 @@ inline void KVStore::Pull(const std::vector& keys, std::vector* ou out_handles.data(), priority), 0); } +inline void KVStore::Pull(const std::vector& keys, + std::vector* outs, int priority) { + CHECK_EQ(keys.size(), outs->size()); + + std::vector key_handles(keys.size()); + std::transform(keys.cbegin(), keys.cend(), key_handles.begin(), + [](const std::string& key) { + return key.c_str(); + }); + std::vector out_handles(keys.size()); + std::transform(outs->cbegin(), outs->cend(), out_handles.begin(), + [](const NDArray& val) { + return val.GetHandle(); + }); + + CHECK_EQ(MXKVStorePullEx(get_kvstore()->get_handle(), key_handles.size(), key_handles.data(), + out_handles.data(), priority), 0); +} + inline void KVStore::Updater(int key, NDArrayHandle recv, NDArrayHandle local, void* handle_) { Optimizer *opt = static_cast(handle_); diff --git a/cpp-package/include/mxnet-cpp/ndarray.hpp b/cpp-package/include/mxnet-cpp/ndarray.hpp index 966cf75c9122..d0438305a62e 100644 --- a/cpp-package/include/mxnet-cpp/ndarray.hpp +++ b/cpp-package/include/mxnet-cpp/ndarray.hpp @@ -233,12 +233,12 @@ inline NDArray NDArray::Reshape(const Shape &new_shape) const { return NDArray(handle); } inline void NDArray::WaitToRead() const { - CHECK_EQ(MXNDArrayWaitToRead(blob_ptr_->handle_), 0); + CHECK_EQ(MXNDArrayWaitToRead(blob_ptr_->handle_), 0) << MXGetLastError(); } inline void NDArray::WaitToWrite() { - CHECK_EQ(MXNDArrayWaitToWrite(blob_ptr_->handle_), 0); + CHECK_EQ(MXNDArrayWaitToWrite(blob_ptr_->handle_), 0) << MXGetLastError(); } -inline void NDArray::WaitAll() { CHECK_EQ(MXNDArrayWaitAll(), 0); } +inline void NDArray::WaitAll() { CHECK_EQ(MXNDArrayWaitAll(), 0) << MXGetLastError(); } inline void NDArray::SampleGaussian(mx_float mu, mx_float sigma, NDArray *out) { Operator("_random_normal")(mu, sigma).Invoke(*out); } @@ -397,11 +397,11 @@ inline size_t NDArray::Size() const { } inline std::vector NDArray::GetShape() const { - const mx_uint *out_pdata; - mx_uint out_dim; - MXNDArrayGetShape(blob_ptr_->handle_, &out_dim, &out_pdata); + const int *out_pdata; + int out_dim; + MXNDArrayGetShapeEx(blob_ptr_->handle_, &out_dim, &out_pdata); std::vector ret; - for (mx_uint i = 0; i < out_dim; ++i) { + for (int i = 0; i < out_dim; ++i) { ret.push_back(out_pdata[i]); } return ret; diff --git a/cpp-package/include/mxnet-cpp/operator.hpp b/cpp-package/include/mxnet-cpp/operator.hpp index edc396f1477c..8cdd78d2c0e9 100644 --- a/cpp-package/include/mxnet-cpp/operator.hpp +++ b/cpp-package/include/mxnet-cpp/operator.hpp @@ -134,9 +134,11 @@ inline void Operator::Invoke(std::vector &outputs) { outputs_receiver = output_handles.data(); } - MXImperativeInvoke(handle_, num_inputs, input_ndarrays_.data(), - &num_outputs, &outputs_receiver, - param_keys.size(), param_keys.data(), param_values.data()); + if (MXImperativeInvoke(handle_, num_inputs, input_ndarrays_.data(), + &num_outputs, &outputs_receiver, + param_keys.size(), param_keys.data(), + param_values.data())) + LOG(FATAL) << MXGetLastError(); if (outputs.size() > 0) return; diff --git a/cpp-package/include/mxnet-cpp/symbol.hpp b/cpp-package/include/mxnet-cpp/symbol.hpp index aed963949060..2e3fb7a2d5de 100644 --- a/cpp-package/include/mxnet-cpp/symbol.hpp +++ b/cpp-package/include/mxnet-cpp/symbol.hpp @@ -188,7 +188,7 @@ inline void Symbol::InferShape( std::vector keys; std::vector arg_ind_ptr; - std::vector arg_shape_data; + std::vector arg_shape_data; for (const auto &arg : arg_shapes) { keys.push_back(arg.first.c_str()); @@ -200,40 +200,40 @@ inline void Symbol::InferShape( arg_ind_ptr.push_back(arg_shape_data.size()); mx_uint in_shape_size; - const mx_uint *in_shape_ndim; - const mx_uint **in_shape_data; + const int *in_shape_ndim; + const int **in_shape_data; mx_uint out_shape_size; - const mx_uint *out_shape_ndim; - const mx_uint **out_shape_data; + const int *out_shape_ndim; + const int **out_shape_data; mx_uint aux_shape_size; - const mx_uint *aux_shape_ndim; - const mx_uint **aux_shape_data; + const int *aux_shape_ndim; + const int **aux_shape_data; int complete; - CHECK_EQ(MXSymbolInferShape(GetHandle(), keys.size(), keys.data(), - arg_ind_ptr.data(), arg_shape_data.data(), - &in_shape_size, &in_shape_ndim, &in_shape_data, - &out_shape_size, &out_shape_ndim, &out_shape_data, - &aux_shape_size, &aux_shape_ndim, &aux_shape_data, - &complete), + CHECK_EQ(MXSymbolInferShapeEx(GetHandle(), keys.size(), keys.data(), + arg_ind_ptr.data(), arg_shape_data.data(), + &in_shape_size, &in_shape_ndim, &in_shape_data, + &out_shape_size, &out_shape_ndim, &out_shape_data, + &aux_shape_size, &aux_shape_ndim, &aux_shape_data, + &complete), 0); if (complete) { for (mx_uint i = 0; i < in_shape_size; ++i) { in_shape->push_back(std::vector()); - for (mx_uint j = 0; j < in_shape_ndim[i]; ++j) { + for (int j = 0; j < in_shape_ndim[i]; ++j) { (*in_shape)[i].push_back(in_shape_data[i][j]); } } for (mx_uint i = 0; i < aux_shape_size; ++i) { aux_shape->push_back(std::vector()); - for (mx_uint j = 0; j < aux_shape_ndim[i]; ++j) { + for (int j = 0; j < aux_shape_ndim[i]; ++j) { (*aux_shape)[i].push_back(aux_shape_data[i][j]); } } for (mx_uint i = 0; i < out_shape_size; ++i) { out_shape->push_back(std::vector()); - for (mx_uint j = 0; j < out_shape_ndim[i]; ++j) { + for (int j = 0; j < out_shape_ndim[i]; ++j) { (*out_shape)[i].push_back(out_shape_data[i][j]); } } diff --git a/cpp-package/tests/ci_test.sh b/cpp-package/tests/ci_test.sh index 18fabea7a7f9..2d1f8e4f68e6 100755 --- a/cpp-package/tests/ci_test.sh +++ b/cpp-package/tests/ci_test.sh @@ -48,8 +48,11 @@ cp ../../build/cpp-package/example/mlp_cpu . cp ../../build/cpp-package/example/mlp_gpu . ./mlp_gpu - cp ../../build/cpp-package/example/test_optimizer . - ./test_optimizer +cp ../../build/cpp-package/example/test_optimizer . +./test_optimizer + +cp ../../build/cpp-package/example/test_kvstore . +./test_kvstore cp ../../build/cpp-package/example/test_score . ./test_score 0.93 diff --git a/dev_menu.py b/dev_menu.py index 1fda4c7aabca..d439d8194f2a 100755 --- a/dev_menu.py +++ b/dev_menu.py @@ -123,7 +123,7 @@ def create_virtualenv_default(): ('[Docker] sanity_check. Check for linting and code formatting and licenses.', [ "ci/build.py --platform ubuntu_cpu /work/runtime_functions.sh sanity_check", - "ci/build.py --platform ubuntu_cpu /work/runtime_functions.sh nightly_test_rat_check", + "ci/build.py --platform ubuntu_rat /work/runtime_functions.sh nightly_test_rat_check", ]), ('[Docker] Python3 CPU unittests', [ @@ -132,12 +132,12 @@ def create_virtualenv_default(): ]), ('[Docker] Python3 GPU unittests', [ - "ci/build.py --platform ubuntu_gpu /work/runtime_functions.sh build_ubuntu_gpu", + "ci/build.py --nvidiadocker --platform ubuntu_gpu /work/runtime_functions.sh build_ubuntu_gpu", "ci/build.py --nvidiadocker --platform ubuntu_gpu /work/runtime_functions.sh unittest_ubuntu_python3_gpu", ]), ('[Docker] Python3 GPU+MKLDNN unittests', [ - "ci/build.py --platform ubuntu_gpu /work/runtime_functions.sh build_ubuntu_gpu_cmake_mkldnn", + "ci/build.py --nvidiadocker --platform ubuntu_gpu /work/runtime_functions.sh build_ubuntu_gpu_cmake_mkldnn", "ci/build.py --nvidiadocker --platform ubuntu_gpu /work/runtime_functions.sh unittest_ubuntu_python3_gpu", ]), ('[Docker] Python3 CPU Intel MKLDNN unittests', diff --git a/docker/docker-python/Dockerfile.mxnet.python.cpu b/docker/docker-python/Dockerfile.mxnet.python.cpu index eb437f3c2334..0858e99e2d7d 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.cpu +++ b/docker/docker-python/Dockerfile.mxnet.python.cpu @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for CPU FROM ubuntu:16.04 +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet +RUN pip install mxnet==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.cpu.mkl b/docker/docker-python/Dockerfile.mxnet.python.cpu.mkl index 043932ff7c8e..dbb7d29f5037 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.cpu.mkl +++ b/docker/docker-python/Dockerfile.mxnet.python.cpu.mkl @@ -19,10 +19,11 @@ # Dockerfile to build MXNet CPU with MKL FROM ubuntu:16.04 +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-mkl +RUN pip install mxnet-mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80 b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80 index 8c83ece434a3..cb22721f48b9 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80 +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80 @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU FROM nvidia/cuda:8.0-cudnn5-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu80 +RUN pip install mxnet-cu80==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80.mkl b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80.mkl index a057c1d20cb1..eda96c90d181 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80.mkl +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu80.mkl @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU with MKL FROM nvidia/cuda:8.0-cudnn5-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu80mkl +RUN pip install mxnet-cu80mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90 b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90 index 1e3d9869ac63..cd36b8afdbd8 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90 +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90 @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU FROM nvidia/cuda:9.0-cudnn7-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu90 +RUN pip install mxnet-cu90==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90.mkl b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90.mkl index d82abd7cf523..0b274c4e0d3f 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90.mkl +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu90.mkl @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU with MKL FROM nvidia/cuda:9.0-cudnn7-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu90mkl +RUN pip install mxnet-cu90mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92 b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92 index ba5c54a2a2aa..2c43187faf79 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92 +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92 @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU FROM nvidia/cuda:9.2-cudnn7-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu92 +RUN pip install mxnet-cu92==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92.mkl b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92.mkl index 96a943980b59..db204897ef60 100644 --- a/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92.mkl +++ b/docker/docker-python/Dockerfile.mxnet.python.gpu.cu92.mkl @@ -19,10 +19,11 @@ # Dockerfile to build MXNet for GPU with MKL FROM nvidia/cuda:9.2-cudnn7-devel +ARG version -RUN apt-get update -RUN apt-get install -y wget python gcc -RUN wget https://bootstrap.pypa.io/get-pip.py -RUN python get-pip.py +RUN apt-get update && \ + apt-get install -y wget python-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python get-pip.py -RUN pip install mxnet-cu92mkl +RUN pip install mxnet-cu92mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.cpu b/docker/docker-python/Dockerfile.mxnet.python3.cpu new file mode 100644 index 000000000000..8ad9950a2f21 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.cpu @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for CPU + +FROM ubuntu:16.04 +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.cpu.mkl b/docker/docker-python/Dockerfile.mxnet.python3.cpu.mkl new file mode 100644 index 000000000000..c6312891c6ea --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.cpu.mkl @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet CPU with MKL + +FROM ubuntu:16.04 +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80 b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80 new file mode 100644 index 000000000000..58af6bfeb273 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80 @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU + +FROM nvidia/cuda:8.0-cudnn5-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu80==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80.mkl b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80.mkl new file mode 100644 index 000000000000..059f002c8560 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu80.mkl @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU with MKL + +FROM nvidia/cuda:8.0-cudnn5-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu80mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90 b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90 new file mode 100644 index 000000000000..a860de918054 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90 @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU + +FROM nvidia/cuda:9.0-cudnn7-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu90==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90.mkl b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90.mkl new file mode 100644 index 000000000000..c0b6145b28e7 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu90.mkl @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU with MKL + +FROM nvidia/cuda:9.0-cudnn7-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu90mkl==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92 b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92 new file mode 100644 index 000000000000..6d877961db9f --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92 @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU + +FROM nvidia/cuda:9.2-cudnn7-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu92==$version diff --git a/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92.mkl b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92.mkl new file mode 100644 index 000000000000..b73df97e5950 --- /dev/null +++ b/docker/docker-python/Dockerfile.mxnet.python3.gpu.cu92.mkl @@ -0,0 +1,29 @@ +# -*- mode: dockerfile -*- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Dockerfile to build MXNet for GPU with MKL + +FROM nvidia/cuda:9.2-cudnn7-devel +ARG version + +RUN apt-get update && \ + apt-get install -y wget python3-dev gcc && \ + wget https://bootstrap.pypa.io/get-pip.py && \ + python3 get-pip.py + +RUN pip3 install mxnet-cu92mkl==$version diff --git a/docker/docker-python/README.md b/docker/docker-python/README.md index 6d17c50439ae..767be6d1eb33 100644 --- a/docker/docker-python/README.md +++ b/docker/docker-python/README.md @@ -19,31 +19,40 @@ # Release Python Docker Images for MXNet -The `docker-python` directory can be used to release mxnet python docker images to dockerhub after any mxnet release. -It uses the appropriate pip binaries to build different docker images as - -* cpu -* cpu_mkl -* latest (same as cpu) -* gpu_cu90 -* gpu_cu90_mkl -* gpu (same as gpu_cu90) -* gpu_cu80 -* gpu_cu80_mkl -* gpu_cu92 -* gpu_cu92_mkl - - -** Note: If you want to use a different pip binary (specific mxnet or cuda version, etc), you can edit the last line of the cpu or gpu dockerfile as required. +The `docker-python` directory can be used to release mxnet python docker images to dockerhub after any mxnet release. +It uses the appropriate pip binaries to build different docker images. Both python2 (default) and python3 images are available as - +* {version}_cpu +* {version}_cpu_mkl +* {version}_gpu_cu90 +* {version}_gpu_cu90_mkl +* {version}_gpu_cu80 +* {version}_gpu_cu80_mkl +* {version}_gpu_cu92 +* {version}_gpu_cu92_mkl +* {version}_cpu_py3 +* {version}_cpu_mkl_py3 +* {version}_gpu_cu90_py3 +* {version}_gpu_cu90_mkl_py3 +* {version}_gpu_cu80_py3 +* {version}_gpu_cu80_mkl_py3 +* {version}_gpu_cu92_py3 +* {version}_gpu_cu92_mkl_py3 -Refer: https://pypi.org/project/mxnet/ +And the following tags will be available without the version string in the image name (for Benchmarking and other use cases): +* latest (same as {version}_cpu) +* gpu (same as {version}_gpu_cu90) +* latest_cpu_mkl_py2 (same as {version}_cpu_mkl) +* latest_cpu_mkl_py3 (same as {version}_cpu_mkl_py3) +* latest_gpu_mkl_py2 (same as {version}_gpu_cu90_mkl) +* latest_gpu_mkl_py3 (same as {version}_gpu_cu90_mkl_py3) -### Usage -`./build_python_dockerfile.sh ` +Refer: https://pypi.org/project/mxnet/ -For example: -`./build_python_dockerfile.sh 1.3.0 ~/build-docker/incubator-mxnet` +### Using the Build Script +`./build_python_dockerfile.sh ` -** Note: The build script picks up the latest pip binaries. This means it uses the latest released mxnet version. The version specified as a parameter to the script is only used to tag the built image correctly. +For example: +`./build_python_dockerfile.sh 1.3.0 1.3.0.post0 ~/build-docker/incubator-mxnet` ### Tests run * [test_conv.py](https://github.com/apache/incubator-mxnet/blob/master/tests/python/train/test_conv.py) @@ -58,3 +67,10 @@ Credentials can be provided in the following ways: * **Set Environment Variables:** Set the following environment variables which the script will pick up to login to dockerhub at runtime - * $MXNET_DOCKERHUB_PASSWORD * $MXNET_DOCKERHUB_USERNAME + + +### Using the Docker Images +* The MXNet Python Docker images can be found here: https://hub.docker.com/r/mxnet/python/ + +* Docker Pull Command: `docker pull mxnet/python:` +* Get started: `docker run -it mxnet/python: bash` diff --git a/docker/docker-python/build_python_dockerfile.sh b/docker/docker-python/build_python_dockerfile.sh index 24a44c28970c..d0f24c8d6a5e 100755 --- a/docker/docker-python/build_python_dockerfile.sh +++ b/docker/docker-python/build_python_dockerfile.sh @@ -17,20 +17,19 @@ # specific language governing permissions and limitations # under the License. -set -e - # Check Params programname=$0 function usage { - echo "usage: $programname [version] [path]" + echo "usage: $programname [version] [pip_tag] [path]" echo " [version] Mxnet Version to build" + echo " [pip_tag] Pip Tag to use" echo " [path] Path to MXNet repository (to run tests)" echo " " exit 1 } -if [ $# -le 1 ] || [ $# -ge 3 ] +if [ $# -le 2 ] || [ $# -ge 4 ] then usage exit 1 @@ -39,29 +38,37 @@ fi # Two params provided echo "Building Docker Images for Apache MXNet (Incubating) v$1" mxnet_version="${1}" -test_dir="${2}" +pip_tag="${2}" +test_dir="${3}" -docker_build_image(){ - echo "Building docker image mxnet/python:${1}" - docker build -t mxnet/python:${1} -f ${2} . -} +# Remove the logs directory if it already exists else it may fail due to old logs. +LOGDIR=~/temp/docker_logs +if [ -d "${LOGDIR}" ]; then + rm -rf ${LOGDIR} +fi + +# Create ~/temp if it does not exist +mkdir -p ~/temp +mkdir ${LOGDIR} -docker_tag_image(){ - docker tag mxnet/python:${1} mxnet/python:${2} -} +# Functions docker_test_image_cpu(){ - echo "Running tests on mxnet/python:${1}" - docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/docker/docker-python/test_mxnet.py ${mxnet_version}" - docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/tests/python/train/test_conv.py" - docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/example/image-classification/train_mnist.py" + image_tag="${1}" + python_version="${2}" + echo "Running tests on mxnet/python:${image_tag}" + docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/docker/docker-python/test_mxnet.py ${mxnet_version}" + docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/tests/python/train/test_conv.py" + docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/example/image-classification/train_mnist.py" } docker_test_image_gpu(){ + image_tag="${1}" + python_version="${2}" echo "Running tests on mxnet/python:${1}" - nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/docker/docker-python/test_mxnet.py ${mxnet_version}" - nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/tests/python/train/test_conv.py --gpu" - nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${1} bash -c "python /mxnet/example/image-classification/train_mnist.py --gpus 2" + nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/docker/docker-python/test_mxnet.py ${mxnet_version}" + nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/tests/python/train/test_conv.py --gpu" + nvidia-docker run -v ${test_dir}:/mxnet mxnet/python:${image_tag} bash -c "${python_version} /mxnet/example/image-classification/train_mnist.py --gpus 0,1,2,3" } # if both $MXNET_DOCKERHUB_PASSWORD and $MXNET_DOCKERHUB_USERNAME environment variables are set, docker will automatically login @@ -79,59 +86,132 @@ docker_account_logout(){ } docker_push_image(){ - docker push mxnet/python:${1} + image_tag="${1}" + docker push mxnet/python:${image_tag} } +docker_generate_image_cpu(){ + image_tag="${1}" + dockerfile="${2}" + python_version="${3}" + echo "Building docker image mxnet/python:${image_tag}" + docker build --build-arg version=${pip_tag} -t mxnet/python:${image_tag} -f ${dockerfile} . + docker_test_image_cpu ${image_tag} ${python_version} +} -# Build and Test dockerfiles - CPU -docker_build_image "${mxnet_version}_cpu" "Dockerfile.mxnet.python.cpu" -docker_test_image_cpu "${mxnet_version}_cpu" - -docker_build_image "${mxnet_version}_cpu_mkl" "Dockerfile.mxnet.python.cpu.mkl" -docker_test_image_cpu "${mxnet_version}_cpu_mkl" - -docker_tag_image "${mxnet_version}_cpu" "latest" -docker_test_image_cpu "latest" - - -#Build and Test dockerfiles - GPU -docker_build_image "${mxnet_version}_gpu_cu90" "Dockerfile.mxnet.python.gpu.cu90" -docker_test_image_gpu "${mxnet_version}_gpu_cu90" - -docker_build_image "${mxnet_version}_gpu_cu90_mkl" "Dockerfile.mxnet.python.gpu.cu90.mkl" -docker_test_image_gpu "${mxnet_version}_gpu_cu90_mkl" +docker_tag_image_cpu(){ + original_tag="${1}" + image_tag="${2}" + python_version="${3}" + docker tag mxnet/python:${original_tag} mxnet/python:${image_tag} + docker_test_image_cpu ${image_tag} ${python_version} +} -docker_tag_image "${mxnet_version}_gpu_cu90" "gpu" -docker_test_image_gpu "gpu" +docker_generate_image_gpu(){ + image_tag="${1}" + dockerfile="${2}" + python_version="${3}" + echo "Building docker image mxnet/python:${1}" + docker build --build-arg version=${pip_tag} -t mxnet/python:${image_tag} -f ${dockerfile} . + docker_test_image_gpu ${image_tag} ${python_version} +} -docker_build_image "${mxnet_version}_gpu_cu80" "Dockerfile.mxnet.python.gpu.cu80" -docker_test_image_gpu "${mxnet_version}_gpu_cu80" +docker_tag_image_gpu(){ + original_tag="${1}" + image_tag="${2}" + python_version="${3}" + docker tag mxnet/python:${original_tag} mxnet/python:${image_tag} + docker_test_image_gpu ${image_tag} ${python_version} +} -docker_build_image "${mxnet_version}_gpu_cu80_mkl" "Dockerfile.mxnet.python.gpu.cu80.mkl" -docker_test_image_gpu "${mxnet_version}_gpu_cu80_mkl" +check_errors(){ + egrep -i "not found|error|returned a non-zero code|fail" ${LOGDIR}/docker* + if [ $? -eq 0 ]; then + echo "ERROR: One of the build/test commands failed. Refer to the filename above to see which image tag caused it." + exit 1 + else + echo "Success: No errors found" + fi +} -docker_build_image "${mxnet_version}_gpu_cu92" "Dockerfile.mxnet.python.gpu.cu92" -docker_test_image_gpu "${mxnet_version}_gpu_cu92" +# Build and Test dockerfiles - CPU +docker_generate_image_cpu "${mxnet_version}_cpu" "Dockerfile.mxnet.python.cpu" "python" > ${LOGDIR}/docker_cpu.out 2>&1 & +docker_generate_image_cpu "${mxnet_version}_cpu_mkl" "Dockerfile.mxnet.python.cpu.mkl" "python" > ${LOGDIR}/docker_cpu_mkl.out 2>&1 & -docker_build_image "${mxnet_version}_gpu_cu92_mkl" "Dockerfile.mxnet.python.gpu.cu92.mkl" -docker_test_image_gpu "${mxnet_version}_gpu_cu92_mkl" +#Build and Test dockerfiles - GPU +docker_generate_image_gpu "${mxnet_version}_gpu_cu90" "Dockerfile.mxnet.python.gpu.cu90" "python" > ${LOGDIR}/docker_gpu_cu90.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu90_mkl" "Dockerfile.mxnet.python.gpu.cu90.mkl" "python" > ${LOGDIR}/docker_gpu_cu90_mkl.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu80" "Dockerfile.mxnet.python.gpu.cu80" "python" > ${LOGDIR}/docker_gpu_cu80.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu80_mkl" "Dockerfile.mxnet.python.gpu.cu80.mkl" "python" > ${LOGDIR}/docker_gpu_cu80_mkl.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu92" "Dockerfile.mxnet.python.gpu.cu92" "python" > ${LOGDIR}/docker_gpu_cu92.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu92_mkl" "Dockerfile.mxnet.python.gpu.cu92.mkl" "python" > ${LOGDIR}/docker_gpu_cu92_mkl.out 2>&1 + +echo "Waiting for MXNet Python2 Docker Images to Build" +wait + +# Build and Test Python3 dockerfiles - CPU +docker_generate_image_cpu "${mxnet_version}_cpu_py3" "Dockerfile.mxnet.python3.cpu" "python3" > ${LOGDIR}/docker_cpu_py3.out 2>&1 & +docker_generate_image_cpu "${mxnet_version}_cpu_mkl_py3" "Dockerfile.mxnet.python3.cpu.mkl" "python3" > ${LOGDIR}/docker_cpu_mkl_py3.out 2>&1 & + +#Build and Test Python3 dockerfiles - GPU +docker_generate_image_gpu "${mxnet_version}_gpu_cu90_py3" "Dockerfile.mxnet.python3.gpu.cu90" "python3" > ${LOGDIR}/docker_gpu_cu90_py3.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu90_mkl_py3" "Dockerfile.mxnet.python3.gpu.cu90.mkl" "python3" > ${LOGDIR}/docker_gpu_cu90_mkl_py3.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu80_py3" "Dockerfile.mxnet.python3.gpu.cu80" "python3" > ${LOGDIR}/docker_gpu_cu80_py3.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu80_mkl_py3" "Dockerfile.mxnet.python3.gpu.cu80.mkl" "python3" > ${LOGDIR}/docker_gpu_cu80_mkl_py3.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu92_py3" "Dockerfile.mxnet.python3.gpu.cu92" "python3" > ${LOGDIR}/docker_gpu_cu92_py3.out 2>&1 & +docker_generate_image_gpu "${mxnet_version}_gpu_cu92_mkl_py3" "Dockerfile.mxnet.python3.gpu.cu92.mkl" "python3" > ${LOGDIR}/docker_gpu_cu92_mkl_py3.out 2>&1 + +echo "Waiting for MXNet Python3 Docker Images to Build" +wait + +echo "Re-Tag 6 images with version-free names (for Benchmarking) - only after previous builds complete. " +docker_tag_image_cpu "${mxnet_version}_cpu" "latest" "python" > ${LOGDIR}/docker_latest.out 2>&1 & +docker_tag_image_gpu "${mxnet_version}_gpu_cu90" "gpu" "python" > ${LOGDIR}/docker_gpu.out 2>&1 & +docker_tag_image_cpu "${mxnet_version}_cpu_mkl" "latest_cpu_mkl_py2" "python" > ${LOGDIR}/docker_latest_cpu_mkl_py2.out 2>&1 & +docker_tag_image_cpu "${mxnet_version}_cpu_mkl_py3" "latest_cpu_mkl_py3" "python3" > ${LOGDIR}/docker_latest_cpu_mkl_py3.out 2>&1 & +docker_tag_image_gpu "${mxnet_version}_gpu_cu90_mkl" "latest_gpu_mkl_py2" "python" > ${LOGDIR}/docker_latest_gpu_mkl_py2.out 2>&1 & +docker_tag_image_gpu "${mxnet_version}_gpu_cu90_mkl_py3" "latest_gpu_mkl_py3" "python3" > ${LOGDIR}/docker_latest_gpu_mkl_py3.out 2>&1 +wait + +# Parse all the docker logfiles to make sure there is no error. Fail script if error is found. +check_errors # Push dockerfiles echo "All images were successfully built. Now login to dockerhub and push images" docker_account_login +# Python2 docker_push_image "${mxnet_version}_cpu" docker_push_image "${mxnet_version}_cpu_mkl" -docker_push_image "latest" docker_push_image "${mxnet_version}_gpu_cu90" docker_push_image "${mxnet_version}_gpu_cu90_mkl" -docker_push_image "gpu" docker_push_image "${mxnet_version}_gpu_cu80" docker_push_image "${mxnet_version}_gpu_cu80_mkl" docker_push_image "${mxnet_version}_gpu_cu92" docker_push_image "${mxnet_version}_gpu_cu92_mkl" +# Python3 +docker_push_image "${mxnet_version}_cpu_py3" +docker_push_image "${mxnet_version}_cpu_mkl_py3" +docker_push_image "${mxnet_version}_gpu_cu90_py3" +docker_push_image "${mxnet_version}_gpu_cu90_mkl_py3" +docker_push_image "${mxnet_version}_gpu_cu80_py3" +docker_push_image "${mxnet_version}_gpu_cu80_mkl_py3" +docker_push_image "${mxnet_version}_gpu_cu92_py3" +docker_push_image "${mxnet_version}_gpu_cu92_mkl_py3" + +docker_push_image "latest" +docker_push_image "gpu" +docker_push_image "latest_cpu_mkl_py2" +docker_push_image "latest_cpu_mkl_py3" +docker_push_image "latest_gpu_mkl_py2" +docker_push_image "latest_gpu_mkl_py3" + + docker_account_logout echo "Successfully Built, Tested and Pushed all Images to Dockerhub. Link: https://hub.docker.com/r/mxnet/python/tags/" + +#Delete the log directory since everything succeeded: +rm -rf ${LOGDIR} \ No newline at end of file diff --git a/docs/_static/js/options.js b/docs/_static/js/options.js index f4fde4e1f2ef..ec3977601c92 100644 --- a/docs/_static/js/options.js +++ b/docs/_static/js/options.js @@ -1,8 +1,9 @@ + /*! * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file + * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at @@ -18,7 +19,7 @@ */ /* Installation page display functions for install selector */ -var versionSelect = defaultVersion = 'v1.3.1'; +var versionSelect = defaultVersion = 'v1.4.0'; var platformSelect = 'Linux'; var languageSelect = 'Python'; var processorSelect = 'CPU'; diff --git a/docs/_static/mxnet-theme/index.html b/docs/_static/mxnet-theme/index.html index 302b1732233d..34f675853924 100644 --- a/docs/_static/mxnet-theme/index.html +++ b/docs/_static/mxnet-theme/index.html @@ -23,9 +23,9 @@
-

MXNet 1.3.1 Released

-

This release includes bug fixes, performance improvements, and documentation updates.

- Learn More +

MXNet 1.4.0 Released

+

This release introduces the Java Inference API and Julia API, as well as Control Flow Operators, MKLDNN optimizations, and SVRG optimization.

+ Learn More

A 60-minute Gluon Crash Course

diff --git a/docs/_static/mxnet-theme/navbar.html b/docs/_static/mxnet-theme/navbar.html index 50c3debba395..d2449fa76e1b 100644 --- a/docs/_static/mxnet-theme/navbar.html +++ b/docs/_static/mxnet-theme/navbar.html @@ -11,7 +11,7 @@

Gluon @@ -23,11 +23,11 @@

  • Python
  • C++
  • Clojure
  • +
  • Java
  • Julia
  • Perl
  • R
  • Scala
  • -
  • Java
  • @@ -77,11 +77,11 @@

  • Python
  • C++
  • Clojure
  • +
  • Java
  • Julia
  • Perl
  • R
  • Scala
  • -
  • Java
  • - +