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

  1. Преобразование ряда данных в [выборки, временные шаги, характеристики] и
  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 length.

Что такое временные шаги и особенности?

Как можно видеть, TrainX представляет собой трехмерный массив с двумя последними измерениями Time_steps и Feature (3 и 1 в этом конкретном коде). Что касается изображения ниже, означает ли это, что мы рассматриваем случай многие к одному, где количество розовых квадратов равно 3? Или это буквально означает, что длина цепочки равна 3 (т.е. учитываются только 3 зеленых прямоугольника).enter image description here

Актуален ли аргумент о характеристиках, когда мы рассматриваем многомерные ряды? например моделирование двух финансовых акций одновременно?

LSTM с отслеживанием состояния

Означают ли LSTM с отслеживанием состояния, что мы сохраняем значения памяти ячеек между запусками пакетов? Если это так, 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

Обновление2:

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

sachinruk

Ответы (4)

Прежде всего, вы выбираете отличные обучающие программы (1,2) для начала.

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

многие ко многим vs. многие к одному: В keras есть параметр return_sequences при инициализации LSTM или GRUили SimpleRNN. Когда return_sequences равно False (по умолчанию), тогда это многие к одному, как показано на рисунке. Его возвращаемая форма - (размер_пакета, скрытая_часть_длина), что представляет последнее состояние. Когда return_sequences равно True, тогда это многие ко многим. Его возвращаемая форма: (размер_пакета, время_шаг, длина_покрытой_единицы)

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

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

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

Когда у вас есть return_sequences в вашем последнем слое RNN, вы не можете использовать простой плотный слой вместо 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))

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

Общее поведение Keras

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

ManyToMany

На этом изображении я увеличил количество шагов до 5, чтобы не путать с другими размерами.

Для этого примера:

  • Имеется N маслобаков
  • Мы потратили 5 часов на ежечасные измерения (временные шаги)
  • Мы измерили две характеристики:
    • Давление 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 обрабатывают все последовательности. Разделение окон - не лучшая идея. Уровень имеет внутренние состояния о том, как последовательность развивается по мере продвижения вперед. Windows исключает возможность изучения длинных последовательностей, ограничивая все последовательности размером окна.

В окнах каждое окно является частью длинной исходной последовательности, но Керас будет рассматривать каждое из них как независимую последовательность:

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

Что такое stateful = 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 выше, вы можете прочитать больше в этом ответе: Сомнения относительно `Понимания Keras LSTM`

См. Этот блог для получения более подробной информации Анимированные RNN, LSTM и GRU.

Рисунок ниже дает вам лучшее представление о LSTM. Это ячейка LSTM. This figure

Как видите, X имеет 3 функций (зеленые кружки), поэтому ввод этой ячейки является вектором размерности 3, а скрытое состояние имеет 2 единиц (красные кружки), поэтому выход этой ячейки (а также состояние ячейки) представляет собой вектор размерности 2.

Пример одного слоя LSTM с 3 временными шагами (3 ячейки LSTM) показан на рисунке ниже:

This image

** Модель может иметь несколько слоев LSTM.

Теперь я снова использую пример Daniel Möllerдля лучшего понимания: У нас есть 10 нефтехранилищ. Для каждого из них мы измеряем 2 характеристики: температуру, давление каждый час 5 раз. теперь параметры:

  • batch_size = количество выборок, используемых в одном прямом / обратном проходе (по умолчанию = 32) -> например, если у вас есть 1000 образцов и вы установили batch_size равным 100, тогда модель займет 10 итераций для однократной передачи всех выборок через сеть (1 эпоха). Чем выше размер пакета, тем больше места в памяти вам понадобится. Поскольку количество выборок в этом примере невелико, мы считаем batch_size равным всем выборкам = 10
  • временные шаги = 5
  • особенности = 2
  • единиц = Это положительное целое число и определяет размер скрытого состояния и состояния ячейки или, другими словами, количество параметров, переданных в следующую ячейку LSTM. Его можно выбрать произвольно или эмпирически в зависимости от характеристик и временных шагов. Использование большего количества единиц приведет к большей точности, а также к увеличению времени вычислений. Но это может привести к переделке.
  • input_shape = (batch_size, временные шаги, характеристики) = (10,5,2)
  • форма_вывода:
    • (размер_пакета, временные шаги, единицы), если return_sequences = True
    • (размер партии, единицы), если return_sequences = False

2022 WebDevInsider