《详解 RNN 循环机制:短序列优势、长依赖问题及 LSTM 解决方案》

14 阅读4分钟

RNN 简介

RNN(Recurrent Neural Network,循环神经网络)一般以序列数据为输入,通过网络内部的结构设计有效捕捉序列之间的关系特征,一般也以序列形式输出。

RNN 的循环机制使模型隐层上一时间步产生的结果,能够作为当下时间步输入的一部分(当下时间步的输入除了正常的输入外还包括上一步的隐层输出)对当下时间步的输出产生影响。

  • 结构:三层(输入层、隐藏层、输出层;循环发生在隐藏层)

image.png

1.1 RNN 模型的作用

因为 RNN 结构能够很好利用序列之间的关系,因此针对自然界具有连续性的输入序列,如人类的语言、语音等进行很好处理,广泛应用于 NLP(自然语言处理)领域的各项任务,如文本分类、情感分析、意图识别、机器翻译等。

语言处理示例

image2.png

2.1 PyTorch 中传统 RNN 的使用

位置:在 torch.nn 中,通过 torch.nn.RNN 可调用。

import torch
import torch.nn as nn

rnn = nn.RNN(5, 6, 2)  # 实例化 rnn 对象
# 参数1:输入张量 x 的维度 - input_size
# 参数2:隐藏层的维度(隐藏层神经元个数)- hidden_size
# 参数3:隐藏层的层数 - num_layers

# torch.randn - 随机产生正态分布的随机数
input1 = torch.randn(1, 3, 5)  # 设定输入张量 x - 序列长 1,批次 3,维度 5
# 参数1:输入序列长度 - sequence_length
# 参数2:批次的样本 - batch_size(表示:3 个样本)
# 参数3:输入张量 x 的维度 - input_size

h0 = torch.randn(2, 3, 6)  # 设定初始化的 h0
# 第一个参数:num_layers * num_directions(层数 * 网络方向数(1 或 2))
# 第二个参数:batch_size(批次的样本数)
# 第三个参数:hidden_size(隐藏层的维度)

output, hn = rnn(input1, h0)
# 最后输出和最后一层的隐藏层输出

print(output)
print(output.shape)
print(hn)
print(hn.shape)

1.2 RNN的局限:长期依赖(Long-TermDependencies)问题

RNN的关键点之一就是他们可以用来连接先前的信息到当前的任务上,例如使用过去的视频段来推测对当前段的理解。如果RNN可以做到这个,他们就变得非常有用。但是真的可以么?答案是,还有很多依赖因素。

有时候,我们仅仅需要知道先前的信息来执行当前的任务。例如,我们有一个语言模型用来基于先前的词来预测下一个词。如果我们试着预测这句话中“the clouds are in the sky”最后的这个词“sky”,我们并不再需要其他的信息,因为很显然下一个词应该是sky。在这样的场景中,相关的信息和预测的词位置之间的间隔是非常小的,RNN可以学会使用先前的信息。

1.2 传统 RNN 优缺点

  • 优势:内部结构简单,对计算资源要求低;相较 LSTM/GRU 参数总量更少;在短序列任务上性能与效果表现优异。
  • 缺点:在长序列关联上表现较差;反向传播时易发生梯度消失或爆炸。

NaN 值(Not a Number,非数):是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。

2.1 LSTM 模型简介

Long ShortTerm 网络——一般就叫做LSTM——是一种RNN特殊的类型,可以学习长期依赖信息。当然,LSTM和基线RNN并没有特别大的结构不同,但是它们用了不同的函数来计算隐状态。

LSTM的“记忆”我们叫做细胞/cells,你可以直接把它们想做黑盒,这个黑盒的输入为前状态和当前输入。这些“细胞”会决定哪些之前的信息和状态需要保留/记住,而哪些要被抹去。实际的应用中发现,这种方式可以有效地保存很长时间之前的关联信息。

image3.png

2.2 PyTorch 中 LSTM 的使用

import torch
import torch.nn as nn

lstm = nn.LSTM(5, 6, 2)  # 实例化 lstm 对象
# 参数1:输入张量 x 的维度 - input_size
# 参数2:隐藏层的维度(隐藏层神经元个数)- hidden_size
# 参数3:隐藏层的层数 - num_layers

input1 = torch.randn(1, 3, 5)  # 设定输入张量 x - 序列长 1,批次 3,维度 5
# 参数1:输入序列长度 - sequence_length
# 参数2:批次的样本 - batch_size
# 参数3:输入张量 x 的维度 - input_size

h0 = torch.randn(2, 3, 6)  # 设定初始化的 h0(隐藏层)
c0 = torch.randn(2, 3, 6)  # 设定初始化的 c0(细胞状态)
# 第一个参数:num_layers * num_directions(层数 * 网络方向数(1 或 2))
# 第二个参数:batch_size(批次的样本数)
# 第三个参数:hidden_size(隐藏层的维度)

output, (hn, cn) = lstm(input1, (h0, c0))
# 最后输出和最后一层的隐藏层输出

print(output)
print(output.shape)
print(hn)
print(hn.shape)
print(cn)
print(cn.shape)