一个销量预测模型的进化史:从 baseline 到上线,我们做了 LightGBM 调参、特征选择、Stacking 融合,误差降低40%。
一、起点:75% 准确率的 baseline
上一篇文章我们构建了23维特征,用 LightGBM 默认参数训练,在验证集上的平均绝对百分比误差(MAPE)为 25%,对应准确率 75%。对于库存建议来说还不够可靠,尤其在波动较大的商品上偏差明显。
本文记录了我们如何通过三轮迭代,将 MAPE 降至 11%(准确率 89%),并部署到生产 API 中。
注:准确率 = 1 - MAPE(简化定义),实际业务中更关注预测误差对库存成本的影响,但为便于理解,本文用此指标。
二、第一轮:LightGBM 超参数调优
LightGBM 参数众多,但真正影响大的只有几个。我们使用 Optuna 自动化调参,搜索空间如下:
参数 搜索范围 作用
num_leaves 31~255 树的复杂度
learning_rate 0.01~0.1 学习率
feature_fraction 0.6~1.0 每次迭代随机选择特征比例
bagging_fraction 0.6~1.0 数据采样比例
lambda_l1 / lambda_l2 1e-5~10 正则化强度
min_child_samples 5~100 叶子节点最少样本数
核心代码:
python
import optuna
import lightgbm as lgb
def objective(trial, X_train, y_train, X_valid, y_valid):
params = {
'objective': 'regression',
'metric': 'mape',
'verbosity': -1,
'num_leaves': trial.suggest_int('num_leaves', 31, 255),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.1, log=True),
'feature_fraction': trial.suggest_float('feature_fraction', 0.6, 1.0),
'bagging_fraction': trial.suggest_float('bagging_fraction', 0.6, 1.0),
'lambda_l1': trial.suggest_float('lambda_l1', 1e-5, 10.0, log=True),
'lambda_l2': trial.suggest_float('lambda_l2', 1e-5, 10.0, log=True),
'min_child_samples': trial.suggest_int('min_child_samples', 5, 100),
}
model = lgb.train(params, lgb.Dataset(X_train, y_train), valid_sets=[lgb.Dataset(X_valid, y_valid)])
pred = model.predict(X_valid)
mape = np.mean(np.abs((y_valid - pred) / y_valid)) * 100
return mape
study = optuna.create_study(direction='minimize')
study.optimize(lambda trial: objective(trial, X_train, y_train, X_valid, y_valid), n_trials=100)
best_params = study.best_params
结果:MAPE 从 25% 降至 19%(准确率提升到 81%)。调参贡献了 6 个百分点的提升。
三、第二轮:特征选择与工程优化
模型对特征重要性的分析给了我们新的思路。
3.1 剔除冗余特征
LightGBM 内置的 feature_importance 显示,部分滞后特征(如 lag_5、lag_6)重要性极低。我们采用 递归特征消除(RFE) 逐步删除低重要性特征,最终保留 18 个核心特征。
python
# 获取特征重要性
importance = model.feature_importance(importance_type='gain')
feat_imp = pd.DataFrame({'feature': feature_cols, 'importance': importance}).sort_values('importance', ascending=False)
# 设定阈值,保留累积重要性 99% 的特征
cumsum = feat_imp['importance'].cumsum() / feat_imp['importance'].sum()
selected = feat_imp[cumsum <= 0.99]['feature'].tolist()
3.2 添加交互特征
我们发现"促销 × 节假日"的交互效应非常显著。例如:周末促销的效果是平时促销的 1.8 倍。于是构造交互特征:
python
df['promo_holiday'] = df['is_promotion'] * (df['holiday_type'] > 0).astype(int)
df['promo_weekend'] = df['is_promotion'] * df['is_weekend']
另外,价格变化率与历史均值的比值(价格冲击系数)也很有用:
python
df['price_shock'] = df['price_change_rate'] / (df['mean_7d'] + 1e-5)
3.3 针对新品添加冷启动特征
对于历史数据不足30天的商品,我们加入一个布尔特征 is_new,并将滞后特征用同类商品的中位数填充。
结果:MAPE 从 19% 降至 15%(准确率 85%)。
四、第三轮:模型融合(Stacking)
单一模型总有盲区,我们尝试了 LightGBM + XGBoost + 简单时序基线 的 Stacking 融合。
4.1 基模型
· LightGBM(调优后参数)
· XGBoost(默认参数 + 早停)
· 季节性朴素预测:去年同期同周销量作为基线(仅对成熟商品有效)
4.2 元模型
使用 线性回归 作为元学习器,训练集为基模型在验证集上的预测值。
python
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
# 生成基模型的OOF预测
oof_lgb = np.zeros(len(y_train))
oof_xgb = np.zeros(len(y_train))
oof_seasonal = np.zeros(len(y_train))
kf = KFold(n_splits=5, shuffle=True, random_state=42)
for train_idx, val_idx in kf.split(X_train):
X_tr, X_val = X_train[train_idx], X_train[val_idx]
y_tr, y_val = y_train[train_idx], y_train[val_idx]
lgb_model = lgb.LGBMRegressor(**best_params).fit(X_tr, y_tr)
oof_lgb[val_idx] = lgb_model.predict(X_val)
xgb_model = xgb.XGBRegressor().fit(X_tr, y_tr)
oof_xgb[val_idx] = xgb_model.predict(X_val)
# 季节性基线:假设验证集的预测值是历史同期均值(略)
oof_seasonal[val_idx] = seasonal_predict(X_val)
# 训练元模型
meta_features = np.column_stack([oof_lgb, oof_xgb, oof_seasonal])
meta_model = LinearRegression().fit(meta_features, y_train)
# 最终预测
final_pred = meta_model.predict(np.column_stack([pred_lgb, pred_xgb, pred_seasonal]))
结果:MAPE 降至 11%(准确率 89%)。融合比最佳单模型(LightGBM)又提升了 4 个百分点。
五、验证与部署
5.1 时间序列交叉验证
普通 KFold 会污染未来信息,我们改用 TimeSeriesSplit:
python
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, val_idx in tscv.split(X):
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
# 训练评估
5.2 业务指标验证
除了 MAPE,我们更关注 库存周转率 和 缺货率 的改善。通过模拟库存策略,新模型相比旧模型:
· 库存金额下降 18%
· 缺货事件减少 32%
六、这些模型已经集成到 API 中
你不必重复上述所有实验。我的销量预测 API 默认使用 融合模型(LightGBM + XGBoost + 季节性基线),并自动处理特征工程、缺失值、新品冷启动等复杂问题。
你只需要传入基础销售数据,即可获得高精度预测。
免费试用 500 次:官网链接
七、迭代永无止境
当前模型仍有改进空间:
· 引入深度学习模型(Transformer)捕捉长期依赖
· 加入天气数据等外部特征
· 实现在线学习,实时更新模型
如果你对这些方向感兴趣,欢迎持续关注。下一篇文章将分享《轻量级模型部署:用 ONNX 将预测速度提升 3 倍》。
所有代码和数据示例可在 GitHub 仓库获取(见官网)。有任何问题或合作意向,欢迎评论区交流。