DDP
DDP (분산 데이터 병렬)
DistributedDataParallel (DDP)은 각 GPU에 모델의 전체 복사본을 유지해요. 각 GPU는 서로 겹치지 않는 데이터 샤드(shard)에 대해 forward pass와 backward pass를 수행해요. 옵티마이저 단계 전에 all-reduce가 모든 GPU의 그라디언트를 평균해 줘서 모든 모델 복사본이 동일하게 유지되도록 해줘요. 모델이 단일 GPU에 들어갈 수 있다면 DDP를 사용하면 되요.
출처: 문서
본문
┌─────────────────┐
│ training data │
└────────┬────────┘
┌──────────────────┼──────────────────┐
│ shard 0 │ shard 1 │ shard 2
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ model │ │ model │ │ model │
│ (copy 0) │ │ (copy 1) │ │ (copy 2) │
│ GPU 0 │ │ GPU 1 │ │ GPU 2 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ grads │ grads │ grads
└──────────────────┼──────────────────┘
all-reduce
(average gradients)
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ optimizer │ │ optimizer │ │ optimizer │
│ step │ │ step │ │ step │
└─────────────┘ └─────────────┘ └─────────────┘
(identical) (identical) (identical)
Accelerate 같은 멀티프로세스 런처로 실행하면 DDP가 자동으로 활성화돼요.
# 4 GPUs on one machine
accelerate launch --num_processes 4 train.py
DDP 구성하기
이 TrainingArguments를 전달해서 DDP 동작을 제어할 수 있어요.
gradient_accumulation_steps()는 all-reduce를 언제 수행할지 결정해요. Trainer는 중간 누적 단계에서는 all-reduce를 건너뛰고 마지막 micro-batch에서만 실행해요. 예를 들어gradient_accumulation_steps=4라면 all-reduce는 backward pass가 4번 일어날 때마다 실행돼요.~TrainingArguments.ddp_find_unused_parameters는 forward pass가 끝났을 때 autograd 그래프를 순회하면서 그라디언트를 받지 못할 파라미터를 찾아 ready 상태로 표시해서 all-reduce를 막지 않도록 해줘요.gradient_checkpointing()과 함께는 사용하지 마세요. 그라디언트 체크포인팅이 중간 활성화(activation)를 버려두고 필요할 때 다시 계산하기 때문이에요.~TrainingArguments.ddp_bucket_cap_mb는 backward pass 동안 그라디언트를 단일 all-reduce로 묶기 위한 버킷 크기예요. 버킷이 클수록 all-reduce 호출 횟수가 줄고 실행 시작 오버헤드도 작아져요.~TrainingArguments.ddp_broadcast_buffers는 매 forward pass 시작 시 rank 0에서 다른 모든 rank로 모델 버퍼(BatchNorm의 running statistics 같은)를 동기화해요. 모델이 LayerNorm만 사용한다면 비활성화해도 돼요.gradient_checkpointing()과 함께는 사용하지 마세요.~TrainingArguments.ddp_backend는 통신 백엔드를 설정해요. NVIDIA GPU에는"nccl"을 (기본값이자 가장 빠른 옵션), CPU 학습이나 디버깅에는"gloo"를, 그 외 하드웨어에는"xccl","hccl","cncl"을 사용해요.ddp_timeout()은 모든 프로세스와 연산(all-reduce, broadcast)이 완료될 때까지의 시간 제한을 설정해요. 대형 모델을 천천히 로딩할 때처럼 프로세스가 멈추면, timeout이 무한정 막히는 대신 에러를 발생시켜 줘요.
from transformers import TrainingArguments
args = TrainingArguments(
...,
gradient_accumulation_steps=4,
ddp_backend="nccl",
ddp_find_unused_parameters=False,
ddp_bucket_cap_mb=25,
ddp_broadcast_buffers=True,
ddp_timeout=1800,
)
더 알아보기 (Learn more)
- 단일 GPU에 들어가기엔 너무 큰 모델을 학습할 때는 FSDP를 확인해 보세요.
- ZeRO 최적화와 offloading은 DeepSpeed를 확인해 보세요.
- DDP가 어떻게 동작하는지에 대해 더 자세히 알고 싶다면 The Ultra-Scale Playbook의 Data Parallelism 장을 읽어 보세요.