nn.moduleList 和Sequential由來、用法和例項 —— 寫網路模型
對於cnn前饋神經網路如果前饋一次寫一個forward函式會有些麻煩,在此就有兩種簡化方式,ModuleList和Sequential。其中Sequential是一個特殊的module,它包含幾個子Module,前向傳播時會將輸入一層接一層的傳遞下去。ModuleList也是一個特殊的module,可以包含幾個子module,可以像用list一樣使用它,但不能直接把輸入傳給ModuleList。下面舉例說明。
目錄
一、nn.Sequential()物件
建立nn.Sequential()物件,必須小心確保一個塊的輸出大小與下一個塊的輸入大小匹配。基本上,它的行為就像一個nn.Module。
1、模型建立方式
第一種寫法:
nn.Sequential()物件.add_module(層名,層class的例項)
1 2 3 4 |
|
第二種寫法:
nn.Sequential(*多個層class的例項)
1 2 3 4 5 |
|
第三種寫法:
nn.Sequential(OrderedDict([*多個(層名,層class的例項)]))
1 2 3 4 5 6 |
|
2、檢查以及呼叫模型
檢視模型
print物件即可
1 2 3 |
|
net1: Sequential( (conv): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)) (batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True) (activation_layer): ReLU() ) net2: Sequential( (0): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)) (1): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True) (2): ReLU() ) net3: Sequential( (conv): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)) (batchnorm): BatchNorm2d(3, eps=1e-05, momentum=0.1, affine=True) (activation_layer): ReLU() )
根據名字或序號
提取子Module物件
1 2 |
|
(Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)), Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)), Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)))
呼叫模型
可以直接網路物件(輸入資料),也可以使用上面的Module子物件分別傳入(input)。
1 2 3 4 5 |
|
二、nn.ModuleList()物件
為什麼有他?
寫一個module然後就寫foreword函式很麻煩,所以就有了這兩個。它被設計用來儲存任意數量的nn. module。
什麼時候用?
如果在建構函式__init__
中用到list、tuple、dict等物件時,一定要思考是否應該用ModuleList或ParameterList代替。
如果你想設計一個神經網路的層數作為輸入傳遞。
和list的區別?
ModuleList
是Module
的子類,當在Module
中使用它的時候,就能自動識別為子module。
當新增 nn.ModuleList 作為 nn.Module 物件的一個成員時(即當我們新增模組到我們的網路時),所有 nn.ModuleList 內部的 nn.Module 的 parameter 也被新增作為 我們的網路的 parameter。
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
def forward(self, x):
# ModuleList can act as an iterable, or be indexed using ints
for i, l in enumerate(self.linears):
x = self.linears[i // 2](x) + l(x)
return x
1. extend和append方法
nn.moduleList定義物件後,有extend和append方法,用法和python中一樣,extend是新增另一個modulelist append是新增另一個module
class LinearNet(nn.Module):
def __init__(self, input_size, num_layers, layers_size, output_size):
super(LinearNet, self).__init__()
self.linears = nn.ModuleList([nn.Linear(input_size, layers_size)])
self.linears.extend([nn.Linear(layers_size, layers_size) for i in range(1, self.num_layers-1)])
self.linears.append(nn.Linear(layers_size, output_size)
2. 建立以及使用方法
建立以及使用方法如下,
1 2 3 4 5 6 |
|
和普通list不一樣,它和torch的其他機制結合緊密,繼承了nn.Module的網路模型class可以使用nn.ModuleList並識別其中的parameters,當然這只是個list,不會自動實現forward方法,
1 2 3 4 5 6 7 8 9 |
|
MyModule( (module_list): ModuleList( (0): Conv2d (3, 3, kernel_size=(3, 3), stride=(1, 1)) (1): ReLU() ) )
1 2 |
|
('module_list.0.weight', torch.Size([3, 3, 3, 3])) ('module_list.0.bias', torch.Size([3]))
可見,普通list中的子module並不能被主module所識別,而ModuleList中的子module能夠被主module所識別。這意味著如果用list儲存子module,將無法調整其引數,因其未加入到主module的引數中。
除ModuleList之外還有ParameterList,其是一個可以包含多個parameter的類list物件。在實際應用中,使用方式與ModuleList類似。
3. yolo v3構建網路
首先module_list = nn.ModuleList()
然後
for index, x in enumerate(blocks[1:]):#根據不同的block 遍歷module
module = nn.Sequential()
然後根據cfg讀進來的資料,
module.add_module("batch_norm_{0}".format(index), bn)
module.add_module("conv_{0}".format(index), conv)
等等
module_list.append(module)