资讯动态

电商需求预测与分仓规划:从特征工程到模型融合的完整方案

发布时间:2026/10/14 14:06:47 来源:尧图企业网站定制
简介这份资源是阿里天池大数据竞赛「菜鸟-需求预测与分仓规划」赛题的完整参赛资料包面向具备一定机器学习与数据分析基础、希望深入供应链预测与仓储优化实战的开发者与竞赛选手。内容围绕需求预测与分仓规划两大核心任务展开涵盖数据清洗、缺失值与异常值处理、特征工程、样本生成等预处理环节并给出XGBoost、GBDT、随机森林、SVR线性核与高斯核等多种回归模型的实现以及分仓独立建模与模型融合的完整思路。资源包共68个文件以58个Python脚本为主辅以6个SQL特征处理脚本、2个CSV数据文件、1个R语言ARIMA实现和1份说明文档压缩包约133KB结构清晰、便于按模块查阅。目前已有2332人学习下载读者可从中获取赛题方案、特征构造逻辑、多模型对比与融合代码及成本计算脚本适合作为供应链预测类项目的参考范例。1. 需求预测与分仓规划一份能跑通全流程的竞赛方案拆解电商大促前两周运营群里最常出现的对话是「这个仓备多少货」和「那个区域要不要临时租仓」。需求预测偏了要么爆仓发不出要么压货压到现金流断裂分仓规划错了跨区调拨的物流成本能吃掉全部毛利。这份天池赛题方案围绕「菜鸟-需求预测与分仓规划」展开核心是把历史订单拆成「预测未来需求量」和「决定每个仓发多少」两个耦合问题用回归模型加模型融合给出可复现的解法。它适合已经会调 sklearn、但没完整走过「特征工程→多模型融合→分仓决策」链路的从业者也适合想拿一套现成 pipeline 改自己业务数据的同学。下面按我实际拆包复现的顺序讲参数和坑都落在具体位置。2. 需求预测建模从原始订单到可训练特征矩阵2.1 数据字段与预测粒度怎么定拿到订单明细后第一件事不是急着 fit而是确认预测粒度。赛题给的是「商品-仓库-日期」维度的出货量但真实业务里往往还要叠加「区域」和「促销标签」。我一般先把字段分成四类标识列商品 ID、仓库 ID、时间列日期、星期、是否节假日、数值列历史出货量、库存量、类别列商品类目、仓库所在区域。预测目标通常是未来 1 到 7 天的日出货量粒度太细会导致稀疏太粗又失去分仓意义。常见做法是把日期做滑窗聚合生成 lag 特征。比如用过去 7 天、14 天、28 天的均值、最大值、标准差作为输入。这里有个容易翻车的点lag 窗口不能跨过预测起点否则就是标签泄漏。我见过有人把未来三天的数据也滚进特征里线下 AUC 高得离谱线上直接崩。正确做法是按时间切分训练集和验证集比如前 80% 天做训练后 20% 做验证滑窗只在训练段内滚动。import pandas as pd import numpy as np def build_lag_features(df, target_col, lags[7, 14, 28]): df: 含 date, item_id, warehouse_id, target_col 的明细 返回带 lag 统计量的特征表 df df.sort_values([item_id, warehouse_id, date]) grp df.groupby([item_id, warehouse_id])[target_col] for lag in lags: # 过去 lag 天的均值shift(1) 保证不包含当天 df[flag_{lag}_mean] grp.transform( lambda x: x.shift(1).rolling(lag, min_periods1).mean() ) df[flag_{lag}_std] grp.transform( lambda x: x.shift(1).rolling(lag, min_periods1).std() ) return df这段代码的关键在shift(1)它把当前天的值排除在统计窗口外。min_periods1是为了让序列开头不产生 NaN否则后面模型训练要额外做填充。lags列表按业务周期选快消品用 7/14/28耐用品可以拉到 30/60/90。如果数据量很大transform加lambda会慢可以换成groupby().rolling()再 merge但逻辑不变。2.2 回归模型选型与训练脚本特征矩阵建好后基模型我通常选三个LightGBM 处理高维稀疏特征XGBoost 做稳定基线Ridge 回归当线性兜底。不要一上来就上深度学习表格数据上 GBDT 的性价比最高。LightGBM 的参数重点调num_leaves控制树复杂度一般 31 到 127、learning_rate0.05 到 0.1、feature_fraction0.7 到 0.9。XGBoost 对应调max_depth4 到 8和subsample0.7 到 0.9。import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error def train_lgb(X_train, y_train, X_val, y_val): params { objective: regression_l1, # MAE 对异常值更稳 metric: mae, num_leaves: 63, learning_rate: 0.05, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1 } dtrain lgb.Dataset(X_train, y_train) dval lgb.Dataset(X_val, y_val, referencedtrain) model lgb.train( params, dtrain, num_boost_round2000, valid_sets[dval], callbacks[lgb.early_stopping(100), lgb.log_evaluation(200)] ) pred model.predict(X_val) print(MAE:, mean_absolute_error(y_val, pred)) return modelobjective选regression_l1而不是默认的 L2是因为出货量常有促销尖峰L2 会被大值带偏。early_stopping(100)表示验证集 100 轮不提升就停防止过拟合。TimeSeriesSplit在时序场景比随机 KFold 靠谱但赛题数据如果按天连续直接切最后 20% 更省事。训练完记得把feature_importance导出来看如果lag_7_mean排第一但lag_28_std几乎为零说明长周期波动没被捕捉要回去检查数据是否做了差分。3. 模型融合把三个基模型的预测拧成一股绳3.1 加权融合与 Stacking 的取舍单模型 MAE 通常在 8% 到 12% 之间浮动融合后能压到 7% 左右。最简单的是加权平均权重按验证集 MAE 倒数分配。但加权平均有个前提基模型误差要相对独立。如果 LightGBM 和 XGBoost 用了同一套特征和相似参数它们的预测相关性可能到 0.95 以上融合收益极小。我一般先算预测值的 Pearson 相关系数超过 0.9 就换模型或换特征子集。Stacking 更稳但更重。做法是用基模型在验证集上的预测作为新特征再训一个 Ridge 或小 LightGBM 做元学习器。注意元学习器的训练数据必须来自交叉验证的 out-of-fold 预测否则还是标签泄漏。实操里我用 5 折时序交叉验证生成 OOF 预测每折内训基模型、折外预测最后拼成完整的新特征矩阵。from sklearn.linear_model import Ridge from sklearn.model_selection import TimeSeriesSplit def stacking_predict(X, y, base_models, meta_model, n_splits5): tscv TimeSeriesSplit(n_splitsn_splits) oof np.zeros((len(X), len(base_models))) for fold, (tr_idx, val_idx) in enumerate(tscv.split(X)): for i, model in enumerate(base_models): model.fit(X.iloc[tr_idx], y.iloc[tr_idx]) oof[val_idx, i] model.predict(X.iloc[val_idx]) meta_model.fit(oof, y) return meta_model, oofTimeSeriesSplit保证每折验证集都在训练集之后符合时序逻辑。oof矩阵的列数等于基模型数量行数等于样本数。元学习器选 Ridge 是因为它不容易过拟合alpha设 1.0 到 10.0 之间。如果基模型预测尺度差异大先做标准化再喂给元学习器。融合后的预测还要做后处理把负值截断到 0因为出货量不可能为负对超过历史最大值的预测做封顶防止促销期外推失控。3.2 分仓规划从预测值到发货决策需求预测输出的是「每个商品在每个仓未来要出多少」分仓规划要回答的是「每个仓实际备多少、从哪个仓发」。赛题里分仓目标通常是最小化「调拨成本 缺货惩罚」。我一般把它建模成运输问题供给端是各仓库存需求端是各区域预测需求量边权是调拨单价。用线性规划或最小费用流求解小规模直接 scipy 的linprog大规模上 OR-Tools。from scipy.optimize import linprog import numpy as np def allocate(warehouse_supply, region_demand, cost_matrix): warehouse_supply: 各仓可用库存 list region_demand: 各区域预测需求 list cost_matrix: 仓到区的单位调拨成本shape(n_wh, n_region) n_wh, n_region cost_matrix.shape c cost_matrix.flatten() # 等式约束每个仓发出的总量 库存 A_ub [] b_ub [] for i in range(n_wh): row np.zeros(n_wh * n_region) row[i*n_region:(i1)*n_region] 1 A_ub.append(row) b_ub.append(warehouse_supply[i]) # 等式约束每个区域收到的总量 需求 A_eq [] b_eq [] for j in range(n_region): row np.zeros(n_wh * n_region) row[j::n_region] 1 A_eq.append(row) b_eq.append(region_demand[j]) res linprog(c, A_ubA_ub, b_ubb_ub, A_eqA_eq, b_eqb_eq, bounds(0, None), methodhighs) return res.x.reshape(n_wh, n_region)cost_matrix的构造是分仓规划里最容易被低估的一步。它不只是快递单价还要叠加时效权重和仓库操作成本。我一般用「基础运费 × 时效系数 固定操作费」来算时效系数按区域远近设 1.0 到 1.5。linprog的methodhighs比默认单纯形快很多适合几千个变量的规模。如果需求大于总供给等式约束会无解这时要把需求约束改成并加缺货惩罚项或者先做一轮需求优先级排序。4. 避坑与排查五个让方案翻车的真实原因4.1 现象线下 MAE 很低线上预测全偏原因通常是特征里混入了未来信息。比如用「当天库存」预测「当天出货」而库存是出货后更新的等于偷看了答案。解决方法是把所有特征按时间戳对齐到预测起点之前用shift或asof合并并在验证集上做一次「时间穿越检测」随机打乱日期后 MAE 应该显著变差如果没变说明特征没时序信号。4.2 现象融合后效果不如单模型基模型预测相关性太高融合只是重复投票。先算相关系数矩阵超过 0.9 的模型只留一个或者给不同模型喂不同特征子集。另一个原因是元学习器过拟合把alpha调大或换成简单平均。我一般先跑加权平均只有它比单模型好才上 Stacking。4.3 现象分仓结果出现大量跨区调拨成本矩阵没设对或者仓库供给约束写反了。检查A_ub的方向linprog默认是A_ub x b_ub如果写成会得到荒谬解。另外确认cost_matrix的对角线同区调拨是不是最小的如果不是说明基础运费算错了。4.4 现象LightGBM 训练到一半报内存不足num_leaves设太大或max_bin默认 255 导致直方图爆内存。把num_leaves降到 63 以下max_bin调到 127或者用feature_fraction做列采样。数据量超过千万行时先做类别特征编码压缩把高基数 ID 用 target encoding 或 frequency encoding 替代 one-hot。4.5 现象预测值出现负数或极端大值回归模型没有输出约束。后处理加np.clip(pred, 0, historical_max * 1.5)historical_max取该商品该仓过去 90 天的最大出货量。如果负值比例超过 5%说明模型选错了换objectiveregression_l1或加对数变换。5. 进阶技巧用滚动预测和分位数回归兜住尾部风险方案跑通后我习惯再加两个动作。一是滚动预测不要只训一次模型用到底而是每天用最新数据重新训一版预测未来 7 天。这样能捕捉促销节奏变化代价是训练时间翻倍可以用增量训练或只更新最后几棵树来省算力。二是分位数回归把objective换成quantilealpha设 0.9得到 P90 预测值。分仓规划时用 P90 而不是均值来备货缺货率能降三成代价是库存周转慢一点。具体选哪个分位数看业务对缺货和压货的容忍度我一般 P75 到 P90 之间试。# 分位数回归示例预测 P90 出货量 params_q { objective: quantile, alpha: 0.9, metric: quantile, num_leaves: 63, learning_rate: 0.05, verbose: -1 } # 训练流程与普通回归一致预测值直接作为备货上限验证分位数效果不能只看 MAE要看「预测值大于真实值的比例」是否接近 0.9。如果只有 0.7说明模型低估了尾部要加更多滞后特征或换更长的训练窗口。我一般把均值预测和 P90 预测都存下来分仓时对高周转商品用 P90对长尾商品用均值避免过度备货。从那以后我每次做完需求预测都会强制走一遍「时间穿越检测 预测相关性检查 分仓成本矩阵复核」这三步少一步都不敢把结果交给运营。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑