导读
前两篇推文分别介绍了DNN和CNN,今天本文来介绍深度学习的另一大基石:循环神经网络,即RNN。RNN应该算是与CNN齐名的一类神经网络,在深度学习发展史上具有奠基性地位。
注:RNN既用于表达循环神经网络这一类网络,也用于表达标准RNN模块。正常情况下不存在理解歧义,因此本文不加以明确区分。
标准的RNN模块结构
如果说从DNN到CNN的技术演进是为了面向图像数据解决提取空间依赖特征的问题,那么RNN的出现则是为了应对序列数据建模,提取时间依赖特征(这里的"时间"不一定要求具有确切的时间信息,仅用于强调数据的先后性)。
延续前文的行文思路,本文仍然从以下四个方面加以介绍:
01 什么是RNN
循环神经网络,英文Recurrent Neural Network,简写RNN。显然,这里的"循环"是最具特色的关键词。那么,如何理解"循环"二字呢?这首先要从RNN适用的任务——序列数据建模说起。
RNN适用于序列数据建模,典型的序列数据可以是时间序列数据,例如股票价格、天气预报等;也可以是文本序列数据,比如文本情感分析,语言翻译等。这些数据都有一个共同的特点,那就是输入数据除了具有特征维度外,还有一个先后顺序的维度。以股票数据为例,假设一支股票包含[open, high, low, close]四个维度特征,那么其数据结构的示意图为:
实际上,这就是一个二维的数据矩阵[T, 4],其中T为时序长度,4为特征个数。在机器学习中,单支股票数据只能算作一个样本,进一步考虑多支股票则可构成标准的序列数据集[N, T, 4],其中N为股票数量。
进一步地,针对这样的序列数据集,RNN是怎样进行特征提取的呢?这里,我们有必要先追溯RNN之前的模型——DNN,并给出一个简单的DNN网络架构如下:
一个具有4个输入特征、单隐藏层的DNN架构
如果我们不考虑股票的时间特性(消除前述数据集的时间维度),则每支股票特征仅有4个,便可以直接利用上述的3层DNN架构完成特征处理,并得到最终的预测结果。上述这一过程可以抽象为:
这其实恰好就是前文提到的内容:神经网络本质上是在拟合一个复杂的复合函数,其中这个复合函数的输入是X,网络参数是W和b。
那么,当引入了时间维度,输入数据不再是4个特征,而是T×4个特征,且这T组特征具有确切的先后顺序,那么RNN要如何处理呢?一个简单的思路是将上述DNN结构堆叠起来,并循环执行,例如网络结构可能长这样:
RNN处理序列数据示意图
如上述示意图所示,纵向上仍然是一个单纯的DNN网络进行数据处理的流程,而横向上则代表了新增的时间维度。也正因为这个时间维度的出现,所以时刻t对应DNN输入数据将来源于两部分:当前时刻t对应的4个输入特征,以及t-1时刻的输出信息,即图中粉色横向宽箭头表示的部分。
是否好奇:为啥要将t-1时刻的的输出作为t时刻的输入呢?当然是因为要序列建模!如果不把相邻时刻的输入输出联系起来,那序列先后顺序又该如何体现?
用数学公式加以抽象表示,就是:
上式中,Wi表达当前输入信息的权重矩阵,Wh表达对前一时刻输入的权重矩阵,且二者在各个时刻是相同的,可理解为面向时间维度的权值共享。
对比该公式和前面DNN中的公式,主要有两点区别:
至此,我们再来看一下开篇中给出的标准RNN模块结构:
上图中的右侧(unfold部分),横向代表了沿时间维度传播的流程,纵向代表了单个时刻的信息处理流程(各时刻都是一个DNN),其中X代表各时刻的输入特征,ht代表各时刻对应的状态信息,U和V分别为当前输入和前一时刻状态的网络权重,W为由当前状态ht拟合最终需要结果的网络权重。而左侧呢,其实就是把这个循环处理的流程抽象为一个循环结构,也就是那个指向自己的箭头。
这个用指向自己的箭头来表示神经网络的循环,乍一看还挺唬人的!
至此,其实就已经完成了标准RNN的结构介绍。用一个更为广泛使用且抽象的RNN单元结构示意图,表达如下:
标准RNN模块的内部结构
标准RNN结构非常简单,通常来说,在神经网络中过于简单的结构也意味着其表达能力有限。比如说,由于每经过一个时间节点的信息传递,都会将之前的历史信息和当前信息进行线性组合,并通过一个tanh激活函数。tanh激活函数的输出值在(-1,1)之间,这也就意味着,如果时间链路较长时历史信息很可能会被淹没!这也是标准RNN结构最大的问题——不容易表达长期记忆——换句话说,就是时间链路较长的历史信息会变得很小。
于是,一个相对更为复杂、可以提供相对长期记忆的循环神经网络——LSTM应运而生。LSTM:Long-Shot Term Memory network,中文即为长短时记忆网络。顾名思义,这是一个能够兼顾长期和短期记忆的网络。内部是如何实现的呢?LSTM单元结构如下:
当然,除了上述这一单元结构示意图,LSTM还往往需要这样一组标准计算公式(这个等到后续择机再讲吧。。):
宏观对照标准RNN和LSTM单元结构,可以概括二者间的主要异同点如下:
深入理解LSTM单元的内部结构,建议参考某国外作者的博客:https://colah.github.io/posts/2015-08-Understanding-LSTMs/。本文中的部分网络模块示意图素材也摘自于此(想必也是国内各种介绍循环神经网络时广泛传播的一组内容)。
这里不再班门弄斧,仅简单补充个人理解:
进一步复盘由RNN到LSTM的改进:虽然LSTM设计的非常精妙,通过三个门结构很好的权衡了历史信息和当前信息对输出结果的影响,但也有一个细节问题——为了控制两部分信息的相对大小,我们是不是只需要1个参数就可以了呢?比如,计算x和y的加权平均时,我们无需为其分别提供两个系数α和β,计算z=αx+βy,而只需z=αx+y就可以,或者写成其归一化形式z=αx+(1-α)y。正是基于这一朴素思想,LSTM的精简版——GRU单元结构顺利诞生!
具体来说,GRU就是将遗忘门和输入门整合为一个更新门,其单元结构如下:
对比下LSTM与GRU的异同点
所以概括一下:从RNN到LSTM的改进是为了增加网络容量,权衡长短期记忆;而从LSTM到GRU的演进则是为了精简模型,去除冗余结构。
上述大体介绍了循环神经网络的起源,并简要介绍了三种最常用的循环神经网络单元结构:RNN、LSTM和GRU。如果说卷积和池化是卷积神经网络中的标志性模块,那么这三个模块无疑就是循环神经网络中的典型代表。
02 RNN为何有效
DNN可以用通用近似定理论证其有效性(更准确地说,通用近似定理适用于所有神经网络,而不止是DNN),CNN也可以抽取若干个特征图直观的表达其卷积的操作结果,但RNN似乎并不容易直接说明其为何会有效。
总体而言,我个人从以下几个方面加以感性理解:
循环神经网络是一个精妙的设计,对于序列建模而言是非常有效的,其历史地位不亚于卷积神经网络。但也值得指出,目前循环神经网络似乎已经有了替代结构——注意力机制——这也是对序列建模非常有效的网络结构,而且无需按时间顺序执行,可以方便的实现并行化,从而提高执行效率——当然,这是后来的故事!
03 RNN适用的场景
论及循环神经网络适用的场景,其实答案是相对明确的,即序列数据建模。进一步地,这里的序列数据既可以是带有时间属性的时序数据,也可以是仅含有先后顺序关系的其他序列数据,例如文本序列等。
与此同时,根据输入数据和预测结果各自序列长度的不同,又衍生了几种不同的任务场景,包括:
04 在PyTorch中的使用
对于标准RNN、LSTM和GRU三种典型的循环神经网络单元,PyTorch中均有相应的实现。对使用者来说,无需过度关心各单元内在的结构,因为三者几乎是具有相近的封装形式,无论是类的初始化参数,还是对输入和输出数据的形式上。
所以,这里以LSTM为例加以阐述。首先来看其类的签名文档:
上述文档仅给出了LSTM的理论介绍,主要是陈列了其内部结构的几个相关理论计算公式。而对于类的初始化参数方面,LSTM并未直接给出形参列表,而是使用*arg和**kwargs来接受自定义的传入参数,其中,常用的参数包括:
具体来说:
接下来是输入和输出信息:
大体来看,输入和输出具有相近的形式(这也是为啥可以循环处理的原因),对于LSTM来说包含三部分,即:
关于循环神经网络的介绍就到这里,后续将基于股票数据集提供一个实际案例。
相关阅读: