ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

EGES召回算法实战:从Graph Embedding到Side Information的冷启动突围

2026/9/7 12:36:27 拓冰建站 浏览量
EGES召回算法实战:从Graph Embedding到Side Information的冷启动突围 最近和一个做电商推荐的朋友聊天他跟我吐槽团队的精排模型越做越精细CTR指标也一直在涨可用户却反馈翻来覆去都是那几样东西。后来一查才发现问题出在召回环节——新上架的商品根本没被召回出来精排模型再准也白搭。他用的一直是item2vec我问他怎么不试试EGES。EGESEnhanced Graph Embedding with Side Information是阿里在KDD 2018发表的经典召回算法全称有点长但核心思想一句话就能说清在Graph Embedding的基础上把物品的类目、品牌、价格带等信息也一起学进向量里。这篇文章我想把EGES从原理到落地完整串一遍内容偏实战适合正在做推荐召回、或者准备从协同过滤/向量召回往图嵌入方向迁移的工程师参考。这篇不打算写成一个纯论文解读。我会先聊聊召回链路到底卡在哪然后把EGES的模型结构拆开讲最后重点说训练数据准备和线上部署这些论文里几乎不会写的细节。如果你只是听说过EGES这个名字想知道它凭什么比DeepWalk强或者你已经打算上EGES但不确定坑在哪里这篇文章应该都能给你一些参考。1. 这模型解决了什么问题召回链路最怕精排再准也救不了召回1.1 先看清楚召回在整个推荐链路中的位置推荐系统从用户请求到最终展示通常要经过召回、粗排、精排、重排这几个阶段。召回负责从全量商品池可能是千万甚至亿级里快速捞出几百个候选粗排和精排再在这一两百个候选上做精细化打分最后重排处理多样性、频控、商业化等约束。很多人容易把精力全放在精排模型上因为精排的AUC、GAUC这些指标看起来最直观迭代起来也最有成就感。但精排模型有一个硬约束它只能对喂给它的候选集做排序。如果某个商品压根没进候选集精排模型就算觉得它再适合用户也无能为力。这就像高考阅卷——阅卷老师水平再高考生答题卡如果没被收上来照样是零分。所以召回这件事本质上是在解决大海捞针的问题如何在极低的时延预算下线上通常要求几十毫秒内完成从海量商品中捞出一批有可能被用户喜欢的商品。它不要求单路召回做到精准但要求覆盖面广、速度快、多路互补。1.2 为什么Embedding召回成了主流方案召回的主流方案大致可以分成几类基于规则和热度的比如销量榜、新品榜、基于协同过滤的ItemCF、UserCF、基于向量的item2vec、双塔、Graph Embedding等。基于热度的召回简单有效但它有个天生的毛病头部效应太严重。热门商品永远排在最前面长尾商品几乎没有曝光机会。基于协同过滤的ItemCF虽然能利用用户行为发现看了A的人也看了B这类关系但遇到完全没有行为记录的新商品ItemCF同样无从下手。向量召回之所以成为主流是因为它把所有商品映射到一个低维稠密向量空间然后用向量相似度来衡量商品之间的语义距离。它的好处是泛化能力强。只要两个商品在行为序列上频繁共现它们的向量就会慢慢靠近不需要显式规则。支持多路召回。可以训练多组向量每组向量代表一个业务视角召回时按权重合并。检索效率高。配合FAISS、HNSW这类ANN近似最近邻索引可以做到亿级商品上的毫秒级检索。但泛化能力强这个优点在Graph Embedding类方法里也有边界——如果一个商品在行为图上几乎没被游走覆盖到它依然很难得到有意义的向量。EGES就是冲着这个边界去的。2. 技术演进路径item2vec、DeepWalk 与 EGES 的一步之遥2.1 item2vec把Word2Vec搬到商品序列上如果你是做推荐的老兵对item2vec应该不陌生。它的思路非常简单把用户的行为序列当成一个句子序列里的每个商品当成词然后直接套Word2Vec的Skip-Gram训练方式让共现过的商品在向量空间里互相靠近。我最早用item2vec的时候也觉得效果挺惊艳同类商品、替代商品确实能聚到一起。但用久了就发现两个问题第一它只用到了行为序列这一种信号。两个商品就算类目完全不一样只要经常出现在同一条行为序列里它们的向量就会很接近。这本身不一定是坏事但容易学到一些伪关联。比如用户先看了手机又看了手机壳再看了充电宝item2vec会把手机、手机壳、充电宝拉得很近这没问题可如果这段序列里混入了一个薯片item2vec也会把薯片和手机拉近这就有点奇怪了。第二稀疏和冷启动问题。一个新上架的商品只有极少数用户浏览过它在序列里出现的次数很少学出来的向量基本就是初始化时的随机结果没有任何语义信息。2.2 DeepWalk和Node2Vec用图结构替代线性序列item2vec的问题在于它把用户行为理解成前后邻居的关系但真实的行为关系更像一张图商品A被用户1浏览过也被用户2浏览过用户2还浏览过商品B用户3浏览过商品B和商品C。这种三角关系用线性序列很难刻画清楚。DeepWalk的做法是先把用户行为构建成一张商品图节点是商品边是商品之间的共现关系然后在这张图上做随机游走生成大量游走序列再拿这些序列去训练Word2Vec。这样每个商品不再只和它前后相邻的商品发生关系而是能通过图的连边关系传递信息。Node2Vec在此基础上加了BFS和DFS的倾向控制让游走过程可以更偏向局部探索或全局探索。如果你对商品的近邻关系更感兴趣可以调高p值如果更想看整个图的结构就调低p值。图嵌入确实比纯item2vec能学到更丰富的结构信息但它依然有一个致命伤图中每个节点都需要有足够的度即足够的连边才能学好。如果一个商品只出现了一两次它在图上就是个孤立点随机游走几乎不会经过它最后学出来的向量依然接近随机。2.3 EGES为什么比它们多走了一步EGES的核心创新就是在图嵌入的基础上引入了Side Information附带信息也就是商品的类目、品牌、店铺、价格带等属性。它把这些属性也映射成向量和商品本身的向量一起加权融合最终得到商品的表示。很多人第一次听这个思路会觉得这不就是把特征拼进模型吗听起来简单但真正做起来有个关键细节EGES不是简单地把side information向量拼在item embedding后面做concatenate而是进行加权平均Weighted Average。为什么用加权平均而不是拼接因为拼接会增加向量维度而且在召回的相似度计算阶段高维向量的计算开销会明显变大加权平均则可以在不改变向量维度的情况下把多个信息源融合进同一个表示空间。更重要的是EGES里的权重不是人定的超参数而是模型自动学出来的。模型会自己判断对于某个商品类目信息更重要还是品牌信息更重要。比如一个iPhone 15 Pro Max手机壳可能品牌信息权重很高因为手机壳这个类目下面有太多不同品牌的产品品牌能帮助区分品质档次但一个纸巾商品品牌的影响就没那么大反而是类目和价格带更能描述它。2.4 类比理解货架管理员的学习过程为了把EGES的直觉讲清楚我喜欢用货架管理员的例子。假设你是一个大型超市的货架管理员你的任务是给每一件商品安排一个货架位置让经常被一起购买的商品离得近一些。如果你只知道谁经常和谁一起被购买这就是item2vec/DeepWalk的信息来源那当一个新商品上架时你完全不知道该把它放哪因为你不知道它和谁经常一起被购买。但如果你额外知道新商品的类目是手机配件品牌是某知名品牌价格带是50-100元那你就能推断它应该和同类目的其他手机配件放在一起尤其应该和同品牌、同价格带的手机配件放得最近。这就是EGES引入side information的直觉——用属性信息来补充行为信息的不足尤其是行为稀少的场景。这个类比也解释了为什么EGES对冷启动友好一个没有行为的新商品它的item embedding是随机初始化的但它的类目、品牌embedding可以从其他同类商品那里学到合理语义加权平均之后新商品的整体向量就继承了同类目/同品牌商品的语义不至于完全是个随机向量。3. EGES 网络结构拆解加权平均的 side information 是怎样炼成的3.1 模型输入一个item 多个side infoEGES的输入不是单个商品ID而是商品ID 该商品的多个side info。假设我们有商品 v它的side info包括类目 c_v、品牌 b_v、价格带 p_v 等那么每个商品在训练时对应一个商品ID k个特征ID的组合。这里有个工程上的小细节side info不是随便选得越多越好。每个side info都需要建立自己独立的ID映射表映射表的规模要和商品总量匹配。比如类目可能有几千个ID品牌可能有几万个ID价格带可能只有十几个ID。训练时这些ID分别查自己的Embedding表得到各自的向量表示。3.2 GES不带加权所有向量做平均EGES论文里其实先定义了一个更简单的版本叫GESGraph Embedding with Side Information它把所有embedding直接做平均。假设商品 v 有 n 个side info加上商品本身的embedding一共有 n1 个向量每个向量维度都是 d。GES的融合方式是[ H_v \frac{1}{n1} \sum_{s0}^{n} W_v^s ]其中 (W_v^0) 是商品本身的embedding(W_v^1) 到 (W_v^n) 是各个side info的embedding。(H_v) 就是最终用来计算相似度的商品向量。GES的思路就是一碗水端平不管是什么信息都平均对待。这样做当然简单但也很粗糙。比如一个品牌很弱的商品它的品牌embedding可能是没学好的或者偏向随机平均之后反而拉低了整体向量质量。3.3 EGES带加权权重也是学出来的EGES在GES基础上做了改进给每个向量加了一个权重 (a_v^s)然后做加权平均[ H_v \frac{\sum_{s0}^{n} a_v^s W_v^s}{\sum_{s0}^{n} a_v^s} ]分母是为了归一化。但这里有个细节如果 (a_v^s) 直接从模型里输出可能为负数或零这会导致加权平均失去意义。论文的做法是对权重取指数[ H_v \frac{\sum_{s0}^{n} \exp(a_v^s) W_v^s}{\sum_{s0}^{n} \exp(a_v^s)} ]取指数保证权重恒大于零同时让不同权重之间的差距通过指数放大。这个设计很巧妙它既保证了权重的有效性又让模型可以学习到哪个信息更重要。我在实际复现时最初直接用了没有指数约束的版本结果训练出来的权重经常出现负值导致融合向量在某些维度上出现明显的语义偏移。后来加上指数约束效果就稳定多了。这个细节论文里写了但很多人实现的时候容易忽略。3.4 损失函数和训练目标负采样下的Softmax近似EGES的训练目标和其他Embedding类方法一样本质上是在做Skip-Gram的变体对于一个用户行为序列中心商品 v 和上下文商品 u 应该具有相似的向量表示而和随机采样的负样本商品 n 应该不相似。用数学表达就是最小化以下损失[ L -\log \sigma(H_v \cdot H_u) - \sum_{n \in NEG} \log \sigma(-H_v \cdot H_n) ]其中 (\sigma) 是sigmoid函数(NEG) 是负样本集合。这个损失函数其实和Word2Vec里的Negative Sampling几乎一样只是把输入向量换成了EGES融合后的 (H_v)。一个值得注意的点是在EGES里中心商品和上下文商品用的是同一套融合逻辑吗答案是肯定的但我在实现时会把中心商品和上下文商品拆成两套embedding矩阵类似Pv-DM里的Input Vector和Output Vector。这么做的好处是在训练时可以加速负采样计算但需要在最终部署前决定用哪一套。通常是用中心商品侧的embedding作为最终线上使用的向量因为它在预测阶段承担的是被检索的角色。3.5 Side embedding与主embedding的关系需要澄清一点side info的embedding矩阵和商品本身的embedding矩阵是分开训练的但它们在计算 (H_v) 的时候被融合到了一起。这意味着side info embedding天生就带有商品级的语义——因为它们是被商品的行为序列信号训练出来的。举个例子类目embedding在训练中会因为手机这个类目下的商品经常和手机壳类目下的商品共现而让两个类目的向量变得接近。这等于side info embedding自己就学会了一种类目间的转移关系而主商品embedding又通过加权平均享受了这种关系带来的好处。我在调试时有时候会直接把side info embedding拿出来画一下t-SNE经常能看到类目embedding比商品embedding更规整、更紧凑。这也解释了为什么EGES对于长尾商品有天然的恢复能力——即使商品本身行为稀疏它的类目embedding已经从类目层面学到了相对稳定的语义通过融合传递给商品向量。4. 数据与训练细节EGES最容易在看不见的地方翻车4.1 行为序列构造不是把日志拼起来就能用EGES的训练数据不像CV或者NLP那样开箱即用。你拿到的原始日志通常是一个个曝光/点击/购买事件需要经过Serialization序列化的步骤才能变成训练样本。构建行为序列的通用做法按用户ID聚合日志按时间戳排序。设置一个Session切分间隔常见的经验值是30分钟。如果用户两次行为之间的时间间隔超过30分钟就认为属于两个不同的Session。对Session内的行为做过滤比如只保留时长超过3秒的点击暴露了但没有实际停留的点击通常是误触。Session切分是我觉得最容易被忽视的一个细节。如果直接把一个用户一天的所有行为揉成一条序列跨Session的用户意图会被强行绑定。比如用户上午在挑办公椅下午在给女朋友挑生日礼物你把它们揉成一条序列模型会努力学习办公椅和口红之间的关联——这个关联可能确实存在但对绝大部分用户来说是弱信号反而会干扰核心语义的学习。训练序列长度也有限制。我一开始用的序列长度是100训练速度明显偏慢而且梯度更新被长序列稀释得很厉害。后来调成50效果几乎没有变差训练速度快了将近一倍。如果你处理的平台用户行为非常长建议加一个序列长度上限比如50~60超出的部分截断而不是强行塞进模型。4.2 正样本抽取和负采样策略序列构造好之后需要生成训练对。沿用Word2Vec的做法我们会把序列里的每个商品作为中心节点它周围窗口内的商品都作为正样本。窗口大小一般取5~10。窗口太大距离远的商品容易产生弱关联窗口太小学到的都是强邻接关系长距离转移信息传不过去。负采样部分的经验值一般是5~10个负样本对应一个正样本。但负采样策略很重要不能完全随机采样。原因很简单推荐系统里的商品热度分布是极度长尾的。如果完全随机采样负样本里会有大量长尾商品模型很快就能把它们和中心商品区分开导致学习信号变弱。实践中更有效的做法是按商品热度做幂律分布采样让热门商品更容易被采为负样本。这样能迫使模型学会区分两个本身都比较热的商品而不是简单靠热度差异来区分。我在一个实际项目中试过把随机负采样换成基于热度的采样离线Recall100提升了接近4%。代价不大收益却不小。4.3 权重初始化和训练超参EGES的训练超参各家实践不太一样但有些经验值可以借鉴参数经验值说明Embedding维度64~128商品量越大维度可以适当调高超过128后收益递减明显窗口大小5~10按业务判断行为链短的平台可以调小负样本数5~10负样本过多会增大训练开销收益有限学习率0.001~0.01配合Adam优化器使用Batch Size256~1024GPU训练时建议偏大CPU训练时512附近比较稳序列长度上限50超过截断权重初始化这里有个小坑side info embedding的初始化不能全用同样的分布否则模型开始训练时加权平均的结果几乎只受item embedding影响side info的效果要很久才能体现出来。我通常会把item embedding的初始化范围设为[-0.01, 0.01]side info embedding的初始化范围放宽到[-0.05, 0.05]让side info在一开始就有一定的话语权。4.4 训练数据里的脏数据一个容易踩的坑训练数据质量直接决定了EGES效果的上限。我踩过一个印象很深的坑有段时间召回效果突然下滑排查了一圈发现是训练数据里混入了大量同一Session内反复点击同一商品的记录。某些用户会在一个商品详情页里反复进出导致同一商品在一条序列里出现很多次模型疯狂把某些商品和它们自己拉近全局语义反而学岔了。解决方法是加一个去重逻辑同一个Session内如果同一商品连续出现多次只保留一次如果不连续分布但出现次数超过阈值比如5次也要做降频处理。另外对购买行为建议单独赋予更高权重或者通过镜像样本的方式把购买行为在训练中做增强。购买行为比点击行为表达了更强的意图如果不做区分模型很容易被高频点击但低意图的行为主导。4.5 图嵌入阶段序列生成还是直接学EGES论文里讲的流程是先构建商品图再进行随机游走最后用游走序列训练embedding。但在工程实现中我建议可以先跳过显式的图构建和随机游走直接用行为序列训练EGES。原因很简单图构建和随机游走在超大规模商品场景下成本很高而且随机游走参数游走长度、游走次数对最终效果的影响是非线性的调参成本很高。直接用行为序列训练相当于把游走过程中的一跳邻居和二跳邻居统一放在一条序列里让模型自己去学邻居传递关系虽然没有显式图结构那么丰富但在大多数业务场景下已经够用。如果你的团队有现成的图计算平台那按论文流程走当然可以如果没有从行为序列起步完全可行模型的主体结构不用改。5. 把EGES部署到线上向量检索、冷启动与更新策略5.1 从训练结果到线上可检索的向量EGES训练完成后你得到的不只是一个模型文件还有好几张Embedding表。线上要用的主要有两张一张是商品主Embedding表另一张是各类Side info的Embedding表。预测时对一个商品 v你需要取出它的主Embedding以及它的类目、品牌、价格带Embedding然后用训练好的权重做加权平均得到最终的 (H_v)。如果你用的是TensorFlow或者PyTorch可以用一个简单的embedding聚合层做Forward Pass把所有商品一次性算完然后存成向量文件。商品量在百万级别时这个过程通常只需要几分钟到十几分钟。存下来的向量文件需要导入到向量检索引擎里。业界最常用的方案是FAISS配合HNSW索引。以100万商品为例64维向量建索引内存占用大约在几百MB加上HNSW的图结构也就1~2GB的水平单机完全可以扛住。召回时按用户向量做TopK检索返回的候选商品再进入下游精排。5.2 新商品冷启动不用等天级任务可以直接算EGES对新商品的友好在线上落地时才真正体现出来。一个全新的商品如果没有任何用户行为它在主Embedding表里是没有向量的。但你依然可以从它的类目、品牌、价格带信息推断出一个合理的初始向量。具体做法新商品上架时系统根据它的类目和品牌ID去Side info Embedding表里取出对应的类目向量和品牌向量再结合一个随机初始化的主向量通常可以忽略用当前学习到的权重做加权平均得到一个大致的初始向量。这个向量虽然不是用行为数据学的但由于Side info本身聚合了大量同类目/同品牌商品的语义它的初始位置比随机初始化要好得多。我实际测试过直接用类目embedding作为新商品的冷启动向量和用同类目下所有老商品向量平均作为冷启动向量两者在一个小样本评估集上的召回效果非常接近。这说明EGES的Side info确实把类目级别的语义学扎实了。5.3 更新频率和训练窗口天级任务完全够用EGES不像精排模型那样需要分钟级更新。理论上越近的行为越能反映当前趋势但考虑到图嵌入需要积累足够多的共现信号训练窗口太短反而容易引入噪声。我的经验是用过去7~14天的行为数据做天级全量训练每天凌晨跑一次早上上线新向量。这里有个工程上的妥协如果每天全量重训计算资源消耗会很大。一种折中方案是每周做一次全量重训每天对新增行为做增量训练只更新有行为变化的商品向量。增量训练虽然理论上会引入一点偏差但在实际操作中效果和全量重训差距很小尤其是在行为日志量级没有剧烈波动的情况下。5.4 线上效果监控一定要看这几项EGES上线后不能只看推荐业务大盘。有几个指标需要单独盯向量漂移程度。每天对比当天向量和前一天向量的平均余弦距离如果某天漂移量突然变大很可能是训练数据出了脏数据。各Side info权重的分布变化。如果类目权重的平均值突然降低说明模型当前更依赖行为信息可以借此判断是不是行为数据质量下降了。召回链路整体的退化情况。EGES通常只是多路召回中的一路如果它负责的那一路召回率下降需要快速定位是训练数据问题、索引问题还是embedding老化问题。我遇到过最诡异的一次问题线上EGES召回率没降但下游精排的CTR下降明显。后来排查发现是因为商品向量被定期重训后整体分布发生了偏移导致精排模型在训练时见过的那批商品向量分布和线上不一致。解决办法是在精排的特征侧对向量做归一化并在向量更新后触发精排模型的快速增量训练。6. 效果怎么衡量命中率、MRR、MAP与离线评估的陷阱6.1 不要只盯AUC召回模型需要另一套指标很多做精排的同事习惯看AUC但召回模型不适合只用AUC来衡量。AUC衡量的是正样本得分比负样本得分高的概率它关心的是全量排序的质量而召回关心的是目标商品是否出现在前K个候选里。这两个目标并不完全一致——一个模型AUC很高但可能只是大部分正样本排在前面具体的某个用户真正想要的那个商品掉出了Top100对召回业务来说就是失败。我习惯同时看四个指标Hit RateK命中率、PrecisionK精确率、RecallK召回率、MRR平均倒数排名偶尔参考MAP平均精度。6.2 指标的定义和适用场景指标公式/定义看什么Hit RateK测试集里有多少用户的目标商品出现在模型推荐的前K个中最直接的召回效果K通常取50或100PrecisionK推荐TopK中命中的比例衡量推荐列表的纯度适合曝光受限的场景RecallK命中的目标商品数 / 该用户所有待预测商品数注意这里的Recall和推荐链路里说的召回率含义略有不同它衡量的是模型对用户兴趣的覆盖能力MRR第一个正确结果的倒数排名的平均值对第一位是否命中更敏感适合强意图场景MAP所有正确结果在推荐列表中的位置综合评估更全面但计算稍微繁琐在电商场景我通常会把Hit Rate100作为主指标因为召回候选集一般是几百这个量级只要目标商品在Top100里精排还有机会把它排上来。如果Hit Rate100都不达标那基本可以直接判断模型效果不行。6.3 离线评估的虚高陷阱在线下评估EGES时一个非常常见的坑是直接把训练序列的下一跳商品作为测试正样本然后发现Hit Rate100高得离谱比如超过60%。但上线后发现线上召回效果远没有这么好。原因是训练数据里存在严重的信息泄漏。如果测试样本是从同一条Session里切出来的模型其实已经见过这个上下文了。它做的只是记忆不是泛化。更合理的做法是用时间切分用过去14天的数据训练用未来3天的真实行为做评估。这样才能贴近真实线上的预测任务。我之前有一个项目离线Hit Rate100是52%看着很漂亮上线后实际只有21%。后来改成时间切分评估离线数据回落到28%虽然数字不好看了但和线上表现基本一致。从那以后我评估embedding类模型再也没用过随机切分。6.4 Side info的消融实验一定要做EGES引入side info的核心优势是冷启动和稀疏场景但并不是所有业务都适合无脑堆side info。我建议在训练时做一组消融实验只用自己的商品ID训练一组向量用商品ID类目训练一组用商品ID类目品牌训练一组在同一个测试集上对比。有一类业务场景如果商品总量不是特别大比如几千个且用户行为足够稠密side info的增益会非常有限甚至因为参数量增加而略微下降。但一旦商品量到了百万级且新商品比例高带side info的版本优势就会非常明显。这个结论不亲自跑实验光看论文是很难体会到的。另一个消融实验是把加权平均改成简单平均来对比。我在多个数据集上测试加权平均通常比简单平均高3~8%的Hit Rate100但个别场景下差距确实不大。如果你时间紧可以先跑GES简单平均版本把链路跑通了再升级到EGES。6.5 线上A/B测试的切分方式最后说一嘴线上评估。EGES的A/B测试建议不要用用户维度切分而是用商品维度切分。因为EGES会影响整个商品向量的分布如果同一用户在不同的流量桶里看到不同版本的推荐结果会干扰很多下游指标。按商品维度切分让两个桶的商品集合不重叠评估更干净。切分比例我一般用1:9或2:8小桶为实验组。跑3~5天观察Hit Rate、人均曝光商品数、人均点击商品数这些指标。如果实验组在Hit Rate上有提升且人均点击没有下降基本可以判断模型正向。最后聊点我实际用下来的感受EGES不是一个特别新的模型但它是我见过性价比最高的召回模型之一。它的代码实现不复杂训练数据准备比双塔模型更简单效果在绝大多数电商、内容推荐场景下都比item2vec和DeepWalk有明显的提升。尤其是在商品冷启动和新品召回这一块side info加权平均的设计确实解决了一个很实际的问题。我个人的建议是如果你还在用item2vec直接试试EGES不会有太高的改造成本如果你已经在用DeepWalk加上side info也只需要在训练前改一下样本构造逻辑。整体收益是正的但别指望它解决所有召回问题——多路召回的组合里EGES最适合担当兴趣探索和长尾召回的角色热门商品的精准召回还是交给热度模型和精排去干更靠谱。