Модель обещает доставку за 42 минуты: строим интервал по её ошибкам
Модель интернет-магазина оценивает время доставки при оформлении заказа. Для ближайшего заказа она выдаёт 42 минуты. Команда хочет показать покупателю диапазон, в который будут попадать примерно девять из десяти фактических сроков. Добавим диапазон к существующему прогнозу, используя историю ошибок этой модели.
Какие ошибки брать
Разделите данные по назначению. На первом наборе обучите модель и выберите признаки. На втором получите прогнозы уже зафиксированной модели и сравните их с фактическим временем. Этот набор называется калибровочным. На третьем проверьте долю попаданий и ширину диапазонов. Если после проверки меняете правила, для финальной оценки понадобится новый набор.
Возьмём абсолютную разницу между фактом и прогнозом. Ошибка 13 минут возникает и при прогнозе 42 вместо 55, и при прогнозе 42 вместо 29. Прибавим выбранный запас к прогнозу и вычтем его из прогноза. Такой симметричный диапазон подходит, когда команда отвечает за оба края. Если клиенту важен только самый поздний срок, нужен односторонний расчёт по знаковым ошибкам и своя метрика опозданий.
Почему индекс берут с поправкой
Сортируем абсолютные ошибки по возрастанию и берём значение на позиции ceil((n + 1) × 0.9), считая с единицы. При девяти калибровочных заказах получаем девятую ошибку, то есть максимум. При обменности десятая ошибка, принадлежащая новому заказу, может с равной вероятностью занять любой ранг при случайном разрешении совпадений. Лишь верхняя позиция допускает ошибку строго больше максимума прошлых девяти. Этот аргумент объясняет прибавление единицы в формуле. Интерполяция обычного 90-го процентиля может выбрать меньшую ширину.
Если нужная позиция больше длины ряда, стандартная конструкция даёт бесконечную границу. Для продукта это означает, что калибровку с заданным уровнем пока нельзя выпускать с конечной шириной. В коде явно поднимем ошибку. Для 90% минимально хватает девяти наблюдений, но при таком количестве результат зависит от единственного максимума и может сильно измениться после следующего пересчёта.
Две границы и тестовый набор
Скрипт запускается на стандартной библиотеке Python. Пары содержат прогноз и фактическое время в минутах. Замените их выгрузкой из журнала прогнозов, присоединив факт по идентификатору заказа. Назначьте дату выгрузки так, чтобы исходы успели прийти. Если выбирать только уже завершённые доставки среди свежих заказов, в калибровке будут хуже представлены самые долгие.
from fractions import Fraction
from math import ceil
calibration = [
(28, 30), (34, 35), (21, 24), (50, 45), (40, 46),
(39, 31), (26, 35), (30, 41), (35, 48),
]
test = [(42, 51), (29, 20), (50, 63), (36, 50), (32, 36)]
def conformal_margin(pairs, target):
errors = sorted(abs(actual - forecast) for forecast, actual in pairs)
rank = ceil((len(errors) + 1) * target)
if not 1 <= rank <= len(errors):
raise ValueError("Недостаточно ошибок для конечной границы")
return errors[rank - 1]
margin = conformal_margin(calibration, Fraction(9, 10))
hits = 0
for forecast, actual in test:
lower, upper = forecast - margin, forecast + margin
covered = lower <= actual <= upper
hits += covered
print(f"{lower:2}..{upper:2} | факт {actual:2} | {covered}")
assert margin == 13 and hits == 4
print(f"Покрытие: {hits}/{len(test)}")
На калибровке запас составляет 13 минут. Заказ с прогнозом 42 получает интервал от 29 до 55. Четыре из пяти контрольных заказов попали в свои интервалы. На этих числах удобно вручную проверить индексы и обработку края. Для решения о запуске смотрите вместе покрытие, ширину и число примеров. Если диапазон почти всегда шире периода, в который покупатель готов ждать дома, хорошее покрытие ещё не делает его полезным.
Как перенести расчёт в сервис
Храните ширину рядом с версией модели и описанием калибровочной выборки. После обновления модели пересчитайте ошибки заново: даже при похожей MAE могли измениться редкие большие промахи. На рабочем трафике считайте покрытие по дате прогноза после завершения заказов. Если в городе открылась дальняя зона доставки, проверьте её отдельно. Общий показатель легко скрывает низкое покрытие в новом районе.
Конечная гарантия split conformal относится к среднему покрытию новых наблюдений при обменности калибровочных и будущих ошибок. При смене логистических правил прошлые и новые ошибки могут различаться систематически. Для выпуска выберите заранее, какой ширины обещание готов показывать продукт и на каких потоках оно должно пройти проверку. Если новый режим сильно изменил сроки, соберите ошибки на новом режиме и повторите калибровку.
Практика на Latorn
Бесплатный урок по линейной регрессии поможет получить исходные прогнозы и сверить их с ответами на отдельной выборке.
В платном курсе «ML Engineer / Data Scientist» вы обучите табличные модели, сравните их по метрикам и доведёте сохранённый прогноз до пакетного расчёта и REST API. В таком пайплайне к модели можно добавить версионированный расчёт диапазона из статьи. Открыть программу курса.
Продолжить на практике
- ML Engineer / Data Scientist: табличные модели в продакшн ↗
Уже обучали модели на Python? Научитесь доводить их до работы в приложении: проверять качество без утечек, сравнивать CatBoost и LightGBM, объяснять прогнозы через SHAP и запускать модель через FastAPI. Практика на табличных данных, Docker, пакетный инференс и мониторинг.
