pytorch手写数字识别【源码实现-小清新版】

cover

引言

手写数字识别,也就是让机器能够习得图片中的手写数字,并能正确归类。


手写数字

本文使用 pytorch 搭建一个简单的神经网络,实现手写数字的识别,
从本文,你可了解到:
1、搭建神经网络的流程
2、完成手写数字识别模型
3、pytorch基本库

1.准备数据

''' 1. 导人必要的模块 '''
import numpy as np
import torch
# 导入 pytorch 内置的 mnist 数据
from torchvision.datasets import mnist 
#导入预处理模块
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
#导入nn及优化器
import torch.nn.functional as F
import torch.optim as optim
from torch import nn

其中,torch.nn 是 pytorch 中重要的神经网络高级封装,其封装了:常见的网络层,如:卷积,以及优化器等。

我们这里使用 mnist 数据集,其里面就包括了 手写数字识别的数据集。
transforms 和 DataLoader 主要用来做数据的下载和预处理。
torch.optim 是我们使用的优化器

下面我们定义一些超参数:

''' 2. 定义一些超参数 '''
train_batch_size = 64     # 训练批次
test_batch_size = 128    # 测试批次
learning_rate = 0.01       # 学习率
num_epoches = 20        
lr = 0.01
momentum = 0.5

接下来,下载 mnist 数据集,并封装到 DataLoader 中:

''' 3. 下载数据并对数据进行预处理 '''
#定义预处理函数,这些预处理依次放在Compose函数中。
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5], [0.5])])
#下载数据,并对数据进行预处理
train_dataset = mnist.MNIST('/Users/zhouzhan/Documents/to_github/NLPLearn/Deep-Learning/data', train=True, transform=transform, download=True)
test_dataset = mnist.MNIST('/Users/zhouzhan/Documents/to_github/NLPLearn/Deep-Learning/data', train=False, transform=transform)
#dataloader是一个可迭代对象,可以使用迭代器一样使用。
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

2.可视化数据源

下载成功后,我们看一下数据集长什么样:

import matplotlib.pyplot as plt
%matplotlib inline
 
examples = enumerate(test_loader)
batch_idx, (example_data, example_targets) = next(examples)
 
fig = plt.figure()
for i in range(6):
  plt.subplot(2,3,i+1)
  plt.tight_layout()
  plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
  plt.title("Ground Truth: {}".format(example_targets[i]))
  plt.xticks([])
  plt.yticks([])

运行结果:


image.png

3.构建模型

构建模型,即:构建神经网络模型
其搭建神经网络所需组件:

  1. 层:神经网络的层级
  2. 模型:层构成的网络
  3. 损失函数:学习过程中的目标函数,即:损失函数最小化
  4. 优化器:如何使损失函数最小化的方法

首先,是构建网络模型,模型是由层构成的网络,我们的模型有2个隐藏层,且每层都含有一个激活函数 ReLU,最后使用 torch.max(out,1) 找出张量 out 最大值对应索引作为预测值:


神经网络结构图.png

代码如下:

''' 1. 构建网络 '''
class Net(nn.Module):
    """
    使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
    """
    def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
        super(Net, self).__init__()
        # 第一层网络
        self.layer1 = nn.Sequential(nn.Linear(in_dim, n_hidden_1),nn.BatchNorm1d(n_hidden_1))
        # 第二层网络
        self.layer2 = nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2),nn.BatchNorm1d(n_hidden_2))
        # 输出层
        self.layer3 = nn.Sequential(nn.Linear(n_hidden_2, out_dim))
        
    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        x = self.layer3(x)
        return x

我们定义了一个 class Net,它继承 nn.Module 类(它是所有网络的基类)
nn.Module 类里面定义了很多模型,如:卷积层、全连接层、池化层等,一般定义网络都需基层该类。
其中,__ init __ 方法,用于定义网络;forward 方法,实现前向传播。

forward函数:任务是把输入层、网络层、输出层链接起来,实现信息的前向传导。

nn.Sequential:一个有序的容器,它可将神经网络模块依次添加到计算图中执行。

实例化网络:

''' 2. 实例化网络 '''
#检测是否有可用的GPU,有则使用,否则使用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#实例化网络
model = Net(28 * 28, 300, 100, 10)
model.to(device)

最后,我们定义损失函数和优化器,则网络构造完毕:

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum

criterion: 交叉熵损失
optimizer:SGD优化器(随机梯度下降算法)

4.训练模型

训练模型的代码有点长,
根据之前设置的 num_epoches 进行多次训练和预测,
其目的是通过训练,学习到适合的参数,动态修改学习率

其步骤如下:

  1. 动态修改学习率;
  2. model.train(),将模型设置为训练模式;
  3. 前向传播;
  4. 反向传播;
  5. 计算训练误差;
  6. model.eval(),将模型设置为预测模式;
  7. 预测模型;
  8. 计算预测误差。

根据以上步骤,其代码实现如下:

''' 1. 训练模型 '''
# 开始训练
losses = []
acces = []
eval_losses = []
eval_acces = []


for epoch in range(num_epoches):
   train_loss = 0
   train_acc = 0
   model.train()
   #动态修改参数学习率
   if epoch%5==0:
       optimizer.param_groups[0]['lr']*=0.1
   for img, label in train_loader:
       img=img.to(device)
       label = label.to(device)
       img = img.view(img.size(0), -1)
       # 前向传播
       out = model(img)
       loss = criterion(out, label)
       # 反向传播
       optimizer.zero_grad()
       loss.backward()
       optimizer.step()
       # 记录误差
       train_loss += loss.item()
       # 计算分类的准确率
       _, pred = out.max(1)
       num_correct = (pred == label).sum().item()
       acc = num_correct / img.shape[0]
       train_acc += acc
       
   losses.append(train_loss / len(train_loader))
   acces.append(train_acc / len(train_loader))
   # 在测试集上检验效果
   eval_loss = 0
   eval_acc = 0
   # 将模型改为预测模式
   model.eval()
   for img, label in test_loader:
       img=img.to(device)
       label = label.to(device)
       img = img.view(img.size(0), -1)
       out = model(img)
       loss = criterion(out, label)
       # 记录误差
       eval_loss += loss.item()
       # 记录准确率
       _, pred = out.max(1)
       num_correct = (pred == label).sum().item()
       acc = num_correct / img.shape[0]
       eval_acc += acc
       
   eval_losses.append(eval_loss / len(test_loader))
   eval_acces.append(eval_acc / len(test_loader))
   print('epoch: {}, Train Loss: {:.4f}, Train Acc: {:.4f}, Test Loss: {:.4f}, Test Acc: {:.4f}'
         .format(epoch, train_loss / len(train_loader), train_acc / len(train_loader), 
                    eval_loss / len(test_loader), eval_acc / len(test_loader)))

下面进行关键代码解释:
前向传播,就是将图片输入到模型中,得出结果,并计算出损失值

# 前向传播
out = model(img)
loss = criterion(out, label)

反向传播
zero_grad():将梯度清零,因为缺省情况下梯度是累加的,所以需要手动清零;
backward():自动生成梯度;
step():执行优化器,把梯度传播回每个网络。

# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()

最后的结果:


结果图

我们看看损失函数:

''' 2. 可视化训练及测试损失值 '''
plt.title('train loss')
plt.plot(np.arange(len(losses)), losses)
plt.legend(['Train Loss'], loc='upper right')
损失函数可视化

5.总结

从这个例子中,我们学到了:

  1. mnist 数据集 是一个不错的入门学习数据集;
  2. 数据集的下载与预处理,可使用:transforms 和 DataLoader 来完成;
  3. 使用 matplotlib.pyplot 来做可视化操作;
  4. 通过继承 nn.Module 来构建神经网络模型;
  5. 训练模型,最后得出结果


    end

最后,你可以根据本文,自己手写代码完成,学习效果更佳哦~

你可能感兴趣的:(pytorch手写数字识别【源码实现-小清新版】)