Kerasで異なる長さの例を使用してRNNをトレーニングする


61

私はRNNについて学び始めようとしていますが、Kerasを使用しています。私はバニラRNNおよびLSTMレイヤーの基本的な前提を理解していますが、トレーニングの特定の技術的ポイントを理解するのに苦労しています。

kerasドキュメントは、RNN層への入力は、形を持たなければならないと言います(batch_size, timesteps, input_dim)。これは、すべてのトレーニング例のシーケンス長が固定されていることを示していますtimesteps

しかし、これは特に典型的なものではありませんか?さまざまな長さの文に対してRNNを動作させたい場合があります。いくつかのコーパスでトレーニングするとき、さまざまな長さの一連の文をフィードします。

私がすべきことは、トレーニングセット内の任意のシーケンスの最大長を見つけてゼロパッドすることです。しかし、それは、それより長い入力長でテスト時に予測を行うことができないことを意味しますか?

これはKerasの特定の実装に関する質問ですが、この種の一般的な問題に直面したときに人々が通常何をするかについても尋ねています。


@kbroseは正しいです。ただし、懸念が1つあります。この例では、無限収量の非常に特殊なジェネレーターがあります。さらに重要なことは、サイズ1000のバッチを生成するように設計されていることです。実際には、これは、不可能ではないにしても、満足させるには難しすぎます。同じ長さのエントリが一緒に配置されるようにエントリを再編成する必要があり、バッチ分割位置を慎重に設定する必要があります。さらに、バッチ間でシャッフルを行う機会がありません。だから、私の意見は次のとおりです。あなたが何をしているかを正確に知っていない限り、Kerasで可変長入力を使用しないでください。パディングを使用し、Maskingレイヤーを無視するように設定します
Bs He

回答:


57

これは、すべてのトレーニング例のシーケンス長が固定されていることを示していますtimesteps

その次元はNone、つまり可変長である可能性があるため、これはまったく正しくありません。単一のバッチ内では、同じ数のタイムステップが必要です(これは通常、0パディングとマスキングが表示される場所です)。しかし、バッチ間ではそのような制限はありません。推論中、任意の長さにすることができます。

トレーニングデータのランダムな長さのバッチを作成するサンプルコード。

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

これありがとう。ただし、シーケンスに0を追加すると、x_tを0として渡し続けるため、隠された状態とメモリセルに影響します。normal fit()では、sequence_lenthパラメータを渡して、シーケンスの長さを指定して除外することができます。ジェネレーターアプローチでは0シーケンスを無視できないように思われますか?
GRS

1
@GRSジェネレータはの3タプルを返す(inputs, targets, sample_weights)ことができsample_weights、0パッドを0に設定できます。ただし、これが双方向RNNに完全に機能するかどうかはわかりません。
kbrose

これは役に立ちましたがmodel.predict_generator、テストセットで使用する例を含めることを望みます。ジェネレーターで予測しようとすると、連結に関するエラーが発生します(テストセットには可変長シーケンスもあります)。私の解決策は、この標準model.predictをハッキーな方法で使用することです。おそらく、これは新しい質問に適しているのでしょうか?
ミッキー

別の質問のように聞こえる@mickey。この質問は、予測ではなく、トレーニングに関するものです。
kbrose

コメントの質問が実際に新しい質問として尋ねられた場合、リンクできますか?
イタマルマシュキン

7

@kbroseにはより良い解決策があるようです

当然のこととして、トレーニングセット内の任意のシーケンスの最大長を見つけてゼロパッドすることになると思います。

これは通常、良い解決策です。シーケンスの最大長+ 100を試してみてください。アプリケーションに最適なものを使用してください。

しかし、それはそれより長い入力長でテスト時に予測を行うことができないことを意味しますか?

必ずしも。ケラで固定長が使用される理由は、固定形状のテンソルを作成することでパフォーマンスが大幅に向上するためです。しかし、それは訓練のためだけです。トレーニング後、タスクに適した重みを学習できます。

何時間もトレーニングした後、モデルの最大長が十分に大きく/小さくなかったことに気づき、時間ステップを変更する必要があります。古いモデルから学習した重みを抽出し、新しい時間ステップで新しいモデルを構築します学習した重みを注入します。

おそらく次のようなものを使用してこれを行うことができます。

new_model.set_weights(old_model.get_weights())

私は自分で試していない。それを試して、結果をここに投稿してください。ここにいくつかのリンクがあります: 1 つ2つ


1
実際、可変長の入力を使用でき、のようなハックを導入する必要はありませんmax length + 100。コード例については私の回答をご覧ください。
kbrose

1
より多くのタイムステップを持つモデルに重みを転送すると、実際に完全にうまく機能します!Bidirectional(LSTM)()RepeatVector()レイヤーのタイムステップを上げましたが、予測は完全に実行可能です。
komodovaran_

@kbroseこれはハックではなく、通常の方法です。1つのbatch_sizeの使用は遅すぎるため、kerasはマスキングレイヤーを有効にして、マスキングが損失に影響しないようにします。
フェルス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.