在没有填充的情况下改变 Keras 中的序列长度

Varying sequence length in Keras without padding

我有一个关于 Keras 中 LSTM 的不同序列长度的问题。我正在将大小为 200 的批次和可变长度序列 (= x) 传递给 LSTM:

LSTM(100, return_sequences=True, stateful=True, input_shape=(None, 100), batch_input_shape=(200, None, 100))

我在以下随机创建的矩阵上拟合模型:

x_train = np.random.random((1000, 50, 100))
x_train_2 = np.random.random((1000, 10,100))

据我正确理解 LSTM(和 Keras 实现),x 应该指的是 LSTM 单元的数量。对于每个 LSTM 单元,必须学习一个状态和三个矩阵(用于单元的输入、状态和输出)。如何在不填充到最大值的情况下将不同的序列长度传递给 LSTM。指定长度,像我一样?代码是 运行,但实际上不应该(以我的理解)。 甚至可以在之后传递另一个序列长度为 60 的 x_train_3,但是额外的 10 个单元不应该有状态和矩阵。

顺便说一下,我使用的是 Keras 1.0.8 版和 Tensorflow GPU 0.9。

这是我的示例代码:

from keras.models import Sequential
from keras.layers import LSTM, Dense
import numpy as np
from keras import backend as K

with K.get_session():

    # create model
    model = Sequential()
    model.add(LSTM(100, return_sequences=True, stateful=True, input_shape=(None, 100),
             batch_input_shape=(200, None, 100)))
    model.add(LSTM(100))
    model.add(Dense(2, activation='softmax'))
    model.compile(loss='categorical_crossentropy',
                  optimizer='rmsprop',
                  metrics=['accuracy'])

    # Generate dummy training data
    x_train = np.random.random((1000, 50, 100))
    x_train_2 = np.random.random((1000, 10, 100))
    y_train = np.random.random((1000, 2))
    y_train_2 = np.random.random((1000, 2))

    # Generate dummy validation data
    x_val = np.random.random((200, 50, 100))
    y_val = np.random.random((200, 2))

    # fit and eval models
    model.fit(x_train, y_train, batch_size=200, nb_epoch=1, shuffle=False, validation_data=(x_val, y_val), verbose=1)
    model.fit(x_train_2, y_train_2, batch_size=200, nb_epoch=1, shuffle=False, validation_data=(x_val, y_val), verbose=1)
    score = model.evaluate(x_val, y_val, batch_size=200, verbose=1)

首先:您似乎不需要 stateful=Truebatch_input。这些适用于当您想要将很长的序列分成几部分,并分别训练每个部分而不让模型认为序列已经结束时。

当您使用有状态层时,当您决定某个批次是长序列的最后一部分时,您必须手动 reset/erase states/memory。

您似乎在处理整个序列。不需要有状态。

填充不是绝对必要的,但似乎您可以使用填充 + 掩码来忽略额外的步骤。如果您不想使用填充,则可以将数据分成更小的批次,每个批次具有不同的序列长度。看到这个:

序列长度(时间步长)不会改变 cells/units 的数量或权重。可以使用不同的长度进行训练。不能改变的维度是特征的数量。


输入维度:

输入维度为 (NumberOfSequences, Length, Features)
输入形状和单元格数量之间绝对没有关系。它只携带步数或递归,也就是Length维度。

单元格:

LSTM 层中的单元格的行为与密集层中的“单元”非常相似。

一个单元格不是一个步骤。一个单元格只是“并行”操作的数量。每组细胞一起执行循环操作和步骤。

正如@Yu-Yang 在评论中所注意到的那样,细胞之间存在对话。但是通过步骤将它们作为同一个实体的想法仍然有效。

您在 this 等图像中看到的那些小块不是细胞,它们是台阶。

可变长度:

也就是说,序列的长度根本不会影响 LSTM 层中参数(矩阵)的数量。它只是影响步数。

固定数量的层内矩阵对于长序列将被重新计算更多次,对于短序列将被重新计算更少次。但在所有情况下,它都是一个矩阵获取更新并传递到下一步。

序列长度仅因更新次数而异。

图层定义:

细胞的数量可以是任意数量,它只是定义了有多少个并行的微型大脑将一起工作(这意味着或多或少强大的网络,或多或少的输出特征)。

LSTM(units=78) 
#will work perfectly well, and will output 78 "features".
#although it will be less intelligent than one with 100 units, outputting 100 features.    

有一个独特的权重矩阵和一个独特的 state/memory 矩阵,不断向前传递到下一步。这些矩阵在每一步中只是简单地“更新”,但并不是每一步都有一个矩阵。

图片示例:

每个方框“A”是使用和更新同一组矩阵(状态、权重...)的一个步骤。

不是 4 个单元格,而是同一个单元格执行 4 次更新,每个输入一次更新。

每个 X1、X2、... 都是您序列在长度维度上的一片。


较长的序列比较短的序列将重复使用和更新矩阵更多次。但它仍然是一个单元格。


单元格的数量确实会影响矩阵的大小,但不取决于序列长度。所有单元将并行工作,并在它们之间进行一些对话。


您的模特

在您的模型中,您可以像这样创建 LSTM 层:

model.add(LSTM(anyNumber, return_sequences=True, input_shape=(None, 100)))
model.add(LSTM(anyOtherNumber))

通过像这样在 input_shape 中使用 None,您已经告诉您的模型它接受任何长度的序列。

你所要做的就是训练。你的训练代码没问题。 唯一不允许的是在内部创建一个不同长度的批次。因此,正如您所做的那样,为每个长度创建一个批次并训练每个批次。