深度学习RNNLSTMGRU 网络使用教程

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习RNNLSTMGRU 网络使用教程 相关的知识,希望对你有一定的参考价值。

前言

1. 数据处理

无论是 RNN、LSTM、GRU 的哪一种网络结构,它们对于输入都有统一的要求,输入的数据都是序列数据。格式必须是 (batch, time_step, input_size) 。

  • batch:该批次样本数,可以为1.
  • time_step: 样本的序列长度。(对于 pytorch, 不同样本之间,序列长度可以不相同,这点后面会说)
  • input_size: 样本每条序列的特征数目。(无论是样本还是序列之间,input_size 必须相同)

由于它对于输入数据格式的特殊性,个人感觉这也是使用 RNN 的一个难点,我们要想办法把我们的训练数据处理成 RNN 网络能够接收的格式。对于这点,会在后面的实战教程中具体操作,仅供参考。

RNN实战-姓名分类 实战中定义的RNN网络模型,输入的文本张量的one-hot表示形式。

class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, num_layers=1):
        """
        :param input_size:代表RNN输入的最后一个维度
        :param hidden_size:代表RNN隐藏层的最后一个维度
        :param output_size:代表RNN网络最后线性层的输出维度
        :param num_layers:代表RNN网络的层数
        """
        super(RNN, self).__init__()
        self._input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.num_layers = num_layers

        # 实例化预定义的RNN,三个参数分别是input_size,hidden_size,num_layers
        self.rnn = nn.RNN(input_size, hidden_size, num_layers)
        # 实例化全连接线形层,作用是将RNN的输出维度转化成指定的输出维度
        self.linear = nn.Linear(hidden_size, output_size)
        # 实例化nn中预定义的softmax层,用于从输出层中获得类别的结果
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input1, hidden):
        # input1:代表人名分类器中的输入张量,形状是 1 * n_letters
        # hidden:代表RNN的隐藏层张量,形状是 self.num_layers * 1 * self.hidden_size
        # 注意:输入到RNN中的张量要求是三维张量,所以要用unsqueeze()函数扩充维度
        input1 = input1.unsqueeze(0)
        # 将input1和hidden输入到RNN的实例化对象中,如果num_layers=1,rr恒等于hn
        rr, hn = self.rnn(input1, hidden)
        # 将从RNN中获得的结果通过线形层的变换和softmax层的处理,最终返回
        return self.softmax(self.linear(rr)), hn

    def initHidden(self):
        # 本函数的作用是用来初始化一个全零的隐藏层张量,维度是3
        return torch.zeros(self.num_layers, 1, self.hidden_size)

在本文中我们选取的是 手写数字识别的例子,这样一张图片就可以看作是一个长度为 28 的序列,每个序列的特征数是 28。(我们的图片是 28 * 28 )。

# 整个数据集上训练次数
EPOCH = 1
# 分批次训练
BATCH_SIZE = 64
# image height
TIME_STEP = 28
# image width
INPUT_SIZE = 28
LR = 0.01
DOWNLOAD_MNIST = False

train_data = dsets.MNIST(
    root='./mnist/',
    train=True,
    # 我们可以使用 transforms.ToTensor() 将 PIL.Image/numpy.ndarray 数据进转化为torch.FloadTensor,并且在训练的时候,归一化到[0, 1.0]
    transform=transforms.ToTensor(),
    download=DOWNLOAD_MNIST,
)
print(train_data.train_data.size()) 
print(train_data.train_labels.size())

# 分批次训练
train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# 这里测试数据我们只取前 2000
test_data = dsets.MNIST(root='./mnist/', train=False)
test_x = test_data.test_data.type(torch.FloatTensor)[:2000]/255. 
test_y = test_data.test_labels.numpy()[:2000]

2. 定义网络结构

class LSTM(nn.Module):
    def __init__(self):
        super(LSTM, self).__init__()
        self.rnn = nn.LSTM(
            input_size=INPUT_SIZE,
            hidden_size=128,
            num_layers=1,
            batch_first=True 
        )
        self.out = nn.Linear(128, 10)

    def forward(self, x):
        # None 表示 hidden state 会用全0的 state
        r_out, h_state = self.rnn(x, None)
        """ 因为是分类,这里我们只要最后一个预测结果 """
        out = self.out(r_out[:,-1,:]) 
        return out

 lstm = LSTM()

3. 定义损失函数

 loss_func = nn.CrossEntropyLoss()

4. 定义优化器

optimizer = torch.optim.Adam(rnn.parameters(), lr=LR) 

5. 模型训练

all_losses = [] 
for epoch in range(EPOCH):
    for step, (train_x, train_y) in enumerate(train_loader):
        # reshape x to (batch, time_step, input_size)
        train_x = train_x.view(-1, 28, 28)  

        output = lstm(train_x)

        loss = loss_func(output, train_y) 

        optimizer.zero_grad() 

        loss.backward() 

        optimizer.step() 
        
        all_losses.append(loss)

import matplotlib.pyplot as plt
%matplotlib inline

plt.figure()
plt.plot(all_losses)

模型效果:


可以看到,随着模型的迭代,loss 越来越小,说明模型的训练是有价值的。

6. 验证模型效果

模型训练完成之后,我们在测试集上验证下模型的准确率

test_output = lstm(test_x.view(-1, 28, 28))
pred_y = torch.max(test_output, 1)[1].data.numpy()
accuracy = float((pred_y == test_y).astype(int).sum()) / float(test_y.size)
print(accuracy)

最后模型的准确率为 0.962,对于 EPOCH 为 1 来说,这个准确率要比之前的 CNN 好很多了。

7. LSTM —> GRU

我们注意到对于 LSTM 来说,返回结果格式和 RNN 一样,也是两个部分 outputhidden。其实 LSTM 返回的 hidden 由两部分构成: hidden 0、hidden 1。了解 LSTM 原理的同学应该清楚,这里不在此篇介绍。 GRU 作为 LSTM 的一个变体。在我们的项目工程使用上几乎没有区别,只需要在 nn 模块调用 GRU 即可。

网络结构定义如下:

class GRU(nn.Module):
   def __init__(self):
       super(GRU, self).__init__()
       self.rnn = nn.GRU(
           input_size=INPUT_SIZE,
           hidden_size=128,
           num_layers=1,
           batch_first=True 
       )
       self.out = nn.Linear(128, 10)

   def forward(self, x):
       # None 表示 hidden state 会用全0的 state
       r_out, h_state = self.rnn(x, None)
       """ 因为是分类,这里我们只要最后一个预测结果 """
       out = self.out(r_out[:,-1,:]) 
       return out

gru = GRU()

之后的模型训练 和前面的 LSTM 完全一样,这里不再具体展示。

在相同条件下使用 GRU 替换 LSTM 训练之后模型的准确率为 0.952。当然通过一次简单的测试并不能说明模型的优劣,本文的重点也是在于介绍 LSTM 和 GRU 的使用,对于算法的差异性,后续再补。

原文链接Link


加油!

感谢!

努力!

以上是关于深度学习RNNLSTMGRU 网络使用教程 的主要内容,如果未能解决你的问题,请参考以下文章

深度学习RNNLSTMGRU 网络使用教程

深度学习RNNLSTMGRU 网络使用教程

自然语言处理入门实战——基于循环神经网络RNNLSTMGRU的文本分类(超级详细,学不会找我!!!)

深度学习保姆级教程(附代码+数据)

深度学习保姆级教程(附代码+数据)

循环神经网络(RNNLSTMGRU)