手写数字是机器学习中非常经典的案例,本文将通过pytorch框架,利用神经网络来实现手写数字识别
pytorch中提供了手写数字的数据集,我们可以直接从pytorch中下载
MNIST中包含70000张手写数字图像:60000张用于训练,10000张用于测试
图像是灰度的,28x28像素
下载数据集
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(),#数据类型转换
)
数据可视化,展示手写数字
from matplotlib import pyplot as plt
figure=plt.figure()
for i in range(9):img,label=training_data[i+59000]figure.add_subplot(3,3,i+1)plt.title(label)plt.axis('off')plt.imshow(img.squeeze(),cmap='gray')a=img.squeeze()
plt.show()
得到结果如下
打包数据
from torch.utils.data import DataLoader #数据包管理工具,打包数据train_dataloader=DataLoader(training_data,batch_size=64)#64张图片为一个包
test_dataloader=DataLoader(test_data,batch_size=64)
判断当前设备是否支持GPU
device='cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
print(f'using {device} device')
构建神经网络模型
from torch import nn #导入神经网络模块class NeuralNetwork(nn.Module):def __init__(self):super().__init__()self.flatten=nn.Flatten()self.hidden1=nn.Linear(28*28,128)self.hidden2=nn.Linear(128,256)self.out=nn.Linear(256,10)def forward(self,x):#前向传播x=self.flatten(x)x=self.hidden1(x)x=torch.relu(x)#引入非线性变换,使神经网络能够学习复杂的非线性关系,增强表达能力。x=self.hidden2(x)x=torch.relu(x)x=self.out(x)return x
返回的x结果大致如图所示
模型传入GPU
model=NeuralNetwork().to(device)
print(model)
损失函数
loss_fn = nn.CrossEntropyLoss() #创建交叉熵损失函数对象,因为手写字识别中一共有10个数字,输出会有10个结果
优化器,用于在训练神经网络时更新模型参数,目的是在神经网络训练过程中,自动调整模型的参数(权重和偏置),以最小化损失函数。
optimizer=torch.optim.Adam(model.parameters(),lr=0.01)#获取模型中所有需要训练的参数
模型训练
def train(dataloader,model,loss_fn,optimizer):model.train()batch_size_num=1for X,y in dataloader:X,y=X.to(device),y.to(device)pred=model.forward(X)#自动初始化 w权值loss=loss_fn(pred,y) #通过交叉熵损失函数计算损失值loss#更新模型参数以最小化损失函数。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+=1epochs=10for i in range(epochs):print(f'第{i}次训练')train(train_dataloader, model, loss_fn, optimizer)
模型测试
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()#进入到模型的测试状态,所有的卷积核权重被设为只读模式test_loss, correct = 0, 0with 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_batchescorrect /= sizeprint(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")test(test_dataloader,model,loss_fn)
得到结果如图