上一篇文章讲解了对数据集的处理,这篇文章我们就开始学习网络模型的Pytorch编写。
虽然大部分时候我们不需要去自己编写模型,只需要使用大佬们写好的模型,然后再去改参数(俗称调参侠),但是我们依然要能看懂模型的代码。
这篇文章会比较短,因为网络模型的程序并不多(常用的),我们只需要看懂,能够模仿编写就可以。
网络模型-基本框架
网络模型与数据预处理的Dataset相同,使用类的方式实现。下面介绍一下神经网络的基本框架:
from torch import nn # 必不可少,下面所有函数都需要使用这个引入库
class my_module(nn.Module):
def __init__(self):
super(my_module,self).__init__() #调用父类初始化函数
# 一般这里把需要使用的卷积、池化等函数在这里给到全局变量
def forward(self,input):
# 类的功能实现部分,input就是这个类的输入,对于模型类来说,这个输入是个图片数据
# 这里使用上面的全局变量,整合成一个完整的神经网络,就是网络的前向过程
# 而网络的反向传播在模型运行之外,torch有直接的函数帮助我们计算反向传播,后面讲解
# ouput就是网络模型的输出,一般是个列表
return output
# 调用上面的模型
# image就是输入的图片,经过神经网络后,输出output
module = my_module()
output = module(image)
神经网络常用函数
神经网络的全部函数都在Pytorch官网,但是很多函数我们平常用的不多,尤其是对于新手,我们只需要了解其中的一小部分,以后要使用其他函数的时候,现查现用。
卷积层 :
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
- in_channels:输入图片的维度(channel),对于第一个卷积,图片的RGB就是三维的,所以一般第一个卷积的in_channels = 3
- out_channels:卷积后产生的维度(channel)
- kernel_size:卷积核的尺寸,可以是3(代表3x3卷积核)或5(代表5x5卷积核)
- stride:步长数值,默认为1
- padding:图片外围填充数值
- dilation:空洞卷积,默认为1(不产生空洞)
- group:分组卷积,默认为1(不分组)
- padding_mode:填充模式(零填充,常数填充,镜像填充,重复填充),默认为zero
池化层(最大池化):
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
- kernel_size:pooling的窗口大小
- stride:pooling的窗口移动的步长。默认是kernel_size
- padding:输入的每一条边补充0的层数
- dilation:空洞池化窗口
- return_indices:如果等于True,会返回输出最大值的序号
- ceil_mode:如果等于True,使用向上取整,默认是向下取整
注:最大池化,平均池化等操作都在官网上有说明,用法都大体一致。
激活函数(ReLU):
torch.nn.ReLU(inplace=False)
- inplace:对计算结果没有影响,只是是否使用中间变量,为True是覆盖原变量,没有中间变量
注:很多激活函数都在官网上有说明,用法大体一致。
线性层:
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)
- in_features:输入样本的大小
- out_features:输出样本的大小
- bias:是否添加偏移量,默认为True,添加bias
序列(Sequential):
torch.nn.Sequential(*args)
- 使用Sequential序列,可以将所有的神经网络函数整合在一起,使代码看起来更简洁
神经网络模型的例子
我们通过一个简单的例子,将上面提到的函数都整合在一起,就形成了神经网络的模型。
对照上面的模型,写一个简单的神经网络模型代码:
from torch import nn # 必不可少,引入库
# 按照上面的基本框架,编写模型
class my_module(nn.Module):
def __init__(self):
super(my_module, self).__init__()
# 卷积1:输入32*32*3的图片,使用5x5的卷积核,生成32*32*32的特征图,步长为1,因此填充为2
self.conv1=nn.Conv2d(3,32,5,padding=2,stride=1)
# 池化1:输入32*32*32的特征图,输出16*16*32的特征图,缩小两倍,所以池化窗口为2
self.pool1=nn.MaxPool2d(2)
# 卷积2:输入16*16*32的图片,使用5x5的卷积核,生成16*16*32的特征图,步长为1,因此填充为2
self.conv2=nn.Conv2d(32,32,5,stride=1,padding=2)
# 池化2:输入16*16*32的特征图,输出8*8*32的特征图,缩小两倍,所以池化窗口为2
self.pool2=nn.MaxPool2d(2)
# 卷积3:输入8*8*32的图片,使用5x5的卷积核,生成8*8*64的特征图,步长为1,因此填充为2
self.conv3=nn.Conv2d(32,64,5,stride=1,padding=2)
# 池化3:输入8*8*64的特征图,输出4*4*64的特征图,缩小两倍,所以池化窗口为2
self.pool3 = nn.MaxPool2d(2)
# 将4*4*64的特征图拉平成一个一维向量,使用flatten函数
self.flatten=nn.Flatten()
# 线性层1:拉平成一维向量4*4*64=1024为线性层的输入,输出为64
self.linear1=nn.Linear(1024,64)
# 线性层2:线性层的输入为64,输出为10
self.linear2=nn.Linear(64,10)
def forward(self,x):
# 按照上图,将各个函数放在一起
x=self.conv1(x)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = self.conv3(x)
x = self.pool3(x)
x=self.flatten(x)
x=self.linear1(x)
x=self.linear2(x)
# 将线性层的最后输出
return x
# 调用模型
moudle = my_module()
# iuput就是输入的图片,经过神经网络之后,输出output就是10个数,对应线性层的最后10个数
output=moudle(input)
如果使用Sequential的话,可以将代码整理的更加简洁:
class my_module(nn.Module):
def __init__(self):
super(my_module, self).__init__()
# 使用Sequential,可以将里面的函数按顺序执行
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2, stride=1),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, stride=1, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self,x):
# x就是神经网络最后线性层的输出,10个数值
x=self.model1
return x