快速结论:当你在加载模型之后再用 model.to(torch.bfloat16) / .half() / .bfloat16() 或 Trainer(bf16_full_eval=True) 做全量评估时,RoPE 的 inv_freq 等频率 buffer 会被一起降精度,导致长上下文下角度误差迅速放大;优先排查加载后是否发生了 dtype 转换。
适用环境:transformers main @ 4692303,也复现于 5.18.0;torch 2.11;CPU 环境(Issue 中所有测量均在 CPU 上完成)。
最快修复方案:暂无确认的一步修复方案。Issue 中给出的 PreTrainedModel._apply 原型改动属于提议方案,报告者自测对 Llama 类模型和语音模型有效,但尚未被合入,需要以实际版本验证。
注意事项:不要把它理解成“bf16 评估不可用”。from_pretrained(dtype=torch.bfloat16) 路径会保留 inv_freq 为 fp32,误差仅来自返回的 cos/sin 量化;问题只出现在加载后再 .to() 的路径。对语音模型(如 Wav2Vec2Conformer)还会出现位置值塌缩,影响更早出现。
问题场景
用户在 Transformers 中加载模型后,通过 model.to(torch.bfloat16)、model.bfloat16()、model.half() 等方式手动转换精度;或在使用 Trainer 并设置 TrainingArguments(bf16_full_eval=True) 时,Trainer 内部会对模型执行 model.to(dtype=torch.bfloat16)。此时如果模型使用 RoPE(旋转位置编码),其频率 buffer 会被一并转换,造成长上下文位置角度与 fp32 参考值出现明显偏差。
受影响的模型不限于 Llama 系。Issue 中额外点名了 Wav2Vec2Conformer、SeamlessM4T、Wav2Vec2Bert、Clvp,以及构建实数角度输入的 MusicFlamingo。这些模型在 .to(torch.bfloat16) 后,旋转位置会在 10 秒音频附近就开始塌缩。
报错原文
model.to(bf16)/half() and Trainer bf16_full_eval cast RoPE inv_freq buffers, unlike from_pretrained(dtype=...): large angle error at long context
torch.float32 torch.bfloat16
from_pretrained(dtype=bf16) angle error vs fp32: mean 0.0006 rad, 0.0% of angles off by > 0.5 rad
model.to(bf16) angle error vs fp32: mean 0.6733 rad, 36.1% of angles off by > 0.5 rad
10s: 121/499 positions collapsed, max angle error 1.00 rad, 11.5% of angles off by > 0.1 rad
30s: 927/1499 positions collapsed, max angle error 3.00 rad, 28.2% of angles off by > 0.1 rad
原因分析
核心原因是 model.to(dtype) 会把模块中所有浮点 buffer 一起转换,包括 RoPE 使用的 inv_freq、original_inv_freq 以及各个 layer type 对应的 *_inv_freq。而 from_pretrained(dtype=torch.bfloat16) 在加载阶段只把模型参数转换为 bf16,这些频率 buffer 仍保持 fp32,因此两条常见路径得到的 RoPE 角度并不一致。
RoPE 的 forward 里虽然存在 .float() 与 maybe_autocast(enabled=False) 保护,但它们只能避免 autocast 期间的精度损失。一旦 buffer 本身已经被舍入到 bf16,后续再调用 .float() 也无法恢复已经丢失的精度。误差会随位置索引增大而累积,在 32767 位置附近,bf16 量化带来的累计漂移可达约 5.85 rad。
对语音模型而言,问题更严重:Wav2Vec2Conformer、SeamlessM4T、Wav2Vec2Bert、Clvp 会以 buffer 的 dtype 构建旋转位置(time_stamps = torch.arange(sequence_length).type_as(self.inv_freq))。当 inv_freq 被降为 bf16 时,原本不同的时间帧位置会舍入到同一个值,导致位置塌缩。这也是同一个 .to() 精度问题在语音模型上的放大表现。
Issue 中还引用了 timm 的同类处理方式(huggingface/pytorch-image-models#2797):_RotaryEmbeddingBase._apply 在频率 buffer 被降精度时会将其重建为 fp32,理由是频率会先乘以可能很大的位置数,再进入 sin/cos,低精度量化会与位置放大效应叠加。
环境排查
- 确认 Transformers 版本:Issue 在
main@ 4692303 复现,也在 5.18.0 复现。 - 确认 PyTorch 版本:Issue 使用 torch 2.11。
- 确认运行设备:Issue 中所有测量均在 CPU 上完成。
- 确认模型加载与精度转换顺序:是先
from_pretrained(..., dtype=torch.bfloat16),还是先加载再用model.to(torch.bfloat16)/.half()/.bfloat16()转换。 - 如果使用 Trainer,确认是否设置了
bf16_full_eval=True,因为该配置会触发模型级别的 dtype 转换。 - 若为语音模型,确认是否属于
Wav2Vec2Conformer、SeamlessM4T、Wav2Vec2Bert、Clvp等使用旋转位置编码且以 buffer dtype 构建位置值的架构。 - 检查目标模型的
inv_freq或*_inv_freqbuffer 的 dtype,判断是否已被降为 bf16/fp16。
解决步骤
- 先判断问题路径:检查模型加载后是否还执行了
model.to(...)、.bfloat16()、.half(),或 Trainer 是否启用了bf16_full_eval=True。 - 如果只是需要 bf16 推理或评估,优先尝试改用
from_pretrained(..., dtype=torch.bfloat16),让模型在加载阶段完成 dtype 转换,避免对inv_freq这类频率 buffer 做后置降精度。 - 对于必须使用
Trainer(bf16_full_eval=True)的场景,可优先尝试在评估前记录目标模型的inv_freq(或*_inv_freq)buffer 的原始 fp32 值,并在 dtype 转换后将其恢复为 fp32;此为 Issue 中_apply原型方案的思路,尚未合入主线。 - 如果存在自定义模型或自定义 RoPE 模块,可优先尝试参照 timm 的做法:在
_apply中检测频率 buffer 被降精度时,将其重建为 fp32,而不是保留 bf16/fp16。 - 上述修改都需要覆盖
to、half、bfloat16、float、cuda等所有经过_apply的路径;Meta tensor(to_empty)不应被重新填充,.double()路径也应保持原行为。 - 验证时同时跑两条路径:
from_pretrained(dtype=bf16)与手动.to(bf16),比较两者的inv_freq.dtype与长上下文角度误差。若两者的角度误差与 fp32 参考一致,说明修复生效。
验证方法
用 Issue 中的最小复现脚本确认:loaded.model.rotary_emb.inv_freq.dtype 应为 torch.float32,cast.model.rotary_emb.inv_freq.dtype 修复前为 torch.bfloat16。在 max_position_embeddings=32768 下,from_pretrained(dtype=bf16) 的角度误差均值应在 0.0006 rad 量级、超过 0.5 rad 的比例为 0.0%;model.to(bf16) 修复前误差均值约 0.6733 rad、超过 0.5 rad 的比例约 36.1%。对语音模型,修复后 10s 与 30s 应均为 0 collapsed、0.00 rad,而不是修复前的 121/499 与 927/1499 位置塌缩。
参考来源
huggingface/transformers #49288
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


