Accelerate 与 torchrun 分布式训练LLM对比
Accelerate 与 torchrun 分布式训练LLM对比
1. 概述
在训练大型语言模型(LLM)时,分布式训练是必不可少的技术。本项目中实现了两种不同的分布式训练方式:
- 使用
torchrun
的传统DDP(DistributedDataParallel)方式 - 使用
Accelerate
库的现代化分布式训练方式
2. 启动方式对比
torchrun 方式
# 启动命令示例
torchrun --nproc_per_node 2 train_pretrain.py
Accelerate 方式
# 启动命令示例
python train_pretrain_accelerate.py --use_accelerate
3. 代码层面差异分析
3.1 初始化分布式环境
torchrun方式(train_pretrain.py):
def init_distributed_mode():if not ddp: returnglobal ddp_local_rank, DEVICEdist.init_process_group(backend="nccl")ddp_rank = int(os.environ["RANK"])ddp_local_rank = int(os.environ["LOCAL_RANK"])ddp_world_size = int(os.environ["WORLD_SIZE"])DEVICE = f"cuda:{ddp_local_rank}"torch.cuda.set_device(DEVICE)
Accelerate方式(train_pretrain_accelerate.py):
# Accelerate会自动处理分布式初始化,无需手动编写初始化代码
if args.use_accelerate and ACCELERATE_AVAILABLE:# 创建Accelerator实例accelerator = Accelerator(mixed_precision="bf16" if args.dtype == "bfloat16" else "no",gradient_accumulation_steps=args.accumulation_steps)# 准备模型、优化器和数据加载器model, optimizer, train_loader = accelerator.prepare(model, optimizer, train_loader)
3.2 模型和数据加载器处理
torchrun方式:
# 手动将模型包装为DistributedDataParallel
if ddp:model._ddp_params_and_buffers_to_ignore = {"pos_cis"}model = DistributedDataParallel(model, device_ids=[ddp_local_rank])# 手动处理数据采样器
train_sampler = DistributedSampler(train_ds) if ddp else None
train_loader = DataLoader(train_ds,batch_size=args.batch_size,pin_memory=True,drop_last=False,shuffle=False,num_workers=args.num_workers,sampler=train_sampler
)
Accelerate方式:
# Accelerate自动处理分布式数据并行
train_loader = DataLoader(train_ds,batch_size=args.batch_size,pin_memory=(not (args.use_accelerate and ACCELERATE_AVAILABLE)),drop_last=False,shuffle=(train_sampler is None),num_workers=args.num_workers,sampler=train_sampler
)# 由Accelerator统一准备模型、优化器和数据加载器
model, optimizer, train_loader = accelerator.prepare(model, optimizer, train_loader
)
3.3 损失计算和反向传播
torchrun方式:
with ctx:res = model(X)loss = loss_fct(res.logits.view(-1, res.logits.size(-1)), Y.view(-1)).view(Y.size())loss = (loss * loss_mask).sum() / loss_mask.sum()loss += res.aux_lossloss = loss / args.accumulation_stepsscaler.scale(loss).backward()if (step + 1) % args.accumulation_steps == 0:scaler.unscale_(optimizer)torch.nn.utils.clip_grad_norm_(model.parameters(), args.grad_clip)scaler.step(optimizer)scaler.update()optimizer.zero_grad(set_to_none=True)
Accelerate方式:
with ctx:res = model(X)loss = loss_fct(res.logits.view(-1, res.logits.size(-1)), Y.view(-1)).view(Y.size())loss = (loss * loss_mask).sum() / loss_mask.sum()if hasattr(res, 'aux_loss'):loss += res.aux_loss# Accelerate处理梯度累积和缩放
accelerator.backward(loss)if (step + 1) % args.accumulation_steps == 0:# 梯度裁剪if accelerator.sync_gradients:accelerator.clip_grad_norm_(model.parameters(), args.grad_clip)optimizer.step()optimizer.zero_grad()
3.4 模型保存
torchrun方式:
if isinstance(model, torch.nn.parallel.DistributedDataParallel):state_dict = model.module.state_dict()
else:state_dict = model.state_dict()state_dict = {k: v.half() for k, v in state_dict.items()} # 半精度保存
torch.save(state_dict, ckp)
Accelerate方式:
# 使用Accelerate保存模型
unwrapped_model = accelerator.unwrap_model(model)
state_dict = unwrapped_model.state_dict()
state_dict = {k: v.half() for k, v in state_dict.items()}
torch.save(state_dict, ckp)
4. 主要区别总结
特性 | torchrun + DDP | Accelerate |
---|---|---|
分布式初始化 | 手动调用dist.init_process_group | Accelerator自动处理 |
模型包装 | 手动使用DistributedDataParallel | accelerator.prepare() 自动处理 |
数据加载器 | 手动使用DistributedSampler | accelerator.prepare() 自动处理 |
梯度累积 | 手动实现逻辑 | 内置支持,通过gradient_accumulation_steps 参数 |
混合精度 | 手动使用GradScaler | 通过mixed_precision 参数配置 |
梯度裁剪 | 手动调用clip_grad_norm_ | 使用accelerator.clip_grad_norm_ |
模型保存 | 手动处理DDP模块 | 使用accelerator.unwrap_model() |
学习率调度 | 手动管理 | 可与accelerator.prepare()一起使用 |
多平台支持 | 仅支持PyTorch DDP | 支持DDP、DeepSpeed、FSDP等多种后端 |
5. 优势对比
torchrun + DDP 优势:
- 更直接的控制权,可以精确控制每个分布式训练的细节
- 不依赖额外库,只需要PyTorch
- 在某些特定场景下可能有更好的性能
Accelerate 优势:
- 简化代码:大量分布式训练的复杂性被封装在Accelerator中
- 更好的可移植性:同一套代码可以无缝切换DDP、DeepSpeed、FSDP等后端
- 内置功能丰富:梯度累积、混合精度等都有一致的接口
- 易于调试:可以在单GPU上运行同样的代码进行调试
- 更好的可读性:代码更简洁,逻辑更清晰
6. 总结
- 新项目:推荐使用Accelerate,可以减少大量样板代码,提高开发效率
- 已有项目:如果项目已经稳定运行在DDP上,可以保持现状;如果需要扩展到DeepSpeed等其他后端,建议迁移到Accelerate
- 学习目的:了解DDP原理有助于深入理解分布式训练,但实际项目中使用Accelerate更高效
总的来说,Accelerate提供了一种更现代化、更简洁的方式来处理分布式训练,而传统的torchrun+DDP方式提供了更多的手动控制选项。对于大多数项目,推荐使用Accelerate以提高开发效率和代码可维护性。