一、下载数据集
MNIST数据集
将下载好的数据集解压放入同级项目路径下
二、导包
import torch
import torch.nn as nnn
import torch.optim as optim
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
from torchvision import datasets,transforms
%matplotlib inline
%matplotlib inline
可将绘图内嵌到notebook中,可以省略掉plt.show()
三、加载数据集
设置一些参数信息、datasets和dataloader
MNIST数据集图像大小均为28*28像素,共10个类别
#输入图像大小为28*28,10个类别,全部图像训练循环3次,每次训练64张
input_size = 28
num_classes = 10
num_epochs = 3
batch_size = 64
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)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=True)
四、构架模型
模型总共分为2层和1个输出
1,layer1包括卷积层、激活函数、最大池化层
因为是数据集样本是单颜色通道,大小为28*28像素点,形状为:[1,28,28]
卷积层:
卷积核为大小为5*5
,卷积核个数为16
,滑动步长为1
,加边圈数为2
由公式计算可得,通过卷积层输出的特征图形状为[16,28,28]
池化层:池化核大小为2*2
,仅对特征图大小进行砍半,即[14,14]
最终通过layer1之后的特征图形状为[16,14,14]
2,layer2包括卷积层、激活函数、最大池化层
通过layer1之后得到的特征图形状为[16,14,14]
卷积层:
卷积核为大小为5*5
,卷积核个数为32
,滑动步长为1
,加边圈数为2
同理通过计算得到特征图形状为[32,14,14]
池化层:池化核大小为2*2
,仅对特征图大小进行砍半,即[7,7]
最终通过layer2之后的特征图形状为[32,7,7]
3,输出层为线性层全连接
通过layer2之后得到的特征图形状为[32,7,7]
首先将该特征图进行展开成一行,x.view(x.size(0),-1)
每张图像的特征元素全部占一行,有多张图像,故得到(batch_size, 32*7*7)
之后因为最终的是十分类任务,在对其通过矩阵[32*7*7,10]
进行线性变换,最终将其转换为十个输出值,即[batch_size,10]
,每张图片为十个值,分别对应预测成为0-9这十个数字的概率,共batch_size张
class yy_model(nn.Module):
def __init__(self):
super(yy_model,self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(in_channels=1,out_channels=16,kernel_size=5,stride=1,padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.layer2 = nn.Sequential(
nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5,stride=1,padding=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.out = nn.Linear(in_features=7*7*32,out_features=10)
def forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = x.view(x.size(0),-1) #(batch_size, 32 * 7 * 7)
output = self.out(x)
return output
五、准确率
传入模型预测结果和实际的真实结果,进行对比即可,找到模型预测结果中最大的值,也就是模型预测成为的数字
def accuracy(predictions, labels):
pred = torch.max(predictions.data, 1)[1]
rights = pred.eq(labels.data.view_as(pred)).sum()
return rights, len(labels)
六、模型训练
# 实例化
net = yy_model()
#损失函数
criterion = nn.CrossEntropyLoss()
#优化器
optimizer = optim.Adam(net.parameters(), lr=0.001) #定义优化器,普通的随机梯度下降算法
#开始训练循环
for epoch in range(num_epochs):
#当前epoch的结果保存下来
train_rights = []
for batch_idx, (data, target) in enumerate(train_loader): #针对容器中的每一个批进行循环
net.train()
output = net(data)
loss = criterion(output, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
right = accuracy(output, target)
train_rights.append(right)
if batch_idx % 100 == 0:
net.eval()
val_rights = []
for (data, target) in test_loader:
output = net(data)
right = accuracy(output, target)
val_rights.append(right)
#准确率计算
train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
print('当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%'.format(
epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.data,
100. * train_r[0].numpy() / train_r[1],
100. * val_r[0].numpy() / val_r[1]))
七、检测模型训练效果
感觉模型训练的效果还行,实际标签为4,预测的结果也是4,嘿嘿
x,y = train_dataset[9]#第9个数据x为图片,对应的结果为4
x.shape # torch.Size([1, 28, 28])
y # 4
x = x.view(-1,1,28,28) # 因为投喂网络需要格式为[B,C,W,H],需要变成相应的格式
x.shape # torch.Size([1, 1, 28, 28])
y_hat = net(x) # 模型预测
y_hat
"""
tensor([[ -7.2561, -3.0549, -3.3932, -6.8128, 10.3861, -11.8726, -7.2241,
-0.6564, -2.5825, -10.9693]], grad_fn=<AddmmBackward0>)
"""
pred_maxvalue, pred_maxindex = torch.max(y_hat,dim=1) # 得到最大的值和索引下标
pred_maxvalue # tensor([10.3861], grad_fn=<MaxBackward0>)
pred_maxindex # tensor([4])
pred_maxindex.item() # 4