原创:方云
1986年,Elman等人提出了用于处理序列数据的循环神经网络(Recurrent Neural Networks)。如同卷积神经网络专门用于处理二维数据信息(如图像)的神经网络,循环神经网络是专用于处理序列信息的神经网络。循环网络可以扩展到更长的序列,大多数循环神经网络可以处理可变长度的序列,循环神经网络的诞生解决了传统神经网络在处理序列信息方面的局限性。
1997年,Hochreiter和Schmidhuber提出了**长短时记忆单元(Long Short Term Memory, LSTM)**用于解决标准循环神经网络时间维度的梯度消失问题(vanishing gradient problem)。标准的循环神经网络结构存储的上下文信息的范围有限,限制了RNN的应用。LSTM型RNN用LSTM单元替换标准结构中的神经元节点,LSTM单元使用输入门、输出门和遗忘门控制序列信息的传输,从而实现较大范围的上下文信息的保存与传输。
1998年,Williams和Zipser提出**随时间反向传播(Backpropagation Through Time,BPTT)**的循环神经网络训练算法。BPTT算法的本质是按照时间序列将循环神经网络展开,展开后的网络包含N(时间步长)个隐含单元和一个输出单元,然后采用反向误差传播方式对神经网络的连接权值进行更新。
2001年,Gers和Schmidhuber提出了具有重大意义的LSTM型RNN优化模型,在传统的LSTM单元中加入了窥视孔连接(peephole connections)。具有窥视孔连接的LSTM型RNN模型是循环神经网络最流行的模型之一,窥视孔连接进一步提高了LSTM单元对具有长时间间隔相关性特点的序列信息的处理能力。2005年,Graves成功将LSTM型RNN应用于语音处理;2007年,Hochreiter将LSTM型RNN应用于生物信息学研究。
RNN背后的想法是利用顺序信息。 在传统的神经网络中,我们假设所有输入(和输出)彼此独立。 但对于许多任务而言,这是一个非常糟糕的想法。 如果你想预测句子中的下一个单词,那你最好知道它前面有哪些单词。 RNN被称为*循环,*因为它们对序列的每个元素执行相同的任务,输出取决于先前的计算。 考虑RNN的另一种方式是它们有一个“记忆”,它可以捕获到目前为止计算的信息。 理论上,RNN可以利用任意长序列中的信息,但实际上它们仅限于回顾几个步骤(稍后将详细介绍)。 这是典型的RNN的样子:
展开的网络在t 时刻, x t x_t xt是输入层的输入,f是隐藏层激活函数,通常是非线性的,如tanh函数或ReLU函数; s t s_t st是隐藏层的输出,其中 s 0 s_0 s0是计算第一个隐藏层所需要的,通常初始化为全零; g 是输出层激活函数,可以是softmax函数, o t o_t ot 是输出层的输出。关键一点是, s t s_t st的值不仅取决于 x t x_t xt,还取决于 s t − 1 s_{t-1} st−1 。循环神经网络的前向计算过程用公式表示如下:
o t = g ( V ⋅ s t + b 2 ) o_t=g(V \cdot s_t + b_2) ot=g(V⋅st+b2) (1)
s t = f ( U ⋅ x t + W ⋅ s t − 1 + b 1 ) s_t=f(U \cdot x_t + W \cdot s_{t-1} + b_1) st=f(U⋅xt+W⋅st−1+b1) (2)
通过两个公式的循环迭代,有以下推导:
o t = g ( V ⋅ s t + b 2 ) o_t=g(V \cdot s_t + b_2) ot=g(V⋅st+b2)
= g ( V ⋅ f ( U ⋅ x t + W ⋅ s t − 1 + b 1 ) + b 2 ) =g(V \cdot f(U \cdot x_t+W \cdot s_{t-1}+b_1)+b_2) =g(V⋅f(U⋅xt+W⋅st−1+b1)+b2)
= g ( V ⋅ f ( U ⋅ x t + W ⋅ f ( U ⋅ x t − 1 + W ⋅ s t − 2 + b 1 ) + b 1 ) + b 2 ) =g(V \cdot f(U \cdot x_t+W \cdot f(U \cdot x_{t-1}+W \cdot s_{t-2}+b1)+b1)+b2) =g(V⋅f(U⋅xt+W⋅f(U⋅xt−1+W⋅st−2+b1)+b1)+b2)
= g ( V ⋅ f ( U ⋅ x t + W ⋅ f ( U ⋅ x t − 1 + W ⋅ f ( U ⋅ x t − 2 + . . . . ) ) ) + b 2 ) =g(V \cdot f(U \cdot x_t+W \cdot f(U \cdot x_{t-1}+W \cdot f(U \cdot x_{t-2}+....)))+b2) =g(V⋅f(U⋅xt+W⋅f(U⋅xt−1+W⋅f(U⋅xt−2+....)))+b2)
可以看到,当前时刻包含了历史信息,这说明循环神经网络能够记忆历史信息。
这里有几点需要注意:
RNN在NLP的许多任务上取得巨大成功。主要的应用领域有:
语言建模与生成文本(本文示例重点介绍)
机器翻译
语音识别
生成图像描述
假设我们的时间序列只有三段,假设在t=3时刻,损失函数为 L 3 = 1 2 ( Y 3 − O 3 ) 2 L_{3}=\frac{1}{2}(Y_{3}-O_{3})^{2} L3=21(Y3−O3)2,
则对于一次训练任务的损失函数为 L = ∑ t = 1 T L t L=\sum_{t=1}^{T}{L_{t}} L=∑t=1TLt ,即每一时刻损失值的累加。
使用随机梯度下降法训练RNN其实就是对 W x W_{x} Wx 、 W s W_{s} Ws 、 W o W_{o} Wo 以及 b 1 b_{1} b1、 b 2 b_{2} b2 求偏导,并不断调整它们以使L尽可能达到最小的过程。(注意此处把上文中的UVW参数替换为 W x W_{x} Wx 、 W s W_{s} Ws 、 W o W_{o} Wo,以保持和引用的文章一致。)
现在假设我们我们的时间序列只有三段,t1,t2,t3。
我们只对t3时刻的 W x W_{x} Wx、 W s W_{s} Ws、 W 0 W_{0} W0 求偏导(其他时刻类似):
∂ L 3 ∂ W 0 = ∂ L 3 ∂ O 3 ∂ O 3 ∂ W o \frac{\partial{L_{3}}}{\partial{W_{0}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{W_{o}}} ∂W0∂L3=∂O3∂L3∂Wo∂O3
∂ L 3 ∂ W x = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W x + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ W x + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ S 1 ∂ S 1 ∂ W x \frac{\partial{L_{3}}}{\partial{W_{x}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{x}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{W_{x}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{S_{1}}}\frac{\partial{S_{1}}}{\partial{W_{x}}} ∂Wx∂L3=∂O3∂L3∂S3∂O3∂Wx∂S3+∂O3∂L3∂S3∂O3∂S2∂S3∂Wx∂S2+∂O3∂L3∂S3∂O3∂S2∂S3∂S1∂S2∂Wx∂S1
∂ L 3 ∂ W s = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ S 1 ∂ S 1 ∂ W s \frac{\partial{L_{3}}}{\partial{W_{s}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{S_{1}}}\frac{\partial{S_{1}}}{\partial{W_{s}}} ∂Ws∂L3=∂O3∂L3∂S3∂O3∂Ws∂S3+∂O3∂L3∂S3∂O3∂S2∂S3∂Ws∂S2+∂O3∂L3∂S3∂O3∂S2∂S3∂S1∂S2∂Ws∂S1
可以看出对于 W 0 W_{0} W0 求偏导并没有长期依赖,但是对于 W x W_{x} Wx、 W s W_{s} Ws 求偏导,会随着时间序列产生长期依赖。因为 S t S_{t} St 随着时间序列向前传播,而 S t S_{t} St 又是 W x W_{x} Wx、 W s W_{s} Ws的函数。
根据上述求偏导的过程,我们可以得出任意时刻对 W x W_{x} Wx、 W s W_{s} Ws 求偏导的公式:
∂ L t ∂ W x = ∑ k = 0 t ∂ L t ∂ O t ∂ O t ∂ S t ( ∏ j = k + 1 t ∂ S j ∂ S j − 1 ) ∂ S k ∂ W x \frac{\partial{L_{t}}}{\partial{W_{x}}}=\sum_{k=0}^{t}{\frac{\partial{L_{t}}}{\partial{O_{t}}}\frac{\partial{O_{t}}}{\partial{S_{t}}}}(\prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}})\frac{\partial{S_{k}}}{\partial{W_{x}}} ∂Wx∂Lt=∑k=0t∂Ot∂Lt∂St∂Ot(∏j=k+1t∂Sj−1∂Sj)∂Wx∂Sk
任意时刻对 W s W_{s} Ws 求偏导的公式同上。
如果加上激活函数, S j = t a n h ( W x X j + W s S j − 1 + b 1 ) S_{j}=tanh(W_{x}X_{j}+W_{s}S_{j-1}+b_{1}) Sj=tanh(WxXj+WsSj−1+b1) ,
则 ∏ j = k + 1 t ∂ S j ∂ S j − 1 = ∏ j = k + 1 t t a n h ′ W s \prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}} = \prod_{j=k+1}^{t}{tanh^{'}}W_{s} ∏j=k+1t∂Sj−1∂Sj=∏j=k+1ttanh′Ws
激活函数tanh和它的导数图像如下。
由上图可以看出 t a n h ′ ≤ 1 tanh^{'}\leq1 tanh′≤1 ,对于训练过程大部分情况下tanh的导数是小于1的,因为很少情况下会出现 W x X j + W s S j − 1 + b 1 = 0 W_{x}X_{j}+W_{s}S_{j-1}+b_{1}=0 WxXj+WsSj−1+b1=0 ,如果 W s W_{s} Ws 也是一个大于0小于1的值,则当t很大时 ∏ j = k + 1 t t a n h ′ W s \prod_{j=k+1}^{t}{tanh^{'}}W_{s} ∏j=k+1ttanh′Ws ,就会趋近于0,和 0.0 1 50 0.01^{50} 0.0150 趋近与0是一个道理。同理当 W s W_{s} Ws 很大时 ∏ j = k + 1 t t a n h ′ W s \prod_{j=k+1}^{t}{tanh^{'}}W_{s} ∏j=k+1ttanh′Ws就会趋近于无穷,这就是RNN中梯度消失和爆炸的原因。
梯度消失是怎样现象?例如在RNN的语言模型中,在预测下一个单词时,没有考虑过去许多时间步的信息。 如下面句子:
Jane walked into the room. John walked in too. It was late in the day. Jane said hi to ____
为什么梯度消失是个问题?简单说,当我们看到 ∏ j = k + 1 t ∂ S j ∂ S j − 1 \prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}} ∏j=k+1t∂Sj−1∂Sj趋近于0时,我们不能判断:
对于梯度爆炸,Mikolov首先提出的解决方案是裁剪梯度,使它们的范数具有一定的最大值。
至于怎么避免这些现象,再看看 ∂ L t ∂ W x = ∑ k = 0 t ∂ L t ∂ O t ∂ O t ∂ S t ( ∏ j = k + 1 t ∂ S j ∂ S j − 1 ) ∂ S k ∂ W x \frac{\partial{L_{t}}}{\partial{W_{x}}}=\sum_{k=0}^{t}{\frac{\partial{L_{t}}}{\partial{O_{t}}}\frac{\partial{O_{t}}}{\partial{S_{t}}}}(\prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}})\frac{\partial{S_{k}}}{\partial{W_{x}}} ∂Wx∂Lt=∑k=0t∂Ot∂Lt∂St∂Ot(∏j=k+1t∂Sj−1∂Sj)∂Wx∂Sk梯度消失和爆炸的根本原因就是 ∏ j = k + 1 t ∂ S j ∂ S j − 1 \prod_{j=k+1}^{t}{\frac{\partial{S_{j}}}{\partial{S_{j-1}}}} ∏j=k+1t∂Sj−1∂Sj这一部分,要消除这种情况就需要把这一部分在求偏导的过程中去掉,至于怎么去掉,一种办法就是使 ∂ S j ∂ S j − 1 ≈ 1 {\frac{\partial{S_{j}}}{\partial{S_{j-1}}}}\approx1 ∂Sj−1∂Sj≈1另一种办法就是使 ∂ S j ∂ S j − 1 ≈ 0 {\frac{\partial{S_{j}}}{\partial{S_{j-1}}}}\approx0 ∂Sj−1∂Sj≈0。其实这就是LSTM做的事情.
长短期记忆(LSTM)模型来解决稳定性和梯度消失的问题。在这个模型中,常规的神经元被存储单元代替,存储单元中管理向单元移除或添加的结构叫门限,有三种:遗忘门、输入门、输出门,门限由 s i g m o i d sigmoid sigmoid 激活函数和逐点乘法运算组成。前一个时间步长的隐藏状态被送到遗忘门、输入门和输出门。在前向计算过程中,输入门学习何时激活让当前输入传入存储单元,而输出门学习何时激活让当前隐藏层状态传出存储单元。单个LSTM 神经元的具体结构如图所示:
我们假设h 为LSTM 单元的隐藏层输出,c 为LSTM 内存单元的值,x 为输入数据。LSTM 单元的更新与前向传播一样,可以分为以下几个步骤。
由Gers和Schmidhuber在2000年引入的一种流行的LSTM变体是添加“窥视孔连接”。这意味着下面情况的改变。
LSTM稍微有点戏剧性的变化是由Cho等人引入的门控循环单元(GRU) 。 它将遗忘和输入门组合成一个“更新门”。它还合并了单元状态和隐藏状态,并进行了一些其他更改。 由此产生的模型比标准LSTM模型简单,并且越来越受欢迎。
LSTM可以抽象成这样:
三个×分别代表的就是forget gate,input gate,output gate,而我认为LSTM最关键的就是forget gate这个部件。这三个gate是如何控制流入流出的呢,其实就是通过下面 f t f_{t} ft, i t i_{t} it, o t o_{t} ot 三个函数来控制,因为 σ ( x ) \sigma(x) σ(x)(代表sigmoid函数) 的值是介于0到1之间的,刚好用趋近于0时表示流入不能通过gate,趋近于1时表示流入可以通过gate。
f t = σ ( W f X t + b f ) f_{t}=\sigma({W_{f}X_{t}}+b_{f}) ft=σ(WfXt+bf)
i t = σ ( W i X t + b i ) i_{t}=\sigma({W_{i}X_{t}}+b_{i}) it=σ(WiXt+bi)
o i = σ ( W o X t + b o ) o_{i}=\sigma({W_{o}X_{t}}+b_{o}) oi=σ(WoXt+bo)
当前的状态 S t = f t S t − 1 + i t X t S_{t}=f_{t}S_{t-1}+i_{t}X_{t} St=ftSt−1+itXt, 将LSTM的状态表达式展开后得:
S t = σ ( W f X t + b f ) S t − 1 + σ ( W i X t + b i ) X t S_{t}=\sigma(W_{f}X_{t}+b_{f})S_{t-1}+\sigma(W_{i}X_{t}+b_{i})X_{t} St=σ(WfXt+bf)St−1+σ(WiXt+bi)Xt
如果加上激活函数, S t = t a n h [ σ ( W f X t + b f ) S t − 1 + σ ( W i X t + b i ) X t ] S_{t}=tanh\left[\sigma(W_{f}X_{t}+b_{f})S_{t-1}+\sigma(W_{i}X_{t}+b_{i})X_{t}\right] St=tanh[σ(WfXt+bf)St−1+σ(WiXt+bi)Xt]
传统RNN求偏导的过程包含 ∏ j = k + 1 t ∂ S j ∂ S j − 1 = ∏ j = k + 1 t t a n h ′ W s \prod_{j=k+1}^{t}\frac{\partial{S_{j}}}{\partial{S_{j-1}}}=\prod_{j=k+1}^{t}{tanh{'}W_{s}} ∏j=k+1t∂Sj−1∂Sj=∏j=k+1ttanh′Ws
对于LSTM同样也包含这样的一项,但是在LSTM中 ∏ j = k + 1 t ∂ S j ∂ S j − 1 = ∏ j = k + 1 t t a n h ’ σ ( W f X t + b f ) \prod_{j=k+1}^{t}\frac{\partial{S_{j}}}{\partial{S_{j-1}}}=\prod_{j=k+1}^{t}{tanh{’}\sigma({W_{f}X_{t}+b_{f}})} ∏j=k+1t∂Sj−1∂Sj=∏j=k+1ttanh’σ(WfXt+bf)
假设 Z = t a n h ′ ( x ) σ ( y ) Z=tanh{'}(x)\sigma({y}) Z=tanh′(x)σ(y) ,则 Z 的函数图像如下图所示:
可以看到该函数值基本上不是0就是1。再看RNN求偏导过程中有:
∂ L 3 ∂ W s = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ S 2 ∂ S 2 ∂ S 1 ∂ S 1 ∂ W s \frac{\partial{L_{3}}}{\partial{W_{s}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{S_{2}}}\frac{\partial{S_{2}}}{\partial{S_{1}}}\frac{\partial{S_{1}}}{\partial{W_{s}}} ∂Ws∂L3=∂O3∂L3∂S3∂O3∂Ws∂S3+∂O3∂L3∂S3∂O3∂S2∂S3∂Ws∂S2+∂O3∂L3∂S3∂O3∂S2∂S3∂S1∂S2∂Ws∂S1
,如果在LSTM中的 ∂ L 3 ∂ W s \frac{\partial{L_{3}}}{\partial{W_{s}}} ∂Ws∂L3可能就会变成:
∂ L 3 ∂ W s = ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 3 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 2 ∂ W s + ∂ L 3 ∂ O 3 ∂ O 3 ∂ S 3 ∂ S 1 ∂ W s \frac{\partial{L_{3}}}{\partial{W_{s}}}=\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{3}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{2}}}{\partial{W_{s}}}+\frac{\partial{L_{3}}}{\partial{O_{3}}}\frac{\partial{O_{3}}}{\partial{S_{3}}}\frac{\partial{S_{1}}}{\partial{W_{s}}} ∂Ws∂L3=∂O3∂L3∂S3∂O3∂Ws∂S3+∂O3∂L3∂S3∂O3∂Ws∂S2+∂O3∂L3∂S3∂O3∂Ws∂S1
这是因为: ∏ j = k + 1 t ∂ S j ∂ S j − 1 = ∏ j = k + 1 t t a n h ’ σ ( W f X t + b f ) ≈ 0 ∣ 1 \prod_{j=k+1}^{t}\frac{\partial{S_{j}}}{\partial{S_{j-1}}}=\prod_{j=k+1}^{t}{tanh{’}\sigma({W_{f}X_{t}+b_{f}})}\approx0|1 ∏j=k+1t∂Sj−1∂Sj=∏j=k+1ttanh’σ(WfXt+bf)≈0∣1,这样就解决了传统RNN中梯度消失的问题。
语言建模(Language Modeling), 通常指实现预测下一个单词的任务。例如下图,输入了"the students opened their"这个未完成的句子,预测下个单词最有可能的是哪一个?
更为形式的描述为:给定一个单词序列, x ( 1 ) , x ( 2 ) , . . . , x ( t ) x^{(1)},x^{(2)},...,x^{(t)} x(1),x(2),...,x(t),计算下一个单词 x t + 1 x^{t+1} xt+1的概率分布:
P ( x t + 1 = w j ∣ x ( t ) , . . . , x ( 1 ) ) P(x^{t+1}=w_j|x^{(t)},...,x^{(1)}) P(xt+1=wj∣x(t),...,x(1))
这里 w j w_j wj是在词汇表 V = { w 1 , . . . , w ∣ V ∣ } V=\{w_1,...,w_{|V|}\} V={w1,...,w∣V∣}任一单词.
或者从句子的角度看,我们预测观察句子(在给定数据集中)的概率为:
P ( w 1 , . . . , w m ) = ∏ i = 1 m P ( w i ∣ w 1 , . . . , w i − 1 ) P(w_1,...,w_m) = \prod_{i=1}^{m} P(w_i \mid w_1,..., w_{i-1}) P(w1,...,wm)=∏i=1mP(wi∣w1,...,wi−1)
对语言来说,句子的概率是每个单词概率的乘积。 因此,判断“他去买一些巧克力”这句话的可能性是“巧克力”在给出“他去买一些”的概率,乘以“一些”在给出“他去买”的概率,依次等等。
日常生活中天天会用到这个模型。例如在手机输入法中:
在Google搜索框中:
参考: https://github.com/dennybritz/rnn-tutorial-rnnlm
其中有:https://github.com/dennybritz/rnn-tutorial-rnnlm/blob/master/RNNLM.ipynb 可以演示具体的运行过程。