跳转到内容
ForeverYoung
返回

当思考(CoT)遇见embedding

2018 到 2022 年,嵌入领域的格局其实没怎么动。BERT 定下双向注意力加 [CLS] 池化的基本盘,SBERT 把它做成句向量,DPR 搬进了稠密检索,E5、GTE、BGE 轮番把 MTEB 往上推,但底下那套东西一直没变:InfoNCE 对比损失,(查询、正例、难负例)三元组,纯编码器。

2023 年之后局面变了。一方面,仅解码器的大语言模型凭着规模和指令跟随杀进排行榜前列,BERT 量级的模型跟不上。另一方面,做搜索的工程师发现原始用户查询太短、太模糊,不得不在进嵌入模型之前,先拉一个独立的大语言模型来改写。两条路都跑起来了。

ICLR 2026 有两篇论文把这两条线拧在一起,问了一个简单的问题:改写步骤和嵌入步骤,能不能用同一个梯度来驱动?

BERT 时代的基准配置

标准配置是双编码器:一个模型同时处理查询和文档,共享权重,跑两次独立前向传播,各出一个向量,算余弦相似度。文档向量可以离线算好、建成索引,检索时只跑查询端,毫秒级搜几百万条没问题。交叉编码器精度更高,但要把查询和文档拼在一起跑,无法预计算,只能做精排候选集,第一阶段用不了。

训练方式是 InfoNCE 对比损失:算一个批次内的相似度矩阵,把对角线项(匹配对)往 1 推,其余往 0 推。

Query "best dense retrieval practices" Doc₁ (positive) "dense retrieval guide…" Doc₂ (negative) "Python syntax guide…" Doc₃ (negative) "Pasta recipe…" Shared BERT Encoder (same weights, independent forward passes) q d₁ d₂ d₃ Similarity Matrix (cosine) d₁ d₂ d₃ q 0.92 ✓ 0.11 0.08 push diagonal ↑ · push off-diagonal ↓ InfoNCE Loss gradient update on encoder weights
单一共享权重编码器的双编码器对比训练。查询和文档在独立的前向传播中编码,生成独立向量。相似度矩阵驱动 InfoNCE 损失,对角线项为正例,其余均为负例。

这套东西结实好用,一跑好几年。麻烦是从底层模型换成完整大语言模型之后才开始的。

对比微调会把推理和生成能力往下拉。训练目标只盯着那个池化向量,其他能力得不到任何梯度更新,会慢慢退化。BERT 量级的模型本来能力就有限,这几乎无所谓。对一个本来就能推理、会生成的 7B 模型来说,这笔代价就不能忽略了。

还有个更根本的问题:编码前没有”想一想”的余地。一次前向传播,token 进去,向量出来,没有任何推理发生的空间。BERT 本来也没这能力,无所谓。7B 大语言模型有,但整套训练方案根本没给它机会用。

仅解码器架构称霸排行榜

E5-mistral-7B(2023)把完全相同的对比训练方案搬到了 7B 仅解码器 Mistral 上。唯一需要适配的地方:原来的 encoder-only 方案用 [CLS] token 做池化,但 Mistral 是 decoder-only,没有 [CLS],于是改成了序列末尾的 [EOS] token。就这一处改动,登顶 MTEB。

Encoder-only (BERT) Decoder-only (Qwen3 / Harrier) [CLS] the quick fox [SEP] full bidirectional attention BERT Encoder (bidirectional) [CLS] Pool [CLS] → embedding first token, bidirectional context the quick fox runs [EOS] causal (left-to-right) attention Causal Transformer Decoder [EOS] Pool [EOS] → embedding last token attends to full left context
纯编码器(左)在完全双向注意力下对 [CLS] 标记进行池化。仅解码器(右)对最后一个 [EOS] 标记进行池化,在因果掩码下,该标记已对之前所有标记做过注意力,汇聚了完整序列的上下文。

2023 年以来的排行榜变化,一张表说明白:

YearModelArchitectureMTEB Score
2022–23E5-large, GTE-large, BGE-largeEncoder-only (330M)~62–64
2023E5-mistral-7BDecoder-only, 7B~66
2024NV-Embed-v2Decoder-only, 7B72.31 (EN)
2025Qwen3-Embedding-8BDecoder-only, 8B70.58 (multilingual)
2026Harrier-OSS-v1-27BDecoder-only, 27B74.3 (multilingual v2)

Harrier 值得单独说说。微软 2026 年 3 月悄无声息地在 Hugging Face 上放出了这个模型,MIT 许可,没有任何公告。基于 Gemma3,最后一个 token 池化加 L2 归一化,支持 100 多种语言和 32k 上下文。27B 版本目前是 Multilingual MTEB v2 第一名,270M 的小版本也能打过大多数纯编码器基准,这有点出乎意料。

解码器模型为什么能赢?规模是最直白的答案:70 亿参数就是比 3.3 亿大。但规模单独解释不了全部。指令跟随也有一份功劳:查询前加一句任务描述,大模型调整表征的幅度远不是 BERT 量级能做到的。最后一个 token 的池化也比看上去合理,decoder-only 模型生成到 [EOS] 时已经”看过”前面所有 token,序列信息都在里面,用作全局摘要没问题。

但这些模型仍然用对比损失训练。解码器被当成一个更大更好的 BERT,生成能力没被碰过一下。ICLR 2026 的两篇论文提的是下一个问题:如果真的让它发挥生成能力,会怎样?

向量之前的扩展

真实的搜索系统很少把用户查询原文直接拿去嵌入。用户输入的往往是”检索最佳实践”这样的字眼,短、模糊,信息量不够,直接向量化效果很差。工程上的通行做法是在进嵌入模型之前,先过一个独立的大语言模型:改写成多个释义,生成一段假想答案(模拟目标文档里可能有的内容),或者拆成子问题,总之先把查询扩展一遍,再向量化。

检索改写的方法非常有效,但有个问题:两边模型之间没有共同的优化目标。改写器不知道什么表述能产生好嵌入,嵌入器对查询是怎么被改写的也全无感知。各优化各的,最终检索效果靠两者的目标碰巧对上。

Production: separate pipeline GRACE / TTE: unified model User query (raw) LLM Query Rewriter generates multiple query variants q₁ q₂ q₃ no shared gradient Embedding Model separate, no rewriting gradient v₁, v₂, v₃ → merge → retrieve User query (raw) Unified LLM 1. generate elaboration rationale (GRACE) / CoT trace (TTE) 2. embed conditioned on elaboration joint gradient: elaboration quality = embedding quality enriched v → retrieve
生产中的查询改写(左)将工作分配给两个没有共享梯度的模型,改写器不知道什么能产生好嵌入,嵌入器也无法改进改写效果。GRACE 和 TTE(右)将这一流程整合到单一模型中:扩展与嵌入共享权重,扩展步骤因此能学会为检索质量服务。

GRACE 和 TTE 都是在解决这个问题,思路一样,切入点不同。GRACE 在训练时让模型生成推理说明,生成得好不好看检索排名说话,强化学习奖励。TTE 则在推理时把这一步显式化:推理器先输出思维链,嵌入器再综合原始查询和这段轨迹出向量。两种方案都让同一个模型承担两步,梯度流通,一起优化。

这跟生产流水线里两个独立模型的本质区别是:共享了权重,扩展步骤学到的就不再只是”把释义写得更流畅”,而是”怎么扩展才能让检索结果更好”。

GRACE:以对比信号作为奖励

Standard Contrastive Learning GRACE (RL) Query Doc BERT Encoder q d cosine sim(q, d) InfoNCE Loss gradient → encoder weights generative pathway unused during training Query LLM Policy generates rationale Rationale (natural language) "query relates to X, key aspects Y…" mean pool → q_vec Doc cosine sim → reward Reward signal policy gradient → policy weights
标准对比学习(左)将对比信号直接作为编码器权重的损失,生成路径被完全绕过。GRACE(右)将同一信号作为奖励:大语言模型生成自然语言推理说明,池化为向量,策略梯度训练模型写出更好的推理说明。

GRACE代码)只改了一件事:对比信号从损失变成奖励。一个字的区别,但逻辑完全不同。

标准对比学习里,InfoNCE 直接更新编码器权重——模型出向量,梯度教它往正例方向靠,生成能力全程没用到。GRACE 的路子是:先让大语言模型用自然语言解释这个查询——它在问什么、跟哪类文档相关——然后对这段解释做均值池化,得到嵌入向量。奖励是这个向量跟目标文档向量的余弦相似度。策略梯度(通过 verl 框架)根据这个奖励更新模型,让它把解释写得更好。

模型不再直接优化某个特定向量。它学的是”怎么把查询说清楚”。这次生成路径真的派上用场了。

数字方面:有监督设置下,四个骨干模型平均比标准对比学习高 11.5%;无监督高 6.9%。不错,但我更感兴趣的是另一块结果。通常的对比学习微调会把推理和生成能力明显往下压,GRACE 训练的模型大多没出现这种退化。原因很简单——整个训练过程中生成路径都在跑,没有被绕过去。

还有个附带效果:推理说明是人能读懂的。每个向量背后都有一段模型自己写的解释,说明它在出向量之前”想”了什么。嵌入第一次有了可解释性。

Think Then Embed:向量前的推理

GRACE 是在训练阶段做的。Think Then Embed 换了个地方:推理阶段。核心逻辑没变,只是把它放在了另一个时间点。

Think Then Embed (TTE) — inference pipeline Query / Image (multimodal input) Reasoner fine-tuned MLLM generates CoT trace Reasoning trace (CoT) "scene contains X near Y, key features are Z…" original query (direct) reasoning trace Embedder conditioned on: query + reasoning trace vec embedding
推理器生成解释查询语义的思维链轨迹。嵌入器随后基于原始查询和该轨迹共同生成向量。推理上下文成为表征的一部分。

论文本来针对的是多模态检索,但这个问题哪里都有。现在的多模态嵌入器都是单次前向传播:输入进去,向量出来,中间没有任何”想一想”的机会。想象一个查询:“找一张日落时有人站在水边、背景里还能看到别人的图片。“颜色、人物、地点、构图,全部细节都得在一次前向传播里消化。模型根本没机会搞清楚自己到底在找什么。

TTE 把这一步拆成两步。先是推理器(一个专门为检索任务微调的多模态大语言模型)输出一段思维链:这个查询在问什么、哪些视觉特征重要、什么样的图才算匹配。然后嵌入器综合原始输入和这段轨迹,出向量。

推理器不是通用描述模型,它是专门为检索训练的,学的是”哪些特征有助于找到匹配项”。在 MMEB-V2 上,TTE 比近期基准模型高 7%,包括一些用更大私有数据集训练的闭源模型。论文也测了把推理器和嵌入器合并成一个模型的方案,为了压低推理开销。

共同线索

两篇论文问的其实是同一个问题,只是从两个不同的门进来的。GRACE 在训练阶段改:策略梯度直接对准检索质量,塑造模型的表达方式。TTE 在推理阶段改:先思考,再编码,思考的过程是明文可读的。

它们共同的对手,是那条用两个独立模型、没有共享梯度的生产流水线。两篇论文的结论指向同一个地方:分开优化两个模块,比不上端到端地训练一个。

这其实是深度学习里最老的道理。端到端训练之所以有效,是因为梯度可以穿透所有步骤,让整个系统朝同一个目标调整。把步骤拆开、各自优化,每个接缝都是信息损耗。GRACE 和 TTE 在嵌入领域重新验证了这一点。


分享这篇文章:

上一篇
PyTorch 2.12 稀疏矩阵深度解析:COO、CSR、CSC、BSR 与 BSC
下一篇
手绘风格的数据可视化