05|训练的一步:从正确标签到参数真正改变
本章目录
本章预备知识
训练要先定义“希望预测什么”,才有误差和更新方向。梯度是参数小变化对损失的局部影响;Adam保存过去梯度的统计,模型参数、激活、优化器状态是不同存储对象。
本章各题列出所需基础。可先完成相应基础课,也可通过自检确认掌握程度。
进入深读前的小检查:用x=2、目标4、w=1算预测2、损失2、梯度−4;以步长0.2更新w为1.8,检查误差是否下降。
术语带虚线时可点击速查。公式中的字母先看定义;不要用猜缩写替代推导。
这一章不以“预训练是预测下一个词”结束,而是跟踪一条训练样本怎样形成标签、损失、梯度、优化器状态和新权重。默认讨论自回归Decoder;具体架构和训练配方可改变细节。
沿着对象与状态理解本章
训练的一批数据里,输入和正确后继已经给定,因此可以对多个位置一起计算预测误差,但因果mask仍禁止偷看未来。读训练公式时先分清前向、损失、反向与更新;backward并不意味着参数已经改变。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
训练:给每个位置一道“下一项”题
为什么训练看完整句,生成却只能往前走?
训练时答案放在试卷的评分区,所以各位置的题目可以同时准备;mask负责不让模型读到自己的答案。生成时后续Token还未决定,下一题的前缀依赖本题选择。两者共享因果预测目标,但已知信息不同。
输入与适用条件:标签对齐使用5项序列;单位置交叉熵与PAD平均分别是明确独立的小例,不假称那些概率来自对齐图的模型。
1 / 5 · 把输入和目标错开一格
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
完整教学序列为“我/爱/学习/!/EOS”。输入用前四项,目标用后四项。每个输入位置预测的是其后一个真实Token。
输入=[1,2,3,5];目标=[2,3,5,6]默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。
为什么需要
用清晰的预测任务,把参数更新与语言序列关系连接。
怎样实现
正确移动标签、限制可见性、屏蔽无效目标、按有效数归一化。
可能的代价与误解
偷看未来会产生漂亮但虚假的训练成绩;训练样本重复或泄漏也会高估泛化。
如何判断是否做对
用极小固定序列验证每个输入位置和目标位置,再验证PAD不影响有效损失。
Q017|Next-token训练到底预测什么?标签如何移动,损失又对哪些位置计算?
核心回答
模型在位置i读取不超过i的前缀,输出位置i+1的分布;训练代码把logits与右移一位的真实Token对齐。所有已知前缀能在一次前向中并行处理,因果mask阻止偷看未来。模型最小化真实Token的负对数概率,不是直接比较生成句子和答案的编辑距离。[TR][RLHF]
用五个Token走完对齐
设训练序列为[BOS, 我, 爱, 编程, EOS]。常见实现把整段作为输入,再在计算loss时对齐如下:
| logits位置 | 可见输入 | 应当预测的标签 |
|---|---|---|
| 0 | BOS | 我 |
| 1 | BOS,我 | 爱 |
| 2 | BOS,我,爱 | 编程 |
| 3 | BOS,我,爱,编程 | EOS |
最后位置没有后继标签,通常不计入这一段loss。等价写法是输入直接取前四个Token、标签取后四个Token;不要同时在数据处理和模型内部再移动一次,否则会变成预测后后一个Token。
对位置t,模型得到词表logits z_t,转为概率p_t,损失是 ℓ_t = −log p_t[y_t]。若正确Token概率是0.1,loss约2.3026;若提升到0.8,loss约0.2231。一个batch的loss通常是有效标签位置的平均。这里“有效”由loss mask决定,不等于attention mask。
SFT样本可能是用户问题 + 助手答案。有的配方只对助手答案位置计loss,但答案位置仍可注意用户问题。把用户位置的标签设置为ignore,不等于从输入删除问题。数据代码必须同时正确维护角色边界、结束标记、padding和被忽略标签。[CHAT]
标签移位错误,训练也能看起来很顺利
输入与条件。输入序列为 [BOS,我,爱,学习,EOS]。正确训练将前四个位置与后四个目标对齐。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1正确输入 | [BOS,我,爱,学习] | 每个位置只看自己及以前。 |
| 2正确目标 | [我,爱,学习,EOS] | 所有目标相对输入移动1位。 |
| 3逐格解释 | 输入“我”所在位置预测“爱” | 不是预测当前位置已经知道的“我”。 |
| 4错误对齐 | 把目标也设为 [BOS,我,爱,学习] | 模型可能轻松复制当前输入,损失下降但没学对next-token目标。 |
| 5额外mask | SFT若只训练助手回答 | 用户Prompt可作为输入但不一定参与loss;具体目标策略要明确。 |
把过程连起来。损失能下降,只证明某个目标容易优化;必须先核验目标含义。
边界与反例。不同训练框架可能在模型内部自动移位,调用者不可再移第二次;这里显示数学对齐而非某个API调用约定。
依据与适用范围:[CE]。示例输入和场景由本书明确设定。
追问:为什么训练准确率很好,实际生成仍会跑偏?
训练常用真实前缀,推理使用模型自己的前缀。某一步选错Token后,后续可能进入训练中较少出现的状态;这类训练与生成条件的差异通常称为exposure bias。另一个原因是loss按Token平均,格式、语言流畅性的改善可以掩盖少数高风险事实错误。验证时不能只看训练loss,还要让模型自己生成,并检查完整任务结果。
追问:Perplexity能直接比较两个不同Tokenizer的模型吗?
一般不能直接比较。Perplexity通常是exp(平均Token负对数似然);一个Tokenizer把同一文本拆成更多Token,平均的单位已不同。跨Tokenizer比较需要明确按字节、字符或统一评估设置归一化,同时控制数据集。不同语料上的PPL也不能当成同一尺度的综合能力榜。
追问:把多个样本拼接训练,插入EOS就能隔离信息吗?
EOS是Token,不自动建立计算上的隔离。若只用普通因果mask,后一个样本仍可看到前一个样本。是否允许这种跨样本关注是训练设计;若要求严格独立,要使用分块attention mask或实现支持的边界机制,同时处理position IDs。把“遇到EOS学会结束”与“显式切断注意力”分开。
Q018|Loss算出来后,参数怎样更新?为什么AdamW不只是梯度乘学习率?
核心回答
反向传播按计算图的链式法则,从loss逐层计算参数梯度;优化器用梯度和自身历史状态得到更新量。AdamW维护梯度的一阶、二阶指数移动平均,并把权重衰减与自适应梯度更新解耦。缓存、梯度、优化器状态是不同对象。[ADAMW]
从输出投影一直走回Embedding
设最终隐藏状态为h,输出矩阵为W,z=hW。对softmax交叉熵,∂ℓ/∂z = p−one_hot(y)。如果正确类别概率太低,该类别logit的梯度为负;梯度下降会推动其logit上升。这是概率预测变为参数调整的第一步。
接着∂ℓ/∂W = hᵀ(p−y),∂ℓ/∂h = (p−y)Wᵀ。后一项继续经过最终归一化、每一层FFN、Attention、残差支路,最后到Embedding。残差分支的梯度要相加,共享参数被多处使用时梯度也要累加。自动求导做的是这些局部导数的组合,不是在搜索“哪个神经元知道这条事实”。
AdamW的一种常见写法为:
m_t = β1 m_(t−1) + (1−β1) g_t
v_t = β2 v_(t−1) + (1−β2) g_t²
m_hat = m_t / (1−β1^t)
v_hat = v_t / (1−β2^t)
θ_t = θ_(t−1) − η m_hat/(sqrt(v_hat)+ε) − η λ θ_(t−1)
平方、除法是逐元素操作。m平滑梯度方向,v估计梯度平方的尺度,偏差修正处理初始状态为零造成的偏差。权重衰减项没有先混入g再被v缩放,因此不同于一般情形下“把L2惩罚加进Adam的loss”。哪些参数衰减、ε放在哪里、混合精度如何维护主权重,要看具体实现。
AdamW的第一步,把动量、偏差修正与衰减都算出来
输入与条件。初始w=1,梯度g=0.2;m0=v0=0;β1=0.9、β2=0.999;学习率0.1,weight_decay=0.01,ε=1e−8。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1更新一阶矩 | m=0.9×0+0.1×0.2 | 0.02。 |
| 2更新二阶矩 | v=0.999×0+0.001×0.2² | 0.00004。 |
| 3偏差修正 | m̂=m/(1−0.9),v̂=v/(1−0.999) | 分别0.2与0.04。 |
| 4解耦权重衰减 | w先乘(1−lr×wd) | 从1变0.999。 |
| 5归一化梯度更新 | 再减0.1×0.2/(√0.04+1e−8) | 新w约0.899000005。 |
| 6与SGD对照 | 只做w−lr×g | 会得0.98,不是同一个更新规则。 |
把过程连起来。AdamW保存历史状态并做归一化与偏差修正;不能只把它描述为“梯度乘学习率”。
边界与反例。本例是第一步标量更新,无梯度裁剪、混合精度或调度器;实际实现参数组可有不同衰减。
依据与适用范围:[ADAMW]。示例输入和场景由本书明确设定。
追问:梯度清零为什么重要?
很多训练框架默认累积.grad。这方便梯度累积,但普通一步一更新若忘记清零,会把前几步的梯度无意叠加。反过来,在每个micro-batch前清零又会破坏有意的累积。正确的位置通常是一个有效优化步开始前或完成后,而不是机械放在所有循环的第一行。
追问:梯度裁剪改变了什么?为什么不能修复全部NaN?
全局范数裁剪会在梯度范数超过阈值时,按比例缩小整个梯度向量,限制极大更新。它不修复已发生的非法前向数值、全mask softmax、除零或损坏数据。混合精度下还要在适当时机解除loss scaling后再裁剪,否则阈值单位不对。出现NaN时应定位最早的非法张量,而不是只减小学习率碰运气。
追问:恢复训练只加载权重够不够?
继续同一条训练轨迹通常还要恢复优化器m/v、学习率调度器、步数、数据采样位置及随机状态;分布式训练还涉及分片与全局进度。只加载权重可以启动新的优化过程,但不等于无缝续训。推理部署一般不需要优化器状态,这就是训练checkpoint与推理权重包体积可能显著不同的原因。
Q019|预训练数据进入模型前经历什么?为什么数据质量会变成系统问题?
核心回答
训练不是“把互联网文本喂进去”这么简单。数据获取、许可和隐私处理、清洗、去重、混合采样、Tokenization、分片及训练/验证划分共同决定模型看到的分布。统计量要在处理链的多个阶段观察,不能只记录最终总Token数。[CHINCHILLA]
一份文档的完整生命周期
先保存原始来源和采集版本,提取正文;HTML导航、重复页脚、乱码和机器生成垃圾会让梯度反复学习无意义模式。随后进行语言、格式、质量及敏感数据处理。过滤器也会引入选择偏差:过于偏爱某种文风,可能误删专业论坛、口语或稀有语言。
去重至少要区分精确重复、近重复和同源改写。精确hash只能识别相同字节;近重复方法通常基于片段特征或相似性,再配合阈值。阈值过宽会丢掉有价值的独立样本,过窄会保留成批模板。去重不仅节约算力,也影响记忆性与评测污染风险。
接下来按数据源、语言、代码和难度设置采样混合,转换Token并装入训练序列。一个来源原始体积大,不意味着它应有相同训练权重;重复采样会让实际训练分布不同于硬盘上的分布。应记录“每类数据在训练中实际消耗了多少Token”,而不只是下载了多少。
评测集应在可能共享来源的层面隔离。把同一仓库相邻函数随机拆到训练和测试,可能让结果主要反映见过的项目;把某道题的不同网站转载分别放两边,仍可能泄漏。对代码Agent,还应区分模型训练污染、检索库泄漏和测试本身暴露,这三条路径并不相同。
重复样本怎样把一次评测变成“背过答案”
输入与条件。资料有文档A、A的空白格式变体A′、独立文档B。先随机逐文件切分,把A放训练、A′放测试。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1原始切分 | 文件名不同 | 看似训练和测试没有同名文件。 |
| 2规范化比较 | 去掉无意义空白后A与A′相同 | 测试已泄露训练内容。 |
| 3按重复簇切分 | 把A/A′作为一组 | 整组只进入一侧,B独立分配。 |
| 4记录处理链 | 原文身份、清洗策略、去重簇、分区 | 后续可追溯数据为何进入训练。 |
| 5评价泛化 | 使用真正未见任务 | 不能用重复样本上的低loss证明新问题能力。 |
把过程连起来。数据工程影响模型能力,也影响对能力的判断;去重不是只为节约磁盘。
边界与反例。语义近重复需要更细策略;去空白只用于这个明确构造的重复例子,不适合所有代码/文本。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:为什么更多数据可能反而降低某项能力?
训练目标优化的是混合分布上的期望。新增数据改变了比例,可能稀释原有高质量任务,也可能引入矛盾、噪声或风格偏移。总loss下降不保证每个任务子集改善,因此需要分语言、任务和风险类别的验证集。数据规模与质量不能用单一数字相互替代。
追问:知识截止日期是数据库里的统一时间戳吗?
通常不是。不同来源被收集和混合的时间不同,训练后还可能进行后训练;模型也可能从工具得到更近事实。对某件事是否知道不能仅靠一个“截止日期”推断。工程系统应把实时事实交给有来源、可更新的检索,而不是依赖模型口头承诺“我知道最新”。
追问:训练数据去重与Agent记忆去重可以用同一套办法吗?
目的不同。语料去重主要控制样本分布和污染;记忆去重还要保留事实适用范围、时间、证据和否定关系。“接口需要重试”与“此接口不能自动重试”词面接近却不能合并。可以复用相似度作为候选发现,但不能直接用它裁决知识等价。
Q020|训练为什么比推理更吃显存?梯度累积、Checkpoint和分片各节省什么?
核心回答
训练除了参数,还要保存梯度、优化器状态和用于反向的激活;推理通常没有这些对象,但长上下文需要KV缓存。不同优化技术针对不同对象,不能把“节省显存”当作同一件事。
从对象清单估算,而不是背一个固定倍数
以一个教学配置为例:低精度参数2字节、低精度梯度2字节、FP32主权重4字节、Adam两个FP32状态8字节,共约16字节/参数,尚不含激活、临时缓冲和通信。某些实现没有独立主权重、梯度为FP32或优化器量化,这个系数就会变。估算时先列对象及dtype,再乘参数量。
激活占用依赖batch、序列长度、层数和内核。标准attention如果显式保存[B,H,T,T]会非常昂贵;更省内存的内核避免物化它。Activation checkpointing则不保存部分中间值,反向时重新前向计算,用算力换显存。它与“保存模型checkpoint文件”不是同一个概念。[FLASH]
梯度累积把大batch切为K个micro-batch,连续反向但延后更新。若每个micro-batch包含相同有效Token数,loss除以K通常能得到对应平均梯度;有效Token数不等时,机械平均各micro-batch平均loss会改变样本权重。应按有效Token总量归一化。Dropout随机性、浮点累加顺序等也会使数值不完全一致。
参数/梯度/优化器分片解决跨设备的冗余;tensor parallel切分层内矩阵计算;pipeline parallel按层拆分;data parallel处理不同样本后同步梯度。它们可组合,但通信与调度成本不同。第08章继续拆解推理侧的并行。
训练显存:同样100万个参数,额外状态从哪里来
输入与条件。假设全部使用FP32:100万参数、对应梯度、一阶矩、二阶矩各一份;每个数4字节。
| 步骤与对象 | 内部动作 | 中间结果 / 新状态 |
|---|---|---|
| 1参数 | 1,000,000×4 | 4MB。 |
| 2梯度 | 同样大小 | 再4MB。 |
| 3Adam状态 | m与v各4MB | 再8MB,四类合计16MB。 |
| 4激活 | 反向还需要中间张量或重算材料 | 其占用取决于batch、长度、层数,尚未包含在16MB中。 |
| 5梯度累积 | 小批逐次算、累积梯度再更新 | 减少单次激活峰值,不删除参数和优化器状态。 |
| 6Checkpoint/分片 | 前者少存激活多重算;后者分摊状态 | 代价分别是计算或通信,不能都叫免费省显存。 |
把过程连起来。先列对象再算大小,才能判断某项优化究竟减少哪一部分。
边界与反例。按十进制MB;真实混合精度、主权重副本、ZeRO实现和工作区会改变账本。
依据与适用范围:[DERIVE]。示例输入和场景由本书明确设定。
追问:梯度累积能降低单条长序列的峰值显存吗?
它降低micro-batch数量造成的峰值,但一条序列本身的激活仍要处理。如果batch已经是1,累积更多次不会缩短这条序列。需要更省内存的attention、checkpointing、序列并行、分片或缩短序列等针对性措施。
追问:量化到4bit是否意味着训练所有内存都变为四分之一?
不是。量化通常主要改变底座权重存储,反向激活、适配器、优化器、量化scale和临时反量化计算仍占内存。QLoRA冻结量化底座、训练低秩适配器,因此不是任意4bit权重都可以直接全参数更新。[QLORA]
追问:训练学习率越小越安全吗?
小学习率减少单步变化,但也可能在预算内学不动;大batch、累积、warmup和数据变化会改变合适尺度。工程上看梯度范数、更新/权重比例、分任务验证损失和数值异常。训练不稳定不是靠单一“保守参数”就能消除。
本章分布式训练与并行部分的延伸依据:PyTorch张量并行说明与ZeRO原始论文。[TP][ZERO]