上周去某家社区超市结账收银员递给我一张打印小票底部多了一行字“您常买的某品牌鲜牛奶下周有促销搭配全麦面包更划算”。我愣了一下——这家店什么时候开始懂我了后来一问才知道他们用一套上线不到三个月的商品推荐系统把小票、会员卡、促销记录和货架数据全部串了起来。市面上聊推荐系统默认语境全是电商千人千面、点击率、实时会话。但超市这套逻辑完全不同顾客在线下推着购物车逛货架位置本身就是推荐位买单瞬间才是黄金窗口。我结合自己做的“超市智能商品推荐系统设计”项目把这几个月踩过的坑和趟出来的规律整理成文希望能帮到正在做线下零售数据化的朋友。1. 超市推荐和电商推荐的底层逻辑差异先别急着上算法1.1 三个本质不同的购买场景电商推荐的整个链路是搜索—点击—加购—支付每一步都有明确的用户反馈用户在网站上的行为密度非常高一天能产生几十上百次交互。超市不一样顾客进店之后的逛、拿、放回这些动作在没有摄像头和货架级传感器的情况下基本采集不到你手里最干净、最完整的信号只有一张张POS小票——也就是结账那一刻的购物篮。第二个差异是商品生命周期。电商绝大部分商品可以长期挂售算法有充足时间学习。超市里生鲜、季节品、临期品每天都在变土豆白菜保质期就几天一个上架周期内可能根本攒不够算法学习所需的样本量。如果照搬电商那种“积累用户行为——更新模型——再推荐”的闭环等模型学会的时候货架早换了一轮。第三个差异藏在推荐位的物理属性里。线上货架无限长推荐错了成本极低线下货架和动线是有限的顾客要走到你推荐的那个货架前才算数。这意味着超市推荐系统输出的任何结果都必须考虑到物理可达性和当下的购物阶段不是算法算出来就完事的。1.2 从“猜你想要什么”到“帮你把这趟买顺”电商推荐的核心是猜用户当前想要什么点击率是生命线。超市推荐的目标则完全不同业务上你要回答的问题是这位顾客这趟来大概要买哪些品类购物篮里还缺什么把缺的那部分补上客单价自然就上去了。举个很直观的例子顾客明明已经选了牛排购物篮里大概率还缺黑胡椒酱、西蓝花、意面甚至一瓶红酒。这不是猜他想买什么而是帮他完成“这顿饭”的拼图。经典零售故事里的“啤酒与尿布”虽然被营销叙事夸张过但原理是成立的——买尿布的年轻父亲和买啤酒的人群高度重合把啤酒放在尿布附近双方都受益。所以设计系统的第一件事不是选算法而是和目标业务方对齐KPI你到底想提升客单价、消化临期库存、还是提高会员复购这三个目标会导向完全不同的推荐策略我在后面会展开说。顺序搞反了算法再漂亮也没人买单。1.3 谁真的需要这套系统以及谁不需要不是所有超市都需要推荐系统。我见过一些便利店老板拿着几百个SKU的Excel表来找我聊协同过滤说实话没必要那个规模下货架摆放和补货策略带来的收益远大于推荐算法。门店类型SKU规模数据基础推荐方案建议社区便利店几百个单店POS流水暂不需要做好陈列即可中型社区超市几百到几千会员覆盖率低关联规则 规则引擎大型商超/连锁上万会员体系完善多门店召回—排序完整架构分店建模超市到家小程序同上有在线浏览行为可以参照电商思路但注意生鲜差异先对号入座再决定投入多少资源这是我给所有线下零售从业者的第一条建议。2. 整体架构与数据链路从POS小票到推荐结果2.1 数据采集POS小票是主干但别忽略辅助信号超市里能采的数据其实比想象中多按重要性排序大概是POS订单明细、会员档案、促销档期、退货记录、库存与缺货记录。POS明细是绝对的主干它能给出订单号、商品编码、数量、金额、门店、交易时间和会员ID。这中间最关键的一点是订单粒度——同一张小票里的所有商品天然构成一个“购物篮”这是所有后续分析的地基。辅助信号同样不能忽略。会员领了哪些优惠券、核销了没有能反映价格敏感度退货记录能告诉你顾客对哪些品类不满意库存和缺货记录能帮你过滤掉“算法推荐了但库里没有”的尴尬。这里有个典型的坑很多超市的POS系统没有把退货单和原销售单关联起来“买了又退”被当成了真实购买这会严重污染关联规则的统计。我在项目初始化阶段花了整整一周做数据清洗剔除退货、剔除员工内部购买、剔除批发团购单后面模型效果才正常。数据质量决定算法上限这句话在超市场景体现得淋漓尽致。2.2 特征体系商品、顾客、门店三个维度缺一不可推荐特征分三大块商品侧、顾客侧、门店侧。商品侧要梳理类目层级、品牌、规格、价格带、保质期类型、冷链要求、是否促销品、季节标签。这里特别要注意“保质期类型”这个特征——长保商品适合被推荐短保生鲜商品推荐要非常克制人家不会因为你的推荐就多买一堆容易烂的菜。顾客侧依赖会员体系包括RFM最近购买时间、频次、金额、平均篮单价、品类偏好分布、常购时段、价格带偏好。没有会员体系的门店可以把匿名小票按“同一时段、同一收银台、连续单号”做临时会话聚合效果打折但聊胜于无。门店侧特征常被忽略门店面积、商圈类型、周边客群结构、历史销售增速这些决定了同一套模型在不同门店的表现差异。别指望用市中心大店的数据训练一个模型去服务所有门店后面踩坑部分会专门讲这个问题。2.3 离线管道和实时管道的分工我的做法是把系统切成两条管道各干各的。离线管道每天凌晨跑T1任务产出每日推荐候选集、关联规则、商品向量、用户偏好画像服务于小程序首页、电子价签、次日促销推荐这些场景对时效性要求不高。实时管道专门服务收银台结账前的那几秒钟逻辑非常简单但要求响应在几十毫秒内完成。实时管道的推荐不是跑复杂模型而是基于“当前购物篮”的规则匹配把购物篮里的商品编码拿过来去查离线算好的规则表命中哪些关联规则按置信度排序取前三个结果返回。整个链路用到的存储也就一个关系型数据库加一个缓存。把缓存打开热点规则全部提前加载接口P99延迟能压到50毫秒以内——这套组合便宜、可控、维护成本低是我最推荐的中小型超市起步方案。3. 算法选型把关联规则、协同过滤和深度模型放在正确的位置3.1 关联规则Apriori 与 FP-Growth 的取舍关联规则是超市场景最容易出效果、也最好解释的算法。核心概念就三个支持度、置信度、提升度。我拿项目里的真实例子说明。假设有一千张小票其中两百张包含牛奶一百五十张包含面包两者同时出现的有六十张。那么支持度就是60 / 1000 6%代表这个组合在全部订单中出现的比例置信度牛奶→面包是60 / 200 30%代表买了牛奶的顾客里有多少也买了面包提升度是30%除以面包的整体购买比例15%等于2。提升度大于1就说明“牛奶和面包”之间有正向关联大于1.5基本可以放心上推荐位。阈值怎么定数据量大时支持度可以设低一些比如1%数据量小时要设高一些避免偶然共现。置信度建议至少20%到30%低于这个值推荐出去的效果很差。算法实现上Apriori在几千个SKU时还能跑SKU上万后性能下降非常明显因为它要反复扫描数据集生成候选集FP-Growth用树结构压缩事务不生成候选集内存友好我最终选了FP-Growth。但不管用哪个生鲜短保商品都建议直接挡在推荐结果之外。3.2 协同过滤没有评分时如何定义“相似”超市的用户—商品矩阵里没有评分只有“买没买”。解决思路是把购买次数当作隐式反馈构造用户—商品共现矩阵。这个矩阵极其稀疏一个普通会员一年也就几十张小票和电商用户动辄上百次点击完全不是一个密度。所以我强烈建议超市场景以Item-based协同过滤为主User-based只能做辅助。Item-based的思路是先算商品与商品之间的相似度——把每个商品在所有小票里的共现向量当作它的“画像”用余弦相似度计算两两相似度顾客买了A就推荐与A最相似且不在当前购物篮里的TopN。相比User-based它不依赖某个用户的丰富历史新用户只要有一张小票就能推荐实用性高得多。这里有个业务细节计算相似度时要忍住诱惑把矿泉水、百元充值卡这类“人人都会买”的商品从共现向量里剔除否则它们会和所有商品都相似推荐出来毫无信息量变成了变相的热销榜。3.3 浅层模型还是深度模型按数据量决定我的判断标准很简单先看每月有效小票量级。小于十万张关联规则加Item-based协同过滤足够别折腾深度学习几十万到百万张可以在排序层上一个梯度提升树模型比如XGBoost和LightGBM这类效果稳定且好调到千万张以上才需要考虑商品向量化、图神经网络这类更复杂的方案。别迷信深度学习。超市的数据密度远低于互联网深度模型在小数据上很容易过拟合或者把促销期的噪音当成规律学进去。一个清醒的判断是推荐系统的离线指标提升和业务收益之间隔着一条巨大的鸿沟简单模型更容易解释、更容易排查、更容易让业务方信任这就够了。整体框架我采用的是标准的召回—排序结构。召回阶段把三类候选合并关联规则候选、相似商品候选、门店热销补充排序阶段用GBDT对候选打分特征拼接顾客、商品、门店、时间四类信息最后过一道规则过滤把临期品、缺货品、禁止推荐品类全部挡掉。这个结构在超市场景完全够用也方便后续扩展。4. 工程实现从购物篮向量到渠道下发4.1 把购物篮当成“句子”商品向量的训练细节排序模型再好也依赖召回质量。我后来做了升级把每个会员的购物篮按时间排序当成“句子”把商品当成“词”用词向量那套方法训练商品向量。道理很简单——经常出现在同一个购物篮里的商品和经常出现在同一句话里的词一样语义上是相近的。这样算出来的商品相似度比itemCF的共现矩阵平滑得多还能通过向量运算做一些有意思的探索。几个训练细节值得记录。一是购物篮序列构造时同一张小票里商品的先后顺序其实没有意义要随机打乱否则模型会学到假的“顺序信息”二是窗口大小设3到5比较合适太大会把无关商品强行拉近三是负采样时必须剔除矿泉水、抽纸这种超高频率商品否则它们会成为万能的“负样本中心”把向量空间搞坏。构建训练样本时我曾经踩过数据泄漏的坑把当天的退货数据直接拿来当负样本结果模型学会了“识别那些会被退货的商品”上线之后推荐质量奇差。后来把退货数据单独清洗作为过滤规则而不是训练样本才恢复正常。4.2 排序层特征、样本与模型排序模型的作用是把召回来的候选商品按“顾客当下会买”的概率重新打分。我的特征分三组。商品侧有价格、折扣力度、历史转化率顾客侧有该顾客对这个类目的历史购买频率、价格带匹配度、距离上次购买该类目的时间。最后一组特别关键超市有周期购买属性牛奶喝完大概一周米面油大概一个月距离上次购买时间太近就不推荐太远也不必追着推。场景侧特征包括当前时刻、门店编号、当前购物篮已有品类数量。比如晚上七点购物篮里已经躺着一盒鸡蛋排序模型会倾向于把配菜类商品排前面。训练样本定义要小心。正样本是推荐后被加入购物篮或者直接购买的商品负样本是展示了但没买。这里必须等A/B测试闭环跑起来才能拿到真实的曝光数据所以第一个月往往是冷启动模型效果不会太好别慌先把曝光日志埋点埋全。4.3 渠道与时机推荐位不是越多越好推荐结果要分发到具体渠道每个渠道的特性完全不同。渠道时机展示数量适合内容收银台屏幕结账前几秒1-3个凑单型、满减门槛补齐电子价签顾客在货架前1个关联提示当前商品的搭配品到家小程序下单前/次日提醒3-6个生鲜补货、促销预告打印小票结账后1-2个优惠券二维码线下回流时机规则比模型重要。不要顾客一进门就推冷饮他还没逛到饮料区不要顾客刚买完某个品类就推同类目他短时间内不会再买生鲜补货提醒适合在晚上八点后推送第二天的菜。我的经验是一篮子推荐不要超过三个超过三个顾客根本看不过来还会觉得被冒犯。4.4 规则过滤与人工兜底这一层最不智能但往往决定系统能不能上线。无论模型给什么结果都必须过一遍硬规则缺货商品不推、临期商品不推、有合规风险的商品烟酒、处方性质保健品坚决不推、儿童营养品类目不能推荐给没有小孩的顾客。价格带也要兜底——一个习惯买高端有机食品的顾客你不该给他推性价比散装货反之也一样。规则兜底还有一个隐藏好处业务方敢把推荐位交给你。零售店长和采购最怕系统乱推影响库存和毛利有了硬规则他们才愿意把真实曝光数据开放给算法团队形成正向循环。5. 冷启动、效果评估与踩坑实录5.1 三类冷启动的应对方案新商品、新门店、新会员三类冷启动要分开处理。新商品没有历史数据我按价格带和品类把它归入已有商品簇先给一个保守的推荐位积累三十次展示之后再进入正常候选池。新门店没有本地数据先用同商圈、同面积级别的老门店模型做迁移跑两到四周后再用本店数据微调。新会员没有历史记录先用门店热销TopN加时段热销顶上同时在会员注册时加一道“偏好品类勾选”——收集的信息量不大但对冷启动很有帮助。5.2 评估指标怎么定推荐系统上线后我每周看四类指标。直接指标是推荐位的点击、扫码、加入购物篮率生意指标是客单价、购物篮商品数、连带率库存指标是临期品清仓率和滞销品动销率体验指标是退货率和投诉率。只看点击率会骗自己超市推荐的价值必须落到生意指标上。A/B测试建议按门店组或会员ID分桶至少跑两周。两周是线下购买周期的约数——很多家庭的超市大采购是两周一次跑不够一个完整周期结论不可信。我不建议按“线上点击用户和线下到店用户”来分实验组这个口径混在一起根本解释不清楚。5.3 我踩过的几个具体的坑第一个是数据泄漏。训练集里混入当天退货单据模型学会了推荐“要退的商品”。清洗退货数据之后离线指标和在线转化率同步明显提升这算是我印象最深的一次教训。第二个是促销干扰。大促期间所有关联规则都被打折商品主导跑出来一堆“满减凑单”规则促销一结束全失效。解决办法是训练时剔除促销占比过高的样本或者干脆给促销单独建一套规则库。第三个是推荐了顾客刚买过的东西。顾客上周刚拿一箱牛奶这周又给他推牛奶体验极差。后来在排序特征里加入“购买间隔”长保商品设三十天冷静期、短保设七天问题才解决。第四个是忽略门店差异。总店数据训练的模型放在写字楼店表现很差因为两个店的顾客结构完全不一样。最终的方案是至少按大店、社区店、写字楼店分三套模型效果才稳定下来。最后再说句实在话如果你的超市还在用Excel看销售排名别急着上推荐系统。先把POS数据规范化、打通会员体系、把品类和价格带标签补全这些基础工作决定了后面所有算法的上限。我做这个项目最大的体会是线下零售的推荐不是要炫技而是要在一趟购物已经接近尾声的时候恰到好处地递上一个让顾客觉得“这趟没白来”的理由。先把关联规则跑通亲眼看到客单价在涨再考虑模型升级这个顺序不会错。