AI练手系列(三)—— RNN实现mnist手写数字识别

mnist数据集作为一个经典的数据集,不少机器学习深度学习的初学者都对其下过手,它干净的数据集、训练难度也不是很高,作为初学练手,很适合了。今天我也来拜会一下这位经典大哥,看看到底感觉如何。

本次训练使用RNN的网络模型,其实其他模型也可以,但是这两天不是正好温习RNN嘛,就刚好学以致用嘛。

请套路先生上场吧:

1.导包定参

当你新建完一个python文件不管是py文件还是ipynb文件,不管会不会,先把常用的包导入进来再说,常定义几个超参数也请上来:

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt 
from torch.utils.data import DataLoader

 #定义超参数
EPOCH = 10
BATCH_SIZE = 64
LR = 0.001
TIME_STEP = 28
INPUT_SIZE = 28

2.数据加载

先把数据加载进来,虽然mnist数据集可以在线下载,但可能比较慢,我更喜欢先把数据下载到本地再加载进来,这一块的处理和cifar-10那篇就很像了。可以去回顾一下:

AI练手系列(一)—— 利用Pytorch训练CIFAR-10数据集

#数据加载
train_data = datasets.MNIST (root='./mnist',train=True,transform=transforms.ToTensor(),download=True )
test_data = datasets.MNIST(root='./mnist',train=False,
                              transform=transforms.ToTensor(),download=True )
                            
print(train_data)
print(test_data)

我们把数据的一些特征打印出来看一眼:

Dataset MNIST

Number of datapoints: 60000

Root location: ./mnist

Split: Train

StandardTransform

Transform: ToTensor()

Dataset MNIST

Number of datapoints: 10000

Root location: ./mnist

Split: Test

StandardTransform

Transform: ToTensor()

可以看到训练集60000个样本,测试集10000个,现在已经都被我们转为tensor格式了。

下面该干嘛了,数据预处理哈,归一化、分批啥的都可以搞起了。

3.数据预处理

图片数据可视化

数据是图像的话我比较喜欢先搞一张出来看看:

plt.imshow(train_data.train_data[0].numpy(),cmap='gray')
plt.show()

可以看到:

image-20200716124631808

这一下就直观多了,我们继续:

数据归一化

test_x = test_data.test_data.type(torch.FloatTensor)[:2000]/255#归一化
test_y = test_data.test_labels.numpy()[:2000]

数据分批

数据分批的套路和之前cifar-10的处理也基本一致:

#数据分批
train_loader = DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True)
test_loader = DataLoader(dataset=test_data,batch_size=BATCH_SIZE,shuffle=True)

#了解一下数据特征,图片是28*28个像素
#print(type(train_loader))
dataiter= iter(train_loader)
imgs,labs = next(dataiter)
print(imgs)#imgs.size()=torch.Size([64, 1, 28, 28])
print(labs)#图片的标签,就是类别,mnist一共有10个类别,故其取值分布在[0,9]之间

4.定义网络模型

#定义网络
class RNN(nn.Module):
    def __init__(self):
        super(RNN,self).__init__()
        #输入是两个月的数据input_size=2,输出是下一个月的数据output_size=1,中间隐藏层的层数任意指定,指定为4
        input_size,hidden_size,output_size = INPUT_SIZE,64,10
        num_layers = 1
        #self.rnn = nn.RNN(input_size,hidden_size,num_layers,batch_first=True)
        self.rnn = nn.LSTM(input_size,hidden_size,num_layers,batch_first=True)
        #self.rnn = nn.GRU(input_size,hidden_size,num_layers,batch_first=True)
        self.out = nn.Linear(hidden_size,output_size)
    def forward(self,x):
        r_out,_ = self.rnn(x)
        out = self.out(r_out[:,-1,:])
        #b,s,h = x.shape #batch,seq,hidden
       # x = x.view(b*s,h)#转化为线性层的输入
       # x = self.out(x)
        #x = x.view(b,s,-1)#(99, 1, 1)
        return out

5.设置使用GPU

#设置使用GPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

#使用定义好的RNN
model = RNN()
#模型和输入数据都需要to device
mode  = model.to(device)

6.定义损失函数及优化器

#定义损失函数,分类问题使用交叉信息熵
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr= LR)

7.模型训练与测试

模型训练一般包含几个步骤:

  1. 取出数据及标签并均送入GPU或CPU
  2. 前向传播
  3. 计算损失函数
  4. 清空上一轮梯度
  5. 反向传播
  6. 参数更新
  7. 周期打印loss值或tensorboard数据保存绘图
  8. 保存模型参数(非必要)
  9. 在训练的同时进行测试,得到准确率(可单独进行)
#开始训练
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('cifar-10')
for epoch in range(EPOCH):
    #60000/BATCH_SIZE
    for step,data in enumerate(train_loader):
        #取出数据及标签
        inputs,labels = data
        inputs = inputs.view(-1,28,28)
        #数据及标签均送入GPU或CPU
        inputs,labels = inputs.to(device),labels.to(device)
        
        #前向传播
        outputs = model(inputs)
        #计算损失函数
        loss = criterion(outputs,labels)
        #清空上一轮的梯度
        optimizer.zero_grad()
        
        #反向传播
        loss.backward()
        #参数更新
        optimizer.step()
        #测试,计算准确率
        if  step%100 == 0:
            writer.add_scalar("Train/Loss", loss.item(), epoch*len(train_loader)+step)

            test_outputs = model(test_x.cuda())
            pred_y = torch.max(test_outputs,1)[1].data.cpu().numpy()
            accuracy = float((pred_y == test_y).astype(int).sum())/ float(test_y.size)
            print ('Epoch: {}, Step: {}, loss: {}, accuracy: {}'.format(epoch, step, loss.item(), accuracy))
            writer.add_scalar("Accuracy", 100.0*accuracy, epoch*len(train_loader)+step)
    #print('epoch{} loss:{:.4f}'.format(epoch+1,loss.item()))

我们EPOCH=10时,看一下开头和结尾的记录:

Epoch: 0, Step: 0, loss: 2.3028504848480225, accuracy: 0.092

Epoch: 0, Step: 100, loss: 1.3167051076889038, accuracy: 0.549

Epoch: 0, Step: 200, loss: 0.7903525829315186, accuracy: 0.743

Epoch: 0, Step: 300, loss: 0.6392905712127686, accuracy: 0.806

Epoch: 0, Step: 400, loss: 0.47715359926223755, accuracy: 0.8445

Epoch: 0, Step: 500, loss: 0.3677961826324463, accuracy: 0.827

Epoch: 0, Step: 600, loss: 0.4239515960216522, accuracy: 0.8695

Epoch: 0, Step: 700, loss: 0.10473807901144028, accuracy: 0.891

Epoch: 0, Step: 800, loss: 0.28696632385253906, accuracy: 0.9055

Epoch: 0, Step: 900, loss: 0.09985525161027908, accuracy: 0.904

可以发现第一轮训练模型的loss就收敛的很快了,准确率迅速上升;

再看最后一轮:

Epoch: 9, Step: 0, loss: 0.029277682304382324, accuracy: 0.9715

Epoch: 9, Step: 100, loss: 0.028733856976032257, accuracy: 0.9735

Epoch: 9, Step: 200, loss: 0.045807115733623505, accuracy: 0.9715

Epoch: 9, Step: 300, loss: 0.09034416824579239, accuracy: 0.973

Epoch: 9, Step: 400, loss: 0.022646501660346985, accuracy: 0.97

Epoch: 9, Step: 500, loss: 0.01956302672624588, accuracy: 0.968

Epoch: 9, Step: 600, loss: 0.19199049472808838, accuracy: 0.974

Epoch: 9, Step: 700, loss: 0.1159246563911438, accuracy: 0.97

Epoch: 9, Step: 800, loss: 0.03719437122344971, accuracy: 0.9655

Epoch: 9, Step: 900, loss: 0.05097261443734169, accuracy: 0.9715

最后会有一点反复了,我们使用了tensorboard,可以对训练过程进行可视化:

image-20200630191115988

image-20200630191149596

效果还行,但还可以优化。

8.模型验证

按上面的结果,我们可以找几个样本来验证一下:

# 从测试集选出30个,进行验证
test_x = test_x.cuda()
test_output = model(test_x[:30].view(-1, 28, 28))
pred_y = torch.max(test_output, 1)[1].data.cpu().numpy()
print('预测数字', pred_y)
print('实际数字', test_y[:30])

看一下结果:

预测数字 [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 8 4 9 6 6 5 4 0 7 4 0 1]

实际数字 [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4 9 6 6 5 4 0 7 4 0 1]

对比看一下,准确率还是挺高的。模型还可以继续优化,包括可以试一下RNN和GRU,包括调整一下超参,都是顺手一改的事,这里就不再赘述了。


小小总结

经典数据集的训练总能让人收益很多,首先它能让你对整个训练过程更加熟悉,而且由于网上有大量前人的笔记,这对初学者来说也是很友好的,拿这样的小作业练手,能让我们对框架使用、模型特性、原理技巧都会有更深的理解。

加油,跑起来,就有了风。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值