大模型训练面临的核心瓶颈是显存容量。单张A100 80GB难以容纳百亿参数模型的完整训练状态,分布式训练成为必选项。DeepSpeed的ZeRO(Zero Redundancy Optimizer)通过将优化器状态、梯度和参数分片到多个GPU上,大幅降低单卡显存占用,使百亿甚至千亿参数模型的训练成为可能。
DeepSpeed ZeRO三个阶段的显存优化原理
ZeRO将训练过程中的显存消耗分解为三部分:优化器状态、梯度和模型参数。标准数据并行(DP)下每张卡都完整保存这三部分,造成大量冗余。ZeRO分三个阶段逐步消除冗余:
ZeRO-1:仅分片优化器状态(如Adam的一阶/二阶矩)。以1.5B参数模型为例,Adam优化器状态约占24GB显存(参数量的16倍),分片到8卡后每卡仅需3GB,显存节省显著但梯度与参数仍全量复制。
ZeRO-2:在ZeRO-1基础上额外分片梯度。梯度分片后,每张卡只保留对应参数分片的梯度,通信模式从all-reduce变为reduce-scatter,通信量不变但显存进一步降低。
ZeRO-3:将参数也分片,彻底消除冗余。每张卡只持有完整模型参数的1/N。前向和反向传播时通过all-gather临时聚合所需参数层,用完即释放。ZeRO-3的通信量约为标准数据并行的1.5倍,但显存占用降至1/N。
DeepSpeed配置文件编写与启动参数
DeepSpeed通过JSON配置文件控制ZeRO行为,下面是一个典型的ZeRO-2配置:
{
"train_micro_batch_size_per_gpu": 4,
"gradient_accumulation_steps": 4,
"optimizer": {
"type": "AdamW",
"params": {
"lr": 2e-5,
"weight_decay": 0.01
}
},
"fp16": {
"enabled": true,
"loss_scale": 0,
"loss_scale_window": 1000,
"initial_loss_scale": 65536
},
"zero_optimization": {
"stage": 2,
"allgather_partitions": true,
"reduce_scatter": true,
"contiguous_gradients": true,
"overlap_comm": true
}
}
关键参数说明:stage控制ZeRO阶段;overlap_comm开启计算与通信重叠,利用GPU流水线隐藏通信延迟;contiguous_gradients将梯度存储为连续内存块,减少碎片化并提升NCCL集合通信效率。
ZeRO-3配置需要额外指定参数分片策略:
"zero_optimization": {
"stage": 3,
"overlap_comm": true,
"contiguous_gradients": true,
"sub_group_size": 1e9,
"reduce_bucket_size": 5e8,
"stage3_prefetch_bucket_size": 5e8,
"stage3_param_persistence_threshold": 1e5
}
reduce_bucket_size控制梯度聚合的桶大小,增大桶可减少通信次数但增加显存占用;stage3_prefetch_bucket_size控制前向传播时预取参数的桶大小,合理设置可隐藏all-gather延迟。
与HuggingFace Transformers集成训练
通过Trainer类的deepspeed参数加载配置文件即可启动分布式训练:
from transformers import TrainingArguments, Trainer, AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
training_args = TrainingArguments(
output_dir="./output",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-5,
fp16=True,
deepspeed="ds_zero2_config.json",
logging_steps=50,
save_steps=500,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
tokenizer=tokenizer,
)
trainer.train()
启动训练使用deepspeed命令行工具,指定GPU数量和配置:
deepspeed --num_gpus=8 train.py --deepspeed ds_zero2_config.json
ZeRO-3中的激活检查点与CPU Offload
ZeRO-3配合激活检查点(Activation Checkpointing)可进一步降低显存。激活检查点在前向传播时不保存中间激活值,反向传播时重新计算,用计算换显存:
model.gradient_checkpointing_enable()
model.config.use_cache = False # 训练时关闭KV Cache
对于超大模型,DeepSpeed还支持ZeRO-Offload,将优化器状态和梯度卸载到CPU内存,利用CPU大容量内存承载训练状态:
"zero_optimization": {
"stage": 2,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
}
}
pin_memory开启CPU页锁定内存,加速CPU-GPU之间的数据传输。ZeRO-Offload的代价是CPU计算优化器步骤的时间开销,适合显存极度受限但CPU核心充足的场景。
通信瓶颈排查与性能调优
分布式训练的性能瓶颈通常在通信。几个排查方向:
NCCL通信延迟:设置NCCL_DEBUG=INFO查看通信日志,确认是否选择了最优的通信后端。多机训练时确保InfiniBand驱动正常工作:
export NCCL_DEBUG=INFO
export NCCL_IB_DISABLE=0 # 确保使用IB
export NCCL_NET_GDR_LEVEL=2 # 启用GPU Direct RDMA
负载不均衡:ZeRO-3下各卡的参数分片大小可能不均,导致部分卡等待。通过stage3_param_persistence_threshold控制小参数不被分片,减少碎片化通信。
梯度聚合桶大小:过小的bucket导致频繁通信启动开销,过大则占用显存。一般建议从500MB起步,根据NCCL带宽和延迟调整。可通过nvidia-smi dmon监控GPU利用率,理想情况下应保持在80%以上。
混合精度训练的Loss Scale策略
FP16训练容易出现梯度下溢,DeepSpeed提供动态Loss Scale机制。配置中的loss_scale设为0表示动态调整,initial_loss_scale设为65536(2^16)作为初始值。当连续loss_scale_window步未出现溢出时,Loss Scale翻倍;出现溢出则减半。如果训练过程中频繁出现NaN,可适当降低初始Loss Scale或切换到BF16:
"bf16": {
"enabled": true
}
BF16具有与FP32相同的动态范围但精度较低,Ampere及以上架构的GPU原生支持BF16计算,不存在梯度下溢问题,不需要Loss Scale。对于H100/A100等架构,BF16通常是比FP16更稳妥的选择。
多节点训练的注意事项
多机训练需要确保所有节点的网络互通且SSH免密配置正常。DeepSpeed的hostfile指定节点信息:
worker-01 slots=8
worker-02 slots=8
启动命令:
deepspeed --hostfile=hostfile --num_nodes=2 --num_gpus=8 train.py
多机场景下的主要瓶颈是跨节点网络带宽。如果没有InfiniBand,以太网环境下建议使用RoCE(RDMA over Converged Ethernet)或至少100Gbps网络。可以通过NCCL_NET环境变量控制网络插件选择。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/da-mo-xing-fen-bu-shi-xun-lian-shi-zhan-deepspeedzero-bing/