web-dev-qa-db-ja.com

Keras LSTMについて

私はLSTMについての私の理解を調整しようとしており、ここで指摘しています このChristopher Olahによる投稿 Kerasで実装されています。私は Jason Brownleeが書いた Kerasチュートリアル用のブログをフォローしています。私が主に混乱しているのは、

  1. データ系列を[samples, time steps, features]に変換し、
  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のシーケンスを取り、各エレメントがN-look_back長のシーケンスであるlook_back配列を返します。

タイムステップと機能とは

ご覧のように、TrainXはTime_stepsとFeatureがそれぞれ最後の2つの次元(この特定のコードでは3と1)である3D配列です。下の画像に関して、これはピンクの箱の数が3であるmany to oneのケースを検討しているということですか?あるいはそれは文字通りチェーンの長さが3であることを意味しているのでしょうか(すなわち3つの緑色の枠のみが考慮されています)。 enter image description here 

多変量級数を考えるとき、features引数は適切になりますか?例えば同時に2つの金融株をモデル化する?

ステートフルLSTM

ステートフルLSTMは、バッチ実行の間にセルメモリ値を保存するという意味ですか?この場合、batch_sizeは1で、トレーニングの実行の間にメモリがリセットされるので、それがステートフルであると言うことのポイントでした。これはトレーニングデータがシャッフルされていないという事実に関連していると私は推測していますが、私はその方法がわかりません。

何かご意見は?画像参照: http://karpathy.github.io/2015/05/21/rnn-effectiveness/ /

編集1:

赤と緑のボックスが等しいという@ vanのコメントについて少し混乱しています。確認のため、次のAPI呼び出しは展開された図に対応していますか?特に2番目の図に注意してください(batch_sizeは任意に選択されたものです): enter image description here enter image description here 

編集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

229
sachinruk

まず第一に、あなたは素晴らしいチュートリアル( 12 )を選びます。開始。

タイムステップの意味:X.shapeのTime-steps==3(データ形状の説明)は、ピンク色の枠が3つあることを意味します。 Kerasでは各ステップが入力を必要とするので、それ故に緑の箱の数は通常赤の箱の数に等しいはずです。構造をハックしない限り。

多対多対多対:ケラスでは、LSTMGRUSimpleRNNを初期化するときにreturn_sequencesパラメータがあります。 return_sequencesが(デフォルトで)Falseのとき、それは図に示すように多対一です。その戻り形状は最後の状態を表す(batch_size, hidden_unit_length)です。 return_sequencesTrueのとき、それは多対多です。その戻り形状は(batch_size, time_step, hidden_unit_length)です

features引数は適切になりますか:feature引数は「赤い四角形の大きさ」または各ステップの入力次元とは何ですか?たとえば8種類の市場情報から予測したい場合は、feature==8を使用してデータを生成できます。

ステートフル:あなたは ソースコードを調べることができます 。状態を初期化するとき、もしstateful==Trueなら、最後のトレーニングからの状態が初期状態として使われます、そうでなければそれは新しい状態を生成します。私はまだstatefulをオンにしていません。しかし、私はbatch_sizestateful==Trueの時に1にしかなれないことには同意しません。

現在、収集したデータを使用してデータを生成しています。あなたの株価情報がストリームとして来るのをイメージして、すべてのシーケンシャルを集める日を待つのではなく、あなたはネットワークでトレーニング/予測しながら入力データオンラインを生成したいです。同じネットワークを共有している400株がある場合は、batch_size==400を設定できます。

121
Van

受け入れられた答えを補完するものとして、この答えはケラス行動とそれぞれの絵を達成する方法を示します。

一般的なケラスの行動

標準的なkerasの内部処理は、次の図のように常に多対多です(ここでは、例としてfeatures=2、pressure、およびtemperatureを使用しました)。

ManyToMany

この画像では、他の寸法との混同を避けるために、ステップ数を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つのシーケンスしかありませんが、ウィンドウを作成するために多くのシーケンスに分割しています。

「シーケンスとは」という概念は抽象的です。重要な部分は以下のとおりです。

  • あなたは多くの個々のシーケンスを持つバッチを持つことができます
  • シーケンスをシーケンスにするのは、それらが段階的に(通常タイムステップで)進化するということです。

「単層」で各ケースを達成

多対多の標準を達成する:

StandardManyToMany

return_sequences=Trueを使うと、単純なLSTMレイヤーで多対多を実現できます。

outputs = LSTM(units, return_sequences=True)(inputs)

#output_shape -> (batch_size, steps, units)

多対一を達成する:

まったく同じ層を使っても、kerasはまったく同じ内部前処理を行いますが、return_sequences=Falseを使うと(あるいは単にこの引数を無視すると)、kerasは自動的に最後までのステップを破棄します。

ManyToOne

outputs = LSTM(units)(inputs)

#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned

1対多を達成する

現在、これはkeras LSTMレイヤだけではサポートされていません。あなたはステップを増やすためにあなた自身の戦略を作成しなければならないでしょう。 2つの良い方法があります。

  • テンソルを繰り返して一定のマルチステップ入力を作成する
  • 1つのステップの出力を繰り返し取り、それを次のステップの入力として使用するには、stateful=Trueを使用します(output_features == input_featuresが必要)。

繰り返しベクトルで1対多

Kerasの標準的な振る舞いに合わせるためには、段階的に入力する必要があります。そのため、必要な長さだけ入力を繰り返すだけです。

OneToManyRepeat

outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)

#output_shape -> (batch_size, steps, units)

ステートフルを理解する= True

今すぐstateful=Trueの考えられる使用法の1つが来ます(あなたのコンピュータのメモリに一度に収まることができないデータをロードすることを避けること以外に)

ステートフルでは、シーケンスの「部分」を段階的に入力できます。違いは:

  • stateful=Falseでは、2番目のバッチに、最初のバッチとは別に、まったく新しいシーケンスが含まれています。
  • stateful=Trueでは、2番目のバッチが最初のバッチを継続し、同じシーケンスを拡張します。

これら2つの主な違いは、ウィンドウ内でシーケンスを分割するのと同じです。

  • これらのウィンドウは重なりません。
  • stateful=Trueは、これらのウィンドウが単一の長いシーケンスとして接続されているのを確認します。

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)を使用してください。

ステートフル= Trueの1対多

ここでは、1つの出力ステップを取得してそれを入力にしたいので、バッチごとに1ステップだけを使用します。

画像内の動作はstateful=Trueによって「引き起こされる」わけではないことに注意してください。以下の手動ループでその振る舞いを強制します。この例では、stateful=Trueはシーケンスを停止し、必要なものを操作し、停止したところから続行することを "許可"するものです。

OneToManyStateful

正直なところ、このアプローチにはおそらくリピートアプローチがより良い選択です。しかし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対多」と同じ方法を使用していますが、次の点が異なります。

  • シーケンス自体をターゲットデータとして使用します。
  • シーケンスの一部を知っているので(結果のこの部分は破棄します)。

ManyToManyStateful

レイヤー(上記と同じ)

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つは、「多対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)

デコーダ:

"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ケースについての詳細が欲しいなら、あなたはこの答えでもっと読むことができます: `Keras LSTMを理解することに関する疑問

102
Daniel Möller

RNNの最後のレイヤーにreturn_sequenceがある場合は、代わりにTimeDistributedを使用して単純なDenseレイヤーを使用することはできません。

これは他の人に役立つかもしれないサンプルコードの一部です。

words = keras.layers.Input(batch_shape =(None、self.maxSequenceLength)、name = "input")

    # 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))
0
Sanjay Krishna