私は、Kerasに実装されたLSTMとChristopher Olahによるこの投稿で指摘されたことについて、私の理解を一致させようとしています。私はKerasのチュートリアルのためにJason Brownleeによって書かれたブログに従っています。私が主に混乱しているのは、次のとおりです。
1.データ系列を [サンプル、タイムステップ、特徴]
にリシェイプすること、および。
2.ステートフル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 のシーケンスを受け取り、各要素が look_back
長さのシーケンスである N-look_back
配列を返します。
TrainX は3次元配列で、Time_steps と Feature はそれぞれ最後の2次元(このコードでは3、1)です。下の画像はピンクのボックスが3つある「many to one」ケースを考えているということでしょうか?それとも文字通り、チェーンの長さが3である(つまり、3つの緑のボックスだけを考慮する)ことを意味するのでしょうか?![ここに画像の説明を入力]]3。
特徴量の議論は、多変量系列を考慮するときに重要になるのでしょうか? 例えば、2つの金融株を同時にモデル化する場合などです。
ステートフルLSTMとは、バッチの実行の間にセルメモリの値を保存するということでしょうか?もしそうなら、batch_size
は1であり、メモリは学習実行の間にリセットされるので、ステートフルであると言った意味は何だったのでしょうか。学習データがシャッフルされないことと関係があるのでしょうが、どうなんでしょう。
何か思い当たることはありますか? 画像参照元:http://karpathy.github.io/2015/05/21/rnn-effectiveness/
赤と緑のボックスが等しいという @van's のコメントについて、少し混乱しています。そこで確認ですが、以下のAPIコールは展開された図に対応しているのでしょうか?特に2番目の図(batch_size
が任意に選ばれている)に注目してください。
![ここに画像の説明を入力]]4。
。
Udacity's の深層学習コースを受講して、まだ time_step 引数に混乱している人は、次のディスカッションを見てください: https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169
model.add(TimeDistributed(Dense(vocab_len)))` が私が探していたものであることがわかった。以下はその例である。https://github.com/sachinruk/ShakespeareBot
LSTMについて私が理解していることのほとんどをここにまとめました:
まず、素晴らしいチュートリアル(1,2)を選んでスタートします。
タイムステップの意味:X.shape (Describing data shape)の Time-steps==3
は、ピンクの箱が3つあることを意味します。Kerasでは各ステップに入力が必要なので、通常は緑のボックスの数と赤のボックスの数は等しくなるはずです。構造をハックしない限りは。
多対多 vs 多対一。kerasでは、LSTM
やGRU
、SimpleRNN
の初期化時に return_sequences
というパラメータがあります。デフォルトで return_sequences
が False
の場合、図のように many to one となる。戻り値の形状は (batch_size, hidden_unit_length)
であり、これは最後の状態を表している。return_sequencesが
Trueの場合は、 **many to many** となる。その戻り値の形状は
(batch_size, time_step, hidden_unit_length)` である。
引数featuresは関係ありますか?feature 引数は "How big is your red box" あるいは各ステップの入力次元が何であるかということです。例えば8種類のマーケット情報から予測したいのであれば、feature==8
でデータを生成すればよい。
Stateful:ソースコード]3を参照してください。状態を初期化するとき、stateful==True
なら前回の学習時の状態を初期状態として使用し、それ以外は新しい状態を生成する。私はまだstateful
をオンにしていません。しかし、stateful==True
の時にbatch_size
が1にしかならないのは納得がいきません。
現状では、収集したデータでデータを生成していますね。株価情報がストリームで流れてくるイメージで、1日かけてシーケンシャルに収集するよりも、オンラインで入力データを生成しながら、ネットワークで学習・予測したいですよね。もし、400の銘柄が同じネットワークを共有しているのであれば、「batch_size==400」と設定すればよいでしょう。
合格答案の補完として、kerasの動作と各絵柄の実現方法を示す答案です。
標準的なkerasの内部処理は、以下の図のように常に多対多です(ここでは例としてfeatures=2
、圧力と温度を使っています)。
この画像では、他の次元との混同を避けるために、ステップ数を5つに増やしました。
この例では
(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つのシーケンスしかありませんが、それを多くのシーケンスに分割してウィンドウを作成していることに注意してください。 シーケンスとは何か」という概念は抽象的です。重要なのは、以下の部分です。
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
全く同じレイヤーを使って、全く同じ内部前処理を行いますが、return_sequences=False
を使うと(あるいは単にこの引数を無視すると)、kerasは最後から前のステップを自動的に破棄します。
![ManyToOne]です。
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
さて、これはkerasのLSTM層だけでは対応できない。ステップを掛け合わせる戦略を独自に作る必要があります。良いアプローチは2つあります。
を使って、あるステップの出力を再帰的に受け取り、それを次のステップの入力として提供する (
output_features == input_features` が必要)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つを紹介します(コンピュータのメモリに収まりきらないデータを一度に読み込まないようにする以外に)。
ステートフルでは、シーケンスの "part" を段階的に入力することができます。という違いがあります。
では、これらのウィンドウが1つの長いシーケンスとして接続されているのがわかります。 stateful=True
では、新しいバッチはすべて前のバッチの続きとして解釈されます (model.reset_states()
を呼び出すまで)。 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)
を使ってください。
今回のケースでは、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()
さて、ここで非常に素晴らしいアプリケーションを手に入れた。入力シーケンスが与えられたら、その将来の未知のステップを予測しようとする。 上記のquot;one to many"と同じ方法を使っていますが、違う点は。
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()
この手法は、この回答やファイルでも使われていました。
上記のすべての例では、1つのレイヤーの挙動を示しました。 もちろん、すべてのレイヤーが同じパターンに従っている必要はなく、多くのレイヤーを重ねて独自のモデルを作成することができます。 面白い例としては、quot;many to oneエンコーダとquot;one to manyデコーダを持つquot;autoencoder"が登場したことがある。 エンコーダ:
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)
デコーダ: repeat"メソッドを使用します。
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
の場合についての詳細が知りたい場合は、こちらの回答が参考になります: https://stackoverflow.com/questions/53955093/doubts-regarding-understanding-keras-lstms
RNNの最終層にreturn_sequencesがある場合、単純なDense層は使用できず、TimeDistributed層が使用される。
以下は、他の人の参考になるかもしれないコード例です。
words = keras.layers.Input(batch_shape=(None, self.maxSequenceLength), name = "入力")
# Build a matrix of size vocabularySize x EmbeddingDimension
# where each row corresponds to a "word embedding" vector.
# This layer will convert replace each word-id with a word-vector of size Embedding Dimension.
embeddings = keras.layers.embeddings.Embedding(self.vocabularySize, self.EmbeddingDimension,
name = "embeddings")(words)
# Pass the word-vectors to the LSTM layer.
# We are setting the hidden-state size to 512.
# The output will be batchSize x maxSequenceLength x hiddenStateSize
hiddenStates = keras.layers.GRU(512, return_sequences = True,
input_shape=(self.maxSequenceLength,
self.EmbeddingDimension),
name = "rnn")(embeddings)
hiddenStates2 = keras.layers.GRU(128, return_sequences = True,
input_shape=(self.maxSequenceLength, self.EmbeddingDimension),
name = "rnn2")(hiddenStates)
denseOutput = TimeDistributed(keras.layers.Dense(self.vocabularySize),
name = "linear")(hiddenStates2)
predictions = TimeDistributed(keras.layers.Activation("softmax"),
name = "softmax")(denseOutput)
# Build the computational graph by specifying the input, and output of the network.
model = keras.models.Model(input = words, output = predictions)
# model.compile(loss='kullback_leibler_divergence', \
model.compile(loss='sparse_categorical_crossentropy', \
optimizer = keras.optimizers.Adam(lr=0.009, \
beta_1=0.9,\
beta_2=0.999, \
epsilon=None, \
decay=0.01, \
amsgrad=False))