Pytorch入门中 —— 搭建网络模型
2021-12-16 11:16
1026 查看
本节内容参照小土堆的pytorch
入门视频教程,主要通过查询文档的方式讲解如何搭建卷积神经网络。学习时要学会查询文档,这样会比直接搜索良莠不齐的博客更快、更可靠。讲解的内容主要是
pytorch核心包中
TORCH.NN中的内容(
nn是
Neural Netwark的缩写)。
通常,我们定义的神经网络模型会继承torch.nn.Module
类,该类为我们定义好了神经网络骨架。
卷积层
对于图像处理来说,我们通常使用二维卷积,即使用torch.nn.Conv2d
类:
创建该类时,我们通常只需要传入以下几个参数,其他不常用参数入门时可以不做了解,使用默认值即可,以后需要时再查询文档:
in_channels (int):输入数据的通道数,图片通常为3 out_channels (int):输出数据的通道数,也是卷积核的个数 kernel_size (int or tuple):卷积核大小,传入int表示正方形,传入tuple代表高和宽 stride (int or tuple, optional):卷积操作的步长,传入int代表横向和纵向步长相同,默认为1 padding (int, tuple or str, optional):填充厚度,传入int代表上下左右四个边填充厚度相同,默认为0,即不填充 padding_mode (string, optional):填充模式,默认为'zeros',即0填充
卷积操作后输出的张量的高和宽计算公式如下:
其中
input和
output中的
N代表
BatchSize,
C代表通道数,他们不影响
H和
W的计算。在保持
dilation为默认值
1的情况下,计算公式可简化为如下: H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] -\text{kernel\_size}[0]}{\text{stride}[0]} + 1\right\rfloor W_{out} = \left\lfloor\frac{W_{in} + 2 \times \text{padding}[1] - \text{kernel\_size}[1]}{\text{stride}[1]} + 1\right\rfloor
池化层
常用的二维最大池化定义在torch.nn.MaxPool2d
类中:
创建该类时,我们通常只需要传入以下几个参数,其他不常用参数入门时可以不做了解,使用默认值即可,以后需要时再查询文档:
kernel_size:池化操作时的窗口大小 stride:池化操作时的步长,默认为kernel_size padding:每个边的填充厚度(0填充)
池化操作后输出的张量的高和宽计算公式与卷积操作后的计算公式相同。
非线性激活
常见的
ReLU激活定义在
torch.nn.ReLU类中:
参数
inplace代表是否将
ouput直接修改在
input中。
线性层
线性层的定义在torch.nn.Linear
类中:
创建线性层使用的参数如下:
in_features:输入特征大小 out_features:输出特征大小 bias:是否添加偏置,默认为True
模型搭建示例
下图是一个
CIFAR10数据集上的分类模型,下面将根据图片进行模型代码的编写。
1.由于
CIFAR10数据集中图片为
3*32*32,所以图中模型的输入为3通道,高宽都为32的张量。
2.使用
5*5的卷积核进行卷积操作,得到通道数为
32,高和宽为
32的张量。因此我们可以推出该卷积层的参数如下:
in_channels = 3 out_channels = 32 kernel_size = 5 stride = 1 padding = 2
注:将 Hin = 32,Hout = 32 以及
kernal_size[0] = 5三个参数带入: H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] -\text{kernel\_size}[0]}{\text{stride}[0]} + 1\right\rfloor
有:
32 = \left\lfloor\frac{32 + 2 \times \text{padding}[0] -\text{5}}{\text{stride}[0]} + 1\right\rfloor发现
stride[0] = 1和
padding[0] = 2可以使得等式成立。同理可以得到
stride[1] = 1和
padding[1] = 2。
3.使用
2*2的核进行最大池化操作,得到通道数为
32,高和宽为
16的张量。可以推出该池化层的参数如下:
kernel_size = 2 stride = 2 padding = 0
注:
stride和
padding推导方式与2中相同。
4.使用
5*5的卷积核进行卷积操作,得到通道数为
32,高和宽为
16的张量。因此我们可以推出该卷积层的参数如下:
in_channels = 32 out_channels = 32 kernel_size = 5 stride = 1 padding = 2
5.使用
2*2的核进行最大池化操作,得到通道数为
32,高和宽为
8的张量。可以推出该池化层的参数如下:
kernel_size = 2 stride = 2 padding = 0
6.使用
5*5的卷积核进行卷积操作,得到通道数为
64,高和宽为
8的张量。因此我们可以推出该卷积层的参数如下:
in_channels = 32 out_channels = 64 kernel_size = 5 stride = 1 padding = 2
7.使用
2*2的核进行最大池化操作,得到通道数为
64,高和宽为
4的张量。可以推出该池化层的参数如下:
kernel_size = 2 stride = 2 padding = 0
8.将
64*4*4的张量进行展平操作得到长为
1024的向量。
9.将长为
1024的向量进行线性变换得到长为
64的向量(隐藏层),可以推出该线性层的参数如下:
in_features:1024 out_features:64
10.将长为
64的向量进行线性变换得到长为
10的向量,可以推出该线性层的参数如下:
in_features:64 out_features:10
因此,模型代码如下:
from torch import nn class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.conv1 = nn.Conv2d(3, 32, 5, padding=2) self.max_pool1 = nn.MaxPool2d(2) self.conv2 = nn.Conv2d(32, 32, 5, padding=2) self.max_pool2 = nn.MaxPool2d(2) self.conv3 = nn.Conv2d(32, 64, 5, padding=2) self.max_pool3 = nn.MaxPool2d(2) self.flatten = nn.Flatten() self.linear1 = nn.Linear(1024, 64) self.linear2 = nn.Linear(64, 10) # 必须覆盖该方法,该方法会在实例像函数一样调用时被调用,后面会有示例 def forward(self, x): x = self.conv1(x) x = self.max_pool1(x) x = self.conv2(x) x = self.max_pool2(x) x = self.conv3(x) x = self.max_pool3(x) x = self.flatten(x) x = self.linear1(x) x = self.linear2(x) return x
sequential
使用torch.nn.sequential
可以简化模型的搭建代码,他是一个顺序存放
Module的容器。当
sequential执行时,会按照
Module在构造函数中的先后顺序依次调用,前面
Module的输出会作为后面
Module的输入。
使用
sequential,上一节的代码可以简化为:
from torch import nn class MyModel(nn.Module): def __init__(self): super.__init__(MyModel, self) self.model = nn.Sequential( nn.Conv2d(3, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 32, 5, padding=2), nn.MaxPool2d(2), nn.Conv2d(32, 64, 5, padding=2), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(1024, 64), nn.Linear(64, 10) ) def forward(self, x): x = self.model(x) return x
损失函数、反向传播以及优化器
上面两节我们已经将
CIFAR10的分类模型搭建好,但还需要进行训练后才能用来预测分类。训练模型时,会用损失函数来衡量模型的好坏,并利用反向传播来求梯度,然后利用优化器对模型参数进行梯度下降,多次循环往复以训练出最优的模型。
模型训练代码如下:
import torch from torch.optim import SGD import torchvision from torch.utils.data import DataLoader from cifar10_model import MyModel from torch import nn from torch.utils import tensorboard def train(): # 获取 cifar10 数据集 root = "./dataset" transform = torchvision.transforms.Compose([ torchvision.transforms.ToTensor() ]) train_cifar10 = torchvision.datasets.CIFAR10(root=root, train=True, transform=transform, download=True) # 创建dataloader train_dataloader = DataLoader(dataset=train_cifar10, batch_size=64, shuffle=True, num_workers=16) # 创建模型 model = MyModel() # 创建交叉熵损失函数 loss = nn.CrossEntropyLoss() # 创建优化器,传入需要更新的参数,以及学习率 optim = SGD(model.parameters(), lr=0.01) # 创建 SummaryWriter writer = tensorboard.SummaryWriter("logs") # 写入模型图,随机生成一个输入 writer.add_graph(model, torch.randn(64, 3, 32, 32)) for epoch in range(20): loss_temp = 0.0 for batch_num, batch_data in enumerate(train_dataloader): images, targets = batch_data # 像调用方法一样调用实例 outputs = model(images) loss_res = loss(outputs, targets) loss_temp = loss_res # 清空前一次计算的梯度 optim.zero_grad() # 反向传播求梯度 loss_res.backward() # 更新参数 optim.step() # 记录每个epoch之后的loss writer.add_scalar("Loss/train", loss_temp, epoch) writer.close() if __name__ == "__main__": train()
模型图如下:
损失函数随训练周期的下降情况如下:
相关文章推荐
- 使用序贯模型搭建简易神经网络——pytorch
- 小白入门篇使用Python搭建点击率预估模型
- 使用pytorch搭建AlexNet操作(微调预训练模型及手动搭建)
- PyTorch快速入门教程十(GANs以及对抗网络)
- PyTorch上搭建简单神经网络实现回归和分类
- Pytorch搭建网络
- TensorFlow从入门到放弃(一)——实现经典神经网络模型LeNet5
- Pytorch: 神经网络模型参数初始化与 Finetune
- 【小白学PyTorch】1 搭建一个超简单的网络
- 网络分析(Network Analysis)入门篇(四) 网络的演化——随机图模型
- 从零开始入门 K8s | Kubernetes 网络模型进阶
- keras入门 ---用预训练好网络模型的bottleneck特征
- [动手学深度学习PyTorch笔记2]文本预处理、语言模型、循环神经网络
- Keras结合Keras后端搭建个性化神经网络模型(不用原生Tensorflow)
- 使用Pytorch搭建模型
- Keras入门-搭建BP网络识别手写字符
- Python实现Keras搭建神经网络训练RNN分类模型
- CNN网络模型的搭建
- Pytorch入门(一):anaconda+pycharm+pytorch环境搭建
- 网络分析(Network Analysis)入门篇(五) 网络的演化——有偏好的交友模型