- Apache MXNet 教程
- Apache MXNet - 首頁
- Apache MXNet - 簡介
- Apache MXNet - 安裝 MXNet
- Apache MXNet - 工具包和生態系統
- Apache MXNet - 系統架構
- Apache MXNet - 系統元件
- Apache MXNet - 統一運算元 API
- Apache MXNet - 分散式訓練
- Apache MXNet - Python 包
- Apache MXNet - NDArray
- Apache MXNet - Gluon
- Apache MXNet - KVStore 和視覺化
- Apache MXNet - Python API ndarray
- Apache MXNet - Python API gluon
- Apache MXNet - Python API autograd 和初始化器
- Apache MXNet - Python API Symbol
- Apache MXNet - Python API Module
- Apache MXNet 有用資源
- Apache MXNet - 快速指南
- Apache MXNet - 有用資源
- Apache MXNet - 討論
Apache MXNet - Gluon
另一個最重要的 MXNet Python 包是 Gluon。在本章中,我們將討論這個包。Gluon 為 DL 專案提供了清晰、簡潔和簡單的 API。它使 Apache MXNet 能夠在不犧牲訓練速度的情況下對 DL 模型進行原型設計、構建和訓練。
塊
塊構成了更復雜網路設計的基礎。在神經網路中,隨著神經網路複雜性的增加,我們需要從設計單個神經元轉向設計整個神經元層。例如,像 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 - 最後,塊將呼叫反向函式並計算相對於其輸入的梯度。通常,此步驟會自動執行。
順序塊
順序塊是一種特殊的塊,其中資料流經一系列塊。在這裡,每個塊都應用於前一個塊的輸出,第一個塊應用於輸入資料本身。
讓我們看看順序類是如何工作的 -
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)>
自定義塊
我們可以輕鬆地超越上面定義的順序塊的串聯。但是,如果我們想進行自定義,那麼塊類也為我們提供了所需的功能。塊類有一個在 nn 模組中提供的模型建構函式。我們可以繼承該模型建構函式來定義我們想要的模型。
在下面的示例中,MLP 類覆蓋了塊類的__init__和前向函式。
讓我們看看它是如何工作的。
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 中建立新層,我們必須建立一個繼承自塊類的類,該類提供最基本的功能。我們可以直接或透過其他子類繼承所有預定義層。
為了建立新層,唯一需要實現的例項方法是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 透過最佳化計算符號圖來提高計算效能。實際上,我們可能會發現,在實現現有層時,塊繼承自混合塊,而不是直接繼承自塊。
以下是這樣做的原因 -
允許我們編寫自定義層:混合塊允許我們編寫自定義層,這些層可以進一步用於指令式程式設計和符號式程式設計。
提高計算效能- 混合塊最佳化計算符號圖,這使得 MXNet 能夠提高計算效能。
示例
在這個例子中,我們將使用混合塊重寫上面建立的示例層
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 上訓練混合網路和非混合網路。
塊和混合塊的區別
如果我們比較塊類和混合塊,我們會發現混合塊已經實現了它的forward()方法。混合塊定義了一個需要在建立層時實現的hybrid_forward()方法。F 引數是forward()和hybrid_forward()之間主要的區別。在 MXNet 社群中,F 引數被稱為後端。F 可以指mxnet.ndarray API(用於指令式程式設計)或mxnet.symbol API(用於符號式程式設計)。
如何將自定義層新增到網路?
這些層不是單獨使用,而是與預定義層一起使用。我們可以使用順序或混合順序容器來形成一個順序神經網路。如前所述,順序容器繼承自塊,而混合順序分別繼承自混合塊。
示例
在下面的示例中,我們將建立一個具有自定義層的基本神經網路。來自密集 (5)層的輸出將成為標準化混合層的輸入。標準化混合層的輸出將成為密集 (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)>
自定義層引數
在神經網路中,一個層有一組與其關聯的引數。我們有時將它們稱為權重,它是層的內部狀態。這些引數扮演不同的角色 -
有時這些是我們希望在反向傳播步驟中學習的引數。
有時這些只是我們在前向傳遞過程中想要使用的常量。
如果我們談論程式設計概念,塊的這些引數(權重)透過引數字典類儲存和訪問,這有助於它們的初始化、更新、儲存和載入。
示例
在下面的示例中,我們將定義以下兩組引數 -
引數權重- 這是可訓練的,並且其形狀在構造階段是未知的。它將在第一次執行前向傳播時推斷出來。
引數比例- 這是一個其值不會改變的常數。與引數權重相反,它的形狀在構造期間定義。
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