RNN for Image Classification(RNN图片分类–MNIST数据集)

  • A+
所属分类:深度学习
摘要这一篇会简单介绍一下使用RNN来实现MNIST数据集得图像的分类。简单看一下RNN的一个应用,和在Pytorch中如何来使用RNN。

简介

这里主要简单介绍一下使用Pytorch实现一个简单的RNN来做MNIST的手写数字的识别。主要参考文章链接 : Building RNNs is Fun with PyTorch and Google Colab

直接使用原文的话来介绍这个:

Let's try to build an image classifier using the MNIST dataset. The MNIST dataset consists of images that contain hand-written numbers from 1–10. Essentially, we want to build a classifier to predict the numbers displayed by a set of images. I know this sounds strange but you will be surprised by how well RNNs perform on this image classification task.

大概原理

我们将一张图片看成一个序列,如MNIST中28行看成28个序列, 每个序列有28个特征(如下图所示).

RNN for Image Classification(RNN图片分类--MNIST数据集)

使用最后一次的output, 后接全连接层, 用来做分类, 整体的框架如下图所示;

RNN for Image Classification(RNN图片分类--MNIST数据集)

可以注意到上图中输入的为x_0到x_27, 相当于依次输入图像的28行.

一个小技巧

我们在初始化网络的W_hh的时候,初始化为对角矩阵。在后面详细实现的地方还会有提到。在这里强调一下。

详细实现

导入需要使用的库

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. import torch.optim as optim
  5. import torchvision
  6. import torchvision.transforms as transforms
  7. import numpy as np
  8. import matplotlib.pyplot as plt

导入数据集

  1. # -------------
  2. # MNIST dataset
  3. # -------------
  4. batch_size = 128
  5. train_dataset = torchvision.datasets.MNIST(root='./',
  6.                                            train=True,
  7.                                            transform=transforms.ToTensor(),
  8.                                            download=True)
  9. test_dataset = torchvision.datasets.MNIST(root='./',
  10.                                           train=False,
  11.                                           transform=transforms.ToTensor())
  12. # Data loader
  13. train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
  14.                                            batch_size=batch_size,
  15.                                            shuffle=True)
  16. test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
  17.                                           batch_size=batch_size,
  18.                                           shuffle=False)

我们查看一下数据集中的数据。

  1. # ---------------------
  2. # Exploring the dataset
  3. # ---------------------
  4. # function to sbow an image
  5. def imshow(img):
  6.     npimg = img.numpy()
  7.     plt.imshow(np.transpose(npimg,(1,2,0)))
  8. # get some random training images
  9. dataiter = iter(train_loader)
  10. images,labels = dataiter.next()
  11. # show image
  12. imshow(torchvision.utils.make_grid(images,nrow=15))
RNN for Image Classification(RNN图片分类--MNIST数据集)

定义模型

  1. # ----------
  2. # parameters
  3. # ----------
  4. N_STEPS = 28
  5. N_INPUTS = 28 # 输入数据的维度
  6. N_NEURONS = 150 # RNN中间的特征的大小
  7. N_OUTPUT = 10 # 输出数据的维度(分类的个数)
  8. N_EPHOCS = 10 # epoch的大小
  9. N_LAYERS = 3
  10. # ------
  11. # models
  12. # ------
  13. class ImageRNN(nn.Module):
  14.     def __init__(self, batch_size, n_inputs, n_neurons, n_outputs, n_layers):
  15.         super(ImageRNN, self).__init__()
  16.         self.batch_size = batch_size # 输入的时候batch_size
  17.         self.n_inputs = n_inputs # 输入的维度
  18.         self.n_outputs = n_outputs # 分类的大小
  19.         self.n_neurons = n_neurons # RNN中输出的维度
  20.         self.n_layers = n_layers # RNN中的层数
  21.         self.basic_rnn = nn.RNN(self.n_inputs, self.n_neurons, num_layers=self.n_layers)
  22.         self.FC = nn.Linear(self.n_neurons, self.n_outputs)
  23.     def init_hidden(self):
  24.         # (num_layers, batch_size, n_neurons)
  25.         # initialize hidden weights with zero values
  26.         # 这个是net的memory, 初始化memory为0
  27.         return (torch.zeros(self.n_layers, self.batch_size, self.n_neurons).to(device))
  28.     def forward(self,x):
  29.         # transforms x to dimensions : n_step × batch_size × n_inputs
  30.         x = x.permute(1,0,2) # 需要把n_step放在第一个
  31.         self.batch_size = x.size(1) # 每次需要重新计算batch_size, 因为可能会出现不能完整方下一个batch的情况
  32.         self.hidden = self.init_hidden() # 初始化hidden state
  33.         rnn_out, self.hidden = self.basic_rnn(x,self.hidden) # 前向传播
  34.         out = self.FC(rnn_out[-1]) # 求出每一类的概率
  35.         return out.view(-1,self.n_outputs) # 最终输出大小 : batch_size X n_output

这里需要注意的是, 在model中需要每次重新计算batch_size, 因为可能会出现一个batch,在最后的时候,不是自己设置的batch_size, 会比batch_size要小。

其他也没什么要注意的地方,自己敲一遍就明白了。

测试数据

首先我们定义一下device, 方便之后使用gpu进行计算。

  1. # --------------------
  2. # Device configuration
  3. # --------------------
  4. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

在这里我们进行一遍前向传播,看一下model写得是否正确。

  1. # ------------------------------------
  2. # Test the model(输入一张图片查看输出)
  3. # ------------------------------------
  4. # 定义模型
  5. model = ImageRNN(batch_size, N_INPUTS, N_NEURONS, N_OUTPUT, N_LAYERS).to(device)
  6. # 初始化模型的weight
  7. model.basic_rnn.weight_hh_l0.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  8. model.basic_rnn.weight_hh_l1.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  9. model.basic_rnn.weight_hh_l2.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  10. # 定义数据
  11. dataiter = iter(train_loader)
  12. images, labels = dataiter.next()
  13. model.hidden = model.init_hidden()
  14. logits = model(images.view(-1,28,28).to(device))
  15. print(logits[0:2])
  16. """
  17. tensor([[-0.2846, -0.1503, -0.1593,  0.5478,  0.6827,  0.3489, -0.2989,  0.4575,
  18.          -0.2426, -0.0464],
  19.         [-0.6708, -0.3025, -0.0205,  0.2242,  0.8470,  0.2654, -0.0381,  0.6646,
  20.          -0.4479,  0.2523]], device='cuda:0', grad_fn=<SliceBackward>)
  21. """

训练模型

接下来就是模型得训练了,我们先定义loss函数和优化器。注意这里对系数进行了初始化,初始化为对角矩阵。

关于对角矩阵得使用,大致如下

  1. # 产生对角线是1的矩阵
  2. torch.eye(n=5, m=5, out=None)
  3. """
  4. tensor([[1., 0., 0., 0., 0.],
  5.         [0., 1., 0., 0., 0.],
  6.         [0., 0., 1., 0., 0.],
  7.         [0., 0., 0., 1., 0.],
  8.         [0., 0., 0., 0., 1.]])
  9. """

下面是训练前得一些准备工作。

  1. # --------
  2. # Training
  3. # --------
  4. model = ImageRNN(batch_size, N_INPUTS, N_NEURONS, N_OUTPUT, N_LAYERS).to(device)
  5. criterion = nn.CrossEntropyLoss()
  6. optimizer = optim.Adam(model.parameters(),lr=0.001)
  7. # 初始化模型的weight
  8. model.basic_rnn.weight_hh_l0.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  9. model.basic_rnn.weight_hh_l1.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  10. model.basic_rnn.weight_hh_l2.data = torch.eye(n=N_NEURONS, m=N_NEURONS, out=None).to(device)
  11. def get_accuracy(logit, target, batch_size):
  12.     """最后用来计算模型的准确率
  13.     """
  14.     corrects = (torch.max(logit, 1)[1].view(target.size()).data == target.data).sum()
  15.     accuracy = 100.0 * corrects/batch_size
  16.     return accuracy.item()

接下来就可以开始训练了。

  1. # ---------
  2. # 开始训练
  3. # ---------
  4. for epoch in range(N_EPHOCS):
  5.     train_running_loss = 0.0
  6.     train_acc = 0.0
  7.     model.train()
  8.     # trainging round
  9.     for i, data in enumerate(train_loader):
  10.         optimizer.zero_grad()
  11.         # reset hidden states
  12.         model.hidden = model.init_hidden()
  13.         # get inputs
  14.         inputs, labels = data
  15.         inputs = inputs.view(-1,28,28).to(device)
  16.         labels = labels.to(device)
  17.         # forward+backward+optimize
  18.         outputs = model(inputs)
  19.         loss = criterion(outputs, labels)
  20.         loss.backward()
  21.         optimizer.step()
  22.         train_running_loss = train_running_loss + loss.detach().item()
  23.         train_acc = train_acc + get_accuracy(outputs, labels, batch_size)
  24.     model.eval()
  25.     print('Epoch : {:0>2d} | Loss : {:<6.4f} | Train Accuracy : {:<6.2f}%'.format(epoch, train_running_loss/i, train_acc/i))
RNN for Image Classification(RNN图片分类--MNIST数据集)

评价模型, 计算测试集准确率

最后拿上面得模型,在测试集上进行测试,准确率还是挺高得。

  1. # ----------------------------------------
  2. # Computer accuracy on the testing dataset
  3. # ----------------------------------------
  4. test_acc = 0.0
  5. for i,data in enumerate(test_loader,0):
  6.     inputs, labels = data
  7.     labels = labels.to(device)
  8.     inputs = inputs.view(-1,28,28).to(device)
  9.     outputs = model(inputs)
  10.     thisBatchAcc = get_accuracy(outputs, labels, batch_size)
  11.     print("Batch:{:0>2d}, Accuracy : {:<6.4f}%".format(i,thisBatchAcc))
  12.     test_acc = test_acc + thisBatchAcc
  13. print('============平均准确率===========')
  14. print('Test Accuracy : {:<6.4f}%'.format(test_acc/i))
  15. """
  16. ============平均准确率===========
  17. Test Accuracy : 96.6026%
  18. """

 定义hook, 查看中间过程

这里额外使用一下hook, 来看一下模型的中间的过程。有助于对RNN的理解吧。

  1. # 定义hook
  2. class SaveFeatures():
  3.     """注册hook和移除hook
  4.     """
  5.     def __init__(self, module):
  6.         self.hook = module.register_forward_hook(self.hook_fn)
  7.     def hook_fn(self, module, input, output):
  8.         self.features = output
  9.     def close(self):
  10.         self.hook.remove()
  11. # 绑定到model上
  12. activations = SaveFeatures(model.basic_rnn)

进行网络的传播

  1. # 定义数据
  2. dataiter = iter(train_loader)
  3. images, labels = dataiter.next()
  4. # 前向传播
  5. model.hidden = model.init_hidden()
  6. logits = model(images.view(-1,28,28).to(device))
  7. activations.close() # 移除hook

查看一下中间输出数据的大小,可以理解一下为什么是28128150.

  1. # 这个是 28(step)*128(batch_size)*150(hidden_size)
  2. activations.features[0].shape
  3. # torch.Size([28, 128, 150])
  4. activations.features[0][-1].shape
  5. # torch.Size([128, 150])

 

  • 微信公众号
  • 关注微信公众号
  • weinxin
  • QQ群
  • 我们的QQ群号
  • weinxin
王 茂南

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: