理解长短期记忆(Long Short Term Memory, LSTM)模型(colah 原文翻译)

本文为译文,详见原文

递归神经网络(Recurrent Neural Network, RNN)

人类每时每刻的思考都不是孤立的从头开始,就像你在阅读这篇文章时,你对每个词的理解都是基于对先前词的理解而产生的,因为你的想法是具有时序关联性的。

传统神经网络的一个主要缺点是——做不到信息的时序关联。举个例子,想象一下你想区分一个电影某个时间点所发生的事件,传统的神经网络就做不到根据之前的事件来推理得到下一个事件。

递归神经网络(RNN)可以解决这一问题,它的网络结构中存在回环,使得之前的信息得以保留。
这里写图片描述

上面的示意图中,模块 A 接收输入xt,并输出一个值 ht ,环形结构允许信息从一个网络状态转移到下一个网络状态。

有了环形结构,使得RNN看上去有些神秘,但如果你仔细想一想,它们其实与常规的神经网络并无不同。一个递归神经网络可以被认为是相同网络的多次复制,每个网络将一个信号传递到它的下一个网络,如果我们将环形结构展开:
展开RNN

该链式结构揭示了RNN与序列(sequences)和列表(lists)紧密关联,用这种神经网络结构处理特定数据(文本,语言等)也是直观自然的。在过去的几年中,已经有一些难以置信的RNN成功应用案例来解决各种问题:语音识别、语言建模、翻译、图片字幕……这个名单还在不断扩展,相关讨论见Andrej Karpathy的博文:The Unreasonable Effectiveness of Recurrent Neural Networks

这些成功背后的本质是“长短期记忆模型(LSTMs)”的使用,这是一种特殊的递归神经网络,它对很多任务都适用,而且相较于标准的RNN模型,它的性能要高出许多,几乎所有基于RNN令人激动的成果都是由它取得。本文也将探索这些LSTM模型。

长期依赖(Long-Term Dependencies)的问题

RNN优势之一在于它们能够将先前的信息关联到当前的任务中,比如用之前的视频帧可以辅助理解当前的视频帧。如果RNN真能做到这些,那将是非常有用的,但真是这样吗?视情况而定!

有时我们只需要看当前的信息就能完成当前的任务,比如:考虑一个语言预测模型想要根据之前的单词预测下一个单词。如果我们想要预测这句话“the clouds are in the sky,”的最后一个词,那么很显然即使根据前面的5个单词我们也能够预测到下一个单词是“sky”。这些情况下,相关的信息距离所求信息的距离不远,所以RNN能够学习使用先前信息。
RNN3

但在有些情况下,我们需要更多的背景信息。如果想要预测“I grew up in France… I speak fluent French.”这句话的最后一个单词,离得近的句子“I speak fluent”暗示后面的单词很可能是一种语言,但如果我们想要确定是哪种语言,我们就需要先前出现的背景信息“France”,但它距离当前预测单词位置太远了。不幸的是,当这个距离变大时,RNN网络将不能够关联这些背景信息了。
这里写图片描述

理论上来说,通过人工挑选合适参数,RNN网络应该是完全可以解决这样的“长期依赖”问题的,但实际上RNN网络好像并不能做到。这个问题被Hochreiter (1991)Bengio, et al. (1994)等人深入探索过,他们发现了一些根本原因。

幸运的是,LSTM不存在这样的问题!

LSTM 网络

Long Short Term Memory networks通常称为“LSTMs”,是一种特殊的RNN,它被设计用来避免长期依赖的问题。

所有的递归神经网络都是重复的神经网络链,在标准的RNN中,这种重复的子模块有一种很简单的结构,比如一个tanh层。
RNN5

LSTMs也有这种链式结构,但是重复的子模块结构不一样,它不仅仅只有单一神经网络层,而是有四个层以一种特殊的方式进行交互。
RNN6

不要担心细节,下面我们将一步步展开讲解LSTM的结构。现在,我们先了解示意图中用到的表示符号:
RNN7

上面的线条符号都包含一个向量,从一个节点到另一个节点,粉色的圆圈代表点对点的操作,比如向量相加,黄色的框代表学习到的神经网络。相交的线条代表信息合并,分开的线条代表信息复制分裂。

LSTMs背后的核心思想

LSTMs的关键是元胞状态(cell state),也就是在顶端的水平直线。元胞状态有点像传送带,它沿着整条链一直向下,只与少数的线条有交互,信息可以容易地从它里面流过,不作过多改变。
RNN8

LSTM也能够往元胞状态增加或者减少信息,这由门(gates)结构控制。门能够选择性地让信息通过。它们由sigmoid神经网络层组成,并经过一个点乘操作。
RNN9

sigmoid层输出0到1之间的数字,描述允许每一部分通过百分之多少的信息。0代表舍弃所有信息,1代表让所有信息通过。一个LSTM有三个这样的门来保护和控制元胞状态。

LSTM工作步骤的详细介绍

LSTM中的第一步是决定什么信息将从元胞状态中被丢弃,该决定由一个叫“遗忘门”的sigmoid层产生,它的输入是 ht1 xt ,输出是一个0到1之间的系数,元胞状态 Ct1 中的每个数字都要与该系数相乘。

让我们回到上面那个根据之前的所有词预测下一个词的语言模型,在这种情况下,元胞状态可能包含当前主语的性别(男or女),根据它使用正确的代词(he/she),当有一个新的主语时,我们希望遗忘先前主语的性别。
RNN10

下一步是决定哪些新的信息将被存储到元胞状态中,这包含两个部分。首先,一个叫“输入门层”的sigmoid层决定我们将要更新哪些值;然后,一个tanh层创建一个新的可被加入元胞状态的候选值向量 Ct~ ;最后,我们将结合上面的一个值和一个向量,更新状态。

用上面的语言模型举例,就是我们想要加入新主语的性别信息到状态中,从而代替我们想遗忘的旧的性别信息。
RNN11

下面要将旧的状态 Ct1 更新到新的状态 Ct ,上面的步骤已经决定了做什么,我们只需要执行就好。

我们将旧状态乘上 ft ,先忘记我们决定忘记的信息。然后加上 itCt~ ,即用每个状态值更新率 it 乘上候选值向量。

在语言模型中,这相当于我们正真丢弃掉关于旧主语性别的信息,同时加上新主语性别的信息。
RNN12

下面我们需要决定输出是什么,这个输出将基于元胞状态,但需要被过滤一下。首先,我们用一个sigmoid门决定元胞状态的哪一部分将被输出;然后,将元胞状态经过一个tanh层(使得值被规范化到-1到1之间);最后,将得到的值乘上sigmoid门输出的结果,从而得到我们决定输出的部分。

类比语言模型例子,因为它只看到了一个主语,下面它可能想要输出与一个动词相关的信息,比如可能输出这个主语是单数还是复数形式的,这样我们就能知道主语后面应该跟哪一个动词形式了。
RNN13

LSTM的变种结构

上面介绍的是一种很普通的LSTM,但并不是所有的LSTM都跟它的一样,LSTM还有一些其他变种模型。实际上,几乎每一篇关于LSTM的文章都使用了稍微不同的版本,但差别都比较细微,下面介绍几种值得一提的变种模型。

Gers & Schmidhuber (2000)等人发明了一种比较受欢迎的LSTM变种模型,在标准LSTM结构中加入了“窥视孔连接”(“peephole connections”),这意味着我们让门能够看到元胞状态。
RNN14

从上面的示意图可以看出,每一个门层都加上了窥视孔,但许多文章会选择一些门加窥视孔,并不是所有门都有。

另一个变种使用连接的遗忘和输入门,没有将决定忘记什么旧信息和加入什么新信息分开先后处理,而是二者同时做出决策。我们忘记的状态信息和新加入的状态信息一样多。
RNN15

一个引人注目的LSTM变种叫门递归单元(Gated Recurrent Unit, GRU),由 Cho, et al. (2014)提出。它将遗忘门和输入门合并成一个“更新门”(“update gate”),它还合并了元胞状态(cell state)和隐藏状态(hidden state),还有其他的一些变化。得到的模型比标准的LSTM模型要简单一些,而且正变得原来越受欢迎。
RNN16

这只是最有名的LSTM变种中的一些,还有很多其他变种,比如Yao, et al. (2015)提出的Depth Gated RNNs。还有一些完全不同的解决长期依赖的方案,比如Koutnik, et al. (2014)提出的Clockwork RNNs。

那么哪一个变种是最好的呢?它们不同之处重要吗?Greff, et al. (2015)做了一个很好工作,比较了那些有名的变种模型后发现它们都差不多。 Jozefowicz, et al. (2015)测试了超过10000个RNN结构,发现了一些在特定任务中表现比LSTMs更好的模型。

结论

之前就提过,人们用RNN所取得的显著结果大多数都是用LSTMs取得的,它们确实在大多数任务中表现更佳。

如果用一连串的数学等式来描述LSTM,看起来确实很唬人,希望通过这篇文章能够让你更好地理解LSTM。

LSTM是RNN基础上的一次飞跃,我们自然会想:还有没有其他飞跃的可能性?研究者的普遍观点是“有!下一步变革就是注意力(Attention)”,这个观点是让RNN有目的性地从大量的信息中挑选出一部分信息。比如,如果你用RNN创建一幅图片的文字描述(caption description),它的每一个输出文字都是选择图片中的一小部分得到的。实际上,Xu, et al. (2015)就做了这个工作,如果你想探索注意力,这将是一个有趣的着眼点。使用注意力已经诞生了许多令人激动的成果,而且有更多有待挖掘的地方。

注意力不是唯一令人激动的RNN研究方向,比如,Kalchbrenner, et al. (2015)的Grid LSTMs似乎也很有前景。在生成模型中使用RNN,比如 Gregor, et al. (2015)Chung, et al. (2015)或者Bayer & Osendorfer (2015)也似乎非常有趣。接下来的几年对RNN来说是令人激动的一段时期,新的研究成果将会更有趣。

Reference

[1] http://colah.github.io/posts/2015-08-Understanding-LSTMs/

下面是一个使用PyTorch实现栈自编码器和基于Attention机制的LSTM对过程参数进行特征提取和预测的示例代码: ```python import torch import torch.nn as nn import torch.optim as optim import numpy as np # 栈自编码器模型 class SAE(nn.Module): def __init__(self, input_size, hidden_sizes): super(SAE, self).__init__() self.encoder = nn.Sequential( nn.Linear(input_size, hidden_sizes[0]), nn.ReLU(), nn.Linear(hidden_sizes[0], hidden_sizes[1]), nn.ReLU(), nn.Linear(hidden_sizes[1], hidden_sizes[2]) ) self.decoder = nn.Sequential( nn.Linear(hidden_sizes[-1], hidden_sizes[-2]), nn.ReLU(), nn.Linear(hidden_sizes[-2], hidden_sizes[-3]), nn.ReLU(), nn.Linear(hidden_sizes[-3], input_size) ) def forward(self, x): x = self.encoder(x) x = self.decoder(x) return x # 基于Attention机制的LSTM模型 class AttentionLSTM(nn.Module): def __init__(self, input_size, hidden_size, num_layers, attention_size): super(AttentionLSTM, self).__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.attention = nn.Sequential( nn.Linear(hidden_size, attention_size), nn.Tanh(), nn.Linear(attention_size, 1), nn.Softmax(dim=1) ) def forward(self, x): output, _ = self.lstm(x) attention_weights = self.attention(output) context = torch.bmm(attention_weights.transpose(1, 2), output) return context # 准备数据 input_data = np.random.rand(100, 10) # 假设有100个样本,每个样本有10个特征 input_data = torch.tensor(input_data, dtype=torch.float32) # 定义模型和优化器 sae = SAE(input_size=10, hidden_sizes=[32, 16, 8]) lstm = AttentionLSTM(input_size=8, hidden_size=16, num_layers=2, attention_size=8) optimizer = optim.Adam(list(sae.parameters()) + list(lstm.parameters()), lr=0.01) # 训练模型 for epoch in range(10): encoded_data = sae.encoder(input_data) context = lstm(encoded_data.unsqueeze(1)) output = sae.decoder(context.squeeze(1)) loss = nn.MSELoss()(output, input_data) optimizer.zero_grad() loss.backward() optimizer.step() print('Epoch: {}, Loss: {:.4f}'.format(epoch+1, loss.item())) # 使用模型进行预测 test_data = np.random.rand(1, 10) # 假设有1个测试样本,每个样本有10个特征 test_data = torch.tensor(test_data, dtype=torch.float32) encoded_test_data = sae.encoder(test_data) context = lstm(encoded_test_data.unsqueeze(1)) predicted_output = sae.decoder(context.squeeze(1)) print('Test Data:', test_data) print('Predicted Output:', predicted_output) ``` 这个示例代码中,首先定义了一个栈自编码器模型和一个基于Attention机制的LSTM模型,并将它们的参数同时进行优化。然后,使用输入数据训练模型,计算模型在每个epoch的损失,并输出。最后,使用训练好的模型对一个测试样本进行预测,并输出预测结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值