bf16精度陷阱:你的微调可能只训练了部分权重

bf16精度下小学习率导致大权重更新被舍入为零,多篇高引论文中招。
文章揭示了一个被忽视的bf16精度问题:当使用bf16存储权重并直接微调时,学习率较小时优化器计算的更新量相对于大权重太小,无法在bf16中表示,导致这些更新被舍入为零,结果只有小权重被训练,形成“稀疏微调器”。作者发现至少半打高引论文(如Quiet-STaR)采用了这种微调方式但未提及该行为,复现显示高精度优化器需要3-10倍更小的学习率才能匹配结果。跨源验证表明,交叉熵的舍入bug与Adam作用导致grokking中的损失尖峰,修复后尖峰消失。两个案例共同说明训练动态中的某些现象可能只是数值精度造成的假象。实践者应检查权重更新分布、考虑混合精度策略、调整学习率并审慎看待复现结果。
一个被忽视的bf16行为:小权重更新,大权重冻结
如果你用bf16精度存储模型权重,并直接用torch.optim.AdamW进行微调,可能会遇到一个奇怪的现象:只有数值较小的权重在更新,而较大的权重几乎不变。
这不是bug,而是bf16的表示精度导致的必然结果。bf16(bfloat16)只有8位指数和7位尾数,这意味着它只能表示权重变化中至少约1/512的部分。当学习率较小时,优化器计算出的更新量相对于大权重来说太小,无法在bf16中表示,于是这些更新被直接舍入为零。
结果就是:你得到了一个稀疏微调器——它只训练小幅度权重,冻结其余部分,而截断点由你的学习率决定。

bf16(bfloat16)是一种16位浮点格式,由Google Brain提出,用于加速深度学习训练。它包含1个符号位、8个指数位和7个尾数位,与fp32的指数位相同(因此动态范围一致),但尾数位大幅缩减。fp32有23位尾数,能表示约7位十进制有效数字;bf16只有7位尾数,仅能表示约2-3位十进制有效数字。这意味着bf16的相对精度约为1/128(2^{-7}),而fp32约为1/8百万(2^{-23})。当两个数相加时,如果较小加数小于较大加数的1/256(考虑舍入),则结果可能不变,这称为“吸收”或“舍入到零”。在权重更新场景中,若更新量相对于权重量级过小,bf16存储会将其舍入掉,导致权重无法更新。这种现象在混合精度训练中常见,但通常优化器使用fp32主权重来避免。直接使用bf16权重和优化器状态则会放大此问题。
问题有多普遍?多篇高引论文中招
这个问题并非理论推测。原文作者调查发现,至少有半打经过同行评审、引用数千次的论文采用了这种微调方式,但没有一篇提及这一行为。
作者深入分析了其中一篇名为Quiet-STaR的论文,原因有三:论文本身很有趣、作者开源了权重、代码可以正常运行。通过对比基础模型和微调后的模型,作者确认:确实只有小幅度权重在训练中被更新。
随后,作者用能够应用每一次更新的优化器重新运行了Quiet-STaR的训练配方,结果模型性能出现发散。要获得相同的结果,高精度优化器需要将学习率降低3到10倍,而且即便如此,也无法达到更好的效果。
Quiet-STaR是Eric Zelikman等人于2024年提出的一种训练语言模型进行内部推理的方法。它扩展了STaR(Self-Taught Reasoner)框架,让模型在生成每个token之前生成多个“思考”步骤,并通过强化学习或拒绝采样来优化这些思考,从而提升模型在复杂推理任务上的表现。Quiet-STaR的关键创新在于将思考过程无缝集成到标准语言模型训练中,无需额外监督。该论文因其新颖性和潜在影响而受到关注,但原文作者发现其开源代码中使用了bf16权重和AdamW优化器直接微调,可能无意中触发了权重冻结现象。这提示即使是前沿研究,也可能忽视数值精度带来的训练动态偏差。
跨源验证:从Softmax修复到训练动态的重新理解
这一发现并非孤立。据Barron、Gnanapragasam与Tschannen的论文《A Fix for Softmax and Cross-Entropy》(2026年10月),标准交叉熵计算方式会在模型足够自信时(bf16下margin约5.5,他们称之为“精度悬崖”),将正确答案的梯度舍入为精确的零。这一舍入效应与Adam优化器相互作用,正是grokking过程中出现的“弹弓”式损失尖峰的成因。修复舍入问题后,尖峰消失。
两个案例方向相反:交叉熵的舍入bug给训练增加了不稳定性,而bf16权重更新的舍入则隐藏了不稳定性。它们共同揭示了一个事实:我们习以为常的训练动态,有时只是数值精度造成的假象。
Grokking是机器学习中一种现象,指模型在训练损失早已收敛后,验证准确率突然大幅提升。它通常出现在小规模算法任务(如模运算)上,训练数千步后测试性能跃升。Grokking的机制尚存争议,但普遍认为与优化动态、正则化和表示学习有关。原文提到的“弹弓”式损失尖峰是指在grokking过程中损失曲线出现的短暂剧烈波动,随后模型泛化能力突变。Barron等人的工作表明,标准交叉熵在模型高置信时(bf16下logit margin约5.5)会将正确类别的梯度舍入为零,与Adam优化器交互产生不稳定尖峰。修复舍入后尖峰消失,说明grokking的部分动态可能源于数值精度而非纯粹的学习机制。这强调了在分析训练现象时需区分真实学习信号与数值伪影。
对实践者的启示
如果你正在用bf16进行微调,以下几点值得注意:
- 检查权重更新分布:对比基础模型和微调后的权重差异,确认是否只有小权重发生了变化。
- 考虑混合精度策略:优化器状态和权重更新可以使用fp32,避免舍入损失。
- 调整学习率:如果必须使用bf16,可能需要更小的学习率来匹配全精度优化的效果。
- 审慎看待复现结果:你复现的论文可能因为精度问题而偏离了原始意图。
作者将完整分析写成了一篇长文,代码、日志和评估结果均已在GitHub上开源(仓库:mweiss17/quietstar-bf16-adamw)。对于从事微调工作的研究者和工程师来说,这是一个值得仔细检查的精度陷阱。
相关推荐

MiniMax H3开源工作流:低显存也能打造呼吸感皮肤
本文介绍B站UP主分享的MiniMax H3双采样工作流,通过融合模型、破线LoRA和低显存节点,解决AI人物面部油腻问题,并让12G显存设备也能运行。包含模型搭配、双采样逻辑与参数设置详解。

手机本地大模型+ESP32:自制热量提醒器
B站UP主开源了一个热量摄入提醒器项目:手机APP用miniCPM开源模型本地拍照识别食物并估算热量,ESP32小屏幕同步数据,用绿黄红圆环展示摄入状态,可磁吸在冰箱门上随时提醒。

12GB显卡跑125B MoE?Strata三级协同引擎解析
开源C++引擎Strata让12GB-16GB家用显卡本地跑125B MoE模型,生成速度达40-95 Token/s。本文拆解其显存、内存、固态三级协同原理,说明模型选型前提、硬件配置门槛与启动方式。