ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

NeMo Speech 分布式训练排障实战:debug-training-logs 技能的日志根因定位方法论

NeMo Speech 分布式训练排障实战:debug-training-logs 技能的日志根因定位方法论 NeMo Speech 分布式训练排障实战debug-training-logs 技能的日志根因定位方法论【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech本文以 NeMo Speech 仓库中.claude/skills/下的debug-training-logs技能文档为核心完整拆解一套面向大规模分布式训练NeMo、Megatron、PyTorch失败日志的排查方法论从收集 SLURM worker stderr 日志与 AIStore daemon 日志开始经过分级错误搜索、NCCL 状态逐 rank 核验、AIStore 存储侧计数器分析最终输出带因果链的根因报告。读完后你将掌握如何在多 rank 级联故障中区分「触发者」与「受害者」、如何用enqueued/completedwork 计数判断卡死发生在 CPU 数据加载还是 GPU 通信、以及 NeMo 每步同步点PreemptionCallback、DDP allreduce、broadcast_buffers如何放大单 rank 停顿。1. debug-training-logs一个面向训练日志的排障技能debug-training-logs是 NeMo Speech 仓库中为 AI 辅助排障设计的技能文件完整定义在 SKILL.md。它与同目录下的 fix-issue、migrate-to-resumable-dataloader 等技能共同构成了该仓库的 Agent 排障/迁移工作流集。技能 frontmatter 声明了它的输入契约与能力边界name: debug-training-logs description: Debug distributed training failures (NeMo, Megatron, PyTorch) from worker stderr logs and optional AIStore daemon logs. Finds root cause across NCCL timeouts, data loading errors, and storage failures. disable-model-invocation: true allowed-tools: Bash Read Grep Glob Agent argument-hint: path-to-logs-dir [ais-logs-dir]两个关键输入目录Worker 日志必填$ARGUMENTS[0]SLURM/torchrun 产生的 stderr 文件即每个计算节点上形如error-JOBID-N.out的文件AIStore daemon 日志可选$ARGUMENTS[1]AIS proxy/target 的 tarball 或已解压目录用于下钻存储侧问题。技能开篇即给出核心诊断哲学找根因而不是症状。分布式训练失败普遍存在级联效应——一个根因会触发大量下游错误正确的做法是从最终崩溃点反向回溯到最初的触发者。disable-model-invocation: true表明该技能不会由模型自动触发必须由用户显式调用并传入日志目录。2. 验证纪律防止把症状当成根因技能文档用专门一节CRITICAL: Verification discipline规定了五条强制性核验规则这是整篇文档最有价值的方法论约束检查全部 rank而不是抽样。看 5 个 rank 就假设其余 123 个相同是大忌。必须对完整输出做sort -u找离群值——单个离群 rank 可能就是整个根因。核验每一个 rank 的 NCCL 状态。抽取所有 rank 的last enqueued work与last completed work找出任何不同的 rank。enqueued completed无未决操作与enqueued completed有未决操作的 rank 本质不同前者根本没进入卡死的集合通信。区分「First PG on this rank to signal dumping」与「Observed flight recorder dump signal from another rank」。前者是发起者自己的 watchdog 触发后者只是被通知它甚至可能不在集合通信里——这是两种完全不同的失效模式。下结论前回到原始日志复核。重新读取实际日志行不要依赖此前写下的摘要——摘要可能是错的。分析过程中如果结论变了必须显式说明原来哪里错了、为什么改。不允许悄悄更换结论。3. Phase 0收集完整日志如果用户未提供 worker 日志第一步是要求下载 SLURM error 日志通常位于计算节点或共享文件系统# 从用户机器 SCP 拉取 SLURM error 日志 scp cluster:/path/to/slurm/logs/error-JOBID-*.out ./training_logs/ # 或者日志位于登录节点可访问的共享文件系统 mkdir -p ./training_logs cp /path/to/slurm/error-JOBID-*.out ./training_logs/硬性要求必须提供全部 per-node error 文件error-JOBID-0.out到error-JOBID-N.out只给一个节点的文件无法定位是哪个 rank 引发了失败。4. Phase 1Worker 日志分诊4.1 先理解这个作业读取几个日志文件的前 80 行确定四件事框架NeMo、Megatron、PyTorch Lightning、DeepSpeed 等、规模GPU 数、节点数、每节点 rank 数、作业在做什么训练/微调/推理、是否从 checkpoint 恢复。这些信息决定了后续搜索的预期 rank 总数与同步点集合。4.2 按三级优先级搜索致命错误在所有日志文件中并行搜索以下模式按优先级分层Tier 1 —— 进程级杀手NCCL.*timeout|Watchdog caught collective operation timeout taking the entire process down SIGTERM|SIGKILL|SIGABRT CUDA error|CUDA out of memory|OOMTier 2 —— 训练循环崩溃RuntimeError|Exception.*Error AISBatchLoaderError|StopIteration Traceback \(most recent call last\)Tier 3 —— 数据加载 / IOConnection reset|Connection broken|Connection refused retrying [0-9]/[0-9] timed out|deadline exceeded broken pipeAISBatchLoaderError是 AIStore batch 加载路径中「实际返回对象数少于请求数」的典型报错它定义在 Lhotse/AIS 客户端数据加载栈中本仓库不定义该异常但针对 AIS batch 加载的集成测试可在 test_lhotse_multimodal_ais_get_batch.py 中找到。4.3 识别发起者与拖后腿者straggler对 NCCL timeout必须检查所有 rank。技能给出三条可直接复制的命令# 所有自己 watchdog 触发的 rank发起者及其 NCCL work 计数 grep failure detected by watchdog error-*.out | grep -o Rank [0-9]*.*last enqueued work: [0-9]*, last completed work: [0-9]* | sort -u # 被通知的 rank自己 watchdog 未触发 grep Observed flight recorder dump signal from another rank error-*.out | grep -o Rank [0-9]* # 统计实际出现 watchdog 失败的唯一 rank 数与预期总数比对 grep failure detected by watchdog error-*.out | grep -o Rank [0-9]* | sort -t -k2 -n -u | wc -l判读规则说 Observed 而不是 detected 的那个 rank 大概率就是 straggler——它没有未决 NCCL 操作因为它从未进入集合通信。检查它的Last enqueued NCCL work若等于last completed NCCL work说明该 rank 卡死在 NCCL 之外训练循环、数据加载等 CPU 侧代码而不是卡死在某个集合通信内部。对于 BROADCAST/ALLREDUCE 超时可以反推集合通信的开始时间start_time timeout_time - timeout_ms默认超时 1800000ms 30 分钟。4.4 计数与分类跨所有文件统计Connection reset的总次数与每文件次数观察重试模式重试始终是1/N第一次重试就恢复属噪声还是会升级到N/N重试耗尽属真实故障统计唯一错误类型数与受影响 rank 数留意AISBatchLoaderError等 batch loader 错误——它们意味着 AIStore 返回的对象少于请求数是存储侧故障进入训练侧的直接信号。4.5 建立因果链时间线判定哪个错误最先发生、哪些是后果。技能给出的典型级联模式是数据加载错误根因 - 部分 rank 退出训练循环 - 崩溃的 rank 无法参与 NCCL 集合通信 - NCCL 集合通信挂起到超时通常 30 分钟 - Watchdog 杀掉所有剩余 rank4.6 NeMo 特有的每步同步点结合源码验证NeMo 存在每步执行的集合通信任何一个 rank 落后都可能在这里显形。技能列举了四个同步点且均可在本仓库源码中逐一对应PreemptionCallbackon_train_batch_end中每个训练步结束时调用torch.distributed.broadcast(interrupted, 0)。若某 rank 的单步耗时比其他 rank 长 30 分钟以上该 broadcast 就会超时。源码见 preemption.py 的interrupted属性构造 GPU 上的 int32 tensor 后从 rank 0 广播与 第 91-106 行 的on_train_batch_end钩子——每个 batch 结束都触发一次 broadcast检测到抢占时经_save_last_checkpoint_and_exit保存last.ckpt并退出。NeMoModelCheckpointcheckpoint 保存/加载过程中的多次trainer.strategy.broadcast()。源码见 nemo_model_checkpoint.py 中的调用点如 第 207 行 广播ckpt_path、第 290 行 广播best_model_path、第 541 行 广播文件存在性。DDP 梯度 all-reduce反向传播期间自动的每步同步。broadcast_buffersDDP 默认 True每次前向从 rank 0 广播模型 buffers如 batch norm 统计量。一个可操作的量化判据若 rank 0 的 NCCL SeqNum 领先其他 rank检查差距是否匹配每步集合通信的数量——PreemptionCallback broadcast DDP allreduce broadcast_buffers ≈ 每步 3 个操作。这能直接估算出落后 rank 卡在了多少步之前。该回调的默认开启行为也可在实验管理器中确认exp_manager.py 第 271 行 定义create_preemption_callback: Optional[bool] True即默认启用、需显式传create_preemption_callback: False关闭与 preemption.py 文档字符串一致。此外实验管理器还提供 straggler 检测钩子create_straggler_detection_callback见 exp_manager.py 第 286-291 行与本文的 straggler 定位思路互补。5. Phase 1.7索取 AIStore daemon 日志当分析指向存储 IO 问题connection reset、数据加载错误、超时而用户未提供 AIS daemon 日志时先检查which ais若可用则引导用户执行# 1. 设置集群 endpoint export AIS_ENDPOINThttps://ais-cluster-endpoint:port # 2. 设置认证 token export AIS_AUTHN_TOKENtoken # 3. 处理 TLS跳过校验 或 指定 CA 证书 ais config cli set cluster.skip_verify_crttrue # 或 export AIS_SERVER_CRT/path/to/ca.crt # 4. 下载所有集群日志proxy 与 target 的 tar.gz 归档 ais log get cluster path-to-worker-logs-dir/ais_logs若未安装aisCLI可从 AIStore 仓库构建cd cmd/cli go install .或下载二进制也可以让用户手动从 AIS 集群拉日志。6. Phase 2AIStore 日志分析6.1 解压与定位正确的时间窗tarball 解压模板mkdir -p extracted cd extracted for f in ../*.tar.gz; do name$(basename $f .tar.gz) mkdir -p $name tar xzf $f -C $name doneAIS daemon 日志命名约定aistarget.ais-target-N.INFO.MMDD-HHMMSS.1 # target 日志 aisproxy.ais-proxy-N.INFO.MMDD-HHMMSS.1 # proxy 日志关键理解——一个 daemon 可能对应多个日志文件文件名中的MMDD-HHMMSS是该文件的起始时间文件覆盖到同一 daemon 下一个文件的起始时间或 daemon 停止/日志被收集时新文件出现在 daemon 重启崩溃、升级、维护时。定位故障窗口的正确文件按文件名时间戳列出每个 daemon 的全部文件找到起始时间早于故障窗口、且下一个文件起始时间晚于故障窗口或无下一个文件的那个文件若某文件起始时间落在故障窗口内部说明 daemon 在窗口内重启过——这本身就是重要证据文件内部日志行只有HH:MM:SS没有日期。跨午夜的文件里同一时刻可能出现两次需借助上下文stats 计数器值、已知事件判别属于哪一天。务必检查某 daemon 的所有文件而不只是最新的——最新文件可能只覆盖重启后几分钟证据在更早的文件里。时区核验AIS daemon 与 worker 日志通常在不同机器、不同时区绝不能假设一致。核验步骤找 AIS 的周期性时间戳标记common:NNN DD Mon YY HH:MM UTC 它显式声明时区通常 UTCworker 侧NeMo 用YYYY-MM-DD HH:MM:SSSLURM 用YYYY-MM-DDThh:mm:ss两者默认都不带时区交叉对齐一个两侧都可见的已知事件——最佳锚点是作业死亡时刻在 worker 日志找 SLURMCANCELLED时间戳再找 AIS stats 中get.n停止增长的精确时刻。若对齐则时区一致若偏移整小时数则两系统处于不同时区时区不一致时先统一施加偏移再关联事件。6.2 故障窗口的 target stats 关键计数器AIStore target 约每 3 分钟输出一次 stats 行。需跟踪的关键计数器err.get.n—— GET 错误应保持稳定突增即异常err.getbatch.n—— batch GET 错误err.http.write.n—— 对客户端的 HTTP 响应中断err.put.n、err.head.n、err.lst.n—— 其他操作错误get.n对比err.get.n—— 计算错误率getbatch.n、getbatch.obj.n—— batch 操作计数方法是对比相邻 stats 行的差值找出该区间内新增的错误数。完整计数器含义见第 8 节附录表。6.3 错误级消息模式grep ^E logfile # Error 消息 grep ^W logfile # Warning 消息AIStore 侧的关键错误模式模式含义x-get-batch.*out-of-bounds indexbatch GET 在 target 间流式传输中丢失对象shared-dm.*terminated.*broken pipetarget 间数据搬运流data mover断裂shared-dm: xid.*not found, dropping recv对象被丢弃因为 batch 作业已中止resource pressure: loadcriticaltarget 处于磁盘/内存/CPU 压力之下lcache.*hk.*dskcritical磁盘处于危急水位housekeeping 被跳过gc:.*free mem/oom:内存压力 / 强制 GC6.4 Proxy 日志Proxy 负责编排 batch GET。检查 proxy statserr.get.n—— 应接近零偏高即 proxy 层路由失败err.http.write.n—— proxy 丢弃客户端连接。判别法则若 proxy 错误平稳而 target 错误突增问题在 target 侧磁盘、内存、target 间网络。6.5x-get-batch失效链条AIStore batch GET 失败的典型因果链存储侧视角与 Phase 1 的训练侧级联首尾相接1. Target 处于资源压力下dskcritical、memlow 2. Target 间 shared-dm 流断裂broken pipe 3. x-get-batch 遇到 out-of-bounds indexrecvd len0 4. Batch 作业中止后续对象被丢弃xid not found 5. 客户端收到的对象少于请求数 6. Lhotse/客户端 batch loader 抛出错误迭代器过早耗尽第 6 步正是 Phase 1 Tier 2 中AISBatchLoaderError的来源两条日志线在此汇合。7. Phase 3综合报告与失效分类7.1 报告结构技能规定了根因报告的五段式输出Job Details—— 框架、规模、开始时间、数据源Timeline Table—— 带时间戳的按时间排序事件表注明证据来源的文件:行Root Cause Chain—— 从触发点到最终崩溃的编号因果链用箭头连接Key Files—— 哪些日志文件包含关键证据Recommendations—— 可执行的修复建议分存储侧、客户端侧、训练配置侧。7.2 失效分类表Storage I/O磁盘压力、broken pipe、connection reset、batch 对象丢失Network无数据错误的 NCCL timeout、网卡故障、交换机问题GPU/CUDAOOM、ECC 错误、CUDA assertionData数据文件损坏/缺失、manifest 不匹配、schema 错误Software版本不匹配、配置错误、Python 进程 OOMInfrastructure节点故障、抢占、SLURM 超时Data loading stall单 rank 卡死在数据加载读取无超时阻塞所有其他 rank 在下一个集合通信处。仓库中也提供了配套的数据加载验证工具 validate_dataloader.py可在训练前校验 dataloader 行为作为「Data loading stall」类的预防手段。8. 附录 ANCCL timeout 解剖与卡死位置判定技能附带的 NCCL 日志字段词典Watchdog caught collective operation timeout—— NCCL watchdog 检测到卡死的集合通信SeqNumN, OpTypeBROADCAST/ALLREDUCE—— 哪个集合通信、序号多少last enqueued work: N, last completed work: M—— work M 已完成work M1 卡住Timeout(ms)1800000—— 30 分钟超时默认First PG on this rank to signal dumping——本 rank 发起了级联Observed flight recorder dump signal from another rank—— 本 rank 是对他人超时的反应To avoid data inconsistency, we are taking the entire process down—— watchdog 杀进程。enqueued与completed计数是判定卡死发生在 CPU数据加载/训练循环还是 GPUNCCL 通信的关键enqueued completed无未决操作该 rank 没有任何 in-flight NCCL 工作卡死在 CPU 侧数据加载、音频解码、batch 组装从未进入集合通信。这就是 straggler——引发挂起的那个 rank。enqueued completed 1恰好提交了一个未完成的操作。它进入了集合通信但因为 straggler rank 没有加入而无法完成。enqueued completed 1多个操作排队——CPU 已越过卡点异步提交了额外操作如 DDP 梯度 allreduce 走 hook。仍在等 straggler。rank 0 的enqueued/completed高于其他 rankrank 0常为 broadcast root完成了自己一侧的 send但接收方因 straggler 未加入而无法完成 receive。两种标志性模式对照数据加载 stall 模式1 个 rank 呈enqueued completed、active collectives: 0、Observed flight recorder dump signal from another rank这是 stragglerN-1 个 rank 呈enqueued completed、failure detected by watchdog在等 stragglerrank 0 若为 broadcast root 可能领先更多。GPU 互联fabric故障模式全部 rankenqueued completed都进入了集合通信全部显示 First PG on this rank to signal dumping——没有 straggler是集合通信本身坏了。结论把所有 rank 的enqueued数放在一起比较哪怕一个离群值都会改变整个诊断。9. 附录 BNeMo/Lhotse 数据加载的常见坑技能最后归纳的五个「单 rank 卡死」常见诱因均可在本仓库找到对应实现Lhotse URL 音频读取无超时AudioSource._prepare_for_reading()调用f.read()无 Lhotse 层超时。下载卡死会无限期阻塞 DataLoader worker——这正是「data loading stall 无超时」类别的直接来源。fault_tolerantTrue静默丢弃失败音频失败音频文件被跳过每 rank 的有效 batch 变小不同 rank 因分到的 shard 不同失败率可能不同。本仓库中该默认值明确可见audio_to_text_lhotse.py 的_make_audio_samples()以fault_tolerant: True构造AudioSamples并在旧版 Lhotse 不支持use_batch_loader时回退到AudioSamples(fault_tolerantTrue)同时给出Lhotse 1.32.0的升级提示说明 ASR 数据集默认走容错路径。BytesIO 中丢失.m4a扩展名从 URL 下载并包进 BytesIO 后扩展名丢失LhotseCompositeAudioBackend无法走 m4a 快速路径TorchaudioFFMPEGBackend退化为昂贵的级联 backend 尝试。空闲 keep-alive 连接重置AIStore 在空闲 30 秒DfltMaxIdleTimeout后关闭 HTTP 连接而 Python SDK 的 urllib3 连接池不匹配该超时导致旧连接上出现Connection reset by peer。这类错误被捕获并第一次重试即成功——它们是噪声不是根因。这与 4.4 节「重试始终 1/N 即恢复」的判别规则呼应。每个 rank 拿到不相交的数据 shardLhotse 以src[rank::world_size]方式切分 shard。某个 rank 可能恰好分到损坏文件更多、音频更大、或所在存储 target 更慢的 shard——这解释了为什么 stall 往往集中在个别 rank。10. 小结与相关仓库资源debug-training-logs文档本质上把一次大规模训练失败的排查固化为可重复执行的流水线收集全量 worker 日志 → 三级错误分诊 → 逐 rank NCCL 状态核验 → 建立因果链时间线 → 按需下钻 AIStore 存储日志时区对齐 stats 差值分析→ 五段式根因报告并以五条验证纪律贯穿始终确保结论经得起原始日志复核。其核心洞察是绝大多数「NCCL timeout 全集群被杀」的表象之下真正根因往往是单个 rank 在 CPU 侧多为数据加载的无超时卡死而enqueued completed的 NCCL work 计数就是分辨「CPU 卡死」与「GPU 通信故障」的判别器。延伸阅读仓库内相对路径技能定义全文.claude/skills/debug-training-logs/SKILL.md每步抢占广播的同步点实现nemo/utils/callbacks/preemption.py实验管理器与默认回调开关nemo/utils/exp_manager.pyCheckpoint 保存路径的 broadcast 调用nemo/utils/callbacks/nemo_model_checkpoint.pyASR Lhotse 数据集与fault_tolerant默认值nemo/collections/asr/data/audio_to_text_lhotse.pyAIS batch 加载集成测试tests/collections/common/test_lhotse_multimodal_ais_get_batch.py可恢复 dataloader 迁移技能含 AIStore 数据路径参考.claude/skills/migrate-to-resumable-dataloader/SKILL.md【免费下载链接】SpeechA scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)项目地址: https://gitcode.com/GitHub_Trending/nem/Speech创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表