博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动手学PyTorch | (35) 长短期记忆(LSTM)
阅读量:4038 次
发布时间:2019-05-24

本文共 4628 字,大约阅读时间需要 15 分钟。

本节将介绍另一种常⽤的⻔控循环神经网络:长短期记忆(long short-term memory,LSTM)。它⽐⻔控循环单元的结构稍微复杂一点。

目录


1. 长短期记忆

LSTM 中引⼊了3个门,即输入门(input gate)、遗忘门(forget gate)和输出门(output gate),以及与隐藏状态形状相同的记忆细胞(某些文献把记忆细胞当成⼀种特殊的隐藏状态),从⽽记录额外的信息。

  • 输入门、遗忘门和输出门

与⻔控循环单元中的􏰀重置门和更新门一样,如下图所示,⻓短期记忆的⻔的输⼊均为当前时间步输⼊X_t与上一时间步隐藏状态H_{t-1},输出由激活函数为sigmoid函数的全连接层计算得到。如此一来,这3个门元素的值域均为[0,1].

具体来说,假设隐藏单元个数为h,给定时间步t的⼩批量输⼊X_t \in R^{n\times d}(样本数为d,输入个数为d)和上⼀时间步隐藏状态H_{t-1} \in R^{n\times h}.时间步t的输⼊⻔I_t \in R^{n\times h}、遗忘门F_t \in R^{n\times h}和输出门O_t \in R^{n\times h}分别计算如下:

其中的W_{xi},W_{xf},W_{xo} \in R^{d\times h};W_{hi},W_{hf},W_{ho} \in R^{h\times h}是权重参数,b_i,b_f,b_o\in R^{1\times h}是偏差参数。

  • 候选记忆细胞

接下来,⻓短期记忆需要计算候选记忆细胞\hat{C_t}.它的计算与上⾯介绍的3个门类似,但使⽤了值域在[-1,1]的tanh函数作为激活函数,如下图所示。

具体来说,时间步t的候选记忆细胞\hat{C_t} \in R^{n\times h}的计算为:

其中W_{xc} \in R^{d\times h},W_{hc} \in R^{h\times h}是权重参数,b_c \in R^{1\times h}是偏差参数。

  • 记忆细胞

我们可以通过元素值域在[0,1]的输⼊门、遗忘门和输出门来控制隐藏状态中信息的流动,这一般也是通过使用按元素乘法(符号为\odot)来实现的。当前时间步记忆细胞C_t \in R^{n\times h}的计算组合了上⼀时间步记忆细胞和当前时间步候选记忆细胞的信息,并通过遗忘⻔和输入门来控制信息的流动:

如下图所示,遗忘门控制上一时间步的记忆细胞C_{t-1}中的信息是否传递到当前时间步,⽽输⼊⻔则控制当前时间步的输入X_t通过候选记忆细胞\hat{C_t} \in R^{n\times h}如何流入当前时间步的记忆细胞。如果遗忘⻔⼀直近似1 且输⼊门⼀直近似0,过去的记忆细胞将⼀一直通过时间保存并传递⾄当前时间步。这个设计可以应对循环神经⽹络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。

 

  • 隐藏状态

有了记忆细胞以后,接下来我们还可以通过输出门来控制从记忆细胞到隐藏状态H_t \in R^{n\times h}的信息的流动:

这⾥的tanh函数确保隐藏状态元素值在-1到1之间。需要注意的是,当输出⻔近似1时,记忆细胞信息将传递到隐藏状态供输出层使用;当输出⻔近似0时,记忆细胞信息只⾃己保留。下图展示了⻓长期记忆中隐藏状态的计算。

 

2. 读取数据集

下⾯我们开始实现并展示⻓短期记忆。和前⼏节中的实验⼀样,这⾥依然使⽤周杰伦歌词数据集来训练模型作词。

import numpy as npimport torchfrom torch import nn, optimimport torch.nn.functional as Fimport syssys.path.append(".") import d2lzh_pytorch as d2ldevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()print(torch.__version__, device)

3. 从0开始实验

  • 初始化模型参数

下⾯的代码对模型参数进行初始化。超参数num_hiddens定义了隐藏单元的个数。

num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_sizeprint('will use', device)def get_params():    def _one(shape):        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)        return torch.nn.Parameter(ts, requires_grad=True)    def _three():        return (_one((num_inputs, num_hiddens)),                _one((num_hiddens, num_hiddens)),                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))        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 = _one((num_hiddens, num_outputs))    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)    return nn.ParameterList([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_q])
  • 定义模型

在初始化函数中,⻓短期记忆的隐藏状态需要返回额外的形状为(批量⼤小, 隐藏单元个数)的值为0的记忆细胞。

def init_lstm_state(batch_size, num_hiddens, device):    #初始化隐藏状态和记忆细胞    return (torch.zeros((batch_size, num_hiddens), device=device),             torch.zeros((batch_size, num_hiddens), device=device))

下⾯根据长短期记忆的计算表达式定义模型。需要注意的是,只有隐藏状态会传递到输出层,而记忆细胞不参与输出层的计算。

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_q] = params    (H, C) = state    outputs = []    for X in inputs: #[num_steps,batch_size,input_size]        #输入门        I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)        #遗忘门        F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)        #输出门        O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)        #候选记忆细胞        C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)        #记忆细胞        C = F * C + I * C_tilda        #隐藏状态        H = O * C.tanh()        #输出层        Y = torch.matmul(H, W_hq) + b_q        outputs.append(Y)    return outputs, (H, C)
  • 训练模型并创作歌词

同上⼀节一样,我们在训练模型时只使用相邻采样。设置好超参数后,我们将训练模型并根据前缀“分开”和“不分开”分别创作长度为50个字符的一段歌词。

num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']

我们每过40个迭代周期便根据当前训练的模型创作一段歌词。

d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,                          vocab_size, device, corpus_indices, idx_to_char,                          char_to_idx, False, num_epochs, num_steps, lr,                          clipping_theta, batch_size, pred_period, pred_len,                          prefixes)

4. 简洁实现

我们可以直接调⽤rnn模块中的LSTM类。

lr = 1e-2 # 注意调整学习率lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)model = d2l.RNNModel(lstm_layer, vocab_size)d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,                                corpus_indices, idx_to_char, char_to_idx,                                num_epochs, num_steps, lr, clipping_theta,                                batch_size, pred_period, pred_len, prefixes)

5. 小结

1)⻓短期记忆的隐藏层输出包括隐藏状态和记忆细胞。只有隐藏状态会传递到输出层。

2)⻓短期记忆的输⼊门、遗忘门和输出门可以控制信息的流动。

3)⻓短期记忆可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较⼤的依赖关系。

 

转载地址:http://twsdi.baihongyu.com/

你可能感兴趣的文章
QT打开项目提示no valid settings file could be found
查看>>
Win10+VS+ESP32环境搭建
查看>>
Ubuntu+win10远程桌面
查看>>
flutter-实现圆角带边框的view(android无效)
查看>>
android 代码实现圆角
查看>>
flutter-解析json
查看>>
android中shader的使用
查看>>
java LinkedList与ArrayList迭代器遍历和for遍历对比
查看>>
drat中构造方法
查看>>
JavaScript的一些基础-数据类型
查看>>
JavaScript基础知识(2)
查看>>
转载一个webview开车指南以及实际项目中的使用
查看>>
android中对于非属性动画的整理
查看>>
一个简单的TabLayout的使用
查看>>
ReactNative使用Redux例子
查看>>
Promise的基本使用
查看>>
coursesa课程 Python 3 programming 统计文件有多少单词
查看>>
coursesa课程 Python 3 programming 输出每一行句子的第三个单词
查看>>
Returning a value from a function
查看>>
coursesa课程 Python 3 programming Functions can call other functions 函数调用另一个函数
查看>>