Apache MXNet 快速指南



Apache MXNet - 簡介

本章重點介紹 Apache MXNet 的功能,並討論此深度學習軟體框架的最新版本。

什麼是 MXNet?

Apache MXNet 是一個強大的開源深度學習軟體框架工具,幫助開發人員構建、訓練和部署深度學習模型。在過去的幾年裡,從醫療保健到交通運輸再到製造業,事實上,在日常生活的方方面面,深度學習的影響都非常廣泛。如今,許多公司都尋求利用深度學習來解決一些難題,例如人臉識別、目標檢測、光學字元識別 (OCR)、語音識別和機器翻譯。

這就是 Apache MXNet 受以下機構支援的原因:

  • 一些大型公司,例如英特爾、百度、微軟、Wolfram Research 等。

  • 公共雲提供商,包括亞馬遜網路服務 (AWS) 和微軟 Azure。

  • 一些大型研究機構,例如卡內基梅隆大學、麻省理工學院、華盛頓大學和香港科技大學。

為什麼選擇 Apache MXNet?

存在各種深度學習平臺,例如 Torch7、Caffe、Theano、TensorFlow、Keras、Microsoft Cognitive Toolkit 等,您可能想知道為什麼選擇 Apache MXNet?讓我們來看看其中的一些原因。

  • Apache MXNet 解決現有深度學習平臺最大的問題之一。問題在於,為了使用深度學習平臺,必須學習另一個不同程式設計風格的系統。

  • 藉助 Apache MXNet,開發人員可以充分利用 GPU 和雲計算的能力。

  • Apache MXNet 可以加速任何數值計算,並特別注重加快大型 DNN(深度神經網路)的開發和部署。

  • 它為使用者提供了命令式和符號式程式設計的能力。

各種功能

如果您正在尋找一個靈活的深度學習庫來快速開發尖端的深度學習研究或一個強大的平臺來推動生產工作負載,那麼您的搜尋在 Apache MXNet 結束。這是因為它具有以下功能:

分散式訓練

無論是多 GPU 還是多主機訓練,都具有接近線性的擴充套件效率,Apache MXNet 允許開發人員充分利用其硬體。MXNet 還支援與 Horovod 整合,Horovod 是 Uber 建立的一個開源分散式深度學習框架。

對於此整合,Horovod 中定義了一些常見的分散式 API:

  • horovod.broadcast()

  • horovod.allgather()

  • horovod.allgather()

在這方面,MXNet 為我們提供了以下功能:

  • 裝置放置- 藉助 MXNet,我們可以輕鬆指定每個資料結構 (DS)。

  • 自動微分- Apache MXNet 自動執行微分,即導數計算。

  • 多 GPU 訓練- MXNet 允許我們根據可用 GPU 的數量實現擴充套件效率。

  • 最佳化的預定義層- 我們可以在 MXNet 中編寫自己的層,也可以最佳化預定義層以提高速度。

混合

Apache MXNet 為其使用者提供混合前端。藉助 Gluon Python API,它可以彌合其命令式和符號式功能之間的差距。可以透過呼叫其混合功能來實現。

更快的計算

線性運算(例如數十或數百個矩陣乘法)是深度神經網路的計算瓶頸。為了解決這個瓶頸,MXNet 提供了:

  • 針對 GPU 的最佳化數值計算

  • 針對分散式生態系統的最佳化數值計算

  • 藉助自動化常用工作流程,可以簡要地表達標準神經網路。

語言繫結

MXNet 與 Python 和 R 等高階語言深度整合。它還支援其他程式語言,例如:

  • Scala

  • Julia

  • Clojure

  • Java

  • C/C++

  • Perl

我們不需要學習任何新的程式語言,相反,MXNet 結合混合功能,允許從 Python 到我們選擇的程式語言中的部署異常平滑的過渡。

最新版本 MXNet 1.6.0

Apache 軟體基金會 (ASF) 於 2020 年 2 月 21 日在 Apache 許可證 2.0 下發布了 Apache MXNet 的穩定版本 1.6.0。這是最後一個支援 Python 2 的 MXNet 版本,因為 MXNet 社群投票決定不再在後續版本中支援 Python 2。讓我們來看看此版本為其使用者帶來的一些新功能。

與 NumPy 相容的介面

由於其靈活性和通用性,NumPy 已被機器學習從業人員、科學家和學生廣泛使用。但眾所周知,如今,圖形處理單元 (GPU) 等硬體加速器已越來越多地融入各種機器學習 (ML) 工具包中,NumPy 使用者為了利用 GPU 的速度,需要切換到具有不同語法的新的框架。

藉助 MXNet 1.6.0,Apache MXNet 正在朝著與 NumPy 相容的程式設計體驗邁進。新的介面為熟悉 NumPy 語法的從業人員提供了等效的可用性和表達能力。同時,MXNet 1.6.0 還使現有的 NumPy 系統能夠利用 GPU 等硬體加速器來加快大規模計算。

與 Apache TVM 整合

Apache TVM 是一款面向 CPU、GPU 和專用加速器等硬體後端的開源端到端深度學習編譯器堆疊,旨在彌合注重生產力的深度學習框架與注重效能的硬體後端之間的差距。藉助最新的 MXNet 1.6.0 版本,使用者可以利用 Apache(孵化) TVM 在 Python 程式語言中實現高效能運算子核心。此新功能的兩個主要優點如下:

  • 簡化了以前的基於 C++ 的開發過程。

  • 能夠跨多個硬體後端(例如 CPU、GPU 等)共享相同的實現。

現有功能的改進

除了上述 MXNet 1.6.0 的功能外,它還在現有功能上提供了一些改進。改進如下:

對 GPU 的逐元素運算分組

眾所周知,逐元素運算的效能受記憶體頻寬限制,這就是為什麼連結此類運算可能會降低整體效能的原因。Apache MXNet 1.6.0 執行逐元素運算融合,這實際上會在可能的情況下生成即時融合運算。這種逐元素運算融合還可以減少儲存需求並提高整體效能。

簡化常用表示式

MXNet 1.6.0 消除了冗餘表示式並簡化了常用表示式。這種增強還提高了記憶體使用率和總執行時間。

最佳化

MXNet 1.6.0 還為現有功能和運算子提供了各種最佳化,如下所示:

  • 自動混合精度

  • Gluon Fit API

  • MKL-DNN

  • 大型張量支援

  • TensorRT 整合

  • 高階梯度支援

  • 運算子

  • 運算子效能分析器

  • ONNX 匯入/匯出

  • Gluon API 的改進

  • Symbol API 的改進

  • 100 多個錯誤修復

Apache MXNet - 安裝 MXNet

要開始使用 MXNet,我們需要做的第一件事是在我們的計算機上安裝它。Apache MXNet 幾乎可以在所有可用的平臺上執行,包括 Windows、Mac 和 Linux。

Linux 作業系統

我們可以透過以下方式在 Linux 作業系統上安裝 MXNet:

圖形處理單元 (GPU)

在這裡,我們將使用 Pip、Docker 和 Source 等多種方法來安裝 MXNet,當我們使用 GPU 進行處理時:

使用 Pip 方法

您可以使用以下命令在您的 Linus 作業系統上安裝 MXNet:

pip install mxnet

Apache MXNet 還提供 MKL pip 包,在英特爾硬體上執行時,這些包速度更快。例如,此處mxnet-cu101mkl 表示:

  • 該軟體包使用 CUDA/cuDNN 構建

  • 該軟體包啟用了 MKL-DNN

  • CUDA 版本為 10.1

對於其他選項,您還可以參考 https://pypi.org/project/mxnet/

使用 Docker

您可以在 DockerHub 上找到帶有 MXNet 的 Docker 映象,該映象位於 https://hub.docker.com/u/mxnet 讓我們檢視以下步驟,以使用 Docker 和 GPU 安裝 MXNet:

步驟 1- 首先,按照位於 https://dockerdocs.tw/engine/install/ubuntu/ 的 Docker 安裝說明。我們需要在我們的機器上安裝 Docker。

步驟 2- 為了啟用從 Docker 容器中使用 GPU,接下來我們需要安裝 nvidia-docker-plugin。您可以按照 https://github.com/NVIDIA/nvidia-docker/wiki 中提供的安裝說明進行操作。

步驟 3- 使用以下命令,您可以拉取 MXNet Docker 映象:

$ sudo docker pull mxnet/python:gpu

現在,為了檢視 mxnet/python docker 映象拉取是否成功,我們可以列出 docker 映象,如下所示:

$ sudo docker images

為了獲得 MXNet 最快的推理速度,建議使用帶有 Intel MKL-DNN 的最新 MXNet。請檢查以下命令:

$ sudo docker pull mxnet/python:1.3.0_cpu_mkl
$ sudo docker images

從原始碼

要從原始碼使用 GPU 構建 MXNet 共享庫,首先我們需要設定 CUDA 和 cuDNN 的環境,如下所示:

  • 下載並安裝 CUDA 工具包,此處推薦 CUDA 9.2。

  • 接下來下載 cuDNN 7.1.4。

  • 現在我們需要解壓縮檔案。還需要更改到 cuDNN 根目錄。還要將標頭和庫移動到本地 CUDA 工具包資料夾,如下所示:

tar xvzf cudnn-9.2-linux-x64-v7.1
sudo cp -P cuda/include/cudnn.h /usr/local/cuda/include
sudo cp -P cuda/lib64/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*
sudo ldconfig

設定 CUDA 和 cuDNN 的環境後,請按照以下步驟從原始碼構建 MXNet 共享庫:

步驟 1- 首先,我們需要安裝必要的軟體包。這些依賴項在 Ubuntu 16.04 或更高版本上是必需的。

sudo apt-get update
sudo apt-get install -y build-essential git ninja-build ccache libopenblas-dev 
libopencv-dev cmake

步驟 2- 在此步驟中,我們將下載 MXNet 原始碼並進行配置。首先,讓我們使用以下命令克隆儲存庫:

git clone –recursive https://github.com/apache/incubator-mxnet.git mxnet
cd mxnet
cp config/linux_gpu.cmake #for build with CUDA

步驟 3- 使用以下命令,您可以構建 MXNet 核心共享庫:

rm -rf build
mkdir -p build && cd build
cmake -GNinja ..
cmake --build .

關於上述步驟,有兩點需要注意:

如果要構建 Debug 版本,則指定如下:

cmake -DCMAKE_BUILD_TYPE=Debug -GNinja ..

為了設定並行編譯作業的數量,請指定以下內容:

cmake --build . --parallel N

成功構建 MXNet 核心共享庫後,在MXNet 專案根目錄中的build資料夾中,您將找到安裝語言繫結(可選)所需的libmxnet.so

中央處理器 (CPU)

在這裡,我們將使用 Pip、Docker 和 Source 等多種方法來安裝 MXNet,當我們使用 CPU 進行處理時:

使用 Pip 方法

您可以使用以下命令在您的 Linus 作業系統上安裝 MXNet:

pip install mxnet

Apache MXNet 也提供支援 MKL-DNN 的 pip 包,在英特爾硬體上執行時速度更快。

pip install mxnet-mkl

使用 Docker

您可以在 DockerHub 上找到包含 MXNet 的 Docker 映象,地址為 https://hub.docker.com/u/mxnet。下面我們來看看使用 Docker 和 CPU 安裝 MXNet 的步驟:

步驟 1− 首先,按照 Docker 安裝說明(位於 https://dockerdocs.tw/engine/install/ubuntu/)安裝 Docker。

步驟 2− 使用以下命令,您可以拉取 MXNet Docker 映象:

$ sudo docker pull mxnet/python

現在,為了檢視 mxnet/python Docker 映象是否成功拉取,我們可以列出 Docker 映象,如下所示:

$ sudo docker images

為了獲得 MXNet 最快的推理速度,建議使用最新的帶有 Intel MKL-DNN 的 MXNet。

檢視以下命令:

$ sudo docker pull mxnet/python:1.3.0_cpu_mkl
$ sudo docker images

從原始碼

要從原始碼構建帶有 CPU 的 MXNet 共享庫,請按照以下步驟操作:

步驟 1- 首先,我們需要安裝必要的軟體包。這些依賴項在 Ubuntu 16.04 或更高版本上是必需的。

sudo apt-get update

sudo apt-get install -y build-essential git ninja-build ccache libopenblas-dev libopencv-dev cmake

步驟 2− 在此步驟中,我們將下載 MXNet 原始碼並進行配置。首先,讓我們使用以下命令克隆儲存庫:

git clone –recursive https://github.com/apache/incubator-mxnet.git mxnet

cd mxnet
cp config/linux.cmake config.cmake

步驟 3− 使用以下命令,您可以構建 MXNet 核心共享庫:

rm -rf build
mkdir -p build && cd build
cmake -GNinja ..
cmake --build .

關於上述步驟,有兩點需要注意:

如果要構建除錯版本,則指定如下:

cmake -DCMAKE_BUILD_TYPE=Debug -GNinja ..

要設定並行編譯作業的數量,請指定以下內容:

cmake --build . --parallel N

成功構建 MXNet 核心共享庫後,您將在 MXNet 專案根目錄下的 build 資料夾中找到 libmxnet.so,這是安裝語言繫結(可選)所需的。

macOS

我們可以透過以下方式在 macOS 上安裝 MXNet:

圖形處理單元 (GPU)

如果您計劃在 macOS 上使用 GPU 構建 MXNet,則沒有可用的 Pip 和 Docker 方法。在這種情況下,唯一的方法是從原始碼構建它。

從原始碼

要從原始碼構建帶有 GPU 的 MXNet 共享庫,首先我們需要為 CUDA 和 cuDNN 設定環境。您需要遵循位於 https://docs.nvidia.comNVIDIA CUDA 安裝指南和位於 https://docs.nvidia.com/deeplearningcuDNN 安裝指南(適用於 macOS)。

請注意,2019 年 CUDA 停止支援 macOS。事實上,未來的 CUDA 版本也可能不支援 macOS。

設定好 CUDA 和 cuDNN 的環境後,請按照以下步驟從原始碼在 OS X (Mac) 上安裝 MXNet:

步驟 1− 由於我們需要 OS X 上的一些依賴項,因此首先需要安裝必要的軟體包。

xcode-select –-install #Install OS X Developer Tools

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" #Install Homebrew

brew install cmake ninja ccache opencv # Install dependencies

我們也可以在沒有 OpenCV 的情況下構建 MXNet,因為 opencv 是一個可選的依賴項。

步驟 2− 在此步驟中,我們將下載 MXNet 原始碼並進行配置。首先,讓我們使用以下命令克隆儲存庫:

git clone –-recursive https://github.com/apache/incubator-mxnet.git mxnet

cd mxnet
cp config/linux.cmake config.cmake

對於支援 GPU 的版本,有必要先安裝 CUDA 依賴項,因為當嘗試在沒有 GPU 的機器上構建支援 GPU 的版本時,MXNet 構建無法自動檢測您的 GPU 架構。在這種情況下,MXNet 將針對所有可用的 GPU 架構。

步驟 3- 使用以下命令,您可以構建 MXNet 核心共享庫:

rm -rf build
mkdir -p build && cd build
cmake -GNinja ..
cmake --build .

關於上述步驟,有兩點需要注意:

如果要構建 Debug 版本,則指定如下:

cmake -DCMAKE_BUILD_TYPE=Debug -GNinja ..

要設定並行編譯作業的數量,請指定以下內容:

cmake --build . --parallel N

成功構建 MXNet 核心共享庫後,您將在 MXNet 專案根目錄下的 build 資料夾中找到 libmxnet.dylib,這是安裝語言繫結(可選)所需的。

中央處理器 (CPU)

這裡,我們將使用 Pip、Docker 和原始碼三種方法來安裝 MXNet(使用 CPU 進行處理):

使用 Pip 方法

您可以使用以下命令在您的 Linus 系統上安裝 MXNet:

pip install mxnet

使用 Docker

您可以在 DockerHub 上找到包含 MXNet 的 Docker 映象,地址為 https://hub.docker.com/u/mxnet。下面我們來看看使用 Docker 和 CPU 安裝 MXNet 的步驟:

步驟 1− 首先,按照位於 https://dockerdocs.tw/docker-for-macDocker 安裝說明,我們需要在我們的機器上安裝 Docker。

步驟 2− 使用以下命令,您可以拉取 MXNet Docker 映象:

$ docker pull mxnet/python

現在,為了檢視 mxnet/python Docker 映象是否成功拉取,我們可以列出 Docker 映象,如下所示:

$ docker images

為了獲得 MXNet 最快的推理速度,建議使用最新的帶有 Intel MKL-DNN 的 MXNet。檢視以下命令:

$ docker pull mxnet/python:1.3.0_cpu_mkl
$ docker images

從原始碼

按照以下步驟從原始碼在 OS X (Mac) 上安裝 MXNet:

步驟 1− 由於我們需要 OS X 上的一些依賴項,因此首先需要安裝必要的軟體包。

xcode-select –-install #Install OS X Developer Tools
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" #Install Homebrew
brew install cmake ninja ccache opencv # Install dependencies

我們也可以在沒有 OpenCV 的情況下構建 MXNet,因為 opencv 是一個可選的依賴項。

步驟 2− 在此步驟中,我們將下載 MXNet 原始碼並進行配置。首先,讓我們使用以下命令克隆儲存庫:

git clone –-recursive https://github.com/apache/incubator-mxnet.git mxnet

cd mxnet

cp config/linux.cmake config.cmake

步驟 3− 使用以下命令,您可以構建 MXNet 核心共享庫:

rm -rf build
mkdir -p build && cd build
cmake -GNinja ..
cmake --build .

關於上述步驟,有兩點需要注意:

如果要構建 Debug 版本,則指定如下:

cmake -DCMAKE_BUILD_TYPE=Debug -GNinja ..

要設定並行編譯作業的數量,請指定以下內容:

cmake --build . --parallel N

成功構建 MXNet 核心共享庫後,您將在 MXNet 專案根目錄下的 build 資料夾中找到 libmxnet.dylib,這是安裝語言繫結(可選)所需的。

Windows 系統

要在 Windows 上安裝 MXNet,以下是先決條件:

最低系統要求

  • Windows 7、10、Server 2012 R2 或 Server 2016

  • Visual Studio 2015 或 2017(任何型別)

  • Python 2.7 或 3.6

  • pip

推薦系統要求

  • Windows 10、Server 2012 R2 或 Server 2016

  • Visual Studio 2017

  • 至少一個支援 NVIDIA CUDA 的 GPU

  • 支援 MKL 的 CPU:Intel® Xeon® 處理器、Intel® Core™ 處理器系列、Intel Atom® 處理器或 Intel® Xeon Phi™ 處理器

  • Python 2.7 或 3.6

  • pip

圖形處理單元 (GPU)

使用 Pip 方法:

如果您計劃在 Windows 上使用 NVIDIA GPU 構建 MXNet,則可以使用 Python 包安裝支援 CUDA 的 MXNet,有兩種方法:

安裝 CUDA 支援

以下是我們可以使用的方法來設定支援 CUDA 的 MXNet。

步驟 1− 首先安裝 Microsoft Visual Studio 2017 或 Microsoft Visual Studio 2015。

步驟 2− 接下來,下載並安裝 NVIDIA CUDA。建議使用 CUDA 9.2 或 9.0 版本,因為過去在 CUDA 9.1 中發現了一些問題。

步驟 3− 現在,下載並安裝 NVIDIA_CUDA_DNN。

步驟 4− 最後,使用以下 pip 命令安裝支援 CUDA 的 MXNet:

pip install mxnet-cu92

安裝 CUDA 和 MKL 支援

以下是我們可以使用的方法來設定支援 CUDA 和 MKL 的 MXNet。

步驟 1− 首先安裝 Microsoft Visual Studio 2017 或 Microsoft Visual Studio 2015。

步驟 2− 接下來,下載並安裝 Intel MKL

步驟 3− 現在,下載並安裝 NVIDIA CUDA。

步驟 4− 現在,下載並安裝 NVIDIA_CUDA_DNN。

步驟 5− 最後,使用以下 pip 命令安裝支援 MKL 的 MXNet:

pip install mxnet-cu92mkl

從原始碼

要從原始碼構建帶有 GPU 的 MXNet 核心庫,我們有以下兩種方法:

方法 1− 使用 Microsoft Visual Studio 2017 構建

要使用 Microsoft Visual Studio 2017 自行構建和安裝 MXNet,您需要以下依賴項。

安裝/更新 Microsoft Visual Studio。

  • 如果您的機器上尚未安裝 Microsoft Visual Studio,請先下載並安裝它。

  • 它會提示安裝 Git。也請安裝它。

  • 如果您的機器上已經安裝了 Microsoft Visual Studio,但您想更新它,則繼續執行下一步修改您的安裝。在這裡,您將有機會更新 Microsoft Visual Studio。

按照位於 https://docs.microsoft.com/en-us 的 Visual Studio 安裝程式的說明來修改各個元件。

在 Visual Studio Installer 應用程式中,根據需要進行更新。之後查詢並選中 VC++ 2017 version 15.4 v14.11 toolset 並單擊 Modify

現在,使用以下命令將 Microsoft VS2017 的版本更改為 v14.11:

"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" -vcvars_ver=14.11

接下來,您需要下載並安裝位於 https://cmake.org/download/CMake。建議使用位於 https://cmake.org/download/CMake v3.12.2,因為它已透過 MXNet 測試。

現在,下載並執行位於 https://sourceforge.net/projects/opencvlibrary/OpenCV 包,這將解壓多個檔案。您可以選擇是否將它們放在另一個目錄中。在這裡,我們將使用路徑 C:\utils(mkdir C:\utils) 作為我們的預設路徑。

接下來,我們需要設定環境變數 OpenCV_DIR 以指向我們剛剛解壓的 OpenCV 構建目錄。為此,請開啟命令提示符並鍵入 set OpenCV_DIR=C:\utils\opencv\build

重要的一點是,如果您沒有安裝 Intel MKL(數學核心庫),則可以安裝它。

您可以使用的另一個開源包是 OpenBLAS。以下說明假設您使用的是 OpenBLAS

因此,下載位於 https://sourceforge.netOpenBlas 包,並解壓檔案,將其重新命名為 OpenBLAS 並將其放在 C:\utils 下。

接下來,我們需要設定環境變數 OpenBLAS_HOME 以指向包含 includelib 目錄的 OpenBLAS 目錄。為此,請開啟命令提示符並鍵入 set OpenBLAS_HOME=C:\utils\OpenBLAS

現在,下載並安裝位於 https://developer.nvidia.com 的 CUDA。請注意,如果您已經安裝了 CUDA,然後安裝了 Microsoft VS2017,您現在需要重新安裝 CUDA,以便您可以獲得用於 Microsoft VS2017 整合的 CUDA 工具包元件。

接下來,您需要下載並安裝 cuDNN。

接下來,您還需要下載並安裝位於 https://gitforwindows.org/ 的 git。

安裝所有必需的依賴項後,請按照以下步驟構建 MXNet 原始碼:

步驟 1− 在 Windows 中開啟命令提示符。

步驟 2− 現在,使用以下命令從 GitHub 下載 MXNet 原始碼:

cd C:\

git clone https://github.com/apache/incubator-mxnet.git --recursive

步驟 3− 接下來,驗證以下內容:

DCUDNN_INCLUDEDCUDNN_LIBRARY 環境變數指向 CUDA 安裝位置的 include 資料夾和 cudnn.lib 檔案。

C:\incubator-mxnet 是您在上一步中剛剛克隆的原始碼的位置。

步驟 4− 接下來,使用以下命令建立一個構建目錄並轉到該目錄,例如:

mkdir C:\incubator-mxnet\build
cd C:\incubator-mxnet\build

步驟 5− 現在,使用 cmake 編譯 MXNet 原始碼,如下所示:

cmake -G "Visual Studio 15 2017 Win64" -T cuda=9.2,host=x64 -DUSE_CUDA=1 -DUSE_CUDNN=1 -DUSE_NVRTC=1 -DUSE_OPENCV=1 -DUSE_OPENMP=1 -DUSE_BLAS=open -DUSE_LAPACK=1 -DUSE_DIST_KVSTORE=0 -DCUDA_ARCH_LIST=Common -DCUDA_TOOLSET=9.2 -DCUDNN_INCLUDE=C:\cuda\include -DCUDNN_LIBRARY=C:\cuda\lib\x64\cudnn.lib "C:\incubator-mxnet"

步驟 6− CMake 成功完成之後,使用以下命令編譯 MXNet 原始碼:

msbuild mxnet.sln /p:Configuration=Release;Platform=x64 /maxcpucount

方法 2:使用 Microsoft Visual Studio 2015 構建

要使用 Microsoft Visual Studio 2015 自行構建和安裝 MXNet,您需要以下依賴項。

安裝/更新 Microsoft Visual Studio 2015。從原始碼構建 MXnet 的最低要求是 Microsoft Visual Studio 2015 的 Update 3。您可以使用 工具 -> 擴充套件和更新... | 產品更新 選單進行升級。

接下來,您需要下載並安裝CMake,可在https://cmake.org/download/ 獲取。建議使用CMake v3.12.2,同樣可在https://cmake.org/download/下載,因為它經過MXNet測試。

現在,下載並執行OpenCV軟體包,可在https://excellmedia.dl.sourceforge.net獲取,這將解壓多個檔案。您可以選擇是否將它們放置在另一個目錄中。

接下來,我們需要設定環境變數OpenCV_DIR,使其指向我們剛剛解壓的OpenCV構建目錄。為此,開啟命令提示符並鍵入set OpenCV_DIR=C:\opencv\build\x64\vc14\bin

重要的一點是,如果您沒有安裝 Intel MKL(數學核心庫),則可以安裝它。

您可以使用的另一個開源包是 OpenBLAS。以下說明假設您使用的是 OpenBLAS

所以,下載OpenBLAS軟體包,可在https://excellmedia.dl.sourceforge.net 獲取,並解壓檔案,將其重新命名為OpenBLAS,並將其放在C:\utils下。

接下來,我們需要設定環境變數OpenBLAS_HOME,使其指向包含include和lib目錄的OpenBLAS目錄。您可以在C:\Program files (x86)\OpenBLAS\找到該目錄。

請注意,如果您已經安裝了CUDA,然後安裝了Microsoft VS2015,則現在需要重新安裝CUDA,以便您可以獲得用於Microsoft VS2017整合的CUDA工具包元件。

接下來,您需要下載並安裝 cuDNN。

現在,我們需要設定環境變數CUDACXX,使其指向CUDA編譯器(例如:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.1\bin\nvcc.exe)

類似地,我們還需要設定環境變數CUDNN_ROOT,使其指向包含include、libbin目錄的cuDNN目錄(例如:C:\Downloads\cudnn-9.1-windows7-x64-v7\cuda)。

安裝所有必需的依賴項後,請按照以下步驟構建 MXNet 原始碼:

步驟1 - 首先,從GitHub下載MXNet原始碼 -

cd C:\
git clone https://github.com/apache/incubator-mxnet.git --recursive

步驟2 - 接下來,使用CMake在./build中建立一個Visual Studio專案。

步驟3 - 現在,在Visual Studio中,我們需要開啟解決方案檔案.sln並編譯它。這些命令將在./build/Release/或./build/Debug資料夾中生成一個名為mxnet.dll的庫。

步驟4 - CMake成功完成之後,使用以下命令編譯MXNet原始碼。

msbuild mxnet.sln /p:Configuration=Release;Platform=x64 /maxcpucount

中央處理器 (CPU)

這裡,我們將使用 Pip、Docker 和原始碼三種方法來安裝 MXNet(使用 CPU 進行處理):

使用 Pip 方法

如果您計劃在Windows上使用CPU構建MXNet,則可以使用Python包安裝MXNet,方法有兩種 -

使用CPU安裝

使用以下命令使用Python安裝使用CPU的MXNet -

pip install mxnet

使用Intel CPU安裝

如上所述,MXNet也實驗性地支援Intel MKL和MKL-DNN。使用以下命令使用Python安裝使用Intel CPU的MXNet -

pip install mxnet-mkl

使用 Docker

您可以在DockerHub找到帶有MXNet的Docker映象,網址為https://hub.docker.com/u/mxnet。讓我們看看以下步驟,使用Docker安裝使用CPU的MXNet -

步驟1 - 首先,按照Docker安裝說明進行操作,可在https://dockerdocs.tw/docker-for-mac/install檢視。我們需要在我們的機器上安裝Docker。

步驟 2− 使用以下命令,您可以拉取 MXNet Docker 映象:

$ docker pull mxnet/python

現在,為了檢視 mxnet/python Docker 映象是否成功拉取,我們可以列出 Docker 映象,如下所示:

$ docker images

為了獲得 MXNet 最快的推理速度,建議使用最新的帶有 Intel MKL-DNN 的 MXNet。

檢視以下命令 -

$ docker pull mxnet/python:1.3.0_cpu_mkl
$ docker images

在雲端和裝置上安裝MXNet

本節重點介紹如何在雲端和裝置上安裝Apache MXNet。讓我們首先學習如何在雲端安裝MXNet。

在雲端安裝MXNet

您還可以在多個雲提供商處獲得支援圖形處理單元(GPU)的Apache MXNet。您可以找到的其他兩種支援如下: -

  • GPU/CPU混合支援,用於可擴充套件推理等用例。
  • 使用AWS Elastic Inference進行階乘GPU支援。

以下是提供具有不同Apache MXNet虛擬機器的GPU支援的雲提供商:

阿里巴巴控制檯

您可以使用阿里巴巴控制檯建立可在https://docs.nvidia.com/ngc獲取的NVIDIA GPU雲虛擬機器(VM)並使用Apache MXNet。

亞馬遜網路服務

它還提供GPU支援,併為Apache MXNet提供以下服務:

Amazon SageMaker

它管理Apache MXNet模型的訓練和部署。

AWS深度學習AMI

它為Python 2和Python 3提供了預安裝的Conda環境,其中包含Apache MXNet、CUDA、cuDNN、MKL-DNN和AWS Elastic Inference。

在AWS上進行動態訓練

它提供針對實驗性手動EC2設定以及半自動CloudFormation設定的培訓。

您可以使用可在https://aws.amazon.com獲取的NVIDIA VM和亞馬遜網路服務。

谷歌雲平臺

谷歌還提供可在https://console.cloud.google.com獲取的NVIDIA GPU雲映象,可用於Apache MXNet。

微軟Azure

Microsoft Azure Marketplace還提供可在https://azuremarketplace.microsoft.com獲取的NVIDIA GPU雲映象,可用於Apache MXNet。

甲骨文雲

甲骨文還提供可在https://docs.cloud.oracle.com獲取的NVIDIA GPU雲映象,可用於Apache MXNet。

中央處理器 (CPU)

Apache MXNet可在每個雲提供商的僅CPU例項上執行。安裝方法多種多樣,例如:

  • Python pip安裝說明。

  • Docker說明。

  • 預安裝選項,例如亞馬遜網路服務提供的AWS深度學習AMI(為Python 2和Python 3預安裝了Conda環境,其中包含MXNet和MKL-DNN)。

在裝置上安裝MXNet

讓我們學習如何在裝置上安裝MXNet。

樹莓派

您還可以在樹莓派3B裝置上執行Apache MXNet,因為MXNet也支援基於Respbian ARM的作業系統。為了在樹莓派3上順利執行MXNet,建議使用記憶體超過1GB的裝置和至少有4GB可用空間的SD卡。

以下方法可以幫助您為樹莓派構建MXNet並安裝庫的Python繫結:

快速安裝

預構建的Python wheel可以在帶有Stretch的樹莓派3B上用於快速安裝。此方法的一個重要問題是,我們需要安裝多個依賴項才能使Apache MXNet正常工作。

Docker安裝

您可以按照Docker安裝說明進行操作,可在https://dockerdocs.tw/engine/install/ubuntu/檢視,以在您的機器上安裝Docker。為此,我們也可以安裝和使用社群版(CE)。

原生構建(從原始碼)

為了從原始碼安裝MXNet,我們需要遵循以下兩個步驟:

步驟1

從Apache MXNet C++原始碼構建共享庫

要在樹莓派Wheezy及更高版本上構建共享庫,我們需要以下依賴項:

  • Git - 從GitHub提取程式碼需要它。

  • Libblas - 線性代數運算需要它。

  • Libopencv - 計算機視覺相關操作需要它。但是,如果您想節省RAM和磁碟空間,它是可選的。

  • C++編譯器 - 編譯和構建MXNet原始碼需要它。以下是支援C++ 11的受支援編譯器:

    • G++(4.8或更高版本)

    • Clang(3.9-6)

使用以下命令安裝上述依賴項:

sudo apt-get update
sudo apt-get -y install git cmake ninja-build build-essential g++-4.9 c++-4.9 liblapack*
libblas* libopencv*
libopenblas* python3-dev python-dev virtualenv

接下來,我們需要克隆MXNet原始碼儲存庫。為此,請在您的主目錄中使用以下git命令:

git clone https://github.com/apache/incubator-mxnet.git --recursive

cd incubator-mxnet

現在,藉助以下命令,構建共享庫:

mkdir -p build && cd build
cmake \
-DUSE_SSE=OFF \
-DUSE_CUDA=OFF \
-DUSE_OPENCV=ON \
-DUSE_OPENMP=ON \
-DUSE_MKL_IF_AVAILABLE=OFF \
-DUSE_SIGNAL_HANDLER=ON \

-DCMAKE_BUILD_TYPE=Release \
-GNinja ..
ninja -j$(nproc)

執行上述命令後,它將啟動構建過程,該過程需要幾個小時才能完成。您將在build目錄中獲得一個名為libmxnet.so的檔案。

步驟2

安裝Apache MXNet支援的特定語言包

在此步驟中,我們將安裝MXNet Pythin繫結。為此,我們需要在MXNet目錄中執行以下命令:

cd python
pip install --upgrade pip
pip install -e .

或者,使用以下命令,您還可以建立一個可以使用pip安裝的whl包

ci/docker/runtime_functions.sh build_wheel python/ $(realpath build)

NVIDIA Jetson裝置

您還可以在NVIDIA Jetson裝置(例如TX2Nano)上執行Apache MXNet,因為MXNet也支援基於Ubuntu Arch64的作業系統。為了在NVIDIA Jetson裝置上順利執行MXNet,必須在您的Jetson裝置上安裝CUDA。

以下方法可以幫助您為NVIDIA Jetson裝置構建MXNet:

  • 使用Jetson MXNet pip wheel進行Python開發

  • 從原始碼

但是,在透過上述任何方法構建MXNet之前,您需要在Jetson裝置上安裝以下依賴項:

Python依賴項

為了使用Python API,我們需要以下依賴項:

sudo apt update
sudo apt -y install \
   build-essential \
   git \
   graphviz \
   libatlas-base-dev \
   libopencv-dev \
   python-pip
sudo pip install --upgrade \
   pip \
   setuptools
sudo pip install \
   graphviz==0.8.4 \
   jupyter \
   numpy==1.15.2

克隆MXNet原始碼儲存庫

在您的主目錄中使用以下git命令克隆MXNet原始碼儲存庫:

git clone --recursive https://github.com/apache/incubator-mxnet.git mxnet

設定環境變數

將以下內容新增到您主目錄中的.profile檔案中:

export PATH=/usr/local/cuda/bin:$PATH
export MXNET_HOME=$HOME/mxnet/
export PYTHONPATH=$MXNET_HOME/python:$PYTHONPATH

現在,使用以下命令立即應用更改:

source .profile

配置CUDA

在使用nvcc配置CUDA之前,您需要檢查正在執行的CUDA版本:

nvcc --version

假設,如果您的裝置或計算機上安裝了多個CUDA版本,並且您想切換CUDA版本,則使用以下命令並將符號連結替換為您想要的版本:

sudo rm /usr/local/cuda
sudo ln -s /usr/local/cuda-10.0 /usr/local/cuda

上述命令將切換到CUDA 10.0,該版本預安裝在NVIDIA Jetson裝置Nano上。

完成上述先決條件後,您現在可以在NVIDIA Jetson裝置上安裝MXNet。因此,讓我們瞭解可以使用哪些方法安裝MXNet:

使用Jetson MXNet pip wheel進行Python開發 - 如果您想使用準備好的Python wheel,則將其下載到您的Jetson並執行它:

原生構建(從原始碼)

為了從原始碼安裝MXNet,我們需要遵循以下兩個步驟:

步驟1

從Apache MXNet C++原始碼構建共享庫

要從Apache MXNet C++原始碼構建共享庫,您可以使用Docker方法或手動進行:

Docker方法

此方法首先需要安裝Docker並能夠在無需sudo的情況下執行(之前的步驟中也有說明)。完成後,執行以下命令透過Docker進行交叉編譯:

$MXNET_HOME/ci/build.py -p jetson

手動方法

此方法需要編輯Makefile(使用以下命令)以安裝具有CUDA繫結的MXNet,從而利用NVIDIA Jetson裝置上的圖形處理單元(GPU)

cp $MXNET_HOME/make/crosscompile.jetson.mk config.mk

編輯Makefile後,需要編輯config.mk檔案以對NVIDIA Jetson裝置進行一些額外更改。

為此,請更新以下設定:

  • 更新CUDA路徑:USE_CUDA_PATH = /usr/local/cuda

  • 將 -gencode arch=compute-63, code=sm_62 新增到CUDA_ARCH設定中。

  • 更新NVCC設定:NVCCFLAGS := -m64

  • 啟用OpenCV:USE_OPENCV = 1

現在,為了確保MXNet使用Pascal的硬體級低精度加速進行構建,我們需要如下編輯Mshadow Makefile:

MSHADOW_CFLAGS += -DMSHADOW_USE_PASCAL=1

最後,您可以使用以下命令構建完整的Apache MXNet庫:

cd $MXNET_HOME
make -j $(nproc)

執行上述命令後,將開始構建過程,該過程需要幾個小時才能完成。您將在mxnet/lib目錄中獲得名為libmxnet.so的檔案。

步驟2

安裝Apache MXNet Python繫結

在此步驟中,我們將安裝MXNet Python繫結。為此,我們需要在MXNet目錄中執行以下命令:

cd $MXNET_HOME/python
sudo pip install -e .

完成上述步驟後,您現在就可以在NVIDIA Jetson TX2或Nano裝置上執行MXNet了。可以使用以下命令進行驗證:

import mxnet
mxnet.__version__

如果一切正常,它將返回版本號。

Apache MXNet - 工具包和生態系統

為了支援在許多領域進行深度學習應用程式的研究和開發,Apache MXNet為我們提供了豐富的工具包、庫等等。讓我們來探索一下:

工具包

以下是MXNet提供的一些最常用和最重要的工具包:

GluonCV

顧名思義,GluonCV是一個由MXNet支援的用於計算機視覺的Gluon工具包。它提供了計算機視覺 (CV) 領域最先進的深度學習 (DL) 演算法的實現。藉助GluonCV工具包,工程師、研究人員和學生可以輕鬆驗證新想法和學習CV。

以下是GluonCV的一些特性

  • 它提供了用於重現最新研究報告中最新成果的訓練指令碼。

  • 170多個高質量的預訓練模型。

  • 採用靈活的開發模式。

  • GluonCV易於最佳化。我們可以部署它而無需保留重量級的深度學習框架。

  • 它提供了精心設計的API,大大降低了實現的複雜性。

  • 社群支援。

  • 易於理解的實現。

以下是GluonCV工具包支援的應用程式

  • 影像分類

  • 目標檢測

  • 語義分割

  • 例項分割

  • 姿態估計

  • 影片動作識別

我們可以使用pip如下安裝GluonCV:

pip install --upgrade mxnet gluoncv

GluonNLP

顧名思義,GluonNLP是由MXNet支援的用於自然語言處理 (NLP) 的Gluon工具包。它提供了NLP領域最先進的深度學習(DL) 模型的實現。

藉助GluonNLP工具包,工程師、研究人員和學生可以構建文字資料管道和模型的模組。基於這些模型,他們可以快速原型化研究想法和產品。

以下是GluonNLP的一些特性

  • 它提供了用於重現最新研究報告中最新成果的訓練指令碼。

  • 一組用於常見NLP任務的預訓練模型。

  • 它提供了精心設計的API,大大降低了實現的複雜性。

  • 社群支援。

  • 它還提供教程來幫助您開始新的NLP任務。

以下是我們可以使用GluonNLP工具包實現的NLP任務:

  • 詞嵌入

  • 語言模型

  • 機器翻譯

  • 文字分類

  • 情感分析

  • 自然語言推理

  • 文字生成

  • 依存句法分析

  • 命名實體識別

  • 意圖分類和槽位標註

我們可以使用pip如下安裝GluonNLP:

pip install --upgrade mxnet gluonnlp

GluonTS

顧名思義,GluonTS是由MXNet支援的用於機率時間序列建模的Gluon工具包。

它提供以下功能:

  • 隨時可以訓練的最先進 (SOTA) 深度學習模型。

  • 用於載入和迭代時間序列資料集的實用程式。

  • 構建塊以定義您自己的模型。

藉助GluonTS工具包,工程師、研究人員和學生可以在他們自己的資料上訓練和評估任何內建模型,快速試驗不同的解決方案,併為他們的時間序列任務提出解決方案。

他們還可以使用提供的抽象和構建塊來建立自定義時間序列模型,並將其快速與基線演算法進行基準測試。

我們可以使用pip如下安裝GluonTS:

pip install gluonts

GluonFR

顧名思義,這是一個用於人臉識別 (FR) 的Apache MXNet Gluon工具包。它提供以下功能:

  • 人臉識別領域最先進 (SOTA) 的深度學習模型。

  • SoftmaxCrossEntropyLoss、ArcLoss、TripletLoss、RingLoss、CosLoss/AMsoftmax、L2-Softmax、A-Softmax、CenterLoss、ContrastiveLoss和LGM Loss等的實現。

為了安裝Gluon Face,我們需要Python 3.5或更高版本。我們還需要首先安裝GluonCV和MXNet,如下所示:

pip install gluoncv --pre
pip install mxnet-mkl --pre --upgrade
pip install mxnet-cuXXmkl --pre –upgrade # if cuda XX is installed

安裝完依賴項後,可以使用以下命令安裝GluonFR:

從原始碼安裝

pip install git+https://github.com/THUFutureLab/gluon-face.git@master

Pip安裝

pip install gluonfr

生態系統

現在讓我們來探索MXNet豐富的庫、包和框架:

Coach RL

Coach是由英特爾人工智慧實驗室建立的Python強化學習 (RL) 框架。它使使用最先進的RL演算法進行輕鬆實驗成為可能。Coach RL支援Apache MXNet作為後端,並允許簡單地整合新環境來解決問題。

為了輕鬆擴充套件和重用現有元件,Coach RL很好地解耦了基本強化學習元件,例如演算法、環境、神經網路架構、探索策略。

以下是Coach RL框架的代理和支援的演算法:

價值最佳化代理

  • 深度Q網路 (DQN)

  • 雙深度Q網路 (DDQN)

  • 雙Q網路

  • 混合蒙特卡洛 (MMC)

  • 持久優勢學習 (PAL)

  • 分類深度Q網路 (C51)

  • 分位數迴歸深度Q網路 (QR-DQN)

  • N步Q學習

  • 神經情景控制 (NEC)

  • 歸一化優勢函式 (NAF)

  • 彩虹

策略最佳化代理

  • 策略梯度 (PG)

  • 非同步優勢行動者評論家 (A3C)

  • 深度確定性策略梯度 (DDPG)

  • 近端策略最佳化 (PPO)

  • 裁剪近端策略最佳化 (CPPO)

  • 廣義優勢估計 (GAE)

  • 具有經驗回放的樣本高效行動者評論家 (ACER)

  • 軟行動者評論家 (SAC)

  • 雙延遲深度確定性策略梯度 (TD3)

通用代理

  • 直接未來預測 (DFP)

模仿學習代理

  • 行為克隆 (BC)

  • 條件模仿學習

分層強化學習代理

  • 分層行動者評論家 (HAC)

深度相簿

深度相簿 (DGL) 由紐約大學和 AWS 上海團隊開發,是一個 Python 包,它在 MXNet 之上提供了圖神經網路 (GNN) 的簡易實現。它還在其他現有的主要深度學習庫(如 PyTorch、Gluon 等)之上提供了 GNN 的簡易實現。

深度相簿是免費軟體。它可在 Ubuntu 16.04 或更高版本的所有 Linux 發行版、macOS X 和 Windows 7 或更高版本上使用。它還需要 Python 3.5 版本或更高版本。

以下是 DGL 的功能:

無需遷移成本 - 使用 DGL 不會產生遷移成本,因為它構建在流行的現有深度學習框架之上。

訊息傳遞 - DGL 提供訊息傳遞,並且對其具有多功能的控制。訊息傳遞範圍從低階操作(例如沿選定邊傳送)到高階控制(例如圖範圍內的特徵更新)。

平滑的學習曲線 - DGL 易於學習和使用,因為強大的使用者定義函式靈活且易於使用。

透明的速度最佳化 - DGL 透過自動批次計算和稀疏矩陣乘法來提供透明的速度最佳化。

高效能 - 為了實現最大效率,DGL 會自動批次處理在一個或多個圖上一起進行的深度神經網路 (DNN) 訓練。

簡單友好的介面 - DGL 為我們提供了簡單友好的介面,用於訪問邊緣特徵以及操作圖結構。

InsightFace

InsightFace,一個用於人臉分析的深度學習工具包,它提供了由 MXNet 支援的計算機視覺中最先進 (SOTA) 人臉分析演算法的實現。它提供:

  • 高質量的大型預訓練模型集。

  • 最先進 (SOTA) 的訓練指令碼。

  • InsightFace易於最佳化。我們可以部署它而無需保留重量級的深度學習框架。

  • 它提供了精心設計的API,大大降低了實現的複雜性。

  • 構建塊以定義您自己的模型。

我們可以使用pip如下安裝InsightFace:

pip install --upgrade insightface

請注意,在安裝InsightFace之前,請根據您的系統配置安裝正確的MXNet包。

Keras-MXNet

眾所周知,Keras是用Python編寫的用於高階神經網路 (NN) 的API,Keras-MXNet為Keras提供了後端支援。它可以在高效能和可擴充套件的Apache MXNet深度學習框架之上執行。

Keras-MXNet的功能如下:

  • 允許使用者輕鬆、流暢、快速地進行原型設計。這一切都透過使用者友好性、模組化和可擴充套件性來實現。

  • 支援卷積神經網路 (CNN) 和迴圈神經網路 (RNN),以及兩者的組合。

  • 在中央處理器 (CPU) 和圖形處理單元 (GPU) 上都能完美執行。

  • 可以在一個或多個GPU上執行。

為了使用此後端,您首先需要安裝keras-mxnet,如下所示:

pip install keras-mxnet

現在,如果您使用的是GPU,則安裝支援CUDA 9的MXNet,如下所示:

pip install mxnet-cu90

但是,如果您只使用CPU,則安裝基本MXNet,如下所示:

pip install mxnet

MXBoard

MXBoard是用Python編寫的日誌記錄工具,用於記錄MXNet資料幀並在TensorBoard中顯示。換句話說,MXBoard旨在遵循tensorboard-pytorch API。它支援TensorBoard中的大多數資料型別。

其中一些如下所示:

  • 標量

  • 直方圖

  • 嵌入

  • 影像

  • 文字

  • 音訊

  • 精確率-召回率曲線

MXFusion

MXFusion是一個具有深度學習功能的模組化機率程式設計庫。MXFusion允許我們充分利用模組化(深度學習庫的關鍵特徵)進行機率程式設計。它易於使用,併為使用者提供了一個方便的介面來設計機率模型並將其應用於現實世界的問題。

MXFusion 已在 macOS 和 Linux 系統的 Python 3.4 及更高版本上驗證。要安裝 MXFusion,首先需要安裝以下依賴項:

  • MXNet >= 1.3

  • Networkx >= 2.1

可以使用以下 pip 命令安裝 MXFusion:

pip install mxfusion

TVM

Apache TVM 是一個開源的端到端深度學習編譯器棧,支援 CPU、GPU 和專用加速器等硬體後端,旨在彌合注重生產力的深度學習框架和注重效能的硬體後端之間的差距。藉助最新的 MXNet 1.6.0 版本,使用者可以使用 Apache(孵化中) TVM 以 Python 程式語言實現高效能運算元核心。

Apache TVM 最初是華盛頓大學 Paul G. Allen 計算機科學與工程學院 SAMPL 組的一個研究專案,現在它是在 Apache 軟體基金會 (ASF) 孵化中的一個專案,由一個包含多個行業和學術機構的開源社群 (OSC) 在 Apache 方式下驅動。

以下是 Apache(孵化中) TVM 的主要功能:

  • 簡化了以前的基於 C++ 的開發過程。

  • 支援在多個硬體後端(例如 CPU、GPU 等)上共享相同的實現。

  • TVM 提供了將各種框架(如 Keras、MXNet、PyTorch、TensorFlow、CoreML、DarkNet)中的深度學習模型編譯成可在不同硬體後端上部署的最小模組。

  • 它還提供基礎架構來自動生成和最佳化張量運算元,以獲得更好的效能。

XFer

Xfer 是一個用 Python 編寫的遷移學習框架。它主要接收一個 MXNet 模型,並訓練一個元模型或修改該模型以適應新的目標資料集。

簡單來說,Xfer 是一個 Python 庫,允許使用者快速輕鬆地遷移儲存在深度神經網路 (DNN) 中的知識。

Xfer 可用於:

  • 對任意數值格式的資料進行分類。

  • 處理常見的影像或文字資料。

  • 作為一個從特徵提取到訓練重新利用器(一個在目標任務中執行分類的物件)的管道。

以下是 Xfer 的功能:

  • 資源效率

  • 資料效率

  • 輕鬆訪問神經網路

  • 不確定性建模

  • 快速原型設計

  • 用於從神經網路提取特徵的實用程式

Apache MXNet - 系統架構

本章將幫助您瞭解 MXNet 系統架構。讓我們從學習 MXNet 模組開始。

MXNet 模組

下圖是 MXNet 系統架構,它顯示了 **MXNet 模組及其互動**的主要模組和元件。

MXNet Modules

在上圖中:

  • 藍色框中的模組是 **面向使用者的模組**。

  • 綠色框中的模組是 **系統模組**。

  • 實線箭頭表示高度依賴,即嚴重依賴介面。

  • 虛線箭頭表示輕度依賴,即為了方便和介面一致性而使用的資料結構。實際上,它可以用替代方案替換。

讓我們更詳細地討論面向使用者和系統模組。

面向使用者的模組

面向使用者的模組如下:

  • **NDArray** - 它為 Apache MXNet 提供靈活的命令式程式。它們是動態的非同步 n 維陣列。

  • **KVStore** - 它充當高效引數同步的介面。在 KVStore 中,KV 代表鍵值。所以,它是一個鍵值儲存介面。

  • **資料載入 (IO)** - 此面向使用者的模組用於高效的分散式資料載入和增強。

  • **符號執行** - 它是一個靜態符號圖執行器。它提供高效的符號圖執行和最佳化。

  • **符號構建** - 此面向使用者的模組為使用者提供了一種構建計算圖(即網路配置)的方法。

系統模組

系統模組如下:

  • **儲存分配器** - 此係統模組顧名思義,在主機(即 CPU)和不同裝置(即 GPU)上高效地分配和回收記憶體塊。

  • **執行時依賴引擎** - 執行時依賴引擎模組根據其讀/寫依賴關係調度和執行操作。

  • **資源管理器** - 資源管理器 (RM) 系統模組管理全域性資源,如隨機數生成器和時間空間。

  • **運算元** - 算子系統模組包含所有定義靜態前向和梯度計算(即反向傳播)的運算元。

Apache MXNet - 系統元件

這裡詳細解釋了 Apache MXNet 中的系統元件。首先,我們將研究 MXNet 中的執行引擎。

執行引擎

Apache MXNet 的執行引擎非常通用。我們可以將其用於深度學習以及任何特定領域的難題:按照其依賴關係執行一系列函式。它的設計方式是,具有依賴關係的函式被序列化,而沒有依賴關係的函式可以並行執行。

核心介面

以下 API 是 Apache MXNet 執行引擎的核心介面:

virtual void PushSync(Fn exec_fun, Context exec_ctx,
std::vector<VarHandle> const& const_vars,
std::vector<VarHandle> const& mutate_vars) = 0;

上述 API 包含以下內容:

  • **exec_fun** - MXNet 的核心介面 API 允許我們將名為 exec_fun 的函式及其上下文資訊和依賴關係推送到執行引擎。

  • **exec_ctx** - 上述函式 exec_fun 應在其執行的上下文資訊。

  • **const_vars** - 這些是函式從中讀取的變數。

  • **mutate_vars** - 這些是要修改的變數。

執行引擎向用戶保證,任何兩個修改公共變數的函式的執行都按其推送順序序列化。

函式

以下是 Apache MXNet 執行引擎的函式型別:

using Fn = std::function<void(RunContext)>;

在上例函式中,**RunContext** 包含執行時資訊。執行時資訊應由執行引擎確定。**RunContext** 的語法如下:

struct RunContext {
   // stream pointer which could be safely cast to
   // cudaStream_t* type
   void *stream;
};

以下是關於執行引擎函式的一些要點:

  • 所有函式均由 MXNet 執行引擎的內部執行緒執行。

  • 將阻塞函式推送到執行引擎不是一個好主意,因為這樣函式將佔用執行執行緒,並降低總吞吐量。

為此,MXNet 提供了另一個非同步函式,如下所示:

using Callback = std::function<void()>;
using AsyncFn = std::function<void(RunContext, Callback)>;
  • 在這個 **AsyncFn** 函式中,我們可以傳遞執行緒的繁重部分,但是直到我們呼叫 **callback** 函式,執行引擎才認為該函式已完成。

上下文

在 **Context** 中,我們可以指定要在其中執行函式的上下文。這通常包括以下內容:

  • 函式是否應在 CPU 或 GPU 上執行。

  • 如果我們在 Context 中指定 GPU,則使用哪個 GPU。

  • Context 和 RunContext 之間存在巨大差異。Context 具有裝置型別和裝置 ID,而 RunContext 具有僅在執行時才能確定的資訊。

VarHandle

VarHandle 用於指定函式的依賴關係,就像一個令牌(特別是執行引擎提供的令牌),我們可以用它來表示函式可以修改或使用的外部資源。

但是問題出現了,為什麼我們需要使用 VarHandle?這是因為 Apache MXNet 引擎的設計與其他 MXNet 模組解耦。

以下是關於 VarHandle 的一些要點:

  • 它很輕量級,因此建立、刪除或複製變數幾乎不會產生操作成本。

  • 我們需要指定不可變變數,即將在 **const_vars** 中使用的變數。

  • 我們需要指定可變變數,即將在 **mutate_vars** 中修改的變數。

  • 執行引擎用於解決函式之間依賴關係的規則是,當其中一個函式修改至少一個公共變數時,任何兩個函式的執行都按其推送順序序列化。

  • 要建立一個新變數,我們可以使用 **NewVar()** API。

  • 要刪除一個變數,我們可以使用 **PushDelete** API。

讓我們用一個簡單的例子來理解它的工作原理:

假設我們有兩個函式 F1 和 F2,它們都修改變數 V2。在這種情況下,如果 F2 在 F1 之後推送,則保證 F2 在 F1 之後執行。另一方面,如果 F1 和 F2 都使用 V2,那麼它們的實際執行順序可能是隨機的。

Push 和 Wait

**Push** 和 **wait** 是執行引擎的另外兩個有用的 API。

以下是 **Push** API 的兩個重要特性:

  • 所有 Push API 都是非同步的,這意味著 API 呼叫立即返回,無論推送的函式是否已完成。

  • Push API 不是執行緒安全的,這意味著一次只有一個執行緒應該進行引擎 API 呼叫。

現在,如果我們談論 Wait API,以下幾點表示它:

  • 如果使用者想要等待特定函式完成,則應在閉包中包含一個回撥函式。包含後,在函式結束時呼叫該函式。

  • 另一方面,如果使用者想要等待涉及某個特定變數的所有函式完成,則應使用 **WaitForVar(var)** API。

  • 如果有人想等待所有推送的函式完成,則使用 **WaitForAll ()** API。

  • 用於指定函式的依賴關係,就像一個令牌。

運算子

Apache MXNet 中的運算元是一個包含實際計算邏輯以及輔助資訊並幫助系統執行最佳化的類。

運算元介面

**Forward** 是核心運算元介面,其語法如下:

virtual void Forward(const OpContext &ctx,
const std::vector<TBlob> &in_data,
const std::vector<OpReqType> &req,
const std::vector<TBlob> &out_data,
const std::vector<TBlob> &aux_states) = 0;

在 **Forward()** 中定義的 **OpContext** 的結構如下:

struct OpContext {
   int is_train;
   RunContext run_ctx;
   std::vector<Resource> requested;
}

**OpContext** 描述運算元的狀態(是否處於訓練或測試階段),運算元應在其執行的裝置以及請求的資源。執行引擎的另外兩個有用的 API。

從上面的 **Forward** 核心介面,我們可以理解請求的資源如下:

  • **in_data** 和 **out_data** 分別表示輸入和輸出張量。

  • **req** 表示計算結果如何寫入 **out_data**。

**OpReqType** 可以定義為:

enum OpReqType {
   kNullOp,
   kWriteTo,
   kWriteInplace,
   kAddTo
};

與 **Forward** 運算元類似,我們可以選擇實現 **Backward** 介面,如下所示:

virtual void Backward(const OpContext &ctx,
const std::vector<TBlob> &out_grad,
const std::vector<TBlob> &in_data,
const std::vector<TBlob> &out_data,
const std::vector<OpReqType> &req,
const std::vector<TBlob> &in_grad,
const std::vector<TBlob> &aux_states);

各種任務

**運算元** 介面允許使用者執行以下任務:

  • 使用者可以指定就地更新,並降低記憶體分配成本。

  • 為了使其更清晰,使用者可以隱藏 Python 中的一些內部引數。

  • 使用者可以定義張量和輸出張量之間的關係。

  • 為了執行計算,使用者可以從系統獲取額外的臨時空間。

運算元屬性 (Operator Property)

眾所周知,在卷積神經網路 (CNN) 中,一次卷積有多種實現方式。為了獲得最佳效能,我們可能需要在這幾種卷積之間切換。

因此,Apache MXNet 將運算元的語義介面與實現介面分開。這種分離是透過**OperatorProperty**類實現的,它包含以下內容:

**InferShape** - InferShape 介面有兩個用途,如下所示:

  • 第一個用途是告訴系統每個輸入和輸出張量的尺寸,以便在**Forward**和**Backward**呼叫之前分配空間。

  • 第二個用途是在執行之前執行大小檢查,以確保沒有錯誤。

語法如下:

virtual bool InferShape(mxnet::ShapeVector *in_shape,
mxnet::ShapeVector *out_shape,
mxnet::ShapeVector *aux_shape) const = 0;

**Request Resource** - 如果您的系統可以管理諸如 cudnnConvolutionForward 之類的操作的計算工作區呢?您的系統可以執行諸如重用空間等最佳化。在這裡,MXNet 可以藉助以下兩個介面輕鬆實現這一點:

virtual std::vector<ResourceRequest> ForwardResource(
   const mxnet::ShapeVector &in_shape) const;
virtual std::vector<ResourceRequest> BackwardResource(
   const mxnet::ShapeVector &in_shape) const;

但是,如果**ForwardResource**和**BackwardResource**返回非空陣列會怎樣?在這種情況下,系統透過**Operator**的**Forward**和**Backward**介面中的**ctx**引數提供相應的資源。

**反向依賴 (Backward dependency)** - Apache MXNet 有以下兩種不同的運算元簽名來處理反向依賴:

void FullyConnectedForward(TBlob weight, TBlob in_data, TBlob out_data);
void FullyConnectedBackward(TBlob weight, TBlob in_data, TBlob out_grad, TBlob in_grad);
void PoolingForward(TBlob in_data, TBlob out_data);
void PoolingBackward(TBlob in_data, TBlob out_data, TBlob out_grad, TBlob in_grad);

這裡需要注意兩點:

  • FullyConnectedForward 中的 out_data 未被 FullyConnectedBackward 使用,並且

  • PoolingBackward 需要 PoolingForward 的所有引數。

這就是為什麼對於**FullyConnectedForward**,一旦使用過的**out_data**張量可以安全釋放,因為反向函式不需要它。藉助這個系統,可以儘早將一些張量作為垃圾回收。

**就地選項 (In place Option)** - Apache MXNet 為使用者提供了另一個介面來節省記憶體分配的成本。該介面適用於輸入和輸出張量具有相同形狀的逐元素操作。

以下是指定就地更新的語法:

建立運算元的示例

藉助 OperatorProperty,我們可以建立一個運算元。為此,請按照以下步驟操作:

virtual std::vector<std::pair<int, void*>> ElewiseOpProperty::ForwardInplaceOption(
   const std::vector<int> &in_data,
   const std::vector<void*> &out_data) 
const {
   return { {in_data[0], out_data[0]} };
}
virtual std::vector<std::pair<int, void*>> ElewiseOpProperty::BackwardInplaceOption(
   const std::vector<int> &out_grad,
   const std::vector<int> &in_data,
   const std::vector<int> &out_data,
   const std::vector<void*> &in_grad) 
const {
   return { {out_grad[0], in_grad[0]} }
}

步驟1

建立運算元 (Create Operator)

首先在 OperatorProperty 中實現以下介面:

virtual Operator* CreateOperator(Context ctx) const = 0;

示例如下:

class ConvolutionOp {
   public:
      void Forward( ... ) { ... }
      void Backward( ... ) { ... }
};
class ConvolutionOpProperty : public OperatorProperty {
   public:
      Operator* CreateOperator(Context ctx) const {
         return new ConvolutionOp;
      }
};

步驟2

引數化運算元 (Parameterize Operator)

如果您要實現卷積運算元,則必須知道核心大小、步幅大小、填充大小等。為什麼?因為這些引數應該在呼叫任何**Forward**或**backward**介面之前傳遞給運算元。

為此,我們需要定義一個**ConvolutionParam**結構,如下所示:

#include <dmlc/parameter.h>
struct ConvolutionParam : public dmlc::Parameter<ConvolutionParam> {
   mxnet::TShape kernel, stride, pad;
   uint32_t num_filter, num_group, workspace;
   bool no_bias;
};

現在,我們需要將其放入**ConvolutionOpProperty**中,並將其傳遞給運算元,如下所示:

class ConvolutionOp {
   public:
      ConvolutionOp(ConvolutionParam p): param_(p) {}
      void Forward( ... ) { ... }
      void Backward( ... ) { ... }
   private:
      ConvolutionParam param_;
};
class ConvolutionOpProperty : public OperatorProperty {
   public:
      void Init(const vector<pair<string, string>& kwargs) {
         // initialize param_ using kwargs
      }
      Operator* CreateOperator(Context ctx) const {
         return new ConvolutionOp(param_);
      }
   private:
      ConvolutionParam param_;
};

步驟 3

將運算元屬性類和引數類註冊到 Apache MXNet

最後,我們需要將運算元屬性類和引數類註冊到 MXNet。這可以使用以下宏來完成:

DMLC_REGISTER_PARAMETER(ConvolutionParam);
MXNET_REGISTER_OP_PROPERTY(Convolution, ConvolutionOpProperty);

在上面的宏中,第一個引數是名稱字串,第二個是屬性類名稱。

Apache MXNet - 統一運算元 API

本章提供有關 Apache MXNet 中統一運算元應用程式程式設計介面 (API) 的資訊。

SimpleOp

SimpleOp 是一個新的統一運算元 API,它統一了不同的呼叫過程。一旦被呼叫,它就會返回到運算元的基本元素。統一運算元專為一元和二元運算而設計。這是因為大多數數學運算子處理一個或兩個運算元,而更多的運算元使與依賴性相關的最佳化變得有用。

我們將透過一個示例來了解其 SimpleOp 統一運算元的工作原理。在這個示例中,我們將建立一個充當**smooth l1 loss**的運算元,它是 l1 和 l2 loss 的混合。我們可以定義並編寫損失,如下所示:

loss = outside_weight .* f(inside_weight .* (data - label))
grad = outside_weight .* inside_weight .* f'(inside_weight .* (data - label))

這裡,在上面的例子中:

  • .* 代表逐元素乘法

  • **f, f’** 是我們假設在**mshadow**中的 smooth l1 loss 函式。

將此特定損失實現為一元或二元運算子似乎是不可能的,但 MXNet 為其使用者提供了符號執行中的自動微分,這將損失直接簡化為 f 和 f’。這就是為什麼我們當然可以將這個特定的損失實現為一元運算子。

定義形狀 (Defining Shapes)

眾所周知,MXNet 的**mshadow 庫**需要顯式記憶體分配,因此我們需要在任何計算發生之前提供所有資料形狀。在定義函式和梯度之前,我們需要提供輸入形狀一致性和輸出形狀,如下所示:

typedef mxnet::TShape (*UnaryShapeFunction)(const mxnet::TShape& src,
const EnvArguments& env);
   typedef mxnet::TShape (*BinaryShapeFunction)(const mxnet::TShape& lhs,
const mxnet::TShape& rhs,
const EnvArguments& env);

mxnet::Tshape 函式用於檢查輸入資料形狀和指定的輸出資料形狀。如果您沒有定義此函式,則預設輸出形狀將與輸入形狀相同。例如,對於二元運算子,lhs 和 rhs 的形狀預設情況下被檢查為相同。

現在讓我們繼續我們的**smooth l1 loss 示例**。為此,我們需要在標頭檔案實現**smooth_l1_unary-inl.h**中定義一個 XPU 到 cpu 或 gpu。原因是在**smooth_l1_unary.cc**和**smooth_l1_unary.cu**中重用相同的程式碼。

#include <mxnet/operator_util.h>
   #if defined(__CUDACC__)
      #define XPU gpu
   #else
      #define XPU cpu
#endif

在我們的**smooth l1 loss 示例**中,輸出與源具有相同的形狀,我們可以使用預設行為。它可以寫成如下:

inline mxnet::TShape SmoothL1Shape_(const mxnet::TShape& src,const EnvArguments& env) {
   return mxnet::TShape(src);
}

定義函式 (Defining Functions)

我們可以建立一個只有一個輸入的一元或二元函式,如下所示:

typedef void (*UnaryFunction)(const TBlob& src,
   const EnvArguments& env,
   TBlob* ret,
   OpReqType req,
   RunContext ctx);
typedef void (*BinaryFunction)(const TBlob& lhs,
   const TBlob& rhs,
   const EnvArguments& env,
   TBlob* ret,
   OpReqType req,
   RunContext ctx);

以下是**RunContext ctx 結構**,其中包含執行時執行所需的資訊:

struct RunContext {
   void *stream; // the stream of the device, can be NULL or Stream<gpu>* in GPU mode
   template<typename xpu> inline mshadow::Stream<xpu>* get_stream() // get mshadow stream from Context
} // namespace mxnet

現在,讓我們看看如何將計算結果寫入**ret**。

enum OpReqType {
   kNullOp, // no operation, do not write anything
   kWriteTo, // write gradient to provided space
   kWriteInplace, // perform an in-place write
   kAddTo // add to the provided space
};

現在,讓我們繼續我們的**smooth l1 loss 示例**。為此,我們將使用 UnaryFunction 來定義此運算元的函式,如下所示:

template<typename xpu>
void SmoothL1Forward_(const TBlob& src,
   const EnvArguments& env,
   TBlob *ret,
   OpReqType req,
RunContext ctx) {
   using namespace mshadow;
   using namespace mshadow::expr;
   mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
   real_t sigma2 = env.scalar * env.scalar;
   MSHADOW_TYPE_SWITCH(ret->type_flag_, DType, {
      mshadow::Tensor<xpu, 2, DType> out = ret->get<xpu, 2, DType>(s);
      mshadow::Tensor<xpu, 2, DType> in = src.get<xpu, 2, DType>(s);
      ASSIGN_DISPATCH(out, req,
      F<mshadow_op::smooth_l1_loss>(in, ScalarExp<DType>(sigma2)));
   });
}

定義梯度 (Defining Gradients)

除了**Input、TBlob**和**OpReqType**加倍之外,二元運算子的梯度函式具有相似的結構。讓我們檢視下面,我們使用各種型別的輸入建立了一個梯度函式:

// depending only on out_grad
typedef void (*UnaryGradFunctionT0)(const OutputGrad& out_grad,
   const EnvArguments& env,
   TBlob* in_grad,
   OpReqType req,
   RunContext ctx);
// depending only on out_value
typedef void (*UnaryGradFunctionT1)(const OutputGrad& out_grad,
   const OutputValue& out_value,
   const EnvArguments& env,
   TBlob* in_grad,
   OpReqType req,
   RunContext ctx);
// depending only on in_data
typedef void (*UnaryGradFunctionT2)(const OutputGrad& out_grad,
   const Input0& in_data0,
   const EnvArguments& env,
   TBlob* in_grad,
   OpReqType req,
   RunContext ctx);

如上所定義,**Input0、Input、OutputValue**和**OutputGrad**都共享**GradientFunctionArgument**的結構。它定義如下:

struct GradFunctionArgument {
   TBlob data;
}

現在讓我們繼續我們的**smooth l1 loss 示例**。為了啟用梯度的鏈式法則,我們需要將來自頂部的**out_grad**乘以**in_grad**的結果。

template<typename xpu>
void SmoothL1BackwardUseIn_(const OutputGrad& out_grad, const Input0& in_data0,
   const EnvArguments& env,
   TBlob *in_grad,
   OpReqType req,
   RunContext ctx) {
   using namespace mshadow;
   using namespace mshadow::expr;
   mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
   real_t sigma2 = env.scalar * env.scalar;
      MSHADOW_TYPE_SWITCH(in_grad->type_flag_, DType, {
      mshadow::Tensor<xpu, 2, DType> src = in_data0.data.get<xpu, 2, DType>(s);
      mshadow::Tensor<xpu, 2, DType> ograd = out_grad.data.get<xpu, 2, DType>(s);
      mshadow::Tensor<xpu, 2, DType> igrad = in_grad->get<xpu, 2, DType>(s);
      ASSIGN_DISPATCH(igrad, req,
      ograd * F<mshadow_op::smooth_l1_gradient>(src, ScalarExp<DType>(sigma2)));
   });
}

將 SimpleOp 註冊到 MXNet

建立形狀、函式和梯度後,我們需要將它們都還原到 NDArray 運算元以及符號運算元中。為此,我們可以使用以下注冊宏:

MXNET_REGISTER_SIMPLE_OP(Name, DEV)
   .set_shape_function(Shape)
   .set_function(DEV::kDevMask, Function<XPU>, SimpleOpInplaceOption)
   .set_gradient(DEV::kDevMask, Gradient<XPU>, SimpleOpInplaceOption)
   .describe("description");

**SimpleOpInplaceOption**可以定義如下:

enum SimpleOpInplaceOption {
   kNoInplace, // do not allow inplace in arguments
   kInplaceInOut, // allow inplace in with out (unary)
   kInplaceOutIn, // allow inplace out_grad with in_grad (unary)
   kInplaceLhsOut, // allow inplace left operand with out (binary)

   kInplaceOutLhs // allow inplace out_grad with lhs_grad (binary)
};

現在讓我們繼續我們的**smooth l1 loss 示例**。為此,我們有一個依賴於輸入資料的梯度函式,因此該函式不能就地編寫。

MXNET_REGISTER_SIMPLE_OP(smooth_l1, XPU)
.set_function(XPU::kDevMask, SmoothL1Forward_<XPU>, kNoInplace)
.set_gradient(XPU::kDevMask, SmoothL1BackwardUseIn_<XPU>, kInplaceOutIn)
.set_enable_scalar(true)
.describe("Calculate Smooth L1 Loss(lhs, scalar)");

SimpleOp 上的 EnvArguments

眾所周知,某些操作可能需要:

  • 標量作為輸入,例如梯度比例

  • 一組控制行為的關鍵字引數

  • 一個臨時空間來加快計算速度。

使用 EnvArguments 的好處是它提供了額外的引數和資源,使計算更具可擴充套件性和效率。

示例

首先讓我們定義如下結構:

struct EnvArguments {
   real_t scalar; // scalar argument, if enabled
   std::vector<std::pair<std::string, std::string> > kwargs; // keyword arguments
   std::vector<Resource> resource; // pointer to the resources requested
};

接下來,我們需要從**EnvArguments.resource**請求額外的資源,例如**mshadow::Random**和臨時記憶體空間。這可以透過以下方式完成:

struct ResourceRequest {
   enum Type { // Resource type, indicating what the pointer type is
      kRandom, // mshadow::Random<xpu> object
      kTempSpace // A dynamic temp space that can be arbitrary size
   };
   Type type; // type of resources
};

現在,註冊將從**mxnet::ResourceManager**請求宣告的資源請求。之後,它會將資源放在**EnvAgruments**中的**std::vector resource**中。

我們可以使用以下程式碼訪問資源:

auto tmp_space_res = env.resources[0].get_space(some_shape, some_stream);
auto rand_res = env.resources[0].get_random(some_stream);

在我們的 smooth l1 loss 示例中,需要一個標量輸入來標記損失函式的轉折點。這就是為什麼在註冊過程中,我們在函式和梯度宣告中使用**set_enable_scalar(true)**和**env.scalar**。

構建張量操作 (Building Tensor Operation)

這裡出現的問題是為什麼我們需要構建張量操作?原因如下:

  • 計算利用 mshadow 庫,有時我們沒有現成的函式。

  • 如果操作不是以逐元素的方式完成,例如 softmax loss 和梯度。

示例

這裡,我們使用上面的 smooth l1 loss 示例。我們將建立兩個對映器,即 smooth l1 loss 和梯度的標量情況:

namespace mshadow_op {
   struct smooth_l1_loss {
      // a is x, b is sigma2
      MSHADOW_XINLINE static real_t Map(real_t a, real_t b) {
         if (a > 1.0f / b) {
            return a - 0.5f / b;
         } else if (a < -1.0f / b) {
            return -a - 0.5f / b;
         } else {
            return 0.5f * a * a * b;
         }
      }
   };
}

Apache MXNet - 分散式訓練

本章介紹 Apache MXNet 中的分散式訓練。讓我們首先了解 MXNet 中的計算模式。

計算模式 (Modes of Computation)

MXNet 是一款多語言 ML 庫,為使用者提供了以下兩種計算模式:

命令式模式 (Imperative mode)

這種計算模式公開了一個類似於 NumPy API 的介面。例如,在 MXNet 中,使用以下命令式程式碼在 CPU 和 GPU 上構建一個全零張量:

import mxnet as mx
tensor_cpu = mx.nd.zeros((100,), ctx=mx.cpu())
tensor_gpu= mx.nd.zeros((100,), ctx=mx.gpu(0))

正如我們在上面的程式碼中看到的,MXNet 指定了儲存張量的位置,在 CPU 或 GPU 裝置上。在上面的示例中,它位於位置 0。MXNet 達到了令人難以置信的裝置利用率,因為所有計算都是延遲發生的,而不是立即發生的。

符號式模式 (Symbolic mode)

雖然命令式模式非常有用,但這種模式的缺點之一是其僵化性,即所有計算都需要預先知道,以及預定義的資料結構。

另一方面,符號式模式公開了一個類似於 TensorFlow 的計算圖。它透過允許 MXNet 使用符號或變數而不是固定/預定義的資料結構來消除命令式 API 的缺點。之後,符號可以解釋為一組操作,如下所示:

import mxnet as mx
x = mx.sym.Variable(“X”)
y = mx.sym.Variable(“Y”)
z = (x+y)
m = z/100

並行型別 (Kinds of Parallelism)

Apache MXNet 支援分散式訓練。它使我們能夠利用多臺機器進行更快、更有效的訓練。

以下是我們可以將訓練神經網路的工作負載分佈到多個裝置(CPU 或 GPU 裝置)上的兩種方法:

資料並行 (Data Parallelism)

在這種並行性中,每個裝置都儲存模型的完整副本,並使用資料集的不同部分。裝置還集體更新共享模型。我們可以將所有裝置放在一臺機器上或多臺機器上。

模型並行 (Model Parallelism)

這是另一種並行性,當模型太大而無法放入裝置記憶體時非常有用。在模型並行中,不同的裝置被分配學習模型的不同部分的任務。這裡需要注意的重要一點是,目前 Apache MXNet 只支援單機模型並行。

分散式訓練的工作原理 (Working of distributed training)

以下概念是理解 Apache MXNet 中分散式訓練工作原理的關鍵:

程序型別 (Types of processes)

程序相互通訊以完成模型的訓練。Apache MXNet 有以下三個程序:

工作節點 (Worker)

工作節點的任務是在一批訓練樣本上執行訓練。工作節點將在處理每一批之前從伺服器拉取權重。處理完批次後,工作節點會將梯度傳送到伺服器。

伺服器 (Server)

MXNet 可以有多個伺服器來儲存模型的引數並與工作節點通訊。

排程器 (Scheduler)

排程器的作用是設定叢集,包括等待每個節點啟動的訊息以及節點正在偵聽的埠。設定集群后,排程器讓所有程序都知道叢集中的其他每個節點。這是因為程序可以相互通訊。只有一個排程器。

KV 儲存 (KV Store)

KV 儲存代表**鍵值**儲存 (Key-Value store)。它是多裝置訓練的關鍵元件。它的重要性在於,在單機或多機上,引數在裝置間的通訊是透過一個或多個帶有 KVStore 的伺服器來傳輸引數的。讓我們透過以下幾點來了解 KVStore 的工作原理:

  • KVStore 中的每個值都由一個**鍵**和一個**值**表示。

  • 網路中的每個引數陣列都分配一個**鍵**,該引數陣列的權重由**值**引用。

  • 之後,工作節點在處理完一個批次後**推送**梯度。它們還在處理新批次之前**拉取**更新後的權重。

KVStore 伺服器的概念僅在分散式訓練期間存在,並且可以透過使用包含單詞**dist** 的字串引數呼叫**mxnet.kvstore.create** 函式來啟用其分散式模式:

kv = mxnet.kvstore.create(‘dist_sync’)

鍵的分佈

並非所有伺服器都儲存所有引數陣列或鍵,而是將它們分佈在不同的伺服器上。KVStore 透明地處理這種跨不同伺服器的鍵的分佈,而哪個伺服器儲存特定鍵的決定是隨機做出的。

如上所述,KVStore 確保每當拉取鍵時,其請求都會發送到擁有相應值的伺服器。如果某個鍵的值很大怎麼辦?在這種情況下,它可能會跨不同的伺服器共享。

分割訓練資料

作為使用者,我們希望每臺機器都能處理資料集的不同部分,尤其是在以資料並行模式執行分散式訓練時。我們知道,為了分割資料迭代器為單一工作器上的資料並行訓練提供的樣本批次,我們可以使用**mxnet.gluon.utils.split_and_load**,然後將批次的每一部分載入到將進一步處理它的裝置上。

另一方面,在分散式訓練的情況下,首先需要將資料集分成**n**個不同的部分,以便每個工作器獲得不同的部分。獲得後,每個工作器可以使用**split_and_load**再次將資料集的那一部分劃分為單臺機器上的不同裝置。所有這些都是透過資料迭代器完成的。**mxnet.io.MNISTIterator** 和**mxnet.io.ImageRecordIter** 是 MXNet 中支援此功能的兩個這樣的迭代器。

權重更新

為了更新權重,KVStore 支援以下兩種模式:

  • 第一種方法聚合梯度並使用這些梯度更新權重。

  • 在第二種方法中,伺服器僅聚合梯度。

如果您使用的是 Gluon,可以透過傳遞**update_on_kvstore** 變數來選擇上述方法。讓我們透過建立**trainer** 物件來了解它:

trainer = gluon.Trainer(net.collect_params(), optimizer='sgd',
   optimizer_params={'learning_rate': opt.lr,
      'wd': opt.wd,
      'momentum': opt.momentum,
      'multi_precision': True},
      kvstore=kv,
   update_on_kvstore=True)

分散式訓練模式

如果 KVStore 建立字串包含單詞 dist,則表示已啟用分散式訓練。以下是可以透過使用不同型別的 KVStore 來啟用的不同分散式訓練模式:

dist_sync

顧名思義,它表示同步分散式訓練。在此模式下,所有工作器在每個批次的開始都使用相同的同步模型引數集。

此模式的缺點是,在每個批次之後,伺服器必須等到從每個工作器接收梯度才能更新模型引數。這意味著如果一個工作器崩潰,它將阻止所有工作器的進度。

dist_async

顧名思義,它表示非同步分散式訓練。在此模式下,伺服器接收來自一個工作器的梯度並立即更新其儲存。伺服器使用更新後的儲存來響應任何進一步的拉取請求。

與**dist_sync 模式**相比,其優點在於,完成處理一個批次的工作器可以從伺服器拉取當前引數並開始下一個批次。即使其他工作器尚未完成處理之前的批次,工作器也可以這樣做。它也比 dist_sync 模式快,因為它可以在沒有任何同步成本的情況下進行更多輪次的收斂。

dist_sync_device

此模式與**dist_sync** 模式相同。唯一的區別是,當每個節點上使用多個 GPU 時,**dist_sync_device** 在 GPU 上聚合梯度並更新權重,而**dist_sync** 在 CPU 記憶體上聚合梯度並更新權重。

它減少了 GPU 和 CPU 之間的昂貴通訊。因此,它比**dist_sync** 快。缺點是它增加了 GPU 的記憶體使用量。

dist_async_device

此模式的工作方式與**dist_sync_device** 模式相同,但處於非同步模式。

Apache MXNet - Python 包

在本章中,我們將學習 Apache MXNet 中可用的 Python 包。

重要的 MXNet Python 包

MXNet 具有以下重要的 Python 包,我們將逐一討論:

  • Autograd(自動微分)

  • NDArray

  • KVStore

  • Gluon

  • 視覺化

首先讓我們從 Apache MXNet 的**Autograd** Python 包開始。

Autograd

**Autograd** 代表**自動微分**,用於將梯度從損失度量反向傳播回每個引數。它結合反向傳播使用動態規劃方法來有效地計算梯度。它也稱為反向模式自動微分。這種技術在“扇入”情況下非常有效,在“扇入”情況下,許多引數會影響單個損失度量。

什麼是梯度?

梯度是神經網路訓練過程的基礎。它們基本上告訴我們如何改變網路的引數以提高其效能。

眾所周知,神經網路 (NN) 由運算子組成,例如求和、乘積、卷積等。這些運算子在其計算中使用引數,例如卷積核中的權重。我們必須找到這些引數的最優值,而梯度則向我們展示了方法並引導我們找到解決方案。

Graph_of_Gradients

我們感興趣的是改變引數對網路效能的影響,梯度告訴我們,當我們改變它所依賴的變數時,給定變數增加或減少的程度。效能通常使用我們試圖最小化的損失度量來定義。例如,對於迴歸,我們可能會嘗試最小化預測值和精確值之間的**L2** 損失,而對於分類,我們可能會最小化**交叉熵損失**。

一旦我們計算出每個引數相對於損失的梯度,我們就可以使用最佳化器,例如隨機梯度下降。

如何計算梯度?

我們有以下選項來計算梯度:

  • **符號微分** - 第一個選項是符號微分,它計算每個梯度的公式。這種方法的缺點是,隨著網路的深入和運算子的複雜化,它會很快導致非常長的公式。

  • **有限差分** - 另一個選項是使用有限差分,它嘗試對每個引數進行細微的差異,並檢視損失度量如何響應。這種方法的缺點是,它在計算上將非常昂貴,並且可能具有較差的數值精度。

  • **自動微分** - 以上方法缺點的解決方案是使用自動微分將梯度從損失度量反向傳播回每個引數。傳播允許我們使用動態規劃方法有效地計算梯度。此方法也稱為反向模式自動微分。

自動微分 (autograd)

在這裡,我們將詳細瞭解 autograd 的工作原理。它基本上分為以下兩個階段:

**階段 1** - 此階段稱為訓練的**“前向傳遞”**。顧名思義,在此階段,它會建立網路用來進行預測和計算損失度量的運算子記錄。

**階段 2** - 此階段稱為訓練的**“反向傳遞”**。顧名思義,在此階段,它會透過此記錄反向工作。向後移動,它會評估每個運算子的部分導數,一直回到網路引數。

Automatic Differentiation

autograd 的優點

以下是使用自動微分 (autograd) 的優點:

  • **靈活** - 它在定義網路時提供的靈活性是使用 autograd 的巨大好處之一。我們可以在每次迭代時更改操作。這些被稱為動態圖,在需要靜態圖的框架中實現起來要複雜得多。即使在這種情況下,autograd 仍然能夠正確地反向傳播梯度。

  • **自動** - Autograd 是自動的,即反向傳播過程的複雜性由它為您處理。我們只需要指定我們感興趣計算哪些梯度。

  • **高效** - Autograd 高效地計算梯度。

  • **可以使用原生 Python 控制流運算子** - 我們可以使用原生的 Python 控制流運算子,例如 if 條件和 while 迴圈。autograd 仍然能夠有效且正確地反向傳播梯度。

在 MXNet Gluon 中使用 autograd

在這裡,我們將透過一個示例來了解如何在 MXNet Gluon 中使用**autograd**。

實現示例

在下面的示例中,我們將實現一個具有兩層的神經網路模型。實現後,我們將使用 autograd 自動計算損失相對於每個權重引數的梯度:

首先匯入 autogrard 和其他所需的包,如下所示:

from mxnet import autograd
import mxnet as mx
from mxnet.gluon.nn import HybridSequential, Dense
from mxnet.gluon.loss import L2Loss

現在,我們需要定義網路,如下所示:

N_net = HybridSequential()
N_net.add(Dense(units=3))
N_net.add(Dense(units=1))
N_net.initialize()

現在我們需要定義損失,如下所示:

loss_function = L2Loss()

接下來,我們需要建立虛擬資料,如下所示:

x = mx.nd.array([[0.5, 0.9]])
y = mx.nd.array([[1.5]])

現在,我們準備進行第一次透過網路的前向傳遞。我們希望 autograd 記錄計算圖,以便我們可以計算梯度。為此,我們需要在**autograd.record** 上下文的範圍內執行網路程式碼,如下所示:

with autograd.record():
   y_hat = N_net(x)
   loss = loss_function(y_hat, y)

現在,我們準備進行反向傳播,我們首先對感興趣的量呼叫反向方法。在我們的例子中,感興趣的量是損失,因為我們試圖計算損失相對於引數的梯度。

loss.backward()

現在,我們有了網路每個引數的梯度,最佳化器將使用這些梯度來更新引數值,以提高效能。讓我們檢查一下第一層的梯度,如下所示:

N_net[0].weight.grad()

輸出

輸出如下:

[[-0.00470527 -0.00846948]
[-0.03640365 -0.06552657]
[ 0.00800354 0.01440637]]
<NDArray 3x2 @cpu(0)>

完整的實現示例

下面是完整的實現示例。

from mxnet import autograd
import mxnet as mx
from mxnet.gluon.nn import HybridSequential, Dense
from mxnet.gluon.loss import L2Loss
N_net = HybridSequential()
N_net.add(Dense(units=3))
N_net.add(Dense(units=1))
N_net.initialize()
loss_function = L2Loss()
x = mx.nd.array([[0.5, 0.9]])
y = mx.nd.array([[1.5]])
with autograd.record():
y_hat = N_net(x)
loss = loss_function(y_hat, y)
loss.backward()
N_net[0].weight.grad()

Apache MXNet - NDArray

在本章中,我們將討論MXNet的多維陣列格式,稱為ndarray

使用NDArray處理資料

首先,我們將瞭解如何使用NDArray處理資料。以下是相同的前提條件:

前提條件

要理解如何使用這種多維陣列格式處理資料,我們需要滿足以下前提條件

  • 在Python環境中安裝MXNet

  • Python 2.7.x 或 Python 3.x

實現示例

讓我們藉助下面的示例來了解基本功能:

首先,我們需要匯入MXNet和來自MXNet的ndarray,如下所示:

import mxnet as mx
from mxnet import nd

匯入必要的庫後,我們將使用以下基本功能

使用Python列表建立一個簡單的1維陣列

示例

x = nd.array([1,2,3,4,5,6,7,8,9,10])
print(x)

輸出

輸出如下所示:

[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
<NDArray 10 @cpu(0)>

使用Python列表建立一個2維陣列

示例

y = nd.array([[1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10]])
print(y)

輸出

輸出如下所示:

[[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]]
<NDArray 3x10 @cpu(0)>

建立一個沒有初始化的NDArray

在這裡,我們將使用.empty函式建立一個具有3行4列的矩陣。我們還將使用.full函式,它將採用一個額外的運算子來指定要在陣列中填充的值。

示例

x = nd.empty((3, 4))
print(x)
x = nd.full((3,4), 8)
print(x)

輸出

輸出如下所示:

[[0.000e+00 0.000e+00 0.000e+00 0.000e+00]
 [0.000e+00 0.000e+00 2.887e-42 0.000e+00]
 [0.000e+00 0.000e+00 0.000e+00 0.000e+00]]
<NDArray 3x4 @cpu(0)>

[[8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]]
<NDArray 3x4 @cpu(0)>

使用.zeros函式建立一個全為零的矩陣

示例

x = nd.zeros((3, 8))
print(x)

輸出

輸出如下所示:

[[0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0.]]
<NDArray 3x8 @cpu(0)>

使用.ones函式建立一個全為一的矩陣

示例

x = nd.ones((3, 8))
print(x)

輸出

輸出如下所示:

[[1. 1. 1. 1. 1. 1. 1. 1.]
   [1. 1. 1. 1. 1. 1. 1. 1.]
   [1. 1. 1. 1. 1. 1. 1. 1.]]
<NDArray 3x8 @cpu(0)>

建立一個值隨機取樣的陣列

示例

y = nd.random_normal(0, 1, shape=(3, 4))
print(y)

輸出

輸出如下所示:

[[ 1.2673576 -2.0345826 -0.32537818 -1.4583491 ]
 [-0.11176403 1.3606371 -0.7889914 -0.17639421]
 [-0.2532185 -0.42614475 -0.12548696 1.4022992 ]]
<NDArray 3x4 @cpu(0)>

查詢每個NDArray的維度

示例

y.shape

輸出

輸出如下所示:

(3, 4)

查詢每個NDArray的大小

示例

y.size

輸出

12

查詢每個NDArray的資料型別

示例

y.dtype

輸出

numpy.float32

NDArray操作

在本節中,我們將向您介紹MXNet的陣列操作。NDArray支援大量的標準數學運算以及就地運算。

標準數學運算

以下是NDArray支援的標準數學運算:

逐元素加法

首先,我們需要匯入MXNet和來自MXNet的ndarray,如下所示

import mxnet as mx
from mxnet import nd
x = nd.ones((3, 5))
y = nd.random_normal(0, 1, shape=(3, 5))
print('x=', x)
print('y=', y)
x = x + y
print('x = x + y, x=', x)

輸出

輸出如下所示:

x=
[[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
<NDArray 3x5 @cpu(0)>
y=
[[-1.0554522 -1.3118273 -0.14674698 0.641493 -0.73820823]
[ 2.031364 0.5932667 0.10228804 1.179526 -0.5444829 ]
[-0.34249446 1.1086396 1.2756858 -1.8332436 -0.5289873 ]]
<NDArray 3x5 @cpu(0)>
x = x + y, x=
[[-0.05545223 -0.3118273 0.853253 1.6414931 0.26179177]
[ 3.031364 1.5932667 1.102288 2.1795259 0.4555171 ]
[ 0.6575055 2.1086397 2.2756858 -0.8332436 0.4710127 ]]
<NDArray 3x5 @cpu(0)>

逐元素乘法

示例

x = nd.array([1, 2, 3, 4])
y = nd.array([2, 2, 2, 1])
x * y

輸出

您將看到以下輸出:

[2. 4. 6. 4.]
<NDArray 4 @cpu(0)>

冪運算

示例

nd.exp(x)

輸出

執行程式碼後,您將看到以下輸出

[ 2.7182817 7.389056 20.085537 54.59815 ]
<NDArray 4 @cpu(0)>

矩陣轉置計算矩陣乘積

示例

nd.dot(x, y.T)

輸出

以下是程式碼的輸出:

[16.]
<NDArray 1 @cpu(0)>

就地操作

在上面的示例中,每次執行一個操作時,我們都會分配一個新的記憶體來儲存其結果。

例如,如果我們寫A = A+B,我們將取消引用A過去指向的矩陣,並將其指向新分配的記憶體。讓我們使用Python的id()函式來了解下面的示例:

print('y=', y)
print('id(y):', id(y))
y = y + x
print('after y=y+x, y=', y)
print('id(y):', id(y))

輸出

執行後,您將收到以下輸出:

y=
[2. 2. 2. 1.]
<NDArray 4 @cpu(0)>
id(y): 2438905634376
after y=y+x, y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)>
id(y): 2438905685664

事實上,我們也可以將結果賦值給以前分配的陣列,如下所示:

print('x=', x)
z = nd.zeros_like(x)
print('z is zeros_like x, z=', z)
print('id(z):', id(z))
print('y=', y)
z[:] = x + y
print('z[:] = x + y, z=', z)
print('id(z) is the same as before:', id(z))

輸出

輸出如下所示:

x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)>
z is zeros_like x, z=
[0. 0. 0. 0.]
<NDArray 4 @cpu(0)>
id(z): 2438905790760
y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)>
z[:] = x + y, z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)>
id(z) is the same as before: 2438905790760

從上面的輸出中,我們可以看到x+y仍然會分配一個臨時緩衝區來儲存結果,然後再將其複製到z。所以現在,我們可以執行就地操作來更好地利用記憶體並避免臨時緩衝區。為此,我們將為每個運算子指定out關鍵字引數,如下所示:

print('x=', x, 'is in id(x):', id(x))
print('y=', y, 'is in id(y):', id(y))
print('z=', z, 'is in id(z):', id(z))
nd.elemwise_add(x, y, out=z)
print('after nd.elemwise_add(x, y, out=z), x=', x, 'is in id(x):', id(x))
print('after nd.elemwise_add(x, y, out=z), y=', y, 'is in id(y):', id(y))
print('after nd.elemwise_add(x, y, out=z), z=', z, 'is in id(z):', id(z))

輸出

執行上述程式後,您將得到以下結果:

x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)> is in id(x): 2438905791152
y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)> is in id(y): 2438905685664
z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)> is in id(z): 2438905790760
after nd.elemwise_add(x, y, out=z), x=
[1. 2. 3. 4.]
<NDArray 4 @cpu(0)> is in id(x): 2438905791152
after nd.elemwise_add(x, y, out=z), y=
[3. 4. 5. 5.]
<NDArray 4 @cpu(0)> is in id(y): 2438905685664
after nd.elemwise_add(x, y, out=z), z=
[4. 6. 8. 9.]
<NDArray 4 @cpu(0)> is in id(z): 2438905790760

NDArray上下文

在Apache MXNet中,每個陣列都有一個上下文,一個上下文可能是CPU,而其他上下文可能是多個GPU。當我們將工作部署到多臺伺服器時,情況可能會更糟。因此,我們需要智慧地將陣列分配給上下文。這將最大限度地減少在裝置之間傳輸資料所花費的時間。

例如,嘗試初始化一個數組,如下所示:

from mxnet import nd
z = nd.ones(shape=(3,3), ctx=mx.cpu(0))
print(z)

輸出

執行上述程式碼後,您應該看到以下輸出:

[[1. 1. 1.]
 [1. 1. 1.]
 [1. 1. 1.]]
<NDArray 3x3 @cpu(0)>

我們可以使用copyto()方法將給定的NDArray從一個上下文複製到另一個上下文,如下所示:

x_gpu = x.copyto(gpu(0))
print(x_gpu)

NumPy陣列與NDArray

我們都熟悉NumPy陣列,但Apache MXNet提供了自己的陣列實現,名為NDArray。實際上,它最初的設計類似於NumPy,但有一個關鍵區別:

關鍵區別在於NumPy和NDArray中計算的執行方式。MXNet中的每個NDArray操作都是非同步且非阻塞的,這意味著當我們編寫類似c = a * b的程式碼時,該函式會被推送到執行引擎,它將啟動計算。

這裡,a和b都是NDArray。使用它的好處是,函式會立即返回,使用者執行緒可以繼續執行,即使之前的計算可能尚未完成。

執行引擎的工作原理

如果我們談論執行引擎的工作原理,它會構建計算圖。計算圖可能會重新排序或組合一些計算,但它始終遵守依賴順序。

例如,如果在程式設計程式碼的後面還有其他對“X”的操作,執行引擎將在“X”的結果可用後開始執行它們。執行引擎將為使用者處理一些重要的工作,例如編寫回調以啟動後續程式碼的執行。

在Apache MXNet中,藉助NDArray,要獲得計算結果,我們只需要訪問結果變數。程式碼流將被阻塞,直到計算結果被賦給結果變數。透過這種方式,它在仍然支援指令式程式設計模式的同時提高了程式碼效能。

將NDArray轉換為NumPy陣列

讓我們學習如何在MXNet中將NDArray轉換為NumPy陣列。

結合少量低階運算子來使用高階運算子

有時,我們可以使用現有的運算子來組裝一個高階運算子。最好的例子之一是np.full_like()運算子,它不存在於NDArray API中。它可以很容易地用現有運算子的組合來替換,如下所示

from mxnet import nd
import numpy as np
np_x = np.full_like(a=np.arange(7, dtype=int), fill_value=15)
nd_x = nd.ones(shape=(7,)) * 15
np.array_equal(np_x, nd_x.asnumpy())

輸出

我們將得到類似於以下的輸出:

True

查詢名稱和/或簽名不同的類似運算子

在所有運算子中,有些運算子的名稱略有不同,但在功能方面是相似的。一個例子是nd.ravel_index()np.ravel()函式。同樣,有些運算子可能具有相似的名稱,但它們的簽名不同。一個例子是np.split()nd.split()是相似的。

讓我們透過以下程式設計示例來了解它

def pad_array123(data, max_length):
data_expanded = data.reshape(1, 1, 1, data.shape[0])
data_padded = nd.pad(data_expanded,
mode='constant',
pad_width=[0, 0, 0, 0, 0, 0, 0, max_length - data.shape[0]],
constant_value=0)
data_reshaped_back = data_padded.reshape(max_length)
return data_reshaped_back
pad_array123(nd.array([1, 2, 3]), max_length=10)

輸出

輸出如下所示:

[1. 2. 3. 0. 0. 0. 0. 0. 0. 0.]
<NDArray 10 @cpu(0)>

最小化阻塞呼叫的影響

在某些情況下,我們必須使用.asnumpy().asscalar()方法,但這將強制MXNet阻塞執行,直到可以檢索結果。當我們認為該值的計算已經完成時,我們可以透過呼叫.asnumpy().asscalar()方法來最小化阻塞呼叫的影響。

實現示例

示例

from __future__ import print_function
import mxnet as mx
from mxnet import gluon, nd, autograd
from mxnet.ndarray import NDArray
from mxnet.gluon import HybridBlock
import numpy as np

class LossBuffer(object):
   """
   Simple buffer for storing loss value
   """
   
   def __init__(self):
      self._loss = None

   def new_loss(self, loss):
      ret = self._loss
      self._loss = loss
      return ret

      @property
      def loss(self):
         return self._loss

net = gluon.nn.Dense(10)
ce = gluon.loss.SoftmaxCELoss()
net.initialize()
data = nd.random.uniform(shape=(1024, 100))
label = nd.array(np.random.randint(0, 10, (1024,)), dtype='int32')
train_dataset = gluon.data.ArrayDataset(data, label)
train_data = gluon.data.DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=2)
trainer = gluon.Trainer(net.collect_params(), optimizer='sgd')
loss_buffer = LossBuffer()
for data, label in train_data:
   with autograd.record():
      out = net(data)
      # This call saves new loss and returns previous loss
      prev_loss = loss_buffer.new_loss(ce(out, label))
   loss_buffer.loss.backward()
   trainer.step(data.shape[0])
   if prev_loss is not None:
      print("Loss: {}".format(np.mean(prev_loss.asnumpy())))

輸出

輸出如下所示

Loss: 2.3373236656188965
Loss: 2.3656985759735107
Loss: 2.3613128662109375
Loss: 2.3197104930877686
Loss: 2.3054862022399902
Loss: 2.329197406768799
Loss: 2.318927526473999

Apache MXNet - Gluon

另一個最重要的MXNet Python包是Gluon。在本章中,我們將討論這個包。Gluon為深度學習專案提供了一個清晰、簡潔和簡單的API。它使Apache MXNet能夠在不犧牲訓練速度的情況下對深度學習模型進行原型設計、構建和訓練。

塊構成了更復雜的網路設計的基石。在神經網路中,隨著神經網路複雜性的增加,我們需要從設計單個神經元轉向設計整個神經元層。例如,ResNet-152之類的NN設計透過由重複層的組成,具有相當高的規律性。

示例

在下面的示例中,我們將為多層感知器編寫一個簡單的塊程式碼,即塊。

from mxnet import nd
from mxnet.gluon import nn
x = nd.random.uniform(shape=(2, 20))
N_net = nn.Sequential()
N_net.add(nn.Dense(256, activation='relu'))
N_net.add(nn.Dense(10))
N_net.initialize()
N_net(x)

輸出

這將產生以下輸出

[[ 0.09543004 0.04614332 -0.00286655 -0.07790346 -0.05130241 0.02942038
0.08696645 -0.0190793 -0.04122177 0.05088576]
[ 0.0769287 0.03099706 0.00856576 -0.044672 -0.06926838 0.09132431
0.06786592 -0.06187843 -0.03436674 0.04234696]]
<NDArray 2x10 @cpu(0)>

從定義層到定義一個或多個層的塊所需的步驟:

步驟1 - 塊將資料作為輸入。

步驟2 - 現在,塊將以引數的形式儲存狀態。例如,在上例程式碼中,塊包含兩個隱藏層,我們需要一個地方來儲存它的引數。

步驟3 - 接下來,塊將呼叫前向函式執行前向傳播。它也稱為前向計算。作為第一次前向呼叫的部分,塊以惰性方式初始化引數。

步驟4 - 最後,塊將呼叫反向函式並計算相對於其輸入的梯度。通常,此步驟會自動執行。

順序塊

順序塊是一種特殊的塊,其中資料流經一系列塊。在此過程中,每個塊都應用於前一個塊的輸出,第一個塊應用於輸入資料本身。

讓我們看看sequential類的使用方法:

from mxnet import nd
from mxnet.gluon import nn
class MySequential(nn.Block):
   def __init__(self, **kwargs):
      super(MySequential, self).__init__(**kwargs)

   def add(self, block):
      self._children[block.name] = block
   def forward(self, x):
   for block in self._children.values():
      x = block(x)
   return x
x = nd.random.uniform(shape=(2, 20))
N_net = MySequential()
N_net.add(nn.Dense(256, activation
='relu'))
N_net.add(nn.Dense(10))
N_net.initialize()
N_net(x)

輸出

輸出如下所示:

[[ 0.09543004 0.04614332 -0.00286655 -0.07790346 -0.05130241 0.02942038
0.08696645 -0.0190793 -0.04122177 0.05088576]
[ 0.0769287 0.03099706 0.00856576 -0.044672 -0.06926838 0.09132431
0.06786592 -0.06187843 -0.03436674 0.04234696]]
<NDArray 2x10 @cpu(0)>

自定義塊

我們可以輕鬆地超越上面定義的順序塊的串聯。但是,如果我們想進行自定義,那麼Block類也為我們提供了所需的功能。Block類在nn模組中提供了一個模型建構函式。我們可以繼承該模型建構函式來定義我們想要的模型。

在下面的示例中,MLP類重寫了Block類的__init__和forward函式。

讓我們看看它是如何工作的。

class MLP(nn.Block):

   def __init__(self, **kwargs):
      super(MLP, self).__init__(**kwargs)
      self.hidden = nn.Dense(256, activation='relu') # Hidden layer
      self.output = nn.Dense(10) # Output layer


   def forward(self, x):
      hidden_out = self.hidden(x)
      return self.output(hidden_out)
x = nd.random.uniform(shape=(2, 20))
N_net = MLP()
N_net.initialize()
N_net(x)

輸出

執行程式碼後,您將看到以下輸出

[[ 0.07787763 0.00216403 0.01682201 0.03059879 -0.00702019 0.01668715
0.04822846 0.0039432 -0.09300035 -0.04494302]
[ 0.08891078 -0.00625484 -0.01619131 0.0380718 -0.01451489 0.02006172
0.0303478 0.02463485 -0.07605448 -0.04389168]]
<NDArray 2x10 @cpu(0)>

自定義層

Apache MXNet的Gluon API帶有一些預定義的層。但是,在某些時候,我們可能會發現需要一個新的層。我們可以輕鬆地在Gluon API中新增一個新的層。在本節中,我們將瞭解如何從頭開始建立一個新的層。

最簡單的自定義層

要在Gluon API中建立一個新的層,我們必須建立一個繼承自Block類的類,該類提供最基本的功能。我們可以直接或透過其他子類從它繼承所有預定義的層。

為了建立新層,唯一需要實現的例項方法是forward (self, x)。此方法定義了在正向傳播期間我們的層將執行的操作。如前所述,塊的反向傳播將由Apache MXNet自動完成。

示例

在下面的示例中,我們將定義一個新的層。我們還將實現forward()方法,透過將輸入資料擬合到[0, 1]範圍內來規範化輸入資料。

from __future__ import print_function
import mxnet as mx
from mxnet import nd, gluon, autograd
from mxnet.gluon.nn import Dense
mx.random.seed(1)
class NormalizationLayer(gluon.Block):
   def __init__(self):
      super(NormalizationLayer, self).__init__()

   def forward(self, x):
      return (x - nd.min(x)) / (nd.max(x) - nd.min(x))
x = nd.random.uniform(shape=(2, 20))
N_net = NormalizationLayer()
N_net.initialize()
N_net(x)

輸出

執行上述程式後,您將得到以下結果:

[[0.5216355 0.03835821 0.02284337 0.5945146 0.17334817 0.69329053
0.7782702 1. 0.5508242 0. 0.07058554 0.3677264
0.4366546 0.44362497 0.7192635 0.37616986 0.6728799 0.7032008

 0.46907538 0.63514024]
[0.9157533 0.7667402 0.08980197   0.03593295 0.16176797 0.27679572
 0.07331014 0.3905285 0.6513384 0.02713427 0.05523694 0.12147208
 0.45582628 0.8139887 0.91629887 0.36665893 0.07873632 0.78268915
 0.63404864 0.46638715]]
 <NDArray 2x20 @cpu(0)>

混合

它可以定義為Apache MXNet用於建立前向計算的符號圖的過程。混合允許MXNet透過最佳化計算符號圖來提高計算效能。事實上,我們可能會發現,在實現現有層時,一個塊繼承自HybridBlock,而不是直接繼承自Block

以下是原因:

  • 允許我們編寫自定義層:HybridBlock允許我們編寫自定義層,這些層可以在指令式程式設計和符號式程式設計中進一步使用。

  • 提高計算效能− HybridBlock最佳化計算符號圖,使MXNet能夠提高計算效能。

示例

在這個例子中,我們將使用HybridBlock重寫上面建立的示例層。

class NormalizationHybridLayer(gluon.HybridBlock):
   def __init__(self):
      super(NormalizationHybridLayer, self).__init__()

   def hybrid_forward(self, F, x):
      return F.broadcast_div(F.broadcast_sub(x, F.min(x)), (F.broadcast_sub(F.max(x), F.min(x))))

layer_hybd = NormalizationHybridLayer()
layer_hybd(nd.array([1, 2, 3, 4, 5, 6], ctx=mx.cpu()))

輸出

輸出如下所示

[0. 0.2 0.4 0.6 0.8 1. ]
<NDArray 6 @cpu(0)>

混合化與GPU上的計算無關,可以在CPU和GPU上訓練混合網路和非混合網路。

Block和HybridBlock的區別

如果我們將Block類和HybridBlock進行比較,我們會看到HybridBlock已經實現了它的forward()方法。HybridBlock定義了一個需要在建立層時實現的hybrid_forward()方法。F引數是forward()hybrid_forward()之間主要的區別。在MXNet社群中,F引數被稱為後端。F可以指mxnet.ndarray API(用於指令式程式設計)或mxnet.symbol API(用於符號式程式設計)。

如何將自定義層新增到網路中?

這些層不是單獨使用自定義層,而是與預定義層一起使用。我們可以使用SequentialHybridSequential容器來形成一個順序神經網路。如前所述,Sequential容器繼承自Block,HybridSequential分別繼承自HybridBlock

示例

在下面的例子中,我們將建立一個具有自定義層的神經網路。Dense (5)層的輸出將成為NormalizationHybridLayer的輸入。NormalizationHybridLayer的輸出將成為Dense (1)層的輸入。

net = gluon.nn.HybridSequential()
with net.name_scope():
net.add(Dense(5))
net.add(NormalizationHybridLayer())
net.add(Dense(1))
net.initialize(mx.init.Xavier(magnitude=2.24))
net.hybridize()
input = nd.random_uniform(low=-10, high=10, shape=(10, 2))
net(input)

輸出

您將看到以下輸出:

[[-1.1272651]
 [-1.2299833]
 [-1.0662932]
 [-1.1805027]
 [-1.3382034]
 [-1.2081106]
 [-1.1263978]
 [-1.2524893]
 
 [-1.1044774]

 [-1.316593 ]]
<NDArray 10x1 @cpu(0)>

自定義層引數

在一個神經網路中,一個層有一組與其相關的引數。我們有時稱它們為權重,它是層的內部狀態。這些引數扮演著不同的角色:

  • 有時這些是我們想要在反向傳播步驟中學習的引數。

  • 有時這些只是我們想要在正向傳遞中使用的常數。

如果我們談論程式設計概念,塊的這些引數(權重)透過ParameterDict類儲存和訪問,這有助於它們的初始化、更新、儲存和載入。

示例

在下面的例子中,我們將定義以下兩組引數:

  • 引數權重 - 這是可訓練的,其形狀在構造階段是未知的。它將在第一次正向傳播執行時推斷出來。

  • 引數比例 - 這是一個常數,其值不會改變。與引數權重相反,它的形狀在構造期間定義。

class NormalizationHybridLayer(gluon.HybridBlock):
   def __init__(self, hidden_units, scales):
      super(NormalizationHybridLayer, self).__init__()
      with self.name_scope():
      self.weights = self.params.get('weights',
      shape=(hidden_units, 0),
      allow_deferred_init=True)
      self.scales = self.params.get('scales',
         shape=scales.shape,
         init=mx.init.Constant(scales.asnumpy()),
         differentiable=False)
      def hybrid_forward(self, F, x, weights, scales):
         normalized_data = F.broadcast_div(F.broadcast_sub(x, F.min(x)),
         (F.broadcast_sub(F.max(x), F.min(x))))
         weighted_data = F.FullyConnected(normalized_data, weights, num_hidden=self.weights.shape[0], no_bias=True)
         scaled_data = F.broadcast_mul(scales, weighted_data)
return scaled_data

Apache MXNet - KVStore 和視覺化

本章介紹python包KVStore和視覺化。

KVStore包

KV儲存代表鍵值儲存。它是用於多裝置訓練的關鍵元件。它很重要,因為引數在單個裝置以及跨多個機器上的裝置之間的通訊是透過一個或多個伺服器與KVStore進行引數傳輸的。

讓我們透過以下幾點來了解KVStore的工作原理

  • KVStore 中的每個值都由一個**鍵**和一個**值**表示。

  • 網路中的每個引數陣列都分配一個**鍵**,該引數陣列的權重由**值**引用。

  • 之後,工作節點在處理完一個批次後**推送**梯度。它們還在處理新批次之前**拉取**更新後的權重。

簡單來說,我們可以說KVStore是一個數據共享的地方,每個裝置都可以將資料推入和拉出。

資料推入和拉出

KVStore可以被認為是跨不同裝置(如GPU和計算機)共享的單個物件,每個裝置都可以將資料推入和拉出。

以下是裝置需要遵循的將資料推入和拉出的實現步驟

實現步驟

初始化 - 第一步是初始化值。在這裡,對於我們的示例,我們將一個(int, NDArray)對初始化到KVStrore中,然後將值拉出:

import mxnet as mx
kv = mx.kv.create('local') # create a local KVStore.
shape = (3,3)
kv.init(3, mx.nd.ones(shape)*2)
a = mx.nd.zeros(shape)
kv.pull(3, out = a)
print(a.asnumpy())

輸出

這將產生以下輸出:

[[2. 2. 2.]
[2. 2. 2.]
[2. 2. 2.]]

推送、聚合和更新 - 初始化後,我們可以將具有相同形狀的新值推送到KVStore中的同一鍵:

kv.push(3, mx.nd.ones(shape)*8)
kv.pull(3, out = a)
print(a.asnumpy())

輸出

輸出如下所示:

[[8. 8. 8.]
 [8. 8. 8.]
 [8. 8. 8.]]

用於推送的資料可以儲存在任何裝置上,例如GPU或計算機。我們還可以將多個值推送到同一個鍵。在這種情況下,KVStore將首先將所有這些值相加,然後按如下方式推送聚合值:

contexts = [mx.cpu(i) for i in range(4)]
b = [mx.nd.ones(shape, ctx) for ctx in contexts]
kv.push(3, b)
kv.pull(3, out = a)
print(a.asnumpy())

輸出

您將看到以下輸出:

[[4. 4. 4.]
 [4. 4. 4.]
 [4. 4. 4.]]

對於您應用的每次推送,KVStore都會將推送的值與已儲存的值合併。這將藉助更新器完成。這裡,預設更新器為ASSIGN。

def update(key, input, stored):
   print("update on key: %d" % key)
   
   stored += input * 2
kv.set_updater(update)
kv.pull(3, out=a)
print(a.asnumpy())

輸出

執行上述程式碼後,您應該看到以下輸出:

[[4. 4. 4.]
 [4. 4. 4.]
 [4. 4. 4.]]

示例

kv.push(3, mx.nd.ones(shape))
kv.pull(3, out=a)
print(a.asnumpy())

輸出

以下是程式碼的輸出:

update on key: 3
[[6. 6. 6.]
 [6. 6. 6.]
 [6. 6. 6.]]

拉取 - 與推送一樣,我們也可以透過一次呼叫將值拉取到多個裝置上:

b = [mx.nd.ones(shape, ctx) for ctx in contexts]
kv.pull(3, out = b)
print(b[1].asnumpy())

輸出

輸出如下所示:

[[6. 6. 6.]
 [6. 6. 6.]
 [6. 6. 6.]]

完整的實現示例

下面是完整的實現示例:

import mxnet as mx
kv = mx.kv.create('local')
shape = (3,3)
kv.init(3, mx.nd.ones(shape)*2)
a = mx.nd.zeros(shape)
kv.pull(3, out = a)
print(a.asnumpy())
kv.push(3, mx.nd.ones(shape)*8)
kv.pull(3, out = a) # pull out the value
print(a.asnumpy())
contexts = [mx.cpu(i) for i in range(4)]
b = [mx.nd.ones(shape, ctx) for ctx in contexts]
kv.push(3, b)
kv.pull(3, out = a)
print(a.asnumpy())
def update(key, input, stored):
   print("update on key: %d" % key)
   stored += input * 2
kv._set_updater(update)
kv.pull(3, out=a)
print(a.asnumpy())
kv.push(3, mx.nd.ones(shape))
kv.pull(3, out=a)
print(a.asnumpy())
b = [mx.nd.ones(shape, ctx) for ctx in contexts]
kv.pull(3, out = b)
print(b[1].asnumpy())

處理鍵值對

我們上面實現的所有操作都涉及單個鍵,但KVStore還提供鍵值對列表的介面:

對於單個裝置

以下是一個示例,用於顯示單個裝置的鍵值對列表的KVStore介面:

keys = [5, 7, 9]
kv.init(keys, [mx.nd.ones(shape)]*len(keys))
kv.push(keys, [mx.nd.ones(shape)]*len(keys))
b = [mx.nd.zeros(shape)]*len(keys)
kv.pull(keys, out = b)
print(b[1].asnumpy())

輸出

您將收到以下輸出:

update on key: 5
update on key: 7
update on key: 9
[[3. 3. 3.]
 [3. 3. 3.]
 [3. 3. 3.]]

對於多個裝置

以下是一個示例,用於顯示多個裝置的鍵值對列表的KVStore介面:

b = [[mx.nd.ones(shape, ctx) for ctx in contexts]] * len(keys)
kv.push(keys, b)
kv.pull(keys, out = b)
print(b[1][1].asnumpy())

輸出

您將看到以下輸出:

update on key: 5
update on key: 7
update on key: 9
[[11. 11. 11.]
 [11. 11. 11.]
 [11. 11. 11.]]

視覺化包

視覺化包是Apache MXNet包,用於將神經網路(NN)表示為由節點和邊組成的計算圖。

視覺化神經網路

在下面的示例中,我們將使用mx.viz.plot_network來視覺化神經網路。以下是先決條件:

前提條件

  • Jupyter notebook

  • Graphviz庫

實現示例

在下面的示例中,我們將視覺化一個用於線性矩陣分解的示例NN:

import mxnet as mx
user = mx.symbol.Variable('user')
item = mx.symbol.Variable('item')
score = mx.symbol.Variable('score')

# Set the dummy dimensions
k = 64
max_user = 100
max_item = 50

# The user feature lookup
user = mx.symbol.Embedding(data = user, input_dim = max_user, output_dim = k)

# The item feature lookup
item = mx.symbol.Embedding(data = item, input_dim = max_item, output_dim = k)

# predict by the inner product and then do sum
N_net = user * item
N_net = mx.symbol.sum_axis(data = N_net, axis = 1)
N_net = mx.symbol.Flatten(data = N_net)

# Defining the loss layer
N_net = mx.symbol.LinearRegressionOutput(data = N_net, label = score)

# Visualize the network
mx.viz.plot_network(N_net)

Apache MXNet - Python API ndarray

本章解釋了Apache MXNet中提供的ndarray庫。

Mxnet.ndarray

Apache MXNet的NDArray庫定義了所有數學計算的核心DS(資料結構)。NDArray的兩個基本工作如下:

  • 它支援在各種硬體配置上的快速執行。

  • 它自動跨可用硬體並行化多個操作。

下面給出的示例顯示瞭如何使用來自普通Python列表的一維和二維“陣列”來建立NDArray:

import mxnet as mx
from mxnet import nd

x = nd.array([1,2,3,4,5,6,7,8,9,10])
print(x)

輸出

輸出如下所示

[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
<NDArray 10 @cpu(0)>

示例

y = nd.array([[1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10], [1,2,3,4,5,6,7,8,9,10]])
print(y)

輸出

這將產生以下輸出:

[[ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
 [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]
 [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.]]
<NDArray 3x10 @cpu(0)>

現在讓我們詳細討論MXNet的ndarray API的類、函式和引數。

下表包含MXNet的ndarray API的類:

定義
CachedOp(sym[, flags]) 它用於快取操作控制代碼。
NDArray(handle[, writable]) 它用作表示固定大小項的多維同質陣列的陣列物件。

函式及其引數

以下是mxnet.ndarray API涵蓋的一些重要函式及其引數:

函式及其引數 定義
Activation([data, act_type, out, name]) 它將啟用函式逐元素應用於輸入。它支援relu、sigmoid、tanh、softrelu、softsign啟用函式。
BatchNorm([data, gamma, beta, moving_mean, …]) 它用於批次歸一化。此函式透過均值和方差歸一化資料批次。它應用比例gamma和偏移beta。
BilinearSampler([data, grid, cudnn_off, …])

此函式將雙線性取樣應用於輸入特徵圖。實際上,它是“空間變換網路”的關鍵。

如果您熟悉OpenCV中的remap函式,則此函式的使用與該函式非常相似。唯一的區別是它有反向傳遞。

BlockGrad([data, out, name]) 顧名思義,此函式停止梯度計算。它基本上阻止輸入的累積梯度在反向方向上流經此運算子。
cast([data, dtype, out, name]) 此函式將輸入的所有元素轉換為新型別。

實現示例

在下面的示例中,我們將使用BilinierSampler()函式將資料縮小兩倍,並將資料水平移動-1畫素:

import mxnet as mx
from mxnet import nd
data = nd.array([[[[2, 5, 3, 6],
   [1, 8, 7, 9],
   [0, 4, 1, 8],
   [2, 0, 3, 4]]]])
affine_matrix = nd.array([[2, 0, 0],
   [0, 2, 0]])

affine_matrix = nd.reshape(affine_matrix, shape=(1, 6))

grid = nd.GridGenerator(data=affine_matrix, transform_type='affine', target_shape=(4, 4))

output = nd.BilinearSampler(data, grid)

輸出

執行上述程式碼時,您應該看到以下輸出

[[[[0. 0. 0. 0. ]
   [0. 4.0000005 6.25 0. ]
   [0. 1.5 4. 0. ]
   [0. 0. 0. 0. ]]]]
<NDArray 1x1x4x4 @cpu(0)>

上述輸出顯示了資料縮小兩倍。

將資料移動-1畫素的示例如下:

import mxnet as mx
from mxnet import nd
data = nd.array([[[[2, 5, 3, 6],
   [1, 8, 7, 9],
   [0, 4, 1, 8],
   [2, 0, 3, 4]]]])
warp_matrix = nd.array([[[[1, 1, 1, 1],
   [1, 1, 1, 1],
   [1, 1, 1, 1],
   [1, 1, 1, 1]],
   [[0, 0, 0, 0],
   [0, 0, 0, 0],
   [0, 0, 0, 0],
   [0, 0, 0, 0]]]])
grid = nd.GridGenerator(data=warp_matrix, transform_type='warp')
output = nd.BilinearSampler(data, grid)

輸出

輸出如下所示:

[[[[5. 3. 6. 0.]
[8. 7. 9. 0.]
[4. 1. 8. 0.]
[0. 3. 4. 0.]]]]
<NDArray 1x1x4x4 @cpu(0)>

類似地,以下示例顯示了cast()函式的使用:

nd.cast(nd.array([300, 10.1, 15.4, -1, -2]), dtype='uint8')

輸出

執行後,您將收到以下輸出:

[ 44 10 15 255 254]
<NDArray 5 @cpu(0)>

ndarray.contrib

Contrib NDArray API在ndarray.contrib包中定義。它通常為新功能提供許多有用的實驗性API。此API作為社群嘗試新功能的地方。特性貢獻者也將獲得反饋。

函式及其引數

以下是mxnet.ndarray.contrib API涵蓋的一些重要函式及其引數:

函式及其引數 定義
rand_zipfian(true_classes, num_sampled, …) 此函式從近似齊夫分佈中抽取隨機樣本。此函式的基本分佈是齊夫分佈。此函式隨機抽取num_sampled個候選樣本,並且sampled_candidates的元素是從上面給出的基本分佈中抽取的。
foreach(body, data, init_states) 顧名思義,此函式在維度0上對NDArrays執行帶有使用者定義計算的for迴圈。此函式模擬for迴圈,body具有for迴圈迭代的計算。
while_loop(cond, func, loop_vars[, …]) 顧名思義,此函式執行帶有使用者定義計算和迴圈條件的while迴圈。此函式模擬一個while迴圈,如果條件滿足,則逐字進行自定義計算。
cond(pred, then_func, else_func) 顧名思義,此函式使用使用者定義的條件和計算執行if-then-else。此函式模擬一個if型別的分支,根據指定的條件選擇執行兩個自定義計算中的一個。
isinf(data) 此函式執行逐元素檢查以確定NDArray是否包含無限元素。
getnnz([data, axis, out, name]) 此函式為我們提供稀疏張量的儲存值的個數。它還包括顯式零。它只支援CPU上的CSR矩陣。
requantize([data, min_range, max_range, …]) 此函式將以int32量化並具有相應閾值的資料重新量化為int8,使用在執行時計算或來自校準的最小和最大閾值。

實現示例

在下面的示例中,我們將使用rand_zipfian函式從近似齊夫分佈中抽取隨機樣本:

import mxnet as mx
from mxnet import nd
trueclass = mx.nd.array([2])
samples, exp_count_true, exp_count_sample = mx.nd.contrib.rand_zipfian(trueclass, 3, 4)
samples

輸出

您將看到以下輸出:

[0 0 1]
<NDArray 3 @cpu(0)>

示例

exp_count_true

輸出

輸出如下所示

[0.53624076]
<NDArray 1 @cpu(0)>

示例

exp_count_sample

輸出

這將產生以下輸出

[1.29202967 1.29202967 0.75578891]
<NDArray 3 @cpu(0)>

在下面的示例中,我們將使用while_loop函式執行使用者定義計算和迴圈條件的while迴圈

cond = lambda i, s: i <= 7
func = lambda i, s: ([i + s], [i + 1, s + i])
loop_var = (mx.nd.array([0], dtype="int64"), mx.nd.array([1], dtype="int64"))
outputs, states = mx.nd.contrib.while_loop(cond, func, loop_vars, max_iterations=10)
outputs

輸出

輸出如下所示:

[
[[       1]
 [      2]
 [      4]
 [      7]
 [     11]
 [     16]
 [     22]
 [     29]
 [3152434450384]
 [     257]]
<NDArray 10x1 @cpu(0)>]

示例

States

輸出

這將產生以下輸出:

[
[8]
<NDArray 1 @cpu(0)>,
[29]
<NDArray 1 @cpu(0)>]

ndarray.image

Image NDArray API在ndarray.image包中定義。顧名思義,它通常用於影像及其特徵。

函式及其引數

以下是mxnet.ndarray.image API涵蓋的一些重要函式及其引數:

函式及其引數 定義
adjust_lighting([data, alpha, out, name]) 顧名思義,此函式調整輸入的光照級別。它遵循AlexNet樣式。
crop([data, x, y, width, height, out, name]) 藉助此函式,我們可以將形狀為(H x W x C)或(N x H x W x C)的影像NDArray裁剪為使用者指定的大小。
normalize([data, mean, std, out, name]) 它將使用均值標準差(SD)對形狀為(C x H x W)或(N x C x H x W)的張量進行歸一化。
random_crop([data, xrange, yrange, width, …]) 與crop()類似,它會隨機裁剪形狀為(H x W x C)或(N x H x W x C)的影像NDArray到使用者指定的大小。如果src小於該大小,它將對結果進行上取樣。
random_lighting([data, alpha_std, out, name]) 顧名思義,此函式會隨機新增PCA噪聲。它也遵循AlexNet樣式。
random_resized_crop([data, xrange, yrange, …]) 它還會隨機裁剪形狀為(H x W x C)或(N x H x W x C)的影像NDArray到指定的大小。如果src小於該大小,它將對結果進行上取樣。它還將隨機化區域和縱橫比。
resize([data, size, keep_ratio, interp, …]) 顧名思義,此函式將調整形狀為 (H x W x C) 或 (N x H x W x C) 的影像 NDArray 的大小到使用者指定的大小。
to_tensor([data, out, name]) 它將形狀為 (H x W x C) 或 (N x H x W x C) 且值在 [0, 255] 範圍內的影像 NDArray 轉換為形狀為 (C x H x W) 或 (N x C x H x W) 且值在 [0, 1] 範圍內的張量 NDArray。

實現示例

在下面的示例中,我們將使用 to_tensor 函式將形狀為 (H x W x C) 或 (N x H x W x C) 且值在 [0, 255] 範圍內的影像 NDArray 轉換為形狀為 (C x H x W) 或 (N x C x H x W) 且值在 [0, 1] 範圍內的張量 NDArray。

import numpy as np
img = mx.nd.random.uniform(0, 255, (4, 2, 3)).astype(dtype=np.uint8)
mx.nd.image.to_tensor(img)

輸出

您將看到以下輸出:

[[[0.972549 0.5058824 ]
   [0.6039216 0.01960784]
   [0.28235295 0.35686275]
   [0.11764706 0.8784314 ]]

[[0.8745098 0.9764706 ]
   [0.4509804 0.03529412]
   [0.9764706 0.29411766]
   [0.6862745 0.4117647 ]]

[[0.46666667 0.05490196]
   [0.7372549 0.4392157 ]
   [0.11764706 0.47843137]
   [0.31764707 0.91764706]]]
<NDArray 3x4x2 @cpu(0)>

示例

img = mx.nd.random.uniform(0, 255, (2, 4, 2, 3)).astype(dtype=np.uint8)

mx.nd.image.to_tensor(img)

輸出

執行程式碼後,您將看到以下輸出:

[[[[0.0627451 0.5647059 ]
[0.2627451 0.9137255 ]
[0.57254905 0.27450982]
[0.6666667 0.64705884]]
[[0.21568628 0.5647059 ]
[0.5058824 0.09019608]
[0.08235294 0.31764707]
[0.8392157 0.7137255 ]]
[[0.6901961 0.8627451 ]
[0.52156866 0.91764706]
[0.9254902 0.00784314]
[0.12941177 0.8392157 ]]]
[[[0.28627452 0.39607844]
[0.01960784 0.36862746]
[0.6745098 0.7019608 ]
[0.9607843 0.7529412 ]]
[[0.2627451 0.58431375]
[0.16470589 0.00392157]
[0.5686275 0.73333335]
[0.43137255 0.57254905]]
[[0.18039216 0.54901963]
[0.827451 0.14509805]
[0.26666668 0.28627452]
[0.24705882 0.39607844]]]]
<NDArgt;ray 2x3x4x2 @cpu(0)>

在下面的示例中,我們將使用normalize函式對形狀為 (C x H x W) 或 (N x C x H x W) 、具有均值標準差 (SD)的張量進行標準化。

img = mx.nd.random.uniform(0, 1, (3, 4, 2))

mx.nd.image.normalize(img, mean=(0, 1, 2), std=(3, 2, 1))

輸出

這將產生以下輸出:

[[[ 0.29391178 0.3218054 ]
[ 0.23084386 0.19615503]
[ 0.24175143 0.21988946]
[ 0.16710812 0.1777354 ]]
[[-0.02195817 -0.3847335 ]
[-0.17800489 -0.30256534]
[-0.28807247 -0.19059572]
[-0.19680339 -0.26256624]]
[[-1.9808068 -1.5298678 ]
[-1.6984252 -1.2839255 ]
[-1.3398265 -1.712009 ]
[-1.7099224 -1.6165378 ]]]
<NDArray 3x4x2 @cpu(0)>

示例

img = mx.nd.random.uniform(0, 1, (2, 3, 4, 2))

mx.nd.image.normalize(img, mean=(0, 1, 2), std=(3, 2, 1))

輸出

執行上述程式碼後,您應該看到以下輸出:

[[[[ 2.0600514e-01 2.4972327e-01]
[ 1.4292289e-01 2.9281738e-01]
[ 4.5158025e-02 3.4287784e-02]
[ 9.9427439e-02 3.0791296e-02]]
[[-2.1501756e-01 -3.2297665e-01]
[-2.0456362e-01 -2.2409186e-01]
[-2.1283737e-01 -4.8318747e-01]
[-1.7339960e-01 -1.5519112e-02]]
[[-1.3478968e+00 -1.6790028e+00]
[-1.5685816e+00 -1.7787373e+00]
[-1.1034534e+00 -1.8587360e+00]
[-1.6324382e+00 -1.9027401e+00]]]
[[[ 1.4528830e-01 3.2801408e-01]
[ 2.9730779e-01 8.6780310e-02]
[ 2.6873133e-01 1.7900752e-01]
[ 2.3462953e-01 1.4930873e-01]]
[[-4.4988656e-01 -4.5021546e-01]
[-4.0258706e-02 -3.2384416e-01]
[-1.4287934e-01 -2.6537544e-01]
[-5.7649612e-04 -7.9429924e-02]]
[[-1.8505517e+00 -1.0953522e+00]
[-1.1318740e+00 -1.9624406e+00]
[-1.8375070e+00 -1.4916846e+00]
[-1.3844404e+00 -1.8331525e+00]]]]
<NDArray 2x3x4x2 @cpu(0)>

ndarray.random

隨機 NDArray API 定義在 ndarray.random 包中。顧名思義,它是 MXNet 的隨機分佈生成器 NDArray API。

函式及其引數

以下是mxnet.ndarray.random API涵蓋的一些重要函式及其引數:

函式及其引數 定義
uniform([low, high, shape, dtype, ctx, out]) 它從均勻分佈中生成隨機樣本。
normal([loc, scale, shape, dtype, ctx, out]) 它從正態 (高斯) 分佈中生成隨機樣本。
randn(*shape, **kwargs) 它從正態 (高斯) 分佈中生成隨機樣本。
exponential([scale, shape, dtype, ctx, out]) 它從指數分佈中生成樣本。
gamma([alpha, beta, shape, dtype, ctx, out]) 它從伽馬分佈中生成隨機樣本。
multinomial(data[, shape, get_prob, out, dtype]) 它從多個多項分佈中生成併發取樣。
negative_binomial([k, p, shape, dtype, ctx, out]) 它從負二項分佈中生成隨機樣本。
generalized_negative_binomial([mu, alpha, …]) 它從廣義負二項分佈中生成隨機樣本。
shuffle(data, **kwargs) 它隨機打亂元素。
randint(low, high[, shape, dtype, ctx, out]) 它從離散均勻分佈中生成隨機樣本。
exponential_like([data, lam, out, name]) 它根據輸入陣列形狀從指數分佈中生成隨機樣本。
gamma_like([data, alpha, beta, out, name]) 它根據輸入陣列形狀從伽馬分佈中生成隨機樣本。
generalized_negative_binomial_like([data, …]) 它根據輸入陣列形狀從廣義負二項分佈中生成隨機樣本。
negative_binomial_like([data, k, p, out, name]) 它根據輸入陣列形狀從負二項分佈中生成隨機樣本。
normal_like([data, loc, scale, out, name]) 它根據輸入陣列形狀從正態 (高斯) 分佈中生成隨機樣本。
poisson_like([data, lam, out, name]) 它根據輸入陣列形狀從泊松分佈中生成隨機樣本。
uniform_like([data, low, high, out, name]) 它根據輸入陣列形狀從均勻分佈中生成隨機樣本。

實現示例

在下面的示例中,我們將從均勻分佈中抽取隨機樣本。為此,我們將使用uniform()函式。

mx.nd.random.uniform(0, 1)

輸出

輸出如下所示:

[0.12381998]
<NDArray 1 @cpu(0)>

示例

mx.nd.random.uniform(-1, 1, shape=(2,))

輸出

輸出如下所示:

[0.558102 0.69601643]
<NDArray 2 @cpu(0)>

示例

low = mx.nd.array([1,2,3])
high = mx.nd.array([2,3,4])
mx.nd.random.uniform(low, high, shape=2)

輸出

您將看到以下輸出:

[[1.8649333 1.8073189]
 [2.4113967 2.5691009]
 [3.1399727 3.4071832]]
<NDArray 3x2 @cpu(0)>

在下面的示例中,我們將從廣義負二項分佈中抽取隨機樣本。為此,我們將使用generalized_negative_binomial()函式。

mx.nd.random.generalized_negative_binomial(10, 0.5)

輸出

執行上述程式碼後,您應該看到以下輸出:

[1.]
<NDArray 1 @cpu(0)>

示例

mx.nd.random.generalized_negative_binomial(10, 0.5, shape=(2,))

輸出

輸出如下所示:

[16. 23.]
<NDArray 2 @cpu(0)>

示例

mu = mx.nd.array([1,2,3])
alpha = mx.nd.array([0.2,0.4,0.6])
mx.nd.random.generalized_negative_binomial(mu, alpha, shape=2)

輸出

以下是程式碼的輸出:

[[0. 0.]
 [4. 1.]
 [9. 3.]]
<NDArray 3x2 @cpu(0)>

ndarray.utils

實用程式 NDArray API 定義在 ndarray.utils 包中。顧名思義,它為 NDArray 和 BaseSparseNDArray 提供實用程式函式。

函式及其引數

以下是mxnet.ndarray.utils API涵蓋的一些重要函式及其引數:

函式及其引數 定義
zeros(shape[, ctx, dtype, stype]) 此函式將返回一個具有給定形狀和型別的新陣列,其中填充為零。
empty(shape[, ctx, dtype, stype]) 它將返回一個具有給定形狀和型別的新陣列,但不初始化條目。
array(source_array[, ctx, dtype]) 顧名思義,此函式將從任何公開陣列介面的物件建立一個數組。
load(fname) 它將從檔案中載入一個數組。
load_frombuffer(buf) 顧名思義,此函式將從緩衝區載入陣列字典或列表。
save(fname, data) 此函式將陣列列表或 str->array 字典儲存到檔案中。

實現示例

在下面的示例中,我們將返回一個具有給定形狀和型別的新陣列,其中填充為零。為此,我們將使用zeros()函式。

mx.nd.zeros((1,2), mx.cpu(), stype='csr')

輸出

這將產生以下輸出:

<CSRNDArray 1x2 @cpu(0)>

示例

mx.nd.zeros((1,2), mx.cpu(), 'float16', stype='row_sparse').asnumpy()

輸出

您將收到以下輸出:

array([[0., 0.]], dtype=float16)

在下面的示例中,我們將儲存一個數組列表和一個字串字典。為此,我們將使用save()函式。

示例

x = mx.nd.zeros((2,3))
y = mx.nd.ones((1,4))
mx.nd.save('list', [x,y])
mx.nd.save('dict', {'x':x, 'y':y})
mx.nd.load('list')

輸出

執行後,您將收到以下輸出:

[
[[0. 0. 0.]
[0. 0. 0.]]
<NDArray 2x3 @cpu(0)>,
[[1. 1. 1. 1.]]
<NDArray 1x4 @cpu(0)>]

示例

mx.nd.load('my_dict')

輸出

輸出如下所示:

{'x':
[[0. 0. 0.]
[0. 0. 0.]]
<NDArray 2x3 @cpu(0)>, 'y':
[[1. 1. 1. 1.]]
<NDArray 1x4 @cpu(0)>}

Apache MXNet - Python API gluon

正如我們在前面章節中討論的那樣,MXNet Gluon 為深度學習專案提供了一個清晰、簡潔和簡單的 API。它使 Apache MXNet 能夠在不犧牲訓練速度的情況下對深度學習模型進行原型設計、構建和訓練。

核心模組

讓我們學習 Apache MXNet Python 應用程式程式設計介面 (API) gluon 的核心模組。

gluon.nn

Gluon 在 gluon.nn 模組中提供了大量的內建 NN 層。這就是它被稱為核心模組的原因。

方法及其引數

以下是mxnet.gluon.nn核心模組涵蓋的一些重要方法及其引數:

方法及其引數 定義
Activation(activation, **kwargs) 顧名思義,此方法將啟用函式應用於輸入。
AvgPool1D([pool_size, strides, padding, …]) 這是用於時間資料的平均池化操作。
AvgPool2D([pool_size, strides, padding, …]) 這是用於空間資料的平均池化操作。
AvgPool3D([pool_size, strides, padding, …]) 這是用於 3D 資料的平均池化操作。資料可以是空間的或時空的。
BatchNorm([axis, momentum, epsilon, center, …]) 它表示批次歸一化層。
BatchNormReLU([axis, momentum, epsilon, …]) 它也表示批次歸一化層,但具有 Relu 啟用函式。
Block([prefix, params]) 它為所有神經網路層和模型提供基類。
Conv1D(channels, kernel_size[, strides, …]) 此方法用於一維卷積層。例如,時間卷積。
Conv1DTranspose(channels, kernel_size[, …]) 此方法用於轉置一維卷積層。
Conv2D(channels, kernel_size[, strides, …]) 此方法用於二維卷積層。例如,影像上的空間卷積。
Conv2DTranspose(channels, kernel_size[, …]) 此方法用於轉置二維卷積層。
Conv3D(channels, kernel_size[, strides, …]) 此方法用於三維卷積層。例如,體積上的空間卷積。
Conv3DTranspose(channels, kernel_size[, …]) 此方法用於轉置三維卷積層。
Dense(units[, activation, use_bias, …]) 此方法表示常規密集連線的 NN 層。
Dropout(rate[, axes]) 顧名思義,此方法將 Dropout 應用於輸入。
ELU([alpha]) 此方法用於指數線性單元 (ELU)。
Embedding(input_dim, output_dim[, dtype, …]) 它將非負整數轉換為固定大小的密集向量。
Flatten(**kwargs) 此方法將輸入展平為二維。
GELU(**kwargs) 此方法用於高斯指數線性單元 (GELU)。
GlobalAvgPool1D([layout]) 藉助此方法,我們可以對時間資料進行全域性平均池化操作。
GlobalAvgPool2D([layout]) 藉助此方法,我們可以對空間資料進行全域性平均池化操作。
GlobalAvgPool3D([layout]) 藉助此方法,我們可以對 3D 資料進行全域性平均池化操作。
GlobalMaxPool1D([layout]) 藉助此方法,我們可以對一維資料進行全域性最大池化操作。
GlobalMaxPool2D([layout]) 藉助此方法,我們可以對二維資料進行全域性最大池化操作。
GlobalMaxPool3D([layout]) 藉助此方法,我們可以對三維資料進行全域性最大池化操作。
GroupNorm([num_groups, epsilon, center, …]) 此方法將組歸一化應用於 n 維輸入陣列。
HybridBlock([prefix, params]) 此方法支援使用SymbolNDArray進行前向傳播。
HybridLambda(function[, prefix]) 藉助此方法,我們可以將運算子或表示式包裝為 HybridBlock 物件。
HybridSequential([prefix, params]) 它按順序堆疊 HybridBlocks。
InstanceNorm([axis, epsilon, center, scale, …]) 此方法將例項歸一化應用於 n 維輸入陣列。

實現示例

在下面的示例中,我們將使用 Block(),它為所有神經網路層和模型提供基類。

from mxnet.gluon import Block, nn
class Model(Block):
   def __init__(self, **kwargs):
      super(Model, self).__init__(**kwargs)
      # use name_scope to give child Blocks appropriate names.
      with self.name_scope():
         self.dense0 = nn.Dense(20)
         self.dense1 = nn.Dense(20)
   def forward(self, x):

      x = mx.nd.relu(self.dense0(x))
      return mx.nd.relu(self.dense1(x))

model = Model()
model.initialize(ctx=mx.cpu(0))
model(mx.nd.zeros((5, 5), ctx=mx.cpu(0)))

輸出

您將看到以下輸出:

[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
<NDArray 5x20 @cpu(0)*gt;

在下面的示例中,我們將使用 HybridBlock(),它支援使用 Symbol 和 NDArray 進行前向傳播。

import mxnet as mx
from mxnet.gluon import HybridBlock, nn


class Model(HybridBlock):
   def __init__(self, **kwargs):
      super(Model, self).__init__(**kwargs)
      # use name_scope to give child Blocks appropriate names.
      with self.name_scope():
         self.dense0 = nn.Dense(20)
         self.dense1 = nn.Dense(20)

   def forward(self, x):
      x = nd.relu(self.dense0(x))
      return nd.relu(self.dense1(x))
model = Model()
model.initialize(ctx=mx.cpu(0))

model.hybridize()
model(mx.nd.zeros((5, 5), ctx=mx.cpu(0)))

輸出

輸出如下所示:

[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
<NDArray 5x20 @cpu(0)>

gluon.rnn

Gluon 在 gluon.rnn 模組中提供了大量的內建迴圈神經網路 (RNN) 層。這就是它被稱為核心模組的原因。

方法及其引數

以下是mxnet.gluon.nn核心模組涵蓋的一些重要方法及其引數:

方法及其引數 定義
BidirectionalCell(l_cell, r_cell[, …]) 它用於雙向迴圈神經網路 (RNN) 單元。
DropoutCell(rate[, axes, prefix, params]) 此方法將對給定輸入應用 dropout。
GRU(hidden_size[, num_layers, layout, …]) 它將多層門控迴圈單元 (GRU) RNN 應用於給定的輸入序列。
GRUCell(hidden_size[, …]) 它用於門控整流單元 (GRU) 網路單元。
HybridRecurrentCell([prefix, params]) 此方法支援混合。
HybridSequentialRNNCell([prefix, params]) 藉助此方法,我們可以順序堆疊多個 HybridRNN 單元。
LSTM(hidden_size[, num_layers, layout, …])0 它將多層長短期記憶 (LSTM) RNN 應用於給定的輸入序列。
LSTMCell(hidden_size[, …]) 它用於長短期記憶 (LSTM) 網路單元。
ModifierCell(base_cell) 它是修飾符單元的基類。
RNN(hidden_size[, num_layers, activation, …]) 它將具有tanhReLU非線性的多層 Elman RNN 應用於給定的輸入序列。
RNNCell(hidden_size[, activation, …]) 它用於 Elman RNN 迴圈神經網路單元。
RecurrentCell([prefix, params]) 它表示 RNN 單元的抽象基類。
SequentialRNNCell([prefix, params]) 藉助此方法,我們可以順序堆疊多個 RNN 單元。
ZoneoutCell(base_cell[, zoneout_outputs, …]) 此方法將 Zoneout 應用於基單元。

實現示例

在下面的示例中,我們將使用 GRU(),它將多層門控迴圈單元 (GRU) RNN 應用於給定的輸入序列。

layer = mx.gluon.rnn.GRU(100, 3)
layer.initialize()
input_seq = mx.nd.random.uniform(shape=(5, 3, 10))
out_seq = layer(input_seq)
h0 = mx.nd.random.uniform(shape=(3, 3, 100))
out_seq, hn = layer(input_seq, h0)
out_seq

輸出

這將產生以下輸出:

[[[ 1.50152072e-01 5.19012511e-01 1.02390535e-01 ... 4.35803324e-01
1.30406499e-01 3.30152437e-02]
[ 2.91542172e-01 1.02243155e-01 1.73325196e-01 ... 5.65296151e-02
1.76546033e-02 1.66693389e-01]
[ 2.22257316e-01 3.76294643e-01 2.11277917e-01 ... 2.28903517e-01
3.43954474e-01 1.52770668e-01]]


[[ 1.40634328e-01 2.93247789e-01 5.50393537e-02 ... 2.30207980e-01
6.61415309e-02 2.70989928e-02]
[ 1.11081995e-01 7.20834285e-02 1.08342394e-01 ... 2.28330195e-02
6.79589901e-03 1.25501186e-01]
[ 1.15944080e-01 2.41565228e-01 1.18612610e-01 ... 1.14908054e-01
1.61080107e-01 1.15969211e-01]]
………………………….

示例

hn

輸出

這將產生以下輸出:

[[[-6.08105101e-02 3.86217088e-02   6.64453954e-03 8.18805695e-02
3.85607071e-02 -1.36945639e-02 7.45836645e-03 -5.46515081e-03
9.49622393e-02 6.39371723e-02 -6.37890724e-03 3.82240303e-02
9.11015049e-02 -2.01375950e-02 -7.29381144e-02 6.93765879e-02
2.71829776e-02 -6.64435029e-02 -8.45306814e-02 -1.03075653e-01
6.72040805e-02 -7.06537142e-02 -3.93818803e-02 5.16211614e-03
-4.79770005e-02 1.10734522e-01 1.56721435e-02 -6.93409378e-03
1.16915874e-01 -7.95962065e-02 -3.06530762e-02 8.42394680e-02
7.60370195e-02 2.17055440e-01 9.85361822e-03 1.16660878e-01
4.08297703e-02 1.24978097e-02 8.25245082e-02 2.28673983e-02
-7.88266212e-02 -8.04114193e-02 9.28791538e-02 -5.70827350e-03
-4.46166918e-02 -6.41122833e-02 1.80885363e-02 -2.37745279e-03
4.37298454e-02 1.28888980e-01 -3.07202265e-02 2.50503756e-02
4.00907174e-02 3.37077095e-03 -1.78839862e-02 8.90695080e-02
6.30150884e-02 1.11416787e-01 2.12221760e-02 -1.13236710e-01
5.39616570e-02 7.80710578e-02 -2.28817668e-02 1.92073174e-02
………………………….

下面的例子中,我們將使用 LSTM(),它將長短期記憶 (LSTM) RNN 應用於給定的輸入序列。

layer = mx.gluon.rnn.LSTM(100, 3)
layer.initialize()

input_seq = mx.nd.random.uniform(shape=(5, 3, 10))
out_seq = layer(input_seq)
h0 = mx.nd.random.uniform(shape=(3, 3, 100))
c0 = mx.nd.random.uniform(shape=(3, 3, 100))
out_seq, hn = layer(input_seq,[h0,c0])
out_seq

輸出

輸出如下所示:

[[[ 9.00025964e-02 3.96071747e-02 1.83841765e-01 ... 3.95872220e-02
1.25569820e-01 2.15555862e-01]
[ 1.55962542e-01 -3.10300849e-02 1.76772922e-01 ... 1.92474753e-01
2.30574399e-01 2.81707942e-02]
[ 7.83204585e-02 6.53361529e-03 1.27262697e-01 ... 9.97719541e-02
1.28254429e-01 7.55299702e-02]]
[[ 4.41036932e-02 1.35250352e-02 9.87644792e-02 ... 5.89378644e-03
5.23949116e-02 1.00922674e-01]
[ 8.59075040e-02 -1.67027581e-02 9.69351009e-02 ... 1.17763653e-01
9.71239135e-02 2.25218050e-02]
[ 4.34580036e-02 7.62207608e-04 6.37005866e-02 ... 6.14888743e-02
5.96345589e-02 4.72368896e-02]]
……………

示例

hn

輸出

執行程式碼後,您將看到以下輸出:

[
[[[ 2.21408084e-02 1.42750628e-02 9.53067932e-03 -1.22849066e-02
1.78788435e-02 5.99269159e-02 5.65306023e-02 6.42553642e-02
6.56616641e-03 9.80876666e-03 -1.15729487e-02 5.98640442e-02
-7.21173314e-03 -2.78371759e-02 -1.90690923e-02 2.21447181e-02
8.38765781e-03 -1.38521893e-02 -9.06938594e-03 1.21346042e-02
6.06449470e-02 -3.77471633e-02 5.65885007e-02 6.63008019e-02
-7.34188128e-03 6.46054149e-02 3.19911093e-02 4.11194898e-02
4.43960279e-02 4.92892228e-02 1.74766723e-02 3.40303481e-02
-5.23341820e-03 2.68163737e-02 -9.43402853e-03 -4.11836170e-02
1.55221792e-02 -5.05655073e-02 4.24557598e-03 -3.40388380e-02
……………………

訓練模組

Gluon 中的訓練模組如下:

gluon.loss

在 **mxnet.gluon.loss** 模組中,Gluon 提供了預定義的損失函式。基本上,它包含用於訓練神經網路的損失函式。這就是它被稱為訓練模組的原因。

方法及其引數

以下是 **mxnet.gluon.loss** 訓練模組涵蓋的一些重要方法及其引數

方法及其引數 定義
Loss(weight, batch_axis, **kwargs) 這是損失函式的基類。
L2Loss([weight, batch_axis]) 它計算 **標籤 (label)** 和 **預測 (prediction/pred)** 之間的均方誤差 (MSE)。
L1Loss([weight, batch_axis]) 它計算 **標籤 (label)** 和 **pred** 之間的平均絕對誤差 (MAE)。
SigmoidBinaryCrossEntropyLoss([…]) 此方法用於二元分類的交叉熵損失。
SigmoidBCELoss 此方法用於二元分類的交叉熵損失。
SoftmaxCrossEntropyLoss([axis, …]) 它計算 softmax 交叉熵損失 (CEL)。
SoftmaxCELoss 它也計算 softmax 交叉熵損失。
KLDivLoss([from_logits, axis, weight, …]) 它用於 Kullback-Leibler 散度損失。
CTCLoss([layout, label_layout, weight]) 它用於連線性時間分類損失 (TCL)。
HuberLoss([rho, weight, batch_axis]) 它計算平滑的 L1 損失。如果絕對誤差超過 rho,則平滑的 L1 損失等於 L1 損失;否則等於 L2 損失。
HingeLoss([margin, weight, batch_axis]) 此方法計算常用於 SVM 的 Hinge 損失函式。
SquaredHingeLoss([margin, weight, batch_axis]) 此方法計算用於 SVM 的軟間隔損失函式。
LogisticLoss([weight, batch_axis, label_format]) 此方法計算邏輯損失。
TripletLoss([margin, weight, batch_axis]) 此方法給定三個輸入張量和一個正邊距,計算三元組損失。
PoissonNLLLoss([weight, from_logits, …]) 該函式計算負對數似然損失。
CosineEmbeddingLoss([weight, batch_axis, margin]) 該函式計算向量之間的餘弦距離。
SDMLLoss([smoothing_parameter, weight, …]) 此方法給定兩個輸入張量和一個平滑權重 SDM 損失,計算批次平滑深度度量學習 (SDML) 損失。它透過使用小批次中的非配對樣本作為潛在的負樣本,學習配對樣本之間的相似性。

示例

眾所周知,**mxnet.gluon.loss.loss** 將計算標籤和預測 (pred) 之間的 MSE(均方誤差)。這是透過以下公式完成的

Mean Squared Error

gluon.parameter

**mxnet.gluon.parameter** 是一個容器,它儲存塊的引數,即權重。

方法及其引數

以下是 **mxnet.gluon.parameter** 訓練模組涵蓋的一些重要方法及其引數:

方法及其引數 定義
cast(dtype) 此方法將把此引數的資料和梯度轉換為新的資料型別。
data([ctx]) 此方法將返回此引數在一個上下文上的副本。
grad([ctx]) 此方法將返回此引數在一個上下文上的梯度緩衝區。
initialize([init, ctx, default_init, …]) 此方法將初始化引數和梯度陣列。
list_ctx() 此方法將返回此引數已在其上初始化的上下文列表。
list_data() 此方法將返回此引數在所有上下文上的副本。這將按照建立順序進行。
list_grad() 此方法將返回所有上下文上的梯度緩衝區。這將按照與 **values()** 相同的順序進行。
list_row_sparse_data(row_id) 此方法將返回“row_sparse”引數在所有上下文上的副本。這將按照建立順序進行。
reset_ctx(ctx) 此方法將引數重新分配到其他上下文。
row_sparse_data(row_id) 此方法將返回“row_sparse”引數與 row_id 相同上下文上的副本。
set_data(data) 此方法將設定此引數在所有上下文上的值。
var() 此方法將返回表示此引數的符號。
zero_grad() 此方法將把所有上下文上的梯度緩衝區設定為 0。

實現示例

在下面的例子中,我們將使用 initialize() 方法初始化引數和梯度陣列,如下所示:

weight = mx.gluon.Parameter('weight', shape=(2, 2))
weight.initialize(ctx=mx.cpu(0))
weight.data()

輸出

輸出如下所示:

[[-0.0256899 0.06511251]
[-0.00243821 -0.00123186]]
<NDArray 2x2 @cpu(0)>

示例

weight.grad()

輸出

輸出如下所示:

[[0. 0.]
[0. 0.]]
<NDArray 2x2 @cpu(0)>

示例

weight.initialize(ctx=[mx.gpu(0), mx.gpu(1)])
weight.data(mx.gpu(0))

輸出

您將看到以下輸出:

[[-0.00873779 -0.02834515]
 [ 0.05484822 -0.06206018]]
<NDArray 2x2 @gpu(0)>

示例

weight.data(mx.gpu(1))

輸出

執行上述程式碼後,您應該看到以下輸出:

[[-0.00873779 -0.02834515]
 [ 0.05484822 -0.06206018]]
<NDArray 2x2 @gpu(1)>

gluon.trainer

mxnet.gluon.trainer 將最佳化器應用於一組引數。它應該與 autograd 一起使用。

方法及其引數

以下是 **mxnet.gluon.trainer** 訓練模組涵蓋的一些重要方法及其引數:

方法及其引數 定義
allreduce_grads() 此方法將減少每個引數(權重)的不同上下文中的梯度。
load_states(fname) 顧名思義,此方法將載入訓練器狀態。
save_states(fname) 顧名思義,此方法將儲存訓練器狀態。
set_learning_rate(lr) 此方法將設定最佳化器的新學習率。
step(batch_size[, ignore_stale_grad]) 此方法將進行一步引數更新。它應該在 **autograd.backward()** 之後和 **record()** 作用域之外呼叫。
update(batch_size[, ignore_stale_grad]) 此方法也將進行一步引數更新。它應該在 **autograd.backward()** 之後和 **record()** 作用域之外呼叫,以及在 trainer.update() 之後。

資料模組

Gluon 的資料模組解釋如下:

gluon.data

Gluon 在 gluon.data 模組中提供了大量的內建資料集實用程式。這就是它被稱為資料模組的原因。

類及其引數

以下是 mxnet.gluon.data 核心模組涵蓋的一些重要方法及其引數。這些方法通常與資料集、取樣和 DataLoader 相關。

Dataset
方法及其引數 定義
ArrayDataset(*args) 此方法表示一個數據集,它組合了兩個或多個數據集狀物件。例如,資料集、列表、陣列等。
BatchSampler(sampler, batch_size[, last_batch]) 此方法包裝另一個 **Sampler**。包裝後,它返回樣本的小批次。
DataLoader(dataset[, batch_size, shuffle, …]) 類似於 BatchSampler,但此方法從資料集中載入資料。載入後,它返回資料的小批次。
這表示抽象資料集類。
FilterSampler(fn, dataset) 此方法表示來自資料集的樣本元素,對於這些元素,fn(函式)返回 **True**。
RandomSampler(length) 此方法表示從 [0, length) 中隨機抽取樣本元素,不放回。
RecordFileDataset(filename) 它表示一個包裝 RecordIO 檔案的資料集。檔案的副檔名為 **.rec**。
Sampler 這是取樣器的基類。
SequentialSampler(length[, start]) 它表示按順序從集合 [start, start+length) 中抽取樣本元素。
它表示按順序從集合 [start, start+length) 中抽取樣本元素。 這表示簡單的資料集包裝器,尤其適用於列表和陣列。

實現示例

在下面的例子中,我們將使用 **gluon.data.BatchSampler()** API,它包裝另一個取樣器。它返回樣本的小批次。

import mxnet as mx
from mxnet.gluon import data
sampler = mx.gluon.data.SequentialSampler(15)
batch_sampler = mx.gluon.data.BatchSampler(sampler, 4, 'keep')
list(batch_sampler)

輸出

輸出如下所示:

[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14]]

gluon.data.vision.datasets

Gluon 在 **gluon.data.vision.datasets** 模組中提供了大量預定義的視覺資料集函式。

類及其引數

MXNet 為我們提供了有用且重要的資料集,其類和引數如下所示:

類及其引數 定義
MNIST([root, train, transform]) 這是一個有用的資料集,它為我們提供了手寫數字。MNIST 資料集的網址是 http://yann.lecun.com/exdb/mnist
FashionMNIST([root, train, transform]) 此資料集包含 Zalando 的文章影像,其中包含時尚產品。它是原始 MNIST 資料集的直接替代品。您可以從 https://github.com/zalandoresearch/fashion-mnist 獲取此資料集
CIFAR10([root, train, transform]) 這是一個來自 https://www.cs.toronto.edu/~kriz/cifar.html 的影像分類資料集。在此資料集中,每個樣本都是形狀為 (32, 32, 3) 的影像。
CIFAR100([root, fine_label, train, transform]) 這是一個來自 https://www.cs.toronto.edu/~kriz/cifar.html 的 CIFAR100 影像分類資料集。它也包含每個樣本都是形狀為 (32, 32, 3) 的影像。
ImageRecordDataset (filename[, flag, transform]) 此資料集包裝了一個包含影像的 RecordIO 檔案。在此檔案中,每個樣本都是一個影像及其對應的標籤。
ImageFolderDataset (root[, flag, transform]) 這是一個用於載入儲存在資料夾結構中的影像檔案的資料集。
ImageListDataset ([root, imglist, flag]) 這是一個用於載入由條目列表指定的影像檔案的資料集。

示例

在下面的例子中,我們將展示 ImageListDataset() 的用法,它用於載入由條目列表指定的影像檔案:

# written to text file *.lst

0 0 root/cat/0001.jpg
1 0 root/cat/xxxa.jpg
2 0 root/cat/yyyb.jpg
3 1 root/dog/123.jpg
4 1 root/dog/023.jpg
5 1 root/dog/wwww.jpg

# A pure list, each item is a list [imagelabel: float or list of float, imgpath]

[[0, root/cat/0001.jpg]
[0, root/cat/xxxa.jpg]
[0, root/cat/yyyb.jpg]
[1, root/dog/123.jpg]
[1, root/dog/023.jpg]
[1, root/dog/wwww.jpg]]

實用程式模組

Gluon 中的實用程式模組如下:

gluon.utils

Gluon 在 gluon.utils 模組中提供了大量的內建並行化實用程式最佳化器。它提供了各種用於訓練的實用程式。這就是它被稱為實用程式模組的原因。

函式及其引數

以下是此名為 **gluon.utils** 的實用程式模組中包含的函式及其引數:

函式及其引數 定義
split_data(data, num_slice[, batch_axis, …]) 此函式通常用於資料並行化,每個切片都會發送到一個裝置,即 GPU。它沿 **batch_axis** 將 NDArray 分割成 **num_slice** 個切片。
split_and_load(data, ctx_list[, batch_axis, …]) 此函式沿 **batch_axis** 將 NDArray 分割成 **len(ctx_list)** 個切片。與上面的 split_data() 函式唯一的區別是,它還將每個切片載入到 **ctx_list** 中的一個上下文中。
clip_global_norm(arrays, max_norm[, …]) 此函式的作用是以這樣的方式重新縮放 NDArrays,使其 2 範數之和小於 **max_norm**。
check_sha1(filename, sha1_hash) 此函式將檢查檔案內容的 sha1 雜湊是否與預期雜湊匹配。
download(url[, path, overwrite, sha1_hash, …]) 顧名思義,此函式將下載給定的 URL。
replace_file(src, dst) 此函式將實現原子的 **os.replace**。這將在 Linux 和 OSX 上完成。

Python API Autograd 和初始化器

本章介紹 MXNet 中的 autograd 和初始化器 API。

mxnet.autograd

這是 MXNet 的用於 NDArray 的 autograd API。它具有以下類:

類:Function()

它用於 autograd 中的自定義微分。它可以寫成 **mxnet.autograd.Function**。如果由於某種原因,使用者不想使用預設鏈式法則計算的梯度,則他/她可以使用 mxnet.autograd 的 Function 類來自定義微分計算。它具有兩個方法,即 Forward() 和 Backward()。

讓我們透過以下幾點來了解這個類的運作機制:

  • 首先,我們需要在forward方法中定義我們的計算。

  • 然後,我們需要在backward方法中提供自定義的微分。

  • 現在,在梯度計算過程中,mxnet.autograd將使用使用者定義的backward函式,而不是使用者自定義的backward函式。我們也可以在forward和backward中將資料轉換為numpy陣列並轉換回來,以進行一些操作。

示例

在使用mxnet.autograd.function類之前,讓我們定義一個帶有backward和forward方法的穩定sigmoid函式,如下所示:

class sigmoid(mx.autograd.Function):
   def forward(self, x):
      y = 1 / (1 + mx.nd.exp(-x))
      self.save_for_backward(y)
      return y
   
   def backward(self, dy):
      y, = self.saved_tensors
      return dy * y * (1-y)

現在,function類可以按如下方式使用:

func = sigmoid()
x = mx.nd.random.uniform(shape=(10,))
x.attach_grad()
with mx.autograd.record():
m = func(x)
m.backward()
dx_grad = x.grad.asnumpy()
dx_grad

輸出

執行程式碼後,您將看到以下輸出:

array([0.21458015, 0.21291625, 0.23330082, 0.2361367 , 0.23086983,
0.24060014, 0.20326573, 0.21093895, 0.24968489, 0.24301809],
dtype=float32)

方法及其引數

以下是mxnet.autograd.function類的使用方法及其引數:

方法及其引數 定義
forward (heads[, head_grads, retain_graph, …]) 此方法用於前向計算。
backward(heads[, head_grads, retain_graph, …]) 此方法用於反向計算。它計算heads相對於先前標記的變數的梯度。此方法的輸入數量與forward的輸出數量相同。它也返回與forward輸入一樣多的NDArray。
get_symbol(x) 此方法用於檢索記錄的計算歷史作為**Symbol**。
grad(heads, variables[, head_grads, …]) 此方法計算heads相對於variables的梯度。計算完成後,梯度將作為新的NDArrays返回,而不是儲存到variable.grad中。
is_recording() 藉助此方法,我們可以獲取記錄和未記錄的狀態。
is_training() 藉助此方法,我們可以獲取訓練和預測的狀態。
mark_variables(variables, gradients[, grad_reqs]) 此方法將NDArrays標記為變數,以便為autograd計算梯度。此方法與變數中的函式.attach_grad()相同,唯一的區別是透過此呼叫我們可以將梯度設定為任何值。
pause([train_mode]) 此方法返回一個作用域上下文,用於在'with'語句中使用,用於不需要計算梯度的程式碼。
predict_mode() 此方法返回一個作用域上下文,用於在'with'語句中使用,其中前向傳遞行為設定為推理模式,而不會更改記錄狀態。
record([train_mode]) 它將返回一個**autograd**記錄作用域上下文,用於在'with'語句中使用,並捕獲需要計算梯度的程式碼。
set_recording(is_recording) 與is_recording()類似,藉助此方法,我們可以獲取記錄和未記錄的狀態。
set_training(is_training) 與is_training()類似,藉助此方法,我們可以將狀態設定為訓練或預測。
train_mode() 此方法將返回一個作用域上下文,用於在'with'語句中使用,其中前向傳遞行為設定為訓練模式,而不會更改記錄狀態。

實現示例

在下面的示例中,我們將使用mxnet.autograd.grad()方法來計算head相對於variables的梯度:

x = mx.nd.ones((2,))
x.attach_grad()
with mx.autograd.record():
z = mx.nd.elemwise_add(mx.nd.exp(x), x)
dx_grad = mx.autograd.grad(z, [x], create_graph=True)
dx_grad

輸出

輸出如下所示:

[
[3.7182817 3.7182817]
<NDArray 2 @cpu(0)>]

我們可以使用mxnet.autograd.predict_mode()方法返回一個作用域,用於在'with'語句中使用:

with mx.autograd.record():
y = model(x)
with mx.autograd.predict_mode():
y = sampling(y)
backward([y])

mxnet.initializer

這是MXNet的權重初始化器API。它具有以下類:

類及其引數

以下是**mxnet.autograd.function**類的使用方法及其引數

類及其引數 定義
Bilinear() 藉助此類,我們可以為上取樣層初始化權重。
Constant(value) 此類將權重初始化為給定值。該值可以是標量,也可以是與要設定的引數形狀匹配的NDArray。
FusedRNN(init, num_hidden, num_layers, mode) 顧名思義,此類初始化融合迴圈神經網路(RNN)層的引數。
InitDesc 它充當初始化模式的描述符。
Initializer(**kwargs) 這是初始化器的基類。
LSTMBias([forget_bias]) 此類將LSTMCell的所有偏差初始化為0.0,但忘記門的偏差設定為自定義值除外。
Load(param[, default_init, verbose]) 此類透過從檔案或字典載入資料來初始化變數。
MSRAPrelu([factor_type, slope]) 顧名思義,此類根據MSRA論文初始化權重。
Mixed(patterns, initializers) 它使用多個初始化器初始化引數。
Normal([sigma]) Normal()類使用從均值為零、標準差(SD)為**sigma**的正態分佈中取樣的隨機值初始化權重。
One() 它將引數的權重初始化為一。
Orthogonal([scale, rand_type]) 顧名思義,此類將權重初始化為正交矩陣。
Uniform([scale]) 它使用從給定範圍內均勻取樣的隨機值初始化權重。
Xavier([rnd_type, factor_type, magnitude]) 它實際上返回一個對權重執行“Xavier”初始化的初始化器。
Zero() 它將引數的權重初始化為零。

實現示例

在下面的示例中,我們將使用mxnet.init.Normal()類建立一個初始化器並檢索其引數:

init = mx.init.Normal(0.8)
init.dumps()

輸出

輸出如下所示:

'["normal", {"sigma": 0.8}]'

示例

init = mx.init.Xavier(factor_type="in", magnitude=2.45)
init.dumps()

輸出

輸出如下所示:

'["xavier", {"rnd_type": "uniform", "factor_type": "in", "magnitude": 2.45}]'

在下面的示例中,我們將使用mxnet.initializer.Mixed()類使用多個初始化器初始化引數:

init = mx.initializer.Mixed(['bias', '.*'], [mx.init.Zero(),
mx.init.Uniform(0.1)])
module.init_params(init)

for dictionary in module.get_params():
for key in dictionary:
print(key)
print(dictionary[key].asnumpy())

輸出

輸出如下所示:

fullyconnected1_weight
[[ 0.0097627 0.01856892 0.04303787]]
fullyconnected1_bias
[ 0.]

Apache MXNet - Python API Symbol

在本章中,我們將學習MXNet中一個稱為Symbol的介面。

Mxnet.ndarray

Apache MXNet的Symbol API是符號程式設計的介面。Symbol API具有以下功能:

  • 計算圖

  • 減少記憶體使用

  • 預先使用函式最佳化

下面的示例顯示瞭如何使用MXNet的Symbol API建立一個簡單的表示式:

使用來自常規Python列表的1-D和2-D“陣列”建立NDArray:

import mxnet as mx
# Two placeholders namely x and y will be created with mx.sym.variable
x = mx.sym.Variable('x')
y = mx.sym.Variable('y')
# The symbol here is constructed using the plus ‘+’ operator.
z = x + y

輸出

您將看到以下輸出:

<Symbol _plus0>

示例

(x, y, z)

輸出

輸出如下所示:

(<Symbol x>, <Symbol y>, <Symbol _plus0>)

現在讓我們詳細討論MXNet的ndarray API的類、函式和引數。

下表包含MXNet的Symbol API的類:

定義
Symbol(handle) 此類即symbol是Apache MXNet的符號圖。

函式及其引數

以下是mxnet.Symbol API涵蓋的一些重要函式及其引數:

函式及其引數 定義
Activation([data, act_type, out, name]) 它將啟用函式逐元素應用於輸入。它支援**relu、sigmoid、tanh、softrelu、softsign**啟用函式。
BatchNorm([data, gamma, beta, moving_mean, …]) 它用於批次歸一化。此函式透過均值和方差對資料批進行歸一化。它應用比例**gamma**和偏移**beta**。
BilinearSampler([data, grid, cudnn_off, …]) 此函式將雙線性取樣應用於輸入特徵圖。實際上,它是“空間變換網路”的關鍵。如果您熟悉OpenCV中的remap函式,則此函式的使用與之非常相似。唯一的區別在於它具有反向傳播。
BlockGrad([data, out, name]) 顧名思義,此函式停止梯度計算。它基本上阻止輸入的累積梯度在反向方向上流經此運算子。
cast([data, dtype, out, name]) 此函式將輸入的所有元素轉換為新型別。
此函式將輸入的所有元素轉換為新型別。 此函式顧名思義,返回一個給定形狀和型別的新的符號,填充為零。
ones(shape[, dtype]) 此函式顧名思義,返回一個給定形狀和型別的新的符號,填充為一。
full(shape, val[, dtype]) 此函式顧名思義,返回一個給定形狀和型別的新的陣列,填充為給定值**val**。
arange(start[, stop, step, repeat, …]) 它將在給定區間內返回均勻間隔的值。這些值是在半開區間[start, stop)內生成的,這意味著該區間包含**start**但不包含**stop**。
linspace(start, stop, num[, endpoint, name, …]) 它將在指定的區間內返回均勻間隔的數字。與函式arange()類似,這些值是在半開區間[start, stop)內生成的,這意味著該區間包含**start**但不包含**stop**。
histogram(a[, bins, range]) 顧名思義,此函式將計算輸入資料的直方圖。
power(base, exp) 顧名思義,此函式將返回**base**元素按**exp**元素的冪進行逐元素計算的結果。兩個輸入,即base和exp,都可以是Symbol或標量。這裡需要注意的是不允許廣播。如果您想使用廣播功能,可以使用**broadcast_pow**。
SoftmaxActivation([data, mode, name, attr, out]) 此函式將softmax啟用應用於輸入。它用於內部層。它實際上已棄用,我們可以使用**softmax()**代替。

實現示例

在下面的示例中,我們將使用函式**power()**,它將返回base元素按exp元素的冪進行逐元素計算的結果

import mxnet as mx
mx.sym.power(3, 5)

輸出

您將看到以下輸出:

243

示例

x = mx.sym.Variable('x')
y = mx.sym.Variable('y')
z = mx.sym.power(x, 3)
z.eval(x=mx.nd.array([1,2]))[0].asnumpy()

輸出

這將產生以下輸出:

array([1., 8.], dtype=float32)

示例

z = mx.sym.power(4, y)
z.eval(y=mx.nd.array([2,3]))[0].asnumpy()

輸出

執行上述程式碼後,您應該看到以下輸出:

array([16., 64.], dtype=float32)

示例

z = mx.sym.power(x, y)
z.eval(x=mx.nd.array([4,5]), y=mx.nd.array([2,3]))[0].asnumpy()

輸出

輸出如下所示:

array([ 16., 125.], dtype=float32)

在下面的示例中,我們將使用函式**SoftmaxActivation()(或softmax())**,它將應用於輸入,並用於內部層。

input_data = mx.nd.array([[2., 0.9, -0.5, 4., 8.], [4., -.7, 9., 2., 0.9]])
soft_max_act = mx.nd.softmax(input_data)
print (soft_max_act.asnumpy())

輸出

您將看到以下輸出:

[[2.4258138e-03 8.0748333e-04 1.9912292e-04 1.7924475e-02 9.7864312e-01]
[6.6843745e-03 6.0796250e-05 9.9204916e-01 9.0463174e-04 3.0112563e-04]]

symbol.contrib

Contrib NDArray API在symbol.contrib包中定義。它通常為新功能提供許多有用的實驗性API。此API作為社群嘗試新功能的地方。功能貢獻者也將獲得反饋。

函式及其引數

以下是**mxnet.symbol.contrib API**涵蓋的一些重要函式及其引數:

函式及其引數 定義
rand_zipfian(true_classes, num_sampled, …) 此函式從近似齊夫分佈中抽取隨機樣本。此函式的基本分佈是齊夫分佈。此函式隨機抽取num_sampled個候選樣本,並且sampled_candidates的元素是從上面給出的基本分佈中抽取的。
foreach(body, data, init_states) 顧名思義,此函式在維度0上對NDArrays執行一個具有使用者定義計算的迴圈。此函式模擬for迴圈,body具有for迴圈迭代的計算。
while_loop(cond, func, loop_vars[, …]) 顧名思義,此函式執行帶有使用者定義計算和迴圈條件的while迴圈。此函式模擬一個while迴圈,如果條件滿足,則逐字進行自定義計算。
cond(pred, then_func, else_func) 顧名思義,此函式使用使用者定義的條件和計算執行if-then-else。此函式模擬if型別的分支,根據指定的條件選擇執行兩個自定義計算之一。
getnnz([data, axis, out, name]) 此函式為我們提供稀疏張量的儲存值的個數。它還包括顯式零。它只支援CPU上的CSR矩陣。
requantize([data, min_range, max_range, …]) 此函式使用最小和最大閾值(在執行時計算或來自校準)將以int32和相應閾值量化的給定資料重新量化為int8。
index_copy([old_tensor, index_vector, …]) 此函式透過按索引向量中給出的順序選擇索引,將**new_tensor**的元素複製到**old_tensor**中。此運算子的輸出將是一個新張量,其中包含old_tensor的其餘元素和new_tensor的已複製元素。
interleaved_matmul_encdec_qk([queries, …]) 此運算子計算多頭注意力中查詢和鍵的投影之間的矩陣乘法,用作編碼器-解碼器。條件是輸入應該是查詢投影的張量,其佈局如下:(seq_length, batch_size, num_heads*, head_dim)。

實現示例

在下面的示例中,我們將使用rand_zipfian函式從近似齊夫分佈中抽取隨機樣本:

import mxnet as mx
true_cls = mx.sym.Variable('true_cls')
samples, exp_count_true, exp_count_sample = mx.sym.contrib.rand_zipfian(true_cls, 5, 6)
samples.eval(true_cls=mx.nd.array([3]))[0].asnumpy()

輸出

您將看到以下輸出:

array([4, 0, 2, 1, 5], dtype=int64)

示例

exp_count_true.eval(true_cls=mx.nd.array([3]))[0].asnumpy()

輸出

輸出如下所示:

array([0.57336551])

示例

exp_count_sample.eval(true_cls=mx.nd.array([3]))[0].asnumpy()

輸出

您將看到以下輸出:

array([1.78103594, 0.46847373, 1.04183923, 0.57336551, 1.04183923])

下面的例子中,我們將使用函式while_loop來執行使用者定義的計算和迴圈條件的while迴圈。

cond = lambda i, s: i <= 7
func = lambda i, s: ([i + s], [i + 1, s + i])
loop_vars = (mx.sym.var('i'), mx.sym.var('s'))
outputs, states = mx.sym.contrib.while_loop(cond, func, loop_vars, max_iterations=10)
print(outputs)

輸出

輸出如下所示

[<Symbol _while_loop0>]

示例

Print(States)

輸出

這將產生以下輸出:

[<Symbol _while_loop0>, <Symbol _while_loop0>]

下面的例子中,我們將使用函式index_copy將new_tensor的元素複製到old_tensor中。

import mxnet as mx
a = mx.nd.zeros((6,3))
b = mx.nd.array([[1,2,3],[4,5,6],[7,8,9]])
index = mx.nd.array([0,4,2])
mx.nd.contrib.index_copy(a, index, b)

輸出

執行上述程式碼後,您應該看到以下輸出:

[[1. 2. 3.]
[0. 0. 0.]
[7. 8. 9.]
[0. 0. 0.]
[4. 5. 6.]
[0. 0. 0.]]
<NDArray 6x3 @cpu(0)>

symbol.image

影像符號API定義在symbol.image包中。顧名思義,它通常用於影像及其特徵。

函式及其引數

以下是mxnet.symbol.image API涵蓋的一些重要函式及其引數:

函式及其引數 定義
adjust_lighting([data, alpha, out, name]) 顧名思義,此函式調整輸入的光照級別。它遵循AlexNet樣式。
crop([data, x, y, width, height, out, name]) 藉助此函式,我們可以將形狀為(H x W x C)或(N x H x W x C)的影像NDArray裁剪到使用者給定的尺寸。
normalize([data, mean, std, out, name]) 它將使用均值標準差(SD)對形狀為(C x H x W)或(N x C x H x W)的張量進行標準化。
random_crop([data, xrange, yrange, width, …]) 類似於crop(),它會隨機裁剪形狀為(H x W x C)或(N x H x W x C)的影像NDArray到使用者給定的尺寸。如果src小於size,它將對結果進行上取樣。
random_lighting([data, alpha_std, out, name]) 顧名思義,此函式會隨機新增PCA噪聲。它也遵循AlexNet樣式。
random_resized_crop([data, xrange, yrange, …]) 它也會將形狀為(H x W x C)或(N x H x W x C)的影像NDArray隨機裁剪到給定尺寸。如果src小於size,它將對結果進行上取樣。它也會隨機化面積和縱橫比。
resize([data, size, keep_ratio, interp, …]) 顧名思義,此函式將調整形狀為 (H x W x C) 或 (N x H x W x C) 的影像 NDArray 的大小到使用者指定的大小。
to_tensor([data, out, name]) 它將形狀為 (H x W x C) 或 (N x H x W x C) 且值在 [0, 255] 範圍內的影像 NDArray 轉換為形狀為 (C x H x W) 或 (N x C x H x W) 且值在 [0, 1] 範圍內的張量 NDArray。

實現示例

在下面的示例中,我們將使用 to_tensor 函式將形狀為 (H x W x C) 或 (N x H x W x C) 且值在 [0, 255] 範圍內的影像 NDArray 轉換為形狀為 (C x H x W) 或 (N x C x H x W) 且值在 [0, 1] 範圍內的張量 NDArray。

import numpy as np

img = mx.sym.random.uniform(0, 255, (4, 2, 3)).astype(dtype=np.uint8)

mx.sym.image.to_tensor(img)

輸出

輸出如下所示:

<Symbol to_tensor4>

示例

img = mx.sym.random.uniform(0, 255, (2, 4, 2, 3)).astype(dtype=np.uint8)

mx.sym.image.to_tensor(img)

輸出

輸出如下所示

<Symbol to_tensor5>

在下面的例子中,我們將使用函式normalize()來使用均值標準差(SD)對形狀為(C x H x W)或(N x C x H x W)的張量進行標準化。

img = mx.sym.random.uniform(0, 1, (3, 4, 2))

mx.sym.image.normalize(img, mean=(0, 1, 2), std=(3, 2, 1))

輸出

以下是程式碼的輸出:

<Symbol normalize0>

示例

img = mx.sym.random.uniform(0, 1, (2, 3, 4, 2))

mx.sym.image.normalize(img, mean=(0, 1, 2), std=(3, 2, 1))

輸出

輸出如下所示:

<Symbol normalize1>

symbol.random

隨機符號API定義在symbol.random包中。顧名思義,它是MXNet的隨機分佈生成器符號API。

函式及其引數

以下是mxnet.symbol.random API涵蓋的一些重要函式及其引數:

函式及其引數 定義
uniform([low, high, shape, dtype, ctx, out]) 它從均勻分佈中生成隨機樣本。
normal([loc, scale, shape, dtype, ctx, out]) 它從正態 (高斯) 分佈中生成隨機樣本。
randn(*shape, **kwargs) 它從正態 (高斯) 分佈中生成隨機樣本。
poisson([lam, shape, dtype, ctx, out]) 它從泊松分佈中生成隨機樣本。
exponential([scale, shape, dtype, ctx, out]) 它從指數分佈中生成樣本。
gamma([alpha, beta, shape, dtype, ctx, out]) 它從伽馬分佈中生成隨機樣本。
multinomial(data[, shape, get_prob, out, dtype]) 它從多個多項分佈中生成併發取樣。
negative_binomial([k, p, shape, dtype, ctx, out]) 它從負二項分佈中生成隨機樣本。
generalized_negative_binomial([mu, alpha, …]) 它從廣義負二項分佈中生成隨機樣本。
shuffle(data, **kwargs) 它隨機打亂元素。
randint(low, high[, shape, dtype, ctx, out]) 它從離散均勻分佈中生成隨機樣本。
exponential_like([data, lam, out, name]) 它根據輸入陣列形狀從指數分佈中生成隨機樣本。
gamma_like([data, alpha, beta, out, name]) 它根據輸入陣列形狀從伽馬分佈中生成隨機樣本。
generalized_negative_binomial_like([data, …]) 它根據輸入陣列形狀從廣義負二項分佈中生成隨機樣本。
negative_binomial_like([data, k, p, out, name]) 它根據輸入陣列形狀從負二項分佈中生成隨機樣本。
normal_like([data, loc, scale, out, name]) 它根據輸入陣列形狀從正態(高斯)分佈中生成隨機樣本。
poisson_like([data, lam, out, name]) 它根據輸入陣列形狀從泊松分佈中生成隨機樣本。
uniform_like([data, low, high, out, name]) 它根據輸入陣列形狀從均勻分佈中生成隨機樣本。

實現示例

在下面的例子中,我們將使用shuffle()函式隨機打亂元素。它將沿著第一個軸打亂陣列。

data = mx.nd.array([[0, 1, 2], [3, 4, 5], [6, 7, 8],[9,10,11]])
x = mx.sym.Variable('x')
y = mx.sym.random.shuffle(x)
y.eval(x=data)

輸出

您將看到以下輸出

[
[[ 9. 10. 11.]
[ 0. 1. 2.]
[ 6. 7. 8.]
[ 3. 4. 5.]]
<NDArray 4x3 @cpu(0)>]

示例

y.eval(x=data)

輸出

執行上述程式碼後,您應該看到以下輸出:

[
[[ 6. 7. 8.]
[ 0. 1. 2.]
[ 3. 4. 5.]
[ 9. 10. 11.]]
<NDArray 4x3 @cpu(0)>]

在下面的例子中,我們將從廣義負二項分佈中抽取隨機樣本。為此,我們將使用函式generalized_negative_binomial()

mx.sym.random.generalized_negative_binomial(10, 0.1)

輸出

輸出如下所示:

<Symbol _random_generalized_negative_binomial0>

symbol.sparse

稀疏符號API定義在mxnet.symbol.sparse包中。顧名思義,它提供稀疏神經網路圖和CPU上的自動微分。

函式及其引數

以下是mxnet.symbol.sparse API涵蓋的一些重要函式(包括符號建立例程、符號操作例程、數學函式、三角函式、雙曲函式、約簡函式、舍入、冪運算、神經網路)及其引數:

函式及其引數 定義
ElementWiseSum(*args, **kwargs) 此函式將逐元素相加所有輸入引數。例如,𝑎𝑑𝑑_𝑛(𝑎1,𝑎2,…𝑎𝑛=𝑎1+𝑎2+⋯+𝑎𝑛)。在這裡,我們可以看到add_n可能比呼叫n次add更高效。
Embedding([data, weight, input_dim, …]) 它將整數索引對映到向量表示,即嵌入。它實際上是將單詞對映到高維空間中的實值向量,這稱為詞嵌入。
LinearRegressionOutput([data, label, …]) 它在反向傳播期間計算並最佳化平方損失,在正向傳播期間只給出輸出資料。
LogisticRegressionOutput([data, label, …]) 應用邏輯函式,也稱為 sigmoid 函式,到輸入。該函式計算為 1/1+exp (−x)。
MAERegressionOutput([data, label, …]) 此運算子計算輸入的平均絕對誤差。MAE實際上是對應於絕對誤差期望值的風險度量。
abs([data, name, attr, out]) 顧名思義,此函式將返回輸入的逐元素絕對值。
adagrad_update([weight, grad, history, lr, …]) 它是AdaGrad最佳化器的更新函式。
adam_update([weight, grad, mean, var, lr, …]) 它是Adam最佳化器的更新函式。
add_n(*args, **kwargs) 顧名思義,它將逐元素相加所有輸入引數。
arccos([data, name, attr, out]) 此函式將返回輸入陣列的逐元素反餘弦。
dot([lhs, rhs, transpose_a, transpose_b, …]) 顧名思義,它將給出兩個陣列的點積。這取決於輸入陣列的維度:一維:向量的內積;二維:矩陣乘法;N維:第一個輸入的最後一個軸和第二個輸入的第一個軸上的求和積。
elemwise_add([lhs, rhs, name, attr, out]) 顧名思義,它將逐元素相加引數。
elemwise_div([lhs, rhs, name, attr, out]) 顧名思義,它將逐元素相除引數。
elemwise_mul([lhs, rhs, name, attr, out]) 顧名思義,它將逐元素相乘引數。
elemwise_sub([lhs, rhs, name, attr, out]) 顧名思義,它將逐元素相減引數。
exp([data, name, attr, out]) 此函式將返回給定輸入的逐元素指數值。
sgd_update([weight, grad, lr, wd, …]) 它充當隨機梯度下降最佳化器的更新函式。
sigmoid([data, name, attr, out]) 顧名思義,它將逐元素計算x的sigmoid值。
sign([data, name, attr, out]) 它將返回給定輸入的逐元素符號。
sin([data, name, attr, out]) 顧名思義,此函式將計算給定輸入陣列的逐元素正弦值。

實現示例

在下面的例子中,我們將使用ElementWiseSum()函式隨機打亂元素。它將整數索引對映到向量表示,即詞嵌入。

input_dim = 4
output_dim = 5

示例

/* Here every row in weight matrix y represents a word. So, y = (w0,w1,w2,w3)
y = [[ 0., 1., 2., 3., 4.],
[ 5., 6., 7., 8., 9.],
[ 10., 11., 12., 13., 14.],
[ 15., 16., 17., 18., 19.]]
/* Here input array x represents n-grams(2-gram). So, x = [(w1,w3), (w0,w2)]
x = [[ 1., 3.],
[ 0., 2.]]
/* Now, Mapped input x to its vector representation y.
Embedding(x, y, 4, 5) = [[[ 5., 6., 7., 8., 9.],
[ 15., 16., 17., 18., 19.]],
[[ 0., 1., 2., 3., 4.],
[ 10., 11., 12., 13., 14.]]]

Apache MXNet - Python API Module

Apache MXNet的模組API類似於前饋模型,它更容易組合,類似於Torch模組。它包含以下類:

BaseModule([logger])

它表示模組的基類。模組可以被認為是計算元件或計算機器。模組的任務是執行前向和反向傳遞。它還會更新模型中的引數。

方法

下表顯示了BaseModule類包含的方法:

此方法將從所有裝置獲取狀態
方法 定義
backward([out_grads]) 顧名思義,此方法實現了反向計算。
bind(data_shapes[, label_shapes, …]) 它繫結符號以構建執行器,在可以使用模組進行計算之前這是必要的。
fit(train_data[, eval_data, eval_metric, …]) 此方法訓練模組引數。
forward(data_batch[, is_train]) 顧名思義,此方法實現了正向計算。此方法支援具有各種形狀的資料批次,例如不同的批次大小或不同的影像大小。
forward_backward(data_batch) 這是一個方便的函式,顧名思義,它同時呼叫正向和反向。
get_input_grads([merge_multi_context]) 此方法將獲取輸入的梯度,這些梯度是在之前的反向計算中計算的。
get_outputs([merge_multi_context]) 顧名思義,此方法將獲取先前正向計算的輸出。
get_params() 它獲取引數,特別是那些可能是用於在裝置上進行計算的實際引數副本的引數。
get_states([merge_multi_context])
init_optimizer([kvstore, optimizer, …]) 此方法安裝並初始化最佳化器。它還為分散式訓練初始化kvstore
init_params([initializer, arg_params, …]) 顧名思義,此方法將初始化引數和輔助狀態。
install_monitor(mon) 此方法將在所有執行器上安裝監控器。
iter_predict(eval_data[, num_batch, reset, …]) 此方法將迭代預測。
load_params(fname) 顧名思義,它將從檔案中載入模型引數。
predict(eval_data[, num_batch, …]) 它將執行預測並收集輸出。
prepare(data_batch[, sparse_row_id_fn]) 該運算子準備模組以處理給定的資料批次。
save_params(fname) 顧名思義,此函式將模型引數儲存到檔案中。
score(eval_data, eval_metric[, num_batch, …]) 它在eval_data上執行預測,並根據給定的eval_metric評估效能。
set_params(arg_params, aux_params[, …]) 此方法將分配引數和輔助狀態值。
set_states([states, value]) 顧名思義,此方法設定狀態的值。
update() 此方法根據已安裝的最佳化器更新給定的引數。它還更新在上一個前向-反向批次中計算的梯度。
update_metric(eval_metric, labels[, pre_sliced]) 顧名思義,此方法評估並累積上一個正向計算輸出的評估指標。
backward([out_grads]) 顧名思義,此方法實現了反向計算。
bind(data_shapes[, label_shapes, …]) 它設定桶併為預設桶鍵繫結執行器。此方法表示BucketingModule的繫結。
forward(data_batch[, is_train]) 顧名思義,此方法實現了正向計算。此方法支援具有各種形狀的資料批次,例如不同的批次大小或不同的影像大小。
get_input_grads([merge_multi_context]) 此方法將獲取輸入的梯度,這些梯度是在之前的反向計算中計算的。
get_outputs([merge_multi_context]) 顧名思義,此方法將獲取先前正向計算的輸出。
get_params() 它獲取當前引數,特別是那些可能是用於在裝置上進行計算的實際引數副本的引數。
get_states([merge_multi_context]) 此方法將從所有裝置獲取狀態。
init_optimizer([kvstore, optimizer, …]) 此方法安裝並初始化最佳化器。它還為分散式訓練初始化kvstore
init_params([initializer, arg_params, …]) 顧名思義,此方法將初始化引數和輔助狀態。
install_monitor(mon) 此方法將在所有執行器上安裝監控器。
load(prefix, epoch[, sym_gen, …]) 此方法將根據先前儲存的檢查點建立一個模型。
load_dict([sym_dict, sym_gen, …]) 此方法將根據將bucket_key對映到符號的字典 (dict) 建立模型。它還共享arg_paramsaux_params
prepare(data_batch[, sparse_row_id_fn]) 該運算子準備模組以處理給定的資料批次。
save_checkpoint(prefix, epoch[, remove_amp_cast]) 顧名思義,此方法將BucketingModule中所有bucket的當前進度儲存到檢查點。建議使用mx.callback.module_checkpoint作為epoch_end_callback在訓練期間儲存。
set_params(arg_params, aux_params[,…]) 顧名思義,此函式將分配引數和輔助狀態值。
set_states([states, value]) 顧名思義,此方法設定狀態的值。
switch_bucket(bucket_key, data_shapes[, …]) 它將切換到不同的bucket。
update() 此方法根據已安裝的最佳化器更新給定的引數。它還更新在上一個前向-反向批次中計算的梯度。
update_metric(eval_metric, labels[, pre_sliced]) 顧名思義,此方法評估並累積上一個正向計算輸出的評估指標。

屬性

下表顯示了BaseModule類的方法中包含的屬性:

屬性 定義
data_names 它包含此模組所需資料的名稱列表。
data_shapes 它包含指定此模組的資料輸入的(名稱,形狀)對列表。
label_shapes 它顯示指定此模組的標籤輸入的(名稱,形狀)對列表。
output_names 它包含此模組輸出的名稱列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。
symbol 顧名思義,此屬性獲取與此模組關聯的符號。

data_shapes:您可以參考以下連結瞭解更多詳情:https://mxnet.apache.org output_shapes:更多資訊

output_shapes:更多資訊請訪問 https://mxnet.apache.org/api/python

BucketingModule(sym_gen[…])

它表示Module的Bucketingmodule類,該類有助於有效處理長度可變的輸入。

方法

下表顯示了BucketingModule類中包含的方法:

屬性

下表顯示了BaseModule類的方法中包含的屬性:

屬性 定義
data_names 它包含此模組所需資料的名稱列表。
data_shapes 它包含指定此模組的資料輸入的(名稱,形狀)對列表。
label_shapes 它顯示指定此模組的標籤輸入的(名稱,形狀)對列表。
output_names 它包含此模組輸出的名稱列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。
Symbol 顧名思義,此屬性獲取與此模組關聯的符號。

data_shapes − 更多資訊請參考以下連結:https://mxnet.apache.org/api/python/docs

output_shapes− 更多資訊請參考以下連結:https://mxnet.apache.org/api/python/docs

Module(symbol[,data_names, label_names,…])

它表示包裝symbol的基本模組。

方法

下表顯示了Module類中包含的方法:

方法 定義
backward([out_grads]) 顧名思義,此方法實現了反向計算。
bind(data_shapes[, label_shapes, …]) 它繫結符號以構建執行器,在可以使用模組進行計算之前這是必要的。
borrow_optimizer(shared_module) 顧名思義,此方法將從共享模組借用最佳化器。
forward(data_batch[, is_train]) 顧名思義,此方法實現了前向計算。此方法支援具有各種形狀的資料批次,例如不同的批次大小或不同的影像大小。
get_input_grads([merge_multi_context]) 此方法將獲取輸入的梯度,這些梯度是在之前的反向計算中計算的。
get_outputs([merge_multi_context]) 顧名思義,此方法將獲取先前正向計算的輸出。
get_params() 它獲取引數,特別是那些可能是用於在裝置上進行計算的實際引數副本的引數。
get_states([merge_multi_context]) 此方法將從所有裝置獲取狀態
init_optimizer([kvstore, optimizer, …]) 此方法安裝並初始化最佳化器。它還為分散式訓練初始化kvstore
init_params([initializer, arg_params, …]) 顧名思義,此方法將初始化引數和輔助狀態。
install_monitor(mon) 此方法將在所有執行器上安裝監控器。
load(prefix, epoch[, sym_gen, …]) 此方法將根據先前儲存的檢查點建立一個模型。
load_optimizer_states(fname) 此方法將載入最佳化器,即從檔案載入更新器狀態。
prepare(data_batch[, sparse_row_id_fn]) 該運算子準備模組以處理給定的資料批次。
reshape(data_shapes[, label_shapes]) 顧名思義,此方法將為新的輸入形狀重新調整模組。
save_checkpoint(prefix, epoch[, …]) 它將當前進度儲存到檢查點。
save_optimizer_states(fname) 此方法將最佳化器或更新器狀態儲存到檔案。
set_params(arg_params, aux_params[,…]) 顧名思義,此函式將分配引數和輔助狀態值。
set_states([states, value]) 顧名思義,此方法設定狀態的值。
update() 此方法根據已安裝的最佳化器更新給定的引數。它還更新在上一個前向-反向批次中計算的梯度。
update_metric(eval_metric, labels[, pre_sliced]) 顧名思義,此方法評估並累積上一個正向計算輸出的評估指標。

屬性

下表顯示了Module類的方法中包含的屬性:

屬性 定義
data_names 它包含此模組所需資料的名稱列表。
data_shapes 它包含指定此模組的資料輸入的(名稱,形狀)對列表。
label_shapes 它顯示指定此模組的標籤輸入的(名稱,形狀)對列表。
output_names 它包含此模組輸出的名稱列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。
label_names 它包含此模組所需標籤的名稱列表。

data_shapes: 訪問以下連結瞭解更多詳情:https://mxnet.apache.org/api/python/docs/api/module

output_shapes: 此處提供的連結 https://mxnet.apache.org/api/python/docs/api/module/index.html 將提供其他重要資訊。

PythonLossModule([name,data_names,…])

此類的基類是mxnet.module.python_module.PythonModule。PythonLossModule類是一個方便的模組類,它將所有或許多模組API實現為空函式。

方法

下表顯示了PythonLossModule類中包含的方法

方法 定義
backward([out_grads]) 顧名思義,此方法實現了反向計算。
forward(data_batch[, is_train]) 顧名思義,此方法實現了前向計算。此方法支援具有各種形狀的資料批次,例如不同的批次大小或不同的影像大小。
get_input_grads([merge_multi_context]) 此方法將獲取輸入的梯度,這些梯度是在之前的反向計算中計算的。
get_outputs([merge_multi_context]) 顧名思義,此方法將獲取先前正向計算的輸出。
install_monitor(mon) 此方法將在所有執行器上安裝監控器。

PythonModule([data_names,label_names…])

此類的基類是mxnet.module.base_module.BaseModule。PythonModule類也是一個方便的模組類,它將所有或許多模組API實現為空函式。

方法

下表顯示了PythonModule類中包含的方法:

方法 定義
bind(data_shapes[, label_shapes, …]) 它繫結符號以構建執行器,在可以使用模組進行計算之前這是必要的。
get_params() 它獲取引數,特別是那些可能是用於在裝置上進行計算的實際引數副本的引數。
init_optimizer([kvstore, optimizer, …]) 此方法安裝並初始化最佳化器。它還為分散式訓練初始化kvstore
init_params([initializer, arg_params, …]) 顧名思義,此方法將初始化引數和輔助狀態。
update() 此方法根據已安裝的最佳化器更新給定的引數。它還更新在上一個前向-反向批次中計算的梯度。
update_metric(eval_metric, labels[, pre_sliced]) 顧名思義,此方法評估並累積上一個正向計算輸出的評估指標。

屬性

下表顯示了PythonModule類的方法中包含的屬性:

屬性 定義
data_names 它包含此模組所需資料的名稱列表。
data_shapes 它包含指定此模組的資料輸入的(名稱,形狀)對列表。
label_shapes 它顯示指定此模組的標籤輸入的(名稱,形狀)對列表。
output_names 它包含此模組輸出的名稱列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。

data_shapes − 更多詳情請訪問以下連結:https://mxnet.apache.org

output_shapes − 更多詳情請訪問以下連結:https://mxnet.apache.org

SequentialModule([logger])

此類的基類是mxnet.module.base_module.BaseModule。SequentialModule類也是一個容器模組,可以將兩個以上(多個)模組連結在一起。

方法

下表顯示了SequentialModule類中包含的方法

方法 定義
add(module, **kwargs) 這是此類最重要的函式。它將模組新增到鏈中。
backward([out_grads]) 顧名思義,此方法實現了反向計算。
bind(data_shapes[, label_shapes, …]) 它繫結符號以構建執行器,在可以使用模組進行計算之前這是必要的。
forward(data_batch[, is_train]) 顧名思義,此方法實現了正向計算。此方法支援具有各種形狀的資料批次,例如不同的批次大小或不同的影像大小。
get_input_grads([merge_multi_context]) 此方法將獲取輸入的梯度,這些梯度是在之前的反向計算中計算的。
get_outputs([merge_multi_context]) 顧名思義,此方法將獲取先前正向計算的輸出。
get_params() 它獲取引數,特別是那些可能是用於在裝置上進行計算的實際引數副本的引數。
init_optimizer([kvstore, optimizer, …]) 此方法安裝並初始化最佳化器。它還為分散式訓練初始化kvstore
init_params([initializer, arg_params, …]) 顧名思義,此方法將初始化引數和輔助狀態。
install_monitor(mon) 此方法將在所有執行器上安裝監控器。
update() 此方法根據已安裝的最佳化器更新給定的引數。它還更新在上一個前向-反向批次中計算的梯度。
update_metric(eval_metric, labels[, pre_sliced]) 顧名思義,此方法評估並累積上一個正向計算輸出的評估指標。

屬性

下表顯示了BaseModule類的方法中包含的屬性:

屬性 定義
data_names 它包含此模組所需資料的名稱列表。
data_shapes 它包含指定此模組的資料輸入的(名稱,形狀)對列表。
label_shapes 它顯示指定此模組的標籤輸入的(名稱,形狀)對列表。
output_names 它包含此模組輸出的名稱列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。
output_shapes 它包含指定此模組輸出的(名稱,形狀)對列表。

data_shapes − 此處提供的連結 https://mxnet.apache.org 將幫助您更詳細地瞭解該屬性。

output_shapes − 更多詳情請訪問以下連結:https://mxnet.apache.org/api

實現示例

在下面的示例中,我們將建立一個mxnet模組。

import mxnet as mx
input_data = mx.symbol.Variable('input_data')
f_connected1 = mx.symbol.FullyConnected(data, name='f_connected1', num_hidden=128)
activation_1 = mx.symbol.Activation(f_connected1, name='relu1', act_type="relu")
f_connected2 = mx.symbol.FullyConnected(activation_1, name = 'f_connected2', num_hidden = 64)
activation_2 = mx.symbol.Activation(f_connected2, name='relu2',
act_type="relu")
f_connected3 = mx.symbol.FullyConnected(activation_2, name='fc3', num_hidden=10)
out = mx.symbol.SoftmaxOutput(f_connected3, name = 'softmax')
mod = mx.mod.Module(out)
print(out)

輸出

輸出如下所示:

<Symbol softmax>

示例

print(mod)

輸出

輸出如下所示:

<mxnet.module.module.Module object at 0x00000123A9892F28>

在下面的示例中,我們將實現前向計算

import mxnet as mx
from collections import namedtuple
Batch = namedtuple('Batch', ['data'])
data = mx.sym.Variable('data')
out = data * 2
mod = mx.mod.Module(symbol=out, label_names=None)
mod.bind(data_shapes=[('data', (1, 10))])
mod.init_params()
data1 = [mx.nd.ones((1, 10))]
mod.forward(Batch(data1))
print (mod.get_outputs()[0].asnumpy())

輸出

執行上述程式碼後,您應該看到以下輸出:

[[2. 2. 2. 2. 2. 2. 2. 2. 2. 2.]]

示例

data2 = [mx.nd.ones((3, 5))]

mod.forward(Batch(data2))
print (mod.get_outputs()[0].asnumpy())

輸出

以下是程式碼的輸出:

[[2. 2. 2. 2. 2.]
[2. 2. 2. 2. 2.]
[2. 2. 2. 2. 2.]]
廣告
© . All rights reserved.