Memahami Keras LSTMs

311

Saya mencoba mendamaikan pemahaman saya tentang LSTM dan ditunjukkan di sini di posting ini oleh Christopher Olah diimplementasikan di Keras. Saya mengikuti blog yang ditulis oleh Jason Brownlee untuk tutorial Keras. Yang paling membuat saya bingung adalah,

  1. Pembentukan kembali seri data menjadi [samples, time steps, features]dan,
  2. LSTM stateful

Mari kita berkonsentrasi pada dua pertanyaan di atas dengan mengacu pada kode yang ditempel di bawah ini:

# 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()

Catatan: create_dataset mengambil urutan panjang N dan mengembalikan N-look_backarray yang masing-masing elemen adalah look_backurutan panjang.

Apa itu Langkah dan Fitur Waktu?

Seperti dapat dilihat TrainX adalah array 3-D dengan Time_steps dan Fitur menjadi dua dimensi terakhir masing-masing (3 dan 1 dalam kode khusus ini). Sehubungan dengan gambar di bawah, apakah ini berarti bahwa kami sedang mempertimbangkan many to onekasus ini, di mana jumlah kotak merah muda adalah 3? Atau apakah secara harfiah berarti panjang rantai adalah 3 (yaitu hanya 3 kotak hijau yang dipertimbangkan).masukkan deskripsi gambar di sini

Apakah argumen fitur menjadi relevan ketika kita mempertimbangkan seri multivarian? mis. memodelkan dua saham finansial secara bersamaan?

LSTM Stateful

Apakah LSTMs stateful berarti bahwa kita menyimpan nilai memori sel antara sejumlah batch? Jika ini masalahnya, batch_sizeadalah satu, dan memori diatur ulang di antara latihan yang berjalan, jadi apa gunanya mengatakan bahwa itu stateful. Saya menduga ini terkait dengan fakta bahwa data pelatihan tidak dikocok, tapi saya tidak yakin bagaimana caranya.

Adakah pikiran? Referensi gambar: http://karpathy.github.io/2015/05/21/rnn-effectiveness/

Edit 1:

Agak bingung tentang komentar @ van tentang kotak merah dan hijau yang sama. Jadi hanya untuk mengonfirmasi, apakah panggilan API berikut sesuai dengan diagram yang belum dibuka? Terutama mencatat diagram kedua ( batch_sizedipilih secara sewenang-wenang.): masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Edit 2:

Untuk orang-orang yang telah melakukan kursus pembelajaran mendalam Udacity dan masih bingung tentang argumen time_step, lihat diskusi berikut: https://discussions.udacity.com/t/rnn-lstm-use-implementation/163169

Memperbarui:

Ternyata model.add(TimeDistributed(Dense(vocab_len)))apa yang saya cari. Berikut ini sebuah contoh: https://github.com/sachinruk/ShakespeareBot

Pembaruan2:

Saya telah merangkum sebagian besar pemahaman saya tentang LSTM di sini: https://www.youtube.com/watch?v=ywinX5wgdEU

sachinruk
sumber
7
Foto pertama harus (batch_size, 5, 1); foto kedua harus (batch_size, 4, 3) (jika tidak ada urutan berikut). Dan mengapa outputnya masih "X"? Haruskah itu "Y"?
Van
1
Di sini saya berasumsi X_1, X_2 ... X_6 adalah nomor tunggal. Dan tiga angka (X_1, X_2, X_3) membuat vektor bentuk (3,). Satu angka (X_1) membuat vektor bentuk (1,).
Van
2
@ Van, anggapan Anda benar. Itu menarik, jadi pada dasarnya model tidak mempelajari pola di luar jumlah time_steps. Jadi jika saya memiliki deret waktu dengan panjang 1000, dan secara visual dapat melihat pola setiap 100 hari, saya harus membuat parameter time_steps minimal 100. Apakah ini pengamatan yang benar?
sachinruk
3
Iya. Dan jika Anda dapat mengumpulkan 3 fitur yang relevan per hari, maka Anda dapat mengatur ukuran fitur menjadi 3 seperti yang Anda lakukan pada foto kedua. Di bawah keadaan itu, bentuk input akan menjadi (batch_size, 100, 3).
Van
1
dan untuk menjawab pertanyaan pertama Anda, itu karena saya mengambil satu rangkaian waktu. Misalnya harga saham, jadi X dan Y berasal dari seri yang sama.
sachinruk

Jawaban:

173

Pertama-tama, Anda memilih tutorial yang bagus ( 1 , 2 ) untuk memulai.

Apa Time-step artinya : Time-steps==3dalam X.shape (Menjelaskan bentuk data) berarti ada tiga kotak merah muda. Karena dalam Keras setiap langkah memerlukan input, oleh karena itu jumlah kotak hijau biasanya harus sama dengan jumlah kotak merah. Kecuali Anda meretas struktur.

banyak ke banyak vs banyak ke satu : Di keras, ada return_sequencesparameter saat Anda menginisialisasi LSTMatau GRUatau SimpleRNN. Ketika return_sequencesadalah False(secara default), maka itu adalah banyak untuk satu seperti yang ditunjukkan pada gambar. Bentuk kembalinya adalah (batch_size, hidden_unit_length), yang mewakili kondisi terakhir. Ketika return_sequencesini True, maka banyak ke banyak . Bentuk kembalinya adalah(batch_size, time_step, hidden_unit_length)

Apakah argumen fitur menjadi relevan : Argumen fitur berarti "Seberapa besar kotak merah Anda" atau berapa dimensi input setiap langkah. Jika Anda ingin memprediksi dari, katakanlah, 8 jenis informasi pasar, maka Anda dapat menghasilkan data Anda feature==8.

Stateful : Anda dapat mencari kode sumber . Ketika menginisialisasi keadaan, jika stateful==True, maka keadaan dari pelatihan terakhir akan digunakan sebagai keadaan awal, jika tidak maka akan menghasilkan keadaan baru. Saya belum nyalakan stateful. Namun, saya tidak setuju dengan itu batch_sizehanya bisa 1 kapan stateful==True.

Saat ini, Anda menghasilkan data Anda dengan data yang dikumpulkan. Gambar informasi stok Anda datang sebagai aliran, daripada menunggu satu hari untuk mengumpulkan semua berurutan, Anda ingin menghasilkan data input online saat pelatihan / prediksi dengan jaringan. Jika Anda memiliki 400 saham yang berbagi jaringan yang sama, maka Anda dapat mengatur batch_size==400.

mobil van
sumber
Sedikit bingung tentang mengapa kotak merah dan hijau harus sama. Bisakah Anda melihat hasil edit yang saya buat (terutama gambar baru) dan komentar?
sachinruk
1
Memang. Periksa dokumen:stateful: Boolean (default False). If True, the last state for each sample at index i in a batch will be used as initial state for the sample of index i in the following batch.
Van
1
@Bisa Jika saya memiliki rangkaian waktu multivarian, haruskah saya tetap menggunakan lookback = 1?
losmen
1
Mengapa dimensi LSTM dari ruang output (32) berbeda dari jumlah neuron (sel LSTM)?
Sticky
1
Tambahan untuk stateful=True: Ukuran bets bisa apa saja yang Anda suka, tetapi Anda harus menaatinya. Jika Anda membangun model Anda dengan ukuran 5, maka semua fit(), predict()dan metode terkait akan membutuhkan 5. Namun perlu dicatat bahwa keadaan ini tidak akan disimpan model.save(), yang mungkin tampak tidak diinginkan. Namun Anda dapat secara manual menambahkan status ke file hdf5, jika Anda memerlukannya. Tetapi secara efektif ini memungkinkan Anda untuk mengubah ukuran batch hanya dengan menyimpan dan memuat ulang model.
jlh
192

Sebagai pelengkap jawaban yang diterima, jawaban ini menunjukkan perilaku keras dan cara mencapai setiap gambar.

Perilaku keras umum

Pemrosesan internal keras standar selalu banyak ke banyak seperti pada gambar berikut (di mana saya menggunakan features=2, tekanan dan suhu, hanya sebagai contoh):

Banyak ke banyak

Dalam gambar ini, saya meningkatkan jumlah langkah menjadi 5, untuk menghindari kebingungan dengan dimensi lain.

Untuk contoh ini:

  • Kami memiliki N tangki minyak
  • Kami menghabiskan 5 jam untuk mengambil tindakan setiap jam (langkah waktu)
  • Kami mengukur dua fitur:
    • Tekanan P
    • Suhu t

Array input kami kemudian harus berbentuk seperti (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]],
        ]

Input untuk jendela geser

Seringkali, lapisan LSTM seharusnya memproses seluruh urutan. Membagi windows mungkin bukan ide terbaik. Lapisan memiliki keadaan internal tentang bagaimana suatu urutan berkembang ketika ia melangkah maju. Windows menghilangkan kemungkinan mempelajari urutan panjang, membatasi semua urutan ke ukuran jendela.

Di windows, setiap jendela adalah bagian dari urutan asli yang panjang, tetapi oleh Keras mereka akan dilihat masing-masing sebagai urutan independen:

        [     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]],
  ....
        ]

Perhatikan bahwa dalam kasus ini, Anda awalnya hanya memiliki satu urutan, tetapi Anda membaginya dalam banyak urutan untuk membuat windows.

Konsep "apa itu urutan" adalah abstrak. Bagian penting adalah:

  • Anda dapat memiliki batch dengan banyak urutan individual
  • apa yang membuat urutan menjadi urutan adalah bahwa mereka berevolusi dalam langkah (biasanya langkah waktu)

Mencapai setiap kasus dengan "lapisan tunggal"

Mencapai standar banyak ke banyak:

StandardManyToMany

Anda dapat mencapai banyak ke banyak dengan lapisan LSTM sederhana, menggunakan return_sequences=True:

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

#output_shape -> (batch_size, steps, units)

Mencapai banyak orang menjadi satu:

Dengan menggunakan layer yang sama persis, keras akan melakukan preprocessing internal yang sama persis, tetapi ketika Anda menggunakan return_sequences=False(atau mengabaikan argumen ini), keras akan secara otomatis membuang langkah-langkah sebelum yang terakhir:

ManyToOne

outputs = LSTM(units)(inputs)

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

Mencapai satu ke banyak

Sekarang, ini tidak didukung oleh lapisan LSTM yang keras saja. Anda harus membuat strategi sendiri untuk melipatgandakan langkah-langkahnya. Ada dua pendekatan yang baik:

  • Buat input multi-langkah yang konstan dengan mengulang tensor
  • Gunakan a stateful=Trueuntuk secara berulang mengambil output dari satu langkah dan menyajikannya sebagai input dari langkah berikutnya (kebutuhan output_features == input_features)

Satu ke banyak dengan vektor berulang

Agar sesuai dengan perilaku standar yang keras, kita membutuhkan input dalam langkah-langkah, jadi, kita cukup mengulangi input untuk panjang yang kita inginkan:

OneToManyUlangi

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

#output_shape -> (batch_size, steps, units)

Memahami stateful = Benar

Kini hadir salah satu kemungkinan penggunaan stateful=True(selain menghindari pemuatan data yang tidak dapat memuat memori komputer Anda sekaligus)

Stateful memungkinkan kita untuk memasukkan "bagian" dari urutan secara bertahap. Perbedaannya adalah:

  • Dalam stateful=False, batch kedua berisi urutan baru, terlepas dari batch pertama
  • Dalam stateful=True, batch kedua melanjutkan batch pertama, memperpanjang urutan yang sama.

Ini seperti membagi urutan di windows juga, dengan dua perbedaan utama ini:

  • windows ini tidak menempatkan !!
  • stateful=True akan melihat jendela-jendela ini terhubung sebagai satu urutan panjang

Dalam stateful=True, setiap batch baru akan ditafsirkan sebagai melanjutkan batch sebelumnya (sampai Anda menelepon model.reset_states()).

  • Urutan 1 dalam batch 2 akan melanjutkan urutan 1 dalam batch 1.
  • Urutan 2 dalam batch 2 akan melanjutkan urutan 2 dalam batch 1.
  • Urutan n dalam batch 2 akan melanjutkan urutan n dalam batch 1.

Contoh input, batch 1 berisi langkah 1 dan 2, batch 2 berisi langkah 3 hingga 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]],
        ]                                  ]

Perhatikan keselarasan tangki dalam kelompok 1 dan kelompok 2! Itu sebabnya kita perlu shuffle=False(kecuali kita hanya menggunakan satu urutan, tentu saja).

Anda dapat memiliki sejumlah batch, tanpa batas. (Untuk memiliki panjang variabel dalam setiap batch, gunakan input_shape=(None,features).

Satu ke banyak dengan stateful = Benar

Untuk kasus kami di sini, kami hanya akan menggunakan 1 langkah per batch, karena kami ingin mendapatkan satu langkah output dan menjadikannya sebagai input.

Harap perhatikan bahwa perilaku dalam gambar ini bukan "disebabkan oleh" stateful=True. Kami akan memaksakan perilaku itu dalam loop manual di bawah ini. Dalam contoh ini, stateful=Trueadalah apa yang "memungkinkan" kita untuk menghentikan urutan, memanipulasi apa yang kita inginkan, dan melanjutkan dari tempat kita berhenti.

OneToManyStateful

Sejujurnya, pendekatan berulang mungkin merupakan pilihan yang lebih baik untuk kasus ini. Tapi karena kita melihat ke dalam stateful=True, ini adalah contoh yang bagus. Cara terbaik untuk menggunakan ini adalah kasus "banyak ke banyak" berikutnya.

Lapisan:

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) 

Sekarang, kita akan memerlukan loop manual untuk prediksi:

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()

Banyak ke banyak dengan stateful = Benar

Sekarang, di sini, kita mendapatkan aplikasi yang sangat bagus: diberi urutan input, cobalah untuk memprediksi langkah-langkah yang tidak diketahui di masa depan.

Kami menggunakan metode yang sama seperti pada "satu ke banyak" di atas, dengan perbedaan bahwa:

  • kami akan menggunakan urutan itu sendiri untuk menjadi data target, selangkah lebih maju
  • kami tahu bagian dari urutan (jadi kami membuang bagian dari hasil ini).

ManyToManyStateful

Layer (sama seperti di atas):

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) 

Latihan:

Kami akan melatih model kami untuk memprediksi langkah selanjutnya dari urutan:

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)

Memprediksi:

Tahap pertama dari prediksi kami melibatkan "membenarkan negara". Itu sebabnya kita akan memprediksi seluruh urutan lagi, bahkan jika kita sudah tahu bagiannya:

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

Sekarang kita pergi ke loop seperti pada kasus satu ke banyak. Tapi jangan setel ulang status di sini! . Kami ingin model mengetahui di mana urutan urutannya (dan ia tahu itu pada langkah baru pertama karena prediksi yang baru saja kami buat di atas)

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()

Pendekatan ini digunakan dalam jawaban dan file ini:

Mencapai konfigurasi yang kompleks

Dalam semua contoh di atas, saya menunjukkan perilaku "satu lapisan".

Anda dapat, tentu saja, menumpuk banyak lapisan di atas satu sama lain, tidak semua harus mengikuti pola yang sama, dan membuat model Anda sendiri.

Salah satu contoh menarik yang telah muncul adalah "autoencoder" yang memiliki "banyak ke satu encoder" diikuti oleh decoder "satu ke banyak":

Encoder:

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)

Dekoder:

Menggunakan metode "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)

Autoencoder:

inputs = Input((steps,features))
outputs = encoder(inputs)
outputs = decoder(outputs)

autoencoder = Model(inputs,outputs)

Berlatih dengan fit(X,X)

Penjelasan tambahan

Jika Anda ingin detail tentang bagaimana langkah-langkah dihitung dalam LSTMs, atau detail tentang stateful=Truekasus - kasus di atas, Anda dapat membaca lebih lanjut dalam jawaban ini: Keraguan mengenai `Memahami Keras LSTMs`

Daniel Möller
sumber
1
Penggunaan stateful sangat menarik dengan menggunakan output sebagai input. Sama seperti catatan tambahan, cara lain untuk melakukan ini adalah dengan menggunakan API Keras fungsional (seperti yang Anda lakukan di sini, meskipun saya yakin Anda bisa menggunakan yang berurutan), dan cukup menggunakan kembali sel LSTM yang sama untuk setiap langkah waktu , sambil melewati status hasil dan keluaran dari sel ke dirinya sendiri. Yaitu my_cell = LSTM(num_output_features_per_timestep, return_state=True), diikuti oleh lingkarana, _, c = my_cell(output_of_previous_time_step, initial_states=[a, c])
Jacob R
1
Sel dan panjang adalah nilai yang sepenuhnya independen. Tidak ada gambar yang mewakili jumlah "sel". Semuanya untuk "panjang".
Daniel Möller
1
@ DanielMöller Saya tahu sedikit terlambat, tetapi jawaban Anda benar-benar menarik perhatian saya. Satu poin Anda menghancurkan segalanya tentang pemahaman saya tentang apa batch untuk LSTM. Anda memberikan contoh dengan N tank, lima langkah dan dua fitur. Saya percaya bahwa, jika batch misalnya dua, itu berarti bahwa dua sampel (tangki dengan fitur 5 langkah 2) akan dimasukkan ke dalam jaringan dan setelah itu bobot akan disesuaikan. Tetapi jika saya benar mengerti, Anda menyatakan bahwa batch 2 berarti bahwa timesteps dari sampel akan dibagi menjadi 2 dan setengah dari semua sampel akan diumpankan ke LSTM-> pembaruan berat dan kemudian yang kedua.
viceriel
1
Iya. Pada status penuh = Benar, kumpulan 1 = grup sampel, perbarui. Kemudian batch 2 = langkah lebih lanjut untuk kelompok sampel yang sama, perbarui.
Daniel Möller
2
Saya berharap saya dapat memperbaiki ini 100 kali. Jawaban super berguna.
adamconkey
4

Ketika Anda memiliki return_afterences di lapisan terakhir RNN Anda, Anda tidak dapat menggunakan lapisan Dense sederhana sebagai gantinya menggunakan TimeDistributed.

Ini adalah contoh kode yang dapat membantu orang lain.

kata = keras.layers.Input (batch_shape = (Tidak ada, 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))
Sanjay Krishna
sumber