第 15 章:Embedding、向量数据库与语义检索¶
1. 本章要解决的问题¶
前面几章,我们基本都在讨论模型本身:
- 第 12 章讲的是怎样把预训练模型做成会按指令回答的助手
- 第 13 章讲的是怎样更省资源地做微调
- 第 14 章讲的是怎样让模型在多个可行回答里更贴近人类偏好
但当你真的开始做应用,很快会碰到另一类完全不同的问题:
模型本身再强,也不等于它天然知道你手里的私有知识。
比如你想做一个课程资料问答助手、公司内部知识库助手,或者一个能回答你项目文档细节的聊天系统,用户一开口问的往往不是:
而是:
这时候即使底座模型很强,也可能出现几个典型问题:
- 它不知道这份文档压根存在
- 它知道相关概念,但不知道你这份资料里的具体结论
- 它会用训练时学到的常识“脑补”答案,产生幻觉
- 它回答得很流畅,但无法给出依据
于是问题就从“模型会不会回答”,变成了:
怎样把外部知识表示成模型可用的检索对象,并在用户提问时快速找回最相关内容。
这正是本章要解决的事。
从全书结构上看,这一章有三个作用:
- 它标志着我们从“训练模型”正式走向“构建应用”
- 它先把 RAG 里最关键的检索基础讲清楚,避免下一章一上来就把流程混在一起
- 它也是很多大模型岗位面试里的高频主题,因为你很少需要从零训练一个大模型,但经常需要把 embedding、检索、向量库接进业务系统
如果说前几章回答的是:
怎样把一个模型训练得更像一个可用助手。
那么这一章要回答的就是:
当助手需要访问训练语料之外的新知识时,我们怎样把文本映射成向量、建立检索系统,并在海量候选内容里找回语义上最相关的信息。
2. 你学完后应该会什么¶
- 能解释什么是 embedding,以及它为什么能支持语义相似度计算
- 能区分 sparse retrieval、dense retrieval、hybrid retrieval 的核心差异
- 能理解 cosine similarity、dot product、L2 distance 在向量检索里的直觉
- 能描述一个最小可用的语义检索流水线:切块、编码、建库、召回、重排
- 能说清 chunking、metadata、top-k、reranking 分别在系统里扮演什么角色
- 能理解为什么需要向量数据库,以及它解决的主要是“规模化检索”问题
- 能自然衔接到下一章的 RAG 系统设计
3. 为什么关键词搜索不够了¶
先从最直观的问题开始。
很多同学第一次做“文档问答”时,会本能地想:
能不能先用关键词匹配?
当然可以,而且在很多场景下它依然有效。
但只靠关键词搜索,你很快会碰到几个明显限制。
3.1 词面匹配不等于语义匹配¶
假设知识库里有一句话:
用户的问题是:
这两个文本表达的是同一件事,但用词并不完全一致:
- 文档里写的是“自注意力”“全局依赖”“并行性”
- 用户问的是“适合并行训练”
如果系统只看关键词重合,可能就会漏掉这段本来很相关的内容。
3.2 现实问题里同义表达很多¶
在实际业务中,同一个意思常常有很多说法:
- “报销” 和 “费用申请”
- “离职” 和 “解除劳动关系”
- “embedding” 和 “向量表示”
- “召回” 和 “retrieve relevant chunks”
如果系统只能按字面匹配,它就会非常依赖你是否刚好说中了文档里的原词。
3.3 大模型应用更关心“相关内容”,不是“同词内容”¶
RAG 或知识问答的目标,通常不是找出“和问题长得最像”的句子,而是找出:
最有助于回答这个问题的证据。
而“有助于回答”往往是语义层面的,不只是词面层面的。
这就是 embedding 出场的原因。
4. Embedding 到底是什么¶
最朴素地说:
embedding 就是把离散对象映射成连续向量表示。
这里的对象可以是:
- 一个词
- 一个句子
- 一段文本
- 一篇文档
- 一张图片
- 甚至用户、商品、代码片段
在本章里,我们主要讨论文本 embedding。
4.1 为什么要把文本变成向量¶
因为计算机更擅长在向量空间里做运算。
一旦文本被编码成固定维度的向量,我们就可以:
- 计算两个文本有多相似
- 在海量文本里找到“最近邻”
- 做聚类、分类、去重、推荐
- 把用户问题和文档片段放到同一个语义空间里比较距离
所以 embedding 的核心价值不是“把文本变成数字”这么简单,而是:
让语义上的接近,可以被几何空间中的接近近似表达。
4.2 一个直觉类比¶
你可以把 embedding 空间想成一张高维地图。
- 语义相近的文本,位置更接近
- 语义差异大的文本,位置更远
- 某些方向可能对应“主题变化”“语气变化”“任务类型变化”
当然,真实向量空间远比地图复杂,也不一定容易可视化。
但这个“语义映射到几何距离”的直觉非常重要。
4.3 Embedding 模型在学什么¶
embedding 模型并不是简单统计词频,而是在训练过程中学会:
让相关文本的表示更接近,不相关文本的表示更远。
不同 embedding 模型的训练目标可能不同,例如:
- 对比学习:拉近正样本,拉远负样本
- 双塔检索训练:让 query 和相关 document 更接近
- 句向量训练:让语义相似句子更接近
所以 embedding 向量里的信息,不只是“出现了哪些词”,还包含一定程度的语义结构。
5. 相似度是怎么计算的¶
文本变成向量之后,下一步就是比较“谁更接近谁”。
最常见的相似度或距离度量有三类。
5.1 Cosine Similarity¶
最常见的是 cosine similarity,定义为:
它关心的重点是:
两个向量方向是否接近。
直觉上,如果两个文本表达的语义方向相似,即使向量长度不同,cosine similarity 也可能很高。
它非常常见,因为很多时候我们更关心“语义方向像不像”,而不是绝对长度。
5.2 Dot Product¶
dot product 也很常见:
如果向量已经做过归一化,那么 dot product 和 cosine similarity 在排序上会非常接近。
但如果没有归一化,dot product 还会受向量模长影响。
这在工程里很重要,因为:
- 有的模型默认输出已归一化向量
- 有的向量库底层直接优化 inner product 检索
- 有的系统会把 cosine 检索转成 normalized dot product 来做
5.3 L2 Distance¶
另一种常见做法是欧氏距离:
距离越小,表示越接近。
它也能用于近邻搜索,但在文本语义检索里,大家更常提的是 cosine similarity 或 inner product。
5.4 先记住工程结论¶
入门阶段不用过早纠结哪一个“理论上绝对最好”,更重要的是先记住:
- embedding 把文本放进向量空间
- 检索就是在这个空间里找最近的候选
- 你的相似度定义,会直接影响召回结果
6. Sparse Retrieval、Dense Retrieval 与 Hybrid Retrieval¶
理解现代检索系统,最重要的一组对比就是这三个词。
6.1 Sparse Retrieval:基于词项的检索¶
sparse retrieval 典型代表是 TF-IDF、BM25。
它的核心思想是:
把文本表示成“哪些词出现了、出现了多少、这些词稀不稀有”。
优点:
- 可解释性强
- 对精确关键词命中很有效
- 不需要训练 embedding 模型也能工作
- 对专有名词、编号、特定术语常常很强
缺点:
- 词面不一致时容易漏召回
- 难以处理同义改写、语义泛化
- 对自然语言问法变化更敏感
6.2 Dense Retrieval:基于向量的检索¶
dense retrieval 的核心思想是:
把 query 和文档都编码成稠密向量,再按向量相似度检索。
优点:
- 能捕捉一定语义相似性
- 对改写、近义表达更鲁棒
- 更适合自然语言问答和知识检索
缺点:
- 对精确字符串、版本号、产品型号不一定稳定
- embedding 模型质量会直接影响检索效果
- 大规模检索需要 ANN 索引和向量库支持
6.3 Hybrid Retrieval:把两者结合¶
现实系统里,很多团队最后都会走向 hybrid retrieval,也就是:
既看关键词信号,也看语义向量信号。
因为两类方法各有偏好:
- sparse 擅长精确命中
- dense 擅长语义泛化
把它们结合起来,通常能减少各自的短板。
例如一个问题里既有非常关键的产品编号,又有自然语言描述时,hybrid 常常比单一路线更稳。
7. 一个最小语义检索系统长什么样¶
把概念放到工程流程里,一般最小可用系统会经过下面几步:
- 准备原始文档
- 把文档切成适合检索的 chunk
- 用 embedding 模型把每个 chunk 编码成向量
- 把向量和 metadata 存进向量数据库
- 用户提问时,把 query 也编码成向量
- 在向量库里做近邻搜索,取回 top-k 候选
- 必要时做 reranking,再把结果交给后续生成模块
注意这里最容易被忽略的一点:
语义检索不是只有“算 embedding”这一步,前后的数据组织同样决定效果。
8. Chunking:为什么不能整篇文档直接塞进去¶
很多人第一次做知识库时会问:
为什么不把一整篇文档编码成一个向量?
因为这样通常太粗了。
8.1 文档太长,语义会被平均掉¶
一篇长文档里可能同时包含:
- 背景介绍
- 方法说明
- 结果分析
- 风险提示
- 附录
如果整个文档只对应一个向量,那么用户只问其中一个细节时,这个大向量往往不够精确。
8.2 检索的目标通常是找证据片段¶
RAG 里真正要喂给模型的,通常不是“整个知识库里最相关的一整篇文档”,而是:
最能支撑当前问题回答的那几段内容。
所以我们通常会先切 chunk。
8.3 Chunk 太小也不行¶
但 chunk 也不是越小越好。太小会带来几个问题:
- 上下文不完整
- 语义边界被切碎
- 召回到的内容不够回答问题
- 后续生成时缺证据链
所以 chunking 的本质,是在两件事之间找平衡:
- 检索粒度够细
- 每个片段又保留足够语义上下文
8.4 常见 chunking 策略¶
常见做法包括:
- 按固定 token 长度切分
- 按段落、标题、小节结构切分
- 设置 overlap,让相邻 chunk 共享一部分内容
- 对表格、代码、问答对做特殊切分
工程里通常没有一刀切的最佳答案。
文档类型不同,最优 chunking 策略也会不同。
9. Metadata:为什么检索不只是向量¶
一个成熟的检索系统里,存的通常不只是:
- chunk 文本
- embedding 向量
还会带上 metadata,例如:
- 文档标题
- 来源链接
- 作者
- 时间
- 章节名
- 权限标签
- 业务域标签
这些信息有几个关键作用。
9.1 用于过滤¶
例如你可以要求只在:
- 某个项目空间内搜索
- 最近三个月文档里搜索
- 用户有权限访问的资料里搜索
9.2 用于排序增强¶
有些场景下,除了语义相似度,你还希望考虑:
- 文档新鲜度
- 来源可信度
- 是否来自官方知识源
9.3 用于答案可追溯¶
如果你想让系统回答时附出处,那 metadata 几乎是必须的。
否则模型即使答对了,也很难告诉用户“这段信息来自哪里”。
10. 为什么需要向量数据库¶
如果文档量很小,你当然可以把所有向量放进内存,逐个算相似度。
这叫 brute-force 检索。
但当数据量大起来,问题就来了:
- 向量数量很多
- 每个向量维度不低
- 用户查询要低延迟返回
- 还要支持增量写入、过滤、删除、权限控制
这时就需要向量数据库或向量检索引擎。
10.1 它解决的核心不是“存”,而是“快而稳地找”¶
向量数据库最核心的价值不是把向量放进去,而是:
在大规模数据上高效做相似向量搜索。
这通常会依赖 ANN,也就是 approximate nearest neighbor,近似最近邻搜索。
它的思想是:
不必每次都和全量向量精确比较,只要用更快的方法找到足够好的近邻。
这样就能在召回质量和查询延迟之间做工程折中。
10.2 常见能力¶
一个向量数据库通常会提供:
- 向量写入与更新
- top-k 相似检索
- metadata filter
- 删除与重建索引
- 持久化和服务化接口
10.3 常见工具¶
学习和项目里常会看到这些名字:
- FAISS:非常经典的向量检索库,更偏底层
- Chroma:上手简单,适合原型和教学
- Milvus:更偏独立向量数据库
- Weaviate、Qdrant、pgvector:也都很常见
入门时不用执着于“谁最好”,更重要的是理解:
无论底层工具是谁,系统都在做同一件事:把 query 向量和文档向量匹配起来,并在规模化条件下尽量又快又准。
11. Reranking:为什么召回之后还要再排一次¶
很多初学者以为检索系统做到 top-k 召回就结束了。
其实在较完整的系统里,常常还会有 reranking。
11.1 召回阶段更像“先别漏掉”¶
第一阶段检索,目标通常是:
先从大库里找出一小批大概率相关的候选。
这时更看重召回率,不希望真正相关内容被漏掉。
11.2 重排阶段更像“把最该看的放前面”¶
有了几十条候选之后,我们可以用更贵但更准的方法重新排序,比如:
- cross-encoder reranker
- 更强的相关性打分模型
- 基于业务规则的重排
因为这时候候选集合已经小很多,所以你可以接受更复杂的打分成本。
11.3 为什么这一步很重要¶
因为 dense retrieval 常常能把“差不多相关”的内容一起召回来,但:
- 哪段最适合直接回答当前问题
- 哪段证据最完整
- 哪段最应该排在第 1 位
往往还需要更细致的比较。
所以一个常见的工业级思路是:
先粗召回,再精重排。
12. 一个简单例子:从问题到检索结果¶
假设你的知识库里有三段文本:
Chunk A: Transformer 使用 self-attention 建模 token 之间关系。
Chunk B: RNN 依赖时间步递归,因此训练时并行性较弱。
Chunk C: Transformer 去掉了顺序递归结构,因此在训练阶段更容易并行计算。
用户问题是:
一个比较合理的检索过程会是:
- 把用户问题编码成 query embedding
- 把三个 chunk 的 embedding 和 query 比较相似度
- 发现 Chunk C 最相关,Chunk B 次相关,Chunk A 也有帮助
- 返回
C + B (+ A)作为候选证据
这里你会发现:
- Chunk C 直接回答“为什么更容易并行”
- Chunk B 提供了和 RNN 的对照依据
- Chunk A 补充了机制背景
这也说明检索不一定只找“唯一答案块”,很多时候它找的是:
一组能共同支撑回答的问题相关证据。
13. 做语义检索时最常见的坑¶
这一节很适合面试,也很适合做项目时提前避坑。
13.1 Embedding 模型和任务不匹配¶
不是所有 embedding 模型都适合同一种任务。
例如有的更偏通用语义匹配,有的更偏搜索,有的更适合中英混合,有的更偏代码。
如果模型选型不对,后面再怎么调 top-k 都可能救不回来。
13.2 Chunk 切分不合理¶
太大,召回不准。
太小,信息不全。
不做 overlap,语义边界容易断裂。
只按字数硬切,可能把表格、代码、标题结构切坏。
13.3 只看检索分数,不做人类抽样检查¶
很多系统离线分数看起来还行,但抽样一看会发现:
- 召回的段落表面相关,实际没法回答问题
- 高分 chunk 有重复内容
- 真正关键段落排位太后
所以检索系统一定要结合人工 case review。
13.4 过度迷信向量检索¶
有些场景下,关键词本来就很关键,比如:
- 产品编号
- 法条编号
- API 名称
- 精确字段名
这时如果只做 dense retrieval,效果可能反而不如 BM25 或 hybrid。
13.5 忽略数据治理¶
检索系统效果差,很多时候不是模型不行,而是数据本身有问题,例如:
- 文档版本混乱
- 重复内容太多
- 过期资料没有清理
- 权限标签缺失
应用工程里一个很现实的结论是:
垃圾进,垃圾出。
14. 评估一个检索系统,应该看什么¶
如果你做的是生成模型,大家常问 loss、BLEU、ROUGE、准确率。
但做 retrieval,更常见的是另一套指标思路。
14.1 先看“有没有把相关内容找回来”¶
例如:
- Recall@k:前 k 个结果里有没有覆盖到相关文档
- Hit@k:前 k 个里是否至少命中一个正确项
这类指标关注的是:
该找回来的,有没有被找回来。
14.2 再看“排得好不好”¶
例如:
- MRR
- NDCG
它们关注的是:
相关结果是不是排得足够靠前。
因为在真实系统里,后续模型能看到的上下文窗口有限,排位靠后的 chunk 很可能根本用不上。
14.3 最后还要看端到端效果¶
检索做得好,不等于最终问答体验就一定好。
因为后面还会受这些环节影响:
- prompt 拼接
- 上下文截断
- 生成模型能力
- 引用格式
- 拒答策略
所以检索评估最好分层看:
- 检索层指标
- RAG 端到端问答指标
- 人工体验评估
15. 和下一章 RAG 的关系¶
到了这里,你应该已经能看出:
RAG 并不是“突然多了一个神奇模块”,而是在 embedding 检索的基础上,把检索结果喂给生成模型。
更具体地说:
- 第十五章解决的是“怎么找资料”
- 第十六章解决的是“找回来的资料,怎么组织进 prompt,并生成可信回答”
所以如果你对 RAG 的理解总是糊成一团,通常不是因为 RAG 太复杂,而是因为你还没有把它拆成两层:
- retrieval:从知识库中找相关证据
- generation:基于证据生成回答
本章讲清楚的,就是第一层。
16. 本章小结¶
这一章最核心的主线可以压缩成下面几句话:
- embedding 的作用,是把文本映射到可以比较语义距离的向量空间
- 语义检索的目标,不是找字面最像的文本,而是找最相关的证据
- dense retrieval 能补足关键词搜索的语义短板,但 sparse retrieval 依然重要
- 一个可用系统通常不止有 embedding,还包括 chunking、metadata、top-k 检索和 reranking
- 向量数据库解决的重点,是规模化条件下高效做近邻搜索
- 这些能力合在一起,才构成下一章 RAG 的检索基础
如果你把前面的训练章节看作“如何把模型练好”,那么从这一章开始,我们正式进入另一条主线:
如何把模型接进真实世界的数据与系统。
而这,正是大模型应用工程最常见、也最有岗位价值的一段路。
17. 面试高频题¶
17.1 什么是 embedding?它和 hidden state 有什么关系?¶
可以先用应用视角回答:
embedding 是把文本映射成向量表示,使得相似文本在向量空间中更接近。
而 hidden state 更像模型内部中间表示,不一定直接拿来做检索。
实际工程里说 embedding,通常指经过专门训练并可直接用于相似度搜索的向量。
17.2 BM25 和 dense retrieval 有什么区别?¶
BM25 更依赖词项匹配和词频统计,擅长精确关键词检索。
dense retrieval 依赖 embedding 的语义表示,擅长处理改写和近义表达。
很多真实系统会做 hybrid,把两者结合。
17.3 为什么 RAG 里要切 chunk?¶
因为整篇文档粒度太粗,难以精确定位证据;但太小又会丢上下文。
切 chunk 是为了在“检索精度”和“语义完整性”之间做平衡。
17.4 向量数据库和传统数据库有什么不同?¶
传统数据库更擅长精确过滤、结构化查询、事务等;向量数据库更擅长基于向量相似度做近邻搜索。
在 RAG 系统里,两者常常是互补关系,而不是谁替代谁。
17.5 为什么召回之后还要 rerank?¶
因为第一阶段通常更强调别漏掉相关候选,排序未必最优。
rerank 可以在小候选集上用更贵但更准的方法,把最该看的证据排到前面。
18. 动手建议¶
如果你准备把这一章变成求职项目,最推荐的最小实践路线是:
- 找一份结构比较清晰的小型文档集,例如课程讲义、博客文章或项目文档
- 自己实现一个 chunking 脚本,并记录不同 chunk 大小的效果差异
- 用一个 embedding 模型把 chunk 建成向量索引
- 对比 BM25、dense retrieval、hybrid retrieval 的检索结果
- 手动构造 20 到 50 个问题,做一次简单的 Recall@k 抽样评估
- 在下一章把它接成一个完整的 RAG Demo
这样做的价值很高,因为你最终展示给面试官的就不再是:
“我会调一个 RAG 框架。”
而是:
“我知道检索系统为什么会好,也知道它为什么会坏。”
这两者的含金量差别非常大。