-
lstm
时间轴: t=1 t=2 t=3 ... t=T
输入: x₁ ---> x₂ ---> x₃ ---> ... ---> x_T
↓ ↓ ↓ ↓
L1: h₁¹ ---> h₂¹ ---> h₃¹ ---> ... ---> h_T¹
↓ ↓ ↓ ↓
L2: h₁² ---> h₂² ---> h₃² ---> ... ---> h_T²
LSTM的数据流动大概如上所示
LSTM单元的设计核心是如下4点,每个单元都有自己的细胞状态
- 遗忘门(Forget Gate) - "该忘记什么?"
公式:f_t = σ(W_f · [h_{t-1}, x_t] + b_f)
作用:决定从上一细胞状态C_{t-1}中丢弃多少信息
理解:看当前输入x_t和上一隐藏状态h_{t-1},为C_{t-1}的每个维度输出0-1之间的值
0 = "完全忘记这个信息"
1 = "完全保留这个信息"
- 输入门(Input Gate) - "该记住什么新信息?"
公式:i_t = σ(W_i · [h_{t-1}, x_t] + b_i)
候选细胞状态:C̃_t = tanh(W_C · [h_{t-1}, x_t] + b_C)
作用:
i_t:决定哪些新信息值得存储
C̃_t:候选的新信息(经过tanh归一化到-1到1)
理解:先产生新信息候选,然后用输入门筛选哪些存入长期记忆
- 细胞状态更新 - "实际更新"
公式:C_t = f_t * C_{t-1} + i_t * C̃_t
这是LSTM最核心的公式!
理解:细胞状态更新 = 遗忘旧信息 + 添加新信息
加法操作是关键:避免了梯度消失(梯度直接流过加法)
- 输出门(Output Gate) - "该输出什么?"
公式:
o_t = σ(W_o · [h_{t-1}, x_t] + b_o)
h_t = o_t * tanh(C_t)
作用:基于细胞状态C_t,决定当前时间步输出什么到隐藏状态h_t
可以看到,lstm单元的设计思路是把每个单元当成一个细胞,不同时间步上每个细胞有自己的状态,这是第一个元素,而每个细胞要受上一个细胞状态的影响,所以需要计算出上一个细胞状态的相关权重,在lstm单元的设计里称之为遗忘门,决定从上一个细胞状态设计多少东西。同理,对当前的输入,也要有加权,因此还有一道输入门决定当前输入的权重,在实现的时候被拆分成了先求得当前细胞候选状态。最后就是输出门,输出每个细胞得隐藏状态和当前细胞输出。
一共四个元素,遗忘门决定上一个细胞状态得加权,输入门决定当前输入的加权,然后更新当前细胞状态(细胞状态也是一个要素),最后输出门输出当前细胞输出和隐藏状态
一层lstm网络实际只有一个lstm单元也就是四个参数在起作用循环使用
这种循环使用使得其可以接受不定长的输入而不会有问题。
考虑过这样共享参数网络能力会不会比较弱实际上的试验结论是不会,而且如果一层铺多个lstm单元会导致需要定长的输入序列了。
另一个问题是,因为每层网络支持不定长输入,所以批数据和批之间不用统一长度,只用批内部统一到最长长度即可。
还一个要注意的点是,每一层网络的输出是一层各时间步的隐藏状态,而每一层的每时间步输入会对应上一层的前一时间步输出,也就是层间是交织的,不是简单层间串联
双向lstm
因为一个时间顺序的lstm层只能建模一个顺序的依赖,而语言中很多时候依赖是既要上文的依赖也要下文的依赖
所以增加了一个把输入句子反向传递的lstm层,然后两个lstm层的隐藏层输出进行合并