Послепродажное обслуживание автомобилей сталкивается с парадоксом: сотни тысяч SKU, из которых 80% продаются реже раза в месяц, при этом дефицит одной критической детали останавливает автомобиль на неделю. Спрос на новые детали (cold start) вообще невозможно предсказать классическими методами. Мы решаем эту задачу, комбинируя ABC-классификацию с ML-моделями, адаптированными под разные типы спроса. Наш подход снижает уровень запасов на 15–25% при сохранении или улучшении сервисного уровня — это проверено на 50+ проектах в дистрибуции запчастей.
Почему Croston и TSB необходимы для intermittent demand?
Большинство деталей продаётся редко и неравномерно. ARIMA или Prophet на таком спросе дают ошибку MAPE >100%. Croston раздельно прогнозирует интервал между продажами и размер транзакции, а TSB (Teunter-Syntetos-Babai) добавляет адаптацию под устаревающие детали — это снижает bias на 30–50%. На реальных данных дистрибьютора Croston показал MAPE 78% против 142% у ARIMA — в 1.8 раза точнее.
from statsforecast.models import CrostonOptimized, IMAPA, TSB
models = [
CrostonOptimized(),
IMAPA(),
TSB(alpha_d=0.1, alpha_p=0.1)
]
Сравнение методов прогноза для intermittent demand:
| Модель |
MAPE на C-классе |
Применимость |
| CrostonOptimized |
78% |
Редкий спрос без тренда |
| TSB |
72% |
Редкий спрос с устареванием |
| ARIMA |
142% |
Частый спрос |
| Prophet |
115% |
Частый спрос с сезонностью |
Как park-based forecast повышает точность?
Спрос на запчасти определяется не только историей продаж, но и парком автомобилей. Мы строим модель: спрос = количество машин × вероятность отказа детали в зависимости от возраста. Источник данных — регистрационные базы (парк по моделям и годам).
def park_based_forecast(part_number, region):
applicable_models = parts_catalog.get_applicable_models(part_number)
park = vehicle_registration_db.count(
models=applicable_models,
region=region,
age_range=(2, 20)
)
failure_curve = get_failure_rate_curve(part_number)
expected_demand = sum(
park[age] * failure_curve[age]
for age in range(2, 21)
) / 12
return expected_demand
Этот метод особенно эффективен для новых деталей или при выводе продукта (cold start), когда истории продаж ещё нет.
Оптимизация safety stock с учётом асимметрии затрат
Классический newsvendor model: мы оптимизируем страховой запас не по симметричному нормальному распределению, а по реальным затратам на дефицит (простой клиента, потеря лояльности) и хранение. Себестоимость дефицита критической детали может в 50 раз превышать стоимость хранения — safety stock для таких SKU поднимается до z=3.0 против стандартного z=1.65. Типичная экономия от такого подхода — до 2 млн рублей в год для среднего склада.
| Класс деталей |
Сервисный уровень |
Коэффициент z |
Cost ratio (дефицит/хранение) |
| Критические (immobilizing) |
98–99% |
2.0–2.3 |
30:1 – 50:1 |
| Стандартные расходники |
93–95% |
1.5–1.65 |
10:1 – 20:1 |
| C-класс (медленные) |
85–90% |
1.0–1.28 |
2:1 – 5:1 |
from scipy.stats import norm
def optimal_safety_stock(mean_demand, std_demand, lead_time_days,
service_level=0.95, holding_cost_rate=0.25,
stockout_cost=50.0):
cu = stockout_cost
co = holding_cost_rate * unit_cost / 365
critical_ratio = cu / (cu + co)
z = norm.ppf(critical_ratio)
demand_during_lt = mean_demand * lead_time_days
std_during_lt = std_demand * np.sqrt(lead_time_days)
safety_stock = z * std_during_lt
reorder_point = demand_during_lt + safety_stock
return safety_stock, reorder_point
Детали расчёта critical ratio
Формула critical_ratio = Cu / (Cu + Co) — где Cu (cost of understock) — потери от дефицита, Co (cost of overstock) — издержки хранения избытка. При Cu/Co = 50 critical_ratio ≈ 0.98, что соответствует сервисному уровню 98%.
Как ML Ops поддерживает модели в продакшене?
Модели деградируют со временем: меняются парк автомобилей, сезонность, ассортимент. Мы настроили ML Ops-конвейер: автоматическое переобучение при снижении точности (alert при MAPE > 90%), логирование метрик в MLflow, A/B-тестирование новых версий. Это гарантирует стабильную работу прогнозов без ежемесячного ручного вмешательства.
Управление устаревающими деталями (phase-out)
Снятая с производства модель — спрос угасает, но не мгновенно. TSB-модель выявляет тренд на снижение, а наш скрипт детектирует признаки EOL:
def detect_obsolescence_risk(part_number, sales_history):
trend = np.polyfit(range(len(sales_history)), sales_history, 1)[0]
park_decline = get_park_trend(part_number)
if trend < -0.1 and park_decline < -0.05:
return 'phase_out', suggest_final_buy_quantity(part_number)
return 'active', None
Final buy — расчёт оптимального заказа на 5–7 лет гарантийного обслуживания. Мы учитываем, что после EOL детали дорожают или становятся недоступны.
Multi-echelon: снижение bullwhip effect
В цепочке OEM → дистрибьютор → дилер вариация спроса усиливается. Мы синхронизируем safety stock на всех уровнях: чем выше буфер у дилера, тем меньше страховой запас дистрибьютора. VMI (Vendor Managed Inventory) в реальном времени пополняет склад дилера — это снижает общий запас по цепочке на 15–20%.
Что входит в работу
- Аудит данных: история продаж, парк автомобилей, каталог деталей.
- Построение ML-моделей: Croston, TSB, park-based, сезонные факторы.
- Оптимизация safety stock и reorder points (дифференцированные сервисные уровни).
- Phase-out management и final buy рекомендации.
- Дашборд с прогнозами, alerting по дефициту/излишкам.
- Интеграция с WMS/ERP, настройка VMI.
- Обучение команды и эксплуатационная документация.
Окупаемость решения составляет менее 12 месяцев. Свяжитесь с нами — оценим ваш складской портфель и покажем потенциал снижения запасов. Наша команда: 10+ лет в ML-решениях для логистики, 50+ внедрённых проектов, сертифицированные специалисты по Croston method.
Какие проблемы прогнозирования временных рядов встречаются чаще всего?
Финансовый директор запрашивает прогнозирование временных рядов продаж на квартал. Аналитик строит SARIMA, добивается MAPE 8.3% на тестовой выборке — и с гордостью деплоит. Через два месяца в production метрика падает до 23%. Причина классическая: модель обучалась на данных до COVID, тестировалась на стабильном периоде, а production попал на промо-акцию и сбой поставок. Data leakage + distribution shift = красивые цифры в ноутбуке и неработающий прогноз в реальности. Мы сталкивались с этим десятки раз. Наш опыт — 5+ лет в прогнозировании временных рядов для ритейла, финтеха и IoT, более 50 завершённых проектов.
Неправильная кросс-валидация. Стандартный train_test_split для временных рядов — ошибка. Случайное разбиение создаёт data leakage: модель видит «будущие» значения в обучении. Правильно — TimeSeriesSplit или walk-forward validation с expanding window.
Множественная сезонность. Почасовые данные потребления электроэнергии имеют три сезонности: суточную (24 ч), недельную (168 ч), годовую (8760 ч). SARIMA справляется только с одной. Prophet обрабатывает несколько, но медленно масштабируется на тысячи рядов.
Пропуски и аномалии в данных. Пропуск в сенсорных данных — это информация (датчик отключился), а не просто NaN. Линейная интерполяция убивает этот сигнал. Правильная обработка зависит от природы пропуска.
Cold start при иерархическом прогнозировании. Новый SKU в ассортименте из 50 000 позиций: исторических данных нет, нужен прогноз. Стандартные подходы тут не работают — нужны cross-learning подходы или feature-based методы.
Какие инструменты и когда применять?
Prophet (Meta) — отличный старт для бизнес-данных с понятной сезонностью и праздниками. Быстро настраивается, интерпретируем, встроенная обработка выбросов и пропусков. Падает в точности при нерегулярных паттернах и не масштабируется на десятки тысяч рядов без параллелизации. Prophet (Facebook) — официальная документация.
Gradient boosting на фичах (LightGBM, XGBoost) — часто недооценённый подход. Создаёте фичи вручную: лаги (t-1, t-7, t-28), скользящие средние, категориальные признаки (день недели, месяц), экзогенные переменные. Модель обучается на всех рядах одновременно — решает cold start через похожие ряды. MAPE на ритейл-прогнозировании часто лучше нейронных сетей при правильной feature engineering.
TFT (Temporal Fusion Transformer) — трансформер, специально разработанный для интерпретируемого прогнозирования с ковариатами. Встроенные механизмы: variable selection (какие признаки важны), temporal self-attention (какие временные точки влияют на прогноз), квантильные предсказания. Доступен в pytorch-forecasting. Требует ~10 000+ записей на ряд для стабильного обучения. Temporal Fusion Transformer — академическая публикация.
PatchTST — трансформер, который делит временной ряд на патчи (аналогично ViT для изображений). Лучше захватывает локальные паттерны, чем классические трансформеры. Хорошо работает для long-horizon forecasting (прогноз на 96–720 шагов). Реализация в neuralforecast от Nixtla.
N-HiTS, N-BEATS — нейронные архитектуры без attention, быстрее TFT, конкурентная точность. N-BEATS выигрывает на M4/M5 benchmark для задач без ковариат.
| Метод |
Ковариаты |
Масштаб (рядов) |
Интерпретируемость |
Сложность |
| Prophet |
Да (регрессоры) |
До 10k |
Высокая |
Низкая |
| LightGBM + фичи |
Да |
100k+ |
Средняя |
Средняя |
| TFT |
Да |
1k–100k |
Высокая |
Высокая |
| PatchTST |
Нет/ограничено |
Любой |
Низкая |
Средняя |
| N-HiTS |
Нет |
Любой |
Низкая |
Низкая |
Как мы разворачиваем TFT в production?
TFT требует тщательной подготовки данных. Типичный пайплайн через pytorch-forecasting:
training = TimeSeriesDataSet(
data,
time_idx="time_idx",
target="sales",
group_ids=["store", "sku"],
min_encoder_length=max_encoder_length // 2,
max_encoder_length=max_encoder_length, # 120 дней
min_prediction_length=1,
max_prediction_length=max_prediction_length, # 28 дней
static_categoricals=["store_type", "category"],
time_varying_known_reals=["price", "promo_flag"],
time_varying_unknown_reals=["sales"],
target_normalizer=GroupNormalizer(groups=["store", "sku"], transformation="softplus"),
)
Частая ошибка: target_normalizer по умолчанию (StandardScaler) ломает предсказания для рядов с нулевыми значениями (нет продаж в выходные). GroupNormalizer с transformation="softplus" — правильный выбор для count-данных.
Пошаговая инструкция по настройке TFT
-
Сбор и подготовка данных. Обработать пропуски (маркировать NaN, интерполировать только если это технический сбой), агрегировать до нужной частоты, сформировать ковариаты (праздники, промо, цены).
-
Создание
TimeSeriesDataSet. Указать group_ids (например, магазин+SKU), временной индекс, горизонт прогноза. Настроить target_normalizer с учётом распределения таргета.
-
Обучение baseline. Сначала Prophet или LightGBM — чтобы понять, насколько сложнее задача.
-
Тренировка TFT. Запустить
TemporalFusionTransformer с loss=QuantileLoss(), подобрать learning rate и размеры hidden слоёв. Использовать pytorch_forecasting или neuralforecast.
-
Валидация и интерпретация. Проверить walk-forward, проанализировать variable selection, построить attention heatmap.
Кейс: прогноз спроса в ритейле. Сеть из 120 магазинов, 8000 SKU, горизонт прогноза 28 дней. Исходная система: SARIMA отдельно для каждого ряда, MAPE 18.4%, полный цикл переобучения — 6 часов. TFT на PyTorch + pytorch-forecasting: одна модель на все ряды, MAPE 11.2%, переобучение — 40 мин на A10G. Дополнительный бонус: feature importance через variable selection — выяснилось, что day_before_holiday влияет сильнее, чем сама дата праздника. Средняя экономия бюджета на инференсе для клиента составила 1.5 млн ₽ в год.
Как правильно оценивать качество прогнозов?
Не используйте RMSE как единственную метрику — она сильно штрафует за большие ошибки на больших значениях. Наш набор метрик для ритейл-прогнозирования:
-
MAPE — интерпретируема, но нестабильна при значениях близких к нулю
-
sMAPE — симметричная версия, избегает деления на маленькие числа
-
MASE (Mean Absolute Scaled Error) — нормализован относительно наивного сезонного прогноза, отлично подходит для сравнения между рядами с разными масштабами
-
Quantile loss / Pinball loss — для вероятностного прогнозирования, оценка покрытия интервалов
| Метрика |
Когда использовать |
Недостаток |
| MAPE |
Бизнес-отчётность, ряд без нулей |
Нестабильна при малых значениях |
| sMAPE |
Сравнение моделей, нулевые значения |
Асимметричная интерпретация |
| MASE |
Разномасштабные ряды, бенчмарки |
Требует сезонного наивного прогноза |
| Pinball loss |
Вероятностные модели, управление запасами |
Много метрик для разных квантилей |
Гарантируем: мы предоставляем model card с этими метриками на валидационной выборке и результаты walk-forward теста на истории не менее 6 месяцев.
Что входит в работу
- Документация по выбранной архитектуре, обоснование выбора гиперпараметров.
- Воспроизводимый пайплайн обучения и инференса (Docker + CI/CD + Airflow/Prefect).
- Код с комментариями и модульными тестами на ключевые компоненты.
- Обучение вашей команды: как переобучать модель, как интерпретировать выходы, как деплоить новые версии.
- Поддержка в течение 3 месяцев после сдачи: консультации, фиксы багов, донастройка.
Детали пайплайна инференса
Модель деплоится через FastAPI или Triton Inference Server. Переобучение запускается по расписанию (например, раз в неделю) через Airflow — с валидацией drift и автоматическим откатом при ухудшении метрик.
Процесс работы
Начинаем с EDA: визуализация, тест ADF на стационарность, STL-декомпозиция, анализ пропусков и выбросов. Это 2–3 дня, но часто выявляет системные проблемы данных, которые блокируют прогнозирование.
Затем: baseline (наивный seasonal, Prophet), feature engineering для LGBM, выбор архитектуры нейронной сети если нужно. Walk-forward validation с реалистичным горизонтом. Деплой через API с автоматическим переобучением по расписанию через Airflow или Prefect.
Сроки ориентировочно: MVP-прогноз на одном типе данных — 3–6 недель. Иерархическая система прогнозирования с автоматизацией — 2–5 месяцев. Стоимость рассчитывается индивидуально.
Наша команда — сертифицированные ML-инженеры (AWS ML Specialty, GCP Professional ML Engineer). За 5 лет на рынке реализовали более 50 проектов по прогнозированию. Свяжитесь с нами для бесплатного анализа ваших данных — мы оценим задачу и дадим первые рекомендации за 1–2 дня. Закажите консультацию и убедитесь, что ваши прогнозы работают в production, а не только в ноутбуке.