打算写一个从RNN到Attention的系列文章,今天先介绍一下循环神经网络RNN和门控循环神经网络LSTM,很多内容为笔者自己的理解,难免有疏漏之处,欢迎大家探讨。
文章有一些修改,因为是在本人的知乎专栏里刘改的,不想来回修改,大家可以去【从RNN到Attention】上篇 循环神经网络RNN,门控循环神经网络LSTM
一.为什么RNN比DNN更适合时间序列问题
DNN求解时序问题
对于一个时间序列问题,以单词预测为例,已知x 1 , x 2 , x 3 , … … , x t x_1,x_2,x_3,……,x_t x 1 , x 2 , x 3 , … … , x t ,求解t时刻的单词x t + 1 x_{t+1} x t + 1 ,那么从概率的角度,该问题可以建模为求解a r g m a x θ P ( x t + 1 ∣ x 1 , x 2 , . . . . x t , θ ) argmax_{\theta}P(x_{t+1}|x_{1},x_2,....x_t,\theta) a r g m a x θ P ( x t + 1 ∣ x 1 , x 2 , . . . . x t , θ ) ,其中θ \theta θ 为模型参数。如果我们用DNN求解该问题,则模型输入输出可以分别表示为
X = [ x 1 , x 2 , x 3 , … … , x t − 1 , x t ] X=[x_1,x_2,x_3,……,x_{t-1},x_t] X = [ x 1 , x 2 , x 3 , … … , x t − 1 , x t ]
Y = x t + 1 Y=x_{t+1} Y = x t + 1
似乎没有什么问题,但是假设一个单词的维度为d d d ,则X X X 的维度为d ∗ t d*t d ∗ t ,仅考虑从输入到第一层隐藏层,且隐藏层的维度为m m m ,那么其中的参数总量为d ∗ t ∗ m d*t*m d ∗ t ∗ m ,如下图所示,随着t的增长,参数量的增长是非常恐怖的,而且采用这种建模方式,x 1 , x 2 , x 3 , … … x t x_1,x_2,x_3,……x_t x 1 , x 2 , x 3 , … … x t 对于模型来说是等价的,丢失了他们的时序关系,因此DNN处理时序问题存在
1.参数量过大
2.丢失了时序关系
RNN求解时序问题
RNN的结构如图表示
其中x i x_{i} x i 为输入,对应单词预测问题即为单词的向量表示,h i h_{i} h i 为隐含层(hidden layer),是循环神经网络中特有的网络结构,其中
H t = ϕ ( X t W x h + H t − 1 W h h + b h ) . \boldsymbol{H}_t = \phi(\boldsymbol{X}_t \boldsymbol{W}{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h). H t = ϕ ( X t W x h + H t − 1 W h h + b h ) .
我们从上述式子可以看出:
隐含状态H t H_t H t 与t时刻输入x t x_t x t 和上一时刻的隐含状态H t − 1 H_{t-1} H t − 1 有关,而H t − 1 H_{t-1} H t − 1 也同样与t-1时刻输入x t − 1 x_{t-1} x t − 1 和上上一时刻的隐含状态H t − 2 H_{t-2} H t − 2 有关,以此类推,H t H_t H t 可以作为t时刻之前的输入和隐藏状态的信息储藏,而由于更近的时刻信息储藏的更加完整,从而既保留了之前的输入信息,同时还保证了他们时序关系 。
X X X 和H t − 1 H_{t-1} H t − 1 分别通过两个矩阵乘法与H t H_t H t 相关联。
如果去掉H t − 1 W h h \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} H t − 1 W h h ,则上式就是一个全连接。
事实上,我们令X t ′ = [ X t , H t − 1 ] , W ′ = [ W x h , W h h ] X^{'}_t=[X_t,H_{t-1}],W^{'}=[W_{xh},W_{hh}] X t ′ = [ X t , H t − 1 ] , W ′ = [ W x h , W h h ] ,则上式可以改写为H t = ϕ ( X t ′ W ′ + b h ) H_t= \phi(X^{'}_tW^{'}+b_h) H t = ϕ ( X t ′ W ′ + b h ) ,我们可以通过全连接来实现RNN 。
我们来看一下参数量,循环神经网络中的隐含状态与隐藏层作用类似,因此我们可以比较两者的参数量大小,我们假定隐藏层的维度也为m,首先忽略b h b_h b h 因为都是m维,则W x h W_{xh} W x h 的维度为x的维度d*隐藏层的维度m,即d ∗ m d*m d ∗ m ,W h h W_{hh} W h h 的维度为m ∗ m m*m m ∗ m ,因此总的维度为( d + m ) ∗ m (d+m)*m ( d + m ) ∗ m ,显然远远小于DNN的d ∗ t ∗ m d*t*m d ∗ t ∗ m ,且与t t t 的长度无关!理论上,我们可以将输入的长度拉倒无限长。
我们再来思考一下为什么循环神经网络的参数量与t t t 的长度无关呢?因为对于长度为t t t 的输入,他们共用了同一个W x h W_{xh} W x h 和W h h W_{hh} W h h ,大大减少了参数量。
我们怎么从隐藏层h t h_t h t 得到y t y_t y t 的呢?其实隐藏层h t h_t h t 的作用和DNN中的隐藏层作用类似,我们可以有很多处理方式,比如直接通过softmax求出y t y_t y t 的概率分布,也可以作为一个全连接层的输入,再经过别的操作得到y t y_t y t 。
二、门控循环神经网络LSTM
从上面的介绍我们可以看出RNN的关键在于H t H_t H t 保存之前的信息应用到当前的任务之上 ,但是H t H_t H t 真的可以做到吗?很难!当时间步距离较大时,循环神经网络在反向传播的过程中的梯度较容易出现衰减或爆炸(详见通过时间反向传播 ),LSTM(Long Short Term Memory)可以避免上述的长期依赖问题,由于GRU和LSTM类似,基本可以视为LSTM的简化版,在这里就不做赘述。
LSTM的网络结构图如下所示:
如果有小伙伴看过这张图,不知道初次看的时候内心是什么感受,反正我当时是一脸懵逼(卧槽,这什么玩意儿?)仔细研究过后,我发现其实LSTM的整个网络结构可以简述为“三门两细胞”,我们依照这个主线来理解应该会更轻松一些,首先来看“三门”:记忆门,遗忘门和输出门。
I t = σ ( X t W x i + H t − 1 W h i + b i ) \begin{aligned} \boldsymbol{I}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xi} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hi} + \boldsymbol{b}i) \end{aligned} I t = σ ( X t W x i + H t − 1 W h i + b i )
F t = σ ( X t W x f + H t − 1 W h f + b f ) , \begin{aligned}\ \boldsymbol{F}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xf} + \boldsymbol{H}_{t-1} \boldsymbol{W}{hf} + \boldsymbol{b}f),\end{aligned} F t = σ ( X t W x f + H t − 1 W h f + b f ) ,
O t = σ ( X t W x o + H t − 1 W h o + b o ) , \begin{aligned}\ \boldsymbol{O}_t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xo} + \boldsymbol{H}_{t-1} \boldsymbol{W}{ho} + \boldsymbol{b}_o), \end{aligned} O t = σ ( X t W x o + H t − 1 W h o + b o ) ,
这三个门在之后的计算中分别承载了不同的物理意义,计算上和之前RNN中隐藏层的计算差不多,也就是矩阵运算+激活函数,同样用到了前一时刻的隐含变量H t − 1 H_{t-1} H t − 1 和当前时刻的输入X t X_t X t ,事实上他们也都可以通过一个全连接表示。
“两细胞”包括候选记忆细胞C ~ t \tilde{\boldsymbol{C}}_t C ~ t 和记忆细胞C t \boldsymbol{C}_t C t 。
候选记忆细胞C ~ t \tilde{\boldsymbol{C}}t C ~ t 的表达式为
C ~ t = tanh ( X t W x c + H t − 1 W h c + b c ) \tilde{\boldsymbol{C}}_t = \text{tanh}(\boldsymbol{X}_t \boldsymbol{W}{xc} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hc} + \boldsymbol{b}_c) C ~ t = tanh ( X t W x c + H t − 1 W h c + b c )
它的计算与上面介绍的3个门也类似,但使用了值域在 [−1,1] 的tanh函数作为激活函数。候选记忆细胞C ~ t \tilde{\boldsymbol{C}}t C ~ t 的作用是作为记忆细胞C t \boldsymbol{C}_t C t 的输入 。
记忆细胞C t \boldsymbol{C}_t C t 的计算公式为:
C t = F t ⊙ C t − 1 + I t ⊙ C ~ t \boldsymbol{C}_t = \boldsymbol{F}_t \odot \boldsymbol{C}_{t-1} + \boldsymbol{I}_t \odot \tilde{\boldsymbol{C}}_t C t = F t ⊙ C t − 1 + I t ⊙ C ~ t
其中⊙ \odot ⊙ 为点乘,此时我们发现在记忆细胞C t \boldsymbol{C}_t C t 的计算公式中,用到了遗忘门F t \boldsymbol{F}_t F t ,并且与前一时刻的记忆细胞C t − 1 \boldsymbol{C}_{t-1} C t − 1 做点乘,表达的物理含义是我们希望对之前记忆的遗忘程度,当遗忘门某维度近似1,则该维度上一时刻的记忆被传递到当前记忆细胞,反之则被遗忘 。
同样的,对于输入门I t \boldsymbol{I}_t I t ,并且与当前时刻的候选记忆细胞C ~ t \tilde{\boldsymbol{C}}_t C ~ t 做点乘,表达对于当前时刻的候选记忆细胞的接收程度,当输入门某维度近似1,则当前时刻的候选记忆细胞的该维度信息被接收到当前记忆细胞,反之被忽略
我们再来做个比较,其实它和RNN的公式H t = ϕ ( X t W x h + H t − 1 W h h + b h ) . \boldsymbol{H}_t = \phi(\boldsymbol{X}_t \boldsymbol{W}{xh} + \boldsymbol{H}_{t-1} \boldsymbol{W}_{hh} + \boldsymbol{b}_h). H t = ϕ ( X t W x h + H t − 1 W h h + b h ) . 很相似,F t \boldsymbol{F}_t F t 类似于W t − 1 \boldsymbol{W}_{t-1} W t − 1 ,都是对于历史数据的处理,输入门I t \boldsymbol{I}_t I t 和W h h \boldsymbol{W}_{hh} W h h 类似,都是表达对于输入的处理,不同的是F t \boldsymbol{F}_t F t 和I t \boldsymbol{I}_t I t 是做点乘,另外二者为矩阵乘法。
最后隐藏层的输出为
H t = O t ⊙ tanh ( C t ) . \boldsymbol{H}_t = \boldsymbol{O}_t \odot \text{tanh}(\boldsymbol{C}_t). H t = O t ⊙ tanh ( C t ) .
同样是点乘,O t \boldsymbol{O}_t O t 是物理含义是对于输出的筛选,当输出门某维度近似1时,记忆细胞将该维度的信息传递到隐藏层供输出层使用;当输出门近似0时,则该维度的信息无法传递到隐藏层 。
我们最后再总结一下LSTM的整个设计思想
当前输入X t X_t X t 和前一时刻的隐含状态H t − 1 H_{t-1} H t − 1 生成输入门I t I_t I t 、输出门O t O_t O t 和遗忘门F t F_t F t ,以及候选记忆细胞C ~ t \tilde{\boldsymbol{C}}_t C ~ t
候选记忆细胞C ~ t \tilde{\boldsymbol{C}}_t C ~ t 和输入门I t I_t I t 控制当前时刻对于记忆细胞C t \boldsymbol{C}_t C t 输入,遗忘门F t F_t F t 和前一时刻的记忆细胞C ~ t − 1 \tilde{\boldsymbol{C}}_{t-1} C ~ t − 1 控制记忆细胞历史时刻的输入,注意这里是点乘
记忆细胞C t \boldsymbol{C}_t C t 和输出门O t O_t O t 控制隐藏层,注意这里也是点乘