跳到主要内容

一句话怎么变成一串数 — 按掩码求平均、384 维,和三种「远近」

这一章讲三件事: 一段文字被压成一串数的中间发生了什么; 「意思近的东西这串数也近」这条性质是从哪来的;以及「远近」到底有哪几种量法。 它在全书链条里的位置: 这是六站里的第三站。前面两站的产物到这里被换了形态 —— 从此后面所有的「像不像」,都变成了对这串数的算术。

1. 顶层全景

一段文字

① 切成标记(第 03 章讲过的那个单位)

["retrieval", "aug", "##mented", "generation", …]

② 每个标记各出一串数

一叠向量:标记 1 一串、标记 2 一串、……

③ 按「哪些位置是真内容」求平均,压成一串

这一段文字的一串数(384 个)

④ 除以自己的长度,让它长度恰好等于 1

可以直接拿去比远近了

图说:①②③ 是模型内部干的事,④ 是可选的一步收尾。
书把 ①②③ 的代码原样摆了出来,却把小标题写成「Tokenize the documents」,一个字不解释。

一句话链条: 机器只会算数 → 那就把文字换成数 → 但要让「意思近」变成「数也近」→ 这条性质靠训练逼出来 → 于是「像不像」变成「远不远」→ 而「远近」有三种量法,方向还不一样。

2. 先看现象:按字面找有一个躲不开的死角

书自己把这个死角说得很清楚。这里说的关键词搜索(拿查询里的字,到文档里逐字去对, 对上的多就排前面)靠的是字面匹配,而查询换一种说法,它就找不到目标了1

举个例子:你的文档里写的是「员工离职需提前一个月提交书面申请」, 而用户问的是「辞职要提前多久打报告」。两句话讲的是同一件事,共同的字一个都没有。

所以需要一种「按意思比」的表示 —— 这就是这一站存在的全部理由。

照实说: 书从头到尾没有演示过一次「换个说法也能命中」。 它的每个演示语料里,查询和目标文档总是共享关键词。 所以这一条我们只能引用书的立论,不能引用书的证据。

3. 不是给一个号,是给一串数

最容易想到的办法行不通

给每个词编个号:「猫」是 3542 号,「狗」是 3543 号。

这条路走不通,理由很简单:编号是随便定的。 3542 和 3543 挨着,不代表猫和狗有什么关系; 你换个排法,它们就离得很远。号码之间的距离不携带任何意思。

真正的办法

不给一个数,给一串数;并且让意思相近的东西,这串数也相近。

「辞职要提前多久打报告」 → [ 0.12, -0.03, 0.44, …… ] 共 384 个数
「员工离职需提前一个月……」 → [ 0.11, -0.05, 0.41, …… ] 共 384 个数
↑ 每一位都很接近

「今天中午吃什么」 → [-0.31, 0.28, -0.07, …… ]
↑ 每一位都对不上

图说:这些数是为演示编的,不是真实数值。要看真数,见第 5 节的走查。

这一步做成了,「像不像」就变成了「远不远」—— 一个机器算得出来的量。

第 01 章说过,这串数叫这段文字的嵌入,也叫它的向量

4. 「意思近 ⇒ 数也近」这条性质是从哪来的

这一节是这一章的地基。跳过它,后面所有「为什么这条没检索到」都推不出来。

先把一个误会拆掉:这不是天生的

一个刚训练完的通用语言模型,它内部那些数并不满足这条性质 —— 语义相近的两句话,在它的输出空间里并不相近2

所以这条性质是被专门训出来的。 干这件事的那类模型有个专名叫句向量模型 (英文 sentence-transformers,你在模型名字里看到的 all-MiniLM-L6-v2 就是其中一个)。

怎么训出来的

做法朴素得出奇:

准备一大堆「该像的一对」:
(一个问题,它真正的答案)
(一句话,它的另一种说法)
(一个查询,它命中的那篇文档)

每次拿一小批出来:
├─ 把「该像的这一对」往一起拉
└─ 把这一批里其余所有的组合往开推

重复几百万次。

图说:「推远谁」这件事决定了训练信号的质量——同一批里凑巧也很像的那些,
被硬推开会伤到模型;所以怎么挑「推远的对象」是这类训练最讲究的地方。

「意思近的东西这串数也近」,就是这么被逼出来的副产品。 没有人写规则告诉它什么叫「意思近」, 它只是被反复奖惩到这个程度3

边界:它只在训练过的那类文本上守信

这条性质不是普适定律,是一个训练结果。所以它有明确的失效范围:

什么时候它不守信为什么
你的文本是它没见过的类型(内部工单编号、化学式、代码)训练时没有这类「该像的一对」,它压出来的位置没有依据
你把正文洗成了不成句的词串(第 02 章那个去停用词的配方)它训练时见的全是完整句子
你的语言它训得少(很多模型是英文为主)同上

这三条,书一条都没提。 而它们是「为什么这条明明相关却没检索到」最常见的三个答案。

5. 主走查:一句话被压成一串数,一步步看数字

这一节是本章的主走查。上面每个机制,在下面都占一步。

先说清楚:这一章书里没有一条连贯的线 —— 四个配方各带各的语料。 下面这条线是我们接起来的,每一步标出配方号,接缝处照实说书换了什么。

出自具体的数 / 状态
① 三句话进去配方 42"RAG is a powerful technique for combining retrieval with language models." 等三句
② 切成标记、补齐长度配方 42书只印了代码没印标记,这一步的中间结果书里没有
③ 每个标记各出一串数配方 42同上
④ 按掩码只对真内容求平均配方 423 个向量;第一句的前 5 个数是 tensor([-0.2326, 0.1575, 0.2014, -0.0072, -0.3706])
接缝配方 41 换了输入(一句话)走的是包好的完整流水线,比上面多一步收尾
⑤ 一句话 → 一串数,有多少个配方 41384 个;前 10 个是 [-0.0455, -0.0481, -0.0086, 0.0733, …]
接缝配方 50 换了输入(三条只差标点和大小写的句子)——
⑥ 除以自己的长度之后配方 50三条的长度全都是 norm=1.00
接缝配方 43 换了输入(四句话)——
⑦ 拿两串数比远近配方 43问「How to search a vector embeddings?」→ 距离 1.02791.3194

下面逐步展开。

第 ①②③ 步:先切成标记,每个标记各出一串数

书有一个配方不用现成的库,直接手写了一遍这个过程4。它是全书唯一一次露出模型内部:

encoded = tokenizer(docs, padding=True, truncation=True, return_tensors='pt')
output = model(**encoded)
output.last_hidden_state ← 每个标记一串数

图说:第一行把三句话切成标记;因为三句长短不一,要一起喂进模型就得补齐到同样长,
补出来的位置塞的是占位符。第三行拿到的是「每个标记各一串数」的一叠。

注意「补齐」这件事 —— 它直接导致了下一步必须多做一个动作。

第 ④ 步:按掩码求平均,把一叠压成一串

现在手上是一叠向量,一个标记一串。可我们要的是「整句话一串」。

最直接的办法是求平均。但不能全都平均 —— 补出来的那些占位符不是内容,平均进去会稀释真内容。

所以模型另外给了一串 0 和 1,标出哪些位置是真内容,哪些是补的。这串 0/1 叫掩码。

三句话补齐到同样长(假设 12 个位置):

句 1: 真 真 真 真 真 真 真 真 真 补 补 补
掩码: 1 1 1 1 1 1 1 1 1 0 0 0

做法: 每个标记的那串数 × 它的掩码 → 再全部相加 → 再除以掩码之和(这里是 9)

图说:乘 0 就等于把补出来的位置整个抹掉;除以 9 而不是除以 12,
所以句子长短不同也不会影响结果的尺度。这一步的正式名字叫 **mean pooling**(按掩码求平均)。
上面的位置数是为演示编的,书里没印标记数。

书里这段代码的小标题写的是「3. Tokenize the documents」—— 它把这一整套动作叫成了「切标记」,mean pooling 是什么、为什么要除以掩码之和,一个字没解释。

这一步跑完,三句话各得到一串数。第一句的前 5 个是:

tensor([-0.2326, 0.1575, 0.2014, -0.0072, -0.3706])

第 ⑤ 步(接缝):走包好的流水线,是 384 个数

书换了输入。 另一个配方用现成的库嵌一句话,打印出5:

Embedding dimension: 384
First 10 values of the embedding:
[-0.04551849886775017, -0.04808536171913147, -0.008631237782537937, 0.07327593117952347, …]

图说:一句话进去,384 个小数出来。上面只印了前 10 个。

这个 384 有个名字叫这串数的维度:说的就是「这串数一共有多少个数」。

384 是多少? 拿那句英文比一下:输入是一句 15 个词左右的话,出来 384 个数。 这一步不是压缩,是换一种表示 —— 数比词还多。

而这里有个值得停一下的差别。 把第 ④ 步和第 ⑤ 步的数字并排看:

手写那一版(第 ④ 步): -0.2326、0.1575、0.2014、-0.0072、-0.3706
包好那一版(第 ⑤ 步): -0.0455、-0.0481、-0.0086、 0.0733、……

图说:同一个模型,手写那一版的数明显大一圈——大约五倍。

判断(我们的,不是书里的): 差别在于包好的那条流水线最后多做了一步: 把向量除以自己的长度。 算一下就知道:384 个数如果平方和等于 1,平均每个数的大小 只有 1÷√384 ≈ 0.051 —— 和第 ⑤ 步那几个数正好一个量级;而第 ④ 步那几个数大到 0.37, 一个长度为 1 的 384 维向量不可能有这么大的分量。 如果错,会错在: 如果两版的差别其实来自输入不同(它们确实是不同的句子), 那这个解释就站不住。判据是量级:换句子会让每个数变,但不会让整体大小系统性地差五倍。

第 ⑥ 步(接缝):除以自己的长度之后,长度恰好是 1

书又换了输入,这次是三条只差标点和大小写的句子。 它先算出向量,再手动除以每个向量自己的长度,打印出来的是6:

Sentence 1 (norm=1.00): [-0.11171343 0.01787068 -0.02697249 0.0171152 -0.0706844 ] ...
Sentence 2 (norm=1.00): [-0.11091074 0.01590041 -0.02369356 0.002776 -0.07597825] ...
Sentence 3 (norm=1.00): [-0.11298969 0.01687417 -0.04100865 0.02022843 -0.07349404] ...

图说:`norm` 就是这串数的长度,三条全是 1.00——这是「除以自己的长度」这个动作的定义,
不是什么发现。真正值得看的是三行数字本身:它们几乎一模一样,只差在小数点后第三位。

这一步正式的名字叫归一化(L2 归一化)。 它到底有什么用、书在这里把它说成了什么,第 8 节专门讲。

第 ⑦ 步(接缝):终于可以比远近了

书最后一次换输入,四句话,建一个最朴素的索引,问「How to search a vector embeddings?」, 取最近的两条7:

Match 1: 'Embedding turns text into numeric vectors.' (distance: 1.0279)
Match 2: 'FAISS enables fast similarity search.' (distance: 1.3194)

图说:这两个数是**距离**——越小越近。
参照物很好记:两串数完全一样时距离是 0;这里第一名是 1.03,第二名是 1.32,
两者只差 0.29,说明这两条候选其实咬得很紧。

到这里,这一章的链条闭合了: 文字 → 标记 → 一叠向量 → 一串数 → 长度归一 → 一个可以排序的距离。

6. 384 是模型定死的,所以两头必须用同一个模型

384 不是你能调的参数

它是模型出厂时就定死的。 你换一个模型,这个数就变:

模型一串数有多少个
all-MiniLM-L6-v2(这本书主要用的)384
all-mpnet-base-v2(这本书第 9 章后半段悄悄换成的)768 —— 正好是前者的两倍8

书没有一处说过这件事。 它只是印出 384,像在报告一个观察结果。

后果:入库和查询必须用同一个模型

入库时: 文档 ──(模型 A)──► 一串 384 个数 ──► 存进库
查询时: 问题 ──(模型 B)──► 一串 768 个数 ──► 拿去比

结果:根本比不了——长度都不一样。

图说:就算两个模型碰巧都是 384 维,也一样不能混用:
两个模型各自把「意思」摆在不同的位置上,同一个 384 维空间里的坐标含义完全不同。

而书里有一处正好犯了这个错: 有个配方建库时调用嵌入模型不传模型名字, 用的是库的默认模型;而同一本书其他地方一律指定 all-MiniLM-L6-v29书从没有警告过这件事。

判断(我们的,不是书里的): 这是这本书里最容易被照抄进生产的一个坑。 因为它不报错 —— 只要两边的维度碰巧一样,程序会正常跑完,只是检索结果全是乱七八糟的东西。 如果错,会错在: 如果那个默认模型恰好就是 all-MiniLM-L6-v2,那这一处就没有问题。 判据是运行时打印一下维度:书里那个配方从没打印过。

7. 三种「远近」,方向还不一样

这一节是这一章最该记住的东西。 前面第 01 章那三个数(0.9408 / 1.3505 / 2.0506)、 本章第 ⑦ 步那两个数(1.0279 / 1.3194),都要在这里才说得清。

三种量各是什么

第一种叫 L2 距离:把两串数看成空间里的两个点,量这两点之间有多远。 越小越近,完全相同是 0,而且没有上限 —— 两个点可以要多远有多远。

第二种叫内积:把两串数对应位置相乘,再全部加起来。越大越像。 它同时受两件事影响:方向像不像,以及各自有多长。

第三种叫余弦相似度(常常简称余弦):只看方向、不看长度。 范围是 −1 到 1 —— 1 是方向完全一致,0 是毫不相干,负数是方向相反。

① L2 距离 越小越近 0 ────────► 没有上限
② 内积 越大越像 受方向和长度双重影响
③ 余弦相似度 越大越像 −1 ──── 0 ──── 1

图说:三种量的方向不一样——①是「越小越好」,②③是「越大越好」。
这就是第 01 章那三个数最容易读反的原因。

用书里的真数字看每一种

量法书里哪一处数字怎么读
L2 距离第 01 章配方 110.9408 / 1.3505 / 2.0506第一名是最小的那个;三者拉得挺开
L2 距离本章第 ⑦ 步1.0279 / 1.3194同上,但两者只差 0.29,咬得很紧
余弦相似度第 9 章配方 910.3682 / 0.0452 / −0.0133满分是 1,第一名只拿到三分之一多一点;第三名是负的,方向已经相反了,照样进了前三
余弦相似度第 9 章配方 950.6190这是全书最高的一个余弦分,也才 0.62

最后一行那两个数值得记住: 在真实语料上,0.6 就已经算「很像」了。 如果你期待相关文档能拿到 0.9,那是对这个量的误解。

三者的关系

这三种量不是三件事,是同一件事的三种切法:

给 a 和 b 都除以自己的长度(也就是第 ⑥ 步那个动作)之后:

内积 = 余弦相似度 ← 长度都是 1,乘出来的就只剩方向
L2 距离² = 2 − 2 × 余弦 ← 所以「L2 越小」和「余弦越大」排出来的名次完全一样

图说:归一化之后,三种量排出来的名次是同一个。这就是下一节的全部内容。

8. 归一化真正的用途 —— 书在这里说错了

书说了什么

真实的文本里总有一些不该有的杂东西:错字、大小写不统一、扫描识别错的字符 —— 这类东西行话叫噪声

书有一节的标题就叫「用预归一化做抗噪声的嵌入」,正文说:真实数据有这些毛病, 而归一化是一种在它们进入下游之前稳住嵌入的技术10

它的真正用途

归一化和抗错字没有任何关系。 它只做一件事:把向量的长度统一成 1。

而这件事换来的是上一节最后那个等式:

长度都是 1 之后,内积就等于余弦相似度。

这就是它全部的实用价值,而且价值很大:

想按「意思像不像」排序 → 想用余弦相似度

└─ 可是向量库里最快、最基础的两种索引,一种算 L2 距离,一种算内积,
**没有一种直接算余弦**

└─ 那就先把所有向量归一化 → 从此内积就是余弦 → 用内积索引即可

图说:归一化是一步「预处理换算力」的交易:入库时每条多做一次除法,
换来查询时可以用最快的索引直接得到余弦。

书自己在第 9 章把它用对了一次: 那个配方在嵌入时打开 normalize_embeddings=True, 然后建的是内积索引,注释里明写「scores are cosine similarities」11同一件事,第 4 章讲错、第 9 章用对,而书没有把两处联系起来。

书自己印出来的那三行,说明的恰好是相反的事

回头看第 ⑥ 步那三行数字。三条句子的区别是:一条正常、一条多了两个感叹号和几个空格、 一条全大写。它们归一化之后的向量几乎一模一样,只差在小数点后第三位。

判断(我们的,不是书里的): 这三条几乎一样,功劳不在归一化,在模型本身。 理由是算术上的:归一化只改长度、不改方向,所以「三条的方向几乎一致」这件事 在归一化之前就已经成立了 —— 否则除以一个正数也变不出来。 书拿这个输出当「抗噪声」的证据,实际上它证明的是「这个模型本来就不怕这点噪声」。 如果错,会错在: 如果这三条在归一化之前长度差得很多,而书想说的「稳住」指的是长度而非方向, 那么这个说法在某种狭义上成立。判据是原文:它说的是抗错字、抗大小写、抗识别失真, 那是方向层面的事,不是长度层面的事。

9. 另起一处:块切得越碎,块与块之间越不像

这是本章唯一一处需要另起的走查,因为它落在失效路径上。

书的最后一个配方想做一件事:把一篇短文切成小块,两两算相似度,超过 0.7 的连一条边, 最后画出一张「哪些块彼此相关」的图。

走一遍

输入: 一段讲 RAG 的短文
切块: 每块最多 80 个字符、重叠 20 ← 注意这个 80
切出: 7 块,其中第 2 块和第 3 块还共享着「them into chunks,」这几个词
两两算相似度,阈值 0.7

输出:
Graph edges based on similarity >= 0.7:
(下面什么都没有)

图说:一条边都没连出来。连相邻且共享文字的第 2、3 块都没过线。
而书照样把它当成功案例收了尾。

为什么会这样

因为 80 个字符太短了。 一块只有半句话,它的「意思」很稀薄 —— 两块各自讲半件事,方向自然对不上。

这条经验可以直接带走:

块越大 → 每块的意思越完整 → 块与块之间越容易相似
块越小 → 每块的意思越单薄 → 相似度整体下滑

图说:所以「阈值定在 0.7」这种写法,在小块上根本不成立——
阈值必须跟着块大小一起调,而书里两个用阈值的配方都是写死的。

这一条同时回答了第 05 章的一个问题: 相关性阈值该定多少? 没有一个普适的数 —— 它取决于你的块有多大、模型是哪个、语料是什么。

10. 作者的判断与证据

书里给了证据的:

说法证据
一句话可以变成固定长度的一串数配方 41 的输出:Embedding dimension: 384
中间要经过「每个标记一串数 → 求平均」配方 42 手写的那段代码,是全书唯一一次露出内部
归一化之后长度是 1配方 50 的输出:三条全是 norm=1.00
有了向量就能排出名次配方 43 的两个距离:1.0279 / 1.3194
归一化 + 内积 = 余弦第 9 章配方 91 的代码注释与那三个余弦分

作者只是断言、没有给证据的:

说法缺什么
「预归一化能抗错字、抗大小写、抗识别失真」这是错的,而且它自己印出来的那三行恰好说明了相反的事
那个块相似图配方「能用来做需要跨块串联的多步问答」输出是零条边,一个用途都演示不了
「只嵌摘要」那个配方说检索到摘要后「可以再展开成全文」代码没有实现:那三个文档根本没有元数据,检索到摘要之后回不到全文
有个配方标题写「用元数据做更好的过滤」代码里一次都没过滤,只是把标签打印出来

11. 边界与局限

  • 书从没解释过 384 是什么、从哪来、换模型会不会变。
  • 书从没警告过「入库和查询必须用同一个模型」,而它自己有一处正好犯了这个错。
  • 三种量法书一种都没讲。 L2、内积、余弦在书里各出现过,但从没有一处说明它们的方向和关系。
  • allow_dangerous_deserialization=True 这个参数在这一章第一次出现,零解释 (它是什么,第 05 章讲)。
  • 有一个配方的正文里混进了另一个配方的介绍 —— 讲的是「先用轻量模型召回、 再用在线服务重排」,和它所在的那一节毫无关系,后面也没有对应的代码。像是删了某个配方后的残留。
  • 多语种、图文混合的嵌入一个字没有(书里那张选型表提了一句「图文混用的场景换别的模型」,再无下文)。

12. 可带走的

全章那条走查,一行写完:

三句话 → 切成标记、补齐长度 → 每个标记各一串数 → 按掩码求平均得 tensor([-0.2326, 0.1575, 0.2014, -0.0072, -0.3706])

(换输入)走完整流水线得 384 个数、前几个是 [-0.0455, -0.0481, …] → (换输入)除以自己的长度后 norm=1.00 → (换输入)拿去比远近得距离 1.0279 / 1.3194

  1. 给一串数,不是给一个号 —— 号码之间的距离不携带意思;
  2. 「意思近 ⇒ 数也近」是训练逼出来的,不是天生的 —— 拿海量「该像的一对」拉近、其余推远;
  3. 所以它有失效范围:没见过的文本类型、被洗成词串的正文、训得少的语言;
  4. 中间三步:切成标记 → 每标记一串数 → 按掩码求平均;掩码是为了不把补齐用的占位符平均进去;
  5. 384 是模型定死的,换模型会变(另一个常用模型是 768,正好两倍);
  6. 入库和查询必须用同一个模型 —— 弄错了不报错,只是结果全是乱的;
  7. 三种量法方向不同: L2 距离越小越近(没有上限)、内积越大越像、余弦范围 −1 到 1;
  8. 真实语料上 0.6 的余弦就算很像了,别期待 0.9;
  9. 归一化的用途是让内积等于余弦,不是抗错字 —— 书这里说错了,而它第 9 章又用对了;
  10. 块越小,块与块之间的相似度整体越低 —— 所以固定阈值必须跟着块大小一起调。

13. 原文地图

主题原书章原文位置
关键词搜索的死角CHAPTER 5 Vector Stores for Semantic Retrievaltext/12-fm-introduction.txt:7(搜「struggles when queries are phrased differently」)
手写 mean pooling(全书唯一一次露出内部)CHAPTER 4 Embedding Strategies for Vector Retrievaltext/11-fm-introduction.txt:233(搜「def mean_pooling(model_output, attention_mask)」) · text/11-fm-introduction.txt:245(搜「padding=True, truncation=True」) · text/11-fm-introduction.txt:273(搜「Document 1 vector (first 5 dims)」)
384 维同上text/11-fm-introduction.txt:150(搜「Embedding dimension: 384」) · text/11-fm-introduction.txt:154(搜「-0.04551849886775017」)
裸索引比距离同上text/11-fm-introduction.txt:409(搜「distance: 1.0279」) · text/11-fm-introduction.txt:411(搜「distance: 1.3194」)
归一化被说成「抗噪声」同上text/11-fm-introduction.txt:1195(搜「typo errors, inconsistent casing」) · text/11-fm-introduction.txt:1197(搜「Pre-normalization is a technique that stabilizes embeddings」)
归一化之后 norm = 1.00同上text/11-fm-introduction.txt:1314(搜「Sentence 1 (norm=1.00)」)
块相似图:零条边同上text/11-fm-introduction.txt:1400(搜「chunk_size=80, chunk_overlap=20」) · text/11-fm-introduction.txt:1462(搜「Graph edges based on similarity >= 0.7」)
建库时不传模型名CHAPTER 1 Foundation of Retrieval-augmented Generationtext/08-fm-introduction.txt:959(搜「embeddings = HuggingFaceEmbeddings()」)
归一化 + 内积 = 余弦(用对的那一次)CHAPTER 9 Effective Search for RAG Systemstext/16-fm-introduction.txt:146(搜「normalize_embeddings=True」) · text/16-fm-introduction.txt:170(搜「scores are cosine similarities」)
真实语料上的余弦分同上text/16-fm-introduction.txt:196(搜「score=0.3682」) · text/16-fm-introduction.txt:200(搜「score=-0.0133」) · text/16-fm-introduction.txt:900(搜「0.6190」)
第 9 章后半段换了嵌入模型同上text/16-fm-introduction.txt:829(搜「all-mpnet-base-v2」)

Footnotes

  1. 出处:「CHAPTER 5 Vector Stores for Semantic Retrieval」第 7 段(text/12-fm-introduction.txt:7,搜「struggles when queries are phrased differently」)。原文:传统的关键词搜索引擎靠字面匹配,「当查询的措辞与它想找的文本不同时,常常力不从心」。

  2. 补充(不在书里,依据我们的 frontier 书架):预训练骨干模型的输出空间里,语义相近的句子并不相近;把它变成一个句向量模型,靠的正是后续的对比训练。 依据: shelf=ai-frontier-reference/sentence-transformers#02-contrastive-training.md @652c8e8a70b9bef7161405ac7e6d8f3b2a828aed 事实=文档写着「预训练骨干(如 MPNet)的输出空间里,语义相近的句子并不相近。训练嵌入模型的任务就是重塑这个空间」。

  3. 补充(不在书里,依据我们的 frontier 书架):训练做的是两件事 —— 拉近「语义该近的对」(同义句、查询与命中文档),推远其余一切;而「推远谁」决定了训练信号的质量。默认损失把同一批里的其他样本当成要推远的对象。 依据: shelf=ai-frontier-reference/sentence-transformers#02-contrastive-training.md @652c8e8a70b9bef7161405ac7e6d8f3b2a828aed 事实=文档列出训练的两条动作是「拉近:语义该近的对」「推远:其余一切——关键是推远谁决定了训练信号的质量」,默认损失是用同批负例的 InfoNCE。

  4. 出处:「CHAPTER 4 Embedding Strategies for Vector Retrieval」第 233 段(text/11-fm-introduction.txt:233,搜「def mean_pooling(model_output, attention_mask)」)。补齐那一行在第 245 段(text/11-fm-introduction.txt:245,搜「padding=True, truncation=True」);输出在第 273 段(text/11-fm-introduction.txt:273,搜「Document 1 vector (first 5 dims)」)。这段代码的小标题写的是「3. Tokenize the documents」(text/11-fm-introduction.txt:227,搜「Tokenize the documents」)。

  5. 出处:同章第 150 段(text/11-fm-introduction.txt:150,搜「Embedding dimension: 384」)与第 154 段(text/11-fm-introduction.txt:154,搜「-0.04551849886775017」)。输入是一句 15 个词左右的英文(text/11-fm-introduction.txt:131,搜「Retrieval-Augmented Generation is a powerful technique for」)。

  6. 出处:同章第 1314 段(text/11-fm-introduction.txt:1314,搜「Sentence 1 (norm=1.00)」)。三条输入句写在第 1265–1270 段(text/11-fm-introduction.txt:1270,搜「RETRIEVAL AUGMENTED GENERATION (RAG) IS AN ARCHITECTURE」),区别只有标点、空格和大小写。

  7. 出处:同章第 409 段(text/11-fm-introduction.txt:409,搜「distance: 1.0279」)与第 411 段(text/11-fm-introduction.txt:411,搜「distance: 1.3194」)。用的索引是最朴素的那一种(text/11-fm-introduction.txt:369,搜「faiss.IndexFlatL2(dimension)」),它算的是 L2 距离。

  8. 出处:「CHAPTER 9 Effective Search for RAG Systems」第 829 段(text/16-fm-introduction.txt:829,搜「all-mpnet-base-v2」)—— 这一章从这个配方起悄悄换了嵌入模型,书没有一个字交代。补充(不在书里,来自通用知识): all-mpnet-base-v2 输出 768 个数,all-MiniLM-L6-v2 输出 384 个。

  9. 出处:「CHAPTER 1 Foundation of Retrieval-augmented Generation」第 959 段(text/08-fm-introduction.txt:959,搜「embeddings = HuggingFaceEmbeddings()」)—— 括号里是空的,没传模型名。同一本书其他地方一律写明 sentence-transformers/all-MiniLM-L6-v2(text/08-fm-introduction.txt:639,搜「model_name="sentence-transformers/all-MiniLM-L6-v2"」)。

  10. 出处:「CHAPTER 4 Embedding Strategies for Vector Retrieval」第 1195 段(text/11-fm-introduction.txt:1195,搜「typo errors, inconsistent casing」)与第 1197 段(text/11-fm-introduction.txt:1197,搜「Pre-normalization is a technique that stabilizes embeddings」)。小节标题是「Noise-resistant embeddings with pre-normalization」(text/11-fm-introduction.txt:1193,搜「Noise-resistant embeddings with pre-normalization」)。

  11. 出处:「CHAPTER 9 Effective Search for RAG Systems」第 146 段(text/16-fm-introduction.txt:146,搜「normalize_embeddings=True」)与第 170 段(text/16-fm-introduction.txt:170,搜「scores are cosine similarities」)。这个配方建的是内积索引(text/16-fm-introduction.txt:156,搜「faiss.IndexFlatIP(dim)」)。