本案例在上一次案例的基础之上对神经网络模型进行了修改,在全连接层之前加入了特征提取层(卷积层+池化层),其他基本保持一致。
import torch
import numpy as np
#导入 pytorch 内置的mnist数据集
from torchvision.datasets import mnist
#导入对图像的预处理模块
import torchvision.transforms as transforms
#导入dataset的分批读取包
from torch.utils.data import DataLoader
#导入神经网络包nn(可用来定义和运行神经网络)
from torch import nn
#functional这个包中包含了神经网络中使用的一些常用函数,这些函数的特点是:不具有可学习的参数(如ReLU,pool,DropOut等)
import torch.nn.functional as F
#optim中实现了大多数的优化方法来更新网络权重和参数,如SGD、Adam
import torch.optim as optim
#导入可视化绘图库
import matplotlib.pyplot as plt
#2定义代码中用到的各个超参数
train_batch_size = 64 #指定DataLoader在训练集中每批加载的样本数量
test_batch_size = 128 #指定DataLoader在测试集中每批加载的样本数量
num_epoches = 15 # 模型训练轮数
lr = 0.01 #设置SGD中的初始学习率
momentum = 0.5 #设置SGD中的冲量
#3对数据进行预处理
# Compose方法即是将两个操作合并一起
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.1307], [0.3081])])
#4下载和分批加载数据集
#将训练和测试数据集下载到同目录下的data文件夹下
train_dataset = mnist.MNIST('.\data', train=True, transform=transform, download=False)
test_dataset = mnist.MNIST('.\data', train=False, transform=transform, download=False)
#dataloader是一个可迭代对象,可以使用迭代器一样使用。
#其中shuffle参数为是否打乱原有数据顺序
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)
在定义神经网络模型中需要注意的是,我们需要确定全连接层输入层的张量大小,也就是需要计算出最后一层特征提取层输出的张量大小,此计算方式有两种,一个是我们根据通道数、卷积核大小、池化核大小来一层一层手动推导出输出的张量大小,另外一个就是我们先不定义全连接层,实例化模型后让程序自己输出最后一层的张量大小
#5定义一个神经网络模型
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1,10,kernel_size=5) #灰度图像单通道
self.conv2 = nn.Conv2d(10,20,kernel_size=5)
self.pooling = nn.MaxPool2d(2) #kernel_size=2的最大池化
self.fc1 = nn.Linear(20*4*4,70)
self.fc2 = nn.Linear(70,10)
def forward(self,x):
batch_size = x.size(0)
x = self.pooling(F.relu(self.conv1(x)))
x = self.pooling(F.relu(self.conv2(x)))
x = x.view(batch_size,-1)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
#实例化网络模型
#检测是否有可用的GPU,否则使用cpu
device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
model=Net()
model.to(device)
#定义模型训练中用到的损失函数和优化器
criterion = nn.CrossEntropyLoss() #交叉熵损失函数
optimizer = optim.SGD(model.parameters(),lr=lr,momentum=momentum)
# parameters()将model中可优化的参数传入到SGD中
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
inputs, target = inputs.to(device), target.to(device)
optimizer.zero_grad() # 清除上一轮的梯度
outputs = model(inputs) #前向传播
loss = criterion(outputs, target)
loss.backward() # 反向传播
optimizer.step() # 优化参数
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_loss = 0.0
def test():
correct = 0
total = 0
model.eval() # 测试不需要反向传播
for data in test_loader:
inputs, target = data
inputs, target = inputs.to(device), target.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, dim = 1) # predicted为最大概率的下标,_为最大的概率值
total += target.size(0) #将每一批样本数量相加
correct += (predicted == target).sum().item()
print('Accuracy of the network on the 10000 test images: %.3f %%' % ( 100 * correct / total))
for epoch in range(num_epoches):
train(epoch)
test()
由下图训练结果与上一篇基于全连接神经网络的模型相比,可见先使用卷积和最大池化对图像进行特征提取,再使用全连接神经网络进行分类,可以很大程度上提升分类器性能。