商品多维特征+时空大模型:库存预测系统重构实践
2026/9/17 2:45:41 网站建设 项目流程

今年年初我们做了一次库存预测服务的整体重构,核心目标就一句话:让仓网里的每个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到30;scenario场景参数很关键,支持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 功能用例与边界用例设计

接口做完之后最怕的就是"自测没问题,一上线就出事"。我们从功能、边界、性能、异常四个维度设计了完整用例集,这里挑几个典型的说说。

功能用例重点验证参数组合和业务语义。比如scenario=promotion时,预测结果应该明显高于normal场景;include_risk_factors=false时,响应里不能出现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 QPS,P99稳定在95毫秒。第三轮是长稳测试,连续跑7天,重点观察内存泄漏和连接池耗尽问题,还真抓到一个特征服务连接池在慢查询时被占满的bug。

异常用例要覆盖各种"接口活着但数据不对"的情况。特征服务超时了怎么办?我们设置了单次特征查询超时200毫秒,超时后跳过该特征并用默认值填充,保证请求不因为单个特征挂了而失败。Redis缓存不可用时自动降级为本地缓存,本地缓存也失效才打最终特征库。下游模型推理引擎返回异常结果(如NaN)时,统一替换为兜底值并记录日志。

5.3 上线后遇到的三个真实问题

第一个问题是冷启动商品预测偏差大。新上架的商品历史数据不足,模型只能靠同类目相似商品的共性模式硬猜,效果很差。我们后来单独训练了一个"商品embedding相似度匹配"模块,冷启动商品自动找到最相似的成熟商品,用它的特征补全序列,预测效果提升明显。

第二个问题是节假日效应被过度放大。模型学到"节假日销量会涨",但对所有商品无差别放大,导致部分日用品的预测偏高。后来给节假日特征加了品类维度,只有历史节假日有显著波动的品类才启用节假日特征,其他品类直接忽略。

第三个问题是跨仓调拨特征的滞后性。调拨矩阵是按近30天统计的,但调拨动作往往有几天延迟,模型看到的"可调拨量"其实并不是当前真实值。我们改成调拨矩阵和实时在途库存双通道输入,同时把调拨在途天数作为单独特征,模型变得更敏感,欠货场景的预测准确率提高了8个百分点。

上线后我们也把测试用例沉淀成了自动化回归集,每次模型迭代、特征变更、接口改动都会跑一遍。这套回归集帮我们拦下了很多低级错误,比如有一次特征口径调整影响了所有预测结果,回归集两个小时内就报警了,换成人工排查至少得一天。库存预测接口随便一个预测偏差都可能放大成几百万的库存成本,必要的自动化保障值得投入。

接口测试用例设计这块,我的个人体会是:不要只盯接口本身的输入输出,要围绕它依赖的下游组件和业务场景来设计用例。特征服务和模型推理各自的异常场景、数据缺失、超时、缓存穿透,都要在接口层面验证兜底逻辑是否生效。接口的价值不只是把正确结果传出去,更是在异常环境下还能稳定给出可用结果。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询