PyTorch Week 3——模型创建
PyTorch Week 2——Dataloader与Dataset
PyTorch Week 1
PyTorch Week 3——nn.Module的容器:Sequential、ModuleList、ModuleDice- 系列文章目录
- 前言
- 一、Sequential
- 1. 模型构建:默认的命名形式与字典命名方式
- 2. 模型拼接
- 二、ModuleList
- 1. 构建
- 2.拼接
- 三、ModuleDict
- 四、构建AlexNet
- 总结
前言
本节介绍使用Pytorch的nn.Module模块的容器功能,包括Sequential、ModuleList、ModuleDice三种方法。
一、Sequential打开lesson-11的module_containers.py,首先通过LeNetSequential类展示默认的Sequential定义方法
1. 模型构建:默认的命名形式与字典命名方式Sequential类有两种命名方式,
#第一种默认的命名方式
class LeNetSequential(nn.Module):#继承nn.Module,LeNet包括特征提取器和分类器
def __init__(self, classes):
super(LeNetSequential, self).__init__()#调用nn.Module的__init__定义属性
self.features = nn.Sequential(#调用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),)#在这一行完成了所有子层的实例化和Sequential的实例化,并将nn.Sequential赋值给self.features
#第二种,类似字典的命名方式
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),
}))
步入Sequential赋值语句,进入container.py的Sequential类,__init__函数首先判断接收的变量是否是一个有序字典,若不是的话,反复调用add_module,将接收的模型变量赋值给‘0’,‘1’,‘2’······;
class Sequential(Module):#首先Sequential类也继承Module,同样有八个字典用于管理变量,变量型的子层和字典型的子层都通过add_Module方法存储在_Module中,
def __init__(self, *args):
super(Sequential, self).__init__()
if len(args) == 1 and isinstance(args[0], OrderedDict):
for key, module in args[0].items():#获取字典的键值对
self.add_module(key, module)#调用module的add_module方法,赋值给Sequential的_module
else:
for idx, module in enumerate(args):#
self.add_module(str(idx), module)
若是的话,调用add_module,将接收的模型变量按照字典的键值赋值
步入调用net
output = net(fake_img)
进入到了LeNetSequential的forward函数
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
进入了module的call函数,步入forward,可以看到self变量列表中已经定义好了两个子模块feature和classifier,其中包含每个子层。
步入classifier子模块,进入module的call函数,再步入,进入container的forward函数,在这里实现了前向传播,反复按照顺序调用Sequential的_module里的子层
def forward(self, input):
for module in self:
input = module(input)
return input
小结:
nn.Sequential是nn.Module的容器,用于按照顺序包装一组网络层,具有两个性质:
- 顺序性:网络层按照定义时的顺序排列
- 自带forward:Sequential自带forward函数,使用for循环调用定义的子层,依次执行前向传播
ModuleList以迭代的方式调用网络层,与list类似,主要包含三种方法:
- append():在ModuleList后添加一个网络层
- extend():拼接两个ModuleList
- insert():在ModuleList中指定位置插入一个网络层
列表生成式的方式生成子层
class ModuleList(nn.Module):
def __init__(self):
super(ModuleList, self).__init__()
self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])
ModuleList的实例化,与列表的方法类似,步入+=是extend方法
def __init__(self, modules: Optional[Iterable[Module]] = None) -> None:
super(ModuleList, self).__init__()
if modules is not None:
self += modules
def __iadd__(self, modules: Iterable[Module]) -> 'ModuleList':
return self.extend(modules)
步出至主函数,可以看到net的_module已经有了20个Linear层
继续运行步入
output = net(fake_data)
进入实例化的ModuleList的forward,前向传播过程与for循环列表类似
def forward(self, x):
for i, linear in enumerate(self.linears):#注意,这里需要手动写for循环实现前向传播,与Sequential不同
x = linear(x)#步入
return x
步入,进入的是linear.py文件,Linear类,forward函数,可以
def forward(self, input: Tensor) -> Tensor:
return F.linear(input, self.weight, self.bias)
查看container.py的ModuleList类后发现该类不具有forward函数,前向传播需要写for循环实现的
三、ModuleDictModuleDict函数是nn.Module的容器,用于包装一组网络层,以索引的方式调用网络层
主要方法:
- clear():清空ModuleDict
- iterms():返回可迭代的键值对(key-value pairs)
- keys():返回字典的键(key)
- values():返回字典的值(value)
- pop():返回一对键值,并从字典中删除
class ModuleDict(nn.Module):
def __init__(self):
super(ModuleDict, self).__init__()
self.choices = nn.ModuleDict({
'conv': nn.Conv2d(10, 10, 3),#ModuleDict的字典定义方式
'pool': nn.MaxPool2d(3)
})
self.activations = nn.ModuleDict({
'relu': nn.ReLU(),
'prelu': nn.PReLU()
})
def forward(self, x, choice, act):
x = self.choices[choice](x)#前向传播中调用:子模块名[键](输入)
x = self.activations[act](x)
return x
前向传播时,需要输入两个子模块的key,比如第二个子模块activate可以选择激活函数是使用relu还是prelu
output = net(fake_img, 'conv', 'relu')四、构建AlexNet
AlexNet特点:
- ReLU减轻梯度消失
- LRN(Local Response Normalization)对数据归一化,减轻梯度消失
- Dropout
- Data Augmentation:TenCrop,色彩修改
分为特征提取器和分类器两部分。
查看Pytorch代码,AlexNet网络分成feature,avgpool,classifier,feature和classifier是Sequential
class AlexNet(nn.Module):
def __init__(self, num_classes: int = 1000) -> None:
super(AlexNet, self).__init__()
self.features = nn.Sequential(#特征提取器
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))#池化层
self.classifier = nn.Sequential(#分类器
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
forward中只有四行,借助了Sequential函数自带的forward。
总结本节学习了nn.Module的三种容器,分别是Sequential,ModuleList,ModuleDict
- nn.Sequential:顺序性,严格按顺序执行,常用语block构建
- nn.ModuleList:迭代性,通过for循环重复构建,常用语大量重复网络构建
- nn.ModuleDict:所隐形,常用于可以选择的网络层
查看了pytorch代码中的AlexNet网络结构



