【Python机器学习】长短期记忆网络(LSTM)

目录

随时间反向传播

实践

模型的使用

脏数据

“未知”词条的处理

字符级建模(英文)

生成聊天文章

进一步生成文本

文本生成的问题:内容不受控

其他记忆机制

更深的网络


尽管在序列数据中,循环神经网络为对各种语言关系建模(因此也可能是因果关系)提供了诸多便利,但是存在一个主要缺陷:当传递了两个词条后,前面词条几乎完全失去了它的作用。第一个节点对第三个节点(第一个时刻再过两个时刻后)的所有作用都将被中间时刻引入的新数据彻底抹平。这对网络的基本结构很重要,但却和人类语言中的常见情景相违背,实际中即使词条在句子中相隔很远,也可能是深度关联的。

如果名词和动词在序列中相隔多个时刻,那么在这个更长的句子中,循环神经网络很难理解主语和主动词之间的关系。对于新句子,循环网络可能会过于强调动词和主语之间的联系,而低估主语与谓语主动词之间的联系。也就是说,在这里失去了句子的主语和谓语动词之间的关联性。在循环网络中,当我们遍历每个句子时,权重会衰减得过快。

这里面临的挑战是建立一个网络,其在新句子中都能“领悟”到相同的核心思想。我们需要的是能够在整个输入序列中记住过去的方法。长短期记忆(LSTM)则是我们需要的一种方法。

长短期记忆网络的现代版本通常使用一种特殊的神经网络单元,称为门控循环单元(GRU)。门控循环单元可以有效地保持长、短期记忆,使LSTM能够更精确地处理长句子或文档。事实上,LSTM工作的非常好,它在几乎所有涉及时间序列、离散序列和NLP领域问题的应用中都取代了循环神经网络。

LSTM对于循环网络的每一层都引入了状态的概念。状态作为网络的记忆。可以把上述过程看成是在面向对象编程中为类添加属性。每个训练样本都会更新记忆状态的属性。

在LSTM中,管理存储在状态(记忆)中信息的规则就是经过训练的神经网络本身。它们可以通过训练来学习要记住什么,同时循环网络的其余部分会学习预测目标标签。随着记忆和状态的引入,我们可以开始学习依赖关系,这些依赖关系不仅可以扩展到一两个词条,甚至还可以扩展到每个数据样本的整体。有了这些长期依赖关系,就可以开始考虑超越文字本身的关于语言更深层次的东西。

有了LSTM,模型可以开始学习人类习以为常和在潜意识层面上处理的语言模式。有了这些模式,不仅可以更精确地预测样本类别,还可以开始使用这些语言模型生成新的文本。

如上图,就像在一般的循环网络中一样,记忆状态受输入的影响,同时也影响层的输出。但是,这种记忆状态在时间序列(句子或文档)的所有时刻会持续存在。因此,每个输入都会对记忆状态和隐藏层的输出产生影响。记忆状态的神奇之处在于,它在学习(使用标准的反向传播)需要记住的信息的同时,还学习输出信息。

首先我们展开一个标准的循环神经网络,并添加记忆单元,下图中,看起来与一般的循环神经网络相似。但是,除了向下一个时刻提供激活函数的输出,这里还添加了一个也经过网络各时刻的记忆状态。在每个时刻的迭代中,隐藏层循环单元都可以访问记忆单元。这个记忆单元的添加,以及与其交互的机制,使它与传统的神经网络层有很大的不同。LSTM层只是一个极为特例化的循环神经网络。

下面仔细看其中的一个元胞:现在,每个元胞不再是一系列输入权重和应用于这些权重的激活函数,而是稍微复杂的结构。与前面一样,到每一层(或元胞)的输入是当前时刻输入和前一个时刻输出的组合。当信息流入这个元胞而不是权重向量时,它现在需经过3个门:遗忘门、输入/候选门和输出门。(如下图:)

这些门中的每一个都由一个前馈网络层和一个激活函数构成,其中的前馈网络包含将要学习的一系列权重。从技术上讲,其中一个门由两个前向路径组成,因此在这个层中将有4组权重需要学习。权重和激活函数的旨在控制信息以不同数量流经元胞,同时也控制信息到达元胞状态(或记忆)的所有路径。

下面是开发示例,使用之前循环神经网络的例子,将SimpleRNN替换成LSTM:

maxlen=200
batch_size=32
embedding_dims=300
epochs=2

import numpy as np

X_train=pad_turnc(X_train,maxlen)
X_test=pad_turnc(X_test,maxlen)
X_train=np.reshape(X_train,(len(X_train),maxlen,embedding_dims))
y_train=np.array(y_train)
X_test=np.reshape(X_test,(len(X_test),maxlen,embedding_dims))
y_test=np.array(y_test)

from keras.api.models import Sequential
from keras.api.layers import Dense,Dropout,Flatten,SimpleRNN,LSTM
num_neurons=50
model=Sequential()
model.add(LSTM(
    num_neurons,return_sequences=True,
    input_shape=(maxlen,embedding_dims)
))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(1,activation='sigmoid'))
model.compile('rmsprop','binary_crossentropy',metrics=['accuracy'])
print(model.summary())

虽然只修改了导入库的部分和其中一行Keras代码,但有很多事情都发生了改变。

从模型摘要中可以看到,对应相同的神经元,LSTM需要训练的参数比SimpleRNN中的要多的多。

在LSTM中,记忆将由一个向量来表示,这个向量与元胞中神经元的元素数量相同。上面的例子只有50个神经元,因此记忆单元将是一个由50个元素长的浮点数向量。

如上图,是门在网络中的运行情况。通过元胞的“旅程”不是一条单一的道路,它有多个分支。

我们从第一个样本中获取第一个词条,并将其300个元素的向量表示传递到第一个LSTM元胞。在进入元胞的过程中,数据的向量表示与前一个时刻的向量输出(第一个时刻的向量为0)拼接起来。在本例中,我们将得到一个长度为300+50个元素的向量。有时我们会看到向量后面加一个代表偏置项的1。因为偏置项在传递到激活函数之前总是将其相关权重乘以值1,所以有时会从输入向量表示中省略该输入,以使图更易理解。

在道路的第一个分叉处,我们将拼接起来的输入向量的副本传递到似乎会预示厄运的遗忘门,遗忘门的目标是:根据给定的输入,学习要遗忘元胞的多少记忆:

想要遗忘和想要记住的想法一样重要。作为人类,当我们从文本中获取某些信息时,例如名词是单数还是复数,我们想要保留这些信息,以便在之后的句子中能识别出与之匹配的正确的动词词形变换或形容词形式。在罗曼斯语序中,我们也必须识别一个名称的性别,然后在句子中使用它。但是输入序列会经常的从一个名词装换到另一个名词,因为输入序列可以由多个短语、句子甚至是文档组成。由于新的思想是在后面的语句中表达的,名词是复数的事实可能与后面不相关的文本没有任何关系。

A thinker sees his own actions as experiments and questions--as attempts to find out something. Success and failure are for him answers above all.

在这句中,动词“see”与名词“thinker”搭配,我们遇到的下一个主动动词时第二个句子中的“to be”。这时“be”动词变形成“are”,与“Success and failure”匹配。如果把它和句子中的第一个名词“thinker”搭配起来,就会使用错误的动词形式“is”,因此LSTM不仅必须对序列中的长期依赖关系建模,而且同样重要的是,还必须随着新依赖关系的出现而忘记长期依赖关系,这就是遗忘门的作用,在我们的记忆元胞中为相关的记忆腾出空间。

网络并不基于这类显式表示进行工作。网络试图找到一组权重,用它们乘以来自词条序列的输入,以便以最小化误差的方式更新记忆元胞和输出。令人惊讶的是它们竟然能工作,并且工作得很好。

遗忘门本身只是一个前馈网络,它由n个神经元组成,每个神经元的权重个数为m+n+1,在本例中,遗忘门由50个神经元,每个神经元有351(300+50+1)个权重。因我们希望遗忘门中的每个神经元的输出值在0到1之间,所以遗忘门的激活函数是Sigmoid函数。

遗忘门的输出向量是某种掩码(多孔掩码),它会遗忘记忆向量的某些元素,当遗忘门输出值接近于1时,对于该时刻,关联元素中更多的记忆知识会被保留,它越接近于0,遗忘的记忆知识就越多:

通过检查核对,上述模型的遗忘门能够主动忘记一些东西。我们最好学会记住一些新的事物,否则它很快就会被遗忘的。就会在“遗忘门”中原因,我们将使用一个小网络,根据两件事来学习需要假如多少记忆:到目前为止的输入和上一个时刻的输出。这是在候选门中发生的事情。

候选门内部有两个独立的神经元,它们做两件事:

  • 决定哪些输入向量元素值得记住(类似于遗忘门中的掩码)
  • 将记住的输入元素按规定路线放置到正确的记忆“槽”

候选门的第一部分是一个具有Sigmoid激活函数的神经元,其目标是学习要更新记忆向量的哪些输入值。这个神经元很像遗忘门中的掩码。

这个门的第二部分决定使用多大的值来更新记忆。第二部分使用一个tanh激活函数,它强制输出值在-1到1之间,这两个向量的输出是按元素相乘,然后将相乘得到的结果向量按元素加到记忆寄存器,从而记住新的细节:

这个门同时学习要提取哪些值以及这些特定值的大小。掩码和大小称为添加到记忆状态的值,与遗忘门一样,候选门会学习在将不适合信息添加到元胞的记忆之前屏蔽掉它们。

所以我们希望旧的、不相关的信息被遗忘,而新的信息能够被记住,然后我们就到达了元胞的最后一个门:输出门

到目前为止,在穿越元胞的过程中,只向元胞的记忆写入了内容,现在是时候利用整个结构了。输出门接收输入(这仍然是t时刻元胞的输入和t-1时刻元胞的输出的拼接),并将其传递到输出门。

拼接的输入被传递到n个神经元的权重中,然后使用Sigmoid激活函数来传递一个n维浮点数向量,就像SimpleRNN的输出一样。但是不同于通过细胞壁来传递信息,在网络中,我们通过暂停部分输出来传递信息。

现在,够级的记忆结构已经准备完成了,它将对我们应该输出什么进行权衡,这将是通过使用记忆创建最后一个掩码来判断的。这个掩码也是一种门,但是要尽量避免使用门这个术语,吟哦我这个掩码没有任何学习过的参数,这有别于前面描述的3个门。

由记忆创建的掩码是对记忆状态的每个元素使用tanh函数,它提供了一个在-1和1之间的n维浮点数向量。然后将掩码向量与输入门第一步中计算的原始向量按元素相乘。得到的n维结果向量作为元胞在t时刻的整数输出最终从元胞中传出:

因此,在获得了t时刻的输入和t-1时刻的输出,以及输入序列中的所有细节之后,元胞的记忆就知道在t时刻,最后一个词输出什么是最重要的。

随时间反向传播

LSTM的学习与其他神经网络一样,也是通过反向传播算法。基本RNN易于受到梯度消失影响是因为在任意给定时刻,导数都是权重的一个决定因素,因此,当我们结合不同的学习率,往之前时刻(词条)传播时,经过几次迭代之后,

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值