Pytorch实现RNN网络对MNIST字体分类

Posted 城南皮卡丘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pytorch实现RNN网络对MNIST字体分类相关的知识,希望对你有一定的参考价值。

 我们知道,循环神经网络RNN非常擅长处理序列数据,但它也可以用来处理图像数据,这是因为一张图像可以看作一组由很长的像素点组成的序列。下面将会使用RNN对MNIST数据集建立分类器。

目录

1.准备数据集、定义数据加载器

2.搭建RNN网络 

3.RNN网络的训练与预测


1.准备数据集、定义数据加载器

在进行数据准备工作时,可以直接从torchvision库的datasets模块导入MNIST手写字体的训练数据集和测试数据集,然后使用Data.DataLoader()函数将两个数据集定义为数据加载器,其中每个batch包含64张图像,最后得到训练集数据加载器train_loader与测试集数据加载器test_loader。在导入的数据集中,训练集包含60000张28×28的灰度图像,测试集包含10000张28×28的灰度图像。

#准备训练数据集
train_data=torchvision.datasets.MNIST(
    root="../Dataset",
    train=True,
    transform=transforms.ToTensor(),
    download=False
)
#定义一个数据加载器
train_loader=Data.DataLoader(
    dataset=train_data,
    batch_size=64,
    shuffle=True,
    num_workers=0
)
#准备测试数据集
test_data=torchvision.datasets.MNIST(
    root="../Dataset",
    train=False,
    transform=transforms.ToTensor(),
    download=False
)
#定义测试数据集的数据加载器
test_loader=Data.DataLoader(
    dataset=test_data,
    batch_size=64,
    shuffle=True,
    num_workers=0
)

2.搭建RNN网络 

下面程序定义了MyRnnMnistNet类,在调用时需要输入4个参数,参数input_dim表示输入数据的维度,针对图像分类器,其值是图片中每行的数据像素点量,针对手写字体数据其值等于28;参数hidden_dim表示构建RNN网络层中包含神经元的个数;参数layer_dim表示在RNN网络层中有多少层RNN神经元;参数output_dim则表示在使用全连接层进行分类时输出的维度,可以使用数据的类别数表示,MNIST数据集表示为10

#定义网络
class MyRnnMnistNet(nn.Module):
    def __init__(self,input_dim,hidden_dim,layer_dim,output_dim):
        # input_dim:输入数据的维度(图片每行的数据像素点)
        #hidden_dim:RNN神经元个数
        #layer_dim:RNN层数
        #output_dim:隐藏层输出的维度(分类的数量)
        super(MyRnnMnistNet,self).__init__()
        self.hidden_dim=hidden_dim
        self.layer_dim=layer_dim
        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):
        # x:[batch,time_step,input_dim]
        #time_step=图像所有像素数量 / input_size
        #out:[batch,time_step,output_size]
        out,h_n=self.rnn(x,None)#None表示h0会使用全0进行初始化
        #选取最后一个时间点的out输出
        out=self.fc1(out[:,-1,:])
        return out
#模型的调用
input_dim=28#图片每行的像素数量
hidden_dim=128#RNN神经元个数
layer_dim=1#RNN层数
output_dim=10#隐藏层输出的维度(10类图像)
MyRnnMnistNet=MyRnnMnistNet(input_dim,hidden_dim,layer_dim,output_dim)
#print(MyRnnMnistNet)

在MyRnnMnistNet类调用nn.RNN()函数时,参数batch_first=True表示使用的数据集中batch在数据的第一个维度,参数nonlinearity='relu'表示RNN层使用的激活函数为ReLU函数。从RNNimc类的forward()函数中,发现网络的self.rnn()层的输入有两个参数,第一个参数为需要分析的数据x,第二个参数则为初始的隐藏层输出,这里使用None代替,表示使用全0进行初始化。而输出则包含两个参数,其中out表示RNN最后一层的输出特征,h_n表示隐藏层的输出。在将RNN层和全连接分类层连接时,将全连接层网络作用于最后一个时间点的out输出。

3.RNN网络的训练与预测

对定义好的网络模型使用训练集进行训练,需要定义优化器和损失函数,优化器使用torch.optim.RMSprop()定义,损失函数则使用交叉嫡损失nn.CrossEntropyLoss()函数定义,并且使用训练集对网络训练30个epoch 

#对模型进行训练
optimizer=torch.optim.RMSprop(MyRnnMnistNet.parameters(),lr=0.0003)
criterion=nn.CrossEntropyLoss()#损失函数
train_loss_all=[]
train_acc_all=[]
test_loss_all=[]
test_acc_all=[]
num_epochs=30
for epoch in range(num_epochs):
    print("Epoch  / ".format(epoch,num_epochs-1))
    MyRnnMnistNet.train()
    corrects=0
    train_num=0
    for step,(b_x,b_y) in enumerate(train_loader):
        xdata=b_x.view(-1,28,28)
        output=MyRnnMnistNet(xdata)
        pre_lab=torch.argmax(output,1)
        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).detach().numpy())
    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]))
    #设置模型为验证模式
    MyRnnMnistNet.eval()
    corrects=0
    test_num=0
    for step,(b_x,b_y) in enumerate(test_loader):
        xdata=b_x.view(-1,28,28)
        output=MyRnnMnistNet(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).detach().numpy())
    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]))

在上面的程序中,每使用训练集对网络进行一轮训练,都会使用测试集来测试当前网络的分类效果,针对训练集和测试集的每个epoch损失和预测精度,都保存在train_loss_all、train_acc_all、test_loss_all、test_acc_all四个列表中。在每个epoch中使用MyRNNimc.train()将网络切换为训练模式,使用MyRnnMnistNet.eval()将网络切换为验证模式。针对网络的输入x,需要从图像数据集[batch, channel, height,width]转化为[batch, time_step, input_dim],即从[64,1,28,28]转化为[64,28,28]。在网络训练完毕后,将网络在训练集和测试集上的损失及预测精度使用折线图可视化,得到的图像如下图所示

#可视化模型训练过程
plt.figure(figsize=(14,5))
plt.subplot(1,2,1)
plt.plot(train_loss_all,"ro-",label="Train Loss")
plt.plot(test_loss_all,"bs-",label="Val Loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("Loss")
plt.subplot(1,2,2)
plt.plot(train_acc_all,"ro-",label="Train acc")
plt.plot(test_acc_all,"bs-",label="Val acc")
plt.xlabel("epoch")
plt.ylabel("acc")
plt.legend()
plt.show()

 上图中左图所示为每个epoch在训练集和测试集上的损失函数的变化情况,右图所示则为每个epoch在训练集和测试集上的预测精度的变化情况,精度最终稳定在0.97附近。可见使用RNN网络也能很好地对手写字体图像数据进行预测。

4.该案例的完整代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import time
import copy
import torch
from torch import nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torch.utils.data as Data
from  torchvision import transforms

#准备训练数据集
train_data=torchvision.datasets.MNIST(
    root="../Dataset",
    train=True,
    transform=transforms.ToTensor(),
    download=False
)
#定义一个数据加载器
train_loader=Data.DataLoader(
    dataset=train_data,
    batch_size=64,
    shuffle=True,
    num_workers=0
)
#准备测试数据集
test_data=torchvision.datasets.MNIST(
    root="../Dataset",
    train=False,
    transform=transforms.ToTensor(),
    download=False
)
#定义测试数据集的数据加载器
test_loader=Data.DataLoader(
    dataset=test_data,
    batch_size=64,
    shuffle=True,
    num_workers=0
)


#定义网络
class MyRnnMnistNet(nn.Module):
    def __init__(self,input_dim,hidden_dim,layer_dim,output_dim):
        # input_dim:输入数据的维度(图片每行的数据像素点)
        #hidden_dim:RNN神经元个数
        #layer_dim:RNN层数
        #output_dim:隐藏层输出的维度(分类的数量)
        super(MyRnnMnistNet,self).__init__()
        self.hidden_dim=hidden_dim
        self.layer_dim=layer_dim
        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):
        # x:[batch,time_step,input_dim]
        #time_step=图像所有像素数量 / input_size
        #out:[batch,time_step,output_size]
        out,h_n=self.rnn(x,None)#None表示h0会使用全0进行初始化
        #选取最后一个时间点的out输出
        out=self.fc1(out[:,-1,:])
        return out
#模型的调用
input_dim=28#图片每行的像素数量
hidden_dim=128#RNN神经元个数
layer_dim=1#RNN层数
output_dim=10#隐藏层输出的维度(10类图像)
MyRnnMnistNet=MyRnnMnistNet(input_dim,hidden_dim,layer_dim,output_dim)
#print(MyRnnMnistNet)
#可视化神经网络


#对模型进行训练
optimizer=torch.optim.RMSprop(MyRnnMnistNet.parameters(),lr=0.0003)
criterion=nn.CrossEntropyLoss()#损失函数
train_loss_all=[]
train_acc_all=[]
test_loss_all=[]
test_acc_all=[]
num_epochs=30
for epoch in range(num_epochs):
    print("Epoch  / ".format(epoch,num_epochs-1))
    MyRnnMnistNet.train()
    corrects=0
    train_num=0
    for step,(b_x,b_y) in enumerate(train_loader):
        xdata=b_x.view(-1,28,28)
        output=MyRnnMnistNet(xdata)
        pre_lab=torch.argmax(output,1)
        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).detach().numpy())
    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]))
    #设置模型为验证模式
    MyRnnMnistNet.eval()
    corrects=0
    test_num=0
    for step,(b_x,b_y) in enumerate(test_loader):
        xdata=b_x.view(-1,28,28)
        output=MyRnnMnistNet(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).detach().numpy())
    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]))
#可视化模型训练过程
plt.figure(figsize=(14,5))
plt.subplot(1,2,1)
plt.plot(train_loss_all,"ro-",label="Train Loss")
plt.plot(test_loss_all,"bs-",label="Val Loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("Loss")
plt.subplot(1,2,2)
plt.plot(train_acc_all,"ro-",label="Train acc")
plt.plot(test_acc_all,"bs-",label="Val acc")
plt.xlabel("epoch")
plt.ylabel("acc")
plt.legend()
plt.show()

基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络softmax)基础版

基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)基础版


文章目录


前言

本篇文章,将基于pytorch平台实现对MNIST数据集的分类分析,并分别以分类“的准确度”和“混淆矩阵”为衡量指标,分析模型的精度。


一、基于“前馈神经网络”模型,分类分析

注意:
根目录’E:\\深度学习’,在这里根据自己实际情况建立;
运行程序时将自动下载数据集,数据集将下载至先前建立的根目录中,用于训练模型及测试。

import torch
from torch import nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
import torchvision.datasets as dsets
import torchvision.transforms as transforms

batch_size = 100
# MNIST dataset
train_dataset = dsets.MNIST(root='E:\\深度学习', train=True, transform=transforms.ToTensor(), download=True)
test_dataset = dsets.MNIST(root='E:\\深度学习', train=False, transform=transforms.ToTensor(), download=True)
# load_data
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)



# original_data
print("train_data:", train_dataset.train_data.size())
print("train_labels:", train_dataset.train_labels.size())
print("test_data:", test_dataset.test_data.size())
print("test_labels:", test_dataset.test_labels.size())
# shuffle batch_size data
print("batch_size:", train_loader.batch_size)
print("load_train_data:", train_loader.dataset.train_data.shape)
print("load_train_labels:", train_loader.dataset.train_labels.shape)


input_size = 784
hidden_size = 500
num_classes = 10

# #定义神经网络模型
#一般把网络中具有可学习参数的层(如全连接层、卷积层等)放在构造函数__init__()中
class Neural_net(nn.Module):
    #最重要__init__初始化方法,便于一些参数的传递
    def __init__(self, input_num, hidden_size, output_num):
        super(Neural_net, self).__init__()#调用父类构造函数,以继承父类一些属性
        self.layers1 = nn.Linear(input_num, hidden_size)
        self.layers2 = nn.Linear(hidden_size, output_num)

    # 最重要的forward方法,便于进行前向传播
    def forward(self, x):
        out = self.layers1(x)
        out = torch.relu(out)
        out = self.layers2(out)
        return out
net = Neural_net(input_size, hidden_size, num_classes)
print(net)


# training
learning_rate = 1e-1
num_epoches = 5
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate)
for epoch in range(num_epoches):
    print("current epoch = ".format(epoch))
    for i, (images,labels) in enumerate(train_loader):
        images = Variable(images.view(-1, 28*28))
        labels = Variable(labels)

        outputs = net(images)
        loss = criterion(outputs, labels)  # calculate loss
        optimizer.zero_grad()  # clear net state before backward
        loss.backward()
        optimizer.step()   # update parameters

        if i%100 == 0:
            print("current loss = %.5f" %loss.item())


# prediction
total = 0
correct = 0
for images, labels in test_loader:
    images = Variable(images.view(-1, 28*28))
    labels = Variable(labels)
    outputs = net(images)

    _,predicts = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicts == labels).sum()
    from sklearn.metrics import confusion_matrix
    C2 = confusion_matrix(predicts, labels, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    import sklearn
    acc = sklearn.metrics.accuracy_score(labels, predicts)
print("混淆矩阵",C2)
print("混淆矩阵中的准确率为", acc)
print("Accuracy = %.2f" %(100*correct/total))

二、基于“softmax”模型,分类分析

import torch
import torch.utils.data as Data
from torchvision import datasets,transforms
import torchvision
from torch.autograd import  Variable
import numpy as np
import matplotlib.pyplot as plt

transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize(mean=[0.5,],std=[0.5,])])
data_train=datasets.MNIST(root="D:\\jupyter_data",  transform=transform, train=True,
                          download=True
                          )
data_test=datasets.MNIST(root="D:\\jupyter_data", transform=transform, train=False)


batch = 256
train_iter = Data.DataLoader(dataset=data_train, batch_size=batch, shuffle=True)
test_iter = Data.DataLoader(data_test,batch, shuffle=True)
print(train_iter)


#定义和初始化模型
for X,y in train_iter:
    print(X.shape)
    print(X.view(X.shape[0],-1).shape)
    break

num_inputs = 784
num_outputs = 10
class LinearNet(torch.nn.Module):
    def __init__(self,num_inputs,num_outputs):
        super(LinearNet,self).__init__()
        self.linear = torch.nn.Linear(num_inputs,num_outputs)
    def forword(self,X): #这里X是28行28列.
        y=self.linear(X.view(X.shape[0],-1))
        return y  #参数中的-1就代表这个位置由其他位置的数字来推断,根据上面得到的shape,一批是256个,所以这里的shape[0]是256
net = LinearNet(num_inputs, num_outputs)
torch.nn.init.normal_(net.linear.weight, mean=0, std=0.01)
torch.nn.init.constant_(net.linear.bias, val=0)

print(net)
for X,y in train_iter:
    y_pre = net.forword(X)
    print(y_pre.shape)
    print(y_pre.view(y_pre.shape[0],-1).shape)
    break
#定义损失函数
loss = torch.nn.CrossEntropyLoss()

#定义优化算法
optimizer = torch.optim.SGD(net.linear.parameters(),lr=0.1)

#训练模型
epoch_size = 3
def softmax_train(train_iter, test_iter, epoch_size, loss, optimizer, net, batchsize):
    for epoch in range(0, epoch_size+1):
        train_acc_count = 0
        train_loss = 0
        n = 0
        for X,y in train_iter:
            y_pre = net.forword(X)
            l = loss(y_pre, y).sum() #注意要加起来,这个是softmax+交叉熵的,之前的线性回归不用
            optimizer.zero_grad() #梯度清零
            l.backward()
            optimizer.step()
            #准确率
            train_acc_count += (y_pre.argmax(dim=1) == y).sum().item()
            train_loss += l.item() #
            n += y.shape[0]
        print('epoch %d,loss %.4f, train acc %.3f'% (epoch + 1, train_loss/n ,train_acc_count/n))
softmax_train(train_iter, test_iter, epoch_size, loss, optimizer, net, batch)

def test_acc(test_iter, net):
    test_acc_count = 0
    n = 0
    for X,y in test_iter:
        y_pre = net.forword(X)
        test_acc_count += (y_pre.argmax(dim=1) == y).sum().item()
        n += y.shape[0]
        #y = y.detach().numpy()
        #y_pre = y_pre.detach().numpy()
        from sklearn.metrics import confusion_matrix
        C2 = confusion_matrix(y_pre.argmax(dim=1), y, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
        import sklearn
        acc = sklearn.metrics.accuracy_score(y, y_pre.argmax(dim=1) )
    print("混淆矩阵", C2)
    print("混淆矩阵中的准确率为", acc)
    print('test acc %.3f'% (test_acc_count/n))


test_acc(test_iter, net)

总结

以上是基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)的“基础版”,升级版在下一篇博客基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)升级版

以上是关于Pytorch实现RNN网络对MNIST字体分类的主要内容,如果未能解决你的问题,请参考以下文章

pytorch学习-7:RNN 循环神经网络 (分类)

pytorch学习-7:RNN 循环神经网络 (分类)

利用knn svm cnn 逻辑回归 mlp rnn等方法实现mnist数据集分类(pytorch实现)

Pytorch Note38 RNN 做图像分类

基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络softmax)基础版

Pytorch Note25 深层神经网络实现 MNIST 手写数字分类