【NLP】RNN实现识别MNIST数据集(Pytorch)

0.前言

        两年前开始接触深度学习,一直到现在都是处于一种又懂又懵的状态。学得一知半解了,准备做遥感图像分割,又因为种种原因,转成NLP做文本处理。Anyway,学什么不是学。

        从此文开始,开始记录自己整个完整的学习过程,包括我学习的内容,我的理解以及我的不解,从零到项目完结。

1.循环神经网络(RNN)

        如上图所示,是典型的RNN结构,对此,我的理解为,其相较于其它网络而言,多了一个隐藏状态,yt时刻的输出,是由ht-1时刻数据与xt-1时刻输入共同决定的。

2.RNN应用于MNIST识别

        这里提一句,一般RNN都是用于处理文本数据或者序列数据,为什么这里可以用来处理图像,是因为一张图像可以看作一组由很长的像素点组成的序列。

2.1导入所需要的包

##导入所需的包
import torch
from torch import nn
import torchvision
import torch.utils.data as Data
from torchvision import transforms

2.2准备数据

        这里我们直接利用torchvision.datasets加载MNIST数据集,加载训练集就将train设置为True,训练集则为False;如果是第一次加载数据集请设置download为True。

        定义一个数据加载器,用于按设定好的批量(batch_size)读取MNIST数据,shuffle是问是否打乱来选取数据,num_workers是问用几个进程来读取,这边建议填0(因为我填2报错了)。

train_data = torchvision.datasets.MNIST(root="./data/MNIST",train=True,transform=transforms.ToTensor(),download=True)
train_loader = Data.DataLoader(dataset=train_data,batch_size=64,shuffle=True,num_workers=0)
test_data = torchvision.datasets.MNIST(root="./data/MNIST",train=False,transform=transforms.ToTensor(),download=True)
test_loader = Data.DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0)

2.3搭建RNN分类器

        首先我们搭建一个我们自己的RNNimc分类器,这个类继承了nn.Module这个父类,对此,我的理解是,继承了父类,很多父类定义的方法,属性在我们自己的类上也可以使用,比如初始化参数。这个类里需要我们输入的参数有四个:input_dim(输入的维度,图片中每行的数据像素点量),hidden_dim(隐藏状态的维度),layer_dim(RNN的层数),output_dim(输出的维度,在这里也就是要分成几类)。

        整个模型包括两层:RNN层和全连接层。

class RNNimc(nn.Module):
    def __init__(self,input_dim,hidden_dim,layer_dim,output_dim):
        super(RNNimc,self).__init__() #对继承自nn.Module的属性进行初始化,并且用nn.Module的初始化方法初始化继承的属性
        self.hidden_dim = hidden_dim
        self.layer_dim = layer_dim
        ##RNN
        self.rnn = nn.RNN(input_dim,hidden_dim,layer_dim,batch_first=True,nonlinearity="relu")
        ##全连接层
        self.fc1 = nn.Linear(hidden_dim,output_dim)
    def forward(self,x):
        out,h_n = self.rnn(x,None) #None:初始的隐藏层输出为0
        out = self.fc1(out[:,-1,:])
        return out

2.4模型实例化

        设置好我们之前设定好的四个参数的值,实例化RNNimc。

input_dim = 28 #因为MNIST数据集里的图片是28×28的图,所以,输入为28,相当于每次的输入是图片的一行
hidden_dim = 128 #这个就随意了吧
layer_dim = 1 #模型的层数(除开最后的全连接层)
output_dim = 10 #因为最后识别的是0-9这10种数字,所以相当于分为10类
MyRNNimc = RNNimc(input_dim,hidden_dim,layer_dim,output_dim) #将上述参数传入RNNimc完成实例化

        实例化后,我们可以打印出来看一下。RNN层包含128个神经元,而全连接层也包含128个神经元,输出为10。

2.5训练模型

        2.5.1前期准备

        1.准备优化器,这里选用RMSprop,传入的是我们模型可学习的一些参数以及学习率。

        2.准备损失函数,用于每次计算损失后更新我们的参数,这里选用交叉熵损失。

        3.准备几个空列表用于存储训练集和测试级的损失和准确率。

        4.设定好num_epochs,也就是我们需要训练几遍整个数据,我这里设定是20。

optimizer = torch.optim.RMSprop(MyRNNimc.parameters(),lr=0.0003) #优化器的选择
criterion = nn.CrossEntropyLoss() #使用交叉熵损失更新参数
train_loss_all = []
train_acc_all = []
test_loss_all = []
test_acc_all = []
num_epochs = 20

        2.5.2正式训练模型

        在一次训练里,将我们的模型设置为训练模式,从之前设置好的数据加载器train_loader里每次拿出b_x(输入)和b_y(标签),然后就是一套熟悉的操作,计算损失,再根据损失计算梯度,用优化器优化参数,然后将损失和精度累加,累加后除以训练的样本大小就得到了经过一个epoch训练后在训练集上的损失和精度。

(这部分我大概懂在做一件什么事,但是细看代码,还是有很多细节不太懂,懂的我都写了注释,当然也可能理解有错,剩下的关于损失和训练次数的累计还是有点麻,最后反正就返回了一次epoch的损失和精度。)

for epoch in range(num_epochs):
    print("Epoch{}/{}".format(epoch,num_epochs - 1))
    MyRNNimc.train() ##设置模型为训练模式
    corrects = 0
    train_num = 0
    for step,(b_x,b_y) in enumerate(train_loader):
        xdata = b_x.view(-1,28,28) #将我们的输入reshape,方便传入网络
        output = MyRNNimc(xdata) #输出为x通过网络的结果
        pre_lab = torch.argmax(output,dim=1) #找出这个一维向量(按行)里面最大值的索引。可以理解为,输出是一个1×10的向量,每个数值都是其是0-9数字的概率,找到概率最大的就是其预测的值
        loss = criterion(output,b_y) #计算输出与真实值的损失函数
        optimizer.zero_grad() 
        loss.backward()
        optimizer.step()
        loss += loss.item()*b_x.size(0)
        corrects += torch.sum(pre_lab == b_y.data)
        train_num += b_x.size(0)
    ##计算经过一个epoch的训练后在训练集上的损失和精度
    train_loss_all.append(loss/train_num)
    train_acc_all.append(corrects.double().item()/train_num)
    print("{} Train Loss:{:.4f} Train Acc:{:.4f}".format(epoch,train_loss_all[-1],train_acc_all[-1]))

       2.5.3在测试集上验证训练好的模型

        将我们的模型设置为验证模式,依次从test_loader里取出b_x和b_y,在验证的时候我们就不再利用优化器进行优化,而是直接用我们的模型对输入进行预测后与标签对比计算损失就可以了。用相同的方法累计我们的损失和精度,然后求平均得到我们一次epoch在训练集上的损失和精度。

##设置为验证模式
    MyRNNimc.eval()
    corrects = 0
    test_num = 0
    for step,(b_x,b_y) in enumerate(test_loader):
        xdata = b_x.view(-1,28,28)
        output = MyRNNimc(xdata)
        pre_lab = torch.argmax(output,1)
        loss = criterion(output,b_y) #计算输出与真实值的损失函数
        loss += loss.item()*b_x.size(0)
        corrects += torch.sum(pre_lab == b_y.data)
        test_num += b_x.size(0)
    ##计算经过一个epoch的训练后在训练集上的损失和精度
    test_loss_all.append(loss / test_num)
    test_acc_all.append(corrects.double().item() / test_num)
    print("{} Test Loss:{:.4f} Test Acc:{:.4f}".format(epoch, test_loss_all[-1], test_acc_all[-1]))

        2.5.4训练过程及结果

        首先打印的是:当前的epoch/总的epoch次数。

        每次epoch打印一次在训练集上的损失和精度、测试集上的损失和精度。

        

         可以看到前三次epoch无论是在训练集还是测试集上,其精度都低于95%

        

        再看看最后三次,可以看出,随着训练次数的增多,损失都快接近于0了,精度也都提高到了97%。书上num_epoch设置为30次,其得到的精度比我的更高一点,几乎稳定在98%左右。 

3.总结

        ok,到这里也算差不多结束了。另外该教程中还有两个可视化的写法,我这里就没有再提及,我也没有试过(问就是,试过,但是好像因为版本不对要报错,我就懒得调试了,后面有时间和需求再做吧)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值