【RNN】深入浅出讲解循环神经网络(介绍、原理)
本文介绍了循环神经网络(RNN)的背景、原理和实现方法。RNN通过引入记忆能力,能够处理序列数据,其核心在于将当前输入与前一个时间步的隐藏状态进行关联。文章详细解释了RNN的结构,包括输入层、隐藏层和输出层,以及其计算流程。此外,还介绍了如何使用PyTorch的RNN类进行实现,包括构造函数的参数设置和示例代码。文章强调了RNN在处理时序数据中的优势,并指出了其局限性,如梯度消失和爆炸问题,同时提到了解决方法如LSTM和GRU等后续改进方法。

本文被收录至《深入浅出讲解自然语言处理》专栏,该专栏致力于呈现自然语言处理领域的经典算法,将定期更新,诚挚邀请您订阅此优质内容。

个人主页:有梦想的程序星空
*

个人背景介绍:我是一名拥有研究生学历的全栈技术开发人员,专注于基于Flask的后端开发、数据挖掘技术、自然语言处理技术、移动开发领域以及自动化技术的研究与实践。具备丰富的软件系统开发经验,同时在人工智能算法服务方面积累了深厚的技术基础。

如果文章对你有帮助,欢迎

关注、

点赞、

收藏、

订阅。
1、RNN的背景
前馈神经网络忽略了数据之间的关联性,其输出仅与当前时刻网络的输入相关。然而,在解决大量实际问题时,我们发现现实问题中存在大量序列数据(如文本、语音以及视频等),在现实场景中,例如室外温度会随着气候的变化呈现周期性变化规律,语言则需要通过上下文关系来确认其表达含义。
这类序列型数据通常具有时序关联性,其输出不仅受当前输入的影响,还与过去某一或多个时间点的输出相关。然而,前馈神经网络难以处理这种关联性,因其缺乏记忆机制,导致无法传递前面时刻的输出到后续时刻。
因此,现代循环神经网络的出现,源于其具备记忆能力,并能够基于这些记忆进行推断。由此可知,其输出结果则主要取决于当前输入信息以及内部记忆状态。相比之下,前馈神经网络缺乏这种深度记忆能力,而循环神经网络则能够通过内部记忆机制处理任意时序的输入序列。
2、RNN的原理
循环神经网络(RNN,由Saratha Sathasivam于1982年提出)是一种起源于霍普菲尔德网络的深度学习模型。与全连接神经网络不同,RNN通过在网络中引入前后时序关系,能够更有效地处理如机器翻译这类与时间序列相关的任务。
循环神经网络是一种在处理序列数据方面表现出色的网络。在模型架构中,通过权值共享机制,模型得以扩展至多样化的样本类型。例如,在CNN中,固定的卷积核能够处理大小不一的图片。将图片划分为多个区域,通过相同的卷积核对每个区域进行处理,最终可以得到良好的处理效果。同样地,循环网络采用类似的模块(形式上相似)对整个序列进行处理,从而能够泛化长序列数据,最终获得所需的结果。
RNN的主要功能是处理序列数据。传统的神经网络架构通常包括输入层、隐含层和输出层,层与层之间通过全连接实现信息传递,而层与层之间的节点是无连接的。然而,这种传统的神经网络在处理许多问题时表现不佳。例如,在预测句子的下一个单词时,通常需要依赖前面的单词,因为句子中的单词并非独立存在。

相较于词袋模型和前馈神经网络模型,RNN能够考虑到词在文本中出现的先后顺序对预测结果的影响。RNN由输入层、隐藏层和输出层三个部分组成。与前馈神经网络不同,RNN能够继承前一时间步的隐藏状态,从而能够处理序列数据。
3、RNN的网络结构
RNN 不是 刚性地存储所有固定长度的序列,而是利用隐藏状态来保存各时间步的信息。

图1 典型的 RNN 是有环结构
从图表中可以看出,一个典型的 RNN 网络架构由输入层、输出层以及一个神经网络单元构成。与普通前馈神经网络不同,RNN 的神经网络单元不仅与输入和输出层相连,还具有自身的一个循环结构。这种循环结构使得信息能够从网络中的一个阶段传递到下一个阶段。
同时,RNN 还能按时间序列展开循环 (unroll the loop) 为如下形式:

图2 展开的 RNN
该架构不仅揭示了RNN的本质:上一个时刻的网络状态将被施加到(作用于)下一个时刻的网络状态,还凸显了RNN与序列数据的密切关联。值得注意的是,RNN要求每个时刻都必须接受输入,但输出并非每个时刻都必须存在。
进一步地,公式化 RNN 的结构:

图3 单个展开的RNN结构

图4 RNN的计算结构图
在RNN结构中,圆形箭头标识了隐藏层的自连接关系。各层之间共享相同的参数矩阵U、V、W,这不仅减少了需要学习的参数总数,还提升了模型的训练效率。
输入单元 (input units):

隐藏单元 (hidden units):

输出单元 (output units):

输入层 :

表示时刻t的输入。
隐藏层 :

,f是非线性激活函数,比如tanh。
输出层 :

,softmax函数是标准化的指数函数,将每个元素的取值范围限制在0到1之间,并确保所有元素的总和为1。
循环神经网络接收序列数据作为输入,每个训练样本是一个时间序列数据,其中包含多个相同维度的向量。在训练过程中,如何确定循环神经网络的参数?这里就需要使用解决循环神经网络训练问题的 Back Propagation Through Time 算法,简称 BPTT。
每个训练样本都是一个时间序列,这些序列具有时序特性。不同时间点的输入值之间存在关联性,这使得循环神经网络能够捕捉到序列中的动态信息。值得注意的是,不同样本的时间序列长度可能存在差异。在训练过程中,首先对时间序列中的每个时间点的输入值进行前向传播,接着,通过反向传播算法计算参数的梯度值,并完成参数更新。
循环神经网络在反向传播过程中也会遇到梯度消失或梯度爆炸的问题,这种现象在时间轴上表现为参数更新的困难。当输入序列的长度较长时,参数更新面临挑战。通常情况下,梯度爆炸比梯度消失更容易处理。当出现梯度爆炸时,可以设定一个梯度阈值,将超出阈值的梯度直接截断。
有三种方法应对梯度消失问题 :
科学的权重初始化方案。该方法通过确保各神经元的激活值分布于合理区间,避免出现梯度消失现象。
(2) 使用 ReLu 代替 sigmoid 和 tanh 作为激活函数。
(3) 采用其他类型的RNN架构,其中一种常见的类型是长短时记忆网络(LSTM),另一种是门控循环单元(GRU),这些结构在实际应用中表现尤为出色。
4、PyTorch的使用
在PyTorch框架中,采用nn.RNN类来构建基于序列的循环神经网络。其构造函数参数包括初始化的隐藏单元数量、输入特征数以及是否启用双向RNN等。
input_size:输入数据X的特征值的数目。
hidden_size:隐藏层的神经元数量,也就是隐藏层的特征数量。
num_layers:循环神经网络的层数,默认值是 1。
bias:默认为 True,如果为 false 则表示神经元不使用 bias 偏移参数。
batch_first:如果设置为 True,则输入数据的维度中第一个维度就是 batch 值,默认为 False。默认情况下第一个维度是序列的长度, 第二个维度才是batch,第三个维度是特征数目。
dropout:如果不为空,则表示最后跟一个 dropout 层抛弃部分数据,抛弃数据的比例由该参数指定。
RNN的核心参数主要包括input_size和hidden_size,这两个参数需要特别注意。其余参数通常无需人工干预,可以依靠系统默认设置来处理。
rnn = torch.nn.RNN(20,50,2)
input = torch.randn(100 , 32 , 20)
h_0 =torch.randn(2 , 32 , 50)
output,hn=rnn(input ,h_0)
print(output.size(),hn.size())
'''
torch.Size([100, 32, 50]) torch.Size([2, 32, 50])
'''
有梦想的程序星空
有梦想的程序星空
有梦想的程序星空
有梦想的程序星空
