拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Transformer在M5销量预测中的实战:从数据预处理到模型优化

简介这是一份面向时间序列预测学习者和竞赛玩家的Python实战项目围绕M5销量数据利用Transformer架构中的自注意力机制处理多维、多频次的商品销售序列。项目从原始数据预处理开始涉及多通道序列展开、时间戳编码等关键步骤并完整实现了基于Encoder的Transformer预测模型包含数据脚本、模型定义、训练与验证代码可帮助读者理解如何将NLP领域的Transformer迁移至时序预测并掌握MAE/MSE损失设计、Adam优化器调参、时间序列分割验证等竞赛实用技巧。资源共29个文件涵盖12个Python脚本、6个字节码文件、模型权重pth、说明文档md、配置xml以及训练损失曲线png等压缩包整体约47.75MB目录内同时包含训练日志与预测脚本结构清晰便于逐个模块复现。目前已有192人学习下载适合具备一定Python和深度学习基础、希望了解Transformer实战细节的读者。1. 项目整体设计与思路拆解1.1 为什么用Transformer来做M5预测先说结论M5这个比赛数据集非常适合用来验证Transformer在时间序列预测上的能力但同时它也非常“刁钻”会暴露Transformer的不少短板。当初我做这个项目的时候就是想看看去掉RNN那套循环结构之后纯注意力机制在零售销量这种强周期性、强随机性的数据上到底能跑成什么样。M5数据集是沃尔玛公开的销售数据核心任务是预测未来28天内商品、商店、部门三个层级的销量。它有三个非常突出的特点一是序列数量极多有30490条商品-商店组合序列二是历史数据长每条序列有1941天的日销售记录三是数据里混合了强趋势、强季节性、节假日扰动和价格变动等因素。用传统时序模型比如ARIMA跑到这个规模基本是灾难用LSTM序列拉长之后梯度传播是个问题而Transformer的全局注意力机制可以把任意位置的信息直接关联起来天然适合处理长序列中的跨周期依赖比如“今年感恩节前后的销量模式”和“去年感恩节前后的销量模式”之间的关联。当然Transformer不是没有代价。它的标准注意力是O(n²)复杂度n是序列长度输入序列越长计算量越大。M5的序列虽然1941天很长但实际做训练时不会把整个历史一次性塞进模型通常会切成固定长度的窗口比如用过去90天预测未来28天。这样窗口内部用Transformer编码既保留了长程依赖的建模能力又把计算量控制在了合理范围内。1.2 与LSTM、TCN等方案的对比选型我一开始不是直接上Transformer的中间对比过LSTM和TCN时间卷积网络。简单说一下实测感受方案优点在M5上的问题LSTM实现简单时序建模自然长序列下梯度易衰减训练速度慢对并行计算不友好TCN训练快感受野可调内存占用小感受野受卷积核和层数限制跨期依赖的捕捉能力不如注意力机制Transformer全局依赖建模能力强训练可并行计算量大位置编码需要对时间序列专门设计对噪声敏感M5这种零售数据里最值钱的特征恰恰是“很久以前某个特殊事件对销量的影响”比如去年同期的大促、某次天气异常。这些信息距离预测日可能隔着两三百天TCN想覆盖这么长的感受野需要堆很深的层数LSTM则容易在传播过程中把这些遥远的信号磨没。Transformer的注意力机制可以直接计算当前位置与历史任意位置的相关性这种能力对零售预测来说非常契合。另外从工程角度看Transformer的并行训练特性在数据量大时优势明显。M5有30490条序列如果每条序列都单独过LSTM训练效率会比较低下而Transformer可以把固定窗口的数据打包成batch并行计算GPU利用率高不少。1.3 模型整体架构选型Encoder-only还是Encoder-Decoder时间序列预测里Transformer有两种常见用法一种是标准的Encoder-Decoder结构比如Informer、Autoformer这些变体另一种是只用Encoder把预测任务建模成“从历史序列回归未来值”的问题。我做这个项目时选择了Encoder-only方案原因很简单M5的预测目标是未来28天的多点输出本质上是个多步回归问题而不是序列生成问题。Encoder-only的实现方式是把历史窗口输入Encoder取输出层每个位置的特征经过一个全连接映射头直接输出未来28天的预测值。相比Encoder-Decoder它少了自回归解码的累积误差问题训练时也不需要mask技巧实现起来干净利落。缺点是模型对远期时间依赖的建模完全依靠输入窗口内的信息如果窗口长度不够很多长周期信号会丢掉。实测下来输入窗口设为90天约13个星期能覆盖完整季度内的周期性比较合理。模型的整体结构如下输入特征经过embedding映射加入时间位置编码进入多层Transformer Encoder每个block包含多头自注意力和前馈网络中间用LayerNorm和残差连接。Encoder输出经过一个全局池化或直接展平再过两层全连接最后输出维度为28未来28天销量。2. M5数据集核心细节与评估体系2.1 数据组成与层级结构M5数据集由三张表组成calendar.csv包含1969天2011-01-29到2016-04-24的日历信息包括日期、星期、事件种类如Super Bowl、Mothers Day等sell_prices.csv是每个商品在各店铺的每日售价sales_train_validation.csv是训练数据列名是d_1到d_1941行是每个商品-商店组合。需要注意sales_train_validation.csv只给了1941天数据而日历表有1969天最后28天d_1942到d_1969是预测目标比赛时没有放出用来在测试集上做评估。验证集则使用d_1914到d_1941这28天用前1913天做训练。这个划分方式很重要做验证的时候必须严格对齐日历否则容易把未来的信息泄漏到模型里。数据集的层级结构是3个州CA、TX、WI→ 10个商店 → 3个部门食品、家庭、爱好→ 商品。总共有30490个商品-商店组合每个组合一条独立的时间序列。但预测时最终的评估指标不是对每条序列单独计算的而是要求你在多个层级上做聚合预测这就需要考虑层级一致性底层预测值相加后是否和上层预测值一致。2.2 WRMSSE指标的计算逻辑M5的官方评估指标是WRMSSEWeighted Root Mean Squared Scaled Error它是在12个层级上分别计算RMSSE再加权求和。这12个层级包括全量所有商品所有店铺、州层级3个、商店层级10个、部门层级3个、州部门层级9个、商店部门层级30个、单个商品序列30490个并剔除掉一些没有预测价值的组合。RMSSE的计算方式和M4比赛用的MASE有点像核心是先算“朴素预测误差”作为缩放因子。具体来说对每条序列先计算训练期内相邻两天销量差的绝对值均值然后以未来28天预测误差的均方根除以这个缩放因子。公式是RMSSE sqrt(mean((y_actual - y_pred)²) / mean((y_t - y_{t-1})²))其中第二个mean是在训练序列上计算的。分母的直觉是模型好不好不是看绝对误差而是看相比“拿昨天当今天”这种最简单预测提升了多少。如果模型预测的误差平方和比朴素预测还大RMSSE就会大于1说明模型连baseline都不如。WRMSSE则是将12个层级的RMSSE按一定权重加权求和权重是根据每个层级包含的商品销售额占比确定的。实际计算时可参考M5官方提供的evaluation.R代码把它改写为Python版本用numpy和pandas实现。2.3 预测任务定义与数据划分预测目标是未来28天即d_1942到d_1969。验证集是d_1914到d_1941训练集是d_1到d_1913。这个划分模拟的是真实业务场景你手上只有截止某一天的历史数据要预测未来一个月。实际项目里我建议先不管官方测试集而是自己从训练数据里再切出一段“本地验证集”比如用d_1886到d_1913作为训练预测d_1914到d_1941。这样在调参时可以快速验证模型效果不需要频繁提交到比赛平台。另外要特别留意M5的销售数据里有大量0值。节假日、促销之前可能有很长的零销量区间尤其是一些冷门商品。处理不当会让模型过度适应全零序列拉低整体指标。3. 数据预处理与特征工程实操3.1 序列切窗与样本构造M5每条序列1941天训练时如果整条序列直接输入显存吃不消而且模型很难学到“近期行为和远期行为”的差异。我的做法是用滑动窗口将长序列切成训练样本窗口长度设为90天输入预测长度28天输出滑动步长可以设为1天或7天。步长为1样本量最大但重叠严重训练较慢步长为7可以大幅减少样本数同时保证每周的周期性能被覆盖到。我实际测试步长设为7时效果几乎没有下降但训练速度快了接近5倍。切窗时要注意训练样本的预测段不能和验证段重叠。比如验证段是d_1914到d_1941那么构造训练样本时最后一个样本的预测段必须落在d_1913之前不能在d_1886到d_1913的序列里切出一个预测段延伸到d_1914之后的样本。这个细节看似简单但一旦写错验证集效果会虚高提交到官方测试却掉线。3.2 特征工程价格与日历特征M5的特征工程远比想象中重要。单纯用销量历史做输入Transformer能学到的信号有限。我把特征分成三大类基础销量特征、外部特征、时间特征。基础销量特征过去90天的销量值这是核心输入以及销售额占比、销量排名等衍生特征。其中销量排名在序列间有可比性放入模型中效果不错。外部特征价格。sell_prices.csv给出了商品在各店铺的日常售价价格变动对销量有直接影响。我把价格做了两种编码一是绝对价格输入给模型二是价格相对变动率即(当前价格 - 前N天最低价) / 前N天最低价。这个相对变动率比绝对价格更能反映促销效应。时间特征星期几、月份、节假日。日历表里有两个关键列event_name_1和event_type_1记录了当天的节假日类型比如宗教节日、文化节日。我将其进行one-hot编码同时计算“距离最近节假日还有几天、已经过去几天”两个特征。后者对零售销量预测帮助极大因为节假日前后几天的销量变化规律往往比节假日当天本身更重要。特征处理的另一个重要点是标准化。Transformer对输入尺度敏感销量数据跨度从0到几百甚至上千直接喂进去会让注意力权重偏向数值大的维度。我的做法是逐条序列做z-score标准化用训练部分销量的均值方差归一化预测的时候再用同样参数还原。注意归一化参数必须在训练段上计算不能包含验证段的统计量否则仍是泄漏。3.3 数据泄漏的三大陷阱数据泄漏是时间序列预测最容易踩的坑我梳理一下M5上最常见的三种第一用未来价格预测过去销量。sell_prices.csv里包含完整时间范围的价格但实际预测时不可能提前知道未来28天的价格走势。训练时如果直接使用销售日后28天的真实价格特征模型会学到“借用未来信息”的作弊路径验证集效果很好但提交后严重下滑。处理方式是把价格特征做时序对齐训练时使用预测窗口之前的已知价格作为特征。第二用全局统计量替代时序统计量。比如对整个训练集计算所有商品销量的均值方差再将标准化后的销量输入模型。这样做的问题是标准化的结果会吸收未来的分布信息因为全局统计量里包含验证段数据。正确做法是逐条序列、按时间截止点计算统计量。第三切窗时未清理重叠区间。步长为1天切窗时相邻两个窗口有89天重叠这会让模型见过“几乎一样的样本”很多次容易过拟合。用步长为7天可以缓解这个问题另一个可选方案是随机采样窗口而不是固定滑动。4. Transformer模型实现与训练细节4.1 位置编码时间序列版Positional Encoding时间序列预测中位置编码是最容易忽略但影响最大的设计之一。NLP里的sinusoidal位置编码针对的是离散token的相对位置而时间序列的输入每个位置本身已经带有时间信息比如星期几、月份。直接用正弦余弦编码模型会很难区分“同一个星期几在不同历史阶段”的差异。我采用的方法是构造一个时间戳特征矩阵对输入窗口内的每个时间点生成一组特征表示——星期几one-hot、月份one-hot、年份趋势归一化的日期偏移量、节假日前后天数。这些特征和销量特征拼接后一起过embedding层。等价于让模型自己学习“不同时间位置应该赋予怎样的先验权重”比固定编码灵活得多。如果你仍然想使用类sinusoidal位置编码也可以但建议在编码中叠加一个“距离当前预测原点的时间差”特征。比如窗口内第i天距预测起始日的天数为(90 - i)将其归一化后拼接到输入里。这个特征能让注意力机制感知到“窗口内哪些位置距离预测更近”在实际项目中效果提升明显。4.2 Encoder层配置与注意力细节我的Encoder配置如下参考了常见时序Transformer的设置并做了调优参数数值说明输入序列长度909天×10周覆盖季节周期特征维度64embedding输出的维度Encoder层数4超过4层在M5上收益不明显注意力头数8多头注意力让模型同时关注不同模式前馈网络隐藏维度128比特征维度大一倍即可Dropout0.1防过拟合实际试验0.1效果较佳注意力计算的核心公式就是标准的softmax(QK^T/√d)V。在M5的实际数据上我观察到注意力权重的分布会自发性地向“去年同期附近”和“最近几周”聚集这验证了Transformer能自动学到周期性依赖。但也正因为如此如果输入窗口不够长比如只给28天注意力机制很容易把权重全压到最近几天上忽略更早的周期模式。预测头的设计上我尝试过两种一是取Encoder最后一个位置的输出接全连接层二是对所有位置输出做全局平均池化再接全连接。实测全局平均池化效果更好因为M5的序列较短平均池化能平滑掉局部噪声。4.3 训练细节损失函数、优化器与batch策略损失函数我建议用MSE虽然比赛指标是RMSSE但RMSSE的分母是常数训练集相邻差均值在训练时可以作为常数缩放因子等价于用带权MSE。如果直接优化RMSSE反而更复杂。也可以用Huber Loss降低离群值影响我测试下来MSE和Huber在M5上差异不大MSE收敛略快。优化器用AdamW学习率建议设1e-3并配合余弦退火调度器。batch size从64到256都可以取决于显存。我之前用的RTX 3090batch size设为128时训练一轮32万样本大约耗时3分钟共训练20个epoch。有一点值得单独提出M5的序列长度不一但切窗后所有样本长度一致不需要padding。这让Transformer的训练特别干净没有NLP里常见的pad_mask等额外处理。4.4 WRMSSE的Python实现计算WRMSSE时需要先按层级聚合预测值再逐层计算RMSSE并加权。核心代码如下import numpy as np import pandas as pd def rmsse(y_true, y_pred, y_train): # y_true, y_pred: (n_series, horizon) # y_train: 训练段相邻差均值作为缩放因子shape (n_series,) numerator np.mean((y_true - y_pred) ** 2, axis1) denominator np.mean(y_train ** 2, axis1) # 已提前算好的相邻差 return np.sqrt(numerator / denominator) def weighted_rmsse(y_true_dict, y_pred_dict, train_scale, weights): y_true_dict, y_pred_dict: 每个层级的键如 all, state_CA, store_1 等 weights: 每个层级对应的权重 total 0.0 for key in y_true_dict: r rmsse(y_true_dict[key], y_pred_dict[key], train_scale[key]) total weights.get(key, 1.0) * r return total这里train_scale需要在训练时按层级提前算好是每条序列训练段相邻销量差的均值。计算时要特别注意相邻差必须使用“预测截止日之前”的数据不能用整条序列的全部历史否则验证指标的参考意义会打折扣。5. 常见问题与排查技巧实录5.1 预测值整体偏低尤其零销量序列M5里大量冷门商品长期销量为零模型在训练时看到这些全零序列会倾向把所有序列的预测值都压低导致热门商品也预测不足。我的解决方法是给零销量序列做特殊处理训练时对这些序列的损失函数乘以一个较低的权重比如0.5让模型把更多注意力放在有销售模式的序列上。同时在特征中加入“历史零销量占比”帮助模型区分冷门和热门商品。如果预测结构中仍然出现全局负值因为用了线性输出层需要做clip到0的处理。但要注意不能全局clip而应按层级聚合后再clip避免“底层为负、上层为正”的矛盾。5.2 显存不足或训练速度过慢Transformer的注意力计算在序列长度较长时显存占用很大。如果你把窗口长度设为180天或更大显存会迅速吃紧。我遇到过batch size设为256时直接OOM的情况。排查思路有三个降低batch size、减少注意力头数或特征维度、使用梯度累积。实际中我把batch size降到64配合8步梯度累积等效batch size仍为512且显存占用大幅下降。另一个容易被忽略的问题DataLoader里的num_workers对训练速度影响巨大。M5的样本构造涉及大量pandas操作把num_workers从2提高到8后数据加载不再是训练瓶颈。5.3 验证集效果不错但本地测试集却崩了这是典型的层级一致性问题的表现。M5的评估要求各个层级都计算RMSSE如果你只在单序列层级上优化模型那么聚合到商店、州层级时误差会被放大。为缓解这个问题我引入了层级一致性损失hierarchical consistency loss在训练时把同一batch内所有样本按州做聚合计算聚合后的预测与真实聚合值之间的MSE加到总损失里。这是我在项目中后期加入的一个改进项对WRMSSE的降低有明显帮助。如果不想改损失函数也可以在后处理阶段做层级调和reconciliation用简单的线性回归把底层预测值调整到与上层聚合值一致。M5官方提供了hts包的R代码可以参考它的调和逻辑移植到Python。5.4 训练不收敛或loss剧烈波动如果出现loss在某个数值附近反复震荡优先检查学习率。Transformer对学习率比较敏感太高了容易震荡太低了收敛极慢。我建议先用一个较小的batch64跑5个epoch做学习率扫描找到合适的峰值学习率再全量训练。另一个常见问题是输入特征的尺度不平衡。价格动辄几十销量可能只有0到二位数两者拼接后如果没有做标准化注意力机制会被价格维度主导。务必对所有连续特征做标准化或至少保证每个特征维度均值接近0、方差接近1。6. 总结与实操扩展建议最后分享一点我在这个项目里沉淀下来的实操体会也可以当作后续扩展方向。第一Transformer用在M5这类数据上核心收益是它能捕捉长周期依赖但前提是输入窗口足够长、特征工程足够细。如果你只是简单地把销量塞进模型效果很可能打不过调好参的LightGBM。我在项目初期就用LightGBM跑过一个baselineWRMSSE大概在0.68左右而第一版Transformer只有0.72直到加上价格特征、节假日前后天数和层级一致性损失之后才反超到0.60附近。这说明特征工程和损失函数的设计比模型结构本身对最终结果的影响更大。第二M5预测的未来可以沿着两个方向扩展一是用Informer、Autoformer这些针对长序列优化的Transformer变体它们对M5这种长度和规模的序列可能有更好的效率表现二是引入外部数据比如天气、宏观经济指标甚至社交媒体热度进一步丰富特征体系。不过M5本身的数据已经足够支撑一个有说服力的实验先跑通标准Transformer再逐步改进是更稳妥的路径。第三不要迷信公开的baseline代码。M5有很多开源实现但不少存在数据泄漏或评估指标计算错误的问题。我在项目中踩过“验证集指标虚高但提交后下滑”的坑后来逐个排查消除泄漏才恢复正常。做时序预测每一步都要问自己如果现在是真实业务场景我在预测时刻能拿到哪些信息把这些信息对齐到训练流程里模型才有真正的实用价值。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门