Метрики прогноза: MAE, MAPE, RMSE
Разбираем три главные метрики ошибки прогноза и понимаем, когда какую выбирать.
MAE — средняя абсолютная ошибка, RMSE — корень из средней квадратичной, MAPE — средняя ошибка в процентах. У каждой свой характер.
Зачем разные метрики
«Хороший прогноз» — относительно чего? Метрика переводит расхождение прогноза и факта в одно число, но разные метрики подчёркивают разное. MAE одинаково считает все промахи. RMSE сильнее наказывает крупные ошибки. MAPE выражает ошибку в процентах и потому сравнима между рядами разного масштаба. Выбор метрики — это выбор того, какие ошибки для бизнеса больнее.
Представьте, что вы прогнозируете спрос на товар в розничной сети. Если модель в среднем ошибается на 5 единиц, это много или мало? Для гвоздей, которых продаётся тысячи в день, — ничтожно. Для дорогого холодильника, которого уходит две штуки в неделю, — катастрофа: либо склад забит неликвидом, либо покупатель ушёл к конкуренту. Одна и та же абсолютная ошибка означает совершенно разные деньги. Именно поэтому метрика — не формальность из учебника, а способ перевести технический промах в язык, на котором говорит бизнес: упущенная выручка, замороженный оборотный капитал, штраф за срыв поставки.
Ещё одна причина держать в голове несколько метрик сразу — они отвечают на разные вопросы. MAE говорит «насколько мы типично ошибаемся в штуках». RMSE предупреждает «есть ли у нас редкие, но болезненные провалы». MAPE отвечает «на сколько процентов мы мажем» и позволяет в одном дашборде сравнить прогноз по молоку и по бытовой технике. Отчитываться одной цифрой удобно, но опасно: модель легко оптимизировать под красивую метрику, испортив то, что метрика не видит. Поэтому на практике смотрят на связку и выбирают ту, которую оптимизируют, осознанно.
Считаем все три
import math
def mae(y, f): return round(sum(abs(a-b) for a, b in zip(y, f)) / len(y), 3)
def rmse(y, f): return round(math.sqrt(sum((a-b)**2 for a, b in zip(y, f)) / len(y)), 3)
def mape(y, f): return round(100 * sum(abs((a-b)/a) for a, b in zip(y, f)) / len(y), 2)
actual = [100, 110, 120, 130]
forecast = [105, 108, 125, 128]
print("MAE: ", mae(actual, forecast))
print("RMSE:", rmse(actual, forecast))
print("MAPE:", mape(actual, forecast), "%")
Вывод:
MAE: 3.5 RMSE: 3.808 MAPE: 3.13 %
RMSE (3.808) больше MAE (3.5): квадрат усилил вклад самого крупного промаха. MAPE (3.13%) удобна для отчёта бизнесу — «ошибаемся в среднем на 3%», и её можно сравнивать между товарами с разными объёмами продаж.
Разберём по строкам, что именно происходит в коде. В mae мы складываем модули отклонений abs(a-b) и делим на число точек — получается ровно «средний промах в тех же единицах, что и сам ряд». В rmse отклонения возводятся в квадрат, усредняются и из результата берётся корень: квадрат делает большие ошибки непропорционально дорогими, а корень возвращает результат в исходные единицы, чтобы число оставалось интерпретируемым. В mape каждая ошибка делится на фактическое значение a, поэтому метрика измеряет относительный, а не абсолютный промах. На наших данных все три цифры близки, потому что прогноз неплох и выбросов нет — расхождение между MAE и RMSE проявляется только тогда, когда ошибки распределены неравномерно.
RMSE наказывает выбросы
Покажем, как один крупный промах раздувает RMSE сильнее, чем MAE.
import math
def mae(y,f): return round(sum(abs(a-b) for a,b in zip(y,f))/len(y),3)
def rmse(y,f): return round(math.sqrt(sum((a-b)**2 for a,b in zip(y,f))/len(y)),3)
y = [50, 50, 50, 50]
small = [52, 48, 51, 49] # равномерные мелкие ошибки
big = [50, 50, 50, 58] # один крупный промах
print("Равномерные: MAE", mae(y,small), "RMSE", rmse(y,small))
print("Один крупный: MAE", mae(y,big), "RMSE", rmse(y,big))
Вывод:
Равномерные: MAE 1.5 RMSE 1.581 Один крупный: MAE 2.0 RMSE 4.0
При одном крупном промахе MAE выросла слабо (1.5 → 2.0), а RMSE — резко (1.58 → 4.0). Если для бизнеса один большой провал хуже многих мелких (например, дефицит товара), выбирайте RMSE.
Эта разница — не математический курьёз, а прямое отражение того, как устроены риски. Вернёмся к складу: десять дней мелких неточностей по паре единиц легко компенсируются страховым запасом и никто их не заметит. Но один день, когда модель промахнулась на 40 единиц и полка опустела в разгар акции, оборачивается потерянными продажами, недовольными покупателями и испорченным отчётом. RMSE «чувствует» именно такие хвостовые события, потому что квадрат превращает редкий крупный промах в доминирующее слагаемое суммы. Если же ваши потери линейны — например, вы платите фиксированную сумму за каждую недопоставленную единицу независимо от того, в один день она набралась или растянулась на месяц, — честнее оптимизировать MAE, и тогда модель не будет переусердствовать, защищаясь от редких выбросов.
Почему не R²
R² показывает долю объяснённой дисперсии и хорош для регрессии на независимых данных. Для временных рядов он обманчив: на ряде с трендом даже наивный прогноз «завтра как сегодня» даёт высокий R², потому что тренд сам по себе «объясняет» дисперсию. R² легко бывает близок к единице у плохой модели — он не отвечает на вопрос «насколько точен прогноз в единицах величины». Поэтому для прогноза используют MAE/RMSE/MAPE, а не R².
Чтобы прочувствовать ловушку, представьте растущий ряд выручки: каждый месяц чуть больше предыдущего. Базовая дисперсия здесь огромна — значения «гуляют» от ста до тысячи. Любая модель, которая хотя бы примерно следует за восходящим движением, автоматически «объясняет» бо́льшую часть этого разброса и получает R² около 0.95, даже если на коротком горизонте она систематически промахивается. Менеджер видит «0.95» и считает модель отличной, хотя в деньгах она ошибается недопустимо. Метрики ошибки в единицах величины этой иллюзии не создают: они показывают абсолютный или процентный промах напрямую, и улучшить их формальным «угадыванием тренда» не получится.
Как работает под капотом
MAE минимизируется медианным прогнозом, RMSE — средним: вот почему модели, обученные на квадратичной ошибке, тянутся к среднему и боятся выбросов. MAPE несимметрична и взрывается при значениях близких к нулю (деление на маленький факт), а ещё штрафует завышение и занижение по-разному. Для рядов с нулями вместо MAPE берут sMAPE или WAPE.
Свойство «MAE тянет к медиане, RMSE — к среднему» имеет прямое практическое следствие. Если в обучении встречаются дни-аномалии (распродажа, сбой кассы, гигантский разовый заказ), модель на квадратичной ошибке будет смещать все прогнозы вверх, лишь бы уменьшить штраф за эти редкие пики, — и в обычные дни начнёт систематически завышать. Модель на MAE спокойнее: медиана игнорирует единичные выбросы, и типичные дни прогнозируются точнее ценой того, что в день пика будет крупный, но единичный промах. Что важнее — решает бизнес.
Асимметрия MAPE заслуживает отдельного внимания. Из-за деления на факт занижение прогноза не может стоить больше 100% (ошибка ограничена сверху), а вот завышение ничем не ограничено: спрогнозировать 300 при факте 100 — это 200% ошибки. В итоге модель, которую тренируют под MAPE, незаметно учится занижать прогноз, потому что так дешевле по метрике. Для рядов, где факт регулярно опускается к нулю — продажи редких товаров, прерывистый спрос, — MAPE и вовсе теряет смысл: одно деление на ноль обнуляет весь отчёт. WAPE (взвешенная по объёму абсолютная ошибка) и sMAPE (симметричная версия) — стандартные замены, которые сохраняют процентную интерпретацию, но не разваливаются на нулях.
Частые ошибки
- Использовать R² как метрику качества прогноза — на трендовых рядах он завышен.
- Считать MAPE на ряде с нулями или близкими к нулю значениями — она взрывается.
- Сравнивать MAE между рядами разного масштаба (для этого нужна процентная MAPE/WAPE).
- Оптимизировать модель под MAPE и не замечать, что она систематически занижает прогноз из-за асимметрии метрики.
- Отчитываться единственной метрикой: красивая цифра может скрывать редкие болезненные провалы, которые видит только RMSE.
Итоги
- MAE — ровная средняя ошибка; RMSE наказывает крупные промахи; MAPE даёт проценты.
- R² для прогноза рядов обманчив — тренд завышает его даже у наивной модели.
- Выбор метрики = выбор того, какие ошибки для бизнеса дороже.
- MAE тянет прогноз к медиане, RMSE — к среднему; это меняет поведение модели на выбросах.
- На рядах с нулями и прерывистым спросом вместо MAPE берите WAPE или sMAPE.