检索的准确率瓶颈,常常卡在「语义被压成一个点」上。《一篇读懂 Embedding》讲过双塔模型的根本妥协:把几百字的段落塞进一个向量,细节注定丢失——同义改写能对上,但「谁对谁做了什么」这类细粒度差别就糊了。另一头,《一篇读懂 Reranker》里的 cross-encoder 把查询和文档拼在一起过模型,词与词的交互看得清清楚楚,所以准——但每对「查询-文档」都要跑一次模型,没法预计算,只能给几百条候选做精排。这篇讲的 ColBERT(Khattab 与 Zaharia,2020)在两者之间劈出第三条路:late interaction——文档侧提前算好 token 级向量,查询时再做细粒度交互,鱼与熊掌各得一部分。
三种匹配范式的光谱
把三种范式排成一条光谱,ColBERT 的位置就清楚了。
单向量(bi-encoder):查询、文档各自压成一个向量,相似度就是两个点的距离。文档侧可以预计算、可以建 ANN 索引,检索亿级库毫秒级返回;代价是全部交互信息在压缩成一维点时蒸发,它是光谱上「最快最不准」的一端。
全交互(cross-encoder):查询和文档拼成一个序列送进模型,注意力层让每个查询 token 与每个文档 token 充分交互,最后输出一个相关性分数。它「看见」的信息最全,所以最准;但分数无法预计算——文档在查询到达之前不知道自己会跟谁配对——于是只能做候选池内的精排,是「最准最慢」的一端。
late interaction(ColBERT):文档离线编码成一组 token 级向量(一个 token 一个向量,一段 200 词的文档约两百个向量),这一步可以预计算、可以建索引;查询在线编码成另一组 token 向量,然后做一次轻量交互。准确率接近 cross-encoder,工程性能接近双塔——它用「多存几十倍向量」买来了这个中间位置。
MaxSim:late interaction 的核心算子
交互怎么算?ColBERT 的答案是一个出奇简洁的算子——MaxSim。对查询里的每个 token 向量,在文档的所有 token 向量里找与它最相似的那一个(余弦最大),把这个最大相似度记下来;最后把查询所有 token 的得分求和,就是这条文档的相关性分数。
用公式说是 score(q, d) = Σᵢ maxⱼ (qᵢ · dⱼ)。翻译成人话:「查询里的每个词,都在文档里找它最好的对应;每个词都找到了好归宿,总分就高」。这个算子的精妙在于它天然带上了细粒度对齐:查询里的「凯利公式」不必和整段文档的压缩向量比较,只需和文档中真正讲到它的那几个 token 对齐;而「苹果手机维修」里的「苹果」也只会对齐到讲手机维修的「苹果」,不会跑去和水果段落的「苹果」配对——单向量时代这类歧义是老大难。
代价:存储与工程复杂度
late interaction 的账单同样明确。存储爆炸:单向量时代一条文档一个向量,ColBERT 一条文档几百个向量,哪怕每个 token 向量压缩到几十字节,总体积仍是单向量方案的几十倍。ColBERTv2(Santhanam 等人,2021)用残差压缩大幅缓解——token 向量先聚类,存储时只存质心编号加低比特残差,体积砍到原来的十分之一量级;配套的 PLAID 索引进一步做候选筛选。即便如此,它仍比单向量方案贵一个数量级。没有现成基础设施:主流向量库的原生索引是「一对象一向量」,跑 ColBERT 要么用专用库,要么把多向量拆成多条记录自己管理聚合,运维复杂度实打实上升。查询侧编码不可省:查询也要过一遍 transformer 编码出 token 向量,比单向量方案的编码开销略高。
| 维度 | 单向量 | late interaction | cross-encoder |
|---|---|---|---|
| 匹配粒度 | 全文一点 | token 级对齐 | 全 token 交互 |
| 文档侧预计算 | 可以 | 可以 | 不可以 |
| 存储 | 1× | 十倍量级(v2 压缩后) | 无需存储 |
| 典型位置 | 首跳召回 | 首跳或精排 | 精排 |
打分工程:MaxSim 跑得快的秘密
「token 级交互」听起来计算量大得吓人——查询 20 个 token 对文档 200 个 token,就是 4000 次点积。让它在工程上跑得动的是三层优化。第一层,预计算:文档侧的全部 token 向量离线算好存起来,查询时零文档编码——这是 late interaction 的立身之本。第二层,矩阵乘法批量算:MaxSim 的「查询向量 × 文档全 token 向量」本质是一次 [20 × dim] × [dim × 200] 的矩阵乘,GPU/现代 CPU 的矩阵指令把它压到微秒级——4000 次点积是一次 GEMM,不是 4000 次循环。第三层,候选预筛(PLAID 的核心):先用质心(token 向量聚类的中心)粗估文档分数,只对少数候选文档做完整的 token 级 MaxSim——绝大部分文档在质心层就被淘汰,完整交互只发生在几十条候选上。三层叠下来,ColBERTv2-PLAID 的端到端延迟可以与单向量方案同量级,这是它能上生产的底气。
查询侧的编码与缓存
late interaction 里唯一在线的模型推理是查询编码(查询 token 化 + transformer 编码)。它有两个工程要点。延迟预算:查询编码通常 10~50ms,比单向量方案的编码略贵(token 多、输出多),在首 token 延迟敏感的场景里要算进预算,常见缓解是蒸馏小编码器。查询缓存:语义缓存的近亲——高频查询的 token 向量按哈希缓存,重复查询跳过编码直取。另有一个细节:ColBERT 对查询做特殊标记(查询标记词 + 填充到固定长度),让模型进入「查询模式」的编码行为——用社区预训练好的 checkpoint 时这些约定已在 tokenizer 里,自训模型时别漏。
怎么验证它在你那里值不值
评估 ColBERT 值不值,跑一组三方案的对比就够了:单向量 + cross-encoder rerank(当前主流基线)、单向量 + ColBERT rerank(ColBERT 当精排)、纯 ColBERT(当首跳)。量三个数:评测集上的 recall@k / MRR(准不准)、P50/P99 延迟(快不快)、存储与索引构建成本(贵不贵)。判据对应前面的站位分析:纯 ColBERT 的精度追平或超过基线且延迟可接受 → 它当首跳省掉一跳模型推理;ColBERT-rerank 的精度接近 cross-encoder-rerank 且延迟显著更低 → 精排位换装划算;两者都不占优 → 维持现状,ColBERT 的存储开销不值得。三方案对比的总成本不高(评测集复用),却能把「要不要上多向量」从社区争论变成你自己的数据决策。
放进流水线的哪个位置
实践里 ColBERT 有两种常见站位。当首跳:语料规模适中(百万级以内)、查询是细粒度实体或短语型(代码标识符、法条编号、人名+动作)时,直接用 ColBERT 做第一跳,省掉 reranker 一跳,端到端延迟更低,精度还常常更好——细粒度查询正是单向量压缩损失最重灾区。当精排:与常规单向量首跳组合,ColBERT 给几百条候选打分替代或补充 cross-encoder reranker——比 cross-encoder 快(交互是预计算后的轻量算术),准确率通常略逊但差距不大。
什么时候不用:亿级语料、延迟预算极紧、查询粗粒度(整句话的自然语言提问)的场景,单向量首跳 + cross-encoder 精排的组合工程上更省心;文档超长(整本书一级)时 token 向量也存不起,先切块再说。
自训还是现成:模型的两个来源
ColBERT 模型有两个获取路径,按团队情况选。现成 checkpoint:社区维护的 ColBERTv2 权重(基于预训练语言模型在 MS MARCO 类检索数据上训练)开箱即用,通用文本检索效果有基准背书,是绝大多数场景的起点;中英混合语料要注意 checkpoint 的语言覆盖——英文为主训出的模型对中文 token 的编码质量需要实测,必要时换多语言基座的相关实现。领域自训:术语密集的垂直领域(法律、医疗、内部黑话多的企业语料),通用 checkpoint 的 token 级匹配也会水土不服,用领域内「查询-相关段落」对做微调(对比学习目标与原论文一致)能拿到可观提升,成本是几千对标注样本加一次训练任务。判断要不要自训的方法老一套:现成模型在自建评测集上的召回与 rerank 基线的差距——差距小就别折腾,差距大且检索层是瓶颈,自训的投入产出才成立。
适用场景清单与失效形态
late interaction 最吃香的四类场景:实体密集型查询(产品型号、法条编号、人名加动作——token 级对齐对「精确子串」的敏感度远超单向量);长查询(整段需求描述里夹着多个关键实体,MaxSim 让每个实体各自找到归宿,压缩成单向量则互相干扰);歧义消解(「苹果维修」不会配到水果段落,词级对齐天然带上下文消歧);引用型检索(法律、医疗场景要求「答案的每个说法能对到原文的具体位置」,token 级对齐直接给出对齐点)。反过来它有两类失效形态:虚词主导的查询(「怎么办」「是什么」类功能词占大头的短查询,MaxSim 分数被无区分度的 token 平摊,反而不如单向量干脆——好在查询理解层可以先识别并改写);超长文档(token 向量随文档长度线性膨胀,千字以上的块存储与打分成本陡增,先切块再进 ColBERT 是强制前置)。场景对上了才上多向量,对不上省下的是一整套专用索引的运维。
小结
ColBERT 的价值在于划清了一个曾被当成两难的问题:「细粒度交互」不必以「无法预计算」为代价——把交互推迟(late)到查询时刻,但文档侧的编码提前完成。MaxSim 一个算子就完成了词级的最佳对齐。记住它的位置:光谱的中间点,存储贵一个数量级,换来接近 reranker 的精度与接近双塔的工程性能;细粒度查询和多跳精排是它最划算的两个战场。
参考资料
- Khattab & Zaharia, ColBERT (SIGIR 2020, arXiv:2004.12832) —— late interaction 与 MaxSim 的原始提出
- Santhanam et al., ColBERTv2 (arXiv:2112.01488) —— 残差压缩与 PLAID 索引
- 本系列前篇:《一篇读懂 Embedding》《一篇读懂 Reranker》《混合检索与重排序》
读者留言
COMMENTS 暂无还没有留言,来说第一句?