Jako uzupełnienie przyjętej odpowiedzi, ta odpowiedź pokazuje zachowania keras i sposób uzyskania każdego zdjęcia.
Ogólne zachowanie Keras
Standardowe przetwarzanie wewnętrzne keras jest zawsze wiele do wielu, jak na poniższym obrazku (gdzie użyłem features=2
, ciśnienie i temperatura, tylko jako przykład):
Na tym obrazie zwiększyłem liczbę kroków do 5, aby uniknąć pomyłek z innymi wymiarami.
W tym przykładzie:
- Mamy N zbiorników oleju
- Spędziliśmy 5 godzin, podejmując działania co godzinę (kroki czasowe)
- Zmierzyliśmy dwie cechy:
- Ciśnienie P
- Temperatura T.
Nasza tablica wejściowa powinna zatem mieć kształ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]],
]
Wejścia do przesuwnych okien
Często warstwy LSTM mają przetwarzać całe sekwencje. Dzielenie okien może nie być najlepszym pomysłem. Warstwa ma wewnętrzne stany dotyczące ewolucji sekwencji w miarę postępów. Windows eliminuje możliwość uczenia się długich sekwencji, ograniczając wszystkie sekwencje do rozmiaru okna.
W oknach każde okno jest częścią długiej oryginalnej sekwencji, ale według Keras będą one postrzegane jako niezależna sekwencja:
[ 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]],
....
]
Zauważ, że w tym przypadku początkowo masz tylko jedną sekwencję, ale dzielisz ją na wiele sekwencji, aby utworzyć okna.
Pojęcie „czym jest sekwencja” jest abstrakcyjne. Ważne części to:
- możesz mieć partie z wieloma pojedynczymi sekwencjami
- to, co sprawia, że sekwencje są sekwencjami, polega na tym, że ewoluują one etapami (zwykle etapami czasowymi)
Osiągnięcie każdego przypadku dzięki „pojedynczym warstwom”
Osiągnięcie standardu wielu do wielu:
Możesz osiągnąć wiele do wielu za pomocą prostej warstwy LSTM, używając return_sequences=True
:
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
Osiągnięcie wielu w jednym:
Używając dokładnie tej samej warstwy, keras wykona dokładnie takie samo wewnętrzne przetwarzanie wstępne, ale kiedy użyjesz return_sequences=False
(lub po prostu zignorujesz ten argument), keras automatycznie odrzuci kroki poprzedzające ostatnie:
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Osiągnięcie jednego do wielu
Teraz nie jest to obsługiwane tylko przez same warstwy LSTM keras. Będziesz musiał stworzyć własną strategię, aby pomnożyć kroki. Istnieją dwa dobre podejścia:
- Stwórz stały, wieloetapowy wkład, powtarzając tensor
- Użyj a,
stateful=True
aby cyklicznie pobierać dane wyjściowe z jednego kroku i służyć jako dane wejściowe do następnego kroku (potrzeby output_features == input_features
)
Jeden do wielu z powtarzalnym wektorem
Aby dopasować się do standardowego zachowania keras, potrzebujemy danych wejściowych w krokach, więc po prostu powtarzamy dane wejściowe dla pożądanej długości:
outputs = RepeatVector(steps)(inputs) #where inputs is (batch,features)
outputs = LSTM(units,return_sequences=True)(outputs)
#output_shape -> (batch_size, steps, units)
Zrozumienie stanowe = Prawda
Teraz pojawia się jedno z możliwych zastosowań stateful=True
(oprócz unikania ładowania danych, które nie mogą jednocześnie zmieścić się w pamięci komputera)
Stateful pozwala nam wprowadzać „części” sekwencji etapami. Różnica polega na:
- W
stateful=False
, druga partia zawiera zupełnie nowe sekwencje, niezależne od pierwszej partii
- W
stateful=True
drugiej partii kontynuuje pierwszą partię, przedłużając te same sekwencje.
To tak, jakby podzielić sekwencje również w oknach, z tymi dwiema głównymi różnicami:
- te okna nie nakładają się !!
stateful=True
zobaczą te okna połączone jako jedną długą sekwencję
W stateful=True
, każda nowa partia będzie interpretowana jako kontynuacja poprzedniej partii (aż do wywołania model.reset_states()
).
- Sekwencja 1 w partii 2 będzie kontynuować sekwencję 1 w partii 1.
- Sekwencja 2 w partii 2 będzie kontynuować sekwencję 2 w partii 1.
- Sekwencja nw partii 2 będzie kontynuować sekwencję nw partii 1.
Przykład danych wejściowych, partia 1 zawiera kroki 1 i 2, partia 2 zawiera kroki od 3 do 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]],
] ]
Zwróć uwagę na wyrównanie zbiorników w partii 1 i 2! Właśnie dlatego potrzebujemy shuffle=False
(chyba że używamy tylko jednej sekwencji).
Możesz mieć dowolną liczbę partii w nieskończoność. (Aby mieć zmienne długości w każdej partii, użyj input_shape=(None,features)
.
Jeden do wielu z stateful = True
W naszym przypadku zastosujemy tylko 1 krok na partię, ponieważ chcemy uzyskać jeden krok wyjściowy i ustawić go jako dane wejściowe.
Zwróć uwagę, że zachowanie na zdjęciu nie jest „spowodowane przez” stateful=True
. Zmusimy to zachowanie do ręcznej pętli poniżej. W tym przykładzie stateful=True
„pozwala” nam zatrzymać sekwencję, manipulować tym, co chcemy i kontynuować od miejsca, w którym się zatrzymaliśmy.
Szczerze mówiąc, powtórzenie podejścia jest prawdopodobnie lepszym wyborem w tym przypadku. Ale skoro się przyglądamy stateful=True
, to dobry przykład. Najlepszym sposobem na użycie tego jest następna sprawa „wielu do wielu”.
Warstwa:
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)
Teraz potrzebujemy ręcznej pętli do prognoz:
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()
Wiele do wielu z stateful = True
Teraz mamy bardzo ładną aplikację: biorąc pod uwagę sekwencję wejściową, spróbuj przewidzieć jej przyszłe nieznane kroki.
Używamy tej samej metody, co w powyższej sekcji „jeden do wielu”, z tą różnicą, że:
- wykorzystamy samą sekwencję jako dane docelowe, o krok do przodu
- znamy część sekwencji (więc odrzucamy tę część wyników).
Warstwa (taka sama jak powyżej):
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)
Trening:
Będziemy trenować nasz model, aby przewidzieć następny krok sekwencji:
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)
Prognozowanie:
Pierwszy etap naszego przewidywania polega na „unicestwieniu stanów”. Dlatego ponownie przewidzieliśmy całą sekwencję, nawet jeśli znamy już tę jej część:
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
Teraz przechodzimy do pętli, jak w przypadku jednego do wielu. Ale nie resetuj stanów tutaj! . Chcemy, aby model wiedział, w którym etapie sekwencji jest (i wie, że jest to na pierwszym nowym etapie z powodu prognozy, którą właśnie stworzyliśmy powyżej)
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()
Takie podejście zastosowano w tych odpowiedziach i pliku:
Osiąganie złożonych konfiguracji
We wszystkich powyższych przykładach pokazałem zachowanie „jednej warstwy”.
Możesz oczywiście układać wiele warstw jeden na drugim, niekoniecznie wszystkie według tego samego wzoru i tworzyć własne modele.
Jednym z interesujących przykładów, które się pojawiły, jest „autoencoder” z koderem „jeden do wielu”, a następnie dekoder „jeden do wielu”:
Enkoder:
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:
Korzystanie z metody „powtarzania”;
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)
Trenuj z fit(X,X)
Dodatkowe wyjaśnienia
Jeśli chcesz uzyskać szczegółowe informacje na temat obliczania kroków w LSTM lub szczegóły dotyczące stateful=True
powyższych przypadków, możesz przeczytać więcej w tej odpowiedzi: Wątpliwości dotyczące `Zrozumienia LSTM Keras`