Keras fit_generator() - Как работает пакет для временных рядов?

Контекст:

В настоящее время я работаю над прогнозированием временных рядов с использованием Keras с бэкэндом Tensorflow и, следовательно, изучал приведенный здесь учебник.

После этого урока я пришел к тому, что fit_generator() генератор для fit_generator(). Выходные данные, которые генерирует этот генератор, следующие (левая выборка, правая цель):

[[[10. 15.]
  [20. 25.]]] => [[30. 35.]]     -> Batch no. 1: 2 Samples | 1 Target
  ---------------------------------------------
[[[20. 25.]
  [30. 35.]]] => [[40. 45.]]     -> Batch no. 2: 2 Samples | 1 Target
  ---------------------------------------------
[[[30. 35.]
  [40. 45.]]] => [[50. 55.]]     -> Batch no. 3: 2 Samples | 1 Target
  ---------------------------------------------
[[[40. 45.]
  [50. 55.]]] => [[60. 65.]]     -> Batch no. 4: 2 Samples | 1 Target
  ---------------------------------------------
[[[50. 55.]
  [60. 65.]]] => [[70. 75.]]     -> Batch no. 5: 2 Samples | 1 Target
  ---------------------------------------------
[[[60. 65.]
  [70. 75.]]] => [[80. 85.]]     -> Batch no. 6: 2 Samples | 1 Target
  ---------------------------------------------
[[[70. 75.]
  [80. 85.]]] => [[90. 95.]]     -> Batch no. 7: 2 Samples | 1 Target
  ---------------------------------------------
[[[80. 85.]
  [90. 95.]]] => [[100. 105.]]   -> Batch no. 8: 2 Samples | 1 Target

В учебном TimeSeriesGenerator использовался TimeSeriesGenerator, но для моего вопроса второстепенно, если используется пользовательский генератор или этот класс. Что касается данных, у нас есть 8 steps_per_epoch и образец формы (8, 1, 2, 2). Генератор подается в рекуррентную нейронную сеть, реализованную LSTM.

Мои вопросы

fit_generator() допускает только одну цель на пакет, как TimeSeriesGenerator. Когда я впервые прочитал о параметрах пакетов для fit(), я подумал, что у меня может быть несколько выборок и соответствующее количество целей (которые обрабатываются партиями, то есть строка за строкой). Но это не разрешено fit_generator() и, следовательно, очевидно ложно. Это будет выглядеть, например, как:

[[[10. 15. 20. 25.]]] => [[30. 35.]]     
[[[20. 25. 30. 35.]]] => [[40. 45.]]    
    |-> Batch no. 1: 2 Samples | 2 Targets
  ---------------------------------------------
[[[30. 35. 40. 45.]]] => [[50. 55.]]    
[[[40. 45. 50. 55.]]] => [[60. 65.]]    
    |-> Batch no. 2: 2 Samples | 2 Targets
  ---------------------------------------------
...

Во-вторых, я подумал, что, например, [10, 15] и [20, 25] использовались в качестве входных данных для RNN последовательно для цели [30, 35], что означает, что это аналог ввода [10, 15, 20 25]. Поскольку выходной сигнал от RNN отличается при использовании второго подхода (я проверял его), это также должно быть неверным выводом.

Следовательно, мои вопросы:

  1. Почему разрешена только одна цель на партию (я знаю, что есть некоторые обходные пути, но должна быть причина)?
  2. Как я могу понять расчет одной партии? Имеется в виду, как обрабатывается некоторый ввод, такой как [[[40, 45], [50, 55]]] => [[60, 65]] и почему он не является аналогом [[[40, 45, 50, 55]]] => [[60, 65]]



Редактировать в соответствии с сегодняшним ответом
Так как есть определенное недопонимание относительно моего определения образцов и целей - я следую тому, что, как я понимаю, Керас пытается сказать мне, говоря:

ValueError: Входные массивы должны иметь то же количество выборок, что и целевые массивы. Найдено 1 входных образцов и 2 целевых образца.

Эта ошибка возникает, когда я создаю, например, пакет, который выглядит следующим образом:

#This is just a single batch - Multiple batches would be fed to fit_generator()
(array([[[0, 1, 2, 3, 4],
        [5, 6, 7, 8, 9]]]), 
                           array([[ 5,  6,  7,  8,  9],
                           [10, 11, 12, 13, 14]]))

Предполагается, что это будет один пакет, содержащий две временные последовательности длиной 5 (5 последовательных точек данных/временных шагов), целью которых также являются две соответствующие последовательности. [ 5, 6, 7, 8, 9] является целью [0, 1, 2, 3, 4] а [10, 11, 12, 13, 14] является соответствующей целью [5, 6, 7, 8, 9].
Образец-формы в этом случае будет shape(number_of_batches, number_of_elements_per_batch, sequence_size) и целевой формой shape(number_of_elements_per_batch, sequence_size).
Керас видит 2 целевых образца (в ValueError), потому что у меня есть два, которые предоставляют 3D-образцы в качестве входных данных и 2D-цели в качестве выходных (возможно, я просто не понимаю, как предоставить 3D-цели...).

Так или иначе, согласно ответу/комментариям @todays, это интерпретируется Keras как два временных шага и пять функций. Что касается моего первого вопроса (где я все еще вижу последовательность в качестве цели для моей последовательности, как в этом примере редактирования), я ищу информацию о том, как/если я могу достичь этого и как будет выглядеть такая партия (как я пытался визуализировать в вопрос).

Ответ 1

Короткие ответы:

Почему разрешена только одна цель на партию (я знаю, что есть некоторые обходные пути, но должна быть причина)?

Это совсем не так. Нет ограничений на количество целевых образцов в партии. Единственное требование - чтобы вы имели одинаковое количество входных и выходных выборок в каждой партии. Прочитайте длинный ответ для дальнейшего разъяснения.

Как я могу понять расчет одной партии? Имеется в виду, как обрабатывается некоторый ввод, такой как [[[40, 45], [50, 55]]] => [[60, 65]] и почему он не является аналогом [[[40, 45, 50, 55]]] => [[60, 65]]?

Первый - это многовариантный временной ряд (т.е. Каждый временной шаг имеет более одного признака), а второй - однофакторный временной ряд (т.е. Каждый временной шаг имеет одну особенность). Так что они не эквивалентны. Прочитайте длинный ответ для дальнейшего разъяснения.

Длинный ответ:

Я дам ответ, который я упомянул в разделе комментариев, и попытаюсь развить его, используя примеры:

Я думаю, что вы смешиваете образцы, временные шаги, функции и цели. Позвольте мне описать, как я понимаю это: в первом примере, который вы предоставили, кажется, что каждая входная выборка состоит из 2 временных шагов, например, [10, 15] и [20, 25], где каждый временной шаг состоит из двух функций, например, 10 и 15 или 20 и 25. Кроме того, соответствующая цель состоит из одного временного шага, например [30, 35], который также имеет две особенности. Другими словами, каждая входная выборка в пакете должна иметь соответствующую цель. Однако форма каждой входной выборки и соответствующей ей цели не обязательно должна быть одинаковой.

Например, рассмотрим модель, в которой вход и выход являются временными рядами. Если мы обозначим форму каждого входного сэмпла как (input_num_timesteps, input_num_features) а форму каждого целевого (то есть выходного) массива как (output_num_timesteps, output_num_features), мы получим следующие случаи:

1) Количество входных и выходных временных шагов одинаково (то есть, input_num_timesteps == output_num_timesteps). В качестве примера, следующая модель может достичь этого:

from keras import layers
from keras import models

inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(..., return_sequences=True)(x)

# a final RNN layer that has 'output_num_features' unit
out = layers.LSTM(output_num_features, return_sequneces=True)(x)

model = models.Model(inp, out)

2) Количество входных и выходных временных шагов различно (т.е. input_num_timesteps ~= output_num_timesteps). Обычно это достигается путем предварительного кодирования входных временных рядов в вектор с использованием стека из одного или нескольких слоев LSTM, а затем повторения этого вектора output_num_timesteps times для получения output_num_timesteps желаемой длины. Для повторной операции мы можем легко использовать слой RepeatVector в RepeatVector. Опять же, в качестве примера, следующая модель может достичь этого:

from keras import layers
from keras import models

inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(...)(x)  # The last layer ONLY returns the last output of RNN (i.e. return_sequences=False)

# repeat 'x' as needed (i.e. as the number of timesteps in output timseries)
x = layers.RepeatVector(output_num_timesteps)(x)

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(x)
# ...
out = layers.LSTM(output_num_features, return_sequneces=True)(x)

model = models.Model(inp, out)

В особом случае, если количество выходных временных шагов равно 1 (например, сеть пытается предсказать следующий временной шаг с учетом последних t временных шагов), нам может не потребоваться использовать повтор, и вместо этого мы можем просто использовать Dense слой (в этом в случае, если форма вывода модели будет (None, output_num_features), а не (None, 1, output_num_features)):

inp = layers.Input(shape=(input_num_timesteps, input_num_features))

# a stack of RNN layers on top of each other (this is optional)
x = layers.LSTM(..., return_sequences=True)(inp)
# ...
x = layers.LSTM(...)(x)  # The last layer ONLY returns the last output of RNN (i.e. return_sequences=False)

out = layers.Dense(output_num_features, activation=...)(x)

model = models.Model(inp, out)

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


Обновление: проблема в том, что вы не уделяете достаточного внимания при чтении моих комментариев и ответов, а также об ошибке, поднятой Keras. Ошибка ясно гласит, что:

... Нашли 1 входной выборки и 2 целевых выборки.

Итак, после прочтения этого внимательно, на вашем месте, я бы сказал себе: "Хорошо, Керас считает, что у входной партии есть 1 входная выборка, но я думаю, что я предоставляю две выборки !! Так как я очень хороший человек (!), Я думаю очень вероятно, что я буду неправ, чем Керас, так что давай узнаем, что я делаю не так! ". Простая и быстрая проверка - просто проверить форму входного массива:

>>> np.array([[[0, 1, 2, 3, 4],
               [5, 6, 7, 8, 9]]]).shape
(1,2,5)

"О, это говорит (1,2,5) ! Так что это означает, что один сэмпл имеет два временных шага и каждый временной шаг имеет пять функций !!! Поэтому я ошибался, полагая, что этот массив состоит из двух выборок длины 5, где каждый временной шаг имеет длину 1 !! Так что мне теперь делать??? " Ну, вы можете это исправить, шаг за шагом:

# step 1: I want a numpy array
s1 = np.array([])

# step 2: I want it to have two samples
s2 = np.array([
               [],
               []
              ])

# step 3: I want each sample to have 5 timesteps of length 1 in them
s3 = np.array([
               [
                [0], [1], [2], [3], [4]
               ],
               [
                [5], [6], [7], [8], [9]
               ]
              ])

>>> s3.shape
(2, 5, 1)

Вуаля! Мы сделали это! Это был входной массив; Теперь проверьте целевой массив, он должен иметь две целевые выборки длиной 5 с каждой характеристикой, т.е. иметь форму (2, 5, 1):

>>> np.array([[ 5,  6,  7,  8,  9],
              [10, 11, 12, 13, 14]]).shape
(2,5)

Почти! Последнее измерение (т.е. 1) отсутствует (ПРИМЕЧАНИЕ: в зависимости от архитектуры вашей модели вам может понадобиться или не потребоваться эта последняя ось). Таким образом, мы можем использовать пошаговый подход, описанный выше, чтобы найти свою ошибку, или, в качестве альтернативы, мы можем быть немного умнее и просто добавить ось в конец:

>>> t = np.array([[ 5,  6,  7,  8,  9],
                  [10, 11, 12, 13, 14]])
>>> t = np.expand_dims(t, axis=-1)
>>> t.shape
(2, 5, 1)

Извините, я не могу объяснить это лучше, чем это! Но в любом случае, когда вы видите, что что-то (то есть форма входных/целевых массивов) повторяется снова и снова в моих комментариях и моем ответе, предположите, что это должно быть чем-то важным и должно быть проверено.

Ответ 2

Размер партии определяется в генераторе. Генератор Python будет генерировать данные бесконечно, поэтому модель не знает, когда заканчивается эпоха. Поэтому нам нужно определить steps_per_epoch в fit_generator чтобы сообщить модели, сколько раз использовать генератор за эпоху.