为什么相似度高不等于相关度高
我的面试题笔记里存着一段对话。候选人介绍:“我的 RAG 系统用了 Top-20 召回。“面试官追问:“相似度高等于相关度高吗?“紧接着是第二刀:“你召回 20 条,15 条是语义相近但答案无关的噪声,LLM 拿着这 15 条垃圾脑补答案——你还以为召回质量挺好?”
两刀扎的是同一个地方:分数高,不等于答得对。第 7 篇讲口径陷阱,但就算时间和口径都对齐了,相似度这个指标本身还有一层错觉。这个问题我在腾讯中报上真的踩过,这篇把它拆开。
一、两个分数:相似度管“长得像“,相关度管“答得上“
先把概念掰开。embedding 模型优化的目标是语义相似度——两段文本在语言表达上的接近程度;用户要的是信息相关度——这个 chunk 里有没有回答这个问题所需的信息。一个管“长得像“,一个管“答得上“。
大多数日常问答里,两者是重合的,所以这个坑平时藏得住。问“腾讯 2025 年营收多少“,包含“2025 年营收 184,504“【待核实:单位与口径(百万元还是亿元)需按中报原文核对】的 chunk,和 query 语义最接近,同时也正好装着答案。分数排第一,答得也对,皆大欢喜。
问题出在脱钩的时候。一旦两者脱钩,相似度分数就从路标变成误导——它还在诚实地告诉你哪段话长得像,但你需要的已经不是长得像了。下面三个现场,都是财报库里真实出现过的脱钩。
二、三个脱钩现场:满分相似、零分信息长什么样
现场一:目录页。
问“腾讯的毛利率“,召回结果里排前面的是个目录 chunk:“毛利……第 45 页”。“毛利“这个 token 在语义上稳稳命中 query,分数很高;但这个 chunk 的信息价值是零——它只告诉你答案在第 45 页,自己一个字也没有。
embedding 为什么上当?它的训练任务是判断语言表达的相似性,“毛利“对“毛利率”,字面近、语义近,分数自然高;它不关心这句话里有没有数值。目录页就是这个问题最极端的样本:满分相似,零分信息。看到 0.9 的相似度打在一个目录 chunk 上,你就知道这两个分数从来不是一回事。
现场二:语义同构,约束不同。
问 2025 Q4 的营收,2020 Q4 的营收 chunk 在文字上几乎同构——都是“第四季度营收 XX 亿元,同比增长 YY%“这一句式。embedding 分不清谁是谁。这种脱钩比目录页隐蔽:chunk 本身不是垃圾,只是答错了年份。第 6 篇讲时间错配时把解法展开过,这里只登记现象,不重复展开。
现场三:功能性句子。
“如上一节所述”“下表列出了”“以下数据来自”——这类过渡句在财报里到处都是。embedding 对“如上一节所述,本集團的營收結構發生了以下變化“和“本集團營收 1,845 億元“这两句话的评分没有区分度,因为它学的是判断语言相似性,不是判断信息有效性。
麻烦在于,过渡句的价值是双面的。它不是答案本身,多数时候该降权;但它是指向答案所在 chunk 的路标,有时又该保留。一刀切删掉所有过渡句,等于把路标也拆了。
三、Bi-Encoder 打分高、Cross-Encoder 打分低,说明什么
面试对话里还有一问:“如果 Bi-Encoder 打分高,但 Cross-Encoder 打分低,说明什么问题?”
两种模型的差别在输入方式。向量检索用的是 Bi-Encoder:query 和 chunk 各自编码成向量,再算相似度,编码时两者互相看不见,吃到的是整体语义相似。reranker 常用 Cross-Encoder:把 query 和 chunk 拼成一对一起过模型,输出两者的相关性分数——因为它看得见 query 里的每个词和 chunk 里每个词的逐词交互,这个分数比“像不像“更接近“答不答得上“。
所以两个分数打架,不是哪个模型坏了,而是第一节那组概念的直接映射:Bi-Encoder 的分是“像不像“,Cross-Encoder 的分更接近“答不答得上“。拿目录页那个 chunk 去过 Cross-Encoder,它能看出“毛利……第 45 页“没法单独回答“毛利率是多少“——相似度和相关度的区分不是玄学,是架构差异的必然结果。
笔记里面试官还有一句:“召回只是第一步,Rerank 才是决定送给 LLM 什么内容的关键。“这句我只同意一半:rerank 确实守在送给 LLM 之前的最后一道关口,但它是兜底,不是主力。为什么,下一节讲。
四、缓解手段:先过滤,再提密度,最后兜底
三个手段按动手位置从上游到下游排,每个都有代价。
首选 metadata 硬过滤。 目录页、页眉页脚、纯页码这类噪声 chunk,embedding 天生会中招——不是模型的错,是数据里混进了不该参与检索的东西,那就别指望调 embedding 模型修噪声。把 section=目录、noise_type=page_header 的 chunk 在检索前滤掉,或者至少在排序时降权。字段怎么设计、怎么标,第 5 篇讲 metadata 时展开过。代价也摆在明处:噪声要在 ingest 阶段标出来,规则漏了,噪声就漏进库;规则太狠,又误杀正常 chunk。
第二,提升 chunk 的信息密度。 在分片阶段——TextBlock 合并为 chunk 那一步——保证每个 chunk 至少含一个实质性内容:一个数值、一个日期、一个定义、一个断论。反过来,一个 chunk 的 token 里停用词和“如下所示““表中数据“这类结构性文字占了一半以上,信息密度就高不起来,不配排在检索结果前面。这是第 4 篇分片逻辑的延伸:切得好不好,最终会反映在检索分数的质量上。代价是分片更慢,规则要跟着文档类型维护。
第三,reranker 兜底。 前两招漏掉的高相似低信息 chunk,Cross-Encoder 在精排阶段还能再筛一遍,上一节说的“答不答得上“就是它给的信号。但边界必须立刻说清:reranker 只能在候选集里调排序,正确答案不在候选集里,它没救。这个论点第 10 篇会展开,这里先记住结论就行。
五、长期方向:多模态 retrieval 会缓解,但别指望它根除
还有一个更远的方向值得跟踪。ColPali、ColQwen 这类 vision-language retrieval 模型不走 OCR 文本提取,直接把 PDF 页面当图像做视觉 embedding——文本内容以字形和布局的形态被视觉编码器隐式捕获。这意味着它天然能区分目录页和正文页的排版、表格区和段落区的视觉差异——现场一那类问题,在“看图“的模型眼里不该发生。方向是对的。
但这条路也有边界:财报、法律这类文档结构极复杂的场景,实测仍有距离,财报上的 fine-tune 还不足【待补:目前只有定性判断,缺具体对比实测数据】。短期更稳的,仍然是 metadata 过滤加 reranker 的组合。
相似度分数只是排序的依据,不是正确的保证。看到 0.9 的分数,先问“它答得上吗“,再问“它排第几“。
本篇讲的是单路向量检索本身的错觉。工程上的第一条补救不是继续打磨这一路,而是给它找个搭档:专有名词、编号、代码这些字面精确匹配的活,向量天生干不好,得让 BM25 上。第 9 篇《Hybrid Retrieval:BM25 和 Dense 到底怎么分工》就讲这个搭档怎么配。