跳到主要内容

K4.2 交叉编码器与 ColBERT:为什么精排更准

凭什么精排更准?差别在「怎么读」​

上一节说精排比召回准,但没说凭什么。答案很简单:差别在「怎么读」。

想象判断两句话是不是一个意思。你有两种读法:一种是分开——先读完 A,脑子里记个大概印象,再读完 B,然后凭两个模糊印象比一比;另一种是摆在一起——把 A、B 并排,一个词一个词地对照着看。哪种准?显然是后者。检索的召回和精排,走的正是这两条路。

召回用的双编码器:分开各读个大概​

K1 讲过召回用的架构叫双编码器(bi-encoder):查询和文档各自独立编码成一个向量,检索时比这两个向量的距离。

它最大的优点是快——文档向量可以离线预先算好、存进库里,检索时只需实时编码「查询」这一个短句,再去库里比距离。正因如此它能扛亿级检索。

但它有个天生的局限:查询和文档从头到尾「没见过面」。文档被编码成向量时,根本不知道将来会有什么查询来找它;它只能把整段话的意思压成一个笼统的向量。就像上面那个「分开各读个大概」的读法——快,但只能表达个大概,遇到「产假」和「陪产假」这种一字之差,就容易糊成一团(这正是上一节召回把李鬼排第一的原因)。

精排用的交叉编码器:拼在一起逐字读​

精排用的是交叉编码器(cross-encoder)。它的做法截然不同:把查询和文档拼成一段,一起送进模型,让注意力(上篇 3.2 节)在查询的每个词和文档的每个词之间交叉比对,最后直接输出一个「相关不相关」的分数。

这就是「摆在一起逐字读」——模型真的在读「这篇文档到底答没答这个问题」,而不是比两个模糊印象。所以它能分清「男员工问的是陪产假,而这段讲的是产假」——一字之差,在交叉注意力下无所遁形。这就是上一节精排能把真答案顶上来的原理。

那为什么不干脆全程用交叉编码器?​

你可能会问:既然交叉编码器这么准,为什么不直接拿它检索整个知识库,省掉召回这一步?

答案是它慢得离谱。交叉编码器必须把「查询 + 文档」成对地一起算——它没法像双编码器那样把文档向量预先算好存起来(因为文档向量依赖于「跟哪个查询配对」)。这意味着:知识库里有 1000 万篇文档,每来一个查询,就要现场把这 1000 万篇分别和查询拼起来算 1000 万次。用户能等到地老天荒。

所以交叉编码器只配得起「精排那几十条」,配不起「检索全库」——这正是上一节两阶段分工的根本原因:让快的双编码器先把范围缩到几十条,再让准的交叉编码器在这几十条上发力。快慢准,各司其职。

ColBERT:快与准之间的第三条路​

双编码器太粗、交叉编码器太慢,有没有折中?有——ColBERT / 多向量(late interaction)就是这条中间路。

它的巧思在于:不像双编码器把整篇压成一个向量(丢了太多信息),也不像交叉编码器现场拼读(太慢);而是给每个词各留一个向量,检索时做「细粒度对齐」——查询的每个词,去文档里找最匹配的那个词。这样既保留了「逐词比对」的精细(比双编码器准),又能把文档的词向量预先算好(比交叉编码器快)。它兼顾了两头,2024–2026 年在深度检索、深度研究类应用里越来越热。

重排也不是万能钥匙

两个提醒。其一,重排模型也要选型和评测——不同语言、不同领域,重排模型的强弱差异很大,中英文各有专门的强者,别随手抓一个就用。其二,也是最容易忘的:再好的重排也只能在召回给的候选里挑,它不产生新候选(回扣上一节「没召回的救不回」)。重排提升的是「排序」——把已有候选里对的那条顶上来;它改变不了「有没有」——真答案要是压根没被召回,重排排破天也变不出来。

小结​

本节要点
  • 精排更准的根源是**「怎么读」**:召回的双编码器分开各编个大概,精排的交叉编码器把 query 和文档拼一起逐字比对。
  • 双编码器能预算文档向量所以快、能扛全库检索,但粗;交叉编码器成对现算所以准,但慢到只配精排几十条。
  • ColBERT / late interaction 给每个词留一个向量、做词级对齐,是快与准之间的折中。
  • 重排只重排召回给的候选——它改变「排序」,改变不了「有没有」。
📝 动手自测0/3
1. 交叉编码器(精排)比双编码器(召回)更准,最根本的原因是?
2. 既然交叉编码器更准,为什么不直接用它检索整个知识库?
3. ColBERT(late interaction / 多向量)的定位是?

延伸阅读​