京东销量预测工程化实践:基于深度学习与PyTorch的完整指南
简介这是一套基于深度学习的京东商品销量预测系统源码主要面向电商数据分析师、机器学习初学者及算法开发者。项目以RNN等深度学习模型为核心覆盖数据预处理、模型构建、训练、评估与销量预测的完整流程并针对SKU级与用户级分别建模可用于电商库存管理、销售策略制定等场景。压缩包共51个文件、约6.96MB包含Python源码、SQL数据库脚本、CSV数据集、PNG结果图表及配置说明文档其中Python源码负责实现模型构建、训练与评估SQL脚本用于数据表和特征生成CSV存放训练与测试数据PNG图片直观展示各模型在用户级、SKU级上的ROC曲线TXT、YAML与Git忽略文件则提供说明、配置与版本控制辅助。目前已有342人学习浏览。整套源码既给出了从数据清洗、特征工程到RNN模型训练调参的完整代码也附带了历年提交结果与可视化图表可帮助读者快速掌握电商销量预测项目的实施流程适合作为课程设计、毕业设计或入门深度学习的实战参考代码模块化程度较高便于二次开发。1. 基于深度学习的京东商品销量预测先弄清它到底解决什么问题一个京东自营采销的日常工作是从打开一张销量明细表开始的。过去30天这款商品卖了多少、库存还能撑几天、下周该给供应商下多少单以前全靠采销的个人经验拍板。我见过一个自营采销团队双11前因为预测偏差某爆款备的货滞销了整整一个季度仓租和折价吃掉大半利润。这不是管理问题是预测问题。基于深度学习的京东商品销量预测就是用历史销量序列加上价格、促销、商品属性等外部变量让模型输出未来7天或30天的销量预估值把「凭感觉备货」变成「带置信区间的备货」。它要解决的核心痛点是三类非平稳情况促销脉冲、季节性波动、缺货截断。这些场景下传统统计模型守不住精度而深度模型能同时吸收时序行为和外生变量。这篇文章写给写过Python、配过深度学习环境的开发者和数据分析师默认你能跑通PyTorch。2. 喂给模型的数据京东销量预测的特征工程与序列构建2.1 预测粒度选择SKU、SPU还是类目聚合线上做销量预测绝大多数项目落在SKU粒度但纯SKU粒度会让长尾商品整体翻车。京东的商品层级是SPU款下挂SKU具体颜色规格销量数据按SKU独立存储同一个SPU下不同颜色的SKU未来销量走势可能完全不同——采销下单也是按SKU来下的。粒度选错后面所有模型工作都是在错误地基上盖楼。我惯用的分层方案是头部SKU单独建模中部商品按SPU聚合后建模再按历史占比拆分尾部商品不拆序列直接给类目增速系数。这个分层不是拍脑袋京东自营的备货单就是按SKU明细下的SPU聚合模型算出的值必须通过历史销量占比拆回到SKU拆回来之后各SKU的占比还要在时间上做平滑不能直接用某一天的占比否则当天促销波动会把拆分比例带偏。商品层级建模方式适用条件头部SKU单SKU专属模型序列长、可上Transformer日均销量≥50件中部SPUSPU聚合序列训练按SKU历史占比拆分日均5~50件尾部商品类目均值季节性系数不训练深度学习日均5件注意头部SKU的个数通常只占全量5%~10%却贡献了70%~80%的GMV。别一开始就把所有SKU塞进同一个LSTM里那会把爆款信号淹在长尾噪声里。这个分层经验来自我接触过的一个家电类目项目尾部6000个SKU的序列信息量加起来还没头部30个SKU大混在一起训就是一个互相拖后腿的局面。2.2 特征体系销量之外还要喂什么先讲一个刚入行容易忽略的事实销量预测模型能不能赢在起跑线八成靠特征而不是模型。京东商品页上能拿到的字段不少但真正进模型的要克制宁可少喂垃圾不要多喂噪声。我把常用特征组整理成一张清单按优先级从高到低排特征组字段示例说明销量序列近7/14/30/60日日销量log1p变换后入模价格当前价、近7日均价、价格变动率价格是销量最强的外生变量促销是否秒杀、促销剩余天数、满减力度大促脉冲的主要来源时间星期几、月底、节假日距今天数周月周期性的来源商品属性叶子类目、品牌、上架天数冷启动时也靠它流量浏览量、加购数、收藏数有就一定要喂没有就跳过京东的秒杀和Plus专享价会同时出现在同一个商品详情页上实际成交价经常和页面标价不一致。做价格特征时一定用订单侧的成交均价不要用列表价否则促销日的特征值全是错的。价格变动率我习惯用当日价 / 近7日均价 - 1好处是量纲统一爆款和长尾能放进同一个特征空间里比较。促销特征的注意点有两个第一秒杀活动往往带「剩余X天」这个字段对销量的影响是最后两天远大于前几天直接做成one-hot不如按剩余天数倒序映射成一个0~1的衰减值第二商品在秒杀频道、Plus专享频道这些位置是否有露出每个位置单独做0/1标志比合成一个「是否促销」更有效。这些特征细节在真实项目里带来的精度提升往往比换模型结构更大。2.3 数据清洗与序列补齐缺货、下架和异常值清洗环节有一个特别容易糊弄过去的坑缺货日的销量是0但需求不是0。如果你直接把0喂给模型模型会把「断货期」学成「低需求期」补货到货后的第一周预测持续偏低采销跟着少下单库存又断形成自我强化的负循环。所以清洗阶段必须把缺货标记单独保留下来哪怕训练时先不处理特征里至少要有is_oos字段。import pandas as pd import numpy as np df pd.read_csv(jd_sales_daily.csv, parse_dates[date]) df df.sort_values([sku_id, date]).reset_index(dropTrue) # 缺货标记库存为0且销量为0代表需求被截断而非真实为零 df[is_oos] (df[stock] 0) (df[sales] 0) # 销量下限截断退款和取消订单会让销量出现负值直接截断 df[sales] df[sales].clip(lower0) # 对数变换压制爆款与长尾的量纲差训练更稳定 df[sales_log] np.log1p(df[sales]) # 丢弃价格为0或空的脏行 df df[df[price].fillna(0) 0] # 按SKU补齐缺失日期日期断层填0 sku_frames [] for sku_id, sub in df.groupby(sku_id): full_index pd.date_range(sub[date].min(), sub[date].max(), freqD) sub sub.set_index(date).reindex(full_index).rename_axis(date).reset_index() sub[sku_id] sub[sku_id].fillna(sku_id) sub[sales_log] sub[sales_log].fillna(0) sub[is_oos] sub[is_oos].fillna(False) sku_frames.append(sub) df pd.concat(sku_frames, ignore_indexTrue)这段脚本的核心是保留两组信息真实缺货的is_oos标记以及经过log1p的销量。log1p 不需要模型额外学习只是压缩量纲预测完再用expm1还原。顺序上必须先做 clip 再做 log否则负数直接报错或变成 NaN。fillna(0)只用于日期补齐产生的空位不用于缺货日替代。如果你手头有同类目竞品的销量数据也可以在补齐后让竞品销量参与类目热度特征的计算但那是进阶做法第一版不建议引入会带入更多脏数据。补齐之后生成价格变动率和促销衰减特征常见做法是分组滚动计算df[price_ma7] df.groupby(sku_id)[price].transform( lambda s: s.rolling(7, min_periods1).mean() ) df[price_ratio] df[price] / df[price_ma7] - 1 # 促销衰减值剩余天数越大衰减越快最后一天为1 df[promo_decay] df[promo_days_left].apply( lambda d: np.exp(-d / 3) if pd.notna(d) else 0.0 )这里有个高频翻车点groupby.transform返回的是保持原索引结构的结果如果上一段reindex后没有重置索引df[price]的索引和 groupby 结果错位赋值完整个价格序列全串行。我的习惯是每跑完一步预处理就检查df.index.is_monotonic_increasing确认索引对齐了再继续。3. 模型选型LSTM、TCN还是Transformer京东销量数据适合谁3.1 为什么先放下ARIMA和Prophet先讲一个背景结论今天做京东销量预测传统统计模型仍然有价值但基本只当基线用。ARIMA 要求序列平稳或差分平稳而京东销量天然有三个特征——周周期性、促销突变、缺货归零——每一项都在破坏平稳性。用 ARIMA 做日粒度备货计划大促结束后的那一个星期预测误差能把整个补货节奏推乱这是我观察到的真实情况。Prophet 比 ARIMA 好一些它的趋势、季节、节假日分解框架能处理周期突变但有两个硬伤。第一它对价格、促销这类外生变量的支持很生硬加 extra_regressor 相当于线性偏置无法表达「秒杀最后一天销量暴涨3倍」这种强非线性第二Prophet 对缺货截断数据有直接问题把0值序列当趋势低谷看补货后的反弹完全预测不到。所以哪怕做基线我也更推荐 LightGBM 加滞后特征。深度模型在这里的胜负手不是「更智能」而是多变量非线性交互价格降了10%同时秒杀还剩1天这种组合效应在 LSTM 或 Transformer 里是隐式学出来的在 ARIMA 里无从表达。3.2 LSTM基线结构与参数LSTM 是销量预测最稳的起点模型。原因不是它最准而是它最容易从零跑到上线且对特征噪声的容忍度最高。GRU 参数少、训练快但遇到促销这种离群脉冲时LSTM 的遗忘门和记忆门能更平滑地吸收尖峰RNN 基本不考虑梯度问题在长序列上必现。如果你刚配好深度学习环境想找 Python 深度学习教程练手拿这个任务做第一个项目很合适结构不复杂但涵盖全流程。我常用的 LSTM 参数表参数取值说明input_size特征维度常见4~10销量价格促销时间hidden_size64头部SKU可用128num_layers21层欠拟合3层以上提升有限dropout0.2只在LSTM层间做Dense层不放window14或30日粒度预测7天推荐14horizon7或30备货计划常用7天lr1e-3Adam下从1e-3起步batch_size256按显存调整不强制选参数有个值得记住的规律预测步长加大时输入序列长度也要按比例拉长否则模型只能学到最后几天的惯性学不到周期。日粒度预测未来7天我至少给14天输入预测30天输入最少45天。逻辑上很好理解——模型要预测一个月的走势至少见过一个完整的月度周期。提示不要一上来就追新模型。LSTM 作为基线的意义是把数据链路、评估流程全部跑通之后换模型才有对比基准。基线没跑通就上 Transformer出了问题你分不清是模型问题还是数据问题。3.3 TCN与Transformer什么时候入场LSTM 过了基线之后下一步不一定是更深 LSTM而是先试 TCN时间卷积网络。TCN 用膨胀因果卷积替代循环结构训练可以并行速度比 LSTM 快很多序列接受域靠卷积层数和膨胀系数控制。对日粒度、周期稳定的中部 SKUTCN 往往训得更快精度也不输 LSTM但遇到促销脉冲时泛化能力略差一点因为卷积核没有显式的记忆门控。Transformer 更适合头部 SKU 的长期预测。头部 SKU 序列长、促销模式复杂自注意力能直接建立「30天前的促销日」和「今天销量」之间的长程依赖这是 LSTM 要堆多层才勉强逼近的能力。具体落地我推荐从 Informer、Autoformer 这类面向长序列的变体入手直接上原生 Transformer 做90天输入预测30天计算量不划算。场景推荐模型原因全量SKU快速验证LSTM稳定、参数少、训练快中部SKU日粒度TCN训练快、周期识别好头部大促震荡SKULSTM注意力长程依赖非线性促销冷启动新品LightGBM类目特征没有历史序列可用需要强调的是纯深度模型在极长尾数据上会退化。很多从业者把 LightGBM 加滞后特征当作第二基线再用深度模型的输出做 Stacking这是工业界最常见的混合做法。别小看树模型在销量预测这种偏表格数据的场景里LightGBM 的 baseline 经常比 LSTM 高深度学习真正的价值体现在头部 SKU 和高频促销场景。4. 核心源码实现一个可复现的PyTorch销量预测项目4.1 项目结构与时序数据集构建这是一个深度学习实战项目该有的最小工程结构我按这个目录组织代码所有参数集中在 config 里方便后面做网格搜索sales_forecast/ ├── config.py # 全局参数 ├── dataset.py # 时序Dataset ├── model.py # LSTM模型定义 ├── train.py # 训练主循环 └── predict.py # 预测与WMAE评估先看 config.py把常改的参数收口在一处# config.py WINDOW 14 # 输入序列长度过去14天 HORIZON 7 # 预测未来7天 HIDDEN_SIZE 64 NUM_LAYERS 2 DROPOUT 0.2 LR 1e-3 EPOCHS 30 BATCH_SIZE 256约定凡是参与网格搜索的参数都收敛在 config 里训练脚本只读不写跑完一组就归档一份 config 副本。这个习惯能让实验复现省掉大量重复排查时间。然后是 dataset.py。时序数据集的构建是整个项目最核心的代码窗口切错了训练再久也白搭import torch from torch.utils.data import Dataset class SalesDataset(Dataset): def __init__(self, df, sku_ids, window14, horizon7, feature_colsNone): self.df df self.sku_ids list(sku_ids) self.window window self.horizon horizon self.feature_cols feature_cols or [ sales_log, price_ratio, promo_decay, weekday ] self.samples [] for sid in self.sku_ids: sub df[df[sku_id] sid].sort_values(date).reset_index(dropTrue) for i in range(len(sub) - window - horizon 1): x sub.iloc[i : i window] y sub.iloc[i window : i window horizon][sales_log].values # 样本内只要包含缺货日就整体降权这里先记录标记 oos_count int(sub.iloc[i : i window][is_oos].sum()) self.samples.append((sid, x, y, oos_count)) def __len__(self): return len(self.samples) def __getitem__(self, idx): sid, x, y, oos_count self.samples[idx] x_tensor torch.tensor(x[self.feature_cols].values, dtypetorch.float32) y_tensor torch.tensor(y, dtypetorch.float32) return x_tensor, y_tensor, oos_count逻辑说明每个 SKU 的历史序列按滑动窗口切成 (window, horizon) 的样本对滑动步长是1天。len(sub) - window - horizon 1保证最后一个样本的输入和输出都不越界。返回值里带了oos_count供训练循环对缺货密集的样本降权这个设计对应避坑章里的缺货问题。参数说明feature_cols默认是销量、价格变动率、促销衰减、星期几四列。注意weekday在 Pandas 里取出来是整数0~6直接喂网络没问题不需要 one-hotLSTM 对这种小整数编码的容忍度很高。如果你想加类目、品牌这类离散特征建议做 embedding 而不是 one-hot否则特征维度爆炸。4.2 LSTM模型定义与训练循环model.py 里的模型结构保持简单。我的原则是第一版模型越朴素越好把复杂度留给特征和调参import torch.nn as nn class SalesLSTM(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.2, horizon7): super().__init__() self.lstm nn.LSTM( input_size, hidden_size, num_layers, batch_firstTrue, dropoutdropout ) self.head nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, horizon) ) def forward(self, x): out, _ self.lstm(x) # out: (B, window, hidden_size) last out[:, -1, :] # 取最后一个时间步的隐藏态 return self.head(last) # (B, horizon)train.py 里的核心是训练循环和损失函数选择。这里的损失函数我建议用 SmoothL1Loss也就是 Huber Loss而不是 MSE。销量序列里促销尖峰是天然离群值MSE 会对离群值给出二次方级别的梯度惩罚一次大促样本能带偏整个 epoch 的方向import torch import torch.nn as nn from torch.utils.data import DataLoader def train_one_epoch(model, loader, optimizer, criterion, clip1.0): model.train() total_loss 0.0 total_weight 0.0 for xb, yb, oos_count in loader: # 缺货天数多的样本降权权重公式样本权重1/(1oos_count) sample_weight 1.0 / (1.0 oos_count.float()).unsqueeze(1) optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss (loss * sample_weight).mean() loss.backward() nn.utils.clip_grad_norm_(model.parameters(), clip) optimizer.step() total_loss loss.item() * xb.size(0) total_weight xb.size(0) return total_loss / total_weight逻辑说明sample_weight对缺货天数多的样本降权具体公式是1 / (1 oos_count)缺货3天权重降到四分之一但不完全丢弃这样模型不会把缺货期的0值当真需求学进去。clip_grad_norm_把梯度范数裁剪到1.0防止长序列回传时梯度爆炸这是 LSTM 训练的常规保护。参数说明SmoothL1Loss在误差小时接近 L2误差大时退化为 L1对促销尖峰天然免疫梯度裁剪阈值clip1.0是我的默认值调小会让收敛变慢调大可能不稳定。这两个设置是销量场景里我认为最值得优先调的两个点。训练主循环dataset SalesDataset(df, sku_ids, windowWINDOW, horizonHORIZON) loader DataLoader(dataset, batch_sizeBATCH_SIZE, shuffleTrue, num_workers4) model SalesLSTM(input_sizelen(dataset.feature_cols), hidden_sizeHIDDEN_SIZE, num_layersNUM_LAYERS, horizonHORIZON) optimizer torch.optim.Adam(model.parameters(), lrLR, weight_decay1e-5) criterion nn.SmoothL1Loss() for epoch in range(EPOCHS): loss train_one_epoch(model, loader, optimizer, criterion) print(fepoch {epoch 1}: loss{loss:.4f})关于shuffleTrue要说明一下很多人觉得时序数据训练不能 shuffle这是误解。窗口内部的时间顺序由 LSTM 自己保持样本之间的顺序不影响时间依赖关系shuffle 反而能打散不同 SKU 之间的相互影响避免模型按 SKU 顺序学到无关的偏置。4.3 预测与WMAE评估predict.py 负责用训练好的模型对未来 horizon 天做预测并用加权指标评估。预测时只取每个 SKU 最近的 window 天作为输入注意这里不能传入未来数据import numpy as np import torch def predict_sku(model, df, sku_id, window14): model.eval() last_window df[df[sku_id] sku_id].sort_values(date).tail(window) feat_cols [sales_log, price_ratio, promo_decay, weekday] x torch.tensor([last_window[feat_cols].values], dtypetorch.float32) with torch.no_grad(): pred_log model(x)[0].numpy() pred np.expm1(pred_log) # 销量下限截断负数预测直接归零 return np.clip(pred, a_min0) def wmae(pred, true, w): # 按销量加权卖得多的SKU权重更高误差更贴近GMV口径 return float(np.sum(w * np.abs(pred - true)) / np.sum(w))逻辑说明predict_sku的输入 shape 必须是 (1, window, feat_dim)因为模型是 batch_first。预测值是 log1p 空间的对数值np.expm1还原成真实销量尺度。WMAE 的权重w一般取该 SKU 在验证期的日均销量或者按目标库存金额算这样误差口径和 GMV 损失一致。参数说明np.clip(pred, a_min0)做下限截断销量预测不能为负。这个后处理在第一版模型里必须加上因为冷启动和长尾 SKU 预测出负数是常见现象不做截断会导致预测金额为负直接污染备货计划。5. 京东销量预测的五个典型坑现象、原因与排查顺序5.1 缺货销量为0但需求不是0现象某 SKU 断货三天那三天销量全为 0模型学成的结果是「这个 SKU 平时卖得少」补货到货后连续两周预测偏低采销跟着少下单库存又断陷入负循环。原因缺失数据被当作真实零需求。LSTM 没有额外的缺货信号它只能从数字上把 0 理解为低需求。这就是缺货截断问题的本质——需求被库存状态截断了而不是消失了。解决训练样本里对is_oosTrue的片段降权权重公式就是 4.2 节里的1 / (1 oos_count)更彻底的做法是把缺货日替换成同类目同价格带 SKU 的销量中位数但替换本身引入估计误差第一版先降权即可。上线前跑一个对照组把is_oos全部置 0 和置 1 各训一次对比验证集 WMAE确认这个标记确实被模型用上了。5.2 促销脉冲把模型带偏现象双11 当天销量是平时 50 倍模型把这个尖峰学进权重之后平时日期的预测被莫名抬高或者反过来模型为了平滑尖峰在促销日输出一个明显偏低的平均估计。备货计划在促销前后同时失真。原因Huber Loss 虽然比 MSE 温和但极端样本仍然贡献了不成比例的梯度。促销脉冲样本在 batch 里占比小、单样本梯度大训练被少数样本主导。解决三件套组合使用。第一目标值 caplog1p之后的销量上限截断到 97 分位防止极端促销值主导权重第二促销日单独维护一个「促销系数」乘在基准预测上基准模型只学平销模式第三改用分位数损失见第 6 章让模型学分位数而不是均值。我实际项目里组合了第二和第三效果最稳。5.3 特征泄漏把未来信息带进了训练现象验证集 WMAE 低得离谱模型上线后误差翻三倍。排查发现所有 SKU 用了同一个归一化 StandardScaler而这个 scaler 是在全量数据上 fit 的——验证集的信息已经被它偷看过了。原因时间序列任务最常见的错误。归一化、滚动均值如果按全量数据计算等于把未来统计量注入了训练特征。随机切分验证集在普通机器学习里没问题在时间序列里是致命错误。解决归一化只 fit 到训练截止日期滚动特征必须用滞后窗口比如shift(1)之后再 rolling训练验证切分必须按日期用date cutoff和date cutoff并且要保证同一天的样本不会跨切分线。排查顺序上先看特征工程代码里有没有fit在全量数据上的操作再看验证集切分是不是用了train_test_split(random_state42)。5.4 冷启动新品没有30天历史现象新上架 SKU 或改规格老 SKU 没有历史序列Dataset 循环里根本造不出样本模型输出 NaN或者直接走长尾分支给一个类目平均值。采销拿到预测值后没法判断可信度。原因纯时序模型必须有历史窗口冷启动本质上是一个缺失序列问题。强制有历史才能预测就屏蔽了新品上市的备货需求。解决第一版约定「上架满 30 天才进 LSTM 训练不足 30 天的走 LightGBM 类目均值」。类目均值的计算注意粒度用同一个叶子类目、同价格带在最近 7 天的销量中位数而不是整个类目的一刀切均值。积累够了序列长度再迁移进深度模型迁移时把前 14 天的历史补零让模型从平销开始学起。5.5 长尾分布爆款和僵尸SKU同吃一锅饭现象头部 SKU 日均几百件尾部 SKU 日均 0.1 件模型训完对几乎所有输出都接近 0——因为整体损失最小化之后尾部商品主导了样本数量。原因所有 SKU 放进同一个 batch销量量纲差异太大log1p虽然压了量纲但尾部商品数量上占优梯度方向被拉向「输出小值」。解决分层建模。头部 SKU 单独训 LSTM中部合并训练但给每个 SKU 加一个 embedding 向量让模型学习 SKU 之间的关联尾部不训深度模型给类目系数。我见过团队把所有 SKU 塞一起训 Transformer 然后验收翻车的根因就在长尾分布不是模型不够强。排查顺序建议线上 WMAE 变高先按 SKU 分层看误差通常问题出在缺货标记或者促销系数然后检查归一化边界和特征泄漏最后看长尾占比是否过高。按这个顺序能避免在模型结构上浪费时间。6. 上线前怎么验证滚动回测、分位数损失与后处理的三个实战技巧6.1 滚动回测才是上线前的必修课销量预测模型的验证不能用一次性切分因为业务本身是非平稳的。我一般做 walk-forward 滚动回测从某个日期开始每 7 天为一个验证窗口模型只用在窗口之前的数据训练逐窗口向前推进模拟线上环境的真实节奏。cutoffs pd.date_range(2023-03-01, 2023-06-01, freq7D) for cutoff in cutoffs: train_df df[df[date] cutoff] val_df df[(df[date] cutoff) (df[date] cutoff pd.Timedelta(days7))] # 在 train_df 上训练在 val_df 上评估 # 每个窗口记录一份 WMAE最终取平均逻辑说明这个循环把 12 周的验证误差平均后得出的结论比单次切分可靠得多尤其是能看出模型在促销周期各阶段的表现差异。如果滚动回测均价和单次切分差异超过 30%基本可以断定有泄漏或者过拟合。6.2 库存决策用分位数损失别用MAE备货成本和缺货成本是不对称的备多了压库存备少了丢销售。MAE 只给一个均值估计而库存决策真正需要的是「有 90% 把握销量不超过某值」的上限。分位数损失能直接学习目标分位数def pinball_loss(pred, true, q0.9): err true - pred return torch.mean(torch.maximum(q * err, (q - 1) * err))逻辑说明q0.9 时模型学的是 90 分位数预测——90% 的日子里真实销量不会超过这个值。采销按这个数下单缺货概率就控制在 10%。这是我在库存项目里最常用的技巧比先预测均值再手动乘系数要严谨得多。训练时把 q 设成 0.5 就是中位数预测相当于一个对离群值更鲁棒的 MAE 替代方案。6.3 后处理促销系数和节假日开关深度模型再强对固定日期的大促也学不透每年双11 的玩法都不一样。我的做法是保留一个外部系数模型输出基准预测业务层乘以促销系数。促销系数用去年同档大促的实际销量 / 平销均值的比例再人工修正一版。# 后处理示例 pred np.clip(pred, a_min0) if promo_flag: # 外部传入今年是否参加大促 pred pred * promo_coef # promo_coef 由去年同类目大促弹性算出这个技巧的妙处在于把「模型学不到的稳定规律」和「业务知道的临时信息」分离。模型专注学序列行为业务系数专注表达大促计划、流量投放这类没法写进特征的事件。我的项目习惯是每次大促结束把今年的实际弹性系数归档下一个大促前用加权平均更新 promo_coef。做销量预测这几年我最大的教训就是别迷信单个模型的精度指标上线前的滚动回测和业务后处理往往决定了模型能不能真正被采销用起来。模型跑通只是开始和业务对齐误差口径、设计降级方案、记录每次大促的系数修正这些琐碎工作才是预测项目真正值钱的部分。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →