Простое экспоненциальное сглаживание
Знакомимся с моделью, где вес прошлого спадает плавно, а вся память ряда упакована в одно число.
Простое экспоненциальное сглаживание (SES) — рекуррентная оценка уровня: s_t = α·x_t + (1-α)·s_{t-1}, где α задаёт скорость забывания старого.
Зачем экспоненциальные веса
Скользящее среднее обрубает память жёстко: точки в окне важны, всё за окном — выкинуто, и все внутри равноправны. Это неестественно: вчера важнее, чем неделю назад, но и неделю назад — не ноль. SES задаёт плавно спадающие веса: каждый шаг назад умножает вклад на (1-α). Так свежее весомее старого, но старое не выбрасывается резко.
Есть и чисто инженерная выгода. Скользящему среднему нужно держать в памяти весь срез из w последних точек и пересчитывать сумму на каждом шаге. SES хранит ровно одно число — текущую оценку уровня — и обновляет его одной строкой. Для потоковых систем, где данные идут бесконечным потоком (метрики серверов, котировки, показания датчиков), это решающее преимущество: память не растёт, обновление мгновенное, не нужно решать, сколько истории «помнить». Вся история уже сжата в накопленный уровень.
Сценарий из жизни: вы оцениваете средний дневной спрос на товар, чтобы держать страховой запас на складе. Поведение покупателей дрейфует — то мода поменялась, то конкурент поднял цены. Жёсткое окно либо реагирует с задержкой, либо нервно дёргается. SES даёт настраиваемый компромисс: одним числом α вы решаете, насколько быстро оценка забывает старое и подстраивается под новое, и эту настройку легко обосновать перед бизнесом — «модель ориентируется в основном на последние две-три недели».
Формула и параметр alpha
Параметр α от 0 до 1 управляет «реактивностью». Большой α (близко к 1) — модель почти повторяет последнюю точку, быстро реагирует, но шумит. Малый α — модель инертна, сильно сглаживает, но запаздывает. SES хорош для рядов без тренда и сезонности, колеблющихся вокруг уровня.
Полезная интуиция: SES примерно соответствует скользящему среднему с окном w ≈ 2/α − 1. То есть α=0.1 ведёт себя как окно около 19 точек (длинная память, сильное сглаживание), а α=0.5 — как окно около трёх точек (короткая память, быстрая реакция). Эта формула помогает переводить интуицию об окне в значение α и наоборот, когда вы переходите с SMA на SES и хотите сопоставимое поведение. Граничные случаи тоже показательны: при α=1 уровень буквально равен последнему наблюдению (наивный прогноз «завтра как сегодня»), а при α, стремящемся к нулю, оценка почти не двигается и превращается в долгое среднее всей истории.
def ses(xs, alpha):
s = xs[0]
out = [round(s, 2)]
for x in xs[1:]:
s = alpha * x + (1 - alpha) * s
out.append(round(s, 2))
return out
data = [10, 12, 11, 13, 15, 14, 16, 18, 17, 19]
print("alpha=0.3:", ses(data, 0.3))
Вывод:
alpha=0.3: [10, 10.6, 10.72, 11.4, 12.48, 12.94, 13.86, 15.1, 15.67, 16.67]
Сглаженный уровень плавно поднимается вслед за рядом. Прогноз SES на любой горизонт вперёд — это просто последнее значение уровня (16.67): без модели тренда экстраполировать рост SES не умеет. Это её принципиальное ограничение.
Обратите внимание на инициализацию: мы стартуем с s = xs[0], то есть первая оценка уровня — это просто первое наблюдение. Это разумный, но не единственный выбор; иногда уровень инициализируют средним по нескольким первым точкам, чтобы старт был устойчивее. На длинном ряде выбор стартового значения почти не влияет — экспоненциальные веса быстро «забывают» начало, — но на коротком он заметно сдвигает первые оценки, и об этом стоит помнить. В выводе видно, как формула работает по шагам: каждое новое значение уровня — это смесь 30% свежего наблюдения и 70% предыдущей оценки, поэтому ряд поднимается плавно, не повторяя дрожание исходных данных.
Влияние alpha наглядно
Сравним инертную и реактивную настройку на одном ряде.
def ses(xs, alpha):
s = xs[0]; out = [round(s, 2)]
for x in xs[1:]:
s = alpha * x + (1 - alpha) * s
out.append(round(s, 2))
return out
data = [10, 12, 11, 13, 15, 14, 16, 18, 17, 19]
print("alpha=0.1:", ses(data, 0.1))
print("alpha=0.8:", ses(data, 0.8))
Вывод:
alpha=0.1: [10, 10.2, 10.28, 10.55, 11.0, 11.3, 11.77, 12.39, 12.85, 13.47] alpha=0.8: [10, 11.6, 11.12, 12.62, 14.52, 14.1, 15.62, 17.52, 17.1, 18.62]
При α=0.1 уровень едва добрался до 13.5 — модель сильно отстала. При α=0.8 он почти повторяет последние точки (18.62). Подбор α — это балансировка между сглаживанием и отзывчивостью.
Этот пример хорошо иллюстрирует, что у SES нет «правильного» α в вакууме — всё зависит от того, что вы считаете шумом, а что сигналом. Здесь ряд устойчиво растёт, поэтому реактивная настройка α=0.8 ближе к данным и выглядит точнее. Но представьте, что этот же рост — случайная флуктуация вокруг стабильного уровня: тогда α=0.8 гналась бы за каждым случайным выбросом и давала бы дёрганый, бесполезный прогноз, а инертная α=0.1 правильно держала бы линию. Вывод практический: чем больше в ряде настоящего шума и чем стабильнее истинный уровень, тем меньше нужен α; чем быстрее и реальнее меняется уровень, тем α больше. Угадать на глаз почти невозможно — отсюда переход к оптимизации.
Как работает под капотом
Если развернуть рекуррентность, окажется, что s_t = α·x_t + α(1-α)·x_{t-1} + α(1-α)²·x_{t-2} + ... — это бесконечная сумма с геометрически убывающими весами. Сумма весов равна 1, поэтому уровень не смещён. Оптимальное α обычно подбирают минимизацией ошибки прогноза на истории (one-step-ahead), а не вручную.
Почему сумма весов равна единице — это не случайность, а ровно то, что гарантирует несмещённость. Веса α, α(1-α), α(1-α)², ... образуют геометрическую прогрессию, сумма которой α/(1−(1−α)) = 1 при любом α из (0;1). Это значит: если ряд стоит на постоянном уровне, SES в среднем выдаст ровно этот уровень, не завышая и не занижая. Геометрическое затухание объясняет и «экспоненциальное» в названии — вклад наблюдения, отстоящего на k шагов, падает как (1-α) в степени k, то есть экспоненциально с возрастом.
Про оптимизацию стоит сказать подробнее, потому что это то, чем SES живёт на практике. Мы прогоняем модель по всей истории, на каждом шаге сравниваем прогноз на один шаг вперёд с реально наступившим значением, копим сумму квадратов этих ошибок и затем численно ищем α, минимизирующее эту сумму. Так подбор перестаёт быть вкусовым: данные сами говорят, насколько быстро им свойственно меняться. Именно так это делают библиотеки вроде statsmodels — вы не задаёте α вручную, а доверяете оптимизатору, который перебирает значения и оставляет лучшее по ошибке на истории.
Частые ошибки
- Применять SES к ряду с трендом — прогноз будет систематически отставать.
- Брать α «на глаз», не оптимизируя его по ошибке прогноза.
- Ждать от SES экстраполяции роста: её прогноз — плоская линия на уровне последней оценки.
Первая ошибка стоит того, чтобы понять её механику. Когда ряд устойчиво растёт, SES всё время «догоняет» уровень снизу: пока она усредняет старые, меньшие значения с новым, оценка отстаёт от реального уровня на величину, пропорциональную скорости роста. Прогноз на следующий шаг (равный текущему уровню) оказывается систематически занижен — это не случайная ошибка, а постоянное смещение в одну сторону. Если на графике остатков вы видите, что прогноз стабильно ниже факта, это верный признак, что в данных есть тренд, а вы применили модель без тренда. Лечится переходом к методу Хольта, который мы разбираем в следующем уроке.
Итоги
- SES хранит уровень рекуррентно с экспоненциально убывающими весами прошлого.
- α управляет реактивностью: большой — быстро, но шумно; малый — гладко, но инертно.
- SES прогнозирует плоскую линию; для тренда нужна модель Хольта.
SES — это минимальная честная модель прогноза: одна формула, один параметр, одна величина в памяти. Её стоит держать как базовую линию (baseline), с которой сравнивают всё более сложное: если навороченная модель не бьёт SES с оптимальным α, она того не стоит. А её главное ограничение — плоский прогноз — ровно та дыра, которую закрывает добавление тренда и сезонности в методах Хольта и Хольта-Винтерса.