SpeechBrain 多 GPU 训练实战:从 DDP 单节点到多节点集群部署
SpeechBrain 多 GPU 训练实战从 DDP 单节点到多节点集群部署【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain本篇技术指南基于 SpeechBrain 官方文档 docs/multigpu.md 展开系统讲解在 PyTorch 分布式训练框架下如何用 Distributed Data ParallelDDP让 SpeechBrain 配方recipes在单机多卡与多机多卡环境中高效训练。你将掌握torchrun的标准启动方式、DDP 安全代码的编写要领、Slurm 集群下的自动化部署以及多节点分离文件系统下 checkpoint 的正确保存策略并了解为何DataParallel已被官方弃用。为什么默认不是多 GPU—— DDP 的起点SpeechBrain 是一个基于 PyTorch 的语音工具包绝大多数训练脚本默认在单个 GPU 上运行。训练速度虽然可以从多卡并行中大幅受益但即使在单台机器上多 GPU 也并非默认开启。官方强烈建议使用Distributed Data ParallelDDP来开启多卡训练而不是传统的torch.nn.DataParallel。DDP 的实现方式是为每张 GPU 派生一个独立进程one process per GPU通过数据并行的方式切分训练数据。它既支持同一台机器上的多张 GPU也支持网络上多台机器之间的协同训练如果你确实需要的话。在使用 CUDA 的前提下本文档默认讨论 CUDA 场景PyTorch 底层通过NCCL通信库完成梯度同步与聚合。此外DDP 属于服务端集群环境相关性较强的机制不同集群配置可能遇到不同的兼容性问题如果训练中出现异常建议先确认 PyTorch 版本足够新并检查 NCCL 相关环境配置。DDP 下 batch size 的语义与 DataParallel 的本质区别在进入命令之前先厘清一个极易混淆的概念DDP 中batch size 是针对单个进程/单张 GPU 定义的。例如你指定batch_size: 16无论使用 1 张还是 8 张 GPU每个进程/GPU 都使用 16 的 batch全局等效 batch size 为16 × GPU 数。DataParallelDP中batch 会按 GPU 数量切分。例如batch_size: 16在 4 卡上运行每张 GPU 实际只拿到 4 个样本。这一差异直接影响超参数配置用 DDP 时若想保持与单卡相同的每卡批量无需调整而用 DP 时则要相应放大 batch size 才能保证每卡样本数不变。SpeechBrain 的Brain.fit等核心方法已经按 DDP 语义处理数据加载详见下文源码层验证一节。编写 DDP 安全的代码run_on_main与 DDP-aware 机制DDP 要求训练脚本本身是DDP 安全DDP-safe的因为同一份脚本会被并发执行多次甚至跨多台机器。官方保证SpeechBrain 自带的配方recipes可以直接在 DDP 下运行同时框架还提供了辅助编写 DDP 安全脚本的工具函数。run_on_main只在主进程执行一次run_on_main确保某个函数只在唯一的进程中执行一次其余进程会阻塞等待。它最典型的用途是配方中的数据准备步骤——例如切分数据集、生成 CSV 清单这些操作只需也应当执行一次否则多个进程会互相冲突、重复写入。其完整签名见 speechbrain/utils/distributed.py为run_on_main( func, # 只在主进程执行的函数 argsNone, # func 的位置参数 kwargsNone, # func 的关键字参数 post_funcNone, # 主进程 func 执行完后在非主进程上运行的函数默认 None post_argsNone, post_kwargsNone, run_post_on_mainFalse, # 是否也在主进程上运行 post_func默认 False )从源码实现看run_on_main的执行链路是用main_process_only(func)装饰器包裹目标函数——在主进程rank 0上真实执行非主进程直接返回None随后调用ddp_barrier()同步所有进程确保主进程完成后再放行如果提供了post_func则默认在非主进程上执行它例如各进程从主进程生成的清单文件加载数据再次ddp_barrier()同步。底层装饰器main_process_only在非分布式场景下会直接执行原函数因此单卡与多卡共用同一份代码无需条件分支。真实配方中的用法在仓库各配方中可以大量看到这一模式例如 recipes/AISHELL-1/ASR/seq2seq/train.py 中数据准备阶段run_on_main( aishell_prepare, kwargs{ data_folder: hparams[data_folder], save_folder: hparams[save_folder], train_splits: hparams[train_splits], split_ratio: hparams[split_ratio], skip_prep: hparams[skip_prep], }, )类似的用法遍布 recipes/AISHELL-1/ASR/transformer/train.py、recipes/AMI/Diarization/experiment.py、recipes/CoVoST/AST/train.py 等配方中。Brain.fit等核心方法天生 DDP-awareSpeechBrain 的Brain.fit、evaluate等方法在设计时就考虑了 DDP训练循环、指标汇总、checkpoint 保存等环节均能感知分布式状态。因此对使用者而言多数情况下你几乎不需要额外修改代码只需保持DDP 安全的意识即可。实际从speechbrain/core.py源码可以看到Brain在_wrap_distributed()中会在distributed_launch为真时自动将各模块包装为torch.nn.parallel.DistributedDataParallel并为gloo后端与nccl后端分别处理device_idsspeechbrain/core.py。单节点多卡训练torchrun --standalone单节点场景指在一台机器上使用多张 GPU拆分训练。使用 SpeechBrain 时标准做法是用 PyTorch 自带的启动器torchruncd recipes/dataset/task/ torchrun --standalone --nproc_per_node4 experiment.py hyperparams.yaml参数说明--standalone单机模式自动假定只有当前一个节点--nproc_per_node4每个节点派生 4 个进程即使用 4 张 GPU按你的实际卡数调整后面照常跟配方入口脚本如experiment.py、train.py与超参数文件如hyperparams.yaml。torchrun会自动为每个子进程设置LOCAL_RANK与RANK环境变量SpeechBrain 的Brain会通过speechbrain.utils.distributed中的get_local_rank()/get_rank()读取这些变量见 speechbrain/utils/distributed.py并将每个进程绑定到对应的 CUDA 设备上infer_device()会把LOCAL_RANK映射为cuda:local_rank。因此你不需要手动处理设备分配。多节点训练手动配置torchrun参数多节点场景指在多台机器上协同训练每台机器可以拥有任意数量的 GPU。需要特别提醒跨节点使用 DDP 会引入通信开销如果节点间网络带宽不足训练可能比单节点还慢因此在部署前务必评估网络速度并实际观测多机扩展是否真正带来了收益。另外DDP 对服务器环境较为敏感某些集群配置可能触发难以预料的 bug遇到问题时优先排查 PyTorch 版本与网络连通性。基础手动配置示例2 节点 × 2 GPU以两台机器、每台 2 张 GPU共 4 卡为例。你需要分别在每台机器上各运行一次torchrun关键参数如下--nproc_per_node2每个节点派生 2 个进程对应 2 张 GPU--nnodes2总共使用 2 个节点--node_rank0/--node_rank1给本节点指定的排名/索引--master_addr/--master_port主控机器的 IP 与端口。这里任意指定第一台机器为主控其余机器第二台连接它。端口5555是示例值若该端口被同节点上其他进程占用例如同时跑多个训练任务需要换一个空闲端口。Machine 1主控cd recipes/dataset/task/ torchrun --nproc_per_node2 --nnodes2 --node_rank0 --master_addr machine_1_address --master_port 5555 experiment.py hyperparams.yamlMachine 2cd recipes/dataset/task/ torchrun --nproc_per_node2 --nnodes2 --node_rank1 --master_addr machine_1_address --master_port 5555 experiment.py hyperparams.yaml在这个 2×2 拓扑下全局 rank 分配如下Machine 1 的 2 个子进程local_rank0, rank0local_rank1, rank1Machine 2 的 2 个子进程local_rank0, rank2local_rank1, rank3其中local_rank是进程在本节点内的编号对应本机 GPUrank是全局编号。使用torchrun时这些环境变量由启动器自动设置无需手工干预。多节点 Slurm 集群SBATCH SRUN 双脚本自动化如果训练环境是使用 Slurm 调度器的计算集群可以自动化上面的手动流程。官方给出了双脚本方案一个SBATCH 脚本负责向调度器申请节点资源并调用第二个脚本一个SRUN 脚本在每个节点上执行实际训练。sbatch.sh资源申请与任务分发#SBATCH --nodes2 # We want two nodes (servers) #SBATCH --ntasks-per-node1 # we will run once the next srun per node #SBATCH --gresgpu:4 # we want 4 GPUs per node #SBATCH --job-nameSBisSOcool #SBATCH --cpus-per-task10 # the only task will request 10 cores #SBATCH --time20:00:00 # Everything will run for 20H. # We jump into the submission dir cd ${SLURM_SUBMIT_DIR} # And we call the srun that will run --ntasks-per-node times (once here) per node srun srun_script.shsrun_script.sh节点内实际训练入口#!/bin/bash # We jump into the submission dir cd ${SLURM_SUBMIT_DIR} # We activate our env conda activate super_cool_sb_env # We extract the master node address (the one that every node must connects to) LISTNODESscontrol show hostname $SLURM_JOB_NODELIST MASTERecho $LISTNODES | cut -d -f1 # here --nproc_per_node4 because we want torchrun to spawn 4 processes (4 GPUs). # Then we give the total amount of nodes requested (--nnodes) and then # --node_rank that is necessary to dissociate the node that we are calling this from. torchrun --nproc_per_node4 --nnodes${SLURM_JOB_NUM_NODES} --node_rank${SLURM_NODEID} --master_addr${MASTER} --master_port5555 train.py hparams/myrecipe.yaml关键点解读srun会根据--nodes与--ntasks-per-node在每个节点上执行一次srun_script.sh通过scontrol show hostname $SLURM_JOB_NODELIST取出节点主机名列表cut取第一个作为主控地址MASTER--nnodes${SLURM_JOB_NUM_NODES}与--node_rank${SLURM_NODEID}直接从 Slurm 环境变量读取总节点数与当前节点编号实现自动化的多节点启动。多节点分离文件系统run_once_per_node与 checkpoint 保存默认的run_on_main假定所有进程共享同一文件系统checkpoint 只保存一次即可全局可见。但在多节点各自拥有独立文件系统的集群上不同节点看不到彼此的磁盘此时需要在每个节点上各保存一份checkpoint。SpeechBrain 为此提供了与run_on_main平行的函数run_once_per_node同样定义于 speechbrain/utils/distributed.py。它与run_on_main的差异在于run_on_main只在全局 rank 0唯一进程上执行run_once_per_node在每个节点的local_rank0进程上各执行一次其他进程阻塞等待。源码中对应的装饰器once_per_node通过is_local_rank_zero()即local_rank 0判断本进程是否为本节点主进程与main_process_only的全局rank 0判断形成对照。checkpoint 便捷函数为了让 checkpoint 保存行为自动适配每节点一次SpeechBrain 在 speechbrain/utils/checkpoints.py 中提供了便捷函数speechbrain.utils.checkpoints.convert_torch_save_hooks_to_once_per_node()调用该函数后DEFAULT_SAVE_HOOKS中所有指向torch_save被main_process_only装饰的保存钩子会被替换为torch_save_once_per_node被once_per_node装饰。二者实现几乎一致都是obj.state_dict()后torch.save区别只在于执行范围前者全局只保存一次后者每个节点各保存一次。因此保存会在每个节点上都发生一次保证任何节点都能从本机文件系统恢复训练。已弃用DataParallel 单节点多卡不推荐使用官方强烈不建议使用torch.nn.DataParallel即使是单节点场景原因包括SpeechBrain 已不再为DataParallel提供支持未来版本的 PyTorch 甚至可能彻底移除DataParallelDDP 在梯度同步与负载均衡上全面优于 DP。如需了解历史用法仅供旧脚本迁移参考其调用方式是给配方脚本追加--data_parallel_backend标志cd recipes/dataset/task/ python experiment.py params.yaml --data_parallel_backend如果只想使用特定的 GPU 设备可用CUDA_VISIBLE_DEVICES环境变量限定cd recipes/dataset/task/ CUDA_VISIBLE_DEVICES1,5 python experiment.py params.yaml --data_parallel_backend重要注意DataParallel 模式下每张 GPU 的实际 batch size 为batch_size / GPU 数因此需要根据卡数相应放大batch_size配置。源码层验证SpeechBrain 的分布式参数与初始化为了帮助读者彻底理解上述命令与框架的关系这里给出几条源码证据链运行参数run_optsSpeechBrain 的分布式相关命令行参数集中定义在 speechbrain/utils/run_opts.pydistributed_backend取值{nccl, gloo, mpi}默认nccldata_parallel_backend布尔标志启用 DataParallel 训练distributed_launch布尔标志表明是否由torchrun等分布式启动器拉起find_unused_parametersDDP 下是否检测未使用参数禁用可规避部分报错。DDP 初始化与设备绑定speechbrain.utils.distributed.ddp_init_group(run_opts)见 speechbrain/utils/distributed.py会从环境变量RANK/LOCAL_RANK依次还兼容SLURM_PROCID、JSM_NAMESPACE_RANK读取进程身份校验所选后端nccl/gloo/mpi在当前环境可用对nccl后端执行torch.cuda.set_device(cuda:local_rank)将进程绑定到对应 GPU调用torch.distributed.init_process_group完成分布式组初始化超时默认 7200 秒。Brain类会在训练前自动完成 DDP 组初始化并在_wrap_distributed阶段speechbrain/core.py将可训练模块包装为SyncBatchNorm DistributedDataParallelnccl后端传入device_ids[self.device]gloo后端传device_idsNone。DDP 数据采样在 speechbrain/core.py 的make_dataloader中可以看到当distributed_launch为真且数据集不是IterableDataset时SpeechBrain 会自动创建DistributedSampler来切分数据这正是DDP 下每个进程看到不同数据子集的实现保证。总结与最佳实践场景推荐启动方式关键参数单机多卡torchrun --standalone --nproc_per_nodeN ...--nproc_per_node等于卡数多机多卡手动每台机器各跑一次torchrun--nnodes、--node_rank、--master_addr、--master_port多机多卡SlurmSBATCH 申请资源 SRUN 执行训练复用SLURM_JOB_NUM_NODES、SLURM_NODEID环境变量分离文件系统在run_on_main处改用run_once_per_node并调用convert_torch_save_hooks_to_once_per_node()每个节点local_rank0各执行一次单机多卡旧python ... --data_parallel_backend已弃用batch size 按卡数切分核心要点回顾优先 DDP为每个 GPU 派生独立进程配合 NCCL 完成同步batch size 按每卡定义保持 DDP 安全数据准备等只需执行一次的操作用run_on_main包起来官方配方开箱即用多节点先评估网络通信开销可能抵消扩展收益务必实测分离文件系统用run_once_per_node配合convert_torch_save_hooks_to_once_per_node()让每个节点各自落盘 checkpoint远离 DataParallel官方已弃用且不再提供支持。后续如需深入了解可继续阅读仓库中的 docs/experiment.md实验运行指南与 docs/multigpu.md本文档原文并对照 speechbrain/utils/distributed.py 与 speechbrain/core.py 的源码理解分布式机制的完整实现。【免费下载链接】speechbrainA PyTorch-based Speech Toolkit项目地址: https://gitcode.com/GitHub_Trending/sp/speechbrain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →