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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容