3. 指数平滑法:一次、二次、三次指数平滑原理,Holt-Winters模型在Python中的实现与调参
指数平滑法(Exponential Smoothing)是供应链需求预测中最常用的时间序列方法之一。其核心思想是:越近期的观测值包含越多的预测信息,因此赋予更高的权重;权重随时间呈指数衰减。本章从一次指数平滑出发,逐步深入到能处理趋势和季节性的Holt-Winters模型,并给出完整的Python实现与调参指南。
3.1 一次指数平滑(Simple Exponential Smoothing, SES)
一次指数平滑适用于无趋势、无季节性的平稳时间序列。其预测公式为:
预测值 F_{t+1} = α * Y_t + (1 - α) * F_t
其中:
- Y_t:t 时刻的实际观测值
- F_t:t 时刻的预测值(即对 t-1 时刻的预测)
- α:平滑系数(0 ≤ α ≤ 1),控制新旧信息的权重
关键特性:
- α 越接近 1,模型越“敏感”,对近期变化反应快,但易受随机波动影响。
- α 越接近 0,模型越“平滑”,历史信息保留多,但滞后性明显。
- 初始值 F_1 通常取 Y_1 或前几期的平均值。
供应链应用场景: 用于预测日销量稳定的快消品、标准件库存消耗。当产品处于成熟期且无促销扰动时,SES 是快速基线预测的首选。
3.2 二次指数平滑(Holt's Linear Trend Model)
当序列存在趋势(持续上升或下降)时,一次指数平滑会产生系统性滞后。Holt 模型引入趋势分量,同时平滑水平和趋势:
水平方程:L_t = α * Y_t + (1 - α) * (L_{t-1} + T_{t-1})
趋势方程:T_t = β * (L_t - L_{t-1}) + (1 - β) * T_{t-1}
预测方程:F_{t+m} = L_t + m * T_t
其中:
- L_t:t 时刻的平滑水平
- T_t:t 时刻的平滑趋势
- α:水平平滑系数
- β:趋势平滑系数(0 ≤ β ≤ 1)
- m:预测步长
参数含义对比:
| 参数 | 取值范围 | 大值效果 | 小值效果 |
|---|---|---|---|
| α(水平) | [0, 1] | 快速跟踪水平变化 | 平滑水平波动 |
| β(趋势) | [0, 1] | 快速响应趋势变化 | 趋势更稳定 |
注意: 当 β=0 时,趋势变为常数,模型退化为一次指数平滑加固定趋势。实际应用中,β 通常设为较小值(0.1~0.3),避免趋势过度反应。
3.3 三次指数平滑(Holt-Winters Model)
当序列同时存在趋势和季节性时,Holt-Winters 模型在 Holt 模型基础上增加季节分量。根据季节效应与趋势的关系,分为加法模型和乘法模型:
加法模型(季节幅度恒定):
水平:L_t = α * (Y_t - S_{t-p}) + (1-α) * (L_{t-1} + T_{t-1})
趋势:T_t = β * (L_t - L_{t-1}) + (1-β) * T_{t-1}
季节:S_t = γ * (Y_t - L_t) + (1-γ) * S_{t-p}
预测:F_{t+m} = L_t + m*T_t + S_{t+m-p}
乘法模型(季节幅度随水平变化):
水平:L_t = α * (Y_t / S_{t-p}) + (1-α) * (L_{t-1} + T_{t-1})
趋势:T_t = β * (L_t - L_{t-1}) + (1-β) * T_{t-1}
季节:S_t = γ * (Y_t / L_t) + (1-γ) * S_{t-p}
预测:F_{t+m} = (L_t + m*T_t) * S_{t+m-p}
其中:
- p:季节周期长度(如 12 个月、7 天)
- γ:季节平滑系数(0 ≤ γ ≤ 1)
- S_t:t 时刻的季节因子
模型选择原则:
- 加法模型: 季节波动幅度不随序列水平变化(如月销量波动 ±500 件,无论基线是 1000 还是 5000)。
- 乘法模型: 季节波动幅度与水平成比例(如旺季销量是淡季的 1.5 倍,基线越高波动越大)。
3.4 Python 实现:Statsmodels 与手动调参
Python 的 statsmodels 库提供了完整的指数平滑实现。以下代码演示如何对带有趋势和季节性的供应链需求数据进行 Holt-Winters 建模与调参:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing
# 1. 生成模拟数据:月销量,含趋势和12个月季节周期
np.random.seed(42)
months = 48 # 4年数据
t = np.arange(months)
trend = 0.5 * t
seasonal = 10 * np.sin(2 * np.pi * t / 12)
noise = np.random.normal(0, 2, months)
sales = 100 + trend + seasonal + noise
df = pd.DataFrame({'month': pd.date_range('2020-01-01', periods=months, freq='M'),
'sales': sales})
# 2. 划分训练集和测试集
train = df.iloc[:36] # 前3年训练
test = df.iloc[36:] # 后1年测试
# 3. 构建 Holt-Winters 加法模型
model = ExponentialSmoothing(
train['sales'],
trend='add', # 加法趋势
seasonal='add', # 加法季节
seasonal_periods=12 # 12个月周期
)
fit = model.fit(
smoothing_level=0.3, # α
smoothing_trend=0.1, # β
smoothing_seasonal=0.2, # γ
optimized=False # 手动指定参数
)
# 4. 预测未来12个月
forecast = fit.forecast(12)
# 5. 评估预测效果
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(test['sales'], forecast)
rmse = np.sqrt(mean_squared_error(test['sales'], forecast))
print(f"MAE: {mae:.2f}, RMSE: {rmse:.2f}")
# 6. 自动优化参数(推荐)
fit_auto = model.fit(optimized=True) # 自动搜索最优 α, β, γ
forecast_auto = fit_auto.forecast(12)
mae_auto = mean_absolute_error(test['sales'], forecast_auto)
print(f"自动调参 MAE: {mae_auto:.2f}")
3.5 调参策略与网格搜索
手动调参效率低,实际项目中通常使用网格搜索(Grid Search)或贝叶斯优化自动寻找最优参数组合。以下是一个简单的网格搜索实现:
from itertools import product
# 定义参数搜索范围
alphas = np.arange(0.1, 0.9, 0.1)
betas = np.arange(0.05, 0.5, 0.05)
gammas = np.arange(0.05, 0.5, 0.05)
best_mae = float('inf')
best_params = {}
for alpha, beta, gamma in product(alphas, betas, gammas):
try:
fit = model.fit(
smoothing_level=alpha,
smoothing_trend=beta,
smoothing_seasonal=gamma,
optimized=False
)
forecast = fit.forecast(12)
mae = mean_absolute_error(test['sales'], forecast)
if mae < best_mae:
best_mae = mae
best_params = {'alpha': alpha, 'beta': beta, 'gamma': gamma}
except:
continue
print(f"最优参数: {best_params}, MAE: {best_mae:.2f}")
调参经验规则:
- α(水平)通常在 0.1~0.5 之间,数据越平稳取值越小。
- β(趋势)通常 ≤ 0.3,趋势突变时才考虑较大值。
- γ(季节)通常在 0.05~0.3,季节模式越稳定取值越小。
- 当数据量较大(≥ 5 个完整季节周期)时,优先使用
optimized=True自动优化。
3.6 模型诊断与选择
选择哪种指数平滑模型,可通过以下步骤判断:
| 数据特征 | 推荐模型 | 关键参数 |
|---|---|---|
| 无趋势、无季节 | 一次指数平滑(SES) | α |
| 有趋势、无季节 | 二次指数平滑(Holt) | α, β |
| 有趋势、有季节(幅度恒定) | Holt-Winters 加法 | α, β, γ |
| 有趋势、有季节(幅度随水平变化) | Holt-Winters 乘法 | α, β, γ |
3.7 供应链应用注意事项
实战避坑指南:
- 异常值处理: 指数平滑对异常值敏感,建模前需先进行异常检测与修正(如使用中位数替代法)。
- 季节周期确认: 供应链数据常见周期为 7 天(周)、12 个月(年)、4 周(月结周期),需根据业务实际设定
seasonal_periods。 - 预测步长限制: 指数平滑适合短期预测(1~3 个季节周期),长期预测误差会快速累积。
- 参数稳定性: 建议每季度重新训练模型,检查参数是否发生显著漂移。