语言模型究竟在预测什么:标签错位、交叉熵与训练损失

成长演进

具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。

ℹ数字花园

🌿 什么是数字花园与思想演进?

遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:

  • 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
  • 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
  • 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
  • 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
浏览全站数字花园与笔记全景 →

训练脚本开始运行,屏幕上不断出现 loss。它从 8 降到 5,再降到 3。看到这条曲线,我们很容易觉得:模型正在学会语言。

但先别急着下结论。如果模型看到了本该预测的答案,或者代码把目标错移了一个位置,损失一样可能下降。损失下降只说明模型正在更好地完成代码实际定义的任务。这个任务是否正确,需要我们先检查。

上一篇解释了文本怎样变成 token。本篇接着追踪这串整数:模型看见什么,要预测什么,以及错了以后怎样计分。

这里讨论最基本的自回归语言建模:每个位置根据此前的内容预测下一个 token。暂不加入标签平滑、类别权重和其他辅助损失。

把一句话展开成几个预测问题#

我们自定义一条教学序列:

[BOS] 今天 下雨 [EOS]

[BOS] 表示开始,[EOS] 表示结束;中间两个片段假设各占一个 token。这是为了看清位置关系,不代表所有模型都使用这些标记或这种切分。

它对应三个问题:

当前已经看见正确的下一个 token
[BOS]今天
[BOS] 今天下雨
[BOS] 今天 下雨[EOS]

第二个问题有多种合理续写。“今天晴天”也说得通,但这条训练记录实际出现的是“下雨”,所以本次用“下雨”作为目标。其他文本可以提供其他续写的训练机会。

语言模型并不是给“这句话是否符合事实”打标签。它学习的是训练数据中的条件分布:在某种上下文后,哪些 token 更可能出现。因此,文本中的错误、偏见或重复,也可能成为容易学习的规律。

为什么整段一起输入,却不算提前看答案#

训练时可以把整条序列放进一个张量,同时计算多个位置的输出。关键在于每个位置能读取哪些信息。

在通常的因果注意力约束下,位置 tt 可以读取自己和更早的位置,不能读取未来。Transformer 原论文也说明了用遮罩阻止解码位置访问后续位置的设计。Attention Is All You Need,第 3.1 节

于是 今天 所在位置虽然与 下雨 一起出现在输入张量里,它的计算仍不能使用后者。

用矩阵表示这种可见关系:

计算哪个位置BOS今天下雨EOS
BOS可见不可见不可见不可见
今天可见可见不可见不可见
下雨可见可见可见不可见
EOS可见可见可见可见

所以可以同时计算前三个预测题,而不是训练时也逐字生成。最后一个 EOS 位置在这个独立例子中没有后续目标,可以不计损失。

这还有一个常被混淆的地方:训练通常提供真实前缀,而不是把模型刚才猜错的 token 接回去。计算“下雨”这个目标时,上文是数据里的“今天”。模型即使在第一个位置最想输出别的词,也不会自动改写这条训练输入。

生成时情况不同:下一步通常接着模型自己生成的内容计算。它前面犯的错会进入后面的上下文。因此,真实前缀下的预测损失与连续生成的表现有关,却不是同一项测量。

标签只应该错位一次#

如果序列的编号是:

[BOS, 今天, 下雨, EOS] = [0, 1, 2, 3]

最直观的手写训练方式是:

输入 x: [0, 1, 2]
标签 y: [1, 2, 3]

每个输入位置的输出与对应标签比较。注意,标签是类别编号,不是要把整数做减法。把“下雨”的编号从 2 换成 200,只要所有映射一致,不会让它离“今天”更远。

一些封装会在模型内部完成错位。例如 Transformers 4.57.1 的 GPT-2 文档允许传入与 input_ids 相同的 labels,因为内部负责把预测位置与下一个标签对齐。这里引用的是这个固定版本的接口约定,不能据此推断所有模型和训练器都相同。GPT-2 标签接口

两种约定都可以。真正危险的是混着用:

情况位置“今天”的输出实际上被要求预测什么
正确错位一次下雨
完全没有错位,又与同位置标签比较今天
外部先错位,内部又错位可能变成 EOS,取决于切片实现

没有错位时,当前位置已经看见自己的输入,任务可能退化为复制。错位两次则可能跳过一个 token。它们不一定立即报错,甚至都可能产生下降的曲线。

最有效的检查不是盯着框架名称,而是打印一条样本实际参与计分的配对:这一行输出能看见的最后一个 token 是什么,标签又是什么? 连续检查三个位置,通常比先跑几千步更有用。

模型怎样给候选 token 分配概率#

模型在每个位置输出一组分数,通常叫 logits。词表有多少个条目,就有多少个候选分数。这些分数可以为负,也不要求加起来等于一。

softmax 把它们转成概率。对当前上下文,候选 jj 的概率为:

pj=exp⁡(zj)∑k=1Vexp⁡(zk)p_j=\frac{\exp(z_j)}{\sum_{k=1}^{V}\exp(z_k)}

zjz_j 是候选分数,VV 是词表大小。分数本身没有绝对刻度:给所有分数加同一个常数,概率不会变。

假设只有三个候选“下雨”“晴天”“结束”,它们的分数分别为 log⁡4\log4、log⁡2\log2、log⁡2\log2。指数化后是 4、2、2,除以总和 8,就得到 0.5、0.25、0.25。

这只是一个可手算的局部示例。真实模型的候选数往往多得多,但计分逻辑相同。

交叉熵为什么惩罚“很有把握地答错”#

这条样本的目标是“下雨”,所以取模型分给它的概率,再计算负对数:

ℓ=−log⁡p下雨\ell=-\log p_{\text{下雨}}

这里使用自然对数。正确目标的概率越小,损失越大:

分给正确目标的概率损失,约值
0.80.2231
0.50.6931
0.12.3026
0.014.6052

因此,损失不只判断“排名第一的候选对不对”。两个模型都把“下雨”排第一,一个给 0.8,另一个给 0.5,它们的损失仍不同。

这个目标也不会直接要求每条样本的概率都等于一。当多个样本具有相同上下文却有不同后续时,模型需要在这些观察之间分配概率。容量、数据和优化过程共同决定最终能接近什么分布。

在无标签平滑的单一类别目标下,softmax 交叉熵对分数的导数可以写成:

∂ℓ∂zj=pj−1[j=y]\frac{\partial\ell}{\partial z_j}=p_j-\mathbf{1}[j=y]

yy 是正确目标编号。前面的三个候选分别得到 −0.5,0.25,0.25-0.5, 0.25, 0.25。若只把这些分数当作可直接调整的变量,沿负梯度走,会提高正确候选分数、压低其他候选分数。

真实网络更新的是共享参数,不是为每条样本保存一套独立分数。因此,一次更新也会影响其他上下文;这正是泛化和相互干扰都可能发生的原因。

实现时,PyTorch 的 CrossEntropyLoss 接收未归一化的 logits,并在内部实现相应的计算。不要先把它们变成概率,再当作 logits 传进去,否则定义的就不是上面的目标。其类别编号、忽略位置和平均方式都应按具体接口检查。PyTorch CrossEntropyLoss

从一个位置的损失,到一个 batch 的损失#

一句话有多个目标,一个 batch 又有多句话。我们需要说明哪些位置参与训练,以及怎样求平均。

设位置 ii 的目标损失是 ℓi\ell_i,参与计分时 mi=1m_i=1,否则为零。在没有额外权重时,可以写成:

L=∑imiℓi∑imiL=\frac{\sum_i m_i\ell_i}{\sum_i m_i}

分母是有效目标数,不是输入张量的全部格子数。padding 等位置不应仅仅因为占了格子就稀释平均值。如果整批没有有效目标,分母为零,需要数据检查或明确跳过,不能把它算成“完美的零损失”。

这里还要区分两种平均:

  • 按 token 平均:所有有效位置合在一起除以总数。
  • 按样本平均:先求每个样本的平均,再给样本相同权重。

假设 A 有两个有效位置,平均损失为 1;B 有八个,平均损失为 3。按 token 平均是 (2×1+8×3)/10=2.6(2\times1+8\times3)/10=2.6;按样本平均是 (1+3)/2=2(1+3)/2=2。

两者没有算术上的对错,但定义了不同的权重。更长的样本在前一种目标里贡献更多位置。训练日志若只显示一个数字,而不说明怎样平均,就很难比较实验。

同样道理,多个验证 batch 长度不同,不能简单平均它们的平均损失。应累加有效目标的损失总和,再除以有效目标总数。梯度累积也会遇到类似问题,后面讨论 batch 时再展开。

loss mask 不负责挡住答案#

这里先记住一个区别,后续 SFT 会反复用到:

attention mask 决定信息能从哪里传过来,loss mask 决定哪些预测位置要计分。

把某个标签设为忽略值,只是不直接计算那个目标的损失,不意味着模型看不见相应的输入。比如做问答 SFT 时,可以让问题参与上下文,但只计算回答的损失。

相反,如果未来答案仍然能通过注意力被读取,单靠忽略某些标签不能消除这种泄漏。检查因果约束和检查计分范围,是两件都要做的事。

常见接口用 -100 代表忽略标签,但它不是词表里的普通 token,也不应该当作输入编号送入嵌入层。是否使用这个值,仍要以实际损失函数的约定为准。PyTorch 忽略标签说明

困惑度可以怎样理解#

在同一套 token 定义和计分范围下,平均负对数损失取指数,就得到常用的困惑度:

PPL=exp⁡(L)\mathrm{PPL}=\exp(L)

如果模型在每个位置都给正确目标 0.5 的概率,那么 L=log⁡2L=\log2,困惑度就是 2。若每个位置都是 0.25,则为 4。

可以把它理解为正确目标概率的几何平均数的倒数,而不是“每次恰好剩两个候选”。不同位置的分布可以差异很大,相同困惑度也不要求候选集合大小相同。

更重要的是,PPL 的单位跟着 token 走。不同分词器、不同语料、不同截断和上下文策略,会改变预测题目本身。不能把一个中文模型的 per-token PPL 与另一个采用不同切分的模型直接排成能力榜。

同一个模型验证损失下降,也还需要检查分项。假设验证集大部分是模板句,模型把模板预测得更准,就能拉低整体损失;稀少但重要的技术问答可能没有改善。

用一个小检查确认代码真的在学下一个 token#

在正式训练前,可以先做下面的对齐实验。它是验证设计,不是本文宣称已经训练出了一个模型。

准备两三条极短序列,并固定分词结果,避免同时排查分词器变化。逐位置保存能看见的前缀、目标 ID、目标概率、是否计分和该位置损失。

先验证三件事:

  1. 人工配对吻合。 比如前缀到“今天”为止,目标确实是“下雨”。
  2. 手算吻合。 使用自造 logits,与独立的负对数计算比较,检查数值与平均方式。
  3. 信息边界吻合。 在评估模式下只改未来 token,检查更早位置的输出是否保持一致;比较时关闭随机性,并允许合理的数值误差。

第三项验证的是因果计算边界。通过它仍不等于整套数据流程正确,例如训练集和验证集可能重复,因此它不能替代第一卷的数据检查。

接下来才做极小样本过拟合:让模型反复看到这几条记录,观察能否明显降低损失。若不能,检查参数是否真的更新、学习率是否合理、梯度是否被意外切断。若能,也只能证明这套配置能拟合小样本,不代表有泛化能力。

正式比较时,再保留未见文本,固定分词、上下文和计分范围,同时报告训练损失、验证损失以及分任务生成表现。这样,曲线变化才有可解释的含义。

两个检查理解的问题#

第一,一条输入是 [BOS, 今天, 下雨, EOS]。若模型内部已经错位,外部还把标签改为 [今天, 下雨, EOS, 忽略],前两个输出分别会被要求预测什么?请根据内部切片约定列出来,不只回答“会错位两次”。

第二,验证 batch A 有十个有效目标,平均损失为 1;batch B 有九十个,平均损失为 3。总体 token 平均损失是多少?为什么直接取两个日志数字的平均值会改变权重?

弄清预测题目以后,下一步才是拆开模型内部的计算:输入编号如何变成向量,向量怎样读取上下文,又如何得到词表上的分数。

文章概念网络

从章节进入正文概念。实线表示所属层级,虚线连接跨章节重复出现的概念,不代表因果关系。

71 / 71 个节点 · 正文标记术语与关键词概念

语言模型究竟在预测什么:标签错位、交叉熵与训练损失同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept语言模型究竟在预测什么:标签错位、交叉熵与训练损失语言模型究竟在预测什么:标签错位、交叉熵与训练损失lossloss文本怎样变成 token文本怎样变成 token把一句话展开成几个预测问题把一句话展开成几个预测问题[BOS][BOS][EOS][EOS][BOS] 今天[BOS] 今天[BOS] 今天 下雨[BOS] 今天 下雨为什么整段一起输入,却不算提前看答案为什么整段一起输入,却不算提前看答案Attention Is All You Need,第 3.1 节Attention Is All You Need,第 3.1 节今天今天下雨下雨标签只应该错位一次标签只应该错位一次inputidsinputidslabelslabelsGPT-2 标签接口GPT-2 标签接口这一行输出能看见的最后一个 token 是什么,标签又是什么这一行输出能看见的最后一个 token 是什么,标签又是什么模型怎样给候选 token 分配概率模型怎样给候选 token 分配概率交叉熵为什么惩罚“很有把握地答错”交叉熵为什么惩罚“很有把握地答错”CrossEntropyLossCrossEntropyLossPyTorch CrossEntropyLossPyTorch CrossEntropyLoss从一个位置的损失,到一个 batch 的损失从一个位置的损失,到一个 batch 的损失按 token 平均按 token 平均按样本平均按样本平均loss mask 不负责挡住答案loss mask 不负责挡住答案-100-100PyTorch 忽略标签说明PyTorch 忽略标签说明困惑度可以怎样理解困惑度可以怎样理解用一个小检查确认代码真的在学下一个 token用一个小检查确认代码真的在学下一个 token人工配对吻合人工配对吻合手算吻合手算吻合信息边界吻合信息边界吻合两个检查理解的问题两个检查理解的问题[BOS, 今天, 下雨, EOS][BOS, 今天, 下雨, EOS][今天, 下雨, EOS, 忽略][今天, 下雨, EOS, 忽略]拆开模型内部的计算拆开模型内部的计算tokentokentokentokenEOSEOSEOSEOSTransformerTransformerAttentionAttentiontokentoken注意力注意力因果因果tokentokenEOSEOSPyTorchPyTorch归一化归一化优化优化tokentoken实验实验SFTSFTAttentionAttentionPyTorchPyTorchtokentoken损失函数损失函数注意力注意力tokentoken验证集验证集分词分词策略策略技术技术分词分词tokentoken验证集验证集训练集训练集评估评估实验实验EOSEOStokentoken
语言模型究竟在预测什么:标签错位、交叉熵与训练损失

语言模型究竟在预测什么:标签错位、交叉熵与训练损失

回到原文 ↗
BIBTEX

引用本文

@article{2026-10-20-llm-training-pretrain-02-objective,
  author  = {枫语},
  title   = {语言模型究竟在预测什么:标签错位、交叉熵与训练损失},
  journal = {枫语},
  year    = {2026},
  month   = {oct},
  url     = {https://meiluosi.github.io/posts/2026-10-20-llm-training-pretrain-02-objective/}
}
🌐

本篇知识星丛与滑动侧栈

语言模型究竟在预测什么:标签错位、交叉熵与训练损失 当前主轴

搜索

正在载入索引…