私はLSTMについての理解とここで指摘したことを調和させようとしていますChristopher Olahによるこの投稿Kerasで実装されています。ジェイソン・ブラウンリーが書いたブログKerasチュートリアルについて。私が主に混乱しているのは、
- データ系列を次のように再形成し
[samples, time steps, features]
、 - ステートフルLSTM
以下に貼り付けたコードを参考に、上記の 2 つの質問に注目してみましょう。
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], look_back, 1))
testX = numpy.reshape(testX, (testX.shape[0], look_back, 1))
########################
# The IMPORTANT BIT
##########################
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(100):
model.fit(trainX, trainY, nb_epoch=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
注: create_dataset は長さ N のシーケンスを受け取り、N-look_back
各要素が長さシーケンスである配列を返しますlook_back
。
タイムステップと機能とは何ですか?
ご覧のとおり、TrainX は 3 次元配列で、Time_steps と Feature がそれぞれ最後の 2 つの次元です (この特定のコードでは 3 と 1)。下の画像に関して、これはmany to one
ピンク色のボックスの数が 3 の場合を考慮していることを意味しますか? それとも、文字通りチェーンの長さが 3 であることを意味しますか (つまり、考慮されるのは 3 つの緑色のボックスのみです)。
多変量系列を考慮する場合、特徴の議論は重要になりますか? たとえば、2 つの金融株を同時にモデル化する場合などです。
ステートフル LSTM
ステートフル LSTM とは、バッチの実行間でセル メモリ値を保存することを意味しますか? そうだとするとbatch_size
、メモリはトレーニングの実行間でリセットされるので、ステートフルであると言う意味は何でしょうか。これは、トレーニング データがシャッフルされないという事実に関連していると思いますが、その理由はよくわかりません。
何かご意見は? 画像参照:http://karpathy.github.io/2015/05/21/rnn-effectiveness/
編集1:
赤いボックスと緑のボックスが等しいという @van のコメントについて少し混乱しています。確認ですが、次の API 呼び出しは展開された図に対応していますか? 特に 2 番目の図 ( はbatch_size
任意に選択されました) に注目してください。
編集2:
Udacity のディープラーニング コースを受講したが、time_step 引数についてまだ混乱している人は、次の説明を参照してください。https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169
アップデート:
それはmodel.add(TimeDistributed(Dense(vocab_len)))
私が探していたものでした。例を挙げます。https://github.com/sachinruk/ShakespeareBot
アップデート2:
LSTM に関する私の理解のほとんどをここにまとめました。https://www.youtube.com/watch?v=ywinX5wgdEU
ベストアンサー1
受け入れられた回答を補足するために、この回答では、keras の動作と各画像を実現する方法を示します。
Kerasの一般的な動作
標準的な Keras の内部処理は、次の図に示すように、常に多対多です (ここではfeatures=2
、例として、圧力と温度を使用しました)。
この画像では、他の次元との混乱を避けるために、ステップ数を 5 に増やしました。
この例では:
- 石油タンクはN個あります
- 1時間ごとに測定するのに5時間を費やしました(時間ステップ)
- 私たちは次の 2 つの特徴を測定しました。
- 圧力P
- 温度T
入力配列は次のようになります(N,5,2)
。
[ Step1 Step2 Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
....
Tank N: [[Pn1,Tn1], [Pn2,Tn2], [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
]
スライディングウィンドウの入力
多くの場合、LSTM レイヤーはシーケンス全体を処理することになっています。ウィンドウを分割することは、最善のアイデアではないかもしれません。レイヤーには、シーケンスが前進するにつれてどのように進化するかに関する内部状態があります。ウィンドウは、すべてのシーケンスをウィンドウ サイズに制限し、長いシーケンスを学習する可能性を排除します。
ウィンドウでは、各ウィンドウは長い元のシーケンスの一部ですが、Keras ではそれぞれが独立したシーケンスとして表示されます。
[ Step1 Step2 Step3 Step4 Step5
Window A: [[P1,T1], [P2,T2], [P3,T3], [P4,T4], [P5,T5]],
Window B: [[P2,T2], [P3,T3], [P4,T4], [P5,T5], [P6,T6]],
Window C: [[P3,T3], [P4,T4], [P5,T5], [P6,T6], [P7,T7]],
....
]
この場合、最初はシーケンスが 1 つしかありませんが、それを複数のシーケンスに分割してウィンドウを作成していることに注意してください。
「シーケンスとは何か」という概念は抽象的です。重要な部分は次のとおりです。
- 多数の個別のシーケンスを持つバッチを作成できます
- シーケンスがシーケンスである理由は、シーケンスが段階的に(通常は時間ステップで)進化するからです。
各ケースを「単層」で実現
標準的な多対多の実現:
次のようにして、単純な LSTM レイヤーで多対多を実現できますreturn_sequences=True
。
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
多数対1の達成:
まったく同じレイヤーを使用すると、keras はまったく同じ内部前処理を実行しますが、return_sequences=False
この引数を使用すると (または単に無視すると)、keras は最後のステップの前のステップを自動的に破棄します。
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
1対多の達成
現在、これは Keras LSTM レイヤーだけではサポートされていません。ステップを増やすには独自の戦略を作成する必要があります。2 つの優れたアプローチがあります。
- テンソルを繰り返して一定のマルチステップ入力を作成する
stateful=True
1つのステップの出力を繰り返し取得し、それを次のステップの入力として提供するには、を使用します( が必要ですoutput_features == input_features
)
繰り返しベクトルを持つ 1 対多
Keras の標準的な動作に適合させるには、段階的に入力する必要があるため、必要な長さだけ入力を繰り返すだけです。
outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)
#output_shape -> (batch_size, steps, units)
ステートフルを理解する = 真実
stateful=True
ここで、(コンピュータのメモリに一度に収まらないデータの読み込みを避ける以外に)考えられる用途の1つを紹介します。
Stateful を使用すると、シーケンスの「部分」を段階的に入力できます。違いは次のとおりです。
- では
stateful=False
、2番目のバッチには最初のバッチとは独立した全く新しいシーケンスが含まれています。 - では
stateful=True
、2 番目のバッチが最初のバッチを続行し、同じシーケンスを拡張します。
シーケンスをウィンドウで分割するのと似ていますが、主な違いは次の 2 つです。
- これらのウィンドウは重なり合いません!!
stateful=True
これらのウィンドウは1つの長いシーケンスとして接続されます
ではstateful=True
、すべての新しいバッチは、( を呼び出すまで) 前のバッチの継続として解釈されますmodel.reset_states()
。
- バッチ 2 のシーケンス 1 は、バッチ 1 のシーケンス 1 を継続します。
- バッチ 2 のシーケンス 2 は、バッチ 1 のシーケンス 2 を継続します。
- バッチ 2 のシーケンス n はバッチ 1 のシーケンス n を継続します。
入力の例: バッチ 1 には手順 1 と 2 が含まれ、バッチ 2 には手順 3 から 5 が含まれます。
BATCH 1 BATCH 2
[ Step1 Step2 | [ Step3 Step4 Step5
Tank A: [[Pa1,Ta1], [Pa2,Ta2], | [Pa3,Ta3], [Pa4,Ta4], [Pa5,Ta5]],
Tank B: [[Pb1,Tb1], [Pb2,Tb2], | [Pb3,Tb3], [Pb4,Tb4], [Pb5,Tb5]],
.... |
Tank N: [[Pn1,Tn1], [Pn2,Tn2], | [Pn3,Tn3], [Pn4,Tn4], [Pn5,Tn5]],
] ]
バッチ 1 とバッチ 2 のタンクの位置合わせに注意してください。これが必要な理由ですshuffle=False
(もちろん、1 つのシーケンスのみを使用している場合を除きます)。
バッチはいくつでも、無制限に作成できます。(各バッチの長さを可変にするには、 を使用しますinput_shape=(None,features)
。
stateful=True の 1 対多
ここでのケースでは、1 つの出力ステップを取得してそれを入力にしたいので、バッチごとに 1 つのステップのみを使用します。
図の動作は によって「引き起こされる」のではないことに注意してくださいstateful=True
。以下の手動ループでその動作を強制します。この例では、 によってstateful=True
シーケンスを停止し、必要な操作を行い、停止した場所から続行することが「可能」になります。
正直なところ、このケースでは繰り返しアプローチの方が適しているかもしれません。しかし、 を調べているのでstateful=True
、これは良い例です。これを使用する最良の方法は、次の「多対多」のケースです。
層:
outputs = LSTM(units=features,
stateful=True,
return_sequences=True, #just to keep a nice output shape even with length 1
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
ここで、予測のための手動ループが必要になります。
input_data = someDataWithShape((batch, 1, features))
#important, we're starting new sequences, not continuing old ones:
model.reset_states()
output_sequence = []
last_step = input_data
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
ステートフル=True の多対多
ここで、非常に優れたアプリケーションが得られます。入力シーケンスが与えられた場合、その将来の未知のステップを予測します。
上記の「1対多」と同じ方法を使用していますが、違いは次のとおりです。
- シーケンス自体をターゲットデータとして使用し、一歩先に進みましょう
- シーケンスの一部はわかっています (したがって、結果のこの部分は破棄します)。
レイヤー(上記と同じ):
outputs = LSTM(units=features,
stateful=True,
return_sequences=True,
input_shape=(None,features))(inputs)
#units = features because we want to use the outputs as inputs
#None because we want variable length
#output_shape -> (batch_size, steps, units)
トレーニング:
シーケンスの次のステップを予測するようにモデルをトレーニングします。
totalSequences = someSequencesShaped((batch, steps, features))
#batch size is usually 1 in these cases (often you have only one Tank in the example)
X = totalSequences[:,:-1] #the entire known sequence, except the last step
Y = totalSequences[:,1:] #one step ahead of X
#loop for resetting states at the start/end of the sequences:
for epoch in range(epochs):
model.reset_states()
model.train_on_batch(X,Y)
予測:
予測の最初の段階は「状態の調整」です。そのため、この部分がすでにわかっている場合でも、シーケンス全体を再度予測します。
model.reset_states() #starting a new sequence
predicted = model.predict(totalSequences)
firstNewStep = predicted[:,-1:] #the last step of the predictions is the first future step
ここで、1対多の場合と同じようにループに入ります。ただし、ここでは状態をリセットしないでください。モデルに、シーケンスのどのステップにいるのかを知らせます (そして、上で行った予測により、モデルは最初の新しいステップにいることを知っています)。
output_sequence = [firstNewStep]
last_step = firstNewStep
for i in steps_to_predict:
new_step = model.predict(last_step)
output_sequence.append(new_step)
last_step = new_step
#end of the sequences
model.reset_states()
このアプローチは、次の回答とファイルで使用されました。
- LSTM を使用して時系列の複数の前方時間ステップを予測する
- Keras モデルを使用して将来の日付やイベントを予測するにはどうすればよいですか?
- https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb
複雑な構成を実現する
上記のすべての例では、「1 つのレイヤー」の動作を示しました。
もちろん、必ずしも同じパターンに従う必要はなく、複数のレイヤーを積み重ねて独自のモデルを作成することもできます。
登場している興味深い例の 1 つは、「多対 1 のエンコーダー」の後に「1 対多」のデコーダーが続く「オートエンコーダー」です。
エンコーダー:
inputs = Input((steps,features))
#a few many to many layers:
outputs = LSTM(hidden1,return_sequences=True)(inputs)
outputs = LSTM(hidden2,return_sequences=True)(outputs)
#many to one layer:
outputs = LSTM(hidden3)(outputs)
encoder = Model(inputs,outputs)
デコーダ:
「繰り返し」方式を使用する。
inputs = Input((hidden3,))
#repeat to make one to many:
outputs = RepeatVector(steps)(inputs)
#a few many to many layers:
outputs = LSTM(hidden4,return_sequences=True)(outputs)
#last layer
outputs = LSTM(features,return_sequences=True)(outputs)
decoder = Model(inputs,outputs)
オートエンコーダ:
inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)
autoencoder = Model(inputs,outputs)
トレーニングfit(X,X)
追加説明
LSTM でステップがどのように計算されるか、または上記のケースの詳細を知りたい場合はstateful=True
、次の回答を読んでください。「Keras LSTM の理解」に関する疑問