数据并行下每卡批次大小怎么调?核心思路是:先跑通单卡最优的per-gpu batch size,再按卡数线性扩展成global batch size;显存不够时用梯度累积缩减每卡batch,同时按比例调学习率。
数据并行下每卡批次大小怎么调?先分清global和local
很多人调参时只盯着一个batch size,其实分布式训练里这个参数有两层,global batch size是指一个训练步骤里所有GPU加起来处理的样本总数,per-gpu batch size是单张卡实际吃进去的样本数,两者之间夹着一个梯度累积(gradient accumulation)的变量。
具体关系就一条公式:
- 无梯度累积时:global batch size = per-gpu batch size × GPU数
- 有梯度累积时:global batch size = per-gpu batch size × 梯度累积步数 × GPU数
不少同学在8卡机器上把每卡batch设为64,以为等效于单卡batch 512,实际上没算对梯度累积那部分,行业共识认为,调参前先把这三者的换算关系写清楚,比直接跑实验更省时间(据PyTorch官方文档对DistributedDataParallel的说明)。
区别在哪?global batch size决定模型收敛的轨迹和梯度噪声,per-gpu batch size决定单卡的计算效率和显存压力,两者职责不同,混为一谈就容易出现“全局看着没问题、单卡已经OOM”的局面。
| 参数维度 | per-gpu batch size | global batch size |
|---|---|---|
| 主要影响 | 显存占用、单卡计算效率 | 收敛方向、梯度噪声 |
| 调整手段 | 直接改数据加载器、混合精度 | 改卡数或梯度累积步数 |
| 常见误区 | 设太大导致OOM | 设太小导致收敛不稳 |
每卡batch size和global batch size,到底有什么区别?
这个问题的本质是控制域不同。
per-gpu batch size是每一张卡自己处理多少样本,它受限于显存大小、算子实现方式、数据加载速度,同一张A100上跑ResNet-50,per-gpu batch设256可能刚好打满算力,设512显存就爆了,这个值只和单卡硬件能力有关,和集群规模无关。
global batch size是整个step消耗的样本总量,它决定了梯度的准确度和训练的动态,batch太大,梯度方向过于平滑,模型容易收敛到尖锐极小值;batch太小,梯度噪声大,训练不稳定,业内专家指出,相当一部分大模型训练团队把global batch size控制在几万到几十万量级,注意,这是针对大模型场景,普通视觉任务几千就够。
实际调参中,很多人被“每卡批次”绊住是因为没有意识到:改GPU数量会同时改变global batch size,从4卡扩到8卡,如果保持per-gpu batch不变,global batch就翻倍了,那学习率不跟着调整,loss曲线大概率会飘。
实操:数据并行下每卡批次大小调节三步法
第一步:单卡跑基准,找到拐点
不要一上来就开多卡,先在单卡上把batch从小往大加,记录每个batch下GPU利用率和每秒处理样本数,你会发现吞吐量先上升后走平,那个走平的拐点就是最优per-gpu batch size的参考值,实际使用中可以比拐点略小一点,留出显存余量。
具体操作路径:
- 用
nvidia-smi观察显存占用率,保持在90%以下 - 用
torch.cuda.max_memory_allocated()查看峰值显存 - 训练日志里记录
samples/sec,画散点图找拐点
第二步:按卡数扩展,同步调整学习率
单卡基准batch是B,卡数是N,那么global batch size ≈ B × N,学习率按线性缩放法则调整:新学习率 = 原学习率 ×(新global batch / 原global batch),这个在训练初期尤其重要,warmup步数最好也按比例增加。
第三步:显存不够时用梯度累积兜底
如果B × N超出显存承受范围,优先保证global batch size不变,然后把per-gpu batch size降下来,用梯度累积步数补齐,例如global batch目标是1024,8卡场景下每卡原本要设128,但显存只跑得动64,那就把梯度累积步数设为2:
- per-gpu batch size:64
- 梯度累积步数:2
- 有效global batch:64 × 2 × 8 = 1024
这样收敛行为接近原设置,单卡压力减小一半。
大模型训练多卡batch size和learning rate怎么联动?
多卡训练里,batch size和learning rate从来不是独立变量,线性缩放法则在batch翻倍时把学习率也翻倍,但要注意两个前提。
第一个前提是warmup。 学习率翻倍后直接进入训练,loss容易炸,需要先跑一段warmup,从极小学习率逐步升到目标值,warmup步数通常设置为总训练步数的1%到5%,根据模型规模酌情调整。
第二个前提是batch的临界规模。 学习率缩放并没有上限,当global batch size超过一定阈值时,纯粹线性增大学习率并不能带来等价收益,业界在语言模型训练中观察到,batch太大时模型收敛变慢,这时需要配合更复杂的调度策略,比如学习率预热后衰减、或者周期性地重置。
建议的调节路径:
- 修改模型的batch size参数
- 同步修改优化器的初始学习率
- 调整warmup步数和调度器中的总步数
- 跑短 epoch 验证loss下降速度和稳定性
多卡训练遇到OOM,每卡batch size怎么降?
OOM是分布式训练最常见的翻车点,遇到OOM,先别急着把batch降一半,按照优先级排查。
第一步:确认是显存不够还是内存碎片化。 用nvidia-smi看内存分配情况,如果显存剩余很多但分配失败,问题出在PyTorch缓存分配器上,可以尝试torch.cuda.empty_cache()或设置PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128。
第二步:确认激活值占用的显存比例。 开混合精度训练,激活值显存消耗能减少接近一半,在PyTorch里加上:
scaler = torch.cuda.amp.GradScaler()
配合autocast使用,模型参数和中间激活都会降精度存储。
第三步:以上都调完还OOM,再动per-gpu batch size。 把每卡batch size减半,同时梯度累积步数乘以2,保证global batch不变,注意动态batch在数据加载器里要支持自动调整长度,避免epoch步数不整除。
如果只想快速看看当前设置能否跑通,先跑一个batch前向传播和反向传播,观察显存峰值,低于卡上限的
80%才比较稳妥。
怎么验证调节后的效果?
调完不是就算了,验证维度至少有四个。
- GPU利用率:用
nvidia-smi dmon或nsys看SM利用率,稳定在90%以上才算充分压榨硬件 - 吞吐量:记录每秒处理样本数,多卡扩展后应接近线性增长,8卡提升不到6倍,说明通信开销拖了后腿
- 显存余量:每卡峰值显存不超过物理显存的90%
- 收敛速度:对比global batch相同情况下,per-gpu batch不同方案的loss下降曲线,差距应很小
如果梯度累积后吞吐下降明显,说明累积步数过多,通信时间被放大,此时考虑增大per-gpu batch配合减少累积步数,或者调优通信后端。
每卡批次大小的调节核心是围绕资源边界和收敛等价性做权衡,动手前先把公式写清楚,动手后盯住收敛曲线和显存占用,基本不会跑偏。
关于数据并行每卡批次大小,几个常见问题
Q1:数据并行时每卡batch size为什么要设成一样?
DDP在反向传播时需要同步各卡梯度,如果每卡batch不一样,梯度含义就不同,averaged梯度没有统计一致性,实际框架里每卡按相同local batch划分数据,就是为了让梯度同步有数学意义。
Q2:我只有2张卡,每卡batch设多少合适?
先测单卡最优batch,然后直接套线性扩展,假设单卡最优batch是64,两张卡per-gpu还是64,global batch变128,学习率从0.01调到0.02(按线性缩放),如果显存紧张,per-gpu降到48,梯度累积步数设为4/3不可行,改为per-gpu 32加梯度累积2步,等效global仍然128。
Q3:每卡batch设得特别小,比如1或2,会有什么后果?
单卡计算效率会很低,GPU大部分时间浪费在kernel启动和内存搬运上,吞吐量大幅下降,同时梯度噪声增大,模型收敛大概率变慢,这种情况下优先增大per-gpu batch,或者用梯度累积弥补的效率损失。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/624899.html





