
今年年初我们做了一次库存预测服务的整体重构核心目标就一句话让仓网里的每个SKU在下一个补货周期到来之前提前知道哪里会缺、缺多少、该从哪个仓调。这背后牵扯的不只是算法还有一套从特征、模型到接口的完整链路。这篇文章就把我们这套商品多维特征提取 时空大模型 实时库存预测接口的设计思路和踩坑过程完整拆开供做供应链、电商中台或时序预测系统的同学参考。之所以叫时空大模型是因为传统库存预测只看单仓的时间序列本质上是一维的我们这次把仓与仓之间的调拨关系、区域消费差异、商品在不同空间的销售节奏全部揉进模型让它同时学到时间和空间两个维度的规律。接口层也重新设计了从原来查一个数变成了拿一段预测 带置信区间 附风险因子对接方采购、补货、大促运营用起来省了很多事。整个项目从立项到上线大概花了四个月前一个半月全砸在特征工程和样本组织上真正调模型和压接口性能只用了不到两个月。这个投入比例我后面会细说很多团队模型跑不出来问题往往不在模型本身而在喂进去的特征脏、乱、不对齐。1. 项目背景与整体设计思路1.1 旧方案为何扛不住业务增长我们之前的库存预测服务是典型的单仓单SKU时间序列方案每个仓库的每个商品单独跑一个模型输入是过去90天的销量输出是未来14天的预测量。单看某个仓、某个SKU效果还行但放到整个仓网里就露馅了。最典型的场景是区域爆品。比如某款商品在华东卖爆了华东仓库存告急但华南仓还有大量库存旧系统只会各自预测看不到互相之间的调拨可能性。结果就是华东这边紧急采购、空运补货华南那边库存滞销、占用资金两头都不划算。再比如季节性商品入秋后北方仓的保暖内衣销量先起来南方仓会滞后两到三周旧模型对这种时间差完全无感等南方销量起来了再去补货早过了最佳窗口。更深层的问题是特征太单薄。旧模型几乎只用销量历史连价格变动、促销计划、天气、节假日这些信息都没接进来。业务侧的同事经常吐槽大促前我们明明已经提报了活动计划系统预测居然一点反应都没有。这话虽然扎心但确实是事实——模型不知道的活动自然预测不出来。1.2 时空大模型带来什么变化这次重构我们换了一条路不再按仓 × SKU拆成几万个独立模型而是把所有仓、所有SKU的数据汇成一个时空样本集让模型自己去学习仓与仓之间的关联、商品与商品之间的相似性以及区域和时间叠加后的复杂规律。空间维度上我们把仓网结构事实化。每个仓有经纬度、覆盖区域、调拨成本仓与仓之间有历史调拨量、运输时效这些信息编码成空间特征后模型就能学到华东缺货可以从华南调这类知识而不需要人工写规则。时间维度上除了销量序列还叠加了促销日历、节假日、季节性、天气等外部变量模型可以捕捉到下雨天某品类外卖需求上升这种细粒度规律。模型骨架用了时空Transformer底层是时序编码器中间加了空间注意力层输出端再接多层感知机做分位数回归。这样既能输出预测均值也能给出10%到90%的分位数也就是置信区间对下游补货决策非常有用。整套模型参数量不到1亿训练和推理成本都可控在大模型这个范畴里算是轻量级选手但效果提升非常明显。1.3 系统架构怎么分层整个系统按数据、特征、模型、服务四层来搭每一层都有独立的存储和职责边界数据层实时接入订单、库存、调拨、价格、促销、天气等十几路数据源落到Kafka再分流到离线数仓和在线特征存储。特征层离线用Spark批处理构造全量特征在线用Flink算实时特征统一落到特征仓库通过特征服务对外提供。模型层训练和推理分开。离线训练用GPU集群跑训练完导出ONNX模型在线推理部署在CPU集群靠模型蒸馏和批处理压性能。接口层统一封装成RESTful接口做鉴权、限流、缓存、降级对外暴露预测查询、批量预测、特征回溯三类能力。这套分层的好处是每一层都能独立扩容、独立优化。比如特征层加了新特征不需要重新部署模型服务模型升级也只需要切换版本不影响数据管道。后面接口出问题、模型要迭代、特征要排查都能各自为战不会动一发而牵全身。2. 多维特征体系预测精度的地基2.1 特征分层与来源盘点多维特征提取是这次项目里最耗时、也最容易被低估的环节。我们最终沉淀下来的一套特征体系大致可以分成四层商品静态特征类目、品牌、价格带、规格、生命周期阶段新品/成熟/衰退、是否季节性商品。这些信息变化慢但决定了商品的基础需求曲线。时间动态特征销量滑动统计7/14/30天均值、标准差、增长率、同比环比、促销标记、节假日倒计时、天气温度降水等。这类特征直接刻画需求的短期波动。空间结构特征仓库覆盖区域的人口/经济指数、仓间调拨时长与成本、商品在不同区域的渗透率差异、区域消费偏好向量。这类特征让模型具备跨仓理解能力。业务计划特征采购计划、在途库存、预售订单、活动提报库存、大促目标销量。这类特征本质上是把业务侧已经知道的信息喂给模型避免重复造轮子。特征来源覆盖了订单域、库存域、商品域、营销域、外部数据五个方向总共接了十几张表。这里要特别提醒一句特征不是越多越好关键是和预测目标的相关性。我们前期做了大量探索性分析比如发现某些天气类特征只对个别品类有效强行加进去反而增加噪音和训练成本这类特征最后都做了过滤。2.2 时间维特征怎么构造才不踩坑时间特征最大的坑是对齐问题。举个例子预测明天某仓的销量你用的过去7天销量均值到底应不应该包含今天如果今天还没结束这个均值就是不完整的直接拿来训练会让模型学到错误模式。我们为此专门做了一套特征时间戳机制每个特征都标注了计算截止时间训练和推理时统一按数据可用时间对齐绝不允许用未来信息。另一个坑是周期性编码。日期特征不能直接传2025-03-18模型学不到规律。我们用了双正弦编码把一年中的第几天和一周中的第几天分别映射到sin/cos值域模型就能自然理解周一和周日差异、季节变换趋势。促销特征也不能简单用0/1布尔值要区分大促、秒杀、满减等不同强度并加上预热期、爆发期、返场期三个阶段。滑动窗口特征的窗口大小也有讲究。快消品用7/14天窗口就够但季节性服装类目需要往前看90天甚至更长。我们最终的做法是分层窗口短窗口捕捉近期趋势长窗口捕捉季节性基准模型自己去学不同窗口的权重。2.3 空间维特征与仓网关系建模空间特征我们下了很大功夫。最开始我们只加了仓库经纬度模型学出来的空间关系非常弱基本等于没加。后来把仓间调拨矩阵也喂进去情况才有所好转。所谓调拨矩阵就是任意两个仓之间过去30天的实际调拨量这个矩阵反映了仓网的实际物流联系比经纬度的抽象距离有用得多。我们还在空间维度上做了区域聚簇。把全国仓网按调拨频率 时效分成七个区域簇同簇内的仓共享部分空间特征。比如华东簇的几个仓在模型看来是可以互相调拨的共同体某仓缺货时模型会参考同簇其他仓的需求变化而不是孤立地看单仓序列。为了把这些关系喂给Transformer我们构造了空间邻接矩阵在注意力层做了mask让模型只能关注到可以调拨的仓。商品在不同空间的消费差异也很关键。同一款饮料在一线城市的销量高峰在晚高峰时段在三四线城市可能集中在中午。我们用区域渗透率向量来刻画这种差异把每个商品在每个区域的历史销量占比做成向量输入模型。这个特征对区域爆品的预测帮助极大华东卖爆、华南还没动这种场景模型能提前感知到趋势传导。2.4 特征质量验证特征造出来之后必须做验证才能进模型。我们内部有个三步走流程第一步是覆盖率检查每个特征在训练样本里缺失率超过5%就要处理要么用均值填充要么直接丢弃。第二步是分布一致性检验对比训练集和线上实时特征分布的差异如果发现某个特征的线上分布和训练时差异过大就要排查是特征口径改了还是业务环境真的变了。第三步是重要性排序我们用SHAP值看每个特征对预测结果的贡献度把贡献度长期垫底的特征清理出模型。这套特征验证流程救了我们很多次。有一次上线后模型预测值突然整体偏高排查了半天最后发现是促销特征的时间戳口径改了导致模型误以为每天都有大促。如果没有分布一致性监控这种问题可能要过一周才能暴露等到发现时库存计划已经跑了几天了。3. 时空大模型的选型与工程化落地3.1 模型选型复盘选型阶段我们对比了好几条路线纯粹升级LSTM/GRU、用LightGBM做大规模特征工程以及我们最终选定的时空Transformer。LightGBM这条路最先被否掉。它虽然训练快、特征友好但很难处理序列数据更没法建模仓与仓之间的空间依赖需要手工构造大量交叉特征特征工程工作量大到不可维护。LSTM类模型倒是能处理序列但对空间关系建模同样薄弱而且训练多个独立模型每仓一个的维护成本太高。最后定的时空Transformer核心结构是Encoder部分用时间卷积加自注意力捕捉序列内部规律中间插入空间注意力层处理仓间关系输出端做分位数回归同时预测多个分位点。选它还有一个重要原因支持所有仓、所有SKU共享一个模型通过embedding区分不同实体这样模型能利用到跨仓跨品类的共性模式冷启动商品也能借力。参数规模控制在8000万左右。这个体量在GPU上训练一个版本大约6小时CPU推理单条样本在20毫秒以内完全满足实时性要求。我们团队内部有过争论要不要上更大的模型最后共识是库存预测这个场景业务收益来自预测准确率和系统稳定性而不是模型参数量的军备竞赛够用就好。3.2 训练样本与数据组织训练样本组织是整个项目里最脏最累的活。我们的样本结构是商品, 仓库, 日期三元组每个样本包含这个商品在这个仓过去90天的特征序列以及未来14天的销量标签。全量样本覆盖了大约50万个SKU × 200个仓 × 730天压缩存储后大概几个TB训练时按时间窗口切分成训练集、验证集、测试集。这里有个关键原则必须强调切分数据时绝对不能随机打乱必须按时间顺序切分。如果随机打乱模型会偷看到未来数据验证集效果虚高上线后立刻现原形。我们按时间切分训练集用前600天验证集用中间60天测试集用最后70天这样评估出的效果才接近真实线上表现。样本权重我们也做了特殊处理。临近预测日期的样本权重更高因为库存预测最重要的是近期要准大促前后的样本单独加权重这类样本虽然少但对业务价值极大。还做了负样本过滤销量长期为零的死SKU和已经下架的商品直接剔除不让它们稀释模型的学习能力。3.3 在线推理性能优化模型训练出来只是第一步真正难的是把推理性能压到接口可用的水平。刚开始我们把原始PyTorch模型直接部署上线单条推理要80毫秒以上批量预测1000个SKU要好几秒完全没法用。第一轮优化是模型导出ONNX并用INT8量化推理耗时直接降到35毫秒。第二轮把模型蒸馏成一个小版本用大模型产出的软标签训练了一个参数量只有原来三分之一的学生模型线上精度损失不到2%推理耗时进一步压到12毫秒。第三轮优化是推理批处理接口层把并发请求攒成batch再送进模型充分利用CPU的并行能力吞吐量提升了两倍多。还有一块优化容易被忽略特征服务缓存。模型推理前要拉取每个SKU的特征向量这个I/O开销占比很大。我们把热SKU的特征缓存到本地内存设置5分钟过期时间命中率能到85%以上。保底兜底是Redis缓存再不行才走特征服务的全量查询。三层缓存叠下来接口的P99延迟稳定在80毫秒以内。4. 实时库存预测接口设计4.1 接口语义与使用方接口设计不能只考虑把模型的输出透传出去要站在调用方的角度想他们真正需要什么。我们梳理下来主要使用方有三类补货系统每天定时调用获取未来7天每个SKU在各个仓的预测销量算补货建议。调拨系统实时触发某个仓库存告急时查询应该从哪个仓调、调多少。运营分析平台人工查询看某商品在某个区域的历史趋势和未来预测辅助决策。这三类场景的接口语义完全不同。补货系统需要批量、全量预测对单条延迟不敏感但对吞吐要求高调拨系统需要实时、单点查询延迟必须极低运营平台查得少但查询维度灵活可能按类目、按区域聚合。我们最终设计了三个接口单点预测接口、批量预测接口、趋势回溯接口分别承接这三类需求。虽然增加了接口数量但每个接口的逻辑都简单清晰反而比一个万能接口更好维护。4.2 请求参数设计要点请求参数的粒度直接决定了接口的灵活度。我们单点预测接口的核心参数如下{ sku_id: 100123456, warehouse_id: WH-2003, forecast_days: 14, scenario: normal, include_risk_factors: true, need_interval: true }sku_id和warehouse_id是必传参数确定预测对象forecast_days控制预测窗口长度取值范围1到30scenario场景参数很关键支持normal、promotion、new_product三种不同场景内部会切换不同的特征模板和模型分支。批量预测接口的参数设计逻辑不太一样它接收一个任务列表最大支持5000组sku, 仓库, 天数组合异步返回任务ID调用方轮询结果。这里有个教训刚开始我们做的是同步批量接口但超过1000组就超时后来改成异步任务模式用户体验好了很多也顺带解决了长任务占连接的问题。调拨场景的请求里还会多一个from_wh_list参数指定候选调出仓模型会把各候选仓的库存和距离信息加权到预测结果里给出从哪里调最合理的建议分数。4.3 响应结构与置信区间响应结构我们反复改了好几版最终定型为{ code: 0, data: { sku_id: 100123456, warehouse_id: WH-2003, forecast_date: 2025-04-01, daily_predictions: [ { date: 2025-04-01, qty_median: 320, qty_p10: 180, qty_p90: 480, confidence: 0.87 } ], risk_factors: { stockout_risk: high, trend: 1.25, contributing_features: { promotion_activity: 0.45, regional_penetration: 0.32, weather_offset: 0.12 } } } }qty_median是预测中位数qty_p10和qty_p90是10%和90%分位数构成一个置信区间。confience是模型自评估的置信度根据历史预测误差分布算出来的比如历史上这类商品预测误差在正负20%以内的比例是87%confidence就是0.87。risk_factors是这次新加的功能也是业务方反馈最香的部分。stockout_risk直接用规则映射如果未来3天预测销量大于当前可用库存就标记为high。trend是未来7天对比过去7天的销量增长倍数方便快速识别爆品。contributing_features是特征贡献度用SHAP值简化后输出的TOP3特征业务同事看到因为大促活动所以预测涨了45%这样的解释比看一堆数字直观太多了。4.4 缓存、降级与限流策略实时接口最怕的就是把压力传导到下游特征服务和模型推理引擎。我们做了两层保护缓存策略上短时间窗口内的相同请求直接命中本地缓存。预测结果有效期设为15分钟因为库存预测对分钟级实时性要求没那么高业务决策至少按小时走。另有一个主动失效机制当特征服务检测到某SKU有新的促销计划变更时会发消息通知接口层主动失效对应缓存保证关键业务变化能立即反映到预测里。降级策略保底。如果模型推理服务异常接口自动降级到规则模式按最近7天销量均值加上固定安全系数默认1.2给出粗粒度预测量保证下游系统不会因为拿不到数据而罢工。规则模式预测精度差一些但总比接口挂掉强。熔断机制也做了连续错误率达到阈值就自动切降级恢复后逐步放量探活。限流按调用方维度做了配额控制。补货系统因为是核心链路配额最高运营分析平台的配额低一些临时商户假如后期接入配额会压得更紧。超限请求直接返回429响应体里带上Retry-After头提醒调用方合理安排频率。5. 接口测试用例设计与上线复盘5.1 功能用例与边界用例设计接口做完之后最怕的就是自测没问题一上线就出事。我们从功能、边界、性能、异常四个维度设计了完整用例集这里挑几个典型的说说。功能用例重点验证参数组合和业务语义。比如scenariopromotion时预测结果应该明显高于normal场景include_risk_factorsfalse时响应里不能出现risk_factors字段缺省scenario参数时默认走normal逻辑。这类用例每个参数和每个参数组合都要覆盖不是只测happy path。边界用例更容易踩坑。forecast_days传0或31要返回参数错误负数SKU编号要有清晰的错误信息批量接口一次传5001组不允许传5000组必须成功空列表批量请求不能直接报500而是返回空任务结果。我们还有一个专门测未来30天预测但该商品已经停产下架的场景模型应该给出趋势下滑的预测而不是生硬报错。5.2 性能与异常用例性能测试我们压过三轮。第一轮是纯模型推理压测单机QPS压到50时P99延迟飙到300毫秒排查发现是特征服务I/O成为瓶颈加了缓存后P99回到80毫秒。第二轮是接口整体压测模拟调用方真实节奏补货系统每小时一次批量、调拨系统随机单点在8核16G的Pod上压到120 QPSP99稳定在95毫秒。第三轮是长稳测试连续跑7天重点观察内存泄漏和连接池耗尽问题还真抓到一个特征服务连接池在慢查询时被占满的bug。异常用例要覆盖各种接口活着但数据不对的情况。特征服务超时了怎么办我们设置了单次特征查询超时200毫秒超时后跳过该特征并用默认值填充保证请求不因为单个特征挂了而失败。Redis缓存不可用时自动降级为本地缓存本地缓存也失效才打最终特征库。下游模型推理引擎返回异常结果如NaN时统一替换为兜底值并记录日志。5.3 上线后遇到的三个真实问题第一个问题是冷启动商品预测偏差大。新上架的商品历史数据不足模型只能靠同类目相似商品的共性模式硬猜效果很差。我们后来单独训练了一个商品embedding相似度匹配模块冷启动商品自动找到最相似的成熟商品用它的特征补全序列预测效果提升明显。第二个问题是节假日效应被过度放大。模型学到节假日销量会涨但对所有商品无差别放大导致部分日用品的预测偏高。后来给节假日特征加了品类维度只有历史节假日有显著波动的品类才启用节假日特征其他品类直接忽略。第三个问题是跨仓调拨特征的滞后性。调拨矩阵是按近30天统计的但调拨动作往往有几天延迟模型看到的可调拨量其实并不是当前真实值。我们改成调拨矩阵和实时在途库存双通道输入同时把调拨在途天数作为单独特征模型变得更敏感欠货场景的预测准确率提高了8个百分点。上线后我们也把测试用例沉淀成了自动化回归集每次模型迭代、特征变更、接口改动都会跑一遍。这套回归集帮我们拦下了很多低级错误比如有一次特征口径调整影响了所有预测结果回归集两个小时内就报警了换成人工排查至少得一天。库存预测接口随便一个预测偏差都可能放大成几百万的库存成本必要的自动化保障值得投入。接口测试用例设计这块我的个人体会是不要只盯接口本身的输入输出要围绕它依赖的下游组件和业务场景来设计用例。特征服务和模型推理各自的异常场景、数据缺失、超时、缓存穿透都要在接口层面验证兜底逻辑是否生效。接口的价值不只是把正确结果传出去更是在异常环境下还能稳定给出可用结果。