Прогнозирование множественного временного шага вперед временного ряда с использованием LSTM

Я хочу предсказать определенные значения, которые предоставляются еженедельно. Мне нужно предсказать весь временной ряд года, образованный неделями года (52 значения - рисунок 1)

Рисунок 1: Годовой временной ряд за неделей

Моей первой идеей была разработка модели LSTM "многие ко многим" (рисунок 2) с использованием Keras и TensorFlow. Я тренирую модель с 52 входами (временные ряды предыдущего года) и прогнозирую 52 выхода (временные ряды следующего года). Форма train_X: (X_examples, 52, 1), иными словами, X примеров для обучения, 52 временных шага из 1 признака. Я понимаю, что кераты будут считать, что 52 входа - это временные ряды одного и того же домена. Форма train_Y одинакова (y_examples, 52, 1). Я добавил слой TimeDistributed. Я думаю, что алгоритм предсказывает значения как временные ряды, а не как отдельные значения (я прав?)

Модель кода в керасе:

y = y.reshape(y.shape[0], 52, 1)
X = X.reshape(X.shape[0], 52, 1)
# design network
model = Sequential()
model.add(LSTM(n_neurons, input_shape=(X.shape[1], X.shape[2]), return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(loss='mean_squared_error', optimizer='adam')
# fit network
model.fit(X, y, epochs=n_epochs, batch_size=n_batch, verbose=2)

Рисунок 2: Архитектура LSTM

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

Второй вопрос: Другая идея состоит в том, чтобы обучить алгоритм с 1 входом и 1 выходом, но в тесте, как я буду предсказывать весь временной ряд 2015 года, не обращаясь к "1 входу"? Данные теста будут иметь другую форму, чем данные тренировки.

2 ответа

Делая то же самое из-за недостатка данных, вы можете сделать это следующим образом.

Во-первых, это хорошая идея, чтобы ваши значения были в диапазоне от -1 до +1, поэтому я бы сначала нормализовал их.

Для модели LSTM вы должны убедиться, что вы используете return_sequences=True,
В вашей модели нет ничего "неправильного", но для достижения желаемого может потребоваться больше или меньше слоев или единиц. (Там нет четкого ответа на это, хотя).

Обучение модели прогнозировать следующий шаг:

Все, что вам нужно, это передать Y как сдвинутый X:

entireData = arrayWithShape((samples,52,1))
X = entireData[:,:-1,:]
y = entireData[:,1:,:]

Тренируйте модель, используя их.

Предсказание будущего:

Теперь для прогнозирования будущего, поскольку нам нужно использовать прогнозируемые элементы в качестве входных данных для более прогнозируемых элементов, мы собираемся использовать цикл и сделать модель stateful=True,

Создайте модель, равную предыдущей, с этими изменениями:

  • Все слои LSTM должны иметь stateful=True
  • Форма ввода должна быть (None, 1) - Это позволяет переменной длины

Скопируйте веса ранее обученной модели:

newModel.set_weights(oldModel.get_weights())

Прогнозируйте только один образец за раз и никогда не забывайте звонить model.reset_states() перед началом любой последовательности.

Сначала сделайте прогноз, используя последовательность, которую вы уже знаете (это обеспечит, чтобы модель правильно подготовила свои состояния для предсказания будущего)

model.reset_states()
predictions = model.predict(entireData)

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

futureElement = predictions[:,-1:,:]

futureElements = []
futureElements.append(futureElement)

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

for i in range(howManePredictions):
    futureElement = model.predict(futureElement)
    futureElements.append(futureElement)

Эта ссылка содержит полный пример, предсказывающий будущее двух функций: https://github.com/danmoller/TestRepo/blob/master/TestBookLSTM.ipynb

Я хотел бы добавить к этому вопросу

Я добавил слой TimeDistributed. Я думал, что алгоритм будет предсказывать значения как временные ряды, а не изолированные значения (я прав?)

поскольку мне самому было довольно трудно понять функциональность слоя Keras TimeDistributed.

Я бы сказал, что ваша мотивация правильна - не изолировать расчеты для прогноза временных рядов. В частности, вы не хотите, чтобы характеристики и взаимозависимости всей серии были объединены при прогнозировании его будущей формы.

Однако это в точности противоположно тому, для чего предназначен слой TimeDistributed. Это для изоляции расчетов на каждом временном шаге. Почему это полезно, спросите вы? Для совершенно разных задач, например, маркировка последовательности, где у вас есть последовательный ввод (i1, i2, i3, ..., i_n) и стремиться к выводу меток (label1, label2, label1, ..., label2) для каждого временного шага отдельно.

Imho лучшее объяснение можно найти в этом посте и в документации Keras.

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

У меня есть данные за 10 лет. Если мой набор данных для обучения: значения от 4 недель для прогнозирования 5-го и я продолжаю сдвигаться, у меня может быть почти 52 X 9 примеров для обучения модели и 52 для прогнозирования (в прошлом году)

Это фактически означает, что у вас есть только 9 обучающих примеров с 52 функциями каждый (если вы не хотите тренироваться на сильно перекрывающихся входных данных). В любом случае, я не думаю, что этого достаточно, чтобы заслужить LSTM,

Я бы предложил попробовать гораздо более простую модель. Ваши входные и выходные данные имеют фиксированный размер, поэтому вы можете попробовать sklearn.linear_model.LinearRegression, который обрабатывает несколько входных функций (в вашем случае 52) для каждого примера обучения и несколько целей (также 52).

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

Архитектура сети в этой реализации:

model = Sequential()

model.add(LSTM(
    input_shape=(layers[1], layers[0]),
    output_dim=layers[1],
    return_sequences=True))
model.add(Dropout(0.2))

model.add(LSTM(
    layers[2],
    return_sequences=False))
model.add(Dropout(0.2))

model.add(Dense(
    output_dim=layers[3]))
model.add(Activation("linear"))

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

Другие вопросы по тегам