原文地址:详解PyTorch中的ModuleList和Sequential
目录
torch.nn.Module()
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.conv1 = nn.Conv2d(3, 32, 3)
self.conv2 = nn.Conv2d(32, 10, 1)
def forward(self, x):
x = F.relu(self.conv1(x)
return F.relu(self.conv2(x))
torch.nn.Sequential(*args)
- nn.Sequential里面的模块是按照顺序进行排列的,所以必须保证前一个模块的输出大小和下一个模块的输入大小是一致的。
- 每个module采用默认的命名方式(按序号0,1,2,…),也可以使用OrderedDict来指定每个module的名字
#使用 Sequential
model = nn.Sequential(
nn.Conv2d(3, 32, 3),
nn.ReLU()
nn.Conv2d(32, 10, 1)
nn.ReLu()
)
# 使用 Sequential 和 OrderedDict
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(3, 32, 3)),
('ReLu1', nn.ReLu()),
('conv2', nn.Conv2d(32, 10, 1)),
('ReLu2', nn.ReLu())
]))
torch.ModuleList(module=None)
- nn.ModuleList,它是一个储存不同 module,并自动将每个 module 的 parameters 添加到网络之中的容器
- 可以把任意 nn.Module 的子类 (比如 nn.Conv2d, nn.Linear 之类的) 加到这个 list 里面,方法和 Python 自带的 list 一样,无非是 extend,append 等操作
- 但不同于一般的 list,加入到 nn.ModuleList 里面的 module 是会自动注册到整个网络上的,同时 module 的 parameters 也会自动添加到整个网络中。若使用python的list,则会出问题。
class net_modlist(nn.Module):
def __init__(self):
super(net_modlist, self).__init__()
self.modlist = nn.ModuleList([
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
])
def forward(self, x):
for m in self.modlist:
x = m(x)
return x
net_modlist = net_modlist()
print(net_modlist)
#net_modlist(
# (modlist): ModuleList(
# (0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
# (1): ReLU()
# (2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
# (3): ReLU()
# )
#)
for param in net_modlist.parameters():
print(type(param.data), param.size())
#<class 'torch.Tensor'> torch.Size([20, 1, 5, 5])
#<class 'torch.Tensor'> torch.Size([20])
#<class 'torch.Tensor'> torch.Size([64, 20, 5, 5])
#<class 'torch.Tensor'> torch.Size([64])
使用Python自带的list
class net_modlist(nn.Module):
def __init__(self):
super(net_modlist, self).__init__()
self.modlist = [
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
]
def forward(self, x):
for m in self.modlist:
x = m(x)
return x
net_modlist = net_modlist()
print(net_modlist)
#net_modlist()
for param in net_modlist.parameters():
print(type(param.data), param.size())
#None
使用 Python 的 list 添加的卷积层和它们的 parameters 并没有自动注册到我们的网络中。我们可以使用 forward 来计算输出结果。但是如果用其实例化的网络进行训练的时候,因为这些层的parameters不在整个网络之中,所以其网络参数也不会被更新,也就是无法训练。
nn.Sequential与nn.ModuleList的区别
不同点1
nn.Sequential内部实现了forward函数,因此可以不用写forward函数。而nn.ModuleList则没有实现内部forward函数。
#例1:这是来自官方文档的例子
seq = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
print(seq)
# Sequential(
# (0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
# (1): ReLU()
# (2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
# (3): ReLU()
# )
#对上述seq进行输入
input = torch.randn(16, 1, 20, 20)
print(seq(input))
#torch.Size([16, 64, 12, 12])
#例2:或者继承nn.Module类的话,就要写出forward函数
class net1(nn.Module):
def __init__(self):
super(net1, self).__init__()
self.seq = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
def forward(self, x):
return self.seq(x)
#注意:按照下面这种利用for循环的方式也是可以得到同样结果的
#def forward(self, x):
# for s in self.seq:
# x = s(x)
# return x
#对net1进行输入
input = torch.randn(16, 1, 20, 20)
net1 = net1()
print(net1(input).shape)
#torch.Size([16, 64, 12, 12])
对于ModuleList而言
#例1:若按照下面这么写,则会产生错误
modlist = nn.ModuleList([
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
])
print(modlist)
#ModuleList(
# (0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
# (1): ReLU()
# (2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
# (3): ReLU()
#)
input = torch.randn(16, 1, 20, 20)
print(modlist(input))
#产生NotImplementedError
#例2:写出forward函数
class net2(nn.Module):
def __init__(self):
super(net2, self).__init__()
self.modlist = nn.ModuleList([
nn.Conv2d(1, 20, 5),
nn.ReLU(),
nn.Conv2d(20, 64, 5),
nn.ReLU()
])
#这里若按照这种写法则会报NotImplementedError错
#def forward(self, x):
# return self.modlist(x)
#注意:只能按照下面利用for循环的方式
def forward(self, x):
for m in self.modlist:
x = m(x)
return x
input = torch.randn(16, 1, 20, 20)
net2 = net2()
print(net2(input).shape)
#torch.Size([16, 64, 12, 12])
不同点2
nn.Sequential可以使用OrderedDict对每层进行命名
不同点3
- nn.Sequential里面的模块按照顺序进行排列的,所以必须确保前一个模块的输出大小和下一个模块的输入大小是一致的。
- nn.ModuleList 并没有定义一个网络,它只是将不同的模块储存在一起,这些模块之间并没有什么先后顺序可言,网络执行顺序是根据forward函数决定的。
总结
- Sequential内部module是有序的,内部已经实现了forward函数
- ModuleList内部module是无序的,需要用户自己写forward函数,module执行的顺序是由forward函数决定的
- 代码技巧:若网络中有很多相似或者重复的层,我们一般会考虑用 for 循环来创建它们,而不是一行一行地写,这时可以使用ModuleList。
class net4(nn.Module):
def __init__(self):
super(net4, self).__init__()
layers = [nn.Linear(10, 10) for i in range(5)]
self.linears = nn.ModuleList(layers)
def forward(self, x):
for layer in self.linears:
x = layer(x)
return x
net = net4()
print(net)
# net4(
# (linears): ModuleList(
# (0): Linear(in_features=10, out_features=10, bias=True)
# (1): Linear(in_features=10, out_features=10, bias=True)
# (2): Linear(in_features=10, out_features=10, bias=True)
# )
# )