Всем, привет. Если вы задавались вопросом, как эффективно построить прогноз для последовательности значений (например, для графика), то вам может оказаться полезным мой опыт. Изначально это должен был быть пост, но материал вышел за его рамки, поэтому получилась небольшая статья.
Построение прогноза последовательности — это тема отдельной книги, поэтому в своей статье я только слегка коснусь двух подходов:
-
построение прогноза по одной точке, используя цикл;
-
построение прогноза на весь период одним махом.
Есть и другие, но эти два рекомендуются наиболее часто, и я постараюсь пояснить на примерах, почему один из них скорее всего будет пустой тратой времени.
Построение прогноза в цикле по одному значению за раз
Подход предсказания по одной точке как правило разбирается первым и состоит в том, что мы обучаем модель предсказывать следующее число в последовательности, на основе некоторого окна прошлых результатов. Для предсказания больше чем на одно значение, мы используем цикл, в котором сначала предсказываем следующее значение на основе окна с реальными данными, а для предсказания 2-го и далее значения передвигаем окно, включая уже предсказанное ранее.
Модель для обучения и предсказания по одному значению может выглядеть, например, так:
model = Sequential([ Input(shape=(64, 1)), # Слой долгой краткосрочной памяти, # чтобы запоминать паттерны поведения последовательности LSTM(64, return_sequences=False), # Защита от переобучения сети Dropout(0.2), # Выходное значение одно Dense(1)])
Прогноз, соответственно будет строиться в цикле на то количество итераций, которое мы хотим предсказать:
# Хотим прогноз на 7 шаговforecast_step = 7# Делаем список из наших начальных данных datalast_window = data[-64:].tolist() # Список для предсказанийpredictions = []for _ in range(forecast_step): # Берем последние 64 елемента, делаем из них входной вектор input_data = np.array(last_window[-64:]).reshape(1, 64, 1) # Получаем предсказание next_val = float(model.predict(input_data, verbose=0)[0][0]) predictions.append(next_val) # Добавляем предсказание в списк данных для следующего прогноза last_window.append(next_val)
Получим примерно такой результат:
Прогноз на 7 дней выглядит неестественно, но это и понятно, для прогноза 2-го и далее значения мы используем практически те же входные данные, добавляя всего по одному новому предсказанному значению. Странно было бы ожидать от модели сильно отличающегося прогноза.
Построение прогноза сразу на весь желаемый период
Если же мы хотим сделать модель, которая будет предсказывать не одно, а сразу нужное нам значение шагов, то она будет выглядеть примерно так:
model = Sequential([ Input(shape=(64, 1)), # Первый LSTM передает последовательность второму LSTM(64, return_sequences=True), Dropout(0.2), # Второй LSTM отдает только финальное состояние LSTM(64, return_sequences=False), Dropout(0.2), # Выходной слой: 7 нейронов соответствуют размеру нашего желаемого прогноза Dense(7)])
А прогноз такой модели прогноз уже будет таким:
Бонусом будет то, что код получения прогноза будет значительно легче, а модель будет работать всего одну итерацию:
# Делаем список из наших начальных данных datalast_window = data[-64:].tolist() # Берем последние 64 елемента, делаем из них входной векторinput_data = np.array(last_window[-64:]).reshape(1, 64, 1)# Получаем предсказаниеpredictions = model.predict(input_data)
Сейчас я бы не хотел углубляться в оценку точности получившегося прогноза (это тоже тема для отдельной статья), но такой прогноз точно выглядит более естественным, чем прогноз по одному значению.
Итоги
Если вам необходимо построить прогноз более чем на одно значение вперед, то подход предсказания по одной точке вам точно не подойдет, на него не стоит тратить время. Постарайтесь обучить и оптимизировать вашу модель для предсказания сразу всего нужного вам интервала прогноза. Это точно будет работать быстрее, и с большой вероятностью точнее, чем прогнозирование по одной точке.
Если статья оказалась вам полезной, я с большим удовольствием отвечу на ваши вопросы и поделюсь опытом.
ссылка на оригинал статьи https://habr.com/ru/articles/1035496/