实现多GPU分布式训练是扩展深度学习模型规模、缩短训练时间的核心手段。目前的实现方案主要分为数据并行、模型并行以及混合并行。
以下是具体的实现逻辑、主流框架用法及代码示例。
在动手写代码前,需要先明确你的瓶颈在哪里:
这是最常用的方法。每张 GPU 持有模型的一份完整副本,数据被切分,每张卡处理不同的 mini-batch,最后汇总梯度更新。
方法 A:DataParallel (DP) —— 不推荐
- 原理:单机多卡。主卡(GPU 0)负责汇总梯度、更新参数,再将新参数广播给其他卡。
- 缺点:主卡负载过重,负载不均,通信效率低。仅适合快速实验。
import torch.nn as nn
model = nn.DataParallel(model) # 一行代码,但不推荐
方法 B:DistributedDataParallel (DDP) —— 强烈推荐
- 原理:每个进程独立运行在单张 GPU 上,每张卡地位平等,通过 Ring AllReduce 同步梯度。推荐单机多卡或多机多卡都使用 DDP。
- 优点:通信效率高,几乎线性加速。
DDP 完整代码模板:
import torch
import torch.distributed as dist
import torch.multiprocessing as mp
import torch.nn as nn
import torch.optim as optim
from torch.nn.parallel import DistributedDataParallel as DDP
def setup(rank, world_size):
# 初始化进程组,使用 NCCL 后端(GPU 通信最快)
dist.init_process_group("nccl", rank=rank, world_size=world_size)
def cleanup():
dist.destroy_process_group()
def train(rank, world_size):
setup(rank, world_size)
# 1. 模型
model = nn.Linear(10, 10).to(rank)
ddp_model = DDP(model, device_ids=[rank])
# 2. 损失与优化器
loss_fn = nn.MSELoss()
optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)
# 3. 数据采样器:必须使用 DistributedSampler
dataset = torch.randn(100, 10)
sampler = torch.utils.data.distributed.DistributedSampler(dataset, num_replicas=world_size, rank=rank)
dataloader = torch.utils.data.DataLoader(dataset, batch_size=32, sampler=sampler)
for epoch in range(10):
# 每个 epoch 开始时打乱数据顺序
sampler.set_epoch(epoch)
for data in dataloader:
data = data.to(rank)
optimizer.zero_grad()
output = ddp_model(data)
loss = loss_fn(output, data)
loss.backward()
optimizer.step()
cleanup()
if __name__ == "__main__":
world_size = torch.cuda.device_count() # 假设有 4 张卡
mp.spawn(train, args=(world_size,), nprocs=world_size, join=True)
当模型参数量超过单卡显存时(例如训练一个 70B 的 LLM),必须把模型切分到不同的 GPU 上。
用 PyTorch 将不同的层手动放在不同的卡上:
import torch
import torch.nn as nn
class Stage0(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 10).to('cuda:0')
def forward(self, x):
return self.linear(x.to('cuda:0'))
class Stage1(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(10, 10).to('cuda:1')
def forward(self, x):
return self.linear(x.to('cuda:1'))
stage0 = Stage0()
stage1 = Stage1()
# 前向传播
x = torch.randn(32, 10)
h = stage0(x) # 在 GPU 0 计算
out = stage1(h) # 将结果传到 GPU 1 计算
这种方式通常较慢,因为数据在 GPU 之间频繁搬运,一般建议直接使用专门框架。
ZeRO 是微软 DeepSpeed 库的核心技术,目前训练大模型的标配。它属于数据并行的升级版,旨在解决数据并行中"每张卡都存完整优化器状态"导致的显存冗余。
不需要像 DDP 那样手动写进程管理代码。
Step 1: 编写 DeepSpeed 配置文件 ds_config.json
{
"train_batch_size": 64,
"gradient_accumulation_steps": 1,
"zero_optimization": {
"stage": 2,
"allgather_partitions": true,
"reduce_scatter": true,
"overlap_comm": true,
"contiguous_gradients": true
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": 0.0001,
"betas": [0.9, 0.999],
"eps": 1e-8
}
},
"fp16": {
"enabled": true
}
}
Step 2: 修改 Python 代码
import deepspeed
import torch.nn as nn
model = nn.Linear(10, 10)
# 初始化 DeepSpeed 引擎
model_engine, optimizer, _, _ = deepspeed.initialize(
model=model,
model_parameters=model.parameters(),
config_params="ds_config.json"
)
# 训练循环
for data in dataloader:
# 前向传播
outputs = model_engine(data)
loss = loss_fn(outputs, data)
# 反向传播
model_engine.backward(loss)
# 更新参数
model_engine.step()
Step 3: 启动命令
deepspeed --num_gpus=4 train.py
如果不想从头写分布式逻辑,可以使用封装好的框架。
它底层自动处理 DDP、DeepSpeed、模型并行等策略,只需指定参数。
from pytorch_lightning import Trainer, LightningModule
class LitModel(LightningModule):
def training_step(self, batch, batch_idx):
# 定义逻辑
return loss
# 使用 4 张 GPU + DDP 策略
trainer = Trainer(accelerator="gpu", devices=4, strategy="ddp")
trainer.fit(model)
# 使用 DeepSpeed 策略
trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2")
trainer.fit(model)
用于 LLM 训练非常便捷。
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./results",
per_device_train_batch_size=8,
gradient_accumulation_steps=2,
deepspeed="ds_config.json", # 直接传入 DeepSpeed 配置
fp16=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
)
trainer.train()
| 你的需求 | 推荐方案 | 工具 | 复杂度 |
|---|---|---|---|
| 模型不大(<1B),想加速训练 | DDP | PyTorch 原生 | 低 |
| 单机多卡,快速验证代码 | DataParallel | PyTorch 原生 | 极低(但不推荐生产使用) |
| 模型较大(1B-10B),显存不足 | ZeRO-2 / ZeRO-3 | DeepSpeed | 中 |
| 模型巨大(>100B),多机多卡 | 混合并行(张量+流水线+ZeRO) | Megatron-DeepSpeed | 高 |
| 不想写底层逻辑,快速训练 | Trainer / Lightning | HF / PyTorch Lightning | 低 |
建议: 如果你刚开始接触,优先使用 PyTorch 的 DDP 或者 Hugging Face 的 Trainer 配合 DeepSpeed。
评论专区
评论加载中...登录后即可发表评论