对于cnn前馈神经网络如果前馈一次写一个forward函数会有些麻烦,在此就有两种简化方式,ModuleList和Sequential。其中Sequential是一个特殊的module,它包含几个子Module,前向传播时会将输入一层接一层的传递下去。ModuleList也是一个特殊的module,可以包含几个子module,可以像用list一样使用它,但不能直接把输入传给ModuleList。下面举例说明。
目录
一、nn.Sequential()对象
1、模型建立方式
第一种写法:
第二种写法:
第三种写法:
2、检查以及调用模型
查看模型
根据名字或序号提取子Module对象
调用模型
二、nn.ModuleList()对象
为什么有他?
什么时候用?
和list的区别?
1. extend和append方法
2. 建立以及使用方法
3. yolo v3构建网络
建立nn.Sequential()对象,必须小心确保一个块的输出大小与下一个块的输入大小匹配。基本上,它的行为就像一个nn.Module。
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 |
|
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 |
|
写一个module然后就写foreword函数很麻烦,所以就有了这两个。它被设计用来存储任意数量的nn. module。
如果在构造函数__init__
中用到list、tuple、dict等对象时,一定要思考是否应该用ModuleList或ParameterList代替。
如果你想设计一个神经网络的层数作为输入传递。
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
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)
建立以及使用方法如下,
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类似。
首先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)