FlagEmbedding 中的 Embedder 详解:从稀疏/稠密向量到语义检索与 RAG 的基石
【免费下载链接】FlagEmbeddingRetrieval and Retrieval-augmented LLMs项目地址: https://gitcode.com/GitHub_Trending/fl/FlagEmbedding
Embedder(嵌入模型,又称 Bi-Encoder / 双塔编码器)是把文本、代码乃至图像映射为高维向量空间中稀疏或稠密数值向量的核心组件,是 FlagEmbedding(BGE)这一套"面向搜索与 RAG 的一站式检索工具包"中最基础、最关键的一环。本篇以 docs/source/Introduction/embedder.rst 为骨架,结合仓库内 BaseEmbedder、M3Embedder 等源码实现与 README 中的模型清单,系统讲解稀疏向量、稠密向量的原理差异、相似度度量方式,以及 BGE 系列模型在语义检索与 RAG 落地中的实际用法,帮助读者建立从概念到工程实现、再到检索实战的完整认知。
一、什么是 Embedder:检索与 RAG 的地基
Embedder(Embedding Model)是一种把输入数据——通常是文本,也可以是代码或图像——转换为稀疏或稠密的数值向量(Embeddings)的模型,输出的向量位于高维向量空间中,其设计目标是让这些向量捕获输入的语义含义或关键特征,从而支持高效的比较与分析。
一个非常著名的经典示例来自 word2vec 论文:词嵌入可以通过向量算术展现语义关系,例如king − man + woman ≈ queen。如上图所示,词向量在空间中的方向与相对位置本身就编码了语义,这正是"向量即语义"思想的起源。如今,embedder 已经能够把整句乃至整篇段落映射到向量空间,被广泛用于检索(Retrieval)、聚类(Clustering)等真实任务;在 LLM 时代,嵌入模型更是 RAG(检索增强生成)的枢纽角色——它让 LLM 得以从海量外部数据集中检索并整合相关的上下文。
从模型架构上看,embedder 通常被称为Bi-Encoder(双塔编码器):查询(Query)与文档(Document)被分别独立编码,再通过相似度指标(如余弦相似度、内积)计算二者之间的相关性分数。与之相对的是 Reranker(Cross-Encoder,交叉编码器),后者将查询与文档拼接后共同输入模型,直接输出一个相关性分数。双塔结构可以预先将全部文档离线编码并建立索引,因此检索时只需编码查询即可,速度快、可扩展性强,适合海量候选集合的首轮召回。
二、稀疏向量(Sparse Vector):关键词匹配的利器
稀疏向量通常具有高维度但只有少数非零值的结构,尤其适合关键词匹配类任务。其典型特征是:向量的维度数通常(但不总是)与语言中出现的不同 token 一一对应,每一维的值代表该 token 在文档中的相对重要程度。
文档中列举了若干经典的稀疏向量嵌入算法:
- 词袋模型(Bag-of-Words):仅统计词语是否出现或出现次数,不考虑顺序与语义;
- TF-IDF:在词频基础上引入逆文档频率,降低常见词(停用词)的权重,突出对文档有区分度的词;
- BM25:现代搜索引擎中最主流的词项加权排序函数,基于 TF、IDF 与文档长度归一化设计,是 Lucene/Elasticsearch 的默认相关度算法。
稀疏向量的最大优势在于精确提取关键术语及其重要程度:它对专有名词、编号、代码标识符等"字面命中"场景极其有效,且天然可解释——非零维度直接对应具体的词。但它的弱点同样明显:无法处理同义词、语义改写、一词多义等语言变体,比如对 "watch a play" 与 "play with a watch" 这类词序与词性完全不同的句子会束手无策。
在 FlagEmbedding 生态中,稀疏检索能力集中体现在BGE-M3上。正如 README 所述,M3 代表Multi-Linguality(100+ 语言)、Multi-Granularities(输入长度可达 8192 token)、Multi-Functionality(统一支持稠密检索、稀疏检索、多向量/ColBERT 三种检索方式)。其稀疏向量通过M3Embedder的return_sparse=True选项输出(见 FlagEmbedding/inference/embedder/encoder_only/m3.py),并以{token_id: weight}词典的形式返回,再通过convert_id_to_token还原为可读的 token 与权重映射,可直接用于 BM25 风格的词项加权匹配或混合检索。
三、稠密向量(Dense Vector):语义的深度编码
稠密向量则使用神经网络把词、句、段落映射到固定维度的潜在向量空间,每一维都参与计算、几乎没有零值。随后可以通过欧氏距离或余弦相似度等度量比较两个对象的相似程度。与稀疏向量基于关键词计数与匹配不同,稠密向量直接捕获语义:
- 能够区分用词相似但语义迥异的句子(如下文示例中的 "watch a play" 与 "play with a watch");
- 能够理解表达方式不同但含义相同的句子(如同义改写、不同语序的表述)。
这是稠密检索相对传统 IR 方法最本质的飞跃。docs/source/Introduction/IR.rst 给出了一个绝佳的例子:sentence_1 = "watch a play"与sentence_2 = "play with a watch",前者表示"看一场演出"(watch 为动词、play 为名词),后者表示"摆弄一块手表"(play 为动词、watch 为名词)。传统 TF-IDF/BM25 因词项几乎完全相同而会把二者判为高度相似,而嵌入模型可以正确区分它们的真实语义。
需要指出的是,稠密向量的"深度"依赖模型的表达能力与训练数据。BGE 系列在这一点上提供了从bge-small/base/large到bge-v1.5、再到多语言bge-m3、具备上下文学习能力的bge-en-icl、以及基于 Gemma 的多语言模型bge-multilingual-gemma2等梯度完整的选择(完整清单见 README 模型列表),覆盖英文、中文、多语言与长文本等不同场景。
四、相似度度量:让向量之间可比
embedder 输出向量之后,"如何判定两个向量是否相似"就取决于相似度度量。docs/source/Introduction/similarity.rst 系统介绍了信息检索中最常用的四类度量,这里结合 BGE 的工程实践逐一说明:
| 度量 | 公式 | 特点与适用场景 | |||
|---|---|---|---|---|---|
| Jaccard 相似度 | J(A,B)= | A∩B | / | A∪B | 集合交集/并集之比,适用于二元数据(词是否出现),常用于关键词集合比较 |
| 欧氏距离 | d(A,B)=‖A−B‖₂=√(Σ(Aᵢ−Bᵢ)²) | 向量空间中的直线距离,距离越小越相似;但对数据尺度敏感,在高维文本嵌入空间中需谨慎使用 | |||
| 余弦相似度 | cos(θ)=A·B/(‖A‖‖B‖) | 衡量两向量夹角余弦,只关注方向、不受向量模长影响,是文本检索中使用最广泛的度量 | |||
| 点积(内积) | A·B=ΣAᵢBᵢ | 同时考虑方向与模长,常用于预训练词/句向量场景,内积越大表示越对齐 |
在 FlagEmbedding 的推理实现中,归一化与相似度计算是配套使用的:以 BaseEmbedder 为例,编码完成后若normalize_embeddings=True(默认开启),会执行torch.nn.functional.normalize(embeddings, dim=-1)将向量归一化到单位长度。归一化之后,内积与余弦相似度完全等价,因此 README 的 Quick Start 中可以直接用embeddings_1 @ embeddings_2.T做矩阵内积来计算任意两两之间的相似度,既高效又准确。
五、在 FlagEmbedding 中如何把 Embedder 用起来
5.1 安装
根据 README 安装说明,按需选择安装方式:
# 仅推理,不微调 pip install -U FlagEmbedding # 需要微调模型时 pip install -U FlagEmbedding[finetune] # 从源码安装(仓库根目录) git clone https://github.com/FlagOpen/FlagEmbedding.git cd FlagEmbedding pip install . # 或 pip install -e . 开发模式5.2 加载模型并编码(Quick Start)
from FlagEmbedding import FlagAutoModel model = FlagAutoModel.from_finetuned('BAAI/bge-base-en-v1.5', query_instruction_for_retrieval="Represent this sentence for searching relevant passages:", use_fp16=True) sentences_1 = ["I love NLP", "I love machine learning"] sentences_2 = ["I love BGE", "I love text retrieval"] embeddings_1 = model.encode(sentences_1) embeddings_2 = model.encode(sentences_2) similarity = embeddings_1 @ embeddings_2.T print(similarity)FlagAutoModel是 FlagEmbedding 提供的统一入口(FlagEmbedding/auto_embedder.py),会根据模型名自动分派到合适的实现类。query_instruction_for_retrieval用于在检索场景下为查询拼接指令前缀(对 v1.5 系列,英文模型推荐"Represent this sentence for searching relevant passages:",中文模型推荐"为这个句子生成表示以用于检索相关文章:",见 README 模型列表),使查询向量与文档向量的语义空间更对齐。
5.3 核心推理参数与底层实现
以 BaseEmbedder 为例,其关键参数及默认值如下:
model_name_or_path:本地模型路径,或 HuggingFace Hub 上的模型名(如BAAI/bge-base-en-v1.5);normalize_embeddings=True:输出前对向量做 L2 归一化;use_fp16=True/use_bf16=False:半精度推理以提速;query_instruction_for_retrieval与query_instruction_format="{}{}":查询指令及其拼接模板;devices:可指定"cuda:0"或["cuda:0", "cuda:1"]等多卡设备;pooling_method="cls":池化方式,支持cls(取首 token 的 hidden state)与mean(按 attention mask 对 hidden state 加权平均),见 pooling 方法实现;batch_size=256、query_max_length=512、passage_max_length=512:推理批大小与截断长度;convert_to_numpy=True:输出为 numpy 数组(否则为 Torch Tensor);truncate_dim=None:可选地对最终嵌入做维度截断(Matryoshka 式降维)。
在 encode_single_device 的实现中,可以观察到几个值得注意的工程细节:先按长度降序排序输入以减少 padding 浪费;遇到torch.cuda.OutOfMemoryError或RuntimeError时自动将batch_size收缩为原来的 3/4 重试;编码完成后按原始顺序恢复排列。若输入为单个字符串,则直接返回该句的向量。
5.4 BGE-M3:三种检索模式统一
若需要多语言 + 长文本 + 多形态检索能力,可使用FlagAutoModel.from_finetuned('BAAI/bge-m3')。M3Embedder(FlagEmbedding/inference/embedder/encoder_only/m3.py)提供了return_dense(默认 True)、return_sparse(默认 False)、return_colbert_vecs(默认 False)三个开关,分别控制稠密向量、稀疏(词项权重)向量与 ColBERT 多向量的输出,配合colbert_dim控制多向量映射维度。稀疏结果通过convert_id_to_token还原为 token→权重词典;ColBERT 向量则用于后期交互(late interaction)的精细匹配。这种"多功能合一"设计让 M3 既可以当纯稠密 retriever,也可以做 BM25 式稀疏检索,或与 reranker 组合成混合检索流水线。
更多推理用法可参考 examples/inference/embedder 下的示例,以及教程 Tutorials/1_Embedding/1.1_Intro&Inference.ipynb。
六、Embedder 与 Reranker 协同:RAG 的标准两阶段范式
虽然 embedder 已经能完成语义召回,但单靠双塔向量的相关性判定精度有限;而交叉编码器(reranker)精度更高,却需要对每个查询-文档对都做一次完整前向计算,代价高昂。因此业界广泛采用两阶段检索:
- 召回阶段(Retrieve):用 Bi-Encoder(embedder)从十万级候选(如 10 万条句子)中快速筛出 Top-100;
- 精排阶段(Rerank):用 Cross-Encoder(reranker)对 Top-100 逐一打分,得到更精准的排序结果。
这一点在 docs/source/Introduction/reranker.rst 中有明确表述,也正如 README 中对bge-reranker-base/large的推荐:"用它们来对 embedding model 返回的 top-k 文档进行重排"。FlagEmbedding 同时提供 embedder 与 reranker 两套完整的推理、评估与微调 API,完整 RAG 流水线示意可参见 docs/source/Introduction/index.rst 中的 RAG pipeline 图。这种"双塔粗召回 + 交叉精排"的组合,正是当前 RAG 系统在精度与效率之间取得平衡的主流方案。
七、小结与延伸阅读
Embedder 是 FlagEmbedding(BGE)一切能力的起点:稀疏向量擅长关键词精确匹配,稠密向量承载语义理解,二者通过余弦相似度/内积等度量参与检索,并最终在 RAG 流水线中与 reranker 协作,为 LLM 提供高质量的外部上下文。理解这些基础概念后,建议继续阅读:
- BGE 系列模型详解:docs/source/bge/index.rst;
- 相似度度量完整推导:docs/source/Introduction/similarity.rst;
- 信息检索与嵌入模型的关系:docs/source/Introduction/IR.rst;
- Reranker 与两阶段检索:docs/source/Introduction/reranker.rst;
- 推理示例:examples/inference/embedder;
- 微调与评估:
FlagEmbedding[finetune]安装后参考 examples/finetune/embedder 与 examples/evaluation。
从向量空间的几何直觉,到稀疏/稠密编码的取舍,再到 FlagEmbedding 中可运行的代码路径,掌握这些之后,你便可以在自己的检索与 RAG 项目中正确选型并落地 embedding 模型。
【免费下载链接】FlagEmbeddingRetrieval and Retrieval-augmented LLMs项目地址: https://gitcode.com/GitHub_Trending/fl/FlagEmbedding
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考