先给生成式大语言模型一段开头。

北京是中国的

它要从词表里预测下一个 token,答案是“首都”。

向量模型拿到的则是一组文本关系。

查询      怎样给 PDF 设置密码?
相关文档  给 PDF 文件添加打开密码
困难负例  忘记 PDF 密码后如何解除

它不续写文字,只需要让查询更接近相关文档,远离那个看起来很像、实际解决另一件事的负例。

两类模型都会先把文本变成数字,让数字经过多层神经网络,再根据错误修改参数。训练数据怎样出题,损失函数怎样判错,最终把相近的底层计算训练成了不同工具。

本文只比较其中两类常见模型。一类根据左侧文本预测下一个 token,另一类把整段文本压成单个向量,再学习哪些文本应该靠近。多向量检索和稀疏向量模型不在这次讨论范围内。

文本怎样进入神经网络

神经网络不能直接对汉字做矩阵乘法。文本先由 Tokenizer 切成若干 token。

北京 / 是 / 中国 / 的 / 首都

这里只按词展示。实际怎样切分取决于模型的 Tokenizer,一个词可能对应一个或多个 token。

每个 token 在词表里都有编号。下面用几个虚构编号演示这个过程。

北京 → 8312
是   → 247
中国 → 3021
的   → 19
首都 → 9276

编号只负责定位。8312 并不比 247“更有语义”。模型会用编号去嵌入表里查出一串可训练的浮点数。

北京 → [0.12, -0.31, 0.08, ...]
是   → [-0.07, 0.21, 0.44, ...]
中国 → [0.35, -0.16, 0.09, ...]

这串数字才是进入 Transformer 的初始向量。4096 维可以先理解成模型为一个 token 保留了 4096 个记录位。它们不是人工指定的“词性位”或“地点位”,单独看某一维通常没有明确含义。

假设一句话有 5 个 token,每个向量有 4096 维,[5, 4096] 就表示 5 个位置各自带着 4096 个数字。每层 Transformer 都读取这组向量,再输出一组更新后的向量。形状通常不变,但每个位置的数字会根据周围 token 重新变化。

某一层计算结束后留下的这组数字,叫作这一层的隐藏状态。“苹果公司发布手机”和“把苹果削皮”中的“苹果”起初可能查到相同嵌入,经过上下文计算后会得到不同状态。这个过程在 向量为什么能够表示语义 中有更完整的展开。

模型可能有数千万到数百亿参数。参数分布在许多计算环节里。有些参数把 token 编号变成初始向量,有些结合上下文更新向量,还有一些把结果变成任务需要的分数。生成式语言模型需要词表输出层,向量模型则可能增加池化或投影层。

训练不会找到“北京”对应的参数,然后把“首都”写进去。每批数据只让参数移动一小步,过程可以拆成五步。

  1. 取出一批训练数据。
  2. 模型根据当前参数完成前向计算。
  3. 损失函数算出这批结果错了多少。
  4. 反向传播计算哪些参数让错误变大或变小。
  5. 优化器根据这些信息更新参数。

一轮调整之后,模型通常仍然会犯错。相似规律在海量数据中反复出现,参数才逐渐形成稳定倾向。预训练留下的是一套参数,不是一份可以直接打开的知识表。

大语言模型怎样走完一次更新

一段文本会变成很多道题

假设训练文本是下面这句话。

北京 是 中国 的 首都

它会被拆成四个预测位置。

看到“北京”              → 预测“是”
看到“北京 是”           → 预测“中国”
看到“北京 是 中国”      → 预测“的”
看到“北京 是 中国 的”   → 预测“首都”

具体做法是把输入和答案错开一位。

输入  北京  是    中国  的
标签  是    中国  的    首都

Transformer 可以并行计算这些位置,但因果掩码不允许前面的位置偷看右侧答案。预测“中国”时只能读取“北京 是”,预测“首都”时可以读取“北京 是 中国 的”。

一段 2048 个有效 token 的序列,大致能提供 2047 个下一个 token 预测位置。网页、书籍和代码不需要人工逐句写答案,向右错开一位就有了密集的监督信号。

隐藏状态怎样变成词表概率

最后一个位置会留下一个 4096 维隐藏向量。

[0.17, -1.23, 0.48, ...]

它还不是 token。词表输出层会把 4096 个数字映射成与整个词表一样长的一组分数。词表有 10 万项时,输出层也会给出 10 万个 logit。

首都 → 2.1
城市 → 3.4
地方 → 2.8
……

logit 是未经归一化的分数。经过 Softmax 后,所有候选分数会变成总和为 100% 的概率。训练程序只检查正确答案拿到了多少概率。

这里使用的判分方法叫交叉熵。模型给正确答案的概率越低,惩罚越大;概率越高,惩罚越小。所有有效预测位置的惩罚取平均,就得到这批文本的损失。

为了看清调整方向,先用一组简化数字示意。真实训练中,单次更新造成的变化通常更小,也不会只影响这一句话。

首都  10%
城市  35%
地方  20%
其他  35%

正确答案“首都”只有 10%,损失会比较大。反向传播沿着前向计算走过的路径倒着检查。如果某个参数增加一点,损失会上升还是下降,变化有多大。这个变化率叫梯度。

优化器根据梯度更新参数。

新参数 = 旧参数 - 学习率 × 梯度

学习率控制步子大小。步子太大,训练可能震荡;步子太小,训练速度很慢。真实训练还会加入控制更新速度和数值稳定性的措施,但没有改变“前向计算—损失—反向传播—参数更新”这条链路。

经过一次示意性的调整,模型再次遇到相近上下文时,概率可能发生下面的变化。

首都  18%
城市  30%
地方  17%
其他  35%

梯度不只会修改词表输出层。只要参数没有被冻结,它就会继续传到每层 Transformer 和嵌入表。模型调整的不只是“首都”的分数,也包括怎样读取“中国”、怎样组合“国家—首都”关系,以及哪些上下文对当前预测更重要。

模型继续读到“中国的首都是北京”“法国的首都是巴黎”“某国的首都位于……”等文本,城市、国家和首都之间的关系才会在许多参数中逐渐稳定下来。

预测下一个 token 为什么会形成语义

想在未见过的上下文中继续降低损失,模型不能只依赖逐句记忆,还需要找出可以重复使用的规律。

当模型预测“发布”时,左侧可能已经出现“苹果公司”;预测“削皮”时,左侧出现的则是“把苹果”。两个目标词不同,模型必须把已知上下文编码成不同状态。对象、动作、位置和搭配因此逐渐进入隐藏状态,词表输出层再读取这些状态完成预测。

代码也有类似压力。如果后文再次使用前面定义的变量,模型只有保留变量关系,才更容易预测正确内容。长文章再次提到某个人物时,模型也需要利用前文信息判断接下来更可能出现什么。

语义表示是在这些预测压力下逐渐形成的。训练目标仍然只检查下一个 token,不会保证模型真正理解,也不会直接验证事实真假。模型可能学会流畅地复述错误材料。

有语义的隐藏状态为什么还不是检索向量

语言模型已经获得了包含语义和上下文的隐藏状态,但这些信息分散在不同层和不同位置,主要服务于后续生成。

预测下一个 token 的损失没有要求一句话必须对应一个稳定的固定长度向量,也没有要求同义文本的余弦相似度高、无关文本的相似度低。直接平均某一层的隐藏状态,确实能得到一串固定长度数字,却不能保证这串数字适合检索。

向量模型通常不会从头重新学习语言。它会继承已经完成语言模型预训练的底座,再把整段文本压成固定长度向量,通过文本之间的相关关系重新整理这个空间。

底座的来源并不唯一。BERT 类模型会遮住句子中的一部分 token,再根据两侧内容把它们猜回来;生成式语言模型则预测下一个 token。两条路线都能先获得词法、句法和上下文表示。对比训练随后补上新的约束,让相关文本靠近,让不相关文本远离。

向量模型怎样走完一次对比训练

训练数据先定义什么叫相关

仍用 PDF 密码问题走完一次训练。

查询      怎样给 PDF 设置密码?
相关文档  给 PDF 文件添加打开密码
困难负例  忘记 PDF 密码后如何解除

“相关”不是固定不变的语言真理。“苹果”和“香蕉”在水果分类里很接近,在手机检索里,“苹果”和“华为”可能更接近。问答检索希望问题靠近答案,商品召回希望需求靠近商品,代码搜索希望自然语言靠近代码片段。

训练数据把什么标成正例,向量空间就会优先保存什么关系。

三段文本怎样变成三个固定向量

三段文本可以交给同一个编码器处理,有些模型也会分别处理查询和文档。无论采用哪种结构,每一段文本经过 Transformer 后,都会得到一组 token 隐藏状态。

假设“给 PDF 设置密码”被切成 5 个 token,每个位置都会留下一个隐藏向量。

PDF    → [0.21, -0.14, ...]
设置   → [0.08,  0.37, ...]
密码   → [0.44, -0.09, ...]
……

检索系统需要给整句话建立索引,数量不定的 token 向量必须先压成一条固定长度记录。这个步骤叫池化。模型可以读取特殊位置或最后一个有效位置,也可以对所有位置取平均,或者学习不同位置的权重。

先用三个二维向量演示平均池化。

[1.0, 0.2]
[0.4, 0.8]
[0.7, 0.5]

第一列的三个数平均为 0.7,第二列平均为 0.5,结果是 [0.7, 0.5]。

真实模型会有几百或几千维。池化后还可以经过投影层,把隐藏维度从 4096 变成 1024。L2 归一化会把向量长度调整为 1,方便比较两个向量的方向是否接近。

无论输入是 10 个还是 500 个 token,模型最终都可以输出 1024 维向量。1024 是架构规定的输出尺寸,训练数据不会为每句话准备 1024 个正确数字。

编码和池化完成后,查询、正例与负例各自得到一个向量。

q = 查询向量
p = 正例向量
n = 困难负例向量

相似度怎样变成训练损失

相似度不是“相关概率”,只是按同一套规则得到的比较分数。分数通常越大,表示两个向量的方向越接近。不同模型的分数范围可能不同,同一次比较只需关注候选之间的高低。

训练初期,负例得分可能反而更高。

相似度(q, p) = 0.42
相似度(q, n) = 0.51

训练程序会把正例与负例的分数放在同一组中,再用类似 Softmax 的计算把它们变成候选概率。正例被视为正确答案。正例概率越低,对比损失越大。

这和语言模型的判分方式很接近。语言模型要在词表候选中提高正确 token 的概率,向量模型要在文档候选中提高正确文档的概率。候选对象不同,交叉熵的基本方向没有改变。

反向传播不会直接拖动某个永久保存的句子向量。它会沿着生成向量的过程往回找,判断哪些参数让错误文档得分过高,哪些参数让正确文档得分过低。优化器修改的是编码器、投影层或可训练池化层中的共享参数。下一次遇到新文本时,这些参数会生成新的向量。

经过多轮更新,正例得分会逐渐占优。

相似度(q, p) = 0.83
相似度(q, n) = 0.27

训练没有指定某个 1024 维数组作为标准答案。它要求正例相对负例取得足够明显的优势。有的损失看正例在一批候选中的概率,有的损失要求正负分数至少拉开一段距离。另一次独立训练可能得到完全不同的坐标,只要文本之间的相对关系满足任务要求即可。

一批样本怎样提供更多负例

一个批次可以放入四组查询和相关文档。

q1 → d1
q2 → d2
q3 → d3
q4 → d4

模型让每个查询与每篇文档两两比较,得到一个 4×4 相似度矩阵。

          d1    d2    d3    d4
q1       0.8   0.2   0.1   0.3
q2       0.4   0.9   0.2   0.1
q3       0.1   0.3   0.7   0.4
q4       0.2   0.1   0.3   0.8

对角线是配对正例,其余位置可以直接充当批内负样本。常见的 InfoNCE 损失会提高对角线在每一行中的候选概率,同时压低其他位置。

这种做法节省了额外编码成本,也可能制造假负例。假设 d2 其实也能正确回答 q1,训练程序仍可能把它当作负例推远。数据表里的“没有配对”不等于语义上的“不相关”。多正例、重复文档和近义问题需要额外标记或屏蔽。

困难负样本决定模型能否学到细边界

随机找一篇“怎么养猫”作为“PDF 加密”的负例,模型很快就能区分,继续训练也学不到细边界。

“忘记 PDF 密码后如何解除”包含相同对象和大量相同词,却与“设置密码”方向相反。这样的困难负例会迫使模型区分动作、条件和意图,而不是只看关键词重合。

负样本也不能难到标错。一个同时适用于查询的文档被当成负例,梯度会坚定地把正确内容推远。向量训练的数据质量,很大一部分就藏在正负关系是否可靠。

从通用向量训练到领域微调

语言模型可以从一段长文本中得到许多下一个 token 预测位置,一组检索数据却只提供有限的相关关系。已有语言底座之后,常见做法是继续使用查询、问答、标题—正文、同义改写和代码说明等大规模文本对训练整段表示。这一步常被称为向量模型的通用对比预训练。

进入具体业务后,还可以补一轮高质量领域数据,重新收紧“相关”的边界。这些阶段可能被省略或合并,并不是每个模型都遵循固定三段式。

在保险推品中,疾病名称的不同表达可以保持接近,险种变化却可能改变推荐结果。在代码搜索中,函数作用比变量名是否相同更重要。具体样本与 InfoNCE 的工程实践见 保险推品中的语义匹配与领域微调。

后续训练可以修改全部参数,也可以冻结大部分底座,只训练投影层或少量新增参数。这会影响成本和模型能被调整的幅度,不会改变正负样本所定义的训练方向。

把两条训练链放在一起

训练环节生成式大语言模型向量模型
原始材料网页、书籍、代码等连续文本查询—文档、句子对、标题—正文等关系数据
模型读到的输入一段 token 序列两段或多段 token 序列
Transformer 输出每个位置一个隐藏向量每段文本的多个隐藏向量
任务读取方式每个位置接词表输出层池化成固定长度向量
模型要答的题下一个 token 是什么哪段文本更相关
损失检查什么正确 token 的概率正例在候选文档中的概率或分数优势
反向传播修改什么未冻结的嵌入、Transformer、词表输出层未冻结的编码器、投影层和可训练池化层
直接得到的能力根据上下文继续生成根据向量距离检索或匹配

训练损失下降以后还要验证什么

语言模型在训练集上的损失下降,可能只是记住了重复文本。向量模型的正负样本如果按同一模板生成,再随机分到训练集和评测集,模型也可能靠模板取得漂亮分数。

数据最好按文档来源、时间、用户或近重复簇隔离。语言模型还要观察留出集损失、困惑度、下游任务和实际生成行为。

向量检索通常用几项指标检查真实排序。

  • Recall@K 检查相关文档是否进入前 K 个候选。
  • MRR 关注第一个正确结果排在多前。
  • nDCG 适合存在多个相关等级的结果。
  • 商品推荐、代码搜索和业务问答还要检查最终返回内容是否真的可用。

向量数据库 负责在已有向量中快速查找和过滤。它可以加快检索,却不能修正训练数据里标错的正负关系。训练日志中的损失下降,也只说明模型越来越会做训练题。向量模型是否可用,最后还得拿真实查询去看,正确文档究竟有没有出现在前几个结果里。

参考