Понимание KES LSTM

Я пытаюсь примирить свое понимание LSTM и указал здесь на этом посту Кристофера Олаха, реализованного в Keras. Я следую блогу, написанному Джейсоном Браунли для учебника Keras. Я в основном смущен,

  1. Перестройка рядов данных в [samples, time steps, features] и,
  2. LSTM с сохранением состояния

Давайте сосредоточимся на вышеуказанных двух вопросах со ссылкой на следующий код:

# 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 - последние два измерения соответственно (3 и 1 в этом конкретном коде). Что касается изображения ниже, значит ли это, что мы рассматриваем many to one случай, когда число розовых коробок составляет 3? Или буквально означает, что длина цепи равна 3 (т.е. Рассматривается только 3 зеленых квадрата). enter image description here

Является ли аргумент признаков актуальным, когда мы рассматриваем многомерные ряды? например, моделирование двух финансовых запасов одновременно?

Стойкие LSTM

Поддерживает ли LSTM с точки зрения состояния, что мы сохраняем значения памяти ячейки между сериями партий? Если это так, batch_size - это один, и память сбрасывается между batch_size обучения, так что смысл говорить, что он был сдержанным. Я предполагаю, что это связано с тем, что данные обучения не перетасовываются, но я не уверен, как это сделать.

Какие-нибудь мысли? Ссылка на изображение: http://karpathy.github.io/2015/05/21/rnn-effectiveness/

Изменить 1:

Немного смущенно о комментарии @van о том, что красные и зеленые коробки равны. Итак, просто чтобы подтвердить, соответствуют ли следующие вызовы API развернутым диаграммам? Особенно отметив вторую диаграмму (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

Update2:

Я кратко изложил большинство из моего понимания LSTM здесь: https://www.youtube.com/watch?v=ywinX5wgdEU

Ответ 1

Прежде всего, вы выбираете большие учебники (1, 2), чтобы начать.

Что означает временной шаг: Time-steps==3 в X.shape(описание формы данных) означает наличие трех розовых коробок. Поскольку в Keras каждый шаг требует ввода, поэтому число зеленых ящиков должно обычно равняться количеству красных ящиков. Если вы не взломаете структуру.

от многих до нескольких и от нескольких до одного. В keras существует параметр return_sequences при инициализации LSTM или GRU или SimpleRNN. Если return_sequences False (по умолчанию), то оно много к одному, как показано на рисунке. Его форма возврата (batch_size, hidden_unit_length), которая представляет собой последнее состояние. Если return_sequences - True, то оно для многих. Его форма возврата (batch_size, time_step, hidden_unit_length)

Является ли аргумент признаков релевантным: аргумент функции означает "Насколько велика ваша красная рамка" или каков размер ввода каждого шага. Если вы хотите прогнозировать, например, 8 видов рыночной информации, то вы можете сгенерировать свои данные с помощью feature==8.

Stateful. Вы можете найти исходный код. При инициализации состояния, если stateful==True, тогда состояние последнего обучения будет использоваться в качестве начального состояния, иначе оно будет генерировать новое состояние. Я еще не включил stateful. Однако я не согласен с тем, что batch_size может быть только 1, когда stateful==True.

В настоящее время вы создаете свои данные с собранными данными. Изображение вашей информации о запасах поступает как поток, вместо того, чтобы ждать дня, чтобы собрать все последовательные, вы хотели бы генерировать входные данные онлайн во время обучения/прогнозирования в сети. Если у вас 400 акций, имеющих одну и ту же сеть, вы можете установить batch_size==400.

Ответ 2

В качестве дополнения к принятому ответу, этот ответ показывает поведение keras и способы достижения каждой картины.

Поведение генерала Кераса

Стандартная внутренняя обработка keras всегда много-много, как показано на следующем рисунке (в качестве примера я использовал features=2, давление и температура):

ManyToMany

На этом изображении я увеличил количество шагов до 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]],
  ....
        ]

Обратите внимание, что в этом случае у вас изначально есть только одна последовательность, но вы делите ее на несколько последовательностей для создания окон.

Понятие "что такое последовательность" абстрактно. Важными частями являются:

  • Вы можете иметь партии со многими отдельными последовательностями
  • что делает последовательности последовательностями, так это то, что они развиваются поэтапно (обычно временные шаги)

Достижение каждого случая с "отдельными слоями"

Достижение стандарта много ко многим:

StandardManyToMany

Вы можете достичь многого с помощью простого слоя LSTM, используя return_sequences=True:

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

Достижение один ко многим

Теперь это не поддерживается только слоями keras LSTM. Вам нужно будет создать свою собственную стратегию, чтобы умножить шаги. Есть два хороших подхода:

  • Создайте постоянный многошаговый ввод, повторяя тензор
  • Используйте stateful=True чтобы периодически принимать выходные данные одного шага и использовать их в качестве входных данных следующего шага (требуется output_features == input_features)

Один ко многим с повторным вектором

Чтобы соответствовать стандартному поведению 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 (кроме того, что нужно избегать загрузки данных, которые не могут сразу вместить память вашего компьютера)

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 - это то, что "позволяет" нам остановить последовательность, манипулировать тем, что мы хотим, и продолжить с того места, где мы остановились.

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

Многие ко многим с Stateful = True

Теперь, здесь, мы получаем очень хорошее приложение: учитывая входную последовательность, попытайтесь предсказать ее будущие неизвестные шаги.

Мы используем тот же метод, что и в примере "один ко многим", с той разницей, что:

  • мы будем использовать саму последовательность, чтобы быть целевыми данными, на шаг впереди
  • мы знаем часть последовательности (поэтому мы отбрасываем эту часть результатов).

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

Теперь перейдем к циклу, как в случае один ко многим. Но не сбрасывайте состояния здесь! , Мы хотим, чтобы модель знала, в каком шаге последовательности она находится (и она знает это на первом новом шаге из-за предсказания, которое мы только что сделали)

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"

Ответ 3

Когда у вас есть return_sequences в вашем последнем слое RNN, вы не можете использовать простой слой Dense вместо использования TimeDistributed.

Вот пример кода, который может помочь другим.

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