“一起购买的好商品” 推荐模型升级
Source: Woowahan Tech
在 Baedal Minjok(배달의민족)不仅可以进行食物外送,连买菜也可以实现当天送达,你知道吗?
Baemin 的买菜·购物服务除了 Baemin B‑Mart,还入驻了超市、便利店、花店、电子产品等各种卖家,让你可以快速收到各种商品。我们从用户进入服务的那一刻起,到完成购买的整个旅程,提供 个性化推荐、一起浏览的商品推荐、实时趋势排名、购物车推荐、支付前的追加商品推荐 等多种推荐,帮助用户发现最合适的商品。

图 1. “买菜·购物” 服务的多种推荐
本文将分享 基于用户购物车中商品的关联商品推荐 的购物车推荐模型的改进过程。面向熟悉推荐系统和基础机器学习概念的读者,分享我们的试错与改进经验,展示如何超越单纯的嵌入相似度,理解用户 购物车中商品的上下文,并推荐可以一起购买的商品,从而提升推荐性能。

图 2. 购物车推荐
现有模型介绍及不足
最初的购物车推荐模型为了快速落地和实现便利,采用了基于订单数据的 Item2Vec 方法。我们把用户的订单历史视为一段句子,将订单中的商品视为单词(Word),使用 Skip‑gram 方式进行训练,并基于 嵌入相似度 来推荐当前购物车中的商品。
然而在模型运行过程中,我们发现了几项与购物车推荐目标相冲突的问题。
替代品偏向
Item2Vec 模型会把同一订单中一起出现的商品映射到相似的向量空间。于是具有相似购买模式的商品会拥有相似的嵌入。
举例来说,当用户把 牛奶 放入购物车时:
| 期望的推荐 | 实际的推荐 |
|---|---|
麦片、面包 等与牛奶 一起购买 的商品 | 其他品牌的 牛奶 系列商品 |
如果仅依赖嵌入相似度,推荐结果会倾向于同一品类的替代品,导致 推荐多样性大幅下降。跨品类销售(Cross‑selling)需要能够提出多样的补充商品。
缺乏序列上下文
用户将商品放入购物车的顺序蕴含了购买意图。但 Item2Vec 只学习商品的共现关系,无法捕捉顺序信息。
| 用户 | 购物车顺序 | 合适的推荐 |
|---|---|---|
| 1 | [方便面, 五花肉, 生菜] | 烧烤所需的 大蒜 |
| 2 | [五花肉, 生菜, 方便面] | 与方便面搭配的 泡菜 |
Item2Vec 无法区分这些序列差异。因此我们将 理解已放入商品的顺序,并自然地推荐下一个可能的商品 作为模型改进的目标。
考虑购买上下文的推荐模型
要“了解商品在什么上下文中被放入”,不仅要建模 商品之间的关系,还要考虑 放入的顺序。为此我们构建了 两阶段(2‑stage)流水线。
- Stage 1. 基于图的商品·类目嵌入 – 将订单数据结构化为图,使用
Node2Vec生成嵌入。 - Stage 2. 基于上下文的下一个商品预测 – 将 Stage 1 生成的嵌入作为输入,使用
Transformer模型学习购物车序列并预测下一个可能加入的商品。
Stage 1. 基于图的商品·类目嵌入
长尾商品的订单频次低,容易出现数据稀疏问题。Node2Vec 通过 随机游走(Random Walk) 生成序列,能够有效学习结构关联性。
图的定义

图 3. 基于共同购买数据的图示例
节点类型
| 类型 | 说明 |
|---|---|
| 商品节点 | 订单数据中出现的单个商品(如 五花肉、酱料) |
| 类目节点 | 商品所属的类目(如 肉类、蔬菜) |
边 & 权重
- Item‑Item – 商品之间的连接。基于订单数据的 关联规则(Association Rule) 赋予关联度权重,避免热门商品过度偏向。
- Item‑Category – 商品与类目之间的连接。即使是新商品或没有订单的商品,也可以通过类目节点获得嵌入,从而缓解 Cold Start 问题。
序列生成与嵌入学习
在图上执行 加权随机游走(Weighted Random Walk),生成虚拟节点序列。例如:
[韩国产五花肉 500g → "肉类" → 无抗生素猪颈肉 500g → 有机生菜 → 国内产蒜瓣 → "蔬菜" → …]
[Roommo 意大利面 → "意面/意大利面" → 巴里拉意面 → 国内产蒜瓣 → 无糖番茄酱 → "酱料" → …]
将这些序列输入 Node2Vec,学习 商品·类目嵌入。得到的嵌入凝聚了购买模式的关联性,并在 Stage 2 中作为 Transformer 的输入使用。
Stage 2. 基于 Transformer 的序列推荐
把购物车中的商品视为一个序列,分析“已放入的商品”,预测用户最有可能接下来放入的商品。

图 4. 模型架构
训练数据构造
从订单数据中利用购物车顺序构造训练样本:
[ \text{Input: } [i_1, i_2, \dots, i_{t-1}] ;\rightarrow; \text{Target: } i_t ]
输入嵌入
每个商品将 商品嵌入 与 类目嵌入 进行拼接,并加上 位置嵌入:
[ h_i = [e_{\text{item}}(i) ; e_{\text{category}}(i)] + e_{\text{pos}}(i) ]
对于 Cold Start 商品,商品嵌入可能较弱,但通过类目嵌入的补足仍能得到有效表示。
Transformer 上下文学习
- Masked Self‑Attention – 参考 SASRec,使用因果掩码(Causal Masking),保证每个时刻只能看到之前的商品,防止目标信息泄露。
- Feed Forward Network – 对 Self‑Attention 的输出进行非线性变换,学习更丰富的表示。
该结构使得即使是相同的商品组合,只要顺序不同,也能产生不同的推荐。例如 [方便面, 五花肉, 生菜] → 大蒜,而 [五花肉, 生菜, 方便面] → 泡菜,模型会给予较高权重。
训练配置与 Loss 函数
- 多任务学习(Multi‑Task Learning) – 同时预测下一个 商品 与 类目,将两任务的 loss 加权求和。
- Focal Loss – 缓解热门商品与长尾商品之间的类别不平衡,对难样本给予更大关注,强化长尾商品的学习。
实验结果与评估
预测结果对比
下面展示了在相同购物车上下文下,Item2Vec(AS‑IS)和改进模型(TO‑BE)给出的推荐示例。

图 5. 推荐模型预测结果对比
Item2Vec 基于嵌入相似度,倾向于推荐 相同品类 的商品;改进模型则主要提供 考虑上下文的补充商品。
离线评估
我们针对多个卖家(Seller)对比了现有模型(Item2Vec)和改进模型的表现。评估数据使用模型训练期间之后的订单记录,并将 购物车中商品的顺序 作为特征,最后一个商品作为真实标签。
| 卖家类型 | 卖家 | Hit Rate@10(提升幅度) | 类目多样性(平均) |
|---|---|---|---|
| 超市 | 卖家 A | 51% ↑ | 7.04 → 8.13 |
| 卖家 B | 71% ↑ | 7.38 → 8.35 | |
| 卖家 C | 120% ↑ | 8.57 → 8.38 | |
| 便利店 | 卖家 D | 54% ↑ | 3.51 → 4.98 |
| 卖家 E | 40% ↑ | 4.80 → 6.51 | |
| 卖家 F | 87% ↑ | 5.51 → 7.11 |
主要结果
- 与原始
Item2Vec相比,Hit Rate@10 提升了 40 %~120 %。在数据稀疏的长尾卖家(C、F)上提升尤为显著,这归功于 Transformer 的 Self‑Attention 能在少量数据中有效捕捉上下文。 - 大多数卖家的 推荐多样性 均有所提升。模型不再局限于同一类目的相似商品,而是能够提出 多种可能一起购买的商品。卖家 C 虽然在准确率上提升显著,但多样性略有下降,这与其长尾特性有关。
- 超市和便利店两大场景均表现出一致的性能提升,说明模型在不同业务域中都具备良好的鲁棒性。