MNIST 手写数字识别
导入相关库
import torch
# pytorch中最重要的模块,封装了神经网络相关的函数
import torch. nn as nn
# 提供了一些常用的函数,如softmax
import torch. nn. functional as F
# 优化模块,封装了求解模型的一些优化器,如Adam SGD
import torch. optim as optim
# pytorch 视觉库中提供了一些数据变换的接口
from torchvision import transforms
# pytorch 视觉库提供了加载数据集的接口
from torchvision import datasets
# 用于下载并导入数据集
import torchvision
from torch. utils. data import DataLoader
设置超参数
# 由于使用批量训练的方法,需要定义每批的训练的样本数目
batch_size = 64
# 总共训练迭代的次数
epoch = 1
# 设定初始的学习率
LR = 0.001
# 让torch判断是否使用GPU (若使用GPU 环境则会更快)
device = torch. device ( 'cuda' if torch. cuda. is_available ( ) else 'cpu' )
获取并装载训练集、测试集
# root 用于指定数据集下载后的存放路径
# train 用于指定数据集下载后载入到训练集or测试集(True: 载入训练集 False: 载入测试集)
# transform 用于指定导入数据集需要对数据进行的操作(转换为tensor数据类型)
# download 设为True表示数据集由程序自动下载
# 下载训练集
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)
# 使用Dataloader数据迭代器加载数据
# dataset 用于指定我们载入的数据集名称
# shuffle 在装载的过程会将数据随机打乱顺序并进行打包,便于后续训练
# 装载训练集
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)
搭建神经网络
# 卷积神经网络LeNet
# 卷积层使用 torch. nn. Conv2d
# 激活层使用 torch. nn. ReLU
# 池化层使用 torch. nn. MaxPool2d
# 全连接层使用 torch. nn. Linear
class LeNet ( nn. Module) :
def __init__ ( self) :
super ( ) . __init__ ( )
# sequential ( ) 按照顺序排列,要保证相邻层的输入输出大小相匹配
# 卷积 conv2d ( in_channels, out_channels, kernel_size, stride, padding)
# in_channels 网络输入的通道数
# out_channels 网络输出的通道数
# kernel_size 卷积核大小q* q
# stride 卷积移动步长
# padding 全零填充
# 激活函数 relu ( )
# 最大池化 maxpool2d ( kernel_size, stride)
# kenel_size 最大池化的窗口大小
# stride 最大池化窗口的移动步长
# 全连接层 linear ( in_features, out_features)
# in_features 输入维数
# out_features 输出维数
# BN 层 batchnorm1d ( num_features)
# num_features 特征维度(等于前一层输出的维度)
self. conv1 = nn. Sequential ( nn. Conv2d ( 1 , 6 , 3 , 1 , 2 ) , nn. ReLU ( ) ,
nn. MaxPool2d ( 2 , 2 ) )
self. conv2 = nn. Sequential ( nn. Conv2d ( 6 , 16 , 5 ) , nn. ReLU ( ) ,
nn. MaxPool2d ( 2 , 2 ) )
self. fc1 = nn. Sequential ( nn. Linear ( 16 * 5 * 5 , 120 ) ,
nn. BatchNorm1d ( 120 ) , nn. ReLU ( ) )
self. fc2 = nn. Sequential (
nn. Linear ( 120 , 84 ) ,
nn. BatchNorm1d ( 84 ) ,
nn. ReLU ( ) ,
# 最后的结果一定要变为 10 ,因为数字的选项是 0 ~ 9
nn. Linear ( 84 , 10 ) )
# 前向传播
def forward ( self, x) :
# 卷积+ 池化
x = self. conv1 ( x)
# 卷积+ 池化
x = self. conv2 ( x)
# 对参数实现扁平化,便于后面全连接层输入
x = x. view ( x. size ( ) [ 0 ] , - 1 )
# 全连接
x = self. fc1 ( x)
# 全连接
x = self. fc2 ( x)
return x
# 将网络操作移动到gpu或者cpu
net = LeNet ( ) . to ( device)
# 定义交叉熵损失函数
criterion = nn. CrossEntropyLoss ( )
# 定义模型优化器(Adam自适应优化算法)
# 输入模型参数和初始学习率
optimizer = optim. Adam (
net. parameters ( ) ,
lr= LR ,
)
训练模型
def train ( epoch) :
sum_loss = 0.0
# 从迭代器抽取图片和标签
for i, data in enumerate ( train_loader) :
inputs, labels = data
# 将优化器内部参数梯度归零
optimizer. zero_grad ( )
# 将数据传入网络进行前向运算
outputs = net ( inputs)
# 计算损失函数值
loss = criterion ( outputs, labels)
# 反向传播
loss. backward ( )
# 通过梯度做一步参数更新
optimizer. step ( )
# 计算每批次的损失值
sum_loss += loss. item ( )
if i % 100 == 99 :
print ( '[%d,%d] loss:%.03f' %
( epoch + 1 , i + 1 , sum_loss / 100 ) )
sum_loss = 0.0
测试模型
def test ( ) :
# 将模型变换为测试模式
net. eval ( )
correct = 0
for data_test in test_loader:
images, labels = data_test
# 测试输入数据得到输出
output_test = net ( images)
# 找到概率最大的分类
# torch. max ( ) 返回value, index(_表示value, predicted表示index)
# dim= 1 表示输出所在行(样本)的最大值 dim= 0 表示输出所在列(类别)的最大值
_, predicted = torch. max ( output_test, 1 )
# 计算正确分类的数目
correct += ( predicted == labels) . sum ( )
# 输出测试集中正确分类的数目
print ( "correct1: " , correct)
# 输出测试集分类正确率
print ( "Test acc: {0}" . format ( correct. item ( ) /
len ( test_dataset) ) )
# 程序入口
if __name__ == '__main__' :
for epoch in range ( epoch) :
train ( epoch)
test ( )