基于PyTorch的深度学习系列教程(三):使用torch.nn.Module建立模型
1. Module类概述
在PyTorch中,无论是自定义的模块与损失函数,还是完整模型,都是通过继承torch.nn.Module
类来构建的。因此,要想建立较复杂的网络,我们必须使用此类。
2. 使用Module类建立模型
在实际使用中,要建立一个模型,我们需要继承torch.nn.Module
类,并更新__init__
与forward
这两个方法。其中,forward
方法定义了模型前向传播的完整过程。
通常,为了代码的简洁性,我们将需要更新参数的操作(例如卷积层、全连接层等)定义在__init__
方法内。对于不需要更新参数的操作(例如池化层、激活函数等),我们直接在foward
中定义。
以下为一个简单的例子:
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms as T
class SimpleNet(nn.Module):
def __init__(self,in_ch): #in_ch: 输入张量的通道数
super(SimpleNet, self).__init__() #必须写,调用父类的构建函数
self.conv1 = nn.Conv2d(in_ch, in_ch, kernel_size=5) #定义卷积层1
self.conv2 = nn.Conv2d(in_ch, in_ch, kernel_size=7) #定义卷积层2
self.linear1 = nn.Linear(13924,2) #定义全连接层1
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) #改变维度
x = self.linear1(x)
x = F.sigmoid(x) #进行Sigmoid变换,无参数
return x
代码分析
在上面的代码中,我们构建了一个名为SimpleNet的类作为模型,并继承了nn.Module类。在类内,我们重构了__init__
和forward
方法。我们在__init__
方法内调用了父类的构建函数,并随后定义了模型的两个卷积层和一个全连接层。在forward
方法内,我们实现了模型的前向传播,并对结果进行了Sigmoid操作,最后返回了结果。
3. 使用nn.Sequential包装层
在实际应用中,我们常常将多个不同的层打包为一个更大的模块(例如ResNet中的ResBlock),从而方便调用。为了实现这一点,我们可以使用nn.Sequential
。
nn.Sequential
是一个Module
的序列容器。我们可以把一个模块的组成部分按顺序传入,也可以传入一个存有组成部分的OrderedDict
,从而形成一个新的Module
,作为我们想构建的模块。可以看到,在PyTorch中,Module
的组合搭配比较灵活自由,既可以作为单个层,也可以作为模块和完整模型使用。
以下为一个使用的小例子:
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms as T
from collections import OrderedDict #导入OrderedDict
class SimpleNet(nn.Module):
def __init__(self,in_ch): #in_ch: 输入张量的通道数
super(SimpleNet, self).__init__() #必须写,调用父类的构建函数
#方法一,依次传入组成部分
self.block1 = nn.Sequential(
nn.Conv2d(in_ch, in_ch, kernel_size=5),
nn.BatchNorm2d(in_ch),
nn.ReLU()
)
#方法二,传入一个OrderedDict
self.block2 = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(in_ch, in_ch, kernel_size=7)),
('bn1', nn.BatchNorm2d(in_ch)),
('relu1', nn.ReLU())
]))
self.linear1 = nn.Linear(13924,2) #定义全连接层1
def forward(self,x):
x = self.block1(x)
x = self.block2(x)
x = x.view(x.size(0), -1) #改变维度
x = self.linear1(x)
x = F.sigmoid(x) #进行Sigmoid变换,无参数
return x
上面的代码使用nn.Sequential
的两种实现方法,分别封装了两个含有卷积层、正则化层、和ReLU激活函数的模块。相比于直接传入层,使用OrderedDict时可以对层添加解释,但代码更繁琐,可根实际据情况灵活使用。
4. 调用模型
和使用其他类一样,我们先将定义好的模型类实例化,之后将输入矩阵传入即可。我们现在来调用刚刚定义好的SimpleNet
模型:
#调用模型
model = SimpleNet(in_ch=1)
input_mat = torch.randn(4,1,128,128)
out = model(input_mat)
print(out.shape)
#输出
torch.Size([4, 2])
可以看到,模型输出是一个尺寸为(4,2)的张量。
5. 常用层
以下为torch.nn
中的常用层:
#卷积层与反卷积层
nn.Conv2d()
nn.ConvTranspose2d()
#池化层
nn.MaxPool2d()
#全连接层
nn.Linear()
nn.Bilinear()
#DropOut层
nn.Dropout()
#扁平化矩阵
nn.Flatten()
在之后的博文中,会详尽介绍上述以及其他常用层的使用方法。