Français
Two sentences can share every word and mean opposite things — if scope changes. We tested whether vector retrieval can tell them apart. It mostly cannot, and the failure is precise.essai · standmeet2026.10.27 · essai
2026.10.27·lecture de 9 min

Same Words, Different Meaning: Can Vector Retrieval Tell Scope Apart? / 同样的词,不同的意思:向量检索分得清辖域吗?

Two sentences can share every word and mean opposite things — if scope changes. We tested whether vector retrieval can tell them apart. It mostly cannot, and the failure is precise.

同样的词,不同的意思:向量检索分得清辖域吗?

我的出发直觉是一段数学。"For all δ there exists ε"(对每个 δ 都存在一个 ε)和 "there exists ε for all δ"(存在一个对所有 δ 都成立的 ε)用的词是一样的,意思却不一样。量词顺序一换,命题就变了:一个方向是每个 δ 各配一个 ε,另一个方向是一个 ε 要对所有 δ 成立。词几乎完全相同,意思不是。

这就引出一个关于检索增强系统底层机制的问题:向量检索分得清这两者吗?如果一本教材里同时有正确表述和它那个辖域互换的孪生表述,用户问的是其中一个,检索器会返回正确的那一个,还是把两个都交出来,把区分的活推给下游?

我进去时的预测很直白:几个最先进的 embedding,乘上几种距离,都会失败。这篇文章报告两次验证这个预测的实验——一次小探针,和一次扩到四十组最小对的规模化版本。预测对了一半,而错的那一半才是更有意思的部分。

设计:最小对与 WIN 规则

实验由最小对构成:两句话共享几乎全部的词,只在辖域上不同——量词顺序、否定位置、或者连接词结合方式——而真值条件确实不同。每组对子都带一个区分场景:一个具体的情形,在其中一个孪生句为真、另一个为假。构造不出这种场景的对子就不是对子,只是同一句话写了两遍。

每对的每一侧各配一条查询,写成该侧读法的意译。判定规则严格且成对:只有当正确段的分数高于它那个辖域互换的孪生段时,这条查询才算 WIN。正确段排第一不是必需的,赢过孪生段才是。这就是全部测试,其余一切——干扰段、其他段、绝对分数——都只是背景。

有一个微妙之处关系到量词换位的弱读一侧。弱读("每个病人都有某个医生")在逻辑上被强读("某个医生治疗所有病人")蕴含,所以弱读一侧的 plain 意译无法排除强读世界。弱读一侧的查询因此用了混合场景——不同病人配不同医生——这是唯一能把读者钉在弱读上的写法。

待测方法:

  • 手写 BM25(k1 = 1.5,b = 0.75),作为关键词对照。
  • 三个双塔模型,均值池化:MiniLM-L6-v2、bge-base-en-v1.5、e5-base-v2(e5 按规范加 query:/passage: 前缀)。
  • 四种距离——cosine、dot、欧氏、曼哈顿——各在原始向量和 L2 归一化向量上都算一遍。
  • 交叉编码重排器 ms-marco-MiniLM-L-6-v2,对全语料打分。这是关键对照:双塔模型永远不会同时看到查询和段落,但交叉编码器是同时读两边的,所以如果有什么东西能看见辖域,应该是它。

全部在 CPU 上跑(torch 2.14.1,transformers 5.20.2)。

ε-δ 对子,细算

我最初的例子是 δ 和 ε 朝另一个方向互换。实验里的 ε-δ 对子是同一类量词顺序互换,只是变量处在标准连续性定义里的角色上:逐点(∀ε∃δ)对均匀(∃δ∀ε)。

  • E1(逐点):"For every epsilon > 0, there exists a delta > 0 such that for all x, ..."
  • E2(互换):"There exists a delta > 0 such that for every epsilon > 0, for all x, ..."

这两句话作为词袋是完全相同的。下面是第一次实验在它们上面的得分(归一化 cosine,交叉编码器除外):

方法Q_E1:正确段 vs 孪生段Q_E2:正确段 vs 孪生段
MiniLM-L6-v20.570 vs 0.585,LOSE0.482 vs 0.465,WIN
bge-base-en-v1.50.824 vs 0.815,WIN(+0.009)0.794 vs 0.789,WIN(+0.005)
e5-base-v20.872 vs 0.866,WIN0.857 vs 0.860,LOSE(−0.003)
交叉编码器第 1 名 vs 孪生第 3 名,WIN第 3 名 vs 孪生第 2 名,LOSE

纯词袋对照 BM25 在这对上两次都是精确平局(6.929 = 6.929 和 7.876 = 7.876)——词袋相同,平局是必然的。Embedding 比平局好一点点:赢出 0.005 到 0.015。这个 margin 是整个结果形态的第一条线索。顺序信息确实有一点进了向量,只是不够强,也不可靠。

实验一:探针

第一次跑用了 14 段:4 组最小对加 6 段干扰(极限定义、导数定义、数列收敛、三段论、可导推出连续、开集定义)。4 组对子是上面的 ε-δ 对、1 组命题逻辑对("It is not true that both Alice and Bob passed",¬(A∧B),对 "Alice did not pass, and Bob passed",(¬A)∧B),和 2 组一阶逻辑对(每个学生喜欢某个老师,对某个被所有学生喜欢的老师;以及母亲版的同一互换)。8 条查询,每侧一条。

方法得分丢掉的查询
BM256/8Q_E1、Q_E2(精确平局)
MiniLM-L6-v26/8Q_E1(0.570 vs 0.585)、Q_F1(0.736 vs 0.749)
bge-base-en-v1.57/8Q_F2(0.933 vs 0.936,差 0.003)
e5-base-v27/8Q_E2(0.857 vs 0.860,差 0.003)
交叉编码器6/8Q_E2(1.486 vs 1.569)、Q_F2(4.160 vs 4.327)

关于距离,有两个事实在这次跑里出现,并在规模化后依然成立。在归一化向量上,cosine、dot、欧氏三者的排名完全相同——这是数学恒等,不是实验巧合。而原始向量的 dot 是最不稳的度量(MiniLM 原始 dot 这次只有 4/8),因为它把向量长度也算进去了。不要用原始 dot。

探针的 verdict:我的预测对了一半。没有全军覆没——bge 和 e5 都是 7/8。但命题逻辑那组是所有方法都赢的一组,也是孪生句表面差别最大的一组,所以它证明的东西最少。真正的试金石是硬对子,即词袋相同或近相同的那些——在那里,每个模型至少丢一组,赢的 margin 全在 0.003 到 0.015 之间,连同时读问句和段落的交叉编码器都丢了两组。不过探针太小了,不能停在这里。8 条查询只是一个 hint,不是结果。

实验二:规模化

第二次跑把第一次的 14 段和 8 条查询原样保留,新增 40 组最小对:20 组命题逻辑对(否定辖域、连接词结合、蕴含辖域、only-if 对 if、if-and-only-if、相容或对异或、unless、逆否命题),15 组一阶量词换位对,覆盖不同领域(病人和医生、城市和市长、问题和解法、员工和经理、歌和歌手、锁和钥匙、课程和老师、书和作者、狗和主人、学生和家教、村庄和水井、队伍和教练、文章和编辑、花园和园丁、电影和导演),以及 5 组否定-量词对("not every" 对 "no","some did not" 对 "none did")。一共 94 段、88 条查询。跑完 exit 0,约四分钟。

各方法结果

方法新对(80)全部(88)
BM2544/80 = 0.55050/88 = 0.568
交叉编码器53/80 = 0.66259/88 = 0.670
MiniLM-L6-v2,归一化 cosine52/80 = 0.65058/88 = 0.659
MiniLM-L6-v2,归一化 dot52/80 = 0.65058/88 = 0.659
MiniLM-L6-v2,归一化欧氏52/80 = 0.65058/88 = 0.659
MiniLM-L6-v2,归一化曼哈顿51/80 = 0.63757/88 = 0.648
MiniLM-L6-v2,原始 cosine52/80 = 0.65058/88 = 0.659
MiniLM-L6-v2,原始 dot43/80 = 0.53747/88 = 0.534
MiniLM-L6-v2,原始欧氏54/80 = 0.67560/88 = 0.682
MiniLM-L6-v2,原始曼哈顿57/80 = 0.71363/88 = 0.716
bge-base-en-v1.5,归一化 cosine62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,归一化 dot62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,归一化欧氏62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,归一化曼哈顿62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,原始 cosine62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,原始 dot61/80 = 0.76268/88 = 0.773
bge-base-en-v1.5,原始欧氏62/80 = 0.77569/88 = 0.784
bge-base-en-v1.5,原始曼哈顿62/80 = 0.77569/88 = 0.784
e5-base-v2,归一化 cosine65/80 = 0.81272/88 = 0.818
e5-base-v2,归一化 dot65/80 = 0.81272/88 = 0.818
e5-base-v2,归一化欧氏65/80 = 0.81272/88 = 0.818
e5-base-v2,归一化曼哈顿64/80 = 0.80071/88 = 0.807
e5-base-v2,原始 cosine65/80 = 0.81272/88 = 0.818
e5-base-v2,原始 dot57/80 = 0.71365/88 = 0.739
e5-base-v2,原始欧氏63/80 = 0.78770/88 = 0.795
e5-base-v2,原始曼哈顿62/80 = 0.77569/88 = 0.784

最好的双塔模型是 e5,新对 65/80(81.2%),然后是 bge 62/80(77.5%),MiniLM 52/80(65.0%)。而同时读问句和段落的交叉编码器只有 53/80(66.2%)——低于 bge 和 e5。它在第一次跑里的中间表现不是小样本偶然偏高,规模化后它掉到了本该被它打败的双塔模型之下。

按类型拆分(headline 方法,归一化 cosine)

方法命题逻辑(40)量词换位(30)否定-量词(10)新对(80)
BM2523/4014/307/1044/80
MiniLM-L6-v226/4019/307/1052/80
bge-base-en-v1.528/4026/308/1062/80
e5-base-v232/4024/309/1065/80
交叉编码器26/4020/307/1053/80

否定-量词对最容易(各方法 70–90%),命题逻辑居中(65–80%),量词换位最硬且 margin 最小。这个排序说得通:"no" 和 "not every" 本身就是强词汇信号,而纯量词换位在表面上几乎什么都没改。

赢的幅度有多大?Margin 分析

赢了不等于赢得结实,还得看 margin。下面是(正确段分数 − 孪生段分数)在归一化 cosine 下的分布;交叉编码器那列是 logit 差,量纲不同,只在本列内可比。

方法类型均值中位数abs margin < 0.015 的占比窄胜(赢了且 abs margin < 0.015)
MiniLM-L6-v2命题逻辑+0.0099+0.01200.3008/26
MiniLM-L6-v2量词换位+0.0186+0.01400.3335/19
MiniLM-L6-v2否定-量词+0.0593+0.07600.0000/7
bge-base-en-v1.5命题逻辑+0.0175+0.02600.1755/28
bge-base-en-v1.5量词换位+0.0207+0.02500.3006/26
bge-base-en-v1.5否定-量词+0.0736+0.10400.2001/8
e5-base-v2命题逻辑+0.0133+0.01200.42514/32
e5-base-v2量词换位+0.0070+0.00900.73317/24
e5-base-v2否定-量词+0.0314+0.03300.1001/9
交叉编码器命题逻辑+0.0764+0.30700.0250/26
交叉编码器量词换位+0.3081+0.49100.0000/20
交叉编码器否定-量词+0.6653+0.88300.0000/7

看 e5 的换位行:73.3% 的换位 margin 落在 ±0.015 内,24 场胜里 17 场是窄胜。最硬对子上的最好模型,赢也是靠噪声赢的。bge 在换位上稳一些(均值 +0.021,中位数 +0.025),但仍有 30% 的 margin 落在窄带里。否定-量词对的 margin 大得多(bge 均值 +0.074),因为区分词本身就是强信号。

交叉编码器没有进这张图,因为它的分数是 logit,量纲不同,0.015 这条带对它不适用。

召回没问题,精确排序才是失败

方法孪生段进前 3(新对)孪生段进前 3(换位对)正确段排第 1(新对)
MiniLM-L6-v20.9251.0000.625
bge-base-en-v1.50.9751.0000.762
e5-base-v20.9501.0000.800
交叉编码器0.9631.0000.637
BM250.7500.7000.475

在量词换位对上,孪生段进前 3 的概率是百分之百——三个双塔模型和交叉编码器全是如此。检索一定会把两个互相矛盾的版本都交出来。所以失败不是正确段落缺席,而是错误段落就贴在旁边,而两者之间的排序是抛硬币。任何一个取 top-k 再往下推理的系统,拿到手的都是矛盾材料,还得自己在下游把它分清楚。

还有两个更精确的事实。第一,弱读比强读难:在换位对上,弱读(∀∃)查询对强读(∃∀)查询的胜率,MiniLM 是 7/15 对 12/15,bge 是 11/15 对 15/15(bge 强读全赢),e5 是 13/15 对 11/15。模型往更短、更"标准"的强读句子上漂。第二,难度是集中的,不是摊开的:80 条新查询里有 8 条被三个 embedding 全丢(P01_a、P02_a、P04_b、P07_a、P14_a、P20_b、F11_a、N04_b)——全是弱读或带否定的弱读形态——而 46 条被三个模型全赢。

BM25 在换位对上值得单独写一行:14/30,平均 margin −0.69。词袋重合度在这里不只是帮不上忙,它系统性地偏向错误的孪生段。

第一次跑被精确复现

规模化没有改变实验的最强证据:第一次跑的 8 条查询,放进 94 段语料里重打分,五个 headline 方法的 WIN/LOSE 逐条一致。

查询BM25MiniLMbgee5交叉编码器
Q_E1LOSELOSEWINWINWIN
Q_E2LOSEWINWINLOSELOSE
Q_P1WINWINWINWINWIN
Q_P2WINWINWINWINWIN
Q_F1WINLOSEWINWINWIN
Q_F2WINWINLOSEWINLOSE
Q_F3WINWINWINWINWIN
Q_F4WINWINWINWINWIN

协议没有漂移,第一次跑的数字也不是小语料假象。方向是稳定的。

这意味着什么

诚实的总结不是"检索失败了",而是更窄、也更有用的一句:检索在召回上成功了,在最后一步上失败了。在最硬的对子上——词相同、只有辖域不同的那些——检索器可靠地把两个版本都返回,然后用一个比噪声还小的 margin 给它们排序。读 top-k 结果的语言模型拿到的是两段互相矛盾的材料,而排序里没有任何信号告诉它哪一段回答了问题。

这就定位了修复点。它不在召回——把网撒得更宽、加更多段落、取更多候选,都分不开本来就已经并排坐在顶部的孪生段。它必须在排序或验证那一步:一个真正拿候选段落和问题的辖域去对的步骤——哪个量词约束哪个变元、否定落在哪里——并拿答案去核对,而不是信任一个从来就没被训练去看辖域的相似度分数。在这样的步骤出现之前,任何精确材料——数学、法律、合同、规格——用这种方式检索都带一个安静的风险:那段看起来对、意思却相反的段落,永远只差一个排名。

必须留在后面的 caveat

  • 查询是为实验写的意译,不是真实用户敲出来的问法。换措辞,结果可能动。
  • 段落是单句。真实教材块是 512 token 的长段,辖域信号可能被周围文本稀释,也可能被上下文加强——没测。
  • 只测了英文。量词辖域标记方式不同的语言没测。
  • 88 条查询仍然是探针规模,不是 benchmark——但第一次跑在更大语料里的精确复现说明方向是稳定的。

可复现

可跑的脚本(exp.py、exp2.py)、原始输出(exp-output.txt、exp2-output.txt),以及带逻辑形式和区分场景的完整对子清单,都在 quant-order-exp 目录里。第二次跑建成时,第一次跑的三个文件保持字节原样;第二次跑是独立的脚本和独立的记录,不是对第一次的修改。

我开始时的问题,现在有了一个更锋利的答案。向量检索看不见辖域。它看见的是和辖域相关的东西——多到能比词袋平局赢出百分之一分,少到在词相同、意思不同的地方没法信任。

demandez à l'IA à propos de ce essai·contexte : « same words, different meaning: can vector retrieval tell scope apart? / 同样的词,不同的意思:向量检索分得清辖域吗? »
›