- 为什么嵌入很重要
- 语义向量:嵌入的基本概念
- 对比学习:训练嵌入的方法
- 双编码器结构
- 难负例:难以区分的负例
- 训练数据与训练阶段
- E5、BGE、GTE 系列的概念
- Matryoshka 表征学习
- MTEB 基准
- 在检索与 RAG 中的应用
- 与重排序的关系
- 向量数据库与近似最近邻
- 检索质量评估指标
- 用自有数据微调
- 实务落地清单
- 局限与注意事项
- 结语
- 参考资料
为什么嵌入很重要
无论大语言模型(LLM)多么强大,要处理模型未曾学习过的内部文档或最新信息,都必须引入外部知识。这时决定"该调取哪些文档"的正是检索(retrieval),而现代检索的核心工具就是文本嵌入(text embedding)。
嵌入是指把单词、句子、段落这类文本转换成固定长度的实数向量。举例来说,把一个句子变成 768 维向量,就等于把这句话的"含义"压缩表示成 768 个数字。在这个向量空间里,意义相近的句子会聚在一起,意义不同的句子则彼此远离。
本文将探讨堪称检索与 RAG 心脏的文本嵌入模型的原理。内容涵盖对比学习这一训练方法、双编码器结构、难负例挖掘,以及以 E5/BGE/GTE 为代表的最新开源系列、Matryoshka 表征学习、MTEB 基准,最后还会谈到与重排序的关系。由于 AI 的 SOTA 变化非常迅速,本文会把重点放在概念与架构原理上,而不是具体排名或数值。
语义向量:嵌入的基本概念
向量空间中的语义
传统的文本表示方法 TF-IDF 或 BM25 依赖于词语的出现频率。这类方式无法把"小狗"和"狗"这样写法不同但意义相同的词关联起来。这被称为词汇不匹配(lexical mismatch)问题。
相反,密集嵌入(dense embedding)把意义表示为向量,因此即便写法不同,只要意义相近,向量也会相近。比如下面这样的关系,就可以在向量空间中被捕捉到。
"小狗在公园里跑" ---- 相近 ---- "狗在外面玩"
| |
疏远 疏远
| |
"加息对股市的影响" --------------------------
相似度测量
两个嵌入向量有多接近,通常用余弦相似度(cosine similarity)来衡量。余弦相似度是两个向量夹角的余弦值,方向越一致就越接近 1,方向相反则越接近 -1。
余弦相似度(a, b) = (a 点积 b) / (norm(a) * norm(b))
- 取值范围:-1(完全相反) ~ 1(方向相同)
- 大多数嵌入模型输出的是归一化向量,
因此余弦相似度就等于内积(dot product)。
检索系统会计算查询(query)嵌入向量与预先嵌入并存储好的文档向量之间的相似度,找出最接近的文档。这称为最近邻搜索(nearest neighbor search)。当文档数量很多时,会使用近似最近邻(ANN, Approximate Nearest Neighbor)算法和向量数据库来实现快速检索。
对比学习:训练嵌入的方法
对比学习的直观理解
要打造好的嵌入,就必须训练模型,让它把"该近的放近,该远的放远"。为此而生的代表性方法就是对比学习(contrastive learning)。
对比学习的基本单位是一种三角关系:作为基准的锚点(anchor)、与锚点意义相通的正例(positive),以及与锚点无关的负例(negative)。
锚点 (问题)
/ \
拉近变近 推远变远
/ \
正例 (正确文档) 负例 (无关文档)
随着训练推进,锚点与正例的向量会相互吸引、逐渐靠近,锚点与负例的向量则相互排斥、逐渐远离。用海量数据反复这个过程,就会形成一个能很好反映语义结构的向量空间。
InfoNCE 损失函数
对比学习中最广泛使用的损失函数是 InfoNCE。NCE 是 Noise Contrastive Estimation 的缩写,它把训练定义为一个分类问题——从一个正例和多个负例中挑出正例。
InfoNCE 损失 (锚点 q, 正例 p, 负例集合 N)
exp(sim(q, p) / T)
L = -log ---------------------------------------
exp(sim(q, p)/T) + Σ exp(sim(q, n)/T)
n in N
- sim:相似度(通常为内积或余弦)
- T:温度(temperature)参数,越小分布越尖锐
- 分子是正例,分母是正例 + 全部负例
直观地说,这个损失给出的信号是"提高与正例的相似度,降低与负例的相似度"。由于它是 softmax 形式,重要的是正例相对负例高出多少。
批内负例
如何获取负例,直接决定训练效率。最常见的技巧是批内负例(in-batch negatives)。当一个批次中包含多个(问题, 答案)对时,对某个问题而言,除了自己的答案之外,其余各对的答案都会成为负例。
批次中有 4 组 (问题, 答案) 时
答案1 答案2 答案3 答案4
问题1 [ 正例 负例 负例 负例 ]
问题2 [ 负例 正例 负例 负例 ]
问题3 [ 负例 负例 正例 负例 ]
问题4 [ 负例 负例 负例 正例 ]
对角线是正例,其余是负例,一次性一起学习
这种方式不需要额外数据,就能免费获得与批大小成正比的负例,因此很高效。所以据称在嵌入训练中,更大的批大小往往有利于性能。
双编码器结构
什么是双编码器
用于检索的嵌入模型大多采用双编码器(dual encoder, bi-encoder)结构。问题和文档各自独立编码成向量,仅凭这两个向量的相似度来判断相关性。
问题 文档
| |
[编码器 A] [编码器 B]
| |
问题向量 文档向量
\ /
\-- 计算相似度 -----/
|
相关性得分
关键在于,问题和文档互不相看,各自独立变成向量。这样一来,文档向量就可以提前计算好并存储起来,检索时只需要编码问题即可。这种可预计算性,正是让大规模检索变得实用的决定性特征。
很多情况下,编码器 A 和 B 会共享权重,也就是说用一个编码器同时编码问题和文档。不过,有时也会附加告知"这是问题""这是文档"的前缀(instruction/prefix)来区分角色。E5 系列使用 query: 和 passage: 前缀就是典型例子。
与交叉编码器的对比
常与双编码器相比较的是交叉编码器(cross-encoder)。交叉编码器把问题和文档拼接成一个整体一起送入编码器,直接用注意力机制计算两者之间的交互。
[问题 + [SEP] + 文档] → [编码器] → 单一相关性得分
交叉编码器同时看到问题和文档,因此精度更高,但每篇文档都要和问题配对逐次计算,无法预先计算,速度也慢。所以交叉编码器通常用在重排序阶段,而一次检索则由双编码器承担,这是常见的分工方式。这层关系会在后面的重排序小节再次讨论。
难负例:难以区分的负例
为什么需要难负例
批内负例得到的负例,大多是与问题完全无关的文档,很容易区分。如果模型反复看到的都是这种简单负例,它筛选出"表面相似但实际并非正确答案"这类微妙文档的能力,就很难得到提升。
于是人们特意构造"容易混淆的负例",也就是难负例(hard negative),加入训练。难负例是指表面上看起来与问题相关、但实际并非正确答案的文档。
问题:"在 Python 中对列表排序的方法"
简单负例:"猫粮推荐" (完全无关,容易区分)
难负例:"在 Python 中创建列表的方法" (主题相同,
但不是问题的答案)
难负例挖掘
难负例通常通过挖掘(mining)过程收集。典型做法是:用已经训练好的检索模型针对问题取出排名靠前的文档,再从中挑出并非正确答案的高排名文档作为难负例。把这样收集到的难负例重新投入训练,模型就会逐渐学会区分更细微的差异。
不过,挖掘出的文档中,可能混有"只是没有打上标签,实际上是正确答案"的情况(false negative)。如果把这些误当负例来训练,反而会拉低性能,所以需要注意排除排名最靠前的几个,或做额外的过滤。
训练数据与训练阶段
数据的种类
嵌入模型的性能很大程度上取决于训练数据的数量与多样性。常用的数据形式如下。
- 查询-文档对:从检索日志、QA 数据集等中获得的(问题, 相关文档)对
- 自然语言推理(NLI)数据:用蕴含/矛盾关系来定义正例和负例
- 句子对相似度数据:带有人工标注相似度分数的句子对
- 网络上天然配对的数据:标题-正文、问题-答案、引用-原文等
对比预训练与监督微调
许多最新的开源嵌入模型都采用两阶段训练。
[第一阶段:大规模弱监督对比预训练]
用网络上天然配对的海量文本对做
对比学习。标签质量低,但数量非常大。
|
v
[第二阶段:高质量监督微调]
用精选的检索/QA/NLI 数据集 + 难负例做
对比学习。数量少,但质量高。
E5 系列据称是清晰提出这一"先弱监督预训练、再监督微调"流程的代表案例。第一阶段学习广泛的语义结构,第二阶段则针对检索任务做精细打磨。
E5、BGE、GTE 系列的概念
这里我们来看代表性开源嵌入系列的概念。具体的版本号或基准排名会随时间和版本频繁变化,因此这里以各系列的核心思路为主来梳理。
E5 系列
E5 据称得名于"EmbEddings from bidirectional Encoder rEpresentations",是把前文所说的弱监督对比预训练与监督微调这一两阶段训练体系化整理出来的系列。它的特征是分别给问题和文档加上 query: 和 passage: 前缀来区分角色。此后,它还扩展出了多语言版本、大型模型,以及以 LLM 为骨干、基于指令(instruction)构建嵌入的方向。
BGE 系列
BGE 是 BAAI General Embedding 的缩写,是由北京智源人工智能研究院(BAAI)公开的嵌入系列。据称它结合了大规模对比预训练与监督微调,广泛支持多种语言和任务。尤其值得一提的是,还公开过在密集嵌入之外、同时处理稀疏表示和多向量表示的扩展版本,尝试把多种检索方式结合到一个模型中。
GTE 系列
GTE 是 General Text Embeddings 的缩写,是阿里巴巴系列公开的嵌入模型。据称它同样采用多阶段对比学习,并广泛混合多个领域的数据来提升通用性。
系列对比小结
| 系列 | 公开主体(据称) | 特征概念 | 角色前缀 |
|---|---|---|---|
| E5 | Microsoft 研究系列 | 确立弱监督预训练 + 监督微调 | 使用 query/passage |
| BGE | BAAI | 多语言、多表示结合扩展 | 存在基于指令的变体 |
| GTE | 阿里巴巴系列 | 多领域混合对比学习 | 存在基于指令的变体 |
上表的细节可能随时间和版本而变化,实际引入时最好查阅各自的模型卡和官方文档确认。
Matryoshka 表征学习
维度截断的思路
嵌入维度越大,表达能力越强,但存储成本和检索成本也会随之增加。Matryoshka 表征学习(Matryoshka Representation Learning, MRL)正是为此而生。它的名字来自俄罗斯套娃玛特廖什卡(Matryoshka)。
核心思路是:训练一个大的嵌入,但即使只截取前面一部分维度来用,语义也依然保留。也就是说,即便只用 768 维向量的前 256 维,甚至前 128 维,也能得到一个不错的嵌入。
完整嵌入 (例如:768 维)
[■■■■■■■■■■■■■■■■■■■■■■■■]
└前 64┘
└── 前 128 ──┘
└──── 前 256 ────┘
└──────── 前 512 ────────┘
└────────── 前 768 (全部) ──────────┘
无论从前面截到哪个位置,
都训练成独立可用的嵌入
训练方法与优点
MRL 的训练方式是:针对多个截断点分别计算对比学习损失,再最小化这些损失之和。结果是一个模型就能同时提供多种维度的嵌入。
在实务中,这一特性是这样被利用的:先用较短的维度(例如前 128 维)对海量候选做快速的一次检索,再只对排名靠前的候选用完整维度精确重新计算。这样既节省了存储空间和检索速度,又把精度损失降到最低。不过,如果维度削减得太狠,精度也会下降,因此需要通过实验找到适合具体任务的截断点。
MTEB 基准
什么是 MTEB
要相互比较嵌入模型,就需要一个共同的评估标准。为此而生的代表性基准就是MTEB(Massive Text Embedding Benchmark)。MTEB 把嵌入被用到的多种任务类型汇总在一起,做综合评估。
MTEB 包含的任务类型(代表性举例)
- Retrieval : 检索(用查询找相关文档)
- Reranking : 重排序
- Clustering : 聚类
- Classification: 分类
- STS : 句子语义相似度
- Pair Classification / Summarization 等
由于是把跨多个任务的分数综合起来,只在特定任务上过拟合的模型,很难在总排名上名列前茅。此后,MTEB 也朝着广泛覆盖多语言的方向不断扩展。
解读基准时的注意事项
MTEB 排行榜很有用,但把排名当作绝对标准来看待是有风险的。这里有几点需要注意。
- 排名和分数会随版本、时间、评估设置而变化。"目前排名第一"这种说法很快就会过时。
- 基准分数与实际业务数据上的表现并不总是一致。如果领域特性不同,排名可能会颠倒。
- 模型大小、嵌入维度、推理速度、许可证等实务限制条件,也需要一并考虑。
因此,最好把 MTEB 当作缩小候选范围的起点,最终选择则应该用自己的数据来直接评估。
在检索与 RAG 中的应用
RAG 流水线中的嵌入
RAG(Retrieval-Augmented Generation)是一种通过检索找到相关文档,再拼接到 LLM 输入中来生成答案的结构。嵌入正是其中检索阶段的核心。
[文档索引准备阶段 (离线)]
文档 → 分块 → 嵌入 → 存入向量数据库
[查询处理阶段 (在线)]
用户提问 → 问题嵌入
→ 在向量数据库中检索相似文档(top-k)
→ (可选)重排序
→ 把文档 + 问题传给 LLM
→ 生成答案
分块与嵌入
把一篇长文档整体嵌入会让语义变得模糊,所以通常会切分成适当大小的分块(chunk)分别嵌入。分块大小和重叠(overlap)如何设定,会极大影响检索质量。分块太大会混入无关内容,太小则上下文被切断,语义变弱。
此外,不同模型能处理的最大输入长度也不一样,分块时要注意不要超出这个限制。最近支持长上下文的嵌入模型也越来越多,但输入越长、语义越容易被稀释这一点,依然需要考虑。
混合检索
仅靠密集嵌入,在精确关键词匹配(专有名词、代码、数字等)上可能会比较弱。因此常常会用到把密集检索和 BM25 这类稀疏检索结合起来的混合检索。把两种方式的分数结合起来,就能同时取得语义检索和精确匹配的优点。
[密集检索] 按语义相似度得到候选 A
[稀疏检索] 按关键词匹配得到候选 B
\ /
分数结合(例如:RRF)
|
最终候选列表
这里的 RRF 指的是 Reciprocal Rank Fusion,是一种把各检索结果的排名取倒数再相加来融合的简单而稳健的方法。
与重排序的关系
前面对比过双编码器和交叉编码器。实务中的检索往往会分成两个阶段。
[第一阶段:检索 (双编码器)]
从向量数据库中快速取出 top-k 候选
(例如:前 100 个)
|
v
[第二阶段:重排序 (交叉编码器)]
只对这 100 个候选做精细重新打分、调整排序
只把排名靠前的少数几个传给 LLM (例如:前 5 个)
第一阶段速度更重要,因此由可以预先计算的双编码器承担;第二阶段精度更重要,因此由同时看问题和文档的交叉编码器承担。这样做既能避免用交叉编码器给全部文档打分的开销,又能提升最终结果的精度。嵌入检索和重排序不是竞争关系,而是相辅相成的关系。
向量数据库与近似最近邻
为什么需要近似
当文档数量达到几百万、几千万时,逐一计算查询向量与所有文档向量的相似度就太慢了。因此实务中会使用近似最近邻(ANN, Approximate Nearest Neighbor)算法,用牺牲一点精度为代价,把检索速度提升数十甚至数百倍。
[穷举搜索(精确)]
查询向量 vs 全部文档向量逐一比较
→ 精确,但速度随文档数量成比例变慢
[近似最近邻(ANN)]
提前把向量整理成索引结构,
只挑选邻近的区域来搜索
→ 有轻微遗漏的可能,但快得多
代表性的索引方式
ANN 索引有多种方式。基于图的 HNSW(Hierarchical Navigable Small World)把向量连接成多层图结构,沿着邻近的节点快速搜索。另一种方式是把向量拆分成多个片段并压缩的乘积量化(PQ, Product Quantization),能大幅节省内存。实务中的向量数据库内部会带有这类索引,并支持结合过滤条件(例如:只检索特定文档类型)进行搜索。
索引在召回率(recall)、速度和内存之间存在权衡。调整参数、在"多快"和"多准"之间取得平衡,正是实务调优的核心。
检索质量评估指标
要定量评估嵌入与检索质量,就需要有指标。这里整理一些代表性的指标。
[代表性检索评估指标]
- Recall@k : 正确答案落在前 k 个之内的比例
- MRR : 正确答案首次出现排名的倒数的平均值
- nDCG@k : 按排名位置加权的累计增益
- MAP : 针对多个正确答案的平均精度的平均值
这里 Recall@k 看的是"有没有漏掉正确答案",MRR 和 nDCG 看的是"把正确答案放在了多靠前的位置"。像 RAG 这样只把排名靠前的少数结果传给 LLM 的场景,头部排序质量(nDCG、MRR)尤其重要。用自己的数据测量这些指标来挑选模型和参数,远比排行榜更值得信赖。
用自有数据微调
如果通用嵌入模型在自己的领域中表现不足,可以考虑用自有数据做微调。常见的流程如下。
[自有数据微调流程]
1. 收集 (问题, 正确文档) 对 (检索日志、人工标注等)
2. 挖掘难负例 (用现有模型收集排名靠前的错误答案)
3. 用 InfoNCE 等对比学习方法微调
4. 用自有评估集测量 Recall@k、nDCG
5. 若有改善则部署,否则重新调整数据/设置
微调能大幅提升领域性能,但如果数据量小,会有过拟合的风险,而且换模型就得重建索引,这也是一笔成本。因此,比较合理的顺序是先尝试调整前缀、混合检索、重排序这类低成本的改进,等这些依然不够时,再转向微调。
实务落地清单
整理一下把嵌入模型引入实际业务时需要考虑的要点。
- 语言:确认所选的多语言模型是否很好地支持服务面向的语言。
- 领域:用自己的数据直接评估,不要只相信排行榜排名。
- 维度与成本:嵌入维度直接决定存储/检索成本。支持 MRL 的模型可以通过调整维度来节省成本。
- 前缀/指令:必须严格遵守模型要求的前缀或指令格式,遗漏会导致性能大幅下降。
- 归一化:如果要用余弦相似度,需确认是否需要对向量做归一化。
- 重排序:如果精度很重要,可以考虑加入交叉编码器重排器。
- 许可证:确认是否允许商业使用及具体许可条款。
局限与注意事项
基于嵌入的检索也有明显的局限。
- 领域迁移:在与训练数据性质不同的领域中,性能可能下降。专业术语越多的领域,自我评估就越重要。
- 精确匹配的弱点:密集嵌入在专有名词、代码、数字等精确匹配上可能较弱,很多时候需要混合检索。
- 分块敏感性:结果会因分块大小和边界设置的不同而产生很大差异。
- 时效性:基准排名和最新模型信息变化非常快。本文对各系列的说明也只是为了帮助理解概念,具体规格务必以官方文档为准。
- 偏见:训练数据中的偏见可能反映到嵌入中,在敏感应用中需要加以验证。
结语
文本嵌入是检索与 RAG 的心脏。对比学习与 InfoNCE 这一训练原理、双编码器这一结构、难负例这一训练技巧,再加上 Matryoshka 表征学习、混合检索、重排序——这些要素相互咬合,共同构成了现代语义检索。
需要记住的要点是这样的。第一,嵌入是把语义压缩成向量,并通过对比学习被训练成"该近的就近"这种排布。第二,用双编码器做快速检索、用交叉编码器做精细重排序的两阶段结构,是实用的做法。第三,模型的选择应该由自己的数据来决定,而不是排行榜。AI 领域的 SOTA 变化很快,但这些原理会长期有效。
参考资料
- InfoNCE / Contrastive Predictive Coding (arXiv 1807.03748): arxiv.org/abs/1807.03748
- Dense Passage Retrieval (arXiv 2004.04906): arxiv.org/abs/2004.04906
- Sentence-BERT (arXiv 1908.10084): arxiv.org/abs/1908.10084
- Text Embeddings by Weakly-Supervised Contrastive Pre-training, E5 (arXiv 2212.03533): arxiv.org/abs/2212.03533
- Matryoshka Representation Learning (arXiv 2205.13147): arxiv.org/abs/2205.13147
- MTEB: Massive Text Embedding Benchmark (arXiv 2210.07316): arxiv.org/abs/2210.07316
- MTEB 排行榜 (Hugging Face): huggingface.co/spaces/mteb/leaderboard
- BGE (FlagEmbedding) 仓库: github.com/FlagOpen/FlagEmbedding
- Sentence Transformers 文档: sbert.net
현재 단락 (1/197)
无论大语言模型(LLM)多么强大,要处理模型未曾学习过的内部文档或最新信息,都必须引入外部知识。这时决定"该调取哪些文档"的正是**检索**(retrieval),而现代检索的核心工具就是**文本嵌入...