问题描述

使用华为昇腾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转换而来)

详细错误日志:

已尝试的解决方案:

  1. 检查了CANN环境变量配置,确认ASCEND_OPP_PATH指向正确路径

  2. 清理了编译缓存:rm -rf ~/ascend_cache/*

  3. 尝试降低batch_size从16降到8,问题依旧

  4. 开启了内存复用:export MS_ENABLE_MEM_REUSE=1

  5. 验证了驱动状态:npu-smi info显示所有卡状态正常

  6. 单独测试了基础矩阵运算,可以正常运行

问题特点:

  • 错误发生在训练中期(约1250次迭代后),不是一开始就报错。

问题解答

在昇腾910上使用MindSpore微调Qwen-14B模型时,训练约30分钟(1250次迭代后)出现"INFNAN模式溢出"和"TBE compile failed for custom operator"双重错误,这确实是一个典型的数值稳定性问题,尤其在混合精度训练中较为常见。以下是系统性的排查与解决方案,按优先级排序。

核心问题分析

  1. INFNAN模式溢出:这是MindSpore默认的溢出检测模式。在该模式下,当计算超出fp16表示范围(最大65504)时,会输出INF/NAN而非饱和值。训练中期出现溢出,往往是因为梯度或激活值在反向传播中逐渐累积,最终击穿阈值。

  2. 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=0op_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=""  # 临时禁用缓存
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐