循环神经网络不仅容易出现梯度弥散或者梯度爆炸,还不能处理较长的句子,也就是具有短时记忆(Short-term memory)
为了克服这些缺点,提出了长短时记忆网络(Long Short-Term Memory,简称 LSTM)。 LSTM 相对于基础的 RNN 网络来说,记忆能力更强,更擅长处理较长的序列信号数据
一、LSTM原理基础的 RNN 网络结构如图,上一个时间戳的状态向量 h𝑡-1与当前时间戳的输入𝒙𝑡经过线性变换后,通过激活函数𝑡𝑎𝑛ℎ后得到新的状态向量 h𝑡。 相对于基础的 RNN网络只有一个状态向量 h𝑡,LSTM 新增了一个状态向量𝑪𝑡,同时引入了门控(Gate)机制, 通过门控单元来控制信息的遗忘和刷新,如图
在 LSTM 中,有两个状态向量𝒄和h , 其中𝒄作为 LSTM 的内部状态向量,可以理解为LSTM 的内存状态向量 Memory,而 h表示 LSTM 的输出向量。 相对于基础的 RNN 来说,LSTM 把内部 Memory 和输出分开为两个变量,同时利用三个门控:输入门(Input Gate)、遗忘门(Forget Gate)和输出门(Output Gate)来控制内部信息的流动。
门控机制可以理解为控制数据流通量的一种手段,在 LSTM 中,阀门开和程度利用门控值向量𝒈表示
通过𝜎(𝒈)激活函数将门控制压缩到[0,1]之间区间, 当𝜎(𝒈) = 0时,门控全部关闭,输出𝒐 = 0;当𝜎(𝒈) = 1时,门控全部打开,输出𝒐 = 𝒙。 通过门控机制可以较好地控制数据的流量程度。
1.遗忘门遗忘门作用于 LSTM 状态向量𝒄上面,用于控制上一个时间戳的记忆𝒄𝑡-1对当前时间戳的影响。
遗忘门的控制变量𝒈𝑓由产生,。 当门控
= 1时,遗忘门全部打开, LSTM 接受上一个状态
的所有信息;当门控
= 0时,遗忘门关闭, LSTM 直接忽略
, 输出为 0的向量,经过遗忘门后, LSTM 的状态向量变为
2.输入门
输入门用于控制 LSTM 对输入的接收程度。
首先通过对当前时间戳的输入和上一个时间戳的输出
做非线性变换得到新的输入向量
:
,tanh 为激活函数,用于将输入标准化到[-1,1]区间。
并不会全部刷新进入 LSTM 的 Memory,而是通过输入门控制接受输入的量
输入门的控制变量同样来自于输入和输出
:
, 输入门控制变量
决定了 LSTM 对当前时间戳的新输入
的接受程度: 当
= 0时, LSTM 不接受任何的新输入
;当
= 1时, LSTM 全部接受新输入
,经过输入门后, 待写入 Memory 的向量为
在遗忘门和输入门的控制下, LSTM 有选择地读取了上一个时间戳的记忆和当前时间戳的新输入
, 状态向量
的刷新方式为
,得到的新状态向量
即为当前时间戳的状态向量
3.输出门
LSTM 的内部状态向量并不会直接用于输出,这一点和基础的 RNN 不一样。基础的RNN 网络的状态向量既用于记忆, 又用于输出,所以基础的 RNN 可以理解为状态向量𝒄和输出向量是同一个对象。 在 LSTM 内部,状态向量并不会全部输出,而是在输出门的作用下有选择地输出。
输出门的门控变量为:
。当输出门
= 0时,输出关闭, LSTM 的内部记忆完全被隔断, 无法用作输出,此时输出为 0 的向量;当输出
= 1时,输出完全打开, LSTM 的状态向量
全部用于输出。 LSTM 的输出由
产生,即内存向量
经过tanh激活函数后与输入门作用,得到 LSTM 的输出。 由于
∈[0,1], tanh(
) ∈ [-1,1],因此 LSTM 的输出
∈ [-1,1]。
在 TensorFlow 中, 同样有两种方式实现 LSTM 网络。既可以使用 LSTMCell 来手动完成时间戳上面的循环运算,也可以通过 LSTM 层方式一步完成前向运算
1.LSTMCellLSTMCell和SimpleRNNCell 用法一致, 区别在于 LSTM 的状态变量 List 有两个, 即[ h𝑡, 𝒄𝑡], 需要分别初始化,其中 List 第一个元素为 h𝑡,第二个元素为𝒄𝑡。 调用 cell完成前向运算时,返回两个元素: 第一个元素为 cell 的输出h𝑡,第二个元素为cell 的更新后的状态 List: [ h𝑡, 𝒄𝑡]。
x = tf.random.normal([2, 80, 100])
cell = layers.LSTMCell(64) # 创建LSTM Cell
# 初始化状态
state = [tf.zeros([2, 64]), tf.zeros([2, 64])]
# 向前计算
for xt in tf.unstack(x, axis=1):
out, state = cell(xt, state)
返回的输出out和state列表的第一个元素ht是一样的
2.LSTM层经过 LSTM 层前向传播后,默认只会返回最后一个时间戳的输出, 如果需要返回每个时间戳上面的输出, 需要设置 return_sequences=True 标志。对于多层神经网络, 可以通过 Sequential 容器包裹多层 LSTM 层,并设置所有非末层网络 return_sequences=True,这是因为非末层的 LSTM 层需要上一层在所有时间戳的输出作为输入
x = tf.random.normal([2, 80, 100])
net = Sequential([
layers.LSTM(64, return_sequences=True), # 非末层需要返回所有时间戳输出
layers.LSTM(64)
])
# 一次通过网络模型,即可得到最末层、最后一个时间戳的输出
out = net(x)