Как проверить прогноз временного ряда без утечки будущего
Хронологические выборки и обучение преобразований только на прошлом.
В этом материале
Короткий ответ
Обучайте модель на ранних наблюдениях, а проверяйте на более поздних. Случайное разбиение может дать ей информацию, недоступную при реальном прогнозе.
Начните с момента, когда нужен прогноз
Допустим, магазин каждое воскресенье вечером прогнозирует спрос на следующие семь дней. Оценка должна воспроизводить именно этот момент: продажи следующей недели и сведения, появившиеся позже воскресенья, недоступны. Случайное разбиение смешивает прошлое и будущее и может показать пользу, которой в реальной работе не будет.
Запишите точку построения прогноза, горизонт и время доступности каждого признака. Календарь известен заранее, а фактическая температура или пересмотренная сумма продаж — не всегда. Если задача использует погоду, берите доступный тогда прогноз погоды, а не наблюдение, полученное позднее.
Зафиксировать горизонт
Размер тестового периода должен соответствовать нужному горизонту прогноза. Масштабирование и другие обучаемые преобразования настраивайте только на тренировочной части. Скользящие признаки используют лишь доступное на момент прогноза.
from sklearn.model_selection import TimeSeriesSplit
observations = list(range(12))
splitter = TimeSeriesSplit(n_splits=3, test_size=2, gap=1)
for train, test in splitter.split(observations):
print(train.tolist(), test.tolist())Как выглядит разбиение на конкретных позициях
В небольшом примере выше 12 упорядоченных наблюдений, три проверочных окна по два наблюдения и зазор в одну строку. Первое обучение использует позиции 0–4, проверка — 6–7. Затем обучение — 0–6, проверка — 8–9. Последняя пара — 0–8 и 10–11.
Зазор задаёт выбранный интервал исключения, а не универсально устраняет утечки. Его длина зависит от задержек публикации и перекрытия целевых окон. TimeSeriesSplit считает строки: две строки не обязательно означают два дня. До интерпретации окон проверьте порядок дат, дубликаты, пропуски и равномерность шага.
Проверить временные предпосылки
Проверьте сортировку, пропущенные даты и интервалы. Добавьте разрыв, если его требуют задержки информации или перекрывающиеся метки. Оставьте финальный период для независимой оценки.
Ищите утечки не только в разбиении
Обучайте масштабирование, заполнение пропусков и отбор признаков только на обучающей части каждого разбиения. К проверочной части применяйте уже полученные преобразования. Pipeline помогает держать обучаемую обработку вместе, но не исправляет признак, в который будущее попало ещё до разбиения.
Для дневных продаж скользящее среднее при прогнозе на день t должно использовать предыдущие наблюдения, например shift(1).rolling(7).mean(). Центрированное окно может захватить будущие дни. При прогнозе всей недели сразу даже реальные продажи её первых дней ещё неизвестны: нельзя подставлять их в прогноз следующих дней как доступные данные.
Сравните с простым прогнозом до настройки модели
Возьмите базовый вариант под задачу: последнее известное значение или соответствующий период предыдущего сезонного цикла. Считайте ошибки на тех же точках построения и горизонте, что и у модели. В условном расчёте ниже фактические 100, 110 и 90 при прогнозах 100 дают MAE ≈ 6,67 в единицах целевого показателя.
Посмотрите ошибки отдельно по горизонту и периодам: одно среднее может скрыть провалы в дни высокого спроса. Процентные метрики неудобны при значениях около нуля. Оставьте финальный период, который не участвует в выборе модели, и повторяйте оценку при изменении условий формирования данных.
actual = [100, 110, 90]
predicted = [100, 100, 100]
mae = sum(abs(a - p) for a, p in zip(actual, predicted)) / len(actual)
print(round(mae, 2))Что проверить
- Разбивайте по времени.
- Обучайте преобразования внутри каждой выборки.
- Проверяйте доступность признаков на момент прогноза.
Границы применения
Наблюдения должны идти хронологически. TimeSeriesSplit оперирует позициями строк: для сопоставимых временных окон проверьте равномерность интервалов.