一、介绍
1、什么是RNN
- 传统的神经网络是层与层之间是全连接的,但是每层之间的神经元是没有连接的(其实是假设各个数据之间是独立的)
- 这种结构不善于处理序列化的问题。比如要预测句子中的下一个单词是什么,这往往与前面的单词有很大的关联,因为句子里面的单词并不是独立的。
RNN
的结构说明当前的的输出与前面的输出也有关,即隐层之间的节点不再是无连接的,而是有连接的- 基本的结构如图,可以看到有个循环的结构,将其展开就是右边的结构
2、运算说明
- 如上图,输入单元(
inputs units
): $\{ {x_0},{x_1}, \cdots \cdots ,{x_t},{x_{t + 1}}, \cdots \cdots \}$,- 输出单元(output units)为:$\{ {o_0},{o_1}, \cdots \cdots ,{o_t},{o_{t + 1}}, \cdots \cdots \}$,
- 隐藏单元(hidden units)输出集: $\{ {s_0},{s_1}, \cdots \cdots ,{ost},{s_{t + 1}}, \cdots \cdots \}$
- 时间
t
隐层单元的输出为:${s_t} = f(U{x_t} + W{s_{t - 1}})$f
就是激励函数,一般是sigmoid,tanh, relu
等- 计算${s_{0}}$时,即第一个的隐藏层状态,需要用到${s_{-1}}$,但是其并不存在,在实现中一般置为0向量
- (如果将上面的竖着立起来,其实很像传统的神经网络,哈哈)
- 时间
t
的输出为:${o_t}=Softmax(V{s_t})$- 可以认为隐藏层状态${s_t}$是网络的记忆单元. ${s_t}$包含了前面所有步的隐藏层状态。而输出层的输出${o_t}$只与当前步的${s_t}$有关。
- (在实践中,为了降低网络的复杂度,往往${s_t}$只包含前面若干步而不是所有步的隐藏层状态)
- 在
RNNs
中,每输入一步,每一层都共享参数U,V,W
,(因为是将循环的部分展开,天然应该相等) RNNs
的关键之处在于隐藏层,隐藏层能够捕捉序列的信息。
3、应用方面
- 循环神经网络(
Recurrent Neural Networks,RNNs
)已经在众多自然语言处理(Natural Language Processing, NLP
)中取得了巨大成功以及广泛应用。目前使用最广泛最成功的模型便是LSTMs
(Long Short-Term Memory,长短时记忆模型)模型(1) 语言模型和文本生成
- 给定一个单词序列,根据前面的单词预测下面单词的可能性
- 也可以根据概率生成新的词
- 这里给出了3篇论文
- 和上面的语言模型很像,只不过是根据一段过生成另外的一段话
- 注意的是开始的输出是在全部输入结束后生成的
- 一些论文
(3) 语音识别
- 论文
- 根据图像,生成一段描述图像的话
- 需要和
CNN
结合使用
二、结构
1、One to One
- 即一个输入对应一个输出,就是上面的图
2、Many to One
- 即多个输入对应一个输出,比如情感分析,一段话中很多次,判断这段话的情感
- 其中$x_{1},x_{2},\ldots,x_{t}$表示句子中的
t
个词,o
代表最终输出的情感标签 - 前向计算就是:$$f(x)=Vs_{t}=V(Ux_{t}+Ws_{t-1})=V(Ux_{t}+W(Ux_{t-1}+Ws_{t-2}))\cdots$$
3、One to Many
- 前向计算类似,不再给出
4、Many to Many
- 前向计算类似,不再给出
5、双向RNN(Bidirectional RNN)
- 比如翻译问题往往需要联系上下文内容才能正确的翻译,我们上面的结构线性传递允许“联系上文”,但是联系下文并没有,所以就有双向RNN
- 前向运算稍微复杂一点,以
t
时刻为例
$
o_{t} = W_t^{(os)}s_t + W_t^{(oh)}h_t \\
\quad = W_t^{(os)} (W_{t-1}^{(ss)} s_{t-1} + W_{t}^{(sx)} x_{t-1}) + W_t^{(oh)} (W_t^{(hh)} h_{t+1} + W_t^{(hx)}x_t)
$
6、深层的RNN
- 上面的结构都是只含有一层的
state
层,根据传统NN和CNN,深层次的结构有更加号的效果,结构如图
三、Back Propagation Through Time(BPTT)训练
- 关于传统神经网络BP算法可以查看这里神经网络部分的推导
1、符号等说明
- 以下图为例
- 符号说明
- $\phi$………………………………………………隐藏层的激励函数
- $\varphi$………………………………………………输出层的变换函数
- $L_{t} = L_{t}\left( o_{t},y_{t} \right)$……………………………模型的损失函数
- 标签数据$y_{t}$是一个
one-hot
向量
- 标签数据$y_{t}$是一个
2、反向传播过程
- 接受完序列中所有样本后再统一计算损失,此时模型的总损失可以表示为(假设输入序列长度为
n
):$$L = \sum_{t = 1}^{n}L_{t}$$ - $o_{t} = \varphi(Vs_t) = \varphi(V(Ux_t + Ws_{t-1}))$
- 其中$s_{0} = \mathbf{0 =}( 0,0,\ldots,0 )^{T}$
- 令:${o_{t}^* = Vs_{t}}, \quad {s_{t}^{*} = Ux_{t} + Ws_{t - 1}}…………(1)$ (就是没有经过激励函数和变换函数前)
- 则:$o_{t} = \varphi( o_{t}^*)$
- $s_{t} = \phi(s_{t}^{*})$
(1) 矩阵V的更新
- 对矩阵 V 的更新过程,根据(1)式可得, (和传统的神经网络一致,根据求导的链式法则):
- $${{{\partial {L_t} \over \partial o_t^{\ast}}} = {{\partial L_t \over \partial o_t } \ast {\partial o_t \over \partial o_{t}^{\ast} }} = {{\partial L_t \over \partial o_t} \ast \varphi ^{'} (o_t^{\ast})}}$$
- $${{{\partial L_t} \over {\partial V }}} = {{\partial L_t \over \partial Vs_t} } \ast {{\partial Vs_t \over \partial V}} = {{\partial L_t \over \partial o_t^\ast}} \times s_t^T = ({{\partial L_t \over \partial o_t} \ast \varphi ^{'} (o_{t}^\ast)}) \times s_t^T$$
- 因为${L = \sum_{t = 1}^{n}L_{t}}$,所以对矩阵V的更新对应的导数:
$${{\partial L \over \partial V} = {\sum\limits_{t=1}^n ({\partial L_t \over \partial o_t} \ast \varphi ^{'} (o_t^\ast)) \times s_t^T}}$$
(2) 矩阵U和W的更新
RNN
的BP
算法的主要难点在于它State
之间的通信- 可以采用循环的方法来计算各个梯度,
t
应从n
开始降序循环至1
- 计算时间通道上的局部梯度(同样根据链式法则)
$$ {{\partial L_t \over \partial s_t^{\ast}}} = {{\partial L_t \over \partial Vs_t}} \times {{\partial s_t^{T} V_t^{T} \over \partial s_t}} \ast {{\partial s_t \over \partial s_t^{\ast}}} = V^T \times ({{\partial L_t \over \partial o_t}} * {\varphi ^{‘} (o_t^{\ast}))} $$
$$ {{\partial L_t \over \partial s_{k-1}^\ast}} ={{\partial s_k^\ast \over \partial s_{k-1}^\ast}} \times {{\partial L_t \over \partial s_{k}^\ast}} = W_T \times ({{\partial L_t \over \partial s_k^\ast} * {\phi ^{'} (s_{k-1}^\ast)}}) , (k=1,……,t) ………(2)$$
- 利用局部梯度计算
U
和W
的梯度- 这里累加是因为权值是共享的,所以往前推算一直用的是一样的权值
$${\partial L_t \over \partial U} + = {\sum\limits_{k=1}^t {\partial L_t \over \partial s_k^\ast} \times {\partial s_k^\ast \over \partial U}} = {\sum\limits_{k=1}^t {\partial L_t\over \partial s_k^\ast}} \times x_t^T $$
$${\partial L_t \over \partial W} + = {\sum\limits_{k=1}^t {\partial L_t \over \partial s_k^\ast} \times {\partial s_k^\ast \over \partial W}} = {\sum\limits_{k=1}^t {\partial L_t\over \partial s_k^\ast}} \times s_{t-1}^T ………………..(3)$$
- 这里累加是因为权值是共享的,所以往前推算一直用的是一样的权值
3、训练问题
- 从 公式(2)和(3) 中可以看出,时间维度上的权重
W
更新需要计算$\phi^{‘} (s_k^{\ast})$,即经过激励函数的导数 - 如果时间维度上很长,则这个梯度是累积的,所以造成梯度消失或爆炸
- 可以想象将结构图竖起来,就是一个深层的神经网络,所以容易出现梯度问题
- 关于梯度消失的问题可以查看我这里一遍博客
RNN
主要的作用就是能够记住之前的信息,但是梯度消失的问题又告诉我们不能记住太久之前的信息,改进的思路有两点- 一是使用一些
trick
,比如合适的激励函数,初始化,BN等等 - 二是改进
state
的传递方式,比如就是下面提及的LSTM
- 关于为何
LSTMs
能够解决梯度消失,直观上来说就是上方时间通道是简单的线性组合
- 关于为何
- 一是使用一些
四、Long Short-Term Memory(LSTM,长短时记忆网络)
1、介绍
LSTM
是一般RNN
的升级,因为一些序列问题,我们可能需要忘记一些东西,LSTM
和普通RNN
相比, 多出了三个控制器. (输入控制, 输出控制, 忘记控制)- 在
LSTM
里,这个叫做cell
(其实就是前面的state
,只是这里更加复杂了), 可以看作一个黑盒,这个cell
结合前面cell
的输出$h_{t-1}$和当前的输入$x_{t}$来决定是否记忆下来,该网络结构在对长序列依赖问题中非常有效
2、结构
- 一个经典的
cell
结构如下图- $\phi_{1} $是
sigmoid
函数,$\phi_{2}$ 是tanh
函数 *
表示element wise
乘法(就是点乘),使用X
表示矩阵乘法
- $\phi_{1} $是
LSTMs
的cell
的时间通道有两条。- 上方的时间通道($h^{\left( {old} \right)} \rightarrow h^{\left( {new} \right)}$)仅包含了两个代数运算,这意味着它信息传递的方式会更为直接 $$h^{(new)} = h^{(old)}*r_1 + r_2$$
- 位于下方的时间通道($s^{\left( {old} \right)} \rightarrow s^{\left( {new} \right)}$)则运用了大量的层结构,在
LSTMs
中,我们通常称这些层结构为门(Gates
)
3、运算说明
Sigmoid
函数取值区间为0-1
,那么当Sigmoid
对应的层结构输出0
时,就对应着遗忘这个过程;当输出1
时,自然就对应着接受这个过程。- 事实上这也是
Sigmoid
层叫门的原因——它能决定“放哪些数据进来”和决定“不让哪些数据通过”
- 事实上这也是
- 最左边的
Sigmoid gate
叫做遗忘门, 控制着时间通道信息的遗忘程度- 前向计算: $r_1 = \phi_1(W_1 \times x^*)$
- 其中 $x^* \buildrel \Delta \over =[x,s^{(old)}] $,表示当前输入样本和下方时间通道$s^{(old)}$连接(
concat
)起来
- 其中 $x^* \buildrel \Delta \over =[x,s^{(old)}] $,表示当前输入样本和下方时间通道$s^{(old)}$连接(
- 前向计算: $r_1 = \phi_1(W_1 \times x^*)$
- 第二个
Sigmoid Gate
通常被称为输入门(Input Gate), 控制着当前输入和下方通道信息对上方通道信息的影响- 前向运算为:$g_{1} = \phi_{1} ( W_{2} \times x^{*} )$,
- 第三个
Tanh Gate
则允许网络结构驳回历史信息, 因为tanh
的值域是(-1,1)- 前向运算为:$g_{2} = \phi_{2} ( W_{3} \times x^{*} )$
- $r_{2} = g_{1}*g_{2}$
- 第四个
Sigmoid Gate
通常被称为输出门(Output Gate),它为输出和传向下一个cell
的下方通道信息作出了贡献。- 对应的前向传导算法为:$g_{3} = \phi_{1}\left( W_{4} \times x^{*} \right)$
- 最终
cell
的输出为:$o = s^{\left( \text{new} \right)} = \phi_{2}\left( h^{\left( \text{new} \right)} \right)*g_{3}$ - 每个
Gate
对应的权值矩阵是不同的($W_{1}\sim W_{4}$),切勿以为它们会共享权值
Reference
- http://www.wildml.com/2015/09/recurrent-neural-networks-tutorial-part-1-introduction-to-rnns/
- http://www.wildml.com/2015/09/recurrent-neural-networks-tutorial-part-2-implementing-a-language-model-rnn-with-python-numpy-and-theano/
- https://zhuanlan.zhihu.com/p/26891871
- https://zhuanlan.zhihu.com/p/26892413
- 本文链接: http://lawlite.me/2017/06/14/RNN-循环神经网络和LSTM-01基础/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议 。转载请注明出处!