尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
GPU 与 CUDA 环境排障实战(6):bf16 和 fp16 训练为什么会出 NaN:Loss Scale 那行日志怎么读
先给结论混合精度训练出 nan多数不是模型写错了是梯度在 fp16 里溢出了。搜索「梯度 NaN」落到这里的人只需要先看一样东西日志里那行 Loss scaling: 65536 - 32768 (overflow) 还在不在更新。还在更新说明缩放器在自救下一步 loss 突然变 nan才要把「梯度溢出导致的 nan」和「数据本身有 nan」分开处理。本篇用两个确定性算术实验复现可表示范围是模型复现不是在显卡上实测原因写在文末最后给一张先动哪个开关的判据表。一、问题现场那行日志长这样训练机上第一次出 nan日志通常只有这一行Loss scaling: 65536 - 32768 (overflow)前面一步 loss 还是数字下一步就显示 nan梯度打印出来也是 nan。这个报错讨厌的地方在于它不提示你去看别处数据管道没坏、模型结构没坏只是 fp16 的数值范围装不下这一批梯度了。fp16 能表示的最大数是 65504而缩放的初始尺度常取 65536——比上限还大一圈。所以日志第一次出现 overflow 这个词时缩放器把尺度对半砍到 32768然后是 16384、8192数字在变训练还没死。真正需要反应的是另一种这几行日志安静下来之后nan 不再收场。二、机制四条每条一句结论第一fp16 的可表示范围窄。最大规格数 65504.0最小规格数 6.103515625e-05超出上界的梯度变 inf掉到下界的被刷成 0inf 和 0 参与梯度运算之后nan 就是必然结果。第二Loss Scale 的作用是把梯度整体搬进能表示的区间loss 先乘尺度放大梯度优化器更新前再除以同一个尺度。一次 overflow 就把尺度砍一半微小梯度才不会被清零。第三bf16 是另一套账它保留 fp32 的指数位不容易上溢通常不需要缩放器代价是尾数只有八位精度比 fp16 粗。bf16 训练还出 nan第一嫌疑人就不是尺度而是数据本身。第四nan 的时间线能分家溢出型的 nan 出现在训练若干步之后且前面一定有 Loss scaling 的 overflow 记录数据型的 nan 第一步就出缩放器还没动过日志里找不到一行 overflow。搜索词「梯度 NaN」命中的其实是这两种的混合读日志就是在把它们拆开。三、实验一可表示范围算术模型先把话说死本篇两个实验都是确定性算术模型复现。本机 Python 3.12.10、numpy 2.5.3where nvidia-smi找不到文件没有 CUDA 设备也没有 torch所以正文里没有任何数字来自显卡实测——下面每个数都来自纯标准库脚本的 stdout连跑两次逐字节一致。脚本把 fp16 区间与缩放平移写成判定函数大于 65504 记 OVERFLOW小于最小规格数记 UNDERFLOW。FP16_MAX65504.0FP16_MIN_NORMAL2.0**-14defjudge(g_abs_max,scale):scaledg_abs_max*scaleifscaledFP16_MAX:returnscaled,OVERFLOWifscaledFP16_MIN_NORMAL:returnscaled,UNDERFLOWreturnscaled,IN_RANGEprint(fp16 区间模型最大规格数%r 最小规格数%r%(FP16_MAX,FP16_MIN_NORMAL))v_a,tag_ajudge(3.2e4,65536.0)print(场景A 梯度绝对值上限%r 尺度%r 缩放值%r 判定%s%(3.2e4,65536.0,v_a,tag_a))scale_b2.0**-32v_b,tag_bjudge(3.0e3,scale_b)print(场景B 梯度绝对值上限%r 尺度2**-32%r 缩放值%r 判定%s%(3.0e3,scale_b,v_b,tag_b))marks[tfortin(tag_a,tag_b)iftin(OVERFLOW,UNDERFLOW)]print(标记行统计 OVERFLOW%d UNDERFLOW%d 合计%d/2%(marks.count(OVERFLOW),marks.count(UNDERFLOW),len(marks)))运行输出fp16 区间模型最大规格数65504.0 最小规格数6.103515625e-05 场景A 梯度绝对值上限32000.0 尺度65536.0 缩放值2097152000.0 判定OVERFLOW 场景B 梯度绝对值上限3000.0 尺度2**-322.3283064365386963e-10 缩放值6.984919309616089e-07 判定UNDERFLOW 标记行统计 OVERFLOW1 UNDERFLOW1 合计2/2场景 A 就是最常见的上溢原形梯度绝对值上限 3.2e4 乘尺度 65536 得 2097152000.0远超 65504判定行打出 OVERFLOW——和日志里那行 overflow 对得上。场景 B 换到另一头如果尺度从 65536 一路对半砍四十八次落到 2.3283064365386963e-10这时上限只有 3.0e3缩放值 6.984919309616089e-07 也掉到了最小规格数下面判定行打出 UNDERFLOW。两条标记行在 stdout 里各占一行随机重跑都在。四、实验二尺度衰减序列第二个模型回答「尺度是怎么掉下来的」。规则照需求口径写死从 65536 起每遇到一次 overflow 就把尺度砍半固定输入八步逐字打印八行 scale 值。期望落点只有一个第 4 行等于 8192。FP16_MAX65504.0START_SCALE65536G_MAX[3.2e4,1.6e4,8.0e3,4.0e3,2.0e3,1.0e3,5.0e2,2.5e2]EXPECTED_ROW48192print(起始尺度%d 规则每遇overflow减半 固定步数%d fp16上限%r%(START_SCALE,len(G_MAX),FP16_MAX))scaleSTART_SCALE row_values[]foriinrange(len(G_MAX)):scaledG_MAX[i]*scale overflowscaledFP16_MAX row_values.append(scale)print(第%d行 scale%d 梯度上限%r 缩放值%r 判定%s 下一步%d%(i1,scale,G_MAX[i],scaled,OVERFLOWifoverflowelseIN_RANGE,scale//2))ifoverflow:scalescale//2row4row_values[3]print(第4行检查 实算%d 期望%d 判定%s%(row4,EXPECTED_ROW4,MATCHifrow4EXPECTED_ROW4elseMISMATCH))print(八行尺度序列%s%,.join(str(x)forxinrow_values))运行输出起始尺度65536 规则每遇overflow减半 固定步数8 fp16上限65504.0 第1行 scale65536 梯度上限32000.0 缩放值2097152000.0 判定OVERFLOW 下一步32768 第2行 scale32768 梯度上限16000.0 缩放值524288000.0 判定OVERFLOW 下一步16384 第3行 scale16384 梯度上限8000.0 缩放值131072000.0 判定OVERFLOW 下一步8192 第4行 scale8192 梯度上限4000.0 缩放值32768000.0 判定OVERFLOW 下一步4096 第5行 scale4096 梯度上限2000.0 缩放值8192000.0 判定OVERFLOW 下一步2048 第6行 scale2048 梯度上限1000.0 缩放值2048000.0 判定OVERFLOW 下一步1024 第7行 scale1024 梯度上限500.0 缩放值512000.0 判定OVERFLOW 下一步512 第8行 scale512 梯度上限250.0 缩放值128000.0 判定OVERFLOW 下一步256 第4行检查 实算8192 期望8192 判定MATCH 八行尺度序列65536,32768,16384,8192,4096,2048,1024,512第 4 行正好落在 8192脚本自己把期望值打成了标记行判定MATCH。如果你的日志里尺度一路减半、八步之后还在减说明梯度上限长期压不住当前尺度该动的是梯度和数据不是继续等缩放器。五、判据链先动这个开关再查你的数据能区分「梯度溢出导致的 nan」和「数据本身有 nan」才知道先动哪个开关。按三步走。第一步看 Loss scaling 的 overflow 行是否还在更新。还在更新是溢出型先降学习率、收紧梯度裁剪阈值尺度会让梯度回到区间里同时留意它砍到 256 以下还不停这对应实验二里越砍越小的那条序列。第二步把梯度绝对值上限打出来套本篇模型跑一遍上限在 3.2e4 这个量级上溢坐实缩放值已经在 6.103515625e-05 这个量级以下是下溢减批次大小或者换 bf16别再折腾尺度。第三步排除数据本身第一步就 nan、日志里一行 overflow 都没有别怀疑 fp16直接查标签有没有无穷值、归一化参数有没有算错。数据型 nan 不会因为尺度砍半而好转溢出型 nan 换个尺度就能活——这是「那行日志怎么读」唯一实用的地方。为什么坚持在这个方向写后台 2026-10-18 10:04:56 0800 实读账号原创 402 篇、总阅读 10.4w、昨日阅读 539按「阅读 ≥300 且 赞藏 ≥10」的达标口径在发系列大多只有 1 篇达标而近 7 天 38 篇新稿评论数全为 0——阅读够了、互动不够。所以这一系列每篇都固定留一份可复制清单和一个有唯一答案的问题。六、带走的东西可复制清单与一个有唯一答案的问题先给可直接复制的判据清单复制这段贴进你的笔记都行混合精度 nan 排查顺序fp16 适用bf16 直接看第三条 1 看 Loss scaling overflow 行还在更新溢出型一行都没有数据型 2 打梯度绝对值上限 GG×尺度 65504 → 降学习率、收紧梯度裁剪 G×尺度 6.103515625e-05 → 下溢减批次或换 bf16 3 数据型查标签无穷值、空值、归一化参数——第 0 步就 nan 的都在这一类 4 尺度连砍八步不回头梯度的问题等缩放器没用再留一个问题答案唯一、就在本篇里实验二打印的八行尺度序列第 5 行的数值是多少一个数字不是「你怎么看」。七、合集与复现说明本稿属于合集《GPU 与 CUDA 环境排障实战》。两个实验的脚本与全部 stdout 逐字存在随稿证据里无显卡的机器上用 Python 3.12.10 跑同一段命令会得到逐字节相同的数字本篇是模型复现不是实机实测涉及的后台统计数字均为本机实读。参考来源NVIDIA CUDA C Programming GuideArithmetic Instructions 一节docs.nvidia.com/cuda/cuda-c-programming-guide/PyTorch 官方文档Automatic Mixed Precisionpytorch.org/docs/stable/amp.htmlWikipediaHalf-precision floating-point formaten.wikipedia.org/wiki/Half-precision_floating-point_formatWikipediaIEEE 754en.wikipedia.org/wiki/IEEE_754¥9.9 付费专栏《Python 自动化接单实战从脚本到第一单》共 10 篇、已完结不再更新一次拿走点此直达
RELATED

相关推荐

Linux 学习笔记(十九):进程程序替换与手写微型 Shell

Linux 学习笔记(十九):进程程序替换与手写微型 Shell

一、 进程程序替换:exec 家族如果我们想让子进程去执行一个全新的程序(比如系统的 ls),而不是执行父进程的代码,就需要用到 exec 族函数。它的工作原理是:加载新程序到当前进程的地址空间中,完全…

📅 2026/10/24 6:41:00
电脑屏幕一键“复制”到另一台电脑?这个方法Windows、Mac都能用

电脑屏幕一键“复制”到另一台电脑?这个方法Windows、Mac都能用

作为一个每天对着电脑工作的上班族,我的办公桌上不知不觉攒了两三台笔记本电脑。有公司配的ThinkPad,有自己之前买的华硕老本子,还有一台几乎没怎么用的旧机子。放着吃灰觉得可惜,卖掉又不值钱,直到我发现了这个功能—…

📅 2026/10/24 6:41:00
硬盘数据恢复6种实用方法汇总:快速找回硬盘数据,零门槛恢复篇

硬盘数据恢复6种实用方法汇总:快速找回硬盘数据,零门槛恢复篇

在数字化生活与工作中,硬盘承载着我们海量的重要数据,从工作资料到珍贵回忆,每一份数据都意义非凡。然而,误操作、病毒入侵、硬盘故障等意外情况,常常导致数据丢失,令人焦头烂额。别担心!掌握有…

📅 2026/10/24 6:36:00
MORE NEWS

更多资讯

📰

宝塔面板下Aria2安装配置与AriaNg部署完整教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Linux多线程压缩工具选型与实战:xz、zstd、7z、lrzip性能对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

原生JS实现锚点定位与Tab跟随:滚动监听、性能优化与移动端适配全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

PCA9422与PIC18F57K42低功耗电源管理方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

基于PCA9422与STM32F413的多路电源管理方案实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

LAS点云分类实战:PointNet++训练全流程与语义标签对齐

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬