模型容器(Containers)
nn.Sequential
nn.Sequential 是 nn.module 的容器,用于按顺序包装一组网络层
- 顺序性:各网络层之间严格按照顺序构建
- 自带forward(): 自带的forward里,通过for循环一次执行前向传播运算。
class LeNetSequential(nn.Module):
def __init__(self, classes):
super(LeNetSequential, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),)
self.classifier = nn.Sequential(
nn.Linear(16*5*5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, classes),)
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
代码调试
1、在71行设置断点,进入LeNetSequential这个类。
2、运行到24行进入到nn.Sequential这个类中。
3、在24行由于池化层和nn.Sequential的构建都在这一行,所以我们先进入池化层,然后退出来,再进入nn.Sequential类中。
我们可以看到nn.Sequential类继承于Module类,所以它包含8个有序的字典来管理属性。程序首先判断输入的数据类型是不是OrderedDict有序字典类型,不是进入else语句。
通过for循环取出每一个网络层,采用self.add_module类方法,将网络层添加到类中。
4、返回到主函数,运行完self.feature的代码,我们可以看到在self的module中多了key为feature,value为Sequential的值。
而在feature中的module里面多了6个网络层,他们是按照顺序构建的。当LeNetSequential完成船舰子模块后,我们接下来就要进行子模块的拼接。
5、进入76行的output = net(fake_img),来观察forward是怎么实现的。
此时我们会进入Module的__call__函数。
6、我们运行到541行,并进入self.forward函数,查看其实现方法。
此时,我们来到了LeNetSequential这个类当中。只要把x分给self.feature这个Sequential,那么程序就会实现Sequential中的六个网络层的前向传播。
7、进入self.features查看其实现方法,由于Sequential继承于Module,因此我们又一次进入到Module类的__call__函数中,我们继续点击到541行,并进入。
此时我们来到了Sequential类的forward函数中。利用for循环对Sequential中的网络层进行for循环的forward。
由于当前层的output就是下一层的input,所以我们要注意数据的格式,形状和大小。
以此类推。这样我们就实现了使用Sequential构建一个LeNet,然后实现了前向传播。
我们打印出网络结构,网络层是没有名称的,只能通过序号进行索引,而不能通过网络的名称进行索引。
nn.SequentialOrderDict
class LeNetSequentialOrderDict(nn.Module):
def __init__(self, classes):
super(LeNetSequentialOrderDict, self).__init__()
self.features = nn.Sequential(OrderedDict({
'conv1': nn.Conv2d(3, 6, 5),
'relu1': nn.ReLU(inplace=True),
'pool1': nn.MaxPool2d(kernel_size=2, stride=2),
'conv2': nn.Conv2d(6, 16, 5),
'relu2': nn.ReLU(inplace=True),
'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
}))
self.classifier = nn.Sequential(OrderedDict({
'fc1': nn.Linear(16*5*5, 120),
'relu3': nn.ReLU(),
'fc2': nn.Linear(120, 84),
'relu4': nn.ReLU(inplace=True),
'fc3': nn.Linear(84, classes),
}))
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
nn.SequentialOrderDict
1、在72行设置断点,并进入LeNetSequentialOrderDict类。
运行至51行,进入池化层后退出来,再进入self.features的SequentialOrderDict类中。
2、我们进入到nn.Sequential类中的__init__()函数中,由于我们传入的类型为OrderedDict所以程序进入第一个for循环中。代码会将Dict中的key和value取出来加入到Sequential中。
此时,key的值为conv1,value为Conv2d卷积层。for循环后续以此类推。
退回到主程序中,我们可以发现,net中的feature的_modules中每个网络层都有名称,我们可以通过名称索引网络层。