3. 训练异常分类:常见异常类型及其特征

大规模模型训练过程中,异常的发生并非随机散落,而是呈现出明显的类型化特征。从系统观测的角度看,绝大多数训练失败或质量劣化都可以归入四类核心异常:损失不收敛、梯度爆炸/消失、显存溢出(OOM)以及通信超时/中断。这四类异常在发生位置、可观测信号、时间尺度以及根因空间上均有显著差异。准确分类是根因定位的第一步——如果连异常类型都判断错误,后续的日志检索、指标分析和代码排查都将失去方向。

3.1 损失不收敛:最隐蔽的质量型异常

损失不收敛并非“程序崩溃”型异常,训练进程往往正常运行,GPU 利用率饱满,日志无报错,但模型评估指标持续徘徊或反向恶化。这类异常最危险,因为它不会触发告警,却直接导致算力资源被无效消耗。

从特征上区分,损失不收敛可细分为三种子类型:

  • 损失震荡不降:loss 曲线在高位剧烈波动,无下行趋势。常见于学习率过大、batch size 与学习率不匹配、数据标签噪声过高。
  • 损失先降后升:训练初期正常下降,到达某一步后开始反弹。典型原因是学习率调度策略不当(如 warmup 后峰值过高)或模型开始过拟合噪声样本。
  • 损失平台期:loss 长时间维持在同一水平,既不下降也不上升。常见于梯度稀疏导致的有效更新不足、激活函数饱和(如 sigmoid 进入饱和区)、或优化器动量参数设置不当。

判断损失不收敛的核心观测指标包括:训练损失与验证损失的差值趋势、梯度全局范数(global gradient norm)的分布、以及每一层参数更新的幅度统计。仅观察 loss 标量远远不够,必须结合梯度统计才能区分“模型容量不足”与“优化过程异常”。

实操建议:在训练脚本中每 100 步记录一次 grad_norm 和 update_ratio(参数更新量/参数绝对值),当 loss 不降时优先检查这两个序列,而非反复调整学习率。

3.2 梯度爆炸与梯度消失:数值稳定性异常

梯度爆炸和梯度消失本质上是反向传播过程中梯度幅值随层数指数级放大或衰减的现象。在大规模模型中,由于网络层数深、残差结构多,梯度异常往往呈现层间分布不均的特征,而非整体性消失或爆炸。

梯度爆炸的典型信号包括:

  • loss 在某一 step 突然变为 NaN 或 Inf,且无法自动恢复。
  • 训练日志中出现 gradient clipping triggered 的频率异常升高(超过总 step 的 30%)。
  • 权重矩阵中出现极端离群值,模型输出为常数或全零。

梯度消失则更为隐蔽,其典型特征为:浅层(靠近输入的层)参数更新量极小,而深层参数正常更新。可通过 tensorboard 或 wandb 查看各层梯度直方图,若前 1/3 层的梯度范数比后 1/3 层小 3 个数量级以上,则高度怀疑梯度消失。

需要特别注意的是,在大规模分布式训练中,梯度爆炸常常以通信数据异常的形式表现出来——某个节点的梯度张量出现 NaN,AllReduce 操作会将 NaN 传播到所有节点,导致全局崩溃。因此,梯度爆炸的排查往往需要同时检查计算图与通信栈。

3.3 显存溢出(OOM):资源边界型异常

显存溢出是大模型训练中最常见的“硬性”异常,其直接表现是进程被 CUDA 运行时终止,日志中包含 CUDA out of memory 或 RuntimeError: CUDA error: device-side assert triggered。但 OOM 的根因远不止“显存不够大”这么简单。

从显存消耗的构成来看,训练过程中的显存占用分为四个部分:

显存分区 主要占用内容 典型占比(混合精度)
模型权重 参数本身 + 梯度 约 20%~30%
优化器状态 Adam 的一阶/二阶动量 约 30%~40%
激活值 前向传播中间结果(用于反向传播) 约 25%~35%
临时缓冲区 通信缓冲、Transformer 中间变量、CUDA context 约 5%~15%

OOM 的排查不能只看总显存,而应通过 torch.cuda.memory_summary() 或 nvidia-smi 的进程级显存占用,定位是哪个分区发生了溢出。例如,若激活值占用异常高,则需检查是否关闭了 activation checkpointing,或序列长度是否超出预期;若优化器状态占用过高,则需检查是否误用了 Adam 而非 AdamW,或未开启混合精度。

3.4 通信超时与中断:分布式特有异常

通信异常仅存在于分布式训练场景,其表现形态多样:训练进程 hang 住(卡死)、NCCL 报错 ncclSystemError: Call to connect returned error、或某个 rank 掉线导致全局 abort。通信异常的根因往往不在代码逻辑,而在网络拓扑、网卡配置或集群调度层面。

通信异常可按发生阶段进一步细分:

  • 初始化阶段失败:rank 之间无法建立 TCP 连接,常见于 MASTER_ADDR/MASTER_PORT 配置错误、防火墙拦截、或节点间网络隔离。
  • 训练中段超时:NCCL 的 AllReduce 或 AllGather 操作等待超时,常见于某节点 GPU 掉卡、PCIe 带宽降级、或共享网络带宽被抢占。
  • 通信数据不一致:各 rank 的 tensor shape 或 dtype 不一致,导致通信原语执行失败,报错信息通常包含 mismatch 或 invalid argument。

通信异常的排查需要同时关注 NCCL_DEBUG=INFO 日志、ethtool 网卡状态、以及 nvidia-smi topo -m 显示的 GPU 拓扑结构。特别要注意,机间通信(跨节点)与机内通信(NVLink)的故障表现完全不同,前者表现为高延迟和超时,后者则可能直接导致 CUDA error。

常见误区:很多工程师在遇到通信超时时,第一反应是增大 NCCL_TIMEOUT 环境变量。这只能掩盖症状,无法解决根因。如果 NCCL 超时是因为某节点 GPU 温度过高导致降频,增大超时只会让训练更慢,且最终仍会失败。应先检查硬件健康状态,再调整超时参数。

3.5 异常分类的判别路径与综合案例

在实际训练中,四类异常并非完全独立。例如,梯度爆炸可能导致 loss 变为 NaN,而 NaN 梯度在 AllReduce 后传播到所有节点,进而引发通信数据校验失败,最终表现为通信异常。因此,建立一套判别路径至关重要。

训练异常判别路径:
1. 训练是否中断(进程退出)?
   ├─ 是 → 查看退出码:
   │     ├─ 退出码 134/135 → 显存 OOM 或 CUDA error
   │     ├─ 退出码 255 → NCCL 通信失败
   │     └─ 退出码 1 → Python 异常(需查 traceback)
   └─ 否 → 训练是否卡死(无日志输出超过 10 分钟)?
         ├─ 是 → 通信 hang 或死锁
         └─ 否 → loss 是否正常下降?
               ├─ 否 → 损失不收敛
               └─ 是 → 观察 eval 指标是否达标

以下是一个真实的综合判别案例:某 70B 模型训练任务在 1200 步时出现 loss 突变为 NaN,同时 NCCL 日志报错 check failed。初步判断为通信异常,但进一步检查发现:

  1. loss 突变前 50 步,梯度范数从 0.8 持续攀升至 15.2,已触发梯度裁剪阈值(设为 1.0)。
  2. 梯度裁剪后,部分层(特别是 embedding 层)的梯度仍包含 Inf 值。
  3. Inf 梯度在 AllReduce 中导致通信数据校验失败。

最终根因是某数据样本中存在极端异常值,且该样本被重复采样多次,导致局部梯度爆炸。修复方案为:在数据加载管线中增加异常值检测(z-score > 10 的样本过滤),并将梯度裁剪策略从“全局裁剪”改为“按层裁剪”。

这个案例说明,异常分类只是起点,真正的根因往往跨越多个类别边界。训练工程师需要建立“信号→类型→根因”的映射思维,而非机械地按类别逐一排查。

大规模训练异常分类判别图 训练异常总入口 进程是否中断? 是(崩溃) 否(挂起/劣化) 显存溢出 (OOM) CUDA out of memory 通信超时/中断 NCCL timeout / hang CUDA 设备异常 device-side assert 损失不收敛 loss 震荡/平台/反弹 梯度爆炸/消失 grad norm 异常 关键判别特征速查 OOM → 显存峰值/碎片化 通信 → NCCL_DEBUG/拓扑 不收敛 → loss曲线/梯度范数 梯度异常 → 分层直方图 梯度爆炸可引发通信校验失败 loss 劣化可能源于梯度消失

3.6 分类后的排查优先级与复盘要点

完成异常分类后,应按照“先硬件、再框架、后模型”的优先级进行排查。硬件层(GPU 温度、NVLink 连接、网卡速率)的检查成本最低,但能排除约 30% 的异常;框架层(NCCL 配置、数据加载、混合精度设置)次之;模型层(网络结构、损失函数、超参数)的排查成本最高,应放在最后。

在复盘阶段,建议为每次异常记录以下信息:异常类型、首次出现 step、持续时间、关联指标(loss、grad norm、显存峰值)、触发条件(数据批次特征、学习率阶段)、以及最终根因。积累 20 条以上记录后,就能形成团队专属的异常特征库,后续定位效率将大幅提升。

本节要点总结:
  • 四类核心异常(不收敛、梯度异常、OOM、通信异常)在信号特征和根因空间上差异显著,分类是定位的第一步。
  • 损失不收敛需结合梯度范数判断,不能只看 loss 标量;梯度爆炸/消失需分层观察梯度分布。
  • OOM 排查需区分显存分区(权重/优化器/激活/缓冲),通信异常需区分初始化、训练中段和数据不一致三种子类型。
  • 异常之间存在级联关系(如梯度爆炸→通信失败),判别路径比静态分类更实用。