旷视MegEngine网络搭建
曠視MegEngine網絡搭建
在 基本概念 中,介紹了計算圖、張量和算子,神經網絡可以看成一個計算圖。在 MegEngine 中,按照計算圖的拓撲結構,將張量和算子連接起來,即可完成對網絡的搭建。MegEngine 提供了基于 functional 和基于 Module 的兩種方式搭建網絡。 functional 僅提供最基本的算子功能,數據連接的工作完全由用戶完成; Module 對網絡模塊(包含若干算子及其參數的基本單元)進行了進一步的封裝,代碼更易復用和維護。
基于 functional 搭建網絡
functional 包提供了常用的算子函數(如 conv2d() 、 batch_norm() 等)。這些函數接受參與計算的張量并返回計算結果。參與計算的張量通常包括兩類:輸入數據和算子自身的參數,其中后者是網路中需要學習的變量。比如,二維卷積( conv2d() )接受多通道的二維圖像作為輸入數據,把卷積核作為參數,輸出經卷積操作后的多通道二維圖像。
算子的輸入和輸出數據都是 Tensor 類型。算子的參數通常由 Parameter 類表示。 Parameter 是 Tensor 的子類,其對象(即網絡參數)可以被優化器更新。更多內容參見 網絡的訓練和測試 。
下面的例子實現了一個兩層卷積網絡(使用 ReLU 作為激活函數):
import megengine as mge
import megengine.functional as F
import numpy as np
def two_layer_conv(x):
# (8, 3, 3, 3) 代表(輸出信道數,輸入信道數,卷積核高度,卷積核寬度)
conv_weight = mge.Parameter(np.random.randn(8, 3, 3, 3).astype(np.float32))
# 對于 8 個卷積核,提供 8 個 bias
conv_bias = mge.Parameter(np.zeros((1, 8, 1, 1), dtype=np.float32))
x = F.conv2d(x, conv_weight, conv_bias)
x = F.relu(x)
conv_weight = mge.Parameter(np.random.randn(16, 8, 3, 3).astype(np.float32))
conv_bias = mge.Parameter(np.zeros((1, 16, 1, 1), dtype=np.float32))
x = F.conv2d(x, conv_weight, conv_bias)
x = F.relu(x)
return x
輸入形狀為 (2, 3, 32, 32) 的張量
x = mge.tensor(np.random.randn(2, 3, 32, 32).astype(np.float32))
out = two_layer_conv(x)
print(out.shape)
輸出:
(2, 16, 28, 28)
基于 Module 搭建網絡
在上面的代碼中,對于每一個需要參數的算子,需要單獨定義其網絡參數。由于“ conv + relu ”這樣的組合出現了兩次,代碼顯得臃腫。對于更加復雜的網絡,這樣的寫法可讀性、可復用性和可維護性會比較差。
為了更好的封裝和復用算子, MegEngine 在 functional 基礎上提供了 module 包。
megengine.module 包定義了抽象的網絡模塊基類 Module 。它是構造網絡的基本單元,可以被組合和疊加。它定義了網絡模塊的基本接口和屬性,如“前向傳播”等。所有 Module 子類都需要實現 Module 定義的兩個抽象方法,介紹如下:
? init() :在構造方法中創建這個模塊,包括定義網絡參數、構造和連接其子模塊等工作。
? forward() : 該方法定義前向傳播計算流程。接受輸入數據并返回前向傳播的計算結果。注意, Module 對象是可被調用的 ( callable ),其實現就是 forward() 。
megengine.module 包提供了常用的網絡基本模塊,如 Conv2d 、Linear 等。以 Conv2d 為例,該類的 init() 方法定義并初始化卷積核參數,其 forward() 方法執行卷積操作。
基于各種常用的網絡模塊,可以方便地搭建非常復雜的網絡。例如,上一個例子的網絡定義可以簡化成如下寫法:
import megengine.module as M
為了演示,在這里定義了一個簡單的卷積模塊。注意: MegEngine 已經提供了更為通用的 Conv2d 模塊。
class ConvReLU(M.Module):
def init(self, in_channels, out_channels):
# 先調用父類的初始化
super().init()
# 定義卷積權重和 bias ,作為模塊參數self.conv_weight = mge.Parameter(np.random.randn(out_channels, in_channels, 3, 3).astype(np.float32))self.conv_bias = mge.Parameter(np.zeros((1, out_channels, 1, 1), dtype=np.float32))# 將激活函數 ReLU 作為子模塊self.relu = M.ReLU()def forward(self, x):x = F.conv2d(x, self.conv_weight, self.conv_bias)x = self.relu(x)return x
基于 ConvReLU ,定義一個兩層卷積網絡
class TwoLayerConv(M.Module):
def init(self):
super().init()
self.conv_relu1 = ConvReLU(3, 8)
self.conv_relu2 = ConvReLU(8, 16)
def forward(self, x):x = self.conv_relu1(x)x = self.conv_relu2(x)return x
輸入形狀為 (2, 3, 32, 32) 的張量
x = mge.tensor(np.random.randn(2, 3, 32, 32).astype(np.float32))
two_layer_conv_module = TwoLayerConv()
out = two_layer_conv_module(x)
print(out.shape)
輸出:
(2, 16, 28, 28)
使用 Module 定義的網絡比使用 functional 進一步封裝了內部實現,更易復用,統一的接口使得代碼更易維護。推薦使用 Module 搭建網絡。
此外, Module 其它常用的方法如下:
? parameters() : 該方法返回包含網絡參數的迭代器。
? named_parameters() : 該方法返回包含參數名稱及對應網絡參數的迭代器。
? state_dict():返回以參數名稱和網絡參數為鍵值對的有序字典,可用于保存訓練好的模型。比如,對于上面定義的 ConvReLU 模塊,打印它的一個實例的 state_dict :
conv_relu = ConvReLU(2, 3)
print(conv_relu.state_dict())
輸出的參數信息有卷積的權重項 ‘conv_weight’ 和偏置項 ‘conv_bias’ :
OrderedDict([(‘conv_bias’, array([[[[0.]],
[[0.]],[[0.]]]], dtype=float32)), ('conv_weight', array([[[[-0.53457755, 0.2799128 , -0.6624546 ],[-0.9222688 , 1.2226251 , -0.5591961 ],[-0.45538583, -0.95166504, 1.1570141 ]],[[-0.89926094, 0.09956062, -0.7329557 ],[-0.67284465, 0.34817234, 0.6731445 ],[ 0.61970276, 1.8007269 , 1.6130987 ]]],[[[ 1.7108068 , -1.7188625 , -0.52539474],[-0.04049037, 0.03099988, -1.4271212 ],[-0.9138133 , 0.3976046 , -1.1582668 ]],[[-1.2193677 , 0.24107741, -0.50833786],[ 0.9088649 , -0.2747458 , -0.1261102 ],[ 0.00594431, 0.65737075, 1.5280651 ]]],[[[ 0.24874896, -1.3824748 , 2.2161844 ],[-0.6629168 , 1.0220655 , -0.53007567],[ 0.37829646, 1.1993718 , 1.0667052 ]],[[-0.66264534, -0.6392335 , -0.41280702],[ 1.7417566 , 0.75295806, -0.4228349 ],[-0.94973356, 2.4136777 , -0.06665667]]]], dtype=float32))])
最后,來搭建更加復雜的、經典的 LeNet 網絡,其結構如下圖:
圖1 LeNet ( http://yann.lecun.com/exdb/publis/pdf/lecun-01a.pdf )
使用 Module 搭建 LeNet 的代碼如下:
class LeNet(M.Module):
def init(self):
super(LeNet, self).init()
# 單信道圖片, 兩層 5x5 卷積 + ReLU + 池化
self.conv1 = M.Conv2d(1, 6, 5)
self.relu1 = M.ReLU()
self.pool1 = M.MaxPool2d(2, 2)
self.conv2 = M.Conv2d(6, 16, 5)
self.relu2 = M.ReLU()
self.pool2 = M.MaxPool2d(2, 2)
# 兩層全連接 + ReLU
self.fc1 = M.Linear(16 * 5 * 5, 120)
self.relu3 = M.ReLU()
self.fc2 = M.Linear(120, 84)
self.relu4 = M.ReLU()
# 分類器
self.classifier = M.Linear(84, 10)
def forward(self, x):x = self.pool1(self.relu1(self.conv1(x)))x = self.pool2(self.relu2(self.conv2(x)))# F.flatten 將原本形狀為 (N, C, H, W) 的張量x從第一個維度(即C)開始拉平成一個維度,# 得到的新張量形狀為 (N, C*H*W) 。 等價于 reshape 操作: x = x.reshape(x.shape[0], -1)x = F.flatten(x, 1)x = self.relu3(self.fc1(x))x = self.relu4(self.fc2(x))x = self.classifier(x)return x
輸入形狀為 (2, 1, 32, 32) 的張量
x = mge.tensor(np.random.randn(2, 1, 32, 32).astype(np.float32))
le_net = LeNet()
調用網絡,即執行 le_net 的 forward 成員方法,返回網絡處理結果
out = le_net(x)
print(out.shape)
輸出:
(2, 10)
總結
以上是生活随笔為你收集整理的旷视MegEngine网络搭建的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 旷视MegEngine基本概念
- 下一篇: 旷视MegEngine数据加载与处理