在 Keras 中使用不同长度的示例训练 RNN

数据挖掘 Python 喀拉斯 rnn 训练
2021-09-27 19:38:21

我正在尝试开始学习 RNN,我正在使用 Keras。我了解 vanilla RNN 和 LSTM 层的基本前提,但我无法理解培训的某个技术点。

keras 文档中,它说 RNN 层的输入必须具有形状(batch_size, timesteps, input_dim)这表明所有的训练样本都有一个固定的序列长度,即timesteps

但这并不是特别典型,不是吗?我可能想让 RNN 对不同长度的句子进行操作。当我在某个语料库上训练它时,我会给它输入成批的句子,长度各不相同。

我想显而易见的事情是找到训练集中任何序列的最大长度并将其补零。但这是否意味着我无法在输入长度大于该长度的测试时做出预测?

我想这是一个关于 Keras 的特定实现的问题,但我也在询问人们在面对这种问题时通常会做什么。

2个回答

这表明所有的训练样本都有一个固定的序列长度,即timesteps

这不太正确,因为该维度可以是None,即可变长度。在单个batch中,您必须具有相同数量的时间步(这通常是您看到 0-padding 和 masking 的地方)。但是批次之间没有这样的限制。在推理期间,您可以有任何长度。

创建随机时间长度批次的训练数据的示例代码。

from keras.models import Sequential
from keras.layers import LSTM, Dense, TimeDistributed
from keras.utils import to_categorical
import numpy as np

model = Sequential()

model.add(LSTM(32, return_sequences=True, input_shape=(None, 5)))
model.add(LSTM(8, return_sequences=True))
model.add(TimeDistributed(Dense(2, activation='sigmoid')))

print(model.summary(90))

model.compile(loss='categorical_crossentropy',
              optimizer='adam')

def train_generator():
    while True:
        sequence_length = np.random.randint(10, 100)
        x_train = np.random.random((1000, sequence_length, 5))
        # y_train will depend on past 5 timesteps of x
        y_train = x_train[:, :, 0]
        for i in range(1, 5):
            y_train[:, i:] += x_train[:, :-i, i]
        y_train = to_categorical(y_train > 2.5)
        yield x_train, y_train

model.fit_generator(train_generator(), steps_per_epoch=30, epochs=10, verbose=1)

这就是它打印的内容。请注意,输出形状(None, None, x)表示可变批量大小和可变时间步长大小。

__________________________________________________________________________________________
Layer (type)                            Output Shape                        Param #
==========================================================================================
lstm_1 (LSTM)                           (None, None, 32)                    4864
__________________________________________________________________________________________
lstm_2 (LSTM)                           (None, None, 8)                     1312
__________________________________________________________________________________________
time_distributed_1 (TimeDistributed)    (None, None, 2)                     18
==========================================================================================
Total params: 6,194
Trainable params: 6,194
Non-trainable params: 0
__________________________________________________________________________________________
Epoch 1/10
30/30 [==============================] - 6s 201ms/step - loss: 0.6913
Epoch 2/10
30/30 [==============================] - 4s 137ms/step - loss: 0.6738
...
Epoch 9/10
30/30 [==============================] - 4s 136ms/step - loss: 0.1643
Epoch 10/10
30/30 [==============================] - 4s 142ms/step - loss: 0.1441

@kbrose 似乎有更好的解决方案

我想显而易见的事情是找到训练集中任何序列的最大长度并将其补零。

这通常是一个很好的解决方案。也许尝试最大序列长度 + 100。使用最适合您的应用程序的任何内容。

但这是否意味着我无法在输入长度大于该长度的测试时做出预测?

不必要。在 keras 中使用固定长度的原因是因为它通过创建固定形状的张量极大地提高了性能。但这只是为了训练。训练后,您将学会适合您任务的重量。

假设,经过几个小时的训练,您意识到模型的最大长度不够大/小,您现在需要更改时间步长,只需从旧模型中提取学习的权重,使用新的时间步长构建新模型并将学习到的权重注入其中。

您可能可以使用以下方法执行此操作:

new_model.set_weights(old_model.get_weights())

我自己没有试过。请尝试并在此处发布您的结果,以使每个人都受益。这里有一些链接: 一二