简介这份资源是2023年泰迪杯B题「产品订单需求预测」的一等奖完整参赛论文PDF格式全文55页不含附录面向参加数据挖掘与数学建模竞赛的学生、需要学习需求预测实战的算法从业者。论文围绕订单需求预测展开先通过Pearson相关性、趋势拟合与方差分析定量研究价格、区域、销售方式、品类、时间段、节假日、促销节及季节周期等8类因素的影响再构建CART、SVR、KNN、随机森林、LightGBM与LSTM等模型并利用GA智能寻优确定超参最终提出LGB-GA-LSTM组合预测模型按周粒度预测MAPE低至0.497%。资源包共1个PDF文件大小约2.58MB内容完整、结构清晰涵盖问题重述、模型假设、符号说明、建模求解与结果分析全流程。目前已有1747人学习适合作为竞赛论文写作与预测建模的参考范本。1. 从一份拿了一等奖的订单预测 Paper 说起GA-LSTM 加 LGB 集成到底怎么落地企业做订单需求预测最怕的不是模型跑不动而是跑出来的数字跟业务对不上。销售说下个月要冲量采购按预测备了货结果月底仓库堆成山——这种翻车现场根子往往不在算法本身而在特征工程和模型组合方式上。这份 2023 年泰迪杯 B 题的一等奖完整 Paper核心就是解决这个问题用 Pearson 相关性模型、趋势拟合、方差分析先把 8 个影响因素定量拆清楚再上 CART、SVR、KNN、随机森林、LightGBM 和 LSTM最后用 GA 智能寻优调超参拼出一个 LGB-GA-LSTM 组合预测模型。全文不含附录 55 页适合正在做需求预测、销量预测、供应链规划的同学直接参考复现。它不只是一篇竞赛论文更是一套从数据预处理到组合模型落地的完整工程链路。2. 数据预处理与特征工程把原始订单表变成模型能吃的格式2.1 分布检验与异常值处理为什么先做 KS 检验再上拉依达准则拿到order_train1.csv之后别急着往模型里灌。第一步是看数据分布。Paper 里对各个指标做了分布拟合通过 KS 检验发现产品订单需求基本符合对数正态分布。这个结论很关键——它决定了后续异常值处理用拉依达准则是否合理因为拉依达准则的前提是数据近似正态。对数正态分布的概率密度函数和期望方差公式在论文里有推导这里不展开重点看实操。import numpy as np import pandas as pd from scipy import stats # 读取训练数据 df pd.read_csv(order_train1.csv) # 对订单需求量做对数变换后做 KS 检验 log_qty np.log1p(df[ord_qty]) ks_stat, p_value stats.kstest(log_qty, norm, args(log_qty.mean(), log_qty.std())) print(fKS统计量: {ks_stat:.4f}, p值: {p_value:.4f}) # p 0.05 说明对数变换后近似正态可用拉依达准则逻辑说明np.log1p做 log(1x) 变换避免零值报错。KS 检验的 p 值大于 0.05 时不能拒绝正态假设说明对数变换后的需求数据可以用拉依达准则剔除异常值。参数上args传入变换后数据的均值和标准差这是 KS 检验对比理论正态分布的必要参数。拉依达准则的做法是偏差超过 3 倍标准差的点视为异常。但 Paper 里没有只依赖这一种方法而是用箱线图做了二次验证。箱线图的四分位距法则对分布假设更宽松两者结合能减少误杀。最终剔除了 6855 个异常样本。注意拉依达准则在厚尾分布上容易漏掉极端值箱线图又可能把业务上的真实大单当成异常。建议先跟业务确认哪些是促销爆量别一刀切。2.2 缺失值填充与布尔特征构造EM 算法和线性插值的分工缺失值处理分两种场景连续变量用 EM 算法或线性插值布尔特征直接编码。Paper 里对缺失数据同时用了 EM 算法和线性插值法这不是重复劳动——EM 适合有相关性的多变量缺失线性插值适合时间序列上的连续缺口。# 对价格等连续变量用线性插值按产品分组后按时间排序 df df.sort_values([item_code, order_date]) df[item_price] df.groupby(item_code)[item_price]\ .transform(lambda x: x.interpolate(methodlinear)) # 构造时间布尔特征 df[order_date] pd.to_datetime(df[order_date]) df[is_month_start] df[order_date].dt.is_month_start.astype(int) df[is_month_end] df[order_date].dt.is_month_end.astype(int) df[is_quarter_start] df[order_date].dt.is_quarter_start.astype(int) df[is_quarter_end] df[order_date].dt.is_quarter_end.astype(int) # 节假日特征需要一张节假日表做映射 holiday_df pd.read_csv(holiday.csv) # 包含日期和是否节假日 df df.merge(holiday_df, onorder_date, howleft) df[is_holiday] df[is_holiday].fillna(0).astype(int)逻辑说明groupby(item_code)保证插值在同一个产品内部进行不会跨产品串数据。interpolate(methodlinear)按索引顺序做线性填充适合时间序列上的短缺口。布尔特征用astype(int)转成 0/1方便后续做热编码和相关性分析。节假日表需要自己根据年份整理Paper 里没有附原始节假日文件但这是需求预测的标配特征。参数上is_month_start和is_month_end直接调 pandas 的 dt 访问器不用手写判断。促销节如 618、双 11 需要单独构造列因为 pandas 没有内置。2.3 相关性分析与方差分析8 个影响因素怎么定量筛特征工程做完下一步是搞清楚哪些因素真正影响需求。Paper 用了 Pearson 相关系数矩阵和单因素方差分析两套工具。Pearson 看线性相关方差分析看分组均值差异。import seaborn as sns import matplotlib.pyplot as plt from scipy import stats # 计算相关系数矩阵 corr_cols [item_price, ord_qty, sales_region_code, is_holiday, is_month_start, is_month_end] corr_matrix df[corr_cols].corr(methodpearson) print(corr_matrix[ord_qty].sort_values(ascendingFalse)) # 单因素方差分析不同区域对需求的影响 groups [group[ord_qty].values for name, group in df.groupby(sales_region_code)] f_stat, p_value stats.f_oneway(*groups) print(fF值: {f_stat:.2f}, p值: {p_value:.4e})逻辑说明corr(methodpearson)计算线性相关系数从结果看订单需求量和产品价格存在线性负相关和其他因素基本没有线性关系。这意味着后续建模不能只靠线性回归。方差分析的f_oneway接受多个分组数组p 值远小于 0.05 说明不同区域的需求均值存在显著差异。Paper 里提到 102 区域的需求几乎是 104 地区的 10 倍这就是方差分析量化出来的结论。注意Pearson 相关系数只能捕捉线性关系如果两个变量是非线性关联相关系数可能接近零但实际有影响。所以 Paper 才补了方差分析和趋势拟合。3. 从随机森林到 LightGBM传统机器学习模型的训练与调参3.1 CART、SVR、KNN、随机森林的对比为什么树模型胜出Paper 在问题二中先上了四个传统机器学习模型CART 回归树、SVR、KNN 回归、随机森林回归。结果树模型精度最高随机森林的 MAPE 最小为 0.682%。这个结论不是拍脑袋来的而是因为订单需求数据里存在大量非线性关系和特征交互树模型天然适合。from sklearn.ensemble import RandomForestRegressor from sklearn.svm import SVR from sklearn.neighbors import KNeighborsRegressor from sklearn.tree import DecisionTreeRegressor from sklearn.metrics import mean_absolute_percentage_error from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler # 特征选择去掉预测集中没有的列 feature_cols [sales_region_code, item_code, first_cate_code, second_cate_code, ord_qty_lag1, ord_qty_lag2, is_holiday, is_month_start, is_month_end] X df[feature_cols].fillna(0) y df[ord_qty] # 极差标准化 scaler MinMaxScaler() X_scaled scaler.fit_transform(X) X_train, X_test, y_train, y_test train_test_split( X_scaled, y, test_size0.2, random_state42) # 随机森林 rf RandomForestRegressor(n_estimators200, max_depth12, min_samples_leaf5, random_state42) rf.fit(X_train, y_train) rf_pred rf.predict(X_test) print(fRF MAPE: {mean_absolute_percentage_error(y_test, rf_pred):.4f}) # SVR svr SVR(kernelrbf, C1.0, epsilon0.1) svr.fit(X_train, y_train) svr_pred svr.predict(X_test) print(fSVR MAPE: {mean_absolute_percentage_error(y_test, svr_pred):.4f})逻辑说明MinMaxScaler做极差标准化把特征缩放到 [0,1]这对 SVR 和 KNN 是必须的树模型其实不需要但统一处理没坏处。RandomForestRegressor的n_estimators控制树的数量max_depth限制深度防止过拟合min_samples_leaf保证叶子节点有足够样本。MAPE 用mean_absolute_percentage_error直接算值越小越好。参数上随机森林的n_estimators从 100 到 500 都可以试max_depth根据数据量调整。SVR 的C和epsilon对结果影响大需要网格搜索。KNN 的n_neighbors一般取 5 到 20。3.2 LightGBM 与网格搜索调参从随机森林到集成学习的升级随机森林是 bagging 思路LightGBM 是 boosting 思路。Paper 用 LightGBM 来优化随机森林的预测核心原因是 LGB 在处理大规模数据时更快且自带特征重要性输出。超参数用网格搜索确定。import lightgbm as lgb from sklearn.model_selection import GridSearchCV # 定义参数网格 param_grid { num_leaves: [31, 63, 127], learning_rate: [0.01, 0.05, 0.1], n_estimators: [100, 300, 500], min_child_samples: [5, 10, 20] } lgb_model lgb.LGBMRegressor(objectiveregression, metricmape, random_state42) grid_search GridSearchCV(estimatorlgb_model, param_gridparam_grid, cv3, scoringneg_mean_absolute_percentage_error, n_jobs-1, verbose1) grid_search.fit(X_train, y_train) print(f最优参数: {grid_search.best_params_}) best_lgb grid_search.best_estimator_ lgb_pred best_lgb.predict(X_test) print(fLGB MAPE: {mean_absolute_percentage_error(y_test, lgb_pred):.4f})逻辑说明GridSearchCV做三折交叉验证scoring用负 MAPE 因为 sklearn 的评分函数越大越好。num_leaves是 LGB 的核心参数控制树的复杂度learning_rate和n_estimators需要联合调学习率小则树要多。min_child_samples防止过拟合。参数上如果数据量超过 10 万行num_leaves可以设到 255 以上但要注意内存。learning_rate一般从 0.1 开始试再降到 0.01 看精度是否提升。网格搜索比较耗时可以先用随机搜索粗调再网格细调。注意LGB 虽然能考虑多种有效特征但无法直接处理时间序列的时序依赖。Paper 里明确指出这一点所以才要进一步上 LSTM。4. GA-LSTM 与组合预测时间序列建模和智能寻优怎么配合4.1 LSTM 的时间粒度选择按天、周、月哪个精度最高LSTM 适合处理时间序列但时间粒度的选择直接影响精度。Paper 对比了按天、周、月三种粒度结果按周预测效果最好MAPE 为 0.497%。这个结论有业务含义按天太碎噪声大按月太粗丢失季节波动按周刚好平衡。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping def build_lstm(input_shape, units64, dropout0.2): model Sequential([ LSTM(units, return_sequencesTrue, input_shapeinput_shape), Dropout(dropout), LSTM(units // 2, return_sequencesFalse), Dropout(dropout), Dense(1) ]) model.compile(optimizeradam, lossmape) return model # 构造时间序列样本用过去 N 周预测下一周 def create_sequences(data, seq_length): X, y [], [] for i in range(len(data) - seq_length): X.append(data[i:iseq_length]) y.append(data[iseq_length]) return np.array(X), np.array(y) # 按周聚合 weekly df.groupby(pd.Grouper(keyorder_date, freqW))[ord_qty].sum() seq_length 8 # 用过去 8 周预测下一周 X_seq, y_seq create_sequences(weekly.values, seq_length) X_seq X_seq.reshape((X_seq.shape[0], X_seq.shape[1], 1)) model build_lstm((seq_length, 1)) early_stop EarlyStopping(monitorloss, patience10, restore_best_weightsTrue) model.fit(X_seq, y_seq, epochs100, batch_size16, callbacks[early_stop], verbose0)逻辑说明create_sequences把时间序列切成滑动窗口seq_length8表示用 8 周历史预测第 9 周。LSTM 层return_sequencesTrue让第一层输出完整序列给第二层第二层只输出最后一个时间步。EarlyStopping防止过拟合patience10表示损失 10 轮不降就停。参数上units一般取 32 到 128dropout取 0.2 到 0.5。seq_length根据数据周期定周数据用 8 到 12月数据用 6 到 12。batch_size小一点训练更稳但速度慢。4.2 GA 智能寻优确定 LSTM 超参遗传算法怎么替代网格搜索LSTM 超参数多网格搜索维度爆炸。Paper 用 GA 智能寻优算法确定近似最优超参。GA 的核心是种群、选择、交叉、变异适合连续和离散混合的超参空间。import random import numpy as np # 定义超参空间 param_space { units: (32, 128), dropout: (0.1, 0.5), seq_length: (4, 12), batch_size: (8, 32) } def fitness(params): units int(params[units]) dropout params[dropout] seq_length int(params[seq_length]) batch_size int(params[batch_size]) X_seq, y_seq create_sequences(weekly.values, seq_length) X_seq X_seq.reshape((X_seq.shape[0], X_seq.shape[1], 1)) model build_lstm((seq_length, 1), unitsunits, dropoutdropout) model.fit(X_seq, y_seq, epochs30, batch_sizebatch_size, verbose0) pred model.predict(X_seq, verbose0) mape np.mean(np.abs((y_seq - pred.flatten()) / y_seq)) return mape # 简化的 GA 流程 population_size 10 generations 5 population [{k: random.uniform(v[0], v[1]) for k, v in param_space.items()} for _ in range(population_size)] for gen in range(generations): scores [(fitness(ind), ind) for ind in population] scores.sort(keylambda x: x[0]) survivors [ind for _, ind in scores[:population_size // 2]] # 交叉和变异 offspring [] while len(offspring) population_size - len(survivors): p1, p2 random.sample(survivors, 2) child {k: random.choice([p1[k], p2[k]]) for k in param_space} # 变异 for k in child: if random.random() 0.1: child[k] random.uniform(*param_space[k]) offspring.append(child) population survivors offspring best_params min(scores, keylambda x: x[0])[1] print(fGA最优参数: {best_params})逻辑说明fitness函数训练一个 LSTM 并返回 MAPEGA 的目标是最小化 MAPE。种群初始化用均匀分布随机采样每代按适应度排序保留一半交叉用随机选择父代参数变异以 0.1 概率随机重置。generations5是演示用实际可以设 20 到 50。参数上种群大小一般 20 到 50交叉率 0.6 到 0.9变异率 0.01 到 0.1。GA 比网格搜索快但不保证全局最优适合超参空间大的场景。4.3 LGB-GA-LSTM 组合预测怎么把两个模型的优势拼起来Paper 最后的组合模型 LGB-GA-LSTM 是核心创新点。思路是LGB 考虑影响因素特征GA-LSTM 考虑时间序列季节变化两者加权组合。对于训练集中缺失的商品用 LGB 预测结果填充。# 假设 lgb_pred 和 lstm_pred 已经对齐到同一时间粒度 # 用验证集确定权重 from scipy.optimize import minimize def combine_loss(weights): w1, w2 weights combined w1 * lgb_pred w2 * lstm_pred return mean_absolute_percentage_error(y_test, combined) # 权重约束和为 1非负 cons ({type: eq, fun: lambda w: w[0] w[1] - 1}) bounds [(0, 1), (0, 1)] result minimize(combine_loss, [0.5, 0.5], boundsbounds, constraintscons) print(f最优权重: LGB{result.x[0]:.3f}, LSTM{result.x[1]:.3f}) # 最终预测 final_pred result.x[0] * lgb_pred result.x[1] * lstm_pred逻辑说明minimize用 SLSQP 优化权重约束是权重和为 1。combine_loss返回组合后的 MAPE优化器自动找最小点。如果某些商品只有 LGB 能预测直接取 LGB 结果权重退化为 1 和 0。参数上权重优化需要验证集不能直接在测试集上调。如果两个模型精度差距大权重会偏向好的那个。Paper 里提到组合模型拟合效果优于单一模型和普通组合预测模型。5. 避坑与排查复现这份 Paper 时最容易翻车的五个地方5.1 现象KS 检验 p 值小于 0.05但数据明明是对数正态原因KS 检验对样本量敏感大样本下微小偏差也会导致 p 值显著。另外如果数据里混了未处理的异常值分布拟合会偏。解决先剔除异常值再做 KS 检验或者用 QQ 图辅助判断。样本量超过 10 万时p 值参考意义下降重点看 QQ 图的偏离程度。5.2 现象随机森林 MAPE 跑出来 5% 以上跟 Paper 的 0.682% 差很远原因特征工程没做全。Paper 里用了滞后特征、时间布尔特征、品类编码等如果只拿原始列跑模型精度肯定差。解决检查是否构造了ord_qty_lag1、ord_qty_lag2等滞后项是否把sales_chan_name做了编码是否按产品分组做了归一化。特征没对齐模型再好也白搭。5.3 现象LSTM 训练损失不下降或者下降后预测全是均值原因序列长度设错了或者数据没做标准化。LSTM 对输入尺度敏感需求值如果跨度大梯度会爆炸或消失。解决先做极差标准化或 Z-score 标准化seq_length从 4 开始试逐步加到 12。如果预测全是均值说明模型没学到时序模式检查序列构造是否打乱了时间顺序。5.4 现象GA 寻优跑了一晚上结果还不如手动调的参数原因适应度函数评估太慢种群和代数设太大或者超参空间范围不合理。解决先用小样本快速评估种群设 10 到 20代数设 5 到 10 看趋势。超参范围参考经验值别把units设到 1000。GA 适合粗调细调还是靠网格。5.5 现象组合模型权重优化后测试集精度反而下降原因权重是在验证集上优化的如果验证集和测试集分布不一致权重会过拟合验证集。解决用交叉验证确定权重或者直接用简单平均。如果两个模型精度差距大固定权重比优化权重更稳。Paper 里的组合模型有业务逻辑支撑不是纯数学加权。6. 进阶技巧用不同时间粒度做敏感性分析验证模型鲁棒性Paper 里有一个容易被忽略的亮点按天、周、月三种粒度分别预测然后分析粒度对精度的影响。这个思路不只是竞赛要求实际业务里也很有用——供应链按周补货财务按月预算销售按天看板不同粒度对应不同决策场景。我一般会这样做敏感性分析先把原始日数据分别重采样到周和月然后对每个粒度跑同一套 LGB-GA-LSTM 流程记录 MAPE 和预测偏差的分布。下面是一个对比表格的示例结构时间粒度样本量LGB MAPEGA-LSTM MAPE组合 MAPE训练耗时按天最大较高较高中等最长按周中等中等最低最低中等按月最小最低中等较低最短从 Paper 的结论看按周预测 MAPE 最低为 0.497%。按天数据噪声大按月样本少周粒度刚好平衡。但这不是绝对规律取决于业务周期。如果促销活动按天爆发按天粒度可能反而更好。验证鲁棒性还有一个技巧滚动预测。不要只切一次训练测试集而是用时间序列交叉验证每次用过去 N 个月预测下一个月滚动前进。这样能看出模型在不同时间段的稳定性。from sklearn.model_selection import TimeSeriesSplit tscv TimeSeriesSplit(n_splits5) for train_idx, test_idx in tscv.split(X_scaled): X_tr, X_te X_scaled[train_idx], X_scaled[test_idx] y_tr, y_te y.iloc[train_idx], y.iloc[test_idx] rf RandomForestRegressor(n_estimators200, random_state42) rf.fit(X_tr, y_tr) pred rf.predict(X_te) print(fFold MAPE: {mean_absolute_percentage_error(y_te, pred):.4f})逻辑说明TimeSeriesSplit保证训练集在测试集之前不会用未来数据预测过去。n_splits5表示切 5 折每折训练集逐步扩大。这样能看出模型在不同时间窗口的表现比单次切分更可靠。参数上n_splits根据数据长度定至少留够一个完整业务周期。如果数据只有一年切 3 到 5 折就够了。从那以后我每次做需求预测都强制走一遍「分布检验 → 异常值处理 → 特征工程 → 多模型对比 → 组合加权 → 滚动验证」的流程少一步都可能在后头翻车。这份 Paper 的价值不在于它拿了奖而在于它把每个环节的参数和坑都写清楚了照着复现能省很多试错时间。希望帮到你。本文还有配套的精品资源点击获取