本文我们通过搭建卷积神经网络模型,实现手写数字识别。

pytorch中提供了手写数字的数据集 ,我们可以直接从pytorch中下载
MNIST中包含70000张手写数字图像:60000张用于训练,10000张用于测试
图像是灰度的,28x28像素
首先,下载数据集
python
import torch
from torchvision import datasets #封装与图像相关的模型,数据集
from torchvision.transforms import ToTensor # #数据转换,张量,将其他类型的数据转换为tensor张量
training_data=datasets.MNIST(
root='data',#表示下载的手写数字到哪个路径
train=True,#读取下载后数据中的训练集
download=True,#如果之前已经下载过,就不用再下载
transform=ToTensor(),#张量,图片不能直接传入神经网络模型
)
test_data=datasets.MNIST(
root='data',
train=False,
download=True,
transform=ToTensor(),
)
打包数据
python
from torch.utils.data import DataLoader
train_dataloader=DataLoader(training_data,batch_size=64)
test_dataloader=DataLoader(test_data,batch_size=64)
判断当前设备是否支持GPU
python
device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'using {device} device')
构建卷积神经网络模型
python
from torch import nn #导入神经网络模块
class CNN(nn.Module):
def __init__(self):#初始化类
super(CNN,self).__init__()#初始化父类
self.conv1=nn.Sequential(# 将多个层(如卷积、激活函数、池化等)按顺序打包,输入数据会依次通过这些层,无需手动编写每一层的传递逻辑。
nn.Conv2d(#2D 卷积层,提取空间特征。
in_channels=1,#输入通道数
out_channels=16,#输出通道数
kernel_size=3,#卷积核大小
stride=1,#步长
padding=1,#填充
),
nn.ReLU(),#激活函数,引入非线性变换,使得神经网络能够学习复杂的非线性变换,增强表达能力
nn.MaxPool2d(kernel_size=2)# 2x2最大池化(尺寸减半)
)
self.conv2=nn.Sequential(
nn.Conv2d(16,32,3,1,1),
nn.ReLU(),
# nn.Conv2d(32,32,3,1,1),
# nn.ReLU(),
nn.MaxPool2d(2),
)
self.conv3=nn.Sequential(
nn.Conv2d(32,64,3,1,1)
)
self.out=nn.Linear(64*7*7,10)
def forward(self,x):#前向传播
x=self.conv1(x)
x=self.conv2(x)
x=self.conv3(x)
x=x.view(x.size(0),-1)# 展平为向量(保留batch_size,合并其他维度)
output=self.out(x) # 全连接层输出
return output
返回的output结果大致如图所示

模型传入GPU
python
model=CNN().to(device)
print(model)
损失函数,衡量的是模型预测的概率分布与真实的类别分布之间的差异。
python
loss_fn=nn.CrossEntropyLoss()
优化器,用于在训练神经网络时更新模型参数,目的是在神经网络训练过程中,自动调整模型的参数(权重和偏置),以最小化损失函数。
python
optimizer=torch.optim.Adam(model.parameters(),lr=0.01)
模型训练
python
def train(dataloader,model,loss_fn,optimizer):
model.train()
batch_size_num=1
for X,y in dataloader:
X,y=X.to(device),y.to(device)
pred=model.forward(X)
loss=loss_fn(pred,y)
# Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络
optimizer.zero_grad() #梯度值清零
loss.backward() #反向传播计算得到每个参数的梯度值
optimizer.step() #根据梯度更新网络参数
loss_value=loss.item()
if batch_size_num%100==0:
print(f'loss:{loss_value:>7f}[number:{batch_size_num}]')
batch_size_num+=1
epochs=10
for i in range(epochs):
print(f'第{i}次训练')
train(train_dataloader, model, loss_fn, optimizer)
模型测试
python
def test(dataloader,model,loss_fn):
size = len(dataloader.dataset)# 测试集总样本数
num_batches = len(dataloader)# 测试集总批次数
model.eval()#进入到模型的测试状态,所有的卷积核权重被设为只读模式
test_loss, correct = 0, 0# 初始化累计损失和正确预测数
#禁用梯度计算
with torch.no_grad():#一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
for X,y in dataloader:
X,y=X.to(device),y.to(device)
pred=model.forward(X)
test_loss+=loss_fn(pred,y).item()
correct+=(pred.argmax(1)==y).type(torch.float).sum().item()
a=(pred.argmax(1)==y)
b=(pred.argmax(1)==y).type(torch.float)
test_loss/=num_batches
correct/=size
print(f'Test result: \n Accuracy:{(100*correct)}%,Avg loss:{test_loss}')
test(test_dataloader,model,loss_fn)
得到结果如图所示
