В качестве дополнения к принятому ответу, этот ответ показывает поведение keras и способы достижения каждой картины.
Поведение генерала Кераса
Стандартная внутренняя обработка keras всегда много-много, как показано на следующем рисунке (где я использовал features=2
давление и температуру, просто в качестве примера):
На этом изображении я увеличил количество шагов до 5, чтобы избежать путаницы с другими измерениями.
Для этого примера:
- У нас есть N нефтяных резервуаров
- Мы потратили 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 должны обрабатывать целые последовательности. Разделительные окна могут быть не лучшей идеей. Слой имеет внутренние состояния о том, как последовательность развивается по мере продвижения вперед. Windows исключает возможность изучения длинных последовательностей, ограничивая все последовательности размером окна.
В окнах каждое окно является частью длинной оригинальной последовательности, но для 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]],
....
]
Обратите внимание, что в этом случае у вас изначально есть только одна последовательность, но вы делите ее на несколько последовательностей для создания окон.
Понятие «что такое последовательность» абстрактно. Важными частями являются:
- Вы можете иметь партии со многими отдельными последовательностями
- что делает последовательности последовательностями, так это то, что они развиваются поэтапно (обычно временные шаги)
Достижение каждого случая с «отдельными слоями»
Достижение стандарта много ко многим:
Вы можете достичь многого с помощью простого слоя LSTM, используя return_sequences=True
:
outputs = LSTM(units, return_sequences=True)(inputs)
#output_shape -> (batch_size, steps, units)
Достижение многих к одному:
Используя точно такой же слой, keras будет выполнять ту же самую внутреннюю предварительную обработку, но когда вы используете return_sequences=False
(или просто игнорируете этот аргумент), keras автоматически отбрасывает шаги, предшествующие последнему:
outputs = LSTM(units)(inputs)
#output_shape -> (batch_size, units) --> steps were discarded, only the last was returned
Достижение один ко многим
Теперь это поддерживается не только слоями keras LSTM. Вам нужно будет создать свою собственную стратегию, чтобы умножить шаги. Есть два хороших подхода:
- Создайте постоянный многошаговый ввод, повторяя тензор
- Используйте a,
stateful=True
чтобы периодически получать выходные данные одного шага и использовать их как входные данные следующего шага (необходимо output_features == input_features
)
Один ко многим с повторным вектором
Чтобы соответствовать стандартному поведению keras, нам нужны входные данные поэтапно, поэтому мы просто повторяем входные данные для нужной длины:
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
(кроме того, чтобы избежать загрузки данных, которые не могут поместиться сразу в память вашего компьютера)
Stateful позволяет вводить «части» последовательностей поэтапно. Разница в том, что:
- Во
stateful=False
втором пакете содержатся совершенно новые последовательности, независимые от первого пакета.
- В
stateful=True
, вторая партия продолжает первую партию, расширяя те же последовательности.
Это похоже на разделение последовательностей в окнах с этими двумя основными отличиями:
- эти окна не накладываются !!
stateful=True
увидит эти окна соединенными в одну длинную последовательность
В stateful=True
, каждая новая партия будет интерпретироваться как продолжение предыдущей партии (пока вы не позвоните model.reset_states()
).
- Последовательность 1 в партии 2 продолжит последовательность 1 в партии 1.
- Последовательность 2 в партии 2 продолжит последовательность 2 в партии 1.
- Последовательность n в партии 2 продолжит последовательность n в партии 1.
Пример входных данных, партия 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
(если, конечно, мы не используем только одну последовательность).
Вы можете иметь любое количество партий, на неопределенный срок. (Для переменной длины в каждом пакете используйте input_shape=(None,features)
.
Один ко многим с сохранением состояния = True
В нашем случае мы будем использовать только 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()
Многие ко многим с Stateful = True
Теперь, здесь, мы получаем очень хорошее приложение: учитывая входную последовательность, попытайтесь предсказать ее будущие неизвестные шаги.
Мы используем тот же метод, что и в примере «один ко многим», с той разницей, что:
- мы будем использовать саму последовательность, чтобы быть целевыми данными, на шаг впереди
- мы знаем часть последовательности (поэтому мы отбрасываем эту часть результатов).
Слой (такой же, как выше):
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
Теперь перейдем к циклу, как в случае один ко многим. Но не сбрасывайте состояния здесь! , Мы хотим, чтобы модель знала, в каком шаге последовательности она находится (и она знает, что это на первом новом шаге из-за прогноза, который мы только что сделали)
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()
Этот подход был использован в этих ответах и файле:
Достижение сложных конфигураций
Во всех приведенных выше примерах я показал поведение «одного слоя».
Конечно, вы можете размещать много слоев друг над другом, не обязательно все по одной схеме, и создавать свои собственные модели.
Один интересный пример, который появился, - это «автоматический кодер», который имеет кодер «многие к одному», за которым следует декодер «один ко многим»:
Кодер:
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
вышеупомянутых случаях, вы можете прочитать больше в этом ответе: Сомнения относительно `Понимания LSTM Keras`