Я хочу использовать LSTM для прогнозирования следующего элемента в последовательности, но у меня возникают проблемы, когда я не могу понять, как сформулировать проблему и, в частности, как структурировать данные и представить их mdel. Я должен сказать, что я довольно новичок в LSTM - я некоторое время проходил курс Coursera по моделям последовательностей go, но кроме упражнений там никогда не удавалось опробовать вещи. В качестве переподготовки я читал некоторые (как правило, очень полезные) сообщения блога Machine Learning Mastery, такие как этот , этот и этот - но они только добавили моего замешательства. Кажется, этот вопрос относится к моему, но то же самое относится: я все время путаюсь, когда пытаюсь перевести его на свою проблему.
Позвольте мне начать с описания моей проблемы. У меня 150 последовательностей предметов разной длины (самая короткая последовательность содержит около 100 предметов; самая длинная около 700). Каждый элемент может быть представлен до четырех функций. Я хочу поэкспериментировать, какие функции будут работать лучше всего; для примера, скажем, у меня есть две особенности, представляющие каждый элемент.
Что я хочу сделать, это предсказать следующий элемент, учитывая n длинную последовательность предыдущих элементов: т. е. прогнозировать элемент на временном шаге t , учитывая элементы на временных шагах [ t - n , ..., t - 2, t -1].
Как уже упоминалось, есть две (связанные) вещи, с которыми я борюсь:
- Как структурировать данные.
- Как передать данные в LSTM.
Давайте начнем с пункта 1. Как упоминалось выше, мои исходные данные состоят из 150 последовательностей различной длины. Каждый элемент i в каждой последовательности представлен двумя функциями: [f0_i, f1_i]
. Допустим, первая последовательность содержит 100 элементов; это дает следующую картину:
[[f0_0, f1_0], [f0_1, f1_1], [f0_2, f1_2], [f0_3, f1_3], [f0_4, f1_4], ... [f0_99, f1_99]]
Пока все хорошо. Теперь предположим, что я хочу использовать историю трех временных шагов (предыдущие элементы), чтобы предсказать следующий элемент - означает ли это, что я должен реструктурировать свои тренировочные данные, чтобы приспособиться к этому? То есть, нужно ли разбивать каждую из 150 последовательностей на несколько подпоследовательностей размером с временные шаги, которые я затем собираю в одном суперсписке, например так (пример для первой последовательности):
X = [
# sequence 0, cut up
[[f0_0, f1_0], [f0_1, f1_1], [f0_2, f1_2]], # items 0-2 = training sample 0
[[f0_1, f1_1], [f0_2, f1_2], [f0_3, f1_3]], # items 1-3 = training sample 1
[[f0_2, f1_2], [f0_3, f1_3], [f0_4, f1_4]], # items 2-4 = training sample 2
...
[[f0_96, f1_96], [f0_97, f1_97], [f0_98, f1_98]] # items 96-98 = training sample 99
# sequence 1, cut up
...
# sequence 149, cut up
]
y = [
# labels for sequence 0, cut up
[f0_3, f1_3], # item 3 = target for training sample 0
[f0_4, f1_4], # item 4 = target for training sample 1
[f0_5, f1_5], # item 5 = target for training sample 2
...
[f0_99, f1_99] # item 99 = target for training sample 96
# labels for sequence 1, cut up
...
# labels for sequence 49, cut up
]
.. где каждый элемент в X
является образцом, а каждый элемент в y
является его целью? (Позднее весь список разбивается на обучающие и тестовые наборы.)
Или я могу просто добавить каждую полную последовательность сразу в этот суперсписок и передать ее в качестве входных данных (пропуская последний элемент в * 1048) * поскольку у него нет y
), вот так (пример для первой последовательности):
X = [
# sequence 0 = training sample 0
[[f0_0, f1_0], [f0_1, f1_1], [f0_2, f1_2], ..., [f0_98, f1_98]]
# sequence 1 = training sample 1
# sequence 2 = training sample 2
...
# sequence 149 = training sample 149
]
y = [
# sequence 0 = targets 0
[[f0_1, f1_1], [f0_2, f1_2], [f0_3, f1_3], ..., [f0_99, f1_99]]
# sequence 1 = targets 1
# sequence 2 = targets 2
...
# sequence 149 = targets 149
]
Во втором случае я, конечно, получаю гораздо меньше выборочных последовательностей (150), но они длиннее. Однако во втором случае код нарушается (см. Ниже), так как model.fit()
ожидает двумерный массив для данных y
. Но как здесь y
быть двумерным массивом?
Теперь перейдем к пункту 2. Вот мой код, использующий X
и y
, как описано выше (этот случай для модели с состоянием, поэтому for
l oop вокруг вызова fit()
):
batch_size = 1
time_steps = 3
num_features = len(train_X[0][0]) # = 2
epochs = 50
model = Sequential()
model.add(LSTM(10, batch_input_shape=(batch_size, time_steps, num_features), stateful=True))
model.add(Dropout(dropout, seed=1))
model.add(Dense(num_features))
model.compile(loss='mean_squared_error', optimizer='Adam', metrics=['MSE'])
for i in range(epochs):
history = model.fit(X, y, epochs=1, validation_split=0.2, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
Кажется очевидным, что я просто установил time_steps
и num_features
на 3 и 2 соответственно. Но что я даю как X
и y
?
- Должен ли я использовать предварительно отформатированные данные, в которых «закодированы» временные шаги и где
X
имеет форму (total_num_subsamples, time_steps
, num_features
) и y
shape (total_num_subsamples, num_features
)? - Или я должен использовать более простой формат, где
X
имеет форму (150, sequence_length, 2) и y
некоторую 2D-форму? Будет ли настройка параметра time_steps
в слое LSTM
позаботиться о просмотре указанного количества временных шагов здесь?
Или ... я все делаю неправильно?
Наконец, предположим, что в основном меня интересует прогнозирование функции 0 на временном шаге t . Эта функция имеет ограниченное количество значений, т.е. Это один из нескольких классов. В этом случае, имеет ли смысл представлять каждый элемент y
как горячий вектор, кодирующий класс? Или же LSTM не сможет это узнать (поскольку для элемента на временном шаге t используется другой формат, чем для элементов на предыдущих временных шагах), и мне придется представлять все элементы , также в X
, в виде векторов с одним горячим вектором?