Прогноз на завтра хорош, на месяц плох: как разделить качество по горизонту

Модель спроса показывает низкую среднюю ошибку. Закупщик всё равно недоволен: заказ на месяц вперёд регулярно приходится исправлять. Команда смотрит на один общий показатель, в котором большинство строк относятся к завтрашнему дню. Качество близкого прогноза скрывает слабость на сроке, ради которого модель внедряли.

Горизонт — расстояние от момента расчёта до даты, для которой нужен ответ. Прогноз на завтра и прогноз на тридцатый день используют разный объём доступной информации. Их стоит оценивать отдельно, даже если оба ответа производит один сервис и сохраняет в одну таблицу.

Одна целевая дата может иметь несколько прогнозов

Для каждой строки нужны идентификатор товара, дата расчёта, целевая дата и версия модели. Прогноз на 1 ноября мог быть выпущен 2 октября, 25 октября и 31 октября. Все три значения корректно сравнить с одним фактом, но они относятся к разным горизонтам и разным решениям бизнеса.

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

Уточните и определение горизонта. В примере считаются календарные дни. Для закупки он может измеряться рабочими днями или номером будущей недели. Прогноз суммы на следующие семь дней отличается от прогноза на седьмой день: у них разные целевые значения и другая цена ошибки.

Добавим точку отсчёта

Плохая MAE на длинном горизонте может быть ожидаема, если задача сама стала сложнее. Поэтому рядом считайте простое правило: например, тот же день прошлой недели или согласованный сезонный baseline. Важно получать его только из информации на дату расчёта, а не из фактов, которые появились позже.

Ниже таблица содержит уже выпущенные прогнозы модели и baseline. Все факты равны 100, чтобы легче увидеть арифметику. По каждому горизонту считаем MAE обеих систем и относительное улучшение к baseline. Нулевую ошибку baseline нельзя использовать в знаменателе: для неё оставляем неопределённое значение.

import pandas as pd
rows = []
for issued in pd.to_datetime(['2026-09-01','2026-09-08']):
    for horizon, model, baseline in [(1,102.,104.),(7,110.,112.),(30,125.,120.)]:
        rows.append((issued, issued+pd.Timedelta(days=horizon), 100., model, baseline))
d = pd.DataFrame(rows, columns=['issued','target','actual','model','baseline'])
d['horizon'] = (d.target-d.issued).dt.days
d['model_ae'] = (d.actual-d.model).abs()
d['baseline_ae'] = (d.actual-d.baseline).abs()
report = d.groupby('horizon', as_index=False).agg(
    n=('actual','size'), model_mae=('model_ae','mean'),
    baseline_mae=('baseline_ae','mean'))
denominator = report.baseline_mae.where(report.baseline_mae != 0)
report['relative_gain'] = 1-report.model_mae/denominator
print(report.to_string(index=False))
assert report.model_mae.tolist() == [2.,10.,25.]
assert report.relative_gain.iloc[0] == .5
assert report.relative_gain.iloc[2] == -.25
assert (d.target > d.issued).all()

На горизонте одного дня модель уменьшает ошибку с четырёх до двух единиц, то есть на 50%. На семи днях улучшение составляет около 16,7%. На тридцати днях модель хуже baseline на 25%: её MAE равна 25 против 20. Общее среднее скрыло бы, что для месячной закупки стоит пока оставить простое правило.

Сравнивайте сопоставимые объекты

Число строк по горизонтам может различаться. Для последних дат длинный прогноз ещё не получил факт. Не заполняйте его нулём и не смешивайте неполное окно с завершённым. Выберите набор дат расчёта, для которых доступны все нужные горизонты, либо явно покажите разное число завершённых наблюдений.

Проверьте состав товаров. Если на завтра оцениваются все позиции, а на месяц только ходовые, результаты нельзя читать как чистое влияние горизонта. Полезно иметь общий сопоставимый набор и дополнительный отчёт по каждому рабочему сценарию. В строках сохраняйте причины исключения, чтобы состав выборки можно было восстановить.

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

Как изменить продукт после проверки

Не обязательно выбирать одну систему для всех сроков. Краткосрочный прогноз модели и сезонный baseline для дальней закупки могут быть разумной промежуточной политикой. Но границу выбора нужно настроить на валидации, затем проверить на отдельном периоде. Зафиксируйте её в сервисе, чтобы отчёт оценивал реальную комбинацию.

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

Если пользователю нужен суммарный объём закупки, дополнительно сложите дневные прогнозы внутри срока поставки и сравните с суммой фактов. Хорошая ошибка отдельного дня не гарантирует правильного недельного заказа: положительные и отрицательные отклонения могут компенсироваться или складываться. Сохраняйте дневной и агрегированный отчёты под разными названиями, чтобы выбор горизонта соответствовал реальному решению закупщика.

В платном курсе «ML Engineer / Data Scientist: табличные модели в продакшн» вы строите валидацию без утечек, сравниваете модели и собираете batch-инференс. Посмотрите программу курса, чтобы превратить таблицу экспериментов в воспроизводимый расчёт, сохраняющий даты, прогнозы и правила выбора модели.

Продолжить на практике

  • ML Engineer / Data Scientist: табличные модели в продакшн ↗

    Практический курс для тех, кто хочет получить навыки ML Engineer / Data Scientist на реальных табличных задачах. В курсе Вы не просто обучите CatBoost/LightGBM, а соберёте полноценный production-style пайплайн: подготовка данных, feature engineering (генерация признаков), корректная валидация, защита от leakage (утечек), тюнинг через Optuna, интерпретация через SHAP, batch-инференс и REST API для предсказаний.