十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PyTorch 训练流程优化与分布式训练实践:这些看似聪明的做法别照搬

PyTorch 训练流程优化与分布式训练实践:这些看似聪明的做法别照搬 PyTorch 训练流程优化与分布式训练实践这些看似聪明的做法别照搬本文围绕“这些看似聪明的做法别照搬”整理检查要点。示例仅用于说明方法请以公开、合成或已脱敏输入复跑。1. 先固定讨论边界训练问题应拆成数值正确性、数据供给、显存使用和通信行为四部分。先以小规模、固定输入验证前向和反向结果再观察多进程路径避免把单一监控值当成整体结论。报告应列明本次训练没有覆盖的条件换了数据或启动方式就重新验证。2. 按最小闭环验证每次试验都应写清框架版本、设备类型、批量形状、随机种子和启动方式。发生偏差时优先比较中间张量与梯度而不是直接调整并行参数。把数值断言、配置快照和关键张量摘要放在同一份实验记录中复查时更容易定位。3. 参考实现与图示# 常见的死锁与 CPU 性能瓶颈写法 class BadDataset(Dataset): def __getitem__(self, idx): # 错误 1PIL 解压单线程效率低下导致主进程等待 IO img Image.open(self.img_paths[idx]).convert(RGB) # 错误 2直接在 CPU Worker 中做昂贵的 CPU Augmentation img self.transforms(img) return imgloss criterion(output, target) # 致命隐患为了打记录打印 loss 值强行触发了 CPU-GPU 同步 current_loss loss.item() if current_loss 10.0: logger.warning(Loss Exploded!)# 盲目复制官方 AMP 导致的 Loss 变为 NaN 异常 scaler torch.cuda.amp.GradScaler() for input, target in dataloader: optimizer.zero_grad() with torch.cuda.amp.autocast(): output model(input) loss criterion(output, target) # 错误做法没有在 step 前检查 scaler 状态就强行做 unscale scaler.scale(loss).backward() # 如果梯度中出现了 Inf/NaNtorch.nn.utils.clip_grad_norm_ 会计算出 NaN 梯度 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) scaler.step(optimizer) scaler.update()import os import torch import torch.nn as nn import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP from torch.utils.data import DataLoader, Dataset, DistributedSampler class CUDAPrefetcher: 异步 CUDA 预取器 利用独立的 CUDA Stream 在 GPU 执行当前 Step 算子时并行将下一个 Batch 数据从 CPU Host 搬运至 GPU 显存 def __init__(self, loader, device): self.ori_loader loader self.loader iter(loader) self.device device self.stream torch.cuda.Stream() self.next_input None self.next_target None self.preload() def preload(self): try: self.next_input, self.next_target next(self.loader) except StopIteration: self.next_input None self.next_target None return with torch.cuda.stream(self.stream): self.next_input self.next_input.to(self.device, non_blockingTrue) self.next_target self.next_target.to(self.device, non_blockingTrue) def next(self): torch.cuda.current_stream().wait_stream(self.stream) input self.next_input target self.next_target if input is not None: input.record_stream(torch.cuda.current_stream()) if target is not None: target.record_stream(torch.cuda.current_stream()) self.preload() return input, target class DummyDataset(Dataset): def __init__(self, size1000): self.size size def __len__(self): return self.size def __getitem__(self, idx): # 模拟产生的数据 return torch.randn(128, 512), torch.randint(0, 10, (128,)) def setup_ddp(): 初始化分布式环境 dist.init_process_group(backendnccl) local_rank int(os.environ[LOCAL_RANK]) torch.cuda.set_device(local_rank) return local_rank def train_production_loop(): local_rank setup_ddp() device torch.device(fcuda:{local_rank}) # 1. 初始化模型与 DDP 包装 model nn.Sequential( nn.Linear(512, 256), nn.ReLU(), nn.Linear(256, 10) ).to(device) model DDP(model, device_ids[local_rank]) dataset DummyDataset() sampler DistributedSampler(dataset) # num_workers 不宜过大通常设为每个 GPU 分配 2~4 个 CPU 核心即可 loader DataLoader( dataset, batch_size32, samplersampler, num_workers4, pin_memoryTrue, # 配合 non_blockingTrue drop_lastTrue ) optimizer torch.optim.AdamW(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() scaler torch.cuda.amp.GradScaler(enabledTrue) # 累加 Tensor 用于无同步记录记录 running_loss_tensor torch.zeros(1, devicedevice) log_interval 20 model.train() for epoch in range(2): sampler.set_epoch(epoch) prefetcher CUDAPrefetcher(loader, device) input, target prefetcher.next() step 0 while input is not None: optimizer.zero_grad(set_to_noneTrue) # set_to_noneTrue 节省显存带宽 # 前向传播使用 AMP 自动混合精度 with torch.cuda.amp.autocast(dtypetorch.float16): output model(input) loss criterion(output, target) # 错误纠正安全放大梯度并反向传播 scaler.scale(loss).backward() # 运行安全做法先 unscale再做 Gradient Clipping scaler.unscale_(optimizer) torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) # 更新参数若梯度含 NaN 则内部自动跳过 step scaler.step(optimizer) scaler.update() # 纯 GPU Tensor 累加绝不触发 CPU-GPU 强同步 sync running_loss_tensor loss.detach() step 1 if step % log_interval 0: # 周期性同步一次记录 dist.all_reduce(running_loss_tensor, opdist.ReduceOp.SUM) avg_loss (running_loss_tensor / (log_interval * dist.get_world_size())).item() if local_rank 0: print(f[Epoch {epoch} | Step {step}] 均化 Training Loss: {avg_loss:.4f}) running_loss_tensor.zero_() input, target prefetcher.next() dist.destroy_process_group() if __name__ __main__: # 需使用 python -m torch.distributed.run --nproc_per_node2 script.py 运行 if LOCAL_RANK in os.environ: train_production_loop()4. 复核清单总结“这些看似聪明的做法别照搬”应以清晰的条件和脚本复核。先记录边界再解释结果。
返回列表