大模型通识 / 大模型理论基础
10. 嵌入表示与语义检索:怎样找到意思相关的资料
原文备份 · 第 11 / 20 篇 · 查看极客时间原知识库 ↗
本篇目录 · 11 节
来源:大模型理论基础
你的复习助手已经会按照清楚的规则提取时间。现在,笔记从三行变成了几千条:有旧计划、新安排、读书摘录、代码错误记录,还有大量重复内容。
你问:“第二章这周改到什么时候复习?”正确答案藏在某一条笔记里。把全部笔记交给模型,既可能超出上下文预算,也会带入大量无关信息。只搜索完整问句,又很可能什么都找不到,因为笔记写的是“第 2 章原定周五,现改为周六”。
这时要先解决的任务是:**从资料中找出值得阅读的候选内容。**本篇讲 Embedding、相似度、关键词检索、混合检索和重排;下一篇再把检索结果接入回答过程。
无需安装软件。本篇的笔记、向量、排名与检查结果均为教学构造,不是某个实际模型的输出。你只需能看懂一组数字和简单的加减乘除。
一、Embedding 把文本变成可比较的表示
在第一部分,我们把向量理解为一组按顺序排列的数字。这里的 Embedding,嵌入,是模型把输入映射成数字表示的结果。
用于文本检索时,一种常见方式是让编码模型把一段文字变成固定维度的向量:
一条笔记 → 文本编码模型 → 一个向量
一个问题 → 配套的查询编码方式 → 一个向量
如果模型学到的表示适合当前任务,问题与相关资料的向量就有机会得到较高的匹配分数。SBERT 的原始研究展示了可分别计算的句子表示,再用余弦相似度比较的做法。《Sentence-BERT》
这里有两个容易混淆的地方。
首先,第 01 篇讲过的 Token 嵌入,是模型处理序列时的基础表示;这里需要的是适合比较整段文字的表示。把任意聊天模型某个位置的内部向量取出来,并不自动得到一个好用的检索模型。
其次,向量的每一维通常没有“第 1 维是章节,第 2 维是日期”这样的固定人工含义。表示由训练塑造,很多维度共同参与表达。你不需要逐维解释它,才能使用相似度检索。
为什么“问题”和“答案段落”能够匹配
有些任务要找近义句,例如“怎样记笔记”和“如何整理学习记录”。另一些任务要给短问题找包含答案的长段落,两边并不是同一种句式。
这分别对应比较对称的相似文本检索,与不对称的问答检索。编码模型是否针对这种任务训练,会影响效果;某些模型还要求问题和资料使用不同的前缀或编码入口。Sentence Transformers:Semantic Search
一种训练思路是:给模型一个问题、一段相关资料和若干不相关资料,让相关配对的分数相对更高。DPR 原始论文采用问题与段落分别编码的结构,使用正负段落训练检索器。《Dense Passage Retrieval》
其中,有的负例很容易区分,例如复习问题配上天气笔记;有的负例很难区分,例如第 2 章的问题配上第 3 章的安排。后者提醒模型:主题相近还不够,细节也要匹配。这只是训练机制的直觉,不表示训练后就不会混淆章节。
你在应用里把新笔记转换成向量,通常是在使用已经训练好的编码模型,不是在重新训练它。新向量进入检索库,也不代表回答模型的参数记住了这份笔记。
二、用四条笔记手算一次相似度
我们准备下面的材料。为简化,“本周”在这组材料中指同一周;C 的失效状态已确认,不需要根据文件顺序猜测。
| 编号 | 笔记内容 | 补充信息 |
|---|---|---|
| A | 第 2 章原定周五,现改为周六复习。 | 当前有效 |
| B | 第 3 章安排在周六复习。 | 当前有效 |
| C | 第 2 章安排在周五复习。 | 旧安排,已失效 |
| D | 复习卡片采用“问题—答案—依据”的格式。 | 格式说明 |
查询 Q 是:“第二章这周改到什么时候复习?”
人为给它们分配以下二维向量,专门用来说明计算和失败情况。这些坐标没有来自模型,也没有给两个维度指定真实语义。
| 对象 | 向量 |
|---|---|
| Q | [1, 0] |
| A | [4, 3] |
| B | [3, 1] |
| C | [2, 1] |
| D | [0, 2] |
一种常用比较方法是 余弦相似度,cosine similarity。它比较向量的方向,公式为:
余弦相似度 = 两个向量的点积 ÷ 两个向量长度的乘积
点积:对应位置相乘,再相加
长度:各位置的平方相加,再开平方
这与信息检索教材中向量空间模型使用的余弦计算一致。《Introduction to Information Retrieval》:Dot products
例如 Q 与 A:
点积 = 1×4 + 0×3 = 4
Q 的长度 = √(1²+0²) = 1
A 的长度 = √(4²+3²) = 5
余弦相似度 = 4 ÷ (1×5) = 0.8
用同样方法计算其余三条:
| 笔记 | 与 Q 的余弦相似度 | 排名 |
|---|---|---|
| B | 3/√10 ≈ 0.949 | 1 |
| C | 2/√5 ≈ 0.894 | 2 |
| A | 0.800 | 3 |
| D | 0.000 | 4 |
Top-k 指按当前分数取前 k 条。这个例子的 Top-2 是 B、C,恰好漏掉了能支持当前答案的 A。
这个反例不是说某个真实模型一定会这样排,而是说明:排序程序即使完全算对,表示与任务之间仍可能存在差距。向量搜索正确执行,与检索结果对用户有用,是两层不同的问题。
0.949 不是“94.9% 正确”
余弦分数不是答案正确率,也不是经过校准的相关概率。对于非零向量,余弦范围在 -1 到 1 之间;某些模型的实际分数可能集中在较窄区间,不能拿一个通用阈值套所有任务。
若把 A 从 [4,3] 放大成 [8,6],方向不变,余弦仍为 0.8;直接点积却从 4 变成 8。如果双方都归一化成长度为 1 的向量,就可以直接用点积计算余弦。检索还可能使用点积或欧氏距离,应遵循所选模型与索引的约定,不能随意换算规则。
零向量的长度是零,不能直接代入余弦公式。实际程序需要处理这种异常,而不是把除零结果当成有效相关性。
三、关键词检索仍然有用
看到语义检索能处理表达差异,很容易以为关键词搜索已经过时。但你的笔记未必总是在问抽象意思。
如果你要找的是错误码 E1042、函数 load_notes 或文件 chapter_02.md,这些精确字符串本身就很重要。把 E1042 与 E1043 视为“都像错误码”,可能导致错误匹配。
关键词检索依据词项匹配来寻找资料。常见的 BM25 不只是计算“出现了几个相同的词”,还考虑词项的区分能力、出现频率和文档长度;同一个词重复很多次,得分不会简单地无限按次数线性增长。《Introduction to Information Retrieval》:Okapi BM25
实际效果也受文本处理影响。例如,“第二章”与“第 2 章”是否能匹配,可能取决于分词、归一化和同义表达配置。关键词系统也能配置同义词或查询扩展,因此不能把它描述成永远只能逐字照抄匹配。
对复习助手,两类方法的侧重点可以这样理解:
| 要找的内容 | 可以先检查什么 |
|---|---|
| 一个明确的错误码、文件名或章节标识 | 精确匹配、关键词和字段筛选 |
| 与“把知识记牢”意思相关、用词不同的学习笔记 | 向量检索是否能找回改写表达 |
| “第 2 章现在几点复习”这样的具体事实 | 章节、有效版本、时间证据是否都正确 |
最后一种任务不是单靠某个相似度就能保证完成的。检索方式应围绕问题选择,而不是围绕“哪种技术更新”选择。
四、混合检索:把两种候选线索放到一起
混合检索,hybrid retrieval,可以把关键词与向量检索的结果合并,让两种方法相互补充。
但直接把 BM25 分数 12.6 与余弦分数 0.8 相加,通常没有清楚的尺度依据。一种做法是先校准或归一化分数;另一种做法只利用各自的名次。
RRF,Reciprocal Rank Fusion,倒数排名融合,属于后一种。它给每条结果按名次分配贡献,再把不同列表中的贡献相加。以下是等权重形式:
某条资料的融合分数 = 各列表贡献之和
某列表中的贡献 = 1 ÷ (c + 该资料在列表中的名次)
没有进入该列表,则该列表贡献为 0
其中 c 是排名常数,名次从 1 开始。它与 Top-k 中的 k 不是同一个含义。Elasticsearch:Reciprocal rank fusion
沿用刚才的向量排名,取前三条。再人为设定一个关键词候选列表,演示融合;这里的关键词顺序不是 BM25 实测结果。
关键词列表:A、C、D
向量列表: B、C、A
取 c=60,仅作为算例,得到:
| 资料 | 关键词名次 | 向量名次 | 融合分数 |
|---|---|---|---|
| A | 1 | 3 | 1/61 + 1/63 ≈ 0.032266 |
| C | 2 | 2 | 1/62 + 1/62 ≈ 0.032258 |
| B | 未进入 | 1 | 1/61 ≈ 0.016393 |
| D | 3 | 未进入 | 1/63 ≈ 0.015873 |
融合后 A 排在最前,C 紧随其后。读者可以拿计算器复核:A 与 C 的差距极小。
这次 A 获得了更多曝光机会,但算法没有理解“C 已失效”这一业务事实。假如列表顺序变化,结果也可能变化;RRF 不保证自动选出有效版本。合并后还应按资料 ID 去重,避免同一条笔记因为来自两条路径而被重复塞进上下文。
五、重排与筛选:候选到了,还要看是否适用
假设检索从几千条笔记里找出了几十条候选。接下来可以用 Reranker,重排器,重新判断它们与问题的关系。
一种常见重排模型是 Cross-Encoder,交叉编码器。它把“问题+某条候选资料”放在一起处理,从而直接计算二者之间的关系;这与先独立计算问题和资料向量、再比较分数的双编码器方式不同。Sentence Transformers:Retrieve & Re-Rank
资料向量可以事先算好,适合对大量内容快速搜索。交叉编码器通常需要为本次问题与每条候选分别计算,因而常放在候选缩小之后。重排分数的范围和含义取决于模型,不应默认它就是正确率。
对于本篇例子,重排时希望识别的是:B 讲错了章节,C 是旧安排,D 没有回答时间,A 才提供了明确更新。要判断 C 的有效性,必须把“已失效”等已知信息一并提供给重排器,或交给程序筛选;只给它正文“第 2 章安排在周五”,不能期待它凭空知道版本状态。它可能改善这样的判断,但仍需要验证,不能把“再过一个模型”当作事实保证。
已知的硬条件,应明确表达
如果笔记已经有可靠的 chapter=2、status=current 等字段,就可以用这些元数据筛选候选。元数据是伴随正文存储的描述信息,例如章节、日期、来源、版本状态。
在我们的四条材料中,按第 2 章且当前有效筛选,A 可以保留,B、C 不符合条件。不要只期待向量把这些约束全学会。
不过,筛选依赖字段可靠。把“最后上传的文件”自动标为最新有效版本,就可能从源头选错。版本无法判定时,应保留冲突线索,而不是凭空加一个 current 标签。
筛选放在哪里也有影响:如果先取向量 Top-2 得到 B、C,再把它们全部过滤掉,就只剩空列表;这不表示库里没有 A。可以在候选搜索时应用筛选,或扩大候选后再筛选,具体取决于系统能力。
flowchart LR
Q[用户问题与明确条件] --> K[关键词候选]
Q --> V[向量候选]
K --> M[合并排名与去重]
V --> M
M --> R[可选重排]
R --> C[检查条件与证据]
C --> O[原文片段与来源]
图中是便于理解的示意流程;章节、权限、版本等适用条件应进入实际检索设计,不必统一等到最后一步才处理。
六、索引负责找得快,模型负责怎样表示
准备检索库时,通常保存的不只是向量,还包括资料 ID、原文或原文位置、元数据。否则找到了一个高分向量,也无法告诉读者它对应哪条笔记。
资料向量可提前计算;用户提问时,再计算查询向量并搜索。若只维护少量笔记,可以直接比较所有向量,不必一开始就部署复杂的向量数据库。
资料很多时,为每次查询逐一比较可能太慢。这时可以用 ANN,Approximate Nearest Neighbor,近似最近邻搜索,通过索引结构减少搜索工作,但可能漏掉真正位于向量前列的条目。Faiss 的索引指南区分了精确搜索与具有速度、内存、精度取舍的索引方案。Faiss:Guidelines to choose an index
这里要区分两种“漏掉”:
- 表示问题:按当前向量精确排序,A 本来就在第三名,Top-2 自然找不到它。
- 搜索近似误差:按当前向量 A 本应进入所取范围,却被近似搜索遗漏。
前者不能只靠把近似索引调得更精确来解决;后者也不能只靠修改回答模型的提示词来解决。
编码配置同样要保持配套。问题与资料可以使用共享或分别训练的编码器,但必须处在兼容的向量空间中。两个模型都输出 768 维,不代表坐标含义一致;仅凭维数相同就混用,排序结果可能失去意义。
更换不兼容的嵌入模型或编码方式后,需要重新生成相应资料向量并更新索引。修改、删除笔记时,也要同步更新原文和索引记录。否则检索可能继续找到已经过期甚至已删除的内容。
七、先检查“找回了什么”,再检查“回答得怎样”
不用调用模型,也可以为检索准备一份小检查集。针对你的真实笔记,写下问题、能支持答案的资料 ID,以及不应混入的干扰项。
| 问题类型 | 示例 | 检查重点 |
|---|---|---|
| 表达改写 | 第二章改到哪天了? | 能否找到“第 2 章现改为周六” |
| 精确标识 | 哪条笔记记录了 E1042? | 是否错取 E1043 |
| 版本变化 | 当前安排是什么? | 是否只找回旧安排 |
| 多份证据 | 对比两章的安排 | 是否漏掉其中一章 |
| 没有答案 | 第 9 章何时复习?但库里没有记录 | 是否把“最相似”误当成“有答案” |
当检索被配置为返回前 k 条时,即使所有内容都不合适,也可能返回一些排名靠前的材料。是否有答案,还要检查证据;相似度阈值可以辅助,但阈值应在自己的模型、数据与任务上验证。
可以先用召回率 Recall 衡量找回了多少相关资料,并在前 k 条的位置计算它;精确率 Precision 则衡量返回内容中有多少是相关的。《Introduction to Information Retrieval》:Precision 与 Recall
本篇使用如下定义:
Recall@k = 前 k 条里找回的相关资料数 ÷ 全部已标注相关资料数
这里的“相关”要按任务预先定义。例如本篇问题要求当前安排,已失效的 C 不算能支持当前答案的相关资料。
在四条材料中,只有 A 符合这一定义。向量排序 B、C、A、D 的 Recall@2 是 0,Recall@3 是 1。后者表示找回了所需资料,不表示前三条都合适,更不表示后续模型一定会据此答对。
再构造一个需要 A、E 两份证据的问题。若前三条是 A、B、D,Recall@3 为 1/2,Precision@3 为 1/3;前者看漏没漏,后者看返回内容里有多少相关资料。这里 E 是为指标练习额外设定的相关资料,不属于前面的四条向量算例。
没有相关资料的查询会使上述召回率分母为零,应单独检查“无答案”处理,不把它机械计为 0 或 1。标注若不完整,指标也会有偏差。
排查时,先看候选列表和原文:正确内容完全没进入候选,优先查检索与数据;正确内容已经在候选里却被移除,查筛选与重排;证据已经交给回答模型却仍答错,才进入下一篇的回答链路。
八、理解检查
1. 一条笔记被转换为向量并存入检索库,是否说明回答模型已经学会它?
没有。通常只是编码与存储,没有更新回答模型的参数。后续还需要按问题找到这条记录,并把所需原文用于回答。
2. A 从 [4,3] 变成 [8,6],与 Q=[1,0] 的余弦是多少?直接点积是多少?
余弦仍为 0.8;直接点积为 8。比例放大没有改变方向,但改变了向量长度和未归一化点积。
3. 向量 Top-2 为 B、C,过滤掉错误章节和失效资料后为空,能否认定没有答案?
不能。A 还在候选范围之外。应检查筛选时机、扩大候选或调整检索方式,不能把候选为空直接等同于资料不存在。
4. RRF 把 A 排在 C 前面,是否证明它理解了新旧版本?
不能。这个计算只用了名次。版本判断仍来自可靠元数据或对原文的进一步分析。
5. 两个嵌入模型输出维度相同,能否直接用一个编码问题、另一个编码笔记?
不能仅凭维度相同这样做。它们必须经过配套训练或其他兼容性验证,才能在同一空间比较。
6. 已知相关资料是 A、E,返回 A、B、D,Recall@3 与 Precision@3 各是多少?怎样改善?
分别是 1/2 和 1/3。先查 E 为什么未被召回,再查 B、D 为什么占据前列。重排只能调整已有候选;如果 E 根本没进入候选,仅重排这三条无法补回它。
现在,你可以把检索过程拆成几个可检查的环节:表示是否适合任务、候选是否覆盖证据、排序是否保留有用资料,以及硬条件是否被正确执行。
下一篇《RAG:怎样让模型依据你的资料回答》,将把这些候选原文组织进上下文,继续处理资料切分、来源引用和回答验证。
上一篇:提示词与上下文学习 · 返回第二部分大纲