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

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

Это не проблема конкретной нейросети. В официальном примере scikit-learn случайное разделение временного ряда даёт чрезмерно оптимистичную оценку по сравнению с разбиением по времени. Учебник Forecasting: Principles and Practice предлагает проверку с «движущейся точкой прогноза»: на каждом шаге модель видит только прошлое, затем предсказывает ещё не наступивший период. Для малого бизнеса такая проверка часто полезнее выбора самой сложной модели.

Практический вопрос не «насколько точно ИИ угадал продажи?», а «помог бы он заказать товар вовремя и в нужном количестве?». Для этого надо сначала определить решение, которое будет принимать человек: заказ на следующую неделю, график смен или план производства. Горизонт проверки должен совпадать с задержкой поставки и моментом, когда заказ ещё можно изменить.

Соберите данные так, как они были известны тогда

Для пилота выберите ограниченную группу товаров с повторяемым спросом. Нужны дата и время продажи, артикул, количество, возвраты, остатки, поставки, отмены, цены и промоакции. Если у поставщика срок исполнения две недели, прогноз на завтра сам по себе не отвечает на вопрос закупки: нужен спрос на период до следующего пополнения. Календарь праздников допустим как заранее известный фактор; фактическая погода будущего дня или итог акции — нет.

Для каждого исторического «момента решения» восстановите снимок доступной информации. Полезно хранить два времени: когда событие произошло и когда запись стала доступна системе. Если продажу за понедельник внесли в ERP в среду, при симуляции решения во вторник этой продажи ещё нет. В витрине признаков полезен контроль `available_at <= cutoff`, а не только `event_date <= cutoff`. Этот приём — редакционная архитектурная рекомендация, а не обещание готовой функции конкретной библиотеки.

Отдельно пометьте дни отсутствия товара. Нулевые продажи при пустой полке не доказывают нулевой спрос: модель может выучить дефицит как норму и заказать ещё меньше. Без подтверждённых неудовлетворённых заказов такие периоды лучше считать неопределёнными для оценки спроса и проверить с владельцем закупок. Не «восстанавливайте» потерянный спрос произвольной цифрой, иначе пилот начнётся с выдуманного результата.

Проверка: несколько прошлых понедельников вместо случайной лотереи

Разделите историю на последовательные окна. Например, в первом окне обучайтесь на данных до конца марта и прогнозируйте апрель; во втором — до конца апреля и прогнозируйте май. Для недельных закупок используйте недельный горизонт и соответствующий шаг. Настройка преобразований, отбор признаков и подбор параметров должны происходить внутри каждого учебного окна. Финальный отложенный период не трогайте до выбора подхода.

При построении признаков из лагов следите за моментом их доступности. Средняя продажа за последние четыре недели допустима, если эти недели закончились до даты решения. Средняя за окно, в которое попала прогнозируемая неделя, — утечка. Если данные приходят с опозданием, между учебным и проверочным окнами может понадобиться зазор; `TimeSeriesSplit` в scikit-learn поддерживает параметр `gap`. Сам по себе зазор не исправит неправильно сформированные будущие признаки: проверяйте их происхождение отдельно.

Сравните хотя бы три решения: действующее правило закупщика, простую сезонную базу и более сложную модель. Учебник Hyndman и Athanasopoulos показывает, что простые «последнее наблюдение» и «тот же период прошлого сезона» служат сильными контрольными вариантами. Если новая система не выигрывает у такого базиса на нескольких окнах, её сложности пока нечем платить.

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

Архитектура пилота без обязательной LLM

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

Локальный контур уместен, если коммерческие данные и цены не должны уходить наружу или объём регулярных расчётов оправдывает собственный сервер. Для небольшой витрины спроса начните с простого статистического метода или табличной модели на CPU. Большая языковая модель не делает временные ряды честнее. Её можно добавить позже для объяснения уже рассчитанного прогноза на русском языке, но численные значения и разрешение на заказ должны приходить из проверяемого расчёта и человеческого согласования.

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

Экономика: считать изменение решения, а не балл модели

Предположим, модельный пример для 50 товаров: при прежнем правиле за месяц остаются 600 лишних единиц и документированы 120 неудовлетворённых заказов. После управляемого внедрения — 500 лишних единиц и 100 подтверждённых неудовлетворённых заказов при сопоставимых условиях. Если содержание лишней единицы стоит условные 8 рублей в месяц, высвобождение 100 единиц даёт 800 рублей. Если 20 реально восстановленных продаж приносят по 250 рублей маржинального дохода, это ещё 5 000 рублей. Совокупный эффект до затрат — 5 800 рублей; при поддержке 3 000 рублей в месяц остаётся 2 800 рублей модельного эффекта.

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

Следующий шаг: две недели в тени закупщика

Возьмите 20–50 товаров с достаточной историей и один реальный цикл пополнения. В первую неделю очистите даты доступности, акции и дни дефицита, затем прогоните несколько временных окон против действующего правила и сезонного базиса. Во вторую неделю выдавайте рекомендации «в тени»: закупщик видит их, но заказы оформляет по текущему процессу. Сохраняйте расхождения и объяснения, особенно когда модель предлагает резкий скачок.

К переходу в ограниченный рабочий режим допускайте только вариант, который не использовал будущие данные, устойчив на разных окнах и улучшает стоимость реального решения, а не одну метрику. Начните с ручного утверждения заказов и права быстро вернуться к прежнему правилу. Обложка создана ИИ для «ВнутрИИ»: менеджер не даёт роботу перенести будущую коробку в прошлые данные. Именно такой барьер нужен и в расчётах.