隐变量模型存在着长期信息保存的短期输入缺失的问题。解决这一问题的最早方法之一是长短期记忆网络LSTM,有许多与门控循环单元一样的属性。有趣的是,长短期记忆网络的设计比门控循环单元稍微复杂一些,却比门控循环单元早出现了近20年。
9.2.1 门控记忆元
长短期记忆网络的设计灵感来自计算机的逻辑门,长短期记忆网络引入了记忆元,或简称为单元cell。有些文献认为记忆元是隐状态的一种特殊类型,与隐状态具有相同的形状,设计目的是用于记忆附加的信息。为了控制记忆元,我们需要许多门,其中一个门用来从记忆元中输出条目,我们将其称为输出门。另一个门用来决定何时将数据读入记忆元,我们将其称为输入门。我们还需要一种机制来重置记忆元的内容, 由遗忘门来管理,这种设计的动机与门控循环单元相同,能够通过专用机制决定什么时候记忆或者忽略隐状态中的输入。
1 输入门,遗忘门和输出门
就如在门控循环单元中一样,当前时间步的输入和前一个时间步的隐状态作为数据送入长短期记忆网络的门中,如图9-4所示,由3个带有sigmoid激活函数的全连接处理,以计算输入门,遗忘门和输出门的值。这3个门的值都在(0,1)范围内。
隐状态Ht-1
输入Xt 遗忘门Ft,输入门It, 输出门Ot
图9-4 长短期记忆网络模型中的输入门,遗忘门和输出门
我们来细化一下长短期记忆网络的数学表达,假设有h个隐藏单元,批量大小为n,输入数为d,因此,输入为Xt属于Rnxd,前一个时间步的隐状态为Ht-1属于Rnxh。相应的,时间步t的门被定义如下:输入门是It属于Rnxh,遗忘门是Ft属于Rnxh,输出门是Ot属于Rnxh,他们的计算方法如下
It = sigma(XtWxi + Ht-1Whi +bi)
Ft = sigma(XtWxf + Ht-1Whf + bf)
Ot = sigma(XtWxo + Ht-1Who + bo)
2 候选记忆元
因为还没有指定各种门的操作,先介绍候选记忆元Ct属于Rnxh 的计算与上面描述的3个门的计算类似,但是使用tanh函数作为激活函数,函数值的范围(-1,1)下面导出在时间步t处的公式。
Ct = tanh(XtWxc +Ht-1Whc +Bc)
其中Wxc属于Rdxh和Whc属于Rhxh是权重参数,Bc属于Rlxh是偏置参数
候选记忆元如图9-5所示
隐状态Ht-1 输入Xt
遗忘门Ft, 输入门It, 候选记忆元Ct 输出门Ot
带有激活函数的全连接层
3 记忆元
在门控循环单元中,有一种机制来控制输入和遗忘。类似的,在长短期记忆网络中,有两个用于这样的目的,输入门It控制采用多少来自Ct的新数据,而遗忘门Ft控制保留多少过去的记忆元Ct-1属于Rnxh的内容,使用按照元素乘法。
Ct = Ft Ct-1 + It Ct
如果遗忘门始终为l且输入门始终为0,则过去的记忆元Ct-1将随时间被保存并传递到当前时间步,引入这种设计是为了缓解梯度消失问题,并更好的捕获序列中的长距离依赖关系。
这样我们就得到了计算记忆元的数据流图,如图9-6所示。
4 隐状态
最后,我们需要定义如何计算隐状态Ht属于Rnxh,就是输出门发挥作用的地方,在长短期记忆网络中,仅仅是记忆元的tanh的门控版本,这就确保了Ht的值始终在区间(-1,1)内
Ht = Ot tanh(Ct)
只要输出门接近l,我们就能有效的将所有记忆信息传递给预测部分,而对于输出门接近0,我么只保留记忆元内的所有信息,不需要更新隐状态。
图9-7提供了数据流的图形化演示。
9.2.2 从零开始实现
实现长短期记忆网络,与8.5节中的实验相同,首先加载时光机器数据集
import torch
from torch import nn
from d2l import torch as d2l
batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)
1初始化模型参数
我们需要定义和初始化模型参数,如前所述,超参数num_hiddens定义隐单元的数量,我们按照标准差0.01的高斯分布初始化权重,并将偏置设置为0.
def get_lstm_params(vocab_size, num_hiddens, device):
num_inputs = num_outputs = vocab_size
def normal(shape):
return torch.randn(size=shape, device = device) * 0.01
def three():
return (normal((num_inputs, num_hiddens)),
normal((num_hiddens, num_hiddens)),
torch.zeros(num_hiddens, device = device))
W_xi,W_hi,b_i = three()
W_xf, W_hf, b_f = three()
W_xo,W_ho,b_o = three()
W_xc,W_hc,b_c = three()
#输出层参数
W_hq = normal((num_hiddens, num_outputs))
b_q = torch.zeros(num_outputs, device =device)
#附加梯度
params = [W_xi,W_hi,b_i, W_xf, W_hf, b_f, W_xo,W_ho,b_o,W_xc,W_hc,b_c]
for param in params
pararm.requeires_grad_(True)
return params
2 定义模型
初始化函数中,长短期记忆网络的隐状态需要返回一个额外的记忆元,其值为0,形状为。因此,我们得到以下的状态初始化
def init_lstm_state(batch_size, num_hiddens, device):
return (torch.zeros(batch_szie, num_hiddens), device = device)
torch.zeros(batch_size, num_hiddens), device = device
实际模型的定义与我们前面讨论的意义,提供3个门和一个额外的记忆元,只有隐状态才会传递到输出层,而记忆元Ct不直接参与输出计算
def lstm(inputs, state, params):
[W_xi,W_hi,b_i, W_xf, W_hf, b_f, W_xo,W_ho,b_o,W_xc,W_hc,b_c,W-Hq, b_qj] = params
(H,C) = state
outputs=[]
for X in inputs:
I = torch.sigmoid((X @ W_xi) + (H @ W_hi) + b_i)
F = torch.sigmoid((X @ W_xf) + (H @ W_hf) + b_f)
O = torch.sigmoid((X @ W_xc) + (H @ W_hc) + b_c)
C = F C + I C_tilda
H = O * torch.tanh(C)
Y = (H @ W_hq) + b_q
outputs.append(Y)
return torch.cat(outputs, dim=0), (H,C)
3 训练和预测
我们通过实例化8.5节中引入的RNNModelScratch类来训练一个长短期记忆网络,就如我们在9.1节中所做的那样。
vocab_szie, num_hiddens, device = len(vocab), 256, d2l.try_gpu()
num_epochs, lr = 500, 1
model = d2l.RNNModeScratch(len(vocab), num_hiddens, device, get_lstm_params,
init_lstm_state, lstm)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
3.2.3 简洁实现
我们可以直接实例化长短期的记忆网络模型,高级API封装了前文介绍的所有配置细节,这段代码运行速度快很多,使用的编译好的运算符而不是Python代码来处理之前阐述的许多细节。
num_inputs = vocab_size
lstm_layer = nn.LSTM(num_inputs, num_hiddens)
model = d2l.RNNModel(lstm_layer, len(vocab))
model = model.to(device)
d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, device)
长短期记忆网络是典型的具有重要状态控制的隐变量自回归模型,多年来已经提供了其许多变体,多层,残差连接,不同类型的正则化,然而,由于序列的长距离依赖性,训练长短期记忆网络和其他序列模型的成本是相当高的,后面我们将讲述的高级的替代模型,如Transformer
小结:
长短期记忆网络有3种类型的门,输入门,遗忘门和输出门
长短期记忆网络的隐藏层输出包括隐状态和记忆元,只有隐状态会传递到输出层。
而记忆元完全属于内部信息
长短期记忆网络可以缓解梯度消失和梯度爆炸。
1662

被折叠的 条评论
为什么被折叠?



