主要实现
- 掌握pytorch自带数据集的导入
- 初步编写DataLoader
- 定义模型、损失和优化器
- 训练简单神经网络
- 将模型结果保存至本地
参考https://zhuanlan.zhihu.com/p/128137225
- 库函数
import torch
import torchvision
from torch.autograd import Variable
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
import cv2
- 数据部分
1.1 Dataset
从 torchvision.datasets
自带的数据集中下载、导入MNIST数据集
train_dataset = datasets.MNIST(
root = 'data/',
train = True,
transform = transforms.ToTensor(),
download = True
)
test_dataset = datasets.MNIST(
root = 'data/',
train = False,
transform = transforms.ToTensor(),
download = True
)
1.2 DataLoader
使用 torch.utils.data.DataLoader()
声明数据发生器
train_loader = DataLoader(
dataset=train_dataset,
batch_size=100,
shuffle=True
)
test_loader = DataLoader(
dataset=test_dataset,
batch_size=100,
shuffle=True
)
1.3 数据可视化
plt.figure("Image")
plt.figure(figsize=(4,4))
plt.imshow(np.array(train_dataset[0][0])[0], cmap=plt.cm.binary)
plt.axis('on')
plt.title('image')
plt.show()
- 定义模型
class Model(torch.nn.Module):
'''
自定义模型继承自torch.nn.Module
将各种层定义到self中
要复写forward()
'''
def __init__(self):
super(Model, self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 64, 3, 1, 1),
torch.nn.ReLU(),
torch.nn.Conv2d(64, 128, 3, 1, 1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2, 2)
)
self.dense = torch.nn.Sequential(
torch.nn.Linear(14*14*128, 1024),
torch.nn.ReLU(),
torch.nn.Dropout(p = 0.5),
torch.nn.Linear(1024, 10)
)
def forward(self, x):
x = self.conv1(x)
x = x.view(-1, 14*14*128)
x = self.dense(x)
return x
2.1 顺序容器——Sequential()
torch.nn.Sequential()
是一个有序的容器。自定义的神经网络模块按照Sequential()的参数顺序依次执行。
因为自定义的Model继承自 nn.Module
下,所以在forward函数中通过for循环依次执行Sequential()中的各模块。
Sequential参考:https://blog.csdn.net/dss_dssssd/article/details/82980222
2.2 卷积层——Conv2d
nn.Conv2d
:对由多个输入平面组成的输入信号做 二维卷积。(Applies a 2D convolution over an input signal composed of several input planes.)
函数定义为
torch.nn.Conv2d(
in_channels,
out_channels,
kernel_size,
stride=1,
padding=0,
dilation=1,
groups=1,
bias=True
)
参数
参数参数类型infoin_channelint输入图像通道数out_channelint输出图像通道数(同时也是卷积核个数)kernel_sizeint or (tuple)卷积核大小strideint or (tuple)卷积步长,默认为1paddingint or (tuple)填充操作个数padding_modestring填充模式dilationint or (tuple)扩张操作(空洞卷积)groupsint控制分组卷积biasbool是否添加可学习的偏置
卷积参考:https://blog.csdn.net/qq_34243930/article/details/107231539
2.3 全连接层——dense
将第一个卷积网络层(self.conv1
)的输出 压缩至一维后输入dense层。
nn.Linear()
是类似于Ax+b的函数,可以设置bias=True or False表示是否有偏置值b。nn.Dropout(x, p)
确保没有过拟合。参数x是输入(上述代码中因为Dropout在Sequential中,所以省去了x);p是需要删除的神经元的比例( p=0时保留全部神经元;p=1时神经元输出值为0)
dense层输入值计算
输 出 = ( i n p u t − k e r n e l + 1 + 2 ∗ p a d d i n g ) / s t r i d e 输出 = (input – kernel + 1 + 2 * padding)/ stride 输出=(i n p u t −k e r n e l +1 +2 ∗p a d d i n g )/s t r i d e
- 第一个卷积层使用了64个3*3的卷积核,步长为1,填充数为1。计算得到的输出大小为( 28 − 3 + 1 + 1 ∗ 2 ) / 1 = 28 (28 – 3 + 1 + 1 * 2) / 1 = 28 (2 8 −3 +1 +1 ∗2 )/1 =2 8。 即64 * 28 * 28
- 第二个卷积层输出大小同理为28。即128 * 28 *28
- 第三个池化层,kernel为2, 步长为2。输出大小为14 = 28 / 2。
由此,卷积网络最后的得到的是128 * 14 * 14的输出
2.4 前馈——forward
将从卷积网络得到的输出x经过变形后送给全连接层
x = x.view(-1, 14*14*128)
- 损失函数与优化方法
cuda_available = torch.cuda.is_available()
if cuda_available:
print("cuda loaded")
device = torch.device('cuda')
model = Model().to(device)
else:
model = Model()
cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
cuda loaded
3.1 实例化模型
model = Model().to(torch.device('cuda'))
使用gpu算力。
3.2 交叉熵损失
公式为H ( p , q ) = − ∑ i p i ∗ l o g q i H(p, q) = – \sum_{i}{p_{i} * log q_{i}}H (p ,q )=−∑i p i ∗l o g q i
3.3 优化器
使用Adam优化器,结合RMSProp(自适应调整学习率)和Momentum(动量)。
- RMSProp方法不断更新xuexil
-
Momentum通过上一次的更新增强或削弱梯度。如果梯度方向与上一次相同,则增强;不同,则削减。
-
训练与测试
if __name__ == '__main__':
model.train()
epochs = 5
for epoch in range(epochs):
sum_loss = 0.0
train_correct = 0
for data in train_loader:
inputs, labels = data
inputs, labels = Variable(inputs).cuda(), Variable(labels).cuda()
optimizer.zero_grad()
outputs = model(inputs)
loss = cost(outputs, labels)
loss.backward()
optimizer.step()
_, id = torch.max(outputs.data, 1)
sum_loss += loss.data
train_correct += torch.sum(id == labels.data)
print('[%d,%d] loss:%.03f' % (epoch + 1, epochs, sum_loss / len(train_loader)))
print(' correct:%.03f%%' % (100 * train_correct / len(train_dataset)))
model.eval() 测试需要用到model的eval()模式,以免将测试数据也用于训练。
with torch.no_grad():
test_correct = 0
for data in test_loader:
inputs, lables = data
inputs, lables = Variable(inputs).cuda(), Variable(lables).cuda()
outputs = model(inputs)
_, id = torch.max(outputs.data, 1)
test_correct += torch.sum(id == lables.data)
print("\ntest correct:%.3f%%" % (100 * test_correct / len(test_dataset)))
输出结果
[1,5] loss:0.006
correct:99.803%
[2,5] loss:0.006
correct:99.800%
[3,5] loss:0.006
correct:99.798%
[4,5] loss:0.005
correct:99.835%
[5,5] loss:0.005
correct:99.865%
test correct:99.160%
4.1 训练
本轮训练进行了5次,即5个epoch。
在训练每个batch时,要依次进行
- 清零上一个batch计算得到的梯度,以免梯度累加
- 计算损失loss
- 利用backward()对损失loss进行反向传播
- 利用step()对优化器进行梯度下降(参数数据都保存在optimizer中)
打印精度
torch.max(a,b)
对a中的固定第b维的情况下,计算最大值,返回最大值及其索引。这里是固定outputs的列,对行求最大值。outputs返回的值可以看作是归属每个类的概率,取最大概率作为最终结果。
Variable
autograd.Variable()
包装一个Tensor,使其包括 .data
和 .grad
两种属性。用 Variable()
封装好的张量可以进行 .backward()
反向传播运算。
Variable():https://blog.csdn.net/scutjy2015/article/details/71214928/
4.2 测试
在模型中,我们通常会加上Dropout层和batch normalization层,在模型预测阶段,我们需要将这些层设置到预测模式。否则会导致不一致的预测结果。
通常也使用 torch.no_grad()
关闭梯度的计算,如
model.eval()
with torch.no_grad():
...
out_data = model(data)
...
详细model.eval():https://zhuanlan.zhihu.com/p/356500543
- 保存模型
将训练结果保存至本地,下次训练直接load结果
torch.save(model.state_dict(), "parameter.pkl")
model.load_state_dict(torch.load('parameter.pkl'))
输出结果
< All keys matched successfully >
Original: https://blog.csdn.net/m0_56945333/article/details/123161867
Author: KimJuneJune
Title: pytorch实现MNIST识别——全流程
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/691491/
转载文章受原作者版权保护。转载请注明原作者出处!