一、概述
上⼀节介绍了RNN模型架构及其参数,RNN模型的前向传播和反向传播,最主要的是有关RNN模型的缺陷长期依赖关系导致的梯度消失/梯度回传消散的现象,这导致的模型观测长度变短,使模型倾向于基于短距离的数据的影响,以及为了解决这⼀现象而提出的关于反向传播算法BPTT的改进,即通过固定反向传播的长度从而假设截断长度计算出的梯度对真实梯度的近似作为结果以及通过引入随机变量根据补偿因子动态的调整截断长度。但是上述方法都不能很好的解决RNN中的梯度消失和梯度爆炸问题,更不能解决根本问题,也就是后来的⼀些学者研究发现的长期依赖关系是这种网络结构本身的问题,因此为了解决这⼀问题,专家们也提出了⼀些专门解决RNN中普遍存在的长期依赖的模型,也就是这节所介绍的 LSTM模型及其变种。
二、LSTM模型
Long Short Term Memory 长短期记忆模型
2.1 结构
- 传送带 Conveyor Belt:类似于笔记本,存储当前和过去的信息
- 遗忘门 Forget Gate:对过去信息的遗忘
- 输入门 Input Gate:对当前信息的筛选
- 新输入 New Value:为避免梯度爆炸/消失对信息进行压缩降维
- 输出门 Output Gate:输出
- 我们可以将上一时刻的输出 h t − 1 h_{t-1} ht−1 和这一时刻的输入 x t x_t xt 的组合内容称为短期记忆,也就是新信息。
- 将传送带上的信息 C t − 1 C_{t-1} Ct−1 和 C t C_t Ct 称为长期记忆,也就是传送带向量
- 这样综合考虑兼顾长期记忆和短期记忆的模型称为LSTM模型
2.2 各部分功能实现
2.2.1 传送带
类似于笔记本的作用,记录过去的内容(长期记忆)
C
t
−
1
C_{t-1}
Ct−1 和当前的内容
x
t
x_t
xt
如图⼀所示,传送带由三部分—遗忘门和输入门和新输入共同组成,遗忘门用来控制过去的传送带向量有多少会被保留下来,输入门的输出是关于新输入的⼀个筛选权重,新输入是给传送带加入当前时刻的新的输入。传送带的作用在于将过去的重要知识记录下来,就像是对过去的数据进行概括总结出关键词之类的特点保存下来
以供随时使用。
2.2.2 遗忘门
- 目标:对过去信息的修正,精简(通过 σ \sigma σ 函数将其映射到[0,1]上)
- 公式:
f
t
=
σ
(
W
f
⋅
[
h
t
−
1
,
x
t
]
+
b
f
)
f_t=\sigma (W_f·[h_{t-1},x_t]+b_f)
ft=σ(Wf⋅[ht−1,xt]+bf)
- W f W_f Wf 为随机初始化特征矩阵
- h t − 1 h_{t-1} ht−1 为上一时刻隐状态输出
- x t x_t xt 为当前输出
- f t f_t ft 是筛选的工具
2.2.3 输入门
- 目标:对当前信息的筛选
- 公式
i
t
=
σ
(
W
i
⋅
[
h
t
−
1
,
x
t
]
+
b
i
)
i_t=\sigma(W_i·[h_{t-1},x_t]+b_i)
it=σ(Wi⋅[ht−1,xt]+bi)
- i t i_t it 是经过筛选后的输入信息,目的是对短期记忆进行筛选,筛选出需要保存的新信息,也就是保存到长期记忆的信息。可以看成是当前输入对更新状态的贡献程度,也叫作输入产生的影响。如果矩阵中的元素值接近1,那么输入对状态的贡献程度就比较大,如果接近0那么输入基本不会对状态产生影响。
- W i W_i Wi 是特征筛选矩阵
2.2.4 新输入
- 目标:防止梯度爆炸对信息进行降维压缩,提取新信息中的关键信息更新笔记
- 使用sigmoid函数对新的信息进行压缩存储,防止梯度爆炸/消失,也称为候选状态,表示当前输入对于状态应该更新成什么样的值。
C t ~ = t a n h ( W c ⋅ [ h t − 1 , x t ] + b c ) \tilde{C_t}=tanh(W_c·[h_{t-1},x_t]+b_c) Ct~=tanh(Wc⋅[ht−1,xt]+bc)- C t ~ \tilde{C_t} Ct~ 候选状态,表示当前输入对于状态应该更新成什么值。
- 更新传送带,也就是更新笔记
C
t
=
f
t
∗
C
t
−
1
+
i
t
∗
C
t
~
C_t=f_t*C_{t-1}+i_t*\tilde{C_t}
Ct=ft∗Ct−1+it∗Ct~
-
C
t
C_t
Ct 为长期记忆内容,也就是现在的笔记,包含了长期记忆中需要保留的部分和短期记忆中需要保留的部分。
-
f
t
∗
C
t
−
1
f_t*C_{t-1}
ft∗Ct−1 为过去长期记忆内容中筛选出来的内容,也就是长期记忆的重要内容
-
i
t
∗
C
t
~
i_t*\tilde{C_t}
it∗Ct~ 为当前添加到长期记忆中的内容,也就是需要被长期记住的重要内容,新添加到长期记忆的内容。
-
i
t
i_t
it 相当于一个对
C
t
~
\tilde{C_t}
Ct~ 的一个筛选矩阵,即对传送带向量的筛选,对
C
t
~
\tilde{C_t}
Ct~ 作一个线性变化,线性变化之后为防止梯度爆炸,往往需要采用sigmoid函数对其进行降维压缩,主动对不需要加入到长期记忆中的新信息做一个损失,将不重要的信息主动损失掉。这样我们通过比较数值就可以将新信息中的重要和不重要的信息做一个分类。
- 类比RNN的BP算法 h t = W h ∗ h t − 1 + W i ∗ x t h_t=W_h*h_{t-1}+W_i*x_t ht=Wh∗ht−1+Wi∗xt
2.2.5 输出门
-
目标:产生短期记忆 h t h_t ht ,降维压缩后进行阶段输出
-
公式 o t = σ ( W o [ h t − 1 , x t ] + b 0 ) o_t=\sigma(W_o[h_{t-1},x_t]+b_0) ot=σ(Wo[ht−1,xt]+b0)
- o t o_t ot 是对新信息的筛选,对重要信息和非重要信息做一个分类
-
输出更新
h t = o t ∗ t a n h ( C t ) h_t=o_t*tanh(C_t) ht=ot∗tanh(Ct)
- h t h_t ht 是短期记忆的输出
- 经过tanh压缩的 C t C_t Ct 相当于一个筛选矩阵,筛选出兼顾当前短期记忆(新信息)和传送带向量(长期记忆)相关的重要信息,传给 h t h_t ht 进行输出和传递给下一个隐状态作为输入。
2.3 总结
2.3.1 tanh和sigmoid激活函数的使用
- 只有在进行筛选的时候,才会使用sigmoid函数,而tanh的作用只是为了防止梯度消失。因为每一次线性或非线性变换后的结果都会导致梯度消失或爆炸(所以sigmoid之后往往要加一个tanh),因此除了需要使用sigmoid的门函数之外,其余所有地方都需要加上一个tanh来防止梯度消失或爆炸。(因此,我们可以想象,当我们需要为网络引入一个非线性关系的时候,我们可以选择relu函数,其不仅可以引入非线性关系还可以有效防止梯度消失)。
- 在同时对输入做tanh和sigmoid函数之后相乘时,这里我们可以理解为sigmoid函数生成一个权重矩阵,对其输入完成一个分类,需要的信息聚集在1附近,不需要的信息聚集在0附近。再与tanh的结果相乘,筛选出重要信息,对不重要的做一个损失,最后去除0附近的信息就可以得到我们需要的信息,这样也可以保留信息对状态的正负反馈。
2.3.2 激活函数的不同输入
除了更新
h
t
h_t
ht 时激活函数的输入是
C
t
C_t
Ct ,其余情况激活函数的输入均为
[
h
t
−
1
,
x
t
]
[h_{t-1},x_t]
[ht−1,xt] ,也就是说每一步都考虑了当前时刻的输入和上一时刻的输出,这也就是为什么LSTM能应对长文本序列,因为从模型上就一直注意着过去信息的利用。
除此之外,更新
h
t
h_t
ht 时激活函数的输入
C
t
C_t
Ct 也包含着过去时刻和当前时刻的信息,更重要的是
C
t
⟶
h
t
C_t\longrightarrow h_t
Ct⟶ht ,也就是当前时刻的传送带向量
C
t
C_t
Ct 进⼀步提炼为当前时刻的状态向量
h
t
h_t
ht ,使得当前时刻的状态向量
h
t
h_t
ht 会作为代表过去信息的先验信息与下⼀时刻的输入
x
t
x_t
xt 参与运算,使得下⼀时刻的计算始终考虑过去的信息与新的输入信息。
[
h
t
−
1
,
x
t
]
⇒
{
f
t
+
1
i
t
+
1
C
~
t
+
1
o
t
⇒
{
C
t
+
1
h
t
+
2
[h_{t-1},x_t] \Rightarrow \begin{cases} f_{t+1} \\ i_{t+1} \\ \tilde{C}_{t+1} \\ o_t \end{cases}\Rightarrow \left\{\begin{aligned} C_{t+1} \\ h_{t+2} \end{aligned}\right.
[ht−1,xt]⇒⎩
⎨
⎧ft+1it+1C~t+1ot⇒{Ct+1ht+2
三、GRU模型
GRU参考资料
Gated Recurrent Unit 门控循环单元控制
- 整体思路:直接对状态向量也就是过去的信息进行总结和信息的提取并加入当前时刻新的信息来更新。由更新门控制过去信息与当前新输入的信息流入未来的占比 z t z_t zt ,由重置门对过去的状态向量进行重置 h t − 1 ′ h_{t-1}' ht−1′ ,之后生成当前时刻的伪状态向量表示当前的输入信息 h t ′ h_{t}' ht′ ,最后利用更新门在过去的状态向量 h t − 1 h_{t-1} ht−1(过去的信息)和当前的伪状态向量(当前的信息) h t ′ h_t' ht′ 之间进行取舍得到最终的当前时刻的状态向量 h t h_t ht,从而实现了将状态向量当作传送带向量在某⼀时刻下的状态向量的更新,因此下面就让我们来看看GRU的内部实现。
- 结构
- 更新门 Update Gate
- 重置门 Reset Gate
GRU看起来比LSTM复杂,但是其输入输出结构和RNN差不多
和LSTM相比,GRU的升级之处在于对长期记忆和短期记忆并不是综合考虑,而是按照权重进行考虑。
3.0 Hadamard乘积
也称为逐元素乘积,将矩阵对应位置的元素相乘的乘法,得到一个新矩阵,适用于同型的两个矩阵。假设A和B是两个同型矩阵,它们的Hadamard乘积C可以表示为 C = A ⊙ B = [ a 11 b 11 a 12 b 12 . . . a 1 n b 1 n a 21 b 21 a 22 b 22 . . . a 2 n b 2 n . . . . . . . . . a n 1 b n 1 a n 2 b n 2 . . . a n n b n n ] C=A\odot B=\left[\begin{matrix}a_{11}b_{11} & a_{12}b_{12} & ... &a_{1n}b_{1n} \\ a_{21}b_{21} & a_{22}b_{22} & ... & a_{2n}b_{2n} \\ ... & ...&&... \\ a_{n1}b_{n1} & a_{n2}b_{n2} & ... &a_{nn}b_{nn} \end{matrix}\right] C=A⊙B= a11b11a21b21...an1bn1a12b12a22b22...an2bn2.........a1nb1na2nb2n...annbnn
3.1 更新门
- 筛选过去信息和现在信息,记住一些信息(接近1的部分),生成输入权重矩阵,类似LSTM中的遗忘门和输入门
z
t
=
σ
(
W
z
x
t
+
U
z
h
t
−
1
)
z_t=\sigma(W_zx_t+U_zh_{t-1})
zt=σ(Wzxt+Uzht−1)
- 将前当前输入和前一时刻隐状态输出作为输入,经过 σ \sigma σ 进行筛选后传给 z t z_t zt ,故 z t z_t zt 为衡量当前输入对更新状态的贡献程度,也就是权重矩阵。
- W z W_z Wz 是更新门中对当前输入的随机初始化特征矩阵,而 U z U_z Uz 是更新门中对上一时刻隐状态的随机初始化特征矩阵。
3.2 重置门
- 作为整个GRU模型中最重要的部分,遗忘门的目的是遗忘过去的一些信息。综合考虑了当前信息和过去信息之后,对过去状态向量所蕴含的信息进行筛选,相当于对已有信息的一个更新和重置。
r
t
=
σ
(
W
(
r
)
⋅
[
h
t
−
1
,
x
t
]
)
=
σ
(
W
r
x
t
+
U
r
h
t
−
1
)
r_t=\sigma(W^{(r)}·[h_{t-1},x_t])=\sigma(W_rx_t+U_rh_{t-1})
rt=σ(W(r)⋅[ht−1,xt])=σ(Wrxt+Urht−1)
- 和更新门相类似, r t r_t rt 只是经过 σ \sigma σ 筛选之后,记录需要遗忘的信息(接近0)一个权重矩阵。
- W r W_r Wr 和 U r U_r Ur 分别是重置门中对当前输入的随机初始化特征矩阵和对上一时刻隐状态输出的随机初始化特征矩阵。而 W ( r ) W^{(r)} W(r) 是由 W r W_r Wr 和 U r U_r Ur 进行上下拼接而成的矩阵。
3.3 当前记忆内容
h
t
′
=
t
a
n
h
(
W
x
t
+
U
⋅
r
t
⊙
h
t
−
1
)
h_t'=tanh(Wx_t+U·r_t\odot h_{t-1})
ht′=tanh(Wxt+U⋅rt⊙ht−1)
-
h
t
′
h_t'
ht′ :将过去信息进行筛选并与输入信息拼接(此处还未筛选),再送入tanh中压缩,防止发生梯度消失/爆炸。
-
r
t
⊙
h
t
−
1
r_t\odot h_{t-1}
rt⊙ht−1 将过去信息中需要遗忘的部分作损失(使其接近0),这样就可以将需要记住的信息保留下来。这里使用Hadamard乘积来代替tanh,其实意义一样。
3.4 更新记忆内容
h
t
=
z
t
⊙
h
t
−
1
+
(
1
−
z
t
)
⊙
h
t
′
h_t=z_t\odot h_{t-1}+(1-z_t)\odot h_t'
ht=zt⊙ht−1+(1−zt)⊙ht′
- 第一项 z t ⊙ h t − 1 z_t\odot h_{t-1} zt⊙ht−1 : 筛选出上一状态输出需要保留的特征(接近0的部分为需要遗忘的部分)
- 第二项 ( 1 − z t ) ⊙ h t (1-z_t)\odot h_t (1−zt)⊙ht : h t h_t ht 为当前时刻输出的临时状态,这个整体可以筛选出当前信息需要保留的特征。
- 这样可以从当前信息中提取出过去信息中没有的特征,通过 1 − z t 1-z_t 1−zt 筛选出来与过去信息无关的内容(保证了 h t − 1 h_{t-1} ht−1 和 h t ′ h_t' ht′ 的无关性,相当于提取最大线性无关组)如此计算得来的 h t h_t ht 才能保证是最精简的同时,又包含了过去信息和当前信息所有的无关特征。
- 实际上也能反映出当前信息和过去信息的一个权重关系,一个多另一个自然就少了。
四、堆叠RNN
4.1 stocked RNN
对于RNN模型而言,堆叠3~5层就是极限了,再往上堆叠也很难产生更好的效果,反而会导致模型太过复杂,计算时间过长等负面效果。实际上就是对每⼀层的状态向量进行重复的利用提取特征(从CV中的特征提取得到的思路)。
- LSTM可以识别50~80词左右的长文本,但stack RNN也并没有完全解决过长文本的问题,只能说有一定的效果但是不明显。
4.2 Bidirectional RNN
我们知道简单的RNN网络的时序都是单向的且RNN模型本身结构造成的长期依赖导致的梯度消失的问题 造成起前面的网络往往不能进行梯度的更新,因此这里采用的⼀个思想就是信息的复用,既然从左往右走的顺序梯度更新的时候左边接收不到,那么我再加⼀层从右往左走的顺序这样我两边的梯度就都可以更新到了,但是超长文本下中间的神经元更新的梯度我想也是会消失的吧,毕竟如果太长的话梯度已经回传不到中间了。