搭配MindSpore框架进行大模型训练时遇到“INFNAN模式溢出”
问题描述
使用华为昇腾AI处理器(Ascend 910)搭配MindSpore框架进行大模型训练时遇到了一个棘手的问题,已经排查了好几天还没解决,特来求助。
正在尝试在单台8卡昇腾910服务器上微调Qwen-14B模型。环境配置完成后,启动训练脚本大约运行30分钟后,程序会突然崩溃,报错信息显示为“INFNAN模式溢出”和“TBE compile failed for custom operator”双重错误。
环境信息:
-
硬件:华为Atlas 800训练服务器(8×Ascend 910)
-
操作系统:EulerOS 2.0 SP8
-
驱动版本:23.0.rc1
-
CANN工具包:7.0.0
-
MindSpore版本:2.2.0(昇腾专用版)
-
Python版本:3.7.5
-
模型:Qwen-14B(从Hugging Face转换而来)
详细错误日志:
已尝试的解决方案:
-
检查了CANN环境变量配置,确认
ASCEND_OPP_PATH指向正确路径 -
清理了编译缓存:
rm -rf ~/ascend_cache/* -
尝试降低batch_size从16降到8,问题依旧
-
开启了内存复用:
export MS_ENABLE_MEM_REUSE=1 -
验证了驱动状态:
npu-smi info显示所有卡状态正常 -
单独测试了基础矩阵运算,可以正常运行
问题特点:
-
错误发生在训练中期(约1250次迭代后),不是一开始就报错。
问题解答
在昇腾910上使用MindSpore微调Qwen-14B模型时,训练约30分钟(1250次迭代后)出现"INFNAN模式溢出"和"TBE compile failed for custom operator"双重错误,这确实是一个典型的数值稳定性问题,尤其在混合精度训练中较为常见。以下是系统性的排查与解决方案,按优先级排序。
核心问题分析
-
INFNAN模式溢出:这是MindSpore默认的溢出检测模式。在该模式下,当计算超出fp16表示范围(最大65504)时,会输出INF/NAN而非饱和值。训练中期出现溢出,往往是因为梯度或激活值在反向传播中逐渐累积,最终击穿阈值。
-
TBE编译失败:可能与算子融合优化有关,特别是在attention的矩阵乘法(query × key)环节,fp16累加容易溢出。
推荐解决方案(按优先级)
1. 立即尝试的快速修复
-
开启动态损失缩放(DynamicLossScaleManager):这是解决混合精度训练溢出的最直接方法。在您的训练脚本中,确保已配置
DynamicLossScaleManager,它会自动调整loss scale来避免下溢/溢出。 -
关闭matmul算子的fixpipe融合:针对CANN 7.0,修改文件
/usr/local/Ascend/CANN-7.0/opp/built-in/fusion_pass/config/fusion_config.json,将"TbeMatmulFixPipeFusionPass"的值从"on"改为"off"。这能解决部分因算子融合导致的编译失败。
2. 调整精度策略
-
尝试bf16训练:bf16具有比fp16更大的动态范围(指数位8位),能显著降低溢出风险。若您的CANN和MindSpore版本支持,可将混合精度改为bf16。
-
关键部分使用fp32:在attention的score计算(query与key矩阵乘)或softmax之前,手动插入
Cast算子将数据转换为fp32,计算完成后再转回fp16。 -
全局fp32训练:作为调试手段,可暂时切换到纯fp32训练,验证是否为精度问题。
3. 梯度与训练稳定性优化
-
梯度裁剪(Gradient Clipping):在优化器更新前,使用
nn.ClipByGlobalNorm对梯度进行裁剪,防止梯度爆炸。例如 -
from mindspore import nn optimizer = nn.Adam(params, learning_rate=lr) grads = ms.ops.GradOperation(get_by_list=True)(loss_fn, optimizer.parameters)(*inputs) grads = nn.ClipByGlobalNorm()(grads) # 默认阈值1.0可调整 optimizer(grads) -
降低学习率或使用warmup:过大的学习率可能导致更新步长过大,引发数值不稳定。尝试将学习率降低一个数量级(例如从2e-5降至1e-5),并确保有足够的warmup步骤。
-
检查权重初始化:确保模型权重初始化范围合理,避免初始值过大。
4. 深入诊断与定位
-
使用TroubleShooter定位溢出点:运行以下命令,精确定位网络中第一个出现NaN/INF的算子:
-
from mindspore import Troubleshooter ts = Troubleshooter() ts.infer_value_and_grad(net, *inputs) # 传入您的网络和输入 -
开启异步Dump进行离线分析:配置
data_dump.json,设置dump_mode=0、op_debug_mode=3,保存中间结果,然后搜索日志中的overflow infos找到具体溢出算子。 -
检查输入数据:确认训练数据中是否存在异常值(如NaN、极大/极小值)。可在数据加载时加入数值范围检查。
5. 环境与配置检查
-
更新软件版本:考虑升级到更稳定的版本组合,如CANN 7.0.x的最新补丁、MindSpore 2.2.x。已知某些版本在bf16支持和大模型训练上更稳定。
-
调整编译缓存:彻底清理编译缓存并尝试禁用缓存:
rm -rf ~/ascend_cache/* ~/.mindspore/kernel_meta/ export MS_COMPILER_CACHE_PATH="" # 临时禁用缓存
更多推荐



所有评论(0)