资讯动态

智慧供应链赛题实战:需求预测与分仓规划完整链路

发布时间:2026/10/20 12:58:56 来源:尧图企业网站定制
简介这是一份天池大数据竞赛“菜鸟需求预测与分仓规划”赛题的实操代码包面向参加数据挖掘竞赛或关注供应链预测的开发者解决从数据清洗、特征工程到分仓建模与融合预测的完整链路。压缩包共68个文件包含58个Python脚本、6个SQL查询、2个CSV结果、1个R脚本和1个说明文档覆盖数据预处理、特征构建、模型训练、结果评估等环节其中XGBoost、GBDT、随机森林、SVR线性核与RBF核均有对应实现并按单仓独立建模思路组织目录。资源仅133KB代码轻量、结构清晰便于直接运行和二次改造。已有2339人学习下载适合正在备赛或想复现此类方案的分析师参考。通过该资源可掌握回归模型融合的常用写法、分仓规划的工程化落地方法以及天池赛题从SQL取数到提交结果的完整流程是一份实用且浓缩的赛题开源方案。1. 先拆清赛题需求预测与分仓规划到底在考什么这类智慧供应链赛题我拆过不止一次表面看是两段式任务——先预测未来某段时间每个站点的包裹需求量再根据预测结果做分仓规划实际上绝大多数参赛者摔跤都摔在同一个地方把时间序列问题当成普通回归问题来做随机切分训练集、不做滞后特征、融合只看分数不看相关性。赛题给的原始数据通常是一张订单流水主表加若干维度表字段不外乎站点ID、品类、日期、需求量、仓库产能上限。你需要输出的东西也清楚一份逐日逐站点的需求预测量以及一份满足各仓库产能约束的分配方案。适合谁想正经打一场数据竞赛、或者在做供应链计划类项目的人。第一眼看上去模型选型决定成败跑到后面你会发现特征对齐和验证集设计比模型重要一个量级——这篇就把整条链路的做法拆开讲。2. 数据准备从主表到训练样本的关键动作2.1 多表合并先定粒度再定主键拿到原始数据后第一件事不是跑模型而是把所有表读进来确认每一张表的粒度。这类赛题通常会有三到四张表订单明细表一行一个订单或一行一个站点某日汇总、站点属性表、仓库产能表有时还带一个假日表。我先用一个示例把最常见的合并逻辑写出来import pandas as pd # 订单明细表站点ID、日期、订单量 orders pd.read_csv(orders.csv, parse_dates[date]) # 站点维度表站点ID、所属区域、绑定仓库ID stations pd.read_csv(stations.csv) # 仓库产能表仓库ID、日期、日产能上限 warehouse pd.read_csv(warehouse.csv) # 先按站点日期聚合得到每日需求量 daily orders.groupby([station_id, date], as_indexFalse)[order_qty].sum() # 关联站点维度拿到区域和仓库映射 df daily.merge(stations, onstation_id, howleft) # 关联仓库产能注意这里两个表都有日期维度 df df.merge(warehouse, on[warehouse_id, date], howleft) print(df.head())这段代码里有一个关键顺序先聚合再合并。如果先把订单表和站点表做大笛卡尔积再聚合数据量会膨胀到没法处理。合并时要注意 how 参数订单明细表是主表必须用 left不能用 inner——如果某天某站点没有订单inner 会直接把这一行丢掉预测时就少了对零需求样本的学习。关联仓库产能表时我一般会检查一下df[capacity].isnull().mean()因为数据里常出现站点绑定了仓库但产能表缺行的情况。缺失比例超过 5% 时不要直接 fillna(0)先看是不是站点和仓库映射关系对不上对不上的站点如果硬填 0模型会学到“这个站点没有产能”的错误信号。常见做法是先填一个中位产能再单独加一列 is_capacity_missing 标记把缺失信息本身变成特征。2.2 训练与验证切分按时间窗口切别按行随机切时序预测任务的验证集切分是翻车重灾区。我见过有人直接用train_test_split(random_state42)把数据打散线上分数比本地验证高一大截原因就是验证集里的“未来”混进了训练集。正确的做法是锁死时间边界例如用最后 N 天做验证# 按日期排序后切分 df df.sort_values([station_id, date]).reset_index(dropTrue) cutoff_date df[date].max() - pd.Timedelta(days14) train df[df[date] cutoff_date].copy() valid df[df[date] cutoff_date].copy() # 检查两个集合的日期是否真的互斥 print(train[date].max(), valid[date].min())这里不要用随机种子切分。很多竞赛赛题要求预测未来 14 天所以验证集也应该是最后连续的 14 天和测试集的时间长度保持一致。一个容易被忽略的点验证集的起始日不要选在周一或月初最好覆盖一个完整自然周否则节假日特征在验证集上的表现会失真。如果原始数据包含多个站点切分后还要检查每个站点在验证集里是否都有记录。有些站点在最后 14 天可能完全没有订单这类站点在训练集里也会缺少近期样本需要单独标记出来否则模型对它们的预测会退化成全局均值。3. 特征工程把时序信号变成监督特征3.1 滞后特征让前 7 天、前 14 天开口说话时序预测最直接的信号就是过去的值。对站点 i 在日期 t 的需求量滞后特征就是y_{t-1}、y_{t-7}、y_{t-14}。为什么要选 7 和 14因为物流需求有强周期周一是峰值、周日是谷值7 天前的同一天最像今天。写出来不复杂for lag in [1, 7, 14]: df[flag_{lag}] df.groupby(station_id)[order_qty].shift(lag)shift 是按 groupby 分组的这一点不能省——如果不分组直接 shift会把上一个站点的值串到当前站点模型直接学歪。滞后列生成后要检查空值比例lag_14在训练集前 14 天一定是 NaN常见做法是用该站点历史均值填充同时加一列is_lag_14_missing告诉模型“这里缺值”。滞后特征构造好之后花点时间看一眼相关性用df[[order_qty, lag_1, lag_7, lag_14]].corr()确认 lag_1 和相关性强而 lag_7 次之。如果 lag_1 相关性弱于 lag_7说明数据本身波动大、不稳定这种情况可以考虑对原始序列做 log1p 平滑后再做滞后。3.2 滚动统计均值、标准差与窗口选择滞后特征只给了离散几个时间点滚动统计则能刻画近期趋势。最常见的组合是过去 7 天均值、过去 14 天均值和过去 7 天标准差。标准差这个特征很容易被忽略但它对分仓规划特别有用——站点需求波动大意味着仓库产能预留不能卡得太死。df[rolling_mean_7] ( df.groupby(station_id)[order_qty] .transform(lambda x: x.shift(1).rolling(7, min_periods1).mean()) ) df[rolling_std_7] ( df.groupby(station_id)[order_qty] .transform(lambda x: x.shift(1).rolling(7, min_periods1).std()) )注意这里用了shift(1)再滚动核心目的是防止信息泄漏计算 t 日的滚动均值只能用 t 日之前的数据如果把 t 日本身算进去模型在训练时“偷看”了当天的答案验证时就没有这个条件分数会虚高。min_periods1用于在序列开头尽量少留空洞但对前 7 天滚动均值实际是近 2~6 天的均值模型会自动学着调整权重不需要额外处理。窗口选 7 还是 14看业务节奏。电商大促场景周期拉长用 14 天更稳日常备货 7 天足够。也可以同时做两个窗口让模型自己选代价是特征数量和训练时间涨一截。3.3 日期特征与外部变量周末、月初和节假日日期特征是成本最低却最有效的信号之一。星期几周一~周日、是否为月初/月末、是否为节假日这些直接决定了需求曲线的形状。代码很直接df[weekday] df[date].dt.weekday df[is_month_start] df[date].dt.is_month_start.astype(int) df[is_weekend] (df[weekday] 5).astype(int) # 节假日表如果有合并进来 if holiday_df is not None: df df.merge(holiday_df, ondate, howleft) df[is_holiday] df[is_holiday].fillna(0).astype(int)星期几建议用整数编码而不是 one-hot树模型对有序整数特征的处理更高效而且周一和周日对预测的影响是近似的整数编码可以保留这种近邻关系。节假日要特别注意滞后特征的空洞——大促前一天的需求往往异常高前 7 天均值被拉高后节后预测容易整体偏高。解决办法是加一个“距离最近节假日天数”的特征或者在大促窗口内把滚动窗口强制拉长到 14 天。站点属性表的类别特征比如站点所属区域、绑定仓库类型用标签编码即可。如果类别数量多且每个类别的样本量足够可以试一下目标编码用历史均值代替类别但必须要在训练集内部做交叉验证编码否则同样会泄漏。4. 建模与融合从单模型基线到加权平均4.1 基线模型LightGBM 的标准姿势这类表格型时序预测任务第一梯队仍然是 GBDT 系模型LightGBM 是应用最顺手的。先搭一个标准基线不要一上来就调参import lightgbm as lgb features [ lag_1, lag_7, lag_14, rolling_mean_7, rolling_std_7, weekday, is_month_start, is_weekend, is_holiday, 区域编码, 仓库编码, ] X_train train[features] y_train train[order_qty] X_valid valid[features] y_valid valid[order_qty] model lgb.LGBMRegressor( objectiveregression, metricrmse, learning_rate0.05, max_depth6, num_leaves31, n_estimators2000, verbose-1, ) model.fit( X_train, y_train, eval_set[(X_valid, y_valid)], callbacks[lgb.early_stopping(100), lgb.log_evaluation(200)], )objective用 regression 而不是 regression_l1因为赛题评分通常落在绝对误差和平方误差的加权组合上RMSE 更贴近max_depth6, num_leaves31是一个保守起步组合能避免一开始就过拟合。learning_rate0.05配合n_estimators2000和早停比直接调大学习率更稳。一个实用的习惯模型训练完用model.feature_importances_看一遍特征重要性排序。如果lag_1排不进前五说明这个站点的需求没有短期延续性反而是周几和节假日特征主导——这时候要回头检查是不是聚合粒度出了问题。4.2 多模型加权融合先看相关性再谈融合单模型调参到一定程度会有天花板竞赛场景下提分最稳定的手段是融合。融合的前提是模型之间有差异一个是树模型一个可以是线性模型或者 MLP。不要融合两个只是参数不同的 LightGBM——它们相关性太高融合收益趋近于零。# 引入第二个模型岭回归对线性趋势敏感 from sklearn.linear_model import Ridge ridge Ridge(alpha1.0) ridge.fit(X_train, y_train) pred_ridge ridge.predict(X_valid) pred_lgb model.predict(X_valid) # 在验证集上搜索最优权重 from scipy.optimize import minimize_scalar def loss(w): return ((w * pred_lgb (1 - w) * pred_ridge - y_valid) ** 2).mean() res minimize_scalar(loss, bounds(0, 1), methodbounded) best_w res.x print(f最优权重: {best_w:.2f})融合权重不是拍脑袋定的而是在验证集上最小化误差搜索出来。这里我把权重限制在 0 到 1 之间避免出现负权重这种不可解释的组合。如果最优权重接近 0.5说明两个模型各有优势如果最优权重是 0.95 以上那第二个模型基本没用还不如不融合。跑融合前先算两个模型预测的相关系数np.corrcoef(pred_lgb, pred_ridge)[0, 1]。相关系数高于 0.95大概率融合后分数纹丝不动低于 0.85 时融合通常有效。我在一个模拟项目里见过LightGBM 和岭回归的相关系数只有 0.72融合后验证集误差直接降了 8%这就是差异化的价值。4.3 预测结果的数值校准树模型输出的是训练集目标分布的拟合值当目标变量右偏严重少数大促日拉了均值预测值会系统性偏低。常见处理是训练时对 y 取 log1p预测后再 expm1 还原y_train_log np.log1p(y_train) model.fit(X_train, y_train_log, eval_set[(X_valid, np.log1p(y_valid))]) pred np.expm1(model.predict(X_valid))这个操作对波动大的站点特别有效。用 log1p 之后模型优化的目标从绝对误差变成了相对误差大促日的权重被拉低平时日的预测精度提高。注意还原之后不要忘记对预测值做下限截断比如np.maximum(pred, 0)避免出现负需求量这种业务上不可能的值。5. 常见问题与排查从数据泄漏到融合失效的翻车现场5.1 验证集分数虚高线上瞬间打回原形现象本地验证集 RMSE 很低一提交测试集分数差到离谱。原因训练集和验证集不是按时间切分随机打散后模型在训练时已经看到过“未来”的样本或者滚动统计特征没有 shift当天值参与了当天预测。解决强制按时间窗口切分特征构造统一走同一个函数并且每次构造完随机抽 50 行人工检查有没有未来信息混入。从那以后我养成一个强迫症所有特征工程代码只允许输入截止到 t 日的数据任何涉及 t1 的操作一律视为 bug。5.2 滞后特征全是 NaN模型直接学了个寂寞现象lag_7 和 lag_14 的覆盖率极低特征重要性第一名的is_lag_14_missing是一个假信号。原因groupby 列写错或者原始表的站点 ID 在前后日期不一致导致 shift 跨站点串行。解决先用df.groupby(station_id)[date].count()检查每个站点的日期连续性如果某站点中间断档lag_14 实际上是 14 天前另一个站点的值。对断档日期做插值或者直接丢弃该站点的滞后特征比填 0 更靠谱。检查 groupby 是否正确最简单的方式是看NaN的分布——正常情况下只有每个站点前 14 行为 NaN如果随机出现在中间行一定是站点 ID 对齐出了问题。5.3 加权融合后误差反而变大现象单模型甲 RMSE10单模型乙 RMSE11融合后 RMSE12。原因两个模型错误模式高度一致且模型乙比模型甲在某个子集上更差加权平均把这个子集的误差放大了。解决看两个模型预测值的散点图x 轴是真实值y 轴是预测值如果两模型的偏移方向相同融合无效如果偏移方向相反融合才有空间。更可靠的融合策略是按站点分组做权重高波动站点用树模型权重 0.7平稳站点用岭回归权重 0.5。融合不是目的找到互补的模型才是。5.4 分仓结果频繁触发产能超限现象预测误差很小但分仓方案在仿真里每天都有仓库爆仓。原因模型优化的是整体均方误差但考核的瓶颈在最差的仓库上均方误差低不代表极端日期的峰值预测准确。解决在预测阶段对每个仓库的日总量单独做一层校准把预测值按仓库维度除以一个系数让每天各仓库的预测总量不超过其产能总和。具体做法是把仓库日产能也做成特征拟合时加一个权重列误差超过产能阈值的样本权重加倍。这个操作用了之后分仓方案的超限次数能降一半。6. 分仓规划落地把预测值变成可执行的备货方案预测做完最后一步是把每天每个站点的需求分配到具体仓库。我把这个环节实现成一个贪心分配函数每一轮优先处理预测需求量最大的站点把它挂到当天剩余产能最多的仓库def assign_warehouse(pred_df, capacity_df, station_wh_map): plan {} for date, group in pred_df.groupby(date): # 按预测需求降序排先处理高需求站点 group group.sort_values(pred_qty, ascendingFalse) remain_cap capacity_df.set_index(warehouse_id)[cap].to_dict() date_plan [] for row in group.itertuples(): candidates station_wh_map[row.station_id] # 挑一个剩余产能最大的仓库 best_wh max(candidates, keylambda w: remain_cap.get(w, 0)) alloc min(row.pred_qty, remain_cap.get(best_wh, 0)) date_plan.append((row.station_id, best_wh, alloc)) remain_cap[best_wh] - alloc plan[date] date_plan return plan这个函数的核心是“按需排队找富余产能仓库”。站点和仓库是多对一映射时逻辑简单一对多时要考虑优先级我的习惯是优先选距离最近且当天剩余产能不低于需求 80% 的仓库这样避免出现一个小站点跨三个仓库拆单的场景。验证分仓方案是否合理我只看两个数字产能利用率和未满足率。用当天的真实需求验证集里拿去对照分配结果计算每个仓库的利用率分布以及整体未满足率。利用率低于 50% 的仓库说明分配保守未满足率超过 5% 说明预测低估或者产能模型太死。这两个数字就是分仓方案的体检报告。这条链路走下来从数据合并、时序特征构造、建模融合到分仓分配每一环都有它的坑。最值钱的经验是我每次切验证集前都强制走一遍“时间轴对齐检查”——把所有特征构造函数跑在测试集时间范围上确认没有用到未来任何信息然后再看分数。赛题资源里给的代码骨架和数据处理脚本基本上修完这几个坑就能直接跑出可提交的结果。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑