LSTM如何处理变长序列

Posted

技术标签:

【中文标题】LSTM如何处理变长序列【英文标题】:How LSTM deal with variable length sequence 【发布时间】:2018-09-30 05:03:35 【问题描述】:

我在deep Deep Learning with Python的第7章第1节找到一段代码如下:

from keras.models import Model
from keras import layers
from keras import Input

text_vocabulary_size = 10000
question_vocabulary_size = 10000
answer_vocabulary_size = 500

# Our text input is a variable-length sequence of integers.
# Note that we can optionally name our inputs!
text_input = Input(shape=(None,), dtype='int32', name='text')

# Which we embed into a sequence of vectors of size 64
embedded_text = layers.Embedding(64, text_vocabulary_size)(text_input)

# Which we encoded in a single vector via a LSTM
encoded_text = layers.LSTM(32)(embedded_text)

# Same process (with different layer instances) for the question
question_input = Input(shape=(None,), dtype='int32', name='question')
embedded_question = layers.Embedding(32, question_vocabulary_size)(question_input)
encoded_question = layers.LSTM(16)(embedded_question)

# We then concatenate the encoded question and encoded text
concatenated = layers.concatenate([encoded_text, encoded_question], axis=-1)

# And we add a softmax classifier on top
answer = layers.Dense(answer_vocabulary_size, activation='softmax')(concatenated)

# At model instantiation, we specify the two inputs and the output:
model = Model([text_input, question_input], answer)
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['acc'])

你看到这个模型的输入没有原始数据的形状信息,那么在Embedding层之后,LSTM的输入或者Embedding的输出都是一些可变长度的序列。

所以我想知道:

在这个模型中,keras如何确定LSTM层中lstm_unit的个数 如何处理变长序列

附加信息:为了解释lstm_unit是什么(我不知道如何调用它,所以只是显示它的图像):

【问题讨论】:

看看这个,要创建一个固定的序列长度,有一个实用函数keras.io/preprocessing/sequence/#pad_sequences 由于这不符合独立答案的条件,因此我将发表评论。除了@Daniel 和@ely 在他们的答案中引用的零填充、1 大小的批次和大小聚类之外,还有另一种很少使用但非常强大的方法来处理可变长度的输入。它被称为有状态学习(您可能已经注意到 Keras 的 RNN 层中的 stateful 选项)。正确使用很棘手,但绝对值得了解。 【参考方案1】:

提供的循环层继承自基本实现keras.layers.Recurrent,其中包括选项return_sequences,默认为False。这意味着默认情况下,循环层将消耗可变长度的输入,并最终在最后的顺序步骤中仅产生该层的输出。

结果,使用None指定可变长度的输入序列维度是没有问题的。

但是,如果您希望该层返回完整的输出序列,即输入序列的每一步的输出张量,那么您必须进一步处理该输出的可变大小。

您可以通过让下一层进一步接受可变大小的输入来做到这一点,然后在网络中解决问题,直到最终您必须从某个可变长度的事物中计算损失函数,或者计算一些固定长度的表示,然后再继续到后面的层,具体取决于您的模型。

或者您可以通过要求固定长度的序列来做到这一点,可能使用特殊的标记值填充序列的末尾,这些值仅指示一个空序列项,纯粹用于填充长度。

另外,Embedding 层是一个非常特殊的层,它也用于处理可变长度的输入。对于输入序列的每个标记,输出形状将具有不同的嵌入向量,因此形状为(批量大小、序列长度、嵌入维度)。由于下一层是 LSTM,所以这没问题……它也会很高兴地消耗可变长度的序列。

但正如Embedding 上的文档中提到的那样:

input_length: Length of input sequences, when it is constant.
      This argument is required if you are going to connect
      `Flatten` then `Dense` layers upstream
      (without it, the shape of the dense outputs cannot be computed).

如果您想直接从Embedding 转到非可变长度表示,则必须提供固定序列长度作为层的一部分。

最后,请注意,当您表达 LSTM 层的维度时,例如LSTM(32),您是在描述该层的输出空间的维度。

# example sequence of input, e.g. batch size is 1.
[
 [34], 
 [27], 
 ...
] 
--> # feed into embedding layer

[
  [64-d representation of token 34 ...],
  [64-d representation of token 27 ...],
  ...
] 
--> # feed into LSTM layer

[32-d output vector of the final sequence step of LSTM]

为了避免批量大小为 1 的低效率,一种策略是按每个示例的序列长度对输入的训练数据进行排序,然后根据常见的序列长度分组为批次,例如使用自定义 Keras数据生成器。

这具有允许大批量大小的优势,特别是如果您的模型可能需要批量归一化或涉及 GPU 密集型训练,甚至只是为了批量更新梯度估计的噪声较小。但它仍然允许您处理输入训练数据集,该数据集对于不同的示例具有不同的批处理长度。

但更重要的是,它还有一个很大的优势,即您不必管理任何填充以确保输入中的通用序列长度。

【讨论】:

【参考方案2】:

如何处理单位?

单位完全独立于长度,因此没有什么特别之处。

长度只会增加“循环步骤”,但循环步骤总是一遍又一遍地使用相同的单元格。

单元格的数量是固定的,由用户定义:

第一个 LSTM 有 32 个单元/单元 第二个 LSTM 有 16 个单元/单元

如何处理变长?

方法 1:创建 1 个序列的单独批次,每个批次都有自己的长度。将每批分别喂给模型。手动循环中的方法train_on_batchpredict_on_batch 是最简单的形式。 理想情况下,每个长度分开批次,每个批次收集具有相同长度的所有序列 方法2:创建一个固定长度的batch,用0填充每个序列未使用的尾长,在嵌入层中使用参数mask_zero=True。 注意不要在嵌入的输入中使用 0 作为实际单词或有意义的数据。

【讨论】:

请注意:创建长度为 1 的批次是不必要的,而且效率极低。批次中的示例具有不同的序列长度是没有问题的。这是一种标准做法。 Numpy 不支持不同长度的批次。可以做的最好的事情是按长度分开批次。 您可以提供一个生成器或其他 Python 序列类型,甚至是一个简单的列表。请记住,原始输入是一批序列,其中序列仅包含标记(例如整数列表)。除此之外,可变序列大小张量在内部根据您为 Keras 选择的任何后端进行处理,从程序员的角度来看并不重要。 因为这是关于 Keras,我不确定这是不是真的。我刚刚测试过,有一个相关的错误(Keras 2.1.0)。 Keras 需要一个 numpy 数组,它不接受列表或数组数组。 --- 顺便说一下,model.fit_generator() 会在手动循环内部调用model.train_on_batch() (github.com/keras-team/keras/blob/master/keras/engine/…) 啊,是的,我相信你是对的。 Keras fit 函数确实接受 ndarrays 列表,但仅适用于多个输入,而不适用于相同输入的序列。实际上,批处理策略似乎是按序列长度对训练数据进行排序,然后创建所有项目具有相同序列长度的批次。这样,您不会招致单元素批次的巨大效率低下,但仍可确保每个批次特定输入张量的矩形形状。我将更新我的答案以添加此详细信息。

以上是关于LSTM如何处理变长序列的主要内容,如果未能解决你的问题,请参考以下文章

如何处理极长的 LSTM 序列长度?

如何处理keras中多元LSTM中的多步时间序列预测

pytorch中如何处理RNN输入变长序列padding

Pytorch 中如何处理 RNN 输入变长序列 padding

深度学习实战pytorch中如何处理RNN输入变长序列padding

keras: 在构建LSTM模型时,使用变长序列的方法