在没有填充的情况下改变 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=True
和 batch_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
,您已经告诉您的模型它接受任何长度的序列。
你所要做的就是训练。你的训练代码没问题。
唯一不允许的是在内部创建一个不同长度的批次。因此,正如您所做的那样,为每个长度创建一个批次并训练每个批次。
我有一个关于 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=True
和 batch_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
,您已经告诉您的模型它接受任何长度的序列。
你所要做的就是训练。你的训练代码没问题。 唯一不允许的是在内部创建一个不同长度的批次。因此,正如您所做的那样,为每个长度创建一个批次并训练每个批次。