跳转至

第六章:Embedding 原理与技术演进

6.1 Embedding 到底在做什么

本章先讨论稠密文本 Embedding:把文本映射成向量,让训练目标所定义的相似或相关文本更容易匹配。 检索中,问题与能回答它的段落不必是同义句。

这句话里有两个关键点,缺一个都不完整:

  1. 稠密:几百到几千维的实数向量,每一维没有独立的可解释含义,语义分布在整个向量上;
  2. 语义相近 → 距离近:这是靠训练目标刻意造出来的性质,不是自动出现的。

第二点是理解 Embedding 的关键。「猫」和「狗」的向量之所以接近,不是因为模型显式存着「它们都属于动物」这条知识,而是因为训练时它们大量出现在相似的上下文中,参数被优化成给出相近的表示。

6.2 为什么 RAG 需要它

未做词项扩展的关键词检索依赖分词后的词项重合。「怎么请年假」与「带薪休假申请流程」可能重合不足;同义词词典、分词和学习式稀疏表示也能缓解这个问题。

Embedding 把两者都映射到「休假申请」这个语义区域,从而能匹配上。

这就是 RAG 用向量检索的根本原因:它跨越了「同一个意思的不同说法」这道鸿沟。

但也要提前说明:向量检索并非全面优于关键词检索。精确的产品型号、错误码、人名、专有术语,关键词检索反而更可靠——这是第十一章要展开的内容。

6.3 四类表示方法

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 22, "padding": 8, "wrappingWidth": 160}}}%%
flowchart TB
    G1[静态词向量] --> G2[上下文词向量]
    G2 --> G3[句向量 双塔]
    G3 --> G4[后期交互 多向量]

上图和下文的“代”按技术思路组织,不是严格年代或替代顺序;双塔、多向量与 cross-encoder 仍可并存。

6.3.1 第一代:静态词向量

代表:Word2Vec、GloVe、FastText。

共同思想:利用词的上下文分布学习表示,但训练目标不同。Word2Vec 的 CBOW/Skip-gram 预测中心词或上下文;FastText 在这类目标中加入字符子词信息;GloVe 则拟合全局词共现统计,使用加权最小二乘目标,不能都称为“预测上下文”。

贡献:以高效的分布式表示学习词语关系;此前已有 LSA 等语义表示方法,并非首次让语义相似度可计算。

致命局限:一词一向量,无法处理多义词。

「苹果」在「吃苹果」和「苹果发布会」里含义不同,但静态词向量给它同一个表示,不能随本次语境区分词义;这不等于两种含义的算术平均。

6.3.2 第二代:上下文相关的词向量

代表:ELMo、BERT。

核心突破:同一个词在不同句子里有不同的向量。BERT 通过双向 Transformer 编码整个句子,每个 Token 的表示都依赖于它的具体上下文。

这缓解了一词一向量的限制,但不保证所有歧义都能正确消解。

这里需要单独区分一个常见误解:

BERT 是编码器骨干,不等于某一种检索架构;未经句向量训练,直接池化通常不是好的检索基线。

原因有两个:

第一,原生 BERT 的句向量质量差。 直接取 [CLS] 位置的向量或对所有 Token 向量做平均,得到的句向量在语义相似度任务上表现很差——因为 BERT 的预训练目标(掩码语言建模)根本没有优化句子级的相似度。

第二,若采用 cross-encoder,就不能像双塔那样离线缓存整段文档表示用于直接打分。

BERT 可以分别编码文本,也可以把 Query 与文档拼接后微调为 cross-encoder。后者意味着:

Cross-Encoder 交互式

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 22, "padding": 8, "wrappingWidth": 160}}}%%
flowchart TB
    Q1[Query] --> CAT[拼接]
    D1[文档] --> CAT
    CAT --> M1[模型] --> S1[相似度分数]

Cross-Encoder 在线为每个 Query–文档对评分,可以批处理,但计算量较大。

Bi-Encoder 双塔

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 22, "padding": 8, "wrappingWidth": 160}}}%%
flowchart TB
    Q2[Query] --> ME1[模型] --> V1[Query 向量]
    D2[文档] --> ME2[模型] --> V2[文档向量<br/>离线预先算好]
    V1 --> SIM[向量距离]
    V2 --> SIM

双塔模型离线预计算文档向量,在线只需编码 Query。两侧向量在距离计算时相遇,不会进入一个联合的 Query–文档编码器。

百万级候选需要在线评分百万个文本对。 批处理减少调用开销,但不能免去这些交互计算,因此通常先用可预计算文档表示的检索器缩小范围。

6.3.3 第三代:专门优化的句向量模型(双塔)

代表:Sentence-BERT、SimCSE、BGE、E5、GTE、Qwen3-Embedding。

两个关键改进:

改进一:架构改成双塔(bi-encoder)。 Query 和文档分别独立编码,各自得到一个向量,用向量距离衡量相似度。这样文档向量可以在离线阶段全部算好并建索引,在线只需算一次 Query 向量。

这是让向量检索在工程上可行的决定性一步。

改进二:训练目标对齐句子表示或检索任务。 对比学习是常用路线:以相关 Query—文档或相似句对为正例,以不相关样本为负例。原始 Sentence-BERT 还比较了分类、相似度回归和三元组目标,不能把双塔架构等同于某一种损失函数。

无监督 SimCSE 展示了一个简洁的做法:同一个句子过两次模型,用独立 Dropout 得到两个略有差异的向量,把这一对当作正样本;同批其他句子作为负样本。论文还给出使用自然语言推断标签的监督版本。批内样本可能实际同义,需要留意假负例。

这一代是当前 RAG 的标准配置。

代价:双塔在线编码时没有跨 Query—文档注意力,匹配依赖压缩后的表示;cross-encoder 通常能补充细粒度交互,但精度高低还取决于训练和领域匹配。

这正是 RAG 采用「双塔粗排 + cross-encoder 精排」两阶段架构的根本原因——用双塔的速度缩小范围,用 cross-encoder 的精度定顺序。第一章 1.5 节的漏斗结构,在这里得到了技术上的解释。

6.3.4 第四代:后期交互与多向量

代表:ColBERT、ColBERTv2、ColPali。

核心思想:在「单向量双塔」和「拼接式 cross-encoder」之间取中间路线。

文档不再压缩成一个向量,而是保留每个 Token 的向量。检索时计算 Query 每个 Token 与文档所有 Token 向量的最大相似度,再求和。

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 22, "padding": 8, "wrappingWidth": 160}}}%%
flowchart TB
    A[单向量双塔] --> B[多向量后期交互]
    B --> C[Cross-Encoder]

箭头比较表示与交互方式,不代表执行阶段。单向量双塔将文档压缩为一个向量,会丢失细粒度信息。后期交互为每篇文档保留 N 个向量,存储取决于数量、维度、数据类型和压缩方式。Cross-Encoder 拼接 Query 与文档,增加在线跨文本交互,因此无法离线预计算联合表示。

优点:保留 Token 级匹配信息,同时文档表示仍可离线预计算。ColBERT 系论文在所测检索任务上展示了收益,但不能推出任意多向量模型都胜过单向量模型,或必然接近 cross-encoder。

代价是每文档多向量的存储与匹配;实际倍数取决于 Token 数、维度和压缩。ColBERTv2 论文使用残差压缩,不能把未经压缩的向量数量比直接当作总成本比。

第四代的一个重要衍生是把它用到视觉上:直接对文档页面图像做多向量嵌入,绕开 OCR 和版面解析(第三章 3.5 节讨论过)。

6.4 相似度怎么算

度量 说明 备注
余弦相似度 只看方向不看长度 RAG 最常用
点积 同时受方向和模长影响 归一化后与余弦等价
欧氏距离 空间直线距离 归一化后与余弦单调等价

实践要点:不要假定输出已归一化,先按模型卡检查。对两端 L2 归一化的非零向量,点积等于余弦,平方欧氏距离等于 2 − 2 × 点积,因此精确排序等价;ANN 实现和量化仍可能产生不同候选。未归一化时点积受模长影响,不能直接换度量。查询算子还必须匹配数据库索引,否则可能不走索引,而非一定算错。

6.5 使用时的几个硬性约束

6.5.1 建库和查询必须用兼容的编码器对

不同模型的向量位于不同的语义空间,跨空间计算距离没有任何意义,且不会报错——只会静默地返回一堆不相关结果。

共同训练的双塔可以使用不同权重,例如 DPR 的 Query 与 Passage 编码器。若 Document 表示变化,通常要重嵌入和迁移索引;仅改变 Query 侧但仍保持兼容时,不必一概重建。兼容性由模型契约和回归评测保证,不由维度相同保证。

6.5.2 注意输入长度上限

输入上限依模型和服务配置而异,不能统一记为 512 Token;超长可能报错或被 tokenizer 截断。对照模型卡检查最大长度、前缀和特殊 Token,并在入库时监控截断。

6.5.3 注意指令前缀

不同 Embedding 模型的 Query 与文档输入约定不同。E5、BGE 等模型可能有特定前缀或指令约定(如 query: / 为这个句子生成表示以用于检索相关文章:),文档可能不加或使用另一前缀,应按各自模型卡配置。Qwen3-Embedding 官方模型卡【362】推荐为 Query 提供任务指令,但这不表示缺少指令就会使调用无效。

遗漏适用的前缀或指令可能降低效果,但影响取决于模型和任务;应按模型卡配置,并在目标查询集上验证。 不要把检索效果建议混同于 API 输入有效性要求。

6.5.4 对称与非对称检索是两回事

类型 场景 说明
对称检索 句子 vs 句子 两侧长度和形式相近,如相似问题匹配
非对称检索 短 Query vs 长文档 RAG 的典型场景

RAG 需要的是非对称检索能力。 用在对称任务上训练的模型做 RAG,效果会打折扣——这也是为什么不能只看模型在通用相似度榜单上的分数。

6.6 常见错误

6.6.1 说「BERT 就能做 RAG 检索」

BERT 可用作双塔或重排骨干;问题在于是否经过适合的检索训练,以及能否预计算文档,而不是骨干名字。

6.6.2 不知道为什么需要双塔

答不出「文档向量必须能离线预计算」,说明没理解检索的工程约束。

6.6.3 把 Embedding 模型和 Rerank 模型混为一谈

Embedding 是表示方式,Rerank 是流水线阶段;常见重排采用 cross-encoder,也可采用多向量精确打分或 LLM 排序,不能把两者当作一一对应的架构名称。

6.6.4 忘记加指令前缀

模型卡明确要求的前缀不加,会明显掉点。

6.6.5 混用不同模型的向量

不报错,但结果全错。

6.6.6 只记模型名不讲演进逻辑

「有 Word2Vec、BERT、BGE」这种罗列没有信息量。要说清每一代解决了上一代的什么问题。

6.7 本章总结

  1. Embedding 把文本映射为稠密向量,让语义相似度可计算;这个性质来自训练目标,不是自动出现的;
  2. 第一代静态词向量解决了「语义可计算」,但一词一向量、无法处理多义;
  3. 上下文词向量缓解多义性;BERT 可作为不同检索架构的骨干,直接池化不等于已训练好的检索表示;
  4. 双塔句向量模型是常用方案:架构上可离线预计算,训练上对齐句子或检索任务;对比学习不是唯一目标;
  5. 后期交互保留 Token 级匹配;空间与算力取决于向量数、维度和压缩率,ColBERTv2 的压缩也应计入比较;
  6. 两阶段架构的技术根源:先复用文档表示缩小范围,再为候选增加交互计算;质量收益仍要评测;
  7. 硬性约束:兼容编码器对、实际长度预算、模型指定前缀与度量,并区分对称和非对称任务。

参考资料

本章的参考资料、阅读建议与来源说明见集中参考资料章节。