资讯动态

基于LightGBM与报童模型的电商需求预测与库存优化实战

发布时间:2026/8/22 8:41:51 来源:尧图企业网站定制
1. 项目概述从一道赛题看供应链预测与优化的实战最近在整理过往的竞赛资料翻到了2023年MathorCup大数据竞赛的B题。这道题当时在圈内讨论度很高因为它完美地戳中了现代供应链管理的核心痛点如何在需求不确定性的迷雾中实现精准的预测与库存优化。题目给了一个电商场景下的销售数据要求参赛者不仅要预测未来一段时间内各商品的需求量还要基于预测结果设计一套库存补货策略以最小化总成本包括库存持有成本和缺货损失。这听起来像是一个经典的运筹学问题但“大数据”这个前缀意味着我们面对的不是几十、几百个SKU库存单位而是海量、高维、具有复杂时空关联的数据。这就不再是教科书上的例题而是一个需要工程思维、统计模型和业务洞察深度融合的实战项目。这道题非常适合两类朋友来深入研究一类是正在学习数据科学、机器学习想找一个有明确业务背景和完整流程从数据清洗到模型构建再到策略输出的练手项目另一类是在电商、零售、物流等行业从事数据分析或供应链相关工作的从业者可以通过这道题的系统性解答梳理和深化自己对需求预测与库存优化方法论的理解。接下来我将以这道赛题为蓝本结合我处理类似问题的经验拆解其核心思路、技术细节与实操要点希望能为你提供一个可复现、可拓展的实战参考框架。2. 赛题核心需求与业务逻辑拆解拿到赛题第一步不是急着找代码或套模型而是彻底读懂题目背后的业务逻辑。MathorCup B题通常有清晰的业务场景描述我们需要将其转化为可量化的数据问题。2.1 问题定义与目标量化题目通常会提供数月的历史销售数据包含字段如商品ID、日期、销售量、是否促销、价格等。核心任务可拆解为两部分需求预测利用历史数据预测未来N天例如未来14天或30天每个商品ID的日需求量。这里的“需求”在数据上体现为“销售量”但需注意销售量可能受缺货影响而小于真实需求这是一个需要处理的噪声。库存优化基于上述预测结果制定一套补货策略。策略需要决定何时补货、补多少货。优化目标是最小化总成本通常包括库存持有成本货物在仓库中停留产生的费用如仓储费、资金占用成本与库存水平和持有时间正相关。缺货成本因库存不足导致销售损失带来的成本可能包括直接利润损失和商誉损失。注意题目中成本参数如单位库存持有成本率、单位缺货惩罚成本可能是给定的也可能需要根据业务常识进行合理假设。这是将数学模型与业务连接的关键一步。2.2 数据特性分析与挑战电商销售数据通常具有以下特性这也是建模的挑战所在间歇性需求对于很多长尾商品销售记录稀疏存在大量零值。传统时间序列模型如ARIMA在此类数据上表现很差。多周期性明显的日周期周末 vs 工作日、周周期、月周期以及年度季节性如节假日、促销季。外部因素影响促销活动、价格变动、节假日、天气对于某些品类会显著冲击销量。品类层级结构商品属于不同的类目同类目商品的需求模式可能相似这为利用层级信息提升预测精度提供了可能。大数据量商品数SKU可能成千上万时间跨度数月数据量庞大对模型训练和计算效率提出要求。理解这些特性才能选择合适的模型和方法。例如面对间歇性需求可能需要采用Croston方法或其机器学习改进版本对于海量SKU的预测需要兼顾模型效果与计算效率可能采用全局模型如LightGBM、深度学习模型同时对所有SKU进行训练并引入商品特征。3. 技术方案选型与核心模型解析针对上述需求与挑战一个完整的解决方案通常包含数据预处理、特征工程、预测模型、库存优化模型四个核心模块。3.1 预测模型选型从传统时序到机器学习预测模型是基石。没有相对准确的预测后续优化就是空中楼阁。常用的模型可分为几类1. 统计时间序列模型SARIMA季节性ARIMA适用于具有明显、稳定季节性的单变量序列。对于电商多周期、受外部因素干扰的数据往往需要复杂的参数调整且对间歇性需求处理不佳。ETS指数平滑同样适用于经典时序有多种变体Holt-Winters等。实现简单但对于复杂模式捕捉能力有限。Croston方法专门为间歇性需求设计。它将需求拆分为“需求间隔”和“需求规模”两部分分别预测再合成。这是处理间歇性需求的基准方法。2. 机器学习模型LightGBM / XGBoost在这类竞赛中最为常用和有效。它们是梯度提升树模型优势在于能天然处理表格数据方便融入大量特征滞后特征、滚动统计特征、日期特征、商品类别特征、促销特征等。对缺失值、异常值有一定鲁棒性。训练速度快支持并行适合大数据场景。能够捕捉非线性关系和特征交互。深度学习模型如LSTM长短期记忆网络、TCN时间卷积网络、Transformer。优势能自动学习更复杂的时间依赖模式对于长序列预测可能有更好表现。劣势需要更多的数据、更精细的调参、更长的训练时间且模型可解释性较差。在有限时间和计算资源的竞赛中风险较高。3. 融合策略模型融合将不同模型的预测结果进行加权平均或 stacking常能提升鲁棒性和精度。例如用LightGBM捕捉特征影响用LSTM捕捉深层时间依赖再将结果融合。层级聚合先预测大类目的销量再通过比例分解到具体SKU有助于提升长尾商品的预测稳定性。实操心得在MathorCup这类比赛中LightGBM往往是首选。它提供了一个在效果、速度和可操作性上极佳的平衡点。优先用LightGBM构建一个强大的基线模型再考虑是否引入深度学习模型进行融合。千万不要一开始就陷入复杂模型的调参泥潭。3.2 特征工程构建模型的信息燃料特征工程的质量直接决定模型的上限。对于时间序列预测问题特征可以大致分为以下几类1. 时间特征基础特征年、月、日、星期几、一年中的第几天、是否为月初/月末。周期性编码将星期几、月份进行循环编码sin/cos让模型理解周期的循环性。节假日标志是否为法定节假日、节前第N天、节后第N天。可以手动构建或使用holidays库。2. 滞后特征与滚动统计特征滞后特征过去1天、7天、14天、30天的销量。这是最重要的特征之一。滚动统计过去N天的均值、标准差、中位数、最大值、最小值、偏度等。例如过去7天平均销量、过去30天销量标准差。扩展窗口统计至今的累计均值、累计标准差等。3. 商品相关特征商品类别类目ID进行标签编码或目标编码Target Encoding。商品价格、原价、折扣率。商品上架时间商品年龄。商品历史平均销量、销量波动性。4. 事件与外部特征是否促销0/1标志。促销强度可以用折扣力度或专门的促销类型编码。天气数据如果题目相关温度、降水量等。宏观经济指数通常不适用于短期预测。5. 目标编码类特征利用历史数据计算商品在同一星期几的历史平均销量、在同一月份的历史平均销量等。这能有效捕捉商品在特定时间点的固有模式。# 特征工程示例代码片段 (Python/Pandas) import pandas as pd import numpy as np def create_features(df): # 时间特征 df[year] df[date].dt.year df[month] df[date].dt.month df[day] df[date].dt.day df[dayofweek] df[date].dt.dayofweek df[dayofyear] df[date].dt.dayofyear # 周期性编码 df[dayofweek_sin] np.sin(2 * np.pi * df[dayofweek] / 7) df[dayofweek_cos] np.cos(2 * np.pi * df[dayofweek] / 7) df[month_sin] np.sin(2 * np.pi * df[month] / 12) df[month_cos] np.cos(2 * np.pi * df[month] / 12) # 滞后特征 for lag in [1, 7, 14, 30]: df[fsales_lag_{lag}] df.groupby(item_id)[sales].shift(lag) # 滚动窗口特征 df[rolling_mean_7] df.groupby(item_id)[sales].transform(lambda x: x.rolling(7, min_periods1).mean()) df[rolling_std_7] df.groupby(item_id)[sales].transform(lambda x: x.rolling(7, min_periods1).std()) # 目标编码商品在星期几的平均销量 # 注意为了避免数据泄露需要使用历史数据的expanding mean df[item_dow_avg] df.groupby([item_id, dayofweek])[sales].transform(lambda x: x.expanding().mean().shift(1)) return df3.3 库存优化模型从预测到决策得到预测后就进入了运筹优化阶段。核心是建立库存模型。常用的模型是报童模型及其扩展。1. 经典报童模型适用于单周期、需求为随机变量的情况。其核心思想是找到一个最优库存水平Q*使得最后一单位产品的期望边际收益等于期望边际损失。公式P(需求 ≤ Q*) Cu / (Cu Co)Cu单位缺货成本Underage Cost。Co单位过剩库存持有成本Overage Cost。应用对于预测结果是概率分布而非单一点估计的情况我们可以得到未来每天需求量的分布例如通过分位数回归或概率预测模型然后对每一天应用报童模型计算当天的建议库存水平。2. (s, S) 策略或 (R, Q) 策略这是多周期库存管理中更实用的策略。(s, S) 策略持续检查库存。当库存水平降至s再订货点时发出订单将库存补充至S最大库存水平。(R, Q) 策略定期每隔R期检查库存并订购固定数量Q。参数求解s和S或R和Q的确定需要基于需求分布、提前期从下单到入库的时间、以及成本参数通过优化算法如动态规划、启发式算法或仿真模拟来求解。3. 仿真优化在复杂场景下如多级库存、容量限制解析解很难求得。这时可以采用仿真优化设定一组库存策略参数如不同的s,S组合。基于历史数据或预测的需求分布模拟长时间如一年的库存动态每日销售、补货、库存水平变化。计算每个策略下的模拟总成本。选择总成本最低的策略参数。注意事项库存优化严重依赖成本参数的准确性。如果题目未给出需要基于业务常识进行合理假设并在报告中明确说明假设及其依据。例如缺货成本通常高于持有成本比例可能在3:1到10:1之间具体取决于商品毛利和客户忠诚度影响。4. 完整实现流程与关键步骤下面我将结合代码片段梳理从数据到策略的完整实现流程。假设我们使用Python作为工具。4.1 数据预处理与探索性分析这是所有数据项目的起点耗时可能占整个项目的40%。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 1. 加载数据 sales_df pd.read_csv(sales_data.csv, parse_dates[date]) product_df pd.read_csv(product_info.csv) # 2. 合并数据 df pd.merge(sales_df, product_df, onitem_id, howleft) # 3. 处理缺失值与异常值 # 检查缺失 print(df.isnull().sum()) # 销售量为负或极大值视为异常可进行截断或置为NaN df[sales] df[sales].clip(lower0) # 假设负值为错误置为0 # 对于缺失的日期-商品组合填充销售量为0代表无销售 df_full df.set_index([date, item_id]).unstack(fill_value0).stack().reset_index() df_full.rename(columns{0: sales}, inplaceTrue) # 4. 探索性分析 # 查看数据整体情况 print(df_full.describe()) # 查看销量时间序列 plt.figure(figsize(15,5)) df_full.groupby(date)[sales].sum().plot(titleTotal Daily Sales) plt.show() # 查看商品销量分布长尾分析 sales_by_item df_full.groupby(item_id)[sales].sum().sort_values(ascendingFalse) sales_by_item.head(20).plot(kindbar, figsize(15,5), titleTop 20 Items by Total Sales) plt.show() # 分析促销影响 promo_effect df_full.groupby(is_promotion)[sales].mean() print(f平均销量 - 促销日: {promo_effect[1]:.2f}, 非促销日: {promo_effect[0]:.2f})4.2 特征工程与数据集构建基于第3.2节的方法为每个(date, item_id)组合生成丰富的特征。关键点在于避免未来信息泄露。所有基于目标变量sales生成的特征如滞后、滚动均值都必须严格使用历史信息。# 假设df_full是完整的面板数据已处理缺失 df_featured create_features(df_full.copy()) # 使用前面定义的函数 # 划分训练集和测试集 # 假设用最后30天作为测试/验证期 cutoff_date df_featured[date].max() - pd.Timedelta(days30) train_df df_featured[df_featured[date] cutoff_date].copy() test_df df_featured[df_featured[date] cutoff_date].copy() # 定义特征列和目标列 # 移除日期和ID以及可能包含未来信息的列 feature_columns [col for col in train_df.columns if col not in [date, item_id, sales]] target_column sales X_train train_df[feature_columns] y_train train_df[target_column] X_test test_df[feature_columns] y_test test_df[target_column]4.3 预测模型训练与调优使用LightGBM进行建模和交叉验证。import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error, mean_squared_error # 定义时间序列交叉验证 tscv TimeSeriesSplit(n_splits5) # 初始化LightGBM参数 params { objective: regression, metric: rmse, boosting_type: gbdt, num_leaves: 31, learning_rate: 0.05, feature_fraction: 0.9, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1, seed: 42 } # 使用交叉验证 cv_scores [] models [] for train_idx, val_idx in tscv.split(X_train): X_tr, X_val X_train.iloc[train_idx], X_train.iloc[val_idx] y_tr, y_val y_train.iloc[train_idx], y_train.iloc[val_idx] lgb_train lgb.Dataset(X_tr, y_tr) lgb_eval lgb.Dataset(X_val, y_val, referencelgb_train) gbm lgb.train(params, lgb_train, num_boost_round1000, valid_sets[lgb_train, lgb_eval], callbacks[lgb.early_stopping(stopping_rounds50), lgb.log_evaluation(50)]) models.append(gbm) y_pred gbm.predict(X_val, num_iterationgbm.best_iteration) score np.sqrt(mean_squared_error(y_val, y_pred)) # RMSE cv_scores.append(score) print(fFold RMSE: {score:.4f}) print(fCV平均RMSE: {np.mean(cv_scores):.4f} (±{np.std(cv_scores):.4f})) # 使用全部训练数据训练最终模型 final_model lgb.train(params, lgb.Dataset(X_train, y_train), num_boost_round1000, callbacks[lgb.log_evaluation(100)]) # 在测试集上评估 test_pred final_model.predict(X_test, num_iterationfinal_model.best_iteration) test_rmse np.sqrt(mean_squared_error(y_test, test_pred)) test_mae mean_absolute_error(y_test, test_pred) print(f测试集 RMSE: {test_rmse:.4f}, MAE: {test_mae:.4f}) # 特征重要性分析 lgb.plot_importance(final_model, figsize(10, 8), max_num_features20) plt.title(Feature Importance) plt.show()4.4 库存策略仿真与优化假设我们采用简单的报童模型进行单周期优化并假设需求预测误差服从正态分布。def newsvendor_optimizer(demand_forecast, demand_std, unit_holding_cost, unit_shortage_cost): 报童模型求解最优库存水平 :param demand_forecast: 需求预测均值 :param demand_std: 需求预测标准差 :param unit_holding_cost: 单位持有成本 :param unit_shortage_cost: 单位缺货成本 :return: 最优库存水平 Q_star # 计算关键分位数 critical_ratio unit_shortage_cost / (unit_holding_cost unit_shortage_cost) # 假设需求服从正态分布 N(demand_forecast, demand_std^2) from scipy.stats import norm Q_star norm.ppf(critical_ratio, locdemand_forecast, scaledemand_std) # 库存不能为负 Q_star max(0, Q_star) return Q_star # 示例为测试集每个商品-日期组合计算建议库存 # 首先我们需要预测的不只是点估计还有预测区间标准差 # 这里简化处理使用模型在验证集上的预测误差来估计标准差 # 更严谨的做法是使用分位数回归或概率预测模型 # 假设我们有一个函数可以输出预测的均值和标准差 def predict_with_uncertainty(model, X): # 这里可以使用多种方法估计不确定性例如 # 1. 使用多个模型的预测方差集成 # 2. 使用分位数回归LightGBM支持 # 3. 使用conformal prediction等方法 # 此处为示例我们简单假设一个固定的变异系数CV point_pred model.predict(X, num_iterationmodel.best_iteration) cv 0.3 # 假设变异系数为30%这是一个需要根据历史误差调整的参数 std_pred point_pred * cv return point_pred, std_pred # 为测试集生成预测和不确定性 test_point_pred, test_std_pred predict_with_uncertainty(final_model, X_test) # 设置成本参数示例值需根据题目或业务设定 unit_holding_cost 1.0 # 每件商品持有一天的成本 unit_shortage_cost 5.0 # 每缺货一件的损失成本 # 计算最优库存水平 optimal_inventory [] for mu, sigma in zip(test_point_pred, test_std_pred): Q newsvendor_optimizer(mu, sigma, unit_holding_cost, unit_shortage_cost) optimal_inventory.append(Q) test_df[demand_forecast] test_point_pred test_df[demand_std] test_std_pred test_df[optimal_inventory] optimal_inventory # 评估库存策略需要模拟实际的库存动态这里仅展示思路 def simulate_inventory_policy(df, init_inventory, lead_time1): 模拟 (s, S) 策略下的库存动态 :param df: 包含日期、商品、预测需求、实际需求的数据框 :param init_inventory: 初始库存 :param lead_time: 补货提前期天 :return: 包含每日库存水平、订单、成本的数据框 # 初始化 df df.sort_values([item_id, date]).copy() df[inventory_on_hand] 0 df[order_placed] 0 df[order_arrives] 0 df[holding_cost] 0 df[shortage_cost] 0 current_inventory init_inventory pipeline_orders {} # 记录在途订单 {arrival_date: quantity} for idx, row in df.iterrows(): date, item_id, demand_forecast, actual_demand row[[date, item_id, demand_forecast, sales]] # 假设sales是实际需求 # 1. 接收到的到货 if date in pipeline_orders: current_inventory pipeline_orders.pop(date) # 2. 满足当日需求 sales min(current_inventory, actual_demand) shortage max(0, actual_demand - sales) current_inventory - sales # 3. 计算成本 holding_cost current_inventory * unit_holding_cost shortage_cost shortage * unit_shortage_cost # 4. 检查库存并下订单简化版 (s, S) s demand_forecast * lead_time * 1.2 # 再订货点示例计算 S demand_forecast * (lead_time 7) * 1.1 # 目标库存水平示例计算 if current_inventory s: order_qty S - current_inventory if order_qty 0: arrival_date date pd.Timedelta(dayslead_time) if arrival_date in pipeline_orders: pipeline_orders[arrival_date] order_qty else: pipeline_orders[arrival_date] order_qty df.loc[idx, order_placed] order_qty # 5. 记录状态 df.loc[idx, inventory_on_hand] current_inventory df.loc[idx, holding_cost] holding_cost df.loc[idx, shortage_cost] shortage_cost df[total_cost] df[holding_cost] df[shortage_cost] return df # 对单个商品进行模拟示例 sample_item test_df[item_id].iloc[0] sample_data test_df[test_df[item_id] sample_item].copy() sim_result simulate_inventory_policy(sample_data, init_inventory50, lead_time2) total_cost sim_result[total_cost].sum() print(f商品 {sample_item} 在模拟期内的总成本为: {total_cost:.2f})5. 常见问题、避坑指南与进阶思考在实际操作和竞赛中会遇到许多典型问题。这里总结一些关键点和进阶思路。5.1 预测环节的典型问题问题1如何处理间歇性需求大量零值避坑不要直接使用回归模型如线性回归、标准LightGBM回归预测原始销量。这会导致模型倾向于预测接近零的值低估真实需求。解决方案两阶段模型先使用分类模型如LightGBM分类预测某天是否有需求sales 0再使用回归模型预测有需求时的销量规模。最后将两者概率相乘。使用专门模型直接使用支持零膨胀分布的模型如LightGBM的poisson或tweedie损失函数。Tweedie损失对于处理大量零值和正偏态数据非常有效。Croston方法作为基准模型使用但其假设较强在复杂场景下可能不如机器学习模型。问题2如何有效利用商品层级信息避坑对每个SKU单独建模单变量时序会忽略品类间的关联且对长尾SKU不友好。解决方案全局模型将所有SKU的数据放在一起训练一个统一的LightGBM模型。通过加入商品类别、商品价格段等作为特征让模型学习跨SKU的模式。层级聚合与分解先预测大类目的总销量通常更稳定然后根据历史比例或模型预测的比例将总量分解到子类目或SKU。迁移学习/元学习为每个SKU训练一个简单模型然后利用所有SKU的模型参数作为特征训练一个“元模型”来预测新SKU或调整现有SKU的预测。问题3如何评估预测效果避坑仅使用RMSE、MAE等对称性误差指标。在库存优化中高估和低估的成本影响是不对称的缺货成本通常更高。解决方案使用分位数损失在训练时使用分位数回归LightGBM支持直接预测需求的不同分位数如10%50%90%为库存决策提供区间估计。使用业务导向的指标计算在模拟库存策略下的总成本这才是最终的“业务指标”。可以将预测模型和库存模型一起进行端到端的优化。5.2 库存优化环节的典型问题问题4成本参数如何设定避坑随意设定成本比例导致优化结果没有业务意义。解决方案敏感性分析在报告中展示不同成本比例如缺货成本与持有成本之比从2:1到10:1下最优库存水平和总成本的变化。这能体现模型的鲁棒性。业务访谈在真实项目中必须与业务部门采购、财务沟通确定合理的成本范围。在竞赛中基于常识给出合理假设并说明。问题5如何考虑补货提前期和订单约束避坑假设补货是即时完成的忽略了供应链中的延迟。解决方案在模型中显式引入提前期在仿真中订单在L天后才能到货。(s, S)策略中的s必须能覆盖提前期L内的需求不确定性。考虑批量约束现实中订货可能有最小起订量MOQ或整车运输约束。在优化时需要将决策变量Q约束为离散值或某个最小值的倍数。考虑仓储容量约束总库存不能超过仓库容量。这会将单商品优化问题升级为多商品联合优化问题可以使用线性规划或启发式算法求解。5.3 工程与实现效率问题6数据量大训练慢怎么办解决方案利用LightGBM的高效性设置合理的num_leaves、max_depth使用feature_fraction和bagging_fraction。采样对于超大数据集可以对长尾SKU进行下采样或对时间序列进行聚合如按周预测后再降采样到日。并行化LightGBM原生支持多线程确保设置n_jobs参数。问题7代码如何组织以支持可复现性建议结构project/ ├── data/ # 原始数据 ├── processed/ # 处理后的数据 ├── notebooks/ # Jupyter notebooks用于探索 ├── src/ # 源代码 │ ├── data_preprocessing.py │ ├── feature_engineering.py │ ├── modeling.py │ ├── inventory_optimization.py │ └── utils.py ├── configs/ # 配置文件参数 ├── models/ # 保存的模型 ├── results/ # 预测结果、图表 └── README.md使用argparse或配置文件管理超参数使用random seed保证可复现性。这道MathorCup B题是一个绝佳的综合性实践项目它串联起了数据科学、机器学习和运筹优化的核心技能。从实际经验来看拿到一个好成绩的关键往往不在于使用了多么前沿复杂的模型而在于对业务问题的深刻理解、稳健可靠的特征工程、以及将预测与决策紧密结合的系统性思维。我个人的体会是在特征工程上多花一倍的时间通常比在模型调参上多花三倍时间带来的提升更大。另外一定要进行端到端的仿真评估用模拟的总成本来最终评判方案优劣而不是仅仅看预测的RMSE。最后在撰写论文或报告时清晰的逻辑、可视化的结果以及对每一步决策的合理解释和模型效果本身同等重要。

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

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

免费获取报价