美文网首页
RNN、LSTM、GRU 网络使用教程 (二)

RNN、LSTM、GRU 网络使用教程 (二)

作者: 自由调优师_大废废 | 来源:发表于2019-12-26 16:04 被阅读0次
  • RNN实战-姓名分类 中介绍了最基础 RNN 网络结构的搭建,个人感觉对 RNN 网络结构的理解与使用远远不够,缺乏普遍性。
  • 本文将对 pytorch 中的 LSTM 和 GRU 网络结构的使用进行介绍,力求在代码逻辑层面达到普遍性。
  • 本文暂不涉及网络结构的数学理论基础,后续单独补。

Step1-数据处理

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

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

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

在本文中我们选取的是 手写数字识别的例子,这样一张图片就可以看作是一个长度为 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]

Step2 - 定义网络结构

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()

Step3 - 定义损失函数

 loss_func = nn.CrossEntropyLoss()

Step4 - 定义优化器

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

Step5 - 模型训练

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)

模型效果:


image.png

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

Step6 - 验证模型效果

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

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 好很多了。

LSTM ---> GRU

我们注意到对于 LSTM 来说,返回结果格式和 RNN 一样,也是两个部分 output 和 hidden。其实 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 的使用,对于算法的差异性,后续再补。

相关文章

网友评论

      本文标题:RNN、LSTM、GRU 网络使用教程 (二)

      本文链接:https://www.haomeiwen.com/subject/ijzcoctx.html