Embedding 模型:把文本变成向量的核心组件

RAG 系统里,Embedding 模型负责把「文本」变成「向量」:一段文字输入模型,输出一个固定长度的浮点数组(比如 768 维或 1024 维),语义相近的文本,向量距离就近。向量检索的本质就是在向量空间里找「最近邻」——把用户问题向量化,在库里找最接近的文档向量。所以 Embedding 模型的质量,直接决定检索「能不能召回对的文档」,是整个 RAG 管线的第一道闸门。

Embedding 模型和使用体验最相关的两个特性是「维度」与「对比学习训练」。维度影响存储和检索成本:1024 维比 768 维需要更多索引内存和更多计算,但通常能装下更细的语义差异。几乎所有现代 Embedding 模型都用对比学习训练:训练时给模型看「匹配的正样本对」和「不匹配的负样本对」,让它学会把正样本拉近、负样本推远——这就是 Embedding 模型能力的来源,也是我们后续微调的理论基础。

本文核心知识点
  • Embedding文本向量化是 RAG 检索的第一道闸门。
  • 对比学习正负样本对训练是 Embedding 能力的来源。
  • 微调用领域数据继续训练可以显著提升召回质量。
  • 评估召回率、MRR 等指标决定能否上线。
Embedding 微调配图:左侧文档方块经虚线投影到右侧半透明高维球体,球体内散布向量圆点,外圈训练回路箭头与齿轮,蓝色渐变扁平风格
文本经过 Embedding 模型映射到高维向量空间,语义相近的文本在空间中彼此靠近。

选型评估:MTEB、领域适配与成本

选 Embedding 模型,第一步看通用能力:MTEB(大规模文本嵌入基准)汇总了多语言检索、相似度、重排、分类等几十个子任务,是行业通用的横向对比榜单。第二步看领域适配:通用榜单第一的模型,到了你所在的垂直领域(医疗、法律、电商、代码)未必最好——你的语料里满是专业术语和特有表达,通用模型可能理解不到位。第三步看成本和工程约束:模型大小(参数量与推理速度)、向量维度(存储与检索成本)、最大输入长度(长文档要不要切块)都要纳入考量。

有一个容易被忽略的点是「输入输出的不对称性」:检索场景里,用户问题通常是短查询,文档是长文本。很多模型对短文本和长文本的语义空间并不完全一致。如果你的场景是「短问题检索长文档」,选型时优先看模型在「短查询→长文档」匹配任务上的表现,而不是只看整体平均分。做完初步选型,用一批真实业务问题做人工抽样评测,比任何榜单数字都可靠。

# 用开源库快速对比候选模型(示意)
# 1. 准备 50 条真实业务 query + 每条对应 1 条相关文档
# 2. 候选模型:bge-large-zh、m3e、text-embedding-3 等
# 3. 对每个模型:文档批量向量化 → 建索引 → query 检索 Top5
# 4. 人工判定 Top5 里相关文档是否出现在前几位
# 5. 记录命中率,结合维度/速度/价格综合决策

对比学习微调的原理:正样本、负样本与损失函数

为什么通用模型在垂直领域会「失灵」?因为训练数据里你的行业表达很少。微调就是用「你的领域数据」继续训练 Embedding 模型,让它把你的术语、简称、特有表达映射到正确的语义位置。Embedding 微调和 LLM 微调(比如 LoRA 微调大模型)不同:LLM 微调学「怎么回答」,Embedding 微调学「什么和什么是一回事」——训练目标是把匹配的文本对拉近、不匹配的推远。

核心是「对比损失」:一个训练批次里有正样本对(query 和它的相关文档)和一批负样本(不相关的文档),模型要学到正样本对相似度尽量高、负样本对相似度尽量低。实现上用 InfoNCE 损失:把「正样本对的相似度」除以「正样本加所有负样本的相似度之和」,让这个比值尽量接近 1。负样本的质量比数量更重要:随机负样本太容易区分,模型学不到东西;「难负样本」——和 query 很像但答案不同的文档——才是真正逼模型进步的关键。

# InfoNCE 对比损失(PyTorch 风格伪代码)
# sim = 余弦相似度矩阵(batch 内 query × doc)
# 对角线是正样本对,其余是批内负样本
# labels = arange(batch) 表示每行应该匹配的 doc 下标
loss = cross_entropy(sim / temperature, labels)
# 难负样本:把容易混淆的文档显式加入负样本集
# 再配合 in-batch 负样本一起参与 loss 计算

训练数据准备:query-doc 对与难负样本挖掘

微调 Embedding 模型,数据质量决定上限。最基础的数据是「query-doc 正样本对」:一条真实用户问题,对应一篇应该被召回的文档。来源可以是搜索日志(用户点了哪个结果)、业务标注(专家标记的相关文档)、或者用 LLM 从文档生成问题(合成数据)。正样本的数量要求不算高,几千到几万条就能看到明显效果,关键是「覆盖面」:你的领域里有哪些典型问法,都要有对应的样本。

负样本有三类来源。随机负样本:任意不相关的文档,提供基础区分能力。批内负样本:同一个训练批次里其他 query 对应的文档,零成本获得。难负样本:用当前模型先检索,把「相似度高但答案错误」的文档挖出来当负样本——这是效果提升最明显的一类。难负样本可以迭代挖掘:微调一轮后重新检索、重新挖负样本、再训一轮,效果会越来越好,这就是业界常说的「负样本迭代挖掘」。

# 难负样本挖掘流程(伪代码)
# 1. 用当前模型向量化全部文档,建索引
# 2. 对每条 query 检索 Top20
# 3. 标注或规则判断:Top20 中与 query 相关但答案不同的 → 难负样本
# 4. 每条 query 配 1 正 + 若干随机负 + 1~2 条难负
# 5. 训练一轮后重新挖掘,循环 2~3 轮

微调实践:从开源模型到 LoRA 训练

工程上微调 Embedding 模型有两个层面。轻量层面:用现成框架(如 sentence-transformers)在领域数据上继续训练,脚本简单、几十分钟就能跑通,适合大多数团队。进阶层面:用 LoRA 在开源基座模型上微调——和微调大模型同一个思路,只训练一小部分低秩适配参数,显存占用小、训练快,还能保留基座模型的通用能力。选哪个看数据量和算力:数据几万条以内、追求快速见效,用轻量训练;数据量大、对效果要求极致,用 LoRA。

训练配置有四个关键点。一是池化方式:模型输出的 token 向量怎么聚合成句向量(均值池化 / CLS),保持和原模型一致。二是归一化:训练和推理时向量都要归一化,余弦相似度才稳定。三是温度与学习率:对比学习的温度参数影响收敛,学习率要比预训练小(通常 1e-5 到 5e-5)。四是保存与部署:导出后要确认向量维度和原模型一致,否则索引重建。微调后务必在同一套评估集上对比「微调前 vs 微调后」,别凭感觉上线。

# sentence-transformers 微调骨架(示意)
from sentence_transformers import SentenceTransformer, losses
model = SentenceTransformer("BAAI/bge-large-zh")
train = load_contrastive_pairs("domain_pairs.jsonl")  # 正负样本对
loss = losses.MultipleNegativesRankingLoss(model)
model.fit(train_objectives=[(train, loss)], epochs=3,
          warmup_steps=100, output_path="./ft-embedding")
# 上线前:同一评估集对比微调前后 MRR/召回率