想象你走进一座十万平米的超级商场:三万多个品牌、几十万件商品摆满货架。如果要求你每个柜台都逛一遍再决定买什么,就算逛三天三夜也逛不完。这时商场门口的老导购出现了,她只问了两句话——“想找什么?”“大概什么价位?”——然后直接带你上了三楼,拐进右手边第五排。你还没来得及看清全貌,就已经站在最可能合适的几个货架前。这个过程,就是推荐系统的召回(Recall):在几十万件商品里,用极短的时间圈出最值得看的几百件。 但光圈出来还不够,导购还要帮你从这几百件里挑出最合适的三五件,那是“精排”的活。召回负责的是“海选”,精排负责“决赛”。本篇文章会把“海选”这件事拆开揉碎:用什么策略圈选(多路召回)、怎么建索引才能查得快(倒排索引)、怎么按“气质”找人带路(向量检索)、怎么衡量导购带路带得好不好(评估指标),以及新店铺没口碑时怎么办(冷启动)、推荐单会不会全是同一款(多样性)。
给你一句话:召回是推荐系统的“第一道漏斗”,决定候选集的天花板——对 AI 从业者来说,掌握召回策略与工程实现,是搭建任何推荐系统、提升业务指标的基本功。
目录
可视化演示
展示多路召回通道汇聚候选集、双塔模型向量检索与多样性保障(MMR/DPP),理解如何在百万级商品库中毫秒级筛选高质量候选并为精排阶段供数。支持三种模式切换与播放暂停。
本节配有交互动效(进入视口后自动加载,约 3KB)。
多路召回 协同/内容/热门/地理 通道融合 多角度覆盖候选,提升覆盖率与多样性。
召回概念:商场门口的导购
还记得前言里的老导购吗?她并没有把商场里几十万件商品挨个拿给你看,而是问两句就带你直奔三楼。推荐系统里的召回干的就是这件“第一件事”:在商品库里快速圈出一个“值得看”的子集,把几十万件压缩到几百件。这一步最重要的不是“选得准”,而是“选得快、兜得住”——先把最有可能让你掏钱的商品一股脑捞进来。
为什么必须“压缩”?因为接下来的精排阶段要上复杂模型,逐个给商品精细打分。这种模型在几百件上跑只要几毫秒,放到几十万件上,一次请求就要算好几秒,用户早没耐心了。于是系统把整个推荐做成漏斗:召回以极低成本筛出候选集(比如 500 件),精排再打分排到 Top20,最后重排出屏。漏斗的第一层如果漏了,后面再聪明也救不回来。
召回的好差先看一个词——召回率:在全部“真正相关”的商品里,召回器捞上来多少。
所以说,召回率就是导购“有没有漏掉你真正想要的那件商品”的概率。老导购再熟练,也不可能记住全部商品;召回层也一样,它会用能接受的候选规模尽量把好东西都兜进来。漏得太多,后面精排再强也无力回天——就像导购一开始就带错了楼层,后面怎么逛都是白费。
- R 集合全部“真正相关”商品的集合,比如你最后加购、收藏或购买的商品。
- S 集合召回层输出的候选商品集合,比如多路召回合并去重后的 500 件。
- 交集 R∩S既在真实相关集合里、又被召回器抓住的“命中商品”。
- 比值 |R∩S| / |R|命中数占全部相关数的比例。若等于 1,说明一件都没漏。
多路召回:多支小分队分头出击
只靠一个导购,记性再好也有限。但商场里还有很多辅助信息:门口的大屏排行榜、老顾客留言墙、常客档案、楼层导视图等等。聪明的导购会把这些渠道全用上——这就是多路召回的思想:单路策略只覆盖一种视角,会漏掉大量合理候选;于是系统同时派出多支“小分队”,每路从一个角度捞商品,最后合并。
各路小分队各有专长:热门召回把销量、点击最高的商品捞进来,相当于门口排行榜,稳妥但千篇一律;协同召回根据“和你像的人喜欢什么”来推荐,相当于老顾客留言墙,有惊喜;语义召回按商品和用户表征向量的相似度捞,相当于导购靠“气质”判断;还有类目召回、价格带召回等等。每路都是一个独立通道(Channel),互不干扰、并行执行。
多支队伍回来后要定个统一标准——所以各路得分需要加权融合成一个总分,再按总分取 TopK:
多路召回就像多支导购小分队各司其职:排行榜分队、口碑分队、气质分队……它们同时出发,回来把推荐单合并、去重、排序,最后由精排接手。正因为每路看世界的角度不同,候选集的天花板才能被撑高,这比任何单打独斗都稳妥。
- 第 k 路得分 r_k(u,i)第 k 路召回机制给“用户 u 和商品 i”的打分,比如热门度、协同相似度。
- 权重 w_k每路结果的重要程度,热门路可能给 0.3,协同路给 0.4。
- 加权求和把 K 路得分乘上各自权重再相加,得到融合总分。
- 取 TopK按融合总分从高到低排序,截取前 K 个作为最终候选集。
倒排索引:电梯口的楼层导视图
商场每层电梯口都有一块“导视图”:数码在 3 楼、运动在 4 楼、家居在 5 楼。没有它,你要找个“无线耳机”得挨个柜台问——这就是正排扫描:查每件商品是什么、再判断匹配不匹配;商品库一上百万,一次查询要遍历全部商品,慢得离谱。倒排索引反着来:先建一本“词 → 商品列表”的词典,就像导视图直接告诉你“耳机 → 3 楼第 5 排第 2 列”。
工程实现上,倒排索引的本质是一个哈希表:建索引时把每件商品的标题、类目、品牌等字段切词,把每个“词项”作为 key,把含有这个词的商品 ID 集合作为 value。查询时给定关键词,直接 O(1) 取出整个“倒排链”——一条存着所有相关商品 ID 的链表或数组。比如搜“无线”得到 [id3, id9, id21],搜“耳机”得到 [id3, id9],求交集就拿到同时命中的商品。
但“无线耳机”四个字谁更关键?如果所有商品都叫“商品”,这个词就毫无区分度。于是要给词项加权,常用的是 TF-IDF:
倒排索引就是商场里那本“导视图”:关键词一翻,直接锁到具体楼层和货架,不用挨家挨户问。它的优点是快、可扩展、完全可解释——缺点是只能做“字面”匹配,用户说“轻便”你索引里只有“轻量”,就漏了。所以商场里还要有那位靠气质认人的老店员,这就是向量检索。
- 词频 tf_{t,d}词 t 在商品 d 的标题、类目等文本中出现的次数。
- 文档频率 df_t整个商品库里有多少件商品包含词 t。
- N / df_t文档总数除以含词文档数,词越稀有,这个比值越大。
- 取对数 log把稀缺度压到温和区间,防止生僻词喧宾夺主。
向量检索:眼睛毒的老店员
有些顾客说不清型号,只描述:“我之前买的那款,挺轻的、白色的、朋友说性价比高。”这时老店员眼睛一眯,凭“感觉”直接带你到某个货架。这里的“感觉”,就是向量:把用户和商品都映射到一个多维空间里,让“气质相近”的向量彼此靠近。
怎么把商品变成向量?用 Embedding(嵌入)技术:通过 Item2Vec、双塔模型等方法,把每个商品学成一个固定长度的向量(比如 128 维),用户的兴趣也变成一个向量。向量的每个维度虽然难以用人话解释,但它的几何位置蕴含语义——“轻便”和“便携”的商品在空间里挨得很近,于是用户说“轻便”,系统能找到“便携”的新品。
有了向量,怎么判断“像不像”?最常用的是余弦相似度,看两个向量的夹角大小:
老店员再厉害,也不可能和全商场的人一一握过手。于是实战中要用 ANN(近似最近邻) 索引,比如 HNSW、IVF,配合 Faiss 这样的向量检索库,在毫秒级从百万向量里找出最近似的几百个。向量检索和倒排索引并不是二选一——倒排管“字面精确匹配”,向量管“语义模糊匹配”,两者像导视图和老店员一样互补,组成多路召回里最常用的两路。
- 向量 q用来表示用户当前兴趣的高维向量。
- 向量 d用来表示某件商品的高维向量。
- 点积 q·d两个向量各维度乘积之和,衡量“同方向”程度。
- 除以模长之积把结果标准化到 [-1, 1],只比方向不比长度。
召回评估:用一把尺子量导购
导购带路带得好不好,顾客心里有杆秤:一是她有没有漏掉你想要的(召回率),二是她有没有带你瞎逛一堆没用的(精确率),三是她带路快不快(延迟)。召回层作为漏斗第一层,评估一定要“宽严并济”:太严会漏、太宽会让精排淹没在学习成本里。
精确率关注“捞上来的东西有多少靠谱”,但召回层不必追求高精确——因为后面还有精排把关;真正要盯住的是召回率。两边一综合考虑,业界常用 F1 分数做单值指标:
除了精度,工程上更要盯效率指标:单次请求的 p99 延迟、每秒查询数 QPS、候选集大小。离线先看召回率与候选规模;上线后还要做 A/B 实验,看线上点击率、转化率有没有真的提升。评价一把尺子始终是“先不漏,后不快,再性价比”——就像商场考核导购,既要看顾客满意度,也要看她一天能接多少客人。
- 精确率 P召回结果里真正相关的比例,看“捞上来的货干不干净”。
- 召回率 R全部相关商品里被捞上来的比例,看“有没有漏货”。
- 分母 P+R两项相加,使调和平均对“偏科”更敏感。
- 分子 2×P×R两倍乘积做调和平均,任何一项为 0,F1 就为 0。
冷启动:新开张的店铺没人带路
商场里新开了一家店,导购不认识、顾客没听过、留言墙上没有一条口碑。大数据的方法全部失灵——这就是冷启动。新产品、新用户一上来没有任何行为记录,协同召回、用户画像全都不好使。如果系统直接放弃它,新品永远没有曝光,也就永远积累不了数据,形成死循环。
破局思路是“看招牌、看装修、先给点机会”——用内容特征兜底:类目、品牌、价格带、颜色、材质等属性,就像看店招牌就知道卖什么;再配合流行度试探:把新品插进一些热门候选旁边,给一点探索曝光。统计上还可以用贝叶斯平滑,避免只凭几条数据就轻率打分:
冷启动的本质是“在没数据的条件下借力”:内容特征给骨架、流行度兜底给曝光、贝叶斯平滑防止误判。系统还要有探索策略,比如 \epsilon-greedy、UCB,给新品一定的随机曝光机会,观察反应后再逐步加大或减少流量。新店想活下来,得先让顾客进店——召回层就是那扇让新品“被看见”的门。
- 样本均值 r̄已观测到的少量行为计算出的平均得分,比如 2 个点击、1 个下单。
- 观测次数 n新品目前积累的行为样本量。
- 先验均值 C同类目商品的全局平均分,代表“行业正常水平”。
- 先验强度 m认为行业均值有多大说服力,m 越大越保守。
多样性:别让推荐单全是同一款
就算导购知道你偏爱白色,她也不会端来十个白色马克杯给你选——那会让人审美疲劳。优秀的导购会在“你喜欢的”和“有点不一样的”之间找平衡。推荐系统同样面临这个矛盾:相关度最高的商品往往高度同质,全部塞给用户反而降低体验,这就是多样性问题。
第一板斧是去重打散:同一品牌、同款颜色、同一商家来源的商品不能扎堆;按类目、商家做“轮询式”均匀分布,保证前十屏里不会有五个一模一样的枕头。第二板斧是来源均衡:不把所有候选都押在某一路召回上,热门路、语义路、协同路的入选比例设上限,防止一队独大。更进一步可以用 MMR 算法在“相关性”和“冗余度”之间做显式权衡:
多样性保障放在召回层,能让候选集本身“花色齐全”,给精排留出足够的腾挪空间;如果召回层全是同质商品,精排再打散也巧妇难为无米之炊。好导购的终极大招不是把所有你喜欢的东西堆到你面前,而是让你在熟悉的舒适区旁边,总能撞见一点点新惊喜——长尾商品也因此有了被看见的机会。
- 候选集 C 与已选集 SC 是召回来的全部候选,S 是已经决定加入推荐名单的商品。
- 相关性 rel(i)商品 i 与用户兴趣的匹配度。
- 最大相似度惩罚商品 i 与已选中商品里最像的那个的相似度,越大越冗余。
- 权衡系数 λλ 大更看重相关,λ 小更看重多样。
AI 应用场景
电商大促:双 11 的千万流量洪峰
阶段:召回层(多路召回 + 倒排索引)
解决的问题:大促期间商品库千万级、流量是平峰十倍以上,精排模型算不过来;传统做法直接全量精排会导致接口超时、用户体验崩盘。AI 召回层用多路召回 + 倒排索引把候选从千万压缩到数百,扛住超高 QPS。
具体案例:某电商平台在双 11 前夜,搜索“降噪耳机”时召回层并行跑四路——倒排索引命中标题词、语义向量召回“同气质”耳机、热门榜兜底、协同过滤补个性化,四路候选合并去重后 500 件进入精排,整体 p99 延迟压在 80ms 以内。
短视频信息流:3 秒抓住用户情绪
阶段:召回层(向量检索 + 冷启动探索)
解决的问题:短视频平台每天新增数百万条内容,用户口味变化极快;纯关键词匹配无法理解视频的“情绪风格”,新视频又没有任何互动数据。向量召回解决语义理解,冷启动探索让新视频获得初始曝光。
具体案例:某短视频 App 对用户实时兴趣向量与视频向量做 ANN 近邻检索,向量池里同时混入“新视频池”——每天按 5%~10% 的探索比例插入冷启动候选,用户划到新视频并产生完播后才把它继续纳入主力召回。
本地生活:一公里内的美食雷达
阶段:召回层(地理位置约束 + 多路融合)
解决的问题:外卖平台必须在“用户附近 3 公里”范围内召回可配送店铺,又不能让推荐全是同一家连锁店;传统按销量排序会让小店主彻底失去曝光。多路召回叠加地理围栏,在约束条件下兼顾热门与长尾。
具体案例:某外卖平台把“地理半径召回”作为硬约束路,再并行跑人气路、评分路、品类路、新店探索路,各路融合后取 Top50 进精排;同时限制单一品牌在候选中的占比不超过 20%,保证中小商家也有上桌机会。
Python 演示
# -*- coding: utf-8 -*-"""商品召回演示:倒排索引 + 多路召回 + 召回率评估"""import random class Product: def __init__(self, pid, title, cate, heat): self.pid = pid self.title = title # 标题,用空格分隔的词 self.cate = cate # 类目 self.heat = heat # 热度(如近 7 天销量) self.like = random.random() # 模拟"用户真实喜好"标签 def build_inverted_index(products): """构建倒排索引:词项 -> [商品 ID, ...]""" index = {} for p in products: for word in set(p.title.split(" ")): index.setdefault(word, []).append(p.pid) return index def recall_route(products, index, query_words, user_cate, topk, w_key, w_cate, w_hot): """多路召回:关键词路 + 类目路 + 热销路,加权融合后取 TopK""" scores = {} for w in query_words: for pid in index.get(w, []): scores[pid] = scores.get(pid, 0.0) + w_key for p in products: if p.cate == user_cate: scores[p.pid] = scores.get(p.pid, 0.0) + w_cate for p in products: hot_score = w_hot * (p.heat / 1000.0) scores[p.pid] = scores.get(p.pid, 0.0) + hot_score return sorted(scores, key=scores.get, reverse=True)[:topk] def recall_rate(products, ranked, threshold=0.5): """召回率:真实相关商品中被候选集命中的比例""" p_map = {p.pid: p for p in products} relevant = [p for p in products if p.like > threshold] hit = [pid for pid in ranked if p_map[pid].like > threshold] return len(hit) / len(relevant) def main(): random.seed(42) products = [Product(i, "无线 耳机 降噪", "数码", 950) for i in range(5)] products += [Product(i, "运动 跑鞋 轻量", "运动", 300) for i in range(5, 10)] products += [Product(i, "保温 杯 办公", "家居", 120) for i in range(10, 15)] index = build_inverted_index(products) cands = recall_route(products, index, ["无线", "耳机"], "数码", topk=5, w_key=2.0, w_cate=1.0, w_hot=0.001) print("召回候选:", sorted(cands)) print("召回率: %.2f" % recall_rate(products, cands)) if __name__ == "__main__": main()
class Product:定义商品对象,包含 ID、标题、类目、热度和“真实喜好”标签,like用于模拟 ground truth。for word in set(p.title.split(" ")):标题切词后去重,把每个词作为倒排索引的 key。index.setdefault(word, []).append(p.pid)词不存在就建空列表,再追加商品 ID,构成“词 → 商品链”。for pid in index.get(w, [])关键词路:用查询词直接命中倒排链,O(1) 拿到候选商品。if p.cate == user_cate:类目路:用户偏好类目下的所有商品各加一次分。hot_score = w_hot * (p.heat / 1000.0)热销路:热度归一化后乘权重作为得分,越热分越高。sorted(scores, key=scores.get, reverse=True)[:topk]三路得分合并后按总分降序,截取 TopK 作为召回候选。p_map = {p.pid: p for p in products}建 ID 到商品的映射,方便后续按 ID 查真实喜好。relevant = [p for p in products if p.like > threshold]从全量商品中筛出“真正相关”的集合。len(hit) / len(relevant)命中相关数除以全部相关数,正是正文中的召回率公式。
常见误区
误区“召回是为了不漏商品,那把候选集拉大,召回率不就能到 100% 吗?全量拿出来让精排自己挑呗。”
正解召回率 100% 意味着把所有商品都送进精排,精排模型在千万级商品上跑一次要好几秒,用户根本等不起。召回的本职是在“时间预算”和“覆盖率”之间取平衡:比如把召回率从 70% 提到 90%,候选集可能要从 500 涨到 5000,而精排延迟随之翻倍。真正要看的是“给定延迟预算下,召回率能到多少”。
为何常见:新手容易只盯着离线指标看,忽略了线上系统的时延约束,忘记了漏斗之所以存在,正是因为计算资源有限。
误区“反正多路召回能提覆盖,那我接 20 路、50 路,总能把候选全捞上来吧?”
正解每一路召回都有工程成本和维护成本:索引要更新、特征要对齐、延迟要叠加。更重要的是,路与路之间高度重叠时,边际收益骤减,还会带来大量重复候选,反而稀释精排资源。工业界往往精心挑选 4~8 路、语义差异大的召回通道,并辅以融合权重调优,而不是一味堆数量。
为何常见:多路召回的“多”字容易让人误以为“多多益善”,忽略了系统设计里“少而精”的成本意识。
误区“用户搜'轻便跑鞋',倒排索引里有'轻量跑鞋',应该也能召回吧?反正都是词匹配。”
正解倒排索引本质是词典精确匹配。“轻便”和“轻量”是不同的词项,除非做了同义词扩展,否则互相搜不到。这也是为什么需要向量检索补充语义召回:向量空间里,“轻便”“轻量”“便携”会聚在一起。倒排负责查得准,向量负责想得多。
为何常见:非工程背景的同学容易把“索引”想象成“理解”,忘了索引只是加速查找的数据结构,本身不带语义理解能力。
误区“新品没有行为数据,那就不进召回池,等用户自然点几次再参与推荐吧。”
正解如果新品不进召回池,就永远不会有曝光;没有曝光就没有行为数据,形成死循环。正确的做法是用内容特征兜底(类目、品牌、价格带)、用流行度试探、用探索策略给少量随机曝光,用贝叶斯平滑防止小样本误判,让新品在“低期望值”下进场,逐步积累真实反馈。
为何常见:把“数据驱动”误解为“没数据就什么都不做”,忽略了冷启动本身正是一套主动获取数据的策略。
误区“召回只是筛一遍,排序全看精排,用简单规则跑一跑不就行了?模型放精排。”
正解召回层决定候选集的天花板——如果召回层把用户真正想要的商品漏掉了,精排模型永远没有机会看到它。所以工业界召回层也在引入深度学习:双塔模型、图神经网络协同过滤、多兴趣向量等。它之所以“简单”,不是能力弱,而是要在毫秒级约束下做到“小模型、大覆盖”。
为何常见:把“快速筛选”等同于“不重要”,忽略了漏斗第一层对全链路的天花板效应。
面试题
Q1. 召回层在推荐流程中扮演什么角色?
- A.从海量商品中筛出候选← 正确答案
- B.对候选进行精细排序
- C.生成用户头像昵称
- D.直接完成支付扣款
答案:A。召回是漏斗第一层,负责从全量商品中快速圈出候选集;精细排序是精排的职责,头像昵称和支付都与召回无关。就像导购先带你到正确的楼层,结账是收银员的事。
Q2. 多路召回最核心的动机是什么?
- A.单路召回覆盖面有限← 正确答案
- B.多路召回延迟更低
- C.可以减少模型参数
- D.避免商品信息重复
答案:A。单路召回只看一种视角(如只看热门或只看协同),会漏掉大量合理候选;多路并行是从不同角度互补覆盖。多路召回通常延迟更高、参数不相关,也难以消除重复。
Q3. 倒排索引的本质是什么?
- A.用户 ID 到订单的映射
- B.词项到商品列表的映射← 正确答案
- C.店铺评分到等级的映射
- D.商品到所属楼层的映射
答案:B。倒排索引以“词项”为 key,以包含该词的商品 ID 列表为 value,查询时直接取链。这就像商场的导视图告诉你“耳机在第 3 楼第 5 排”,而不是挨个柜台问。
Q4. HNSW / Faiss 主要服务于哪种召回?
- A.热门商品召回
- B.向量近邻召回← 正确答案
- C.关键词倒排召回
- D.人工运营召回
答案:B。HNSW 是图结构的近似最近邻索引算法,Faiss 是向量检索库,都以向量为输入、返回最近邻。它们解决的是“语义相似”的查找,而非热门榜或关键词匹配。
Q5. 召回率的计算方式是?
- A.召回候选数除以总商品数
- B.命中相关数除以全部相关数← 正确答案
- C.召回候选数除以精排结果数
- D.全部相关数除以候选数
答案:B。召回率 = |R∩S| / |R|,分子是“既相关又被召回”的命中数,分母是全部真正相关商品数。A 把分母换成了全量商品,C、D 引入了精排,都与定义不符。
Q6. 以下哪些是新品冷启动的可行手段?
- A.类目内容特征兜底← 正确答案
- B.用户点击行为统计
- C.商品流行度试探← 正确答案
- D.探索策略随机曝光← 正确答案
答案:ACD。新品没有历史行为,B 的“点击行为统计”无数据可用。A 用类目、品牌内容特征做兜底,C 给出流行度初始分,D 通过探索曝光积累数据,三者正是冷启动的标准打法。
Q7. 多路召回的候选融合方式包括?
- A.线性加权求和← 正确答案
- B.轻量模型统一打分← 正确答案
- C.多路候选直接取并← 正确答案
- D.随机抽签决定最终推荐
答案:ABC。线性加权(Σ w_k r_k)、轻量模型打分、直接取并集再统一处理都是工业界常用的融合方式;随机抽签没有任何相关性保障,会让候选质量失控。注意“取并”仍需后续截断与去重。
Q8. IDF(逆文档频率)衡量的是什么?
- A.词在单篇文档中的出现次数
- B.词在全部文档中的稀有程度← 正确答案
- C.当前文档的文本总长度
- D.用户点击该词条目的次数
答案:B。IDF = log(N/df_t),df_t 是包含词 t 的文档数,包含词 t 的文档越少,IDF 越大、词越稀有。A 是 TF(词频),C、D 与 IDF 定义无关。
Q9. 关于 MMR 多样性算法的说法,正确的是?
- A.同时衡量相关性与冗余度← 正确答案
- B.完全随机选择商品
- C.惩罚与已选商品的相似度← 正确答案
- D.放弃相关度只求多样性
答案:AC。MMR 的目标是“相关度减冗余度”:λ 控制天平,既要 rel(i) 高,又要扣掉与已选集合的最大相似度。B 随机违背目标,D 只偏多样性、放弃相关度,都与 MMR 的权衡思想相悖。
Q10. 评估召回层最应关注的指标组合是?
- A.候选规模与查询时延← 正确答案
- B.页面配色是否协调
- C.客服话术是否亲切
- D.商品拍摄角度
答案:A。召回层既要看召回率、精确率等覆盖指标,也要看 QPS、p99 延迟等效率指标。页面配色、客服话术、拍摄角度属于前端体验和商品运营范畴,与召回层评估无关。