基于《大规模语言模型:从理论到实践(第2版)》第9章 检索增强生成
爆款小标题:检索准不准,一半看嵌入:原书第9章延伸之向量与检索器选型
为什么这一节重要
RAG 的检索质量很大程度上取决于「查询与文档」在向量空间中的表示是否一致、以及检索是否又快又准。嵌入模型负责把文本变成向量,向量库与检索方式决定在百万级甚至更大规模下如何高效找出最相似的文档。选错嵌入(如用生成模型最后一层隐状态直接当向量)或忽略归一化、索引类型,会导致检索相关性差、延迟高或显存爆。本节基于原书第 9 章相关讨论,把「文本→嵌入→相似度检索」的链路、常用相似度与 ANN 的取舍、以及嵌入与生成模型的匹配问题讲清,并给出选型与避坑要点。
学习目标
- 理解嵌入在 RAG 中的角色:说明「文本→嵌入向量→相似度检索」的完整链路,以及嵌入模型如何影响检索相关性。
- 掌握相似度与索引类型:说明余弦相似度、内积、欧氏距离在 L2 归一化下的关系;区分精确检索与近似最近邻(ANN)的适用规模与精度/延迟权衡。
- 能做初步选型:根据数据规模、延迟与成本选嵌入模型与向量库;理解中英文/多语言场景下嵌入模型的选择;知道何时需考虑「嵌入与生成模型」的语义一致(原书第 9 章)。
一、嵌入模型的作用与训练目标(原书第 9 章)
作用:嵌入模型把不定长的文本映射为固定维度的稠密向量,使得「语义相近」的文本在向量空间中距离更近(如余弦相似度更高、欧氏距离更小)。在 RAG 中,文档块与用户查询都被编码成向量,检索即是在向量空间中找与查询向量最接近的文档向量。嵌入质量直接决定检索相关性:若嵌入空间不能很好反映「查询与文档的语义相似度」,检索结果会偏或不相关,后续生成再强也难以补救。
训练目标:常用对比学习(如 InfoNCE、triplet loss)——拉近「正例对」(如查询与相关文档)、推远「负例对」(查询与不相关文档),使模型学会对「语义相关」给出高相似度。未经对比学习或检索任务训练的表示(如生成模型最后一层隐状态)往往不适合做相似度检索,因为其优化目标是语言建模而非检索。
二、嵌入模型选型(原书第 9 章)
通用嵌入:如 OpenAI text-embedding、BGE、E5、bge-m3 等,在通用语料上训练,适合大多数场景。需关注:维度、多语言支持、API 成本与延迟(若用云服务)。
领域微调:在医疗、法律、金融等垂直领域数据上继续训练或微调,可提升领域内检索效果;但需要领域数据与训练资源。
嵌入与生成模型的语义一致:原书第 9 章指出,若嵌入空间与生成模型「看到的」语义不一致,可能导致「检索到相关文档但生成时未充分利用」。一种做法是用生成模型自身做嵌入(取其 encoder 或某层表示并做投影);另一种是选用与生成模型同系列或同数据分布的嵌入模型,以减少分布差异。中英文或多语言场景应选用多语言嵌入模型,避免单一语言主导导致检索偏置。
常见误区:不要用「生成模型最后一层隐状态」直接当嵌入。未经对比学习的隐状态通常不适合相似度检索;应使用专用嵌入模型或经过对比学习/检索目标训练的表示。
三、相似度与检索方式(原书第 9 章)
相似度:常用余弦相似度或内积。对 L2 归一化后的向量,内积等于余弦相似度;实现时若用内积,务必先对向量做 L2 归一化,否则向量长度会影响排序结果。欧氏距离与余弦在归一化向量下可互相转换,视向量库支持选择即可。
精确检索:暴力计算查询向量与所有文档向量的相似度并排序,返回全局 Top-K。优点:精度最高;缺点:计算量与文档数线性相关,适合小规模(如万级以下);百万级以上时延迟与内存都成问题。
近似最近邻(ANN):用 HNSW、IVF、LSH 等索引在「可接受的精度损失」下加速检索,适合百万级甚至更大规模。如 HNSW 通过图的层级结构做近似最近邻搜索,需调参(如 ef_search、ef_construction)平衡召回率与延迟:ef_search 越大召回越高但越慢。原书第 9 章对精确与 ANN 的取舍、以及不同索引的特点有讨论。
四、案例:BGE 嵌入 + FAISS 的快速搭建步骤
完整可运行代码(依赖:pip install FlagEmbedding faiss-cpu 或 pip install sentence-transformers faiss-cpu):
import numpy as np
try:
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel("BAAI/bge-m3", use_fp16=False)
dim = 1024
def encode(texts, **kw):
return model.encode(texts, **kw)
except ImportError:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
dim = model.get_sentence_embedding_dimension()
def encode(texts, batch_size=8, max_length=512, return_dense=True):
return {"dense_vecs": model.encode(texts, batch_size=batch_size, normalize_embeddings=True)}
import faiss
docs = ["本公司退货政策:商品签收后7天内可无理由退货。", "远程办公政策:每周可远程办公2天,需提前申请。"]
out = encode(docs, batch_size=8, max_length=512, return_dense=True)
embeddings = (out["dense_vecs"] / np.linalg.norm(out["dense_vecs"], axis=1, keepdims=True)).astype("float32")
index = faiss.IndexFlatIP(dim)
index.add(embeddings)
query = "退货政策是什么?"
q_out = encode([query], return_dense=True)
q_emb = (q_out["dense_vecs"] / np.linalg.norm(q_out["dense_vecs"], axis=1, keepdims=True)).astype("float32")
scores, ids = index.search(q_emb, k=2)
for i, (idx, sc) in enumerate(zip(ids[0], scores[0])):
print(f"[{i+1}] score={sc:.4f} {docs[idx]}")
百万级以上可改用 IndexHNSWFlat,设置 efConstruction=200、efSearch=64。
五、工程实战要点
1. 嵌入维度与向量库要匹配
小规模(万级以下)可精确检索,实现简单、精度最高;百万级以上建议 ANN(如 HNSW、IVF),并做召回率与延迟的测试。HNSW 的 ef_search 越大召回越高但越慢,可从小值(如 64)起步,逐步增大直到召回率满足业务要求;ef_construction 影响索引构建质量,建库时适当调大(如 200–400)可提升召回,但会增加建库时间。
2. 多语言与领域
中英文混合或小语种场景选用多语言嵌入(如 bge-m3、E5-multilingual);单一语言场景可选单语模型以节省维度与延迟。领域敏感(医疗、法律、金融)时可考虑领域微调或专用模型;微调时需保证正例对(query–相关 doc)与负例对的构造合理,避免过拟合到特定表述。
3. 向量归一化与相似度计算
无论用余弦还是内积,建议在存入向量库前对向量做 L2 归一化;检索时对 query 向量同样归一化。否则向量长度会影响内积结果,长文档的向量可能天然「更长」,导致排序偏向长文档。多数向量库支持在写入时自动归一化,需确认配置正确。
六、查询与文档是否用同一嵌入模型(原书第 9 章延伸)
通常建议:查询与文档用同一嵌入模型,保证二者在同一向量空间中可比。若用不同模型,需确保二者经过对齐或兼容训练,否则相似度含义不一致。
例外:在非对称检索(如 query 短、document 长)场景下,有的方法会对 query 与 document 用不同的编码方式(如 query 编码器与 doc 编码器分开),但需在训练时 explicitly 对齐;一般不推荐随意混用未对齐的模型。ColBERT 等 late interaction 方法对 query 与 doc 分别编码后再做细粒度匹配,属于另一类设计,适合对精度要求极高、能接受更高延迟的场景。
文档切块与嵌入的关系:切块策略会影响嵌入效果。若块过大,单个向量需表征整段语义,可能过于「平均」而损失关键信息;若块过小,语义碎片化,检索时可能召回大量零散片段。建议根据文档类型与查询特点调块大小(如 256–512 token),并做重叠(overlap)以缓解边界割裂;切块后每个块独立嵌入,存入向量库。
七、常见误区与避坑指南
- 用生成模型隐状态当嵌入:避坑:用专用嵌入模型或经过对比/检索训练的表示。
- 忽略归一化:用余弦时务必 L2 归一化,否则内积受向量长度影响,排序会偏。
- 规模与索引类型不匹配:小规模用 ANN 可能没必要且增加复杂度;大规模用精确检索会过慢。避坑:根据文档量选择精确或 ANN,并做召回率与延迟测试。
八、小结与衔接
本节基于原书第 9 章梳理了嵌入模型在 RAG 中的角色、相似度与精确/ANN 检索的取舍、以及嵌入与生成模型的匹配与多语言选型;并强调了专用嵌入、归一化与规模匹配。下一模块将进入Agent 与工具:智能体架构、工具学习与 LangChain/Coze 实践(原书第 8 章)。
课后思考题
- 若文档库有 100 万条 chunk、嵌入维度 768,精确检索与 HNSW 近似检索在「单次查询延迟」和「召回精度」上各有什么特点?
- 「查询」和「文档」是否必须用同一个嵌入模型?什么情况下你会考虑使用不同模型或不同表示?