搜索召回的四大主流算法:从倒排索引到多路召回,一篇讲透
10 毫秒。
这是淘宝、抖音、Google 这类系统,留给"召回"这一步的全部时间预算。
在这 10 毫秒里,它要做一件听起来不可能的事:从几亿甚至上百亿条候选里,挑出几百到几千条"可能相关"的结果,交给后面的排序模型去精挑细选。
我换个比方你就懂了。这就好比让你在 10 毫秒内,从中国 14 亿人里圈出 500 个"最可能买你这款产品"的人。你不可能挨个看一遍——光是遍历一次就要好几秒。所以召回这一步,从来就不是"找得准",而是"找得快、还不能漏"。
很多人对搜索的理解停在"关键词匹配"。但你有没有想过:为什么你搜"番茄炒蛋",能搜到标题写着"西红柿炒鸡蛋"的菜谱?为什么你刚看完一个露营视频,首页立刻给你推帐篷——可你压根没搜过"帐篷"这个词?
这些"魔法",全发生在召回这一层。今天我就带你把召回阶段的四大主流算法一次拆透:倒排索引与 BM25、向量检索(ANN/HNSW/FAISS)、双塔模型、以及把它们串起来的多路召回。读完你会发现,现代搜索的强大,不是靠某一个绝招,而是靠一场精心编排的合奏。
一、召回到底在解一道什么题?
在拆算法之前,我们得先把题目读懂。否则你会困惑:为什么不直接上最聪明的模型,一步到位排出最好的结果?
答案是:算力不允许。
一个成熟的搜索/推荐系统,通常分成三步走:召回 → 粗排 → 精排。这就像企业招聘——简历来了一万份,你不可能让 CTO 每一份都亲自面。先用关键词海选筛到 500 份(召回),再用初级面试官刷到 50 份(粗排),最后才轮到 CTO 终面(精排)。
每一层的候选越来越少,但用的模型越来越重、越来越准。召回是漏斗最宽的入口,它的任务不是排序,而是"圈定范围"。
理解了这一点,后面所有算法的设计取舍就都顺了。召回算法的世界里,有一条铁律:能离线算好的,绝不留到线上算。因为线上只有 10 毫秒,而离线你有一整晚。
记住这句话,它是理解倒排索引、向量索引、双塔模型为什么长成这个样子的总钥匙。现在,我们从最古老、也最不该被低估的那一个开始。
二、倒排索引与 BM25:搜索的地基
先问你一个问题:一本书后面的"索引页",是干嘛用的?
你想查"量子纠缠"在哪几页讲过,不会从第一页翻到最后一页,而是翻到书末的索引,找到"量子纠缠 …… 见 87、142、203 页"。直接跳过去就行。
倒排索引(Inverted Index),干的就是这件事,只不过对象是几亿篇文档。
正排 vs 倒排:一次方向的翻转
正常存文档,是"文档 → 包含哪些词":第 1 篇文章包含 [搜索, 召回, 算法]。这叫正排。但用户查询时,给的是词,要的是文档。如果用正排,你得遍历每一篇文章看它含不含这个词——几亿次,慢到没法用。
倒排,就是把这个映射反过来:从"词 → 包含它的文档列表"。
用户搜"召回 算法",系统就取出"召回"和"算法"两条倒排链,做个交集,瞬间得到 doc1、doc23。几亿篇文档的检索,被压缩成了几次链表的合并运算。这就是搜索引擎能"秒回"的物理基础。Elasticsearch、Lucene 这些你耳熟能详的工具,内核全是倒排索引。
BM25:命中之后,谁排前面?
但光"命中"还不够。一篇文章提了"召回"一次,另一篇提了二十次,显然后者更相关。怎么量化?这就是 BM25 出场的地方——它是过去二三十年里,文本相关性打分事实上的工业标准。
BM25 的打分逻辑,你不用记公式,记住三个直觉就够了:
- 词出现得越多越相关,但有上限(词频饱和):一篇文章提"召回"提了 100 次,并不比提 20 次相关 5 倍。BM25 用一个参数
k1让得分增长"边际递减"。这是它比老土的 TF-IDF 聪明的地方。 - 越稀有的词越值钱(IDF):"的、是、了"这种词哪篇都有,几乎不携带信息;而"召回"这种专业词,命中了就很说明问题。
- 长文章要打个折(文档长度归一化,参数
b):一篇万字长文里出现"召回",和一条标题里出现"召回",含金量不同。BM25 会按文档长度做惩罚,避免长文靠"堆字数"占便宜。
BM25 的优点是:极快、可解释、零训练成本。新上线一个搜索,不用任何数据,BM25 当天就能跑出像样的效果。直到今天,它依然是绝大多数搜索系统的默认基线,也是 RAG 系统里最稳的那一路召回。
但它有一个致命的盲区。你搜"番茄炒蛋",它永远找不到只写了"西红柿炒鸡蛋"的菜谱——因为这两个词,字面上一个字都不沾。BM25 只认字,不认意思。
这道横在"字面"和"语义"之间的鸿沟,我们叫它词汇鸿沟(Vocabulary Mismatch)。要跨过它,搜索必须学会一件全新的事:理解意思。这就要轮到向量检索登场了。
三、向量检索:让机器读懂"意思"
怎么让机器知道"番茄"约等于"西红柿"?
办法是:把每一段文字,通过一个模型(Embedding 模型),映射成一个几百维的数字向量。这个映射有个神奇的性质——意思相近的文本,向量在空间里的位置也相近。"番茄"和"西红柿"的向量几乎重叠,而它俩离"汽车"则隔着十万八千里。
于是,"找语义相关的文档"这个模糊问题,被翻译成了一个无比清晰的数学问题:给定一个查询向量,在几亿个文档向量里,找出离它最近的 K 个。这就是最近邻搜索(Nearest Neighbor Search)。
从"精确"到"近似":ANN 的妥协智慧
问题来了:最朴素的办法,是把查询向量和几亿个文档向量逐个算距离,取最近的——这叫暴力 KNN。准是绝对准,但慢得发指,几亿次浮点运算,根本塞不进 10 毫秒。
所以工业界集体做了一个妥协,叫 ANN(Approximate Nearest Neighbor,近似最近邻)。注意那个"近似"——我们主动放弃"找到绝对最近的"这个执念,只要求"大概率找到很近的那几个"。用一点点精度,换回几百上千倍的速度。
这个取舍,正是召回哲学的完美体现:召回要的从来不是完美,而是又快又不漏。那 ANN 具体怎么做到"既快又准"?最主流的答案,是一种叫 HNSW 的图结构。
HNSW:给向量世界修一套"高速公路网"
HNSW(Hierarchical Navigable Small World,分层可导航小世界)的思路,我用一个你天天在用的东西类比:跳表,或者说地图导航的分层。
想象你要从北京的一个胡同,导航到上海的一条小街。导航不会让你一格一格挪。它分层:
- 最高层(高速公路):节点稀疏,但每条边跨度极大。你先从北京"跳"到上海,一步千里。
- 中间层(国道省道):落到上海后,在城市间快速逼近目标区域。
- 最底层(街道小巷):节点最密,做最后的精细定位,找到那条小街。
HNSW 检索时,就是从最高层的入口点出发,贪心地往离目标更近的邻居走,走到头就下沉一层,继续逼近,直到最底层锁定结果。原本要比对几亿次的搜索,被压缩到几百次跳转。这就是它能在毫秒级返回的奥秘。
HNSW 有三个你迟早会调的参数,顺手记一下:M(每个节点连多少邻居,越大越准也越占内存)、efConstruction(建图时的搜索宽度,影响索引质量)、efSearch(查询时的搜索宽度,这是线上调"精度/速度"最常用的旋钮——调大更准更慢,调小更快但可能漏)。
FAISS:把这一切变成工程现实
算法再漂亮,也得有趁手的工具落地。FAISS(Facebook AI Similarity Search)就是这个领域最知名的开源库,Meta 出品,几乎是向量检索的工业默认选项。
FAISS 厉害在它不是一个算法,而是一整套"积木"。除了 HNSW,它还提供另一条主流路线 IVF(倒排文件):先用 k-means 把所有向量聚成几千个簇,查询时只在最近的几个簇里搜(参数 nprobe 控制搜几个簇)——这又是一次"缩小范围"的经典操作。再配上 PQ(乘积量化),把高维向量压缩存储,能让上亿向量塞进一台机器的内存。
讲到这,向量检索解决了"理解意思"和"快速找近邻"两件事。但有个根本问题我们一直绕过去了:那个把文本变成"好向量"的模型,到底长什么样、怎么训出来的?这就是双塔模型的主场。
四、双塔模型:召回的深度学习答案
向量检索能成立,前提是有人先把 query 和 document 都变成"靠谱的向量"。在搜索和推荐里,产出这些向量的主力模型,就是双塔模型(Two-Tower Model)。
它的结构正如其名,两根柱子:一根是用户塔/查询塔,吃进用户特征或查询词,吐出一个用户向量;另一根是物品塔/文档塔,吃进物品特征,吐出一个物品向量。两个向量算个内积(或余弦相似度),分数高就代表"匹配"。
为什么是"两座分开的塔"?这是天才设计,不是偷懒
你可能会问:让 query 和 document 早点交互、充分纠缠,模型不是更准吗?没错,那种"交叉模型"(Cross-Encoder)确实更准。但它有个致命问题:必须拿到 query 才能算分,因为它的每一层都把 query 和 doc 揉在一起算。
双塔的精髓,恰恰在于它故意不让两塔在中间交互,只在最顶层碰一次内积。这个"克制",换来了一个杀手锏能力:
看懂这一层,你就理解了整个召回-排序架构的分工逻辑:双塔因为能离线预计算物品向量,所以适合做召回(海量、要快);交叉模型因为更准但算不快,所以留给精排(少量、要准)。还记得第一章那句铁律吗——"能离线算好的,绝不留到线上算"。双塔模型就是这句话的最佳代言人。
训练的胜负手:负样本怎么选
双塔模型怎么学?正样本好找——用户点过、买过的就是正例。难的是负样本:你不能只拿"用户看了没点"的当负例,那些样本太"接近正例",学不出区分度。
工业界最经典的解法叫 In-Batch Negative(批内负采样):在一个训练批次里,把别的样本对应的正例物品,当成当前用户的负例。一个 batch 有 256 个样本,就等于白嫖了 255 个负例,几乎零额外成本。这是 Google 2019 年那篇双塔论文带火的做法,至今是默认配置。
但它有个偏差:越热门的物品越容易出现在 batch 里,就越容易被当成负例,导致模型"打压热门"。于是后来又有了 Mixed Negative Sampling(混合负采样)——批内负例之外,再从全库均匀采一批负例补进来,缓解这个偏差,Google Play 的线上实验证明确有提升。负样本工程,某种意义上比模型结构更决定双塔的成败。
到这儿,四大主角里的三个——倒排/BM25、向量检索、双塔——都登场了。但真实的系统从不只用一个。它们怎么协同?这就是召回的最后一块拼图。
五、多路召回:没有银弹,只有合奏
讲了这么多算法,现在我要泼一盆冷水:没有任何一路召回,能搞定所有情况。
每一种召回都有天生的盲区:
- 倒排/BM25:精确匹配之王,但跨不过词汇鸿沟,搜"番茄"找不到"西红柿"。
- 向量/语义召回:懂意思,但有时"太懂了"——你搜一个精确的商品型号 "iPhone 15 Pro 256G",它可能给你返回一堆"语义相近"的别的手机,反而漏了那个精确 SKU。
- 协同过滤:擅长"猜你喜欢",但碰到全新用户或新上架的物品,没有任何行为数据,直接抓瞎——这就是著名的冷启动问题。
既然各有所长又各有盲区,那答案就很自然了:全都要。让多路召回并行跑,各自捞出一批候选,再汇到一起去重、合并,统一交给粗排。这就是多路召回(Multi-way Recall)。
三路最常见的召回,各管一摊
语义召回,我们前两章已经讲透了——就是用双塔/向量检索那一套,负责"懂意思",跨越词汇鸿沟。它是这几年增长最快的一路。
内容召回,是最朴素也最可靠的一路。基于物品自身的属性、标签、分类来匹配:你看了篇讲"露营"的文章,我就用"露营""户外"这些标签,去倒排索引里捞同标签的内容。它的好处是可解释、对新物品友好(只要打了标签就能被召回)。
协同过滤(Collaborative Filtering),是推荐系统的灵魂一路,它的哲学一句话就能说清:"和你口味相似的人喜欢的,你大概也喜欢",或者反过来,"经常被一起购买的东西,值得一起推荐"。它完全不看物品内容是什么,只看用户行为的共现模式。你刚看完露营视频就被推帐篷,八成就是它干的——因为"看露营的人,后来很多都买了帐篷"。
合奏的艺术:召回多路,怎么融合?
多路召回最后要汇到一起。但每一路给的"分数"含义不同——BM25 的分和向量内积的分,根本不在一个量纲上,没法直接比大小。
实务中常见两种做法。一种是配额制:不比分数,给每一路分配固定名额(比如语义路取 300 个、协同路取 200 个、内容路取 100 个),各取各的 TopK,去重后合并。简单、可控、好排查。另一种是统一打分:用 RRF(倒数排名融合)这类方法,只看每个候选在各路里的"排名",而不看原始分数,从而把不同量纲的结果公平地揉到一起。
说到底,多路召回的本质,是一种承认局限的智慧。它不指望某个绝顶聪明的单一算法包打天下,而是让一群各有专长、各有偏见的"专家"分头去找,再用一套机制把它们的意见融合起来。这,才是现代搜索真正的样子。
六、写在最后:召回的本质
我们从 10 毫秒的极限约束出发,走完了召回阶段的四大主流算法。回头看,它们其实是在用四种不同的方式,回答同一个问题:怎么在海量候选里,又快又不漏地圈出对的那一批。
- 倒排索引 + BM25:搜索的地基。靠字面精确匹配,极快、可解释、零训练,至今仍是不可替代的基线。
- 向量检索(ANN/HNSW/FAISS):跨越词汇鸿沟,用"近似"换速度,让机器第一次真正读懂"意思"。
- 双塔模型:深度学习给召回的答案。靠"两塔不交互"的克制设计,换来物品向量可离线预计算的杀手锏。
- 多路召回:把上面这些和协同过滤、内容召回编排成一场合奏,用互补对抗各自的盲区。
如果要我把整篇浓缩成一句话:召回的进化史,就是搜索从"认字"到"懂意思"再到"懂你"的进化史。倒排认字,向量懂意思,协同过滤懂你。
而真正的高手,从不在这四者里搞"路线之争",非要分出谁取代谁。BM25 没有死,向量也没有杀死它——它们今天就肩并肩跑在同一套多路召回里。承认每个算法的边界,再用工程把它们的优势拼起来,这才是召回工程的成熟。
所以今天给你留一个能动手的挑战:
用 50 行左右的 Python,搭一个最小的"双路召回"玩具。一路用 rank-bm25 库做关键词召回,一路用 sentence-transformers 把文本编码成向量、再用 faiss 建一个 HNSW 索引做语义召回。准备十几条短文本当语料,然后构造两个查询:一个用"近义词不同字"(比如语料里写"自行车",你搜"单车"),一个用"精确术语"(比如某个型号编号)。
跑一遍你会亲眼看到:语义那路能召回"单车→自行车",BM25 那路漏了;而精确术语那路,BM25 稳稳命中,向量反而给你返回一堆"差不多"的噪声。
那一刻,这篇文章里所有的道理,会变成你指尖上的肌肉记忆。看懂多路召回为什么存在,最快的方式,就是亲手让其中一路漏掉一次。
参考资料
- Okapi BM25 — Wikipedia(BM25 公式与参数详解)
- Malkov & Yashunin, Efficient and robust approximate nearest neighbor search using HNSW graphs (arXiv:1603.09320)
- FAISS Wiki — Facebook AI Similarity Search 官方文档
- ANN Search Explained: IVF vs HNSW vs PQ — TiDB
- Yi et al., Sampling-Bias-Corrected Neural Modeling for Large Corpus Item Recommendations (Google 双塔, RecSys 2019)
- Yang et al., Mixed Negative Sampling for Learning Two-tower Neural Networks (WWW 2020)
- Cross-Batch Negative Sampling for Training Two-Tower Recommenders (arXiv:2110.15154)