인공지능/공부

Multi-GPU 기본 개념 DDP, Data Parallel, Model Parallel

이게될까 2025. 7. 30. 23:57
728x90
728x90

여러 개의 GPU를 통해 학습을 돌리고 있다.


인공지능에서 Multi-GPU란?


1. 왜 Multi-GPU가 필요한가요?

현대의 딥러닝 모델은 다음과 같은 이유로 엄청난 연산 자원을 요구합니다.

  • 파라미터 수가 수억~수천억 개 (예: GPT, ViT, BERT)
  • 학습 데이터가 수 TB 이상
  • 한 번의 학습에 며칠~몇 주 소요

💡 단일 GPU로는 처리 시간이 길고, 메모리 용량에도 한계가 있습니다. → 이를 해결하기 위해 Multi-GPU 환경이 필요합니다.


2. Multi-GPU란?

Multi-GPU는 하나의 컴퓨터(또는 분산 시스템)에 여러 개의 GPU를 연결하여 연산을 병렬로 수행하는 구조입니다.

  • 목적: 속도 향상 + 메모리 분산 + 대규모 모델 처리
  • 구성: 1대의 머신에 여러 GPU (ex. 4x RTX 3090), 또는 여러 머신(GPU 서버) 연결

3. Multi-GPU의 2가지 주요 방식

구분 Data Parallelism Model Parallelism
개념 동일한 모델을 복제하여 각 GPU에 데이터의 일부를 분산 모델을 파트별로 나누어 서로 다른 GPU에 배치
장점 구현이 쉬움 (PyTorch, HuggingFace 지원) 매우 큰 모델도 처리 가능 (파라미터가 GPU 하나를 초과해도 학습 가능)
단점 모델 크기가 GPU 메모리에 들어가야 함 구현 복잡, 디버깅 어려움
예시 배치 크기 64 → GPU 4개 → 각각 16개씩 분산 처리 GPT의 Transformer Layer 1~6 → GPU 0, Layer 7~12 → GPU 1

4. Multi-GPU의 주요 도구

  • PyTorch
    • nn.DataParallel: 쉬우나 속도 비효율적
    • torch.nn.parallel.DistributedDataParallel (DDP)표준
  • DeepSpeed / Hugging Face Accelerate / FairScale
    • Mixed precision, ZeRO optimizer 등 활용 가능
  • NCCL (NVIDIA Collective Communication Library)
    • GPU 간 통신을 빠르게 처리하는 NVIDIA 라이브러리

5. 실행 예시: PyTorch DDP 구조

# 초기화
dist.init_process_group("nccl")

# 모델을 각 GPU에 할당
model = MyModel().to(local_rank)
model = DDP(model, device_ids=[local_rank])

# 데이터 로더는 DistributedSampler와 함께 사용
sampler = DistributedSampler(dataset)
loader = DataLoader(dataset, sampler=sampler, ...)

💡 torchrun, torch.distributed.launch 등을 통해 프로세스를 GPU 수만큼 실행


6. Multi-GPU 환경에서 주의할 점

  • GPU 간 통신 비용: GPU 간 데이터 이동은 느릴 수 있음 → 최소화해야 함
  • 동기화 비용: 각 GPU가 병렬적으로 계산하더라도 업데이트(gradient sync)는 동기화 필요
  • Random seed / Dropout 등은 GPU별로 일관성 있게 설정
  • Batch size는 GPU 수에 맞게 나누어야 함 (예: 총 128, GPU 4개 → 각 32)

7. 기초 실습 구성 추천

  • 🔹 torchrun --nproc_per_node=2 train.py
    → 로컬에서 2개 GPU로 학습
  • 🔹 DataParallel vs DDP 비교 실험
    → DDP가 훨씬 빠르고 안정적인 결과 제공

마무리

항목 요약
목적 속도 향상, 대규모 모델 학습
핵심 기법 Data Parallel, Model Parallel
도구 DDP, DeepSpeed, Accelerate
주의점 통신/동기화 비용, 메모리 할당, seed 고정


핵심 개념 비교

항목 DataParallel DistributedDataParallel (DDP)
실행 방식 단일 프로세스, 여러 GPU에서 연산 GPU당 하나의 프로세스 (멀티 프로세스)
메인 GPU 모든 연산 결과를 메인 GPU에서 수집 각 GPU에서 동시 병렬 처리 후 동기화
성능 병목이 심함, 느림 최적화된 병렬화, 빠름 ✅
권장 여부 실험용/비추천 ❌ 실제 사용/표준 방식 ✅
통신 방식 Python-level 통신 NCCL 기반 GPU 간 고속 통신
사용 API nn.DataParallel(model) torch.nn.parallel.DistributedDataParallel(model)

구조도적 차이

DataParallel 구조 (단일 프로세스, 메인 GPU 집중)

           +--------------+
  Input -->|   Main GPU   |   (Model, Loss, Backward)
           +------+-------+
                  |
        -----------------------
       |         |           |
   GPU 1       GPU 2       GPU 3  ← Submodule 복사
       \_________/_________/
               |
        결과 수집 + loss 계산은 **Main GPU**

→ → 문제점: 모든 gradient가 메인 GPU로 모여 연산되어 병목 발생 (ex. GPU0 과부하)


DDP 구조 (프로세스 당 1 GPU, 진정한 병렬)

[Process 0] GPU 0: 모델 + 데이터 조각 → forward → backward
[Process 1] GPU 1: 모델 + 데이터 조각 → forward → backward
[Process 2] GPU 2: 모델 + 데이터 조각 → forward → backward
[Process N] ...
        ↘ NCCL 기반 통신으로 gradient 동기화 ↙

→ → 장점: 모든 GPU가 자기 몫의 연산과 업데이트를 병렬로 수행
→ 통신은 CUDA 커널 수준에서 이루어지며 병목 거의 없음


예제 코드 비교

DataParallel 예제

model = MyModel()
model = nn.DataParallel(model)
model = model.cuda()

DDP 예제

# GPU당 1 프로세스 실행 필수 (ex. torchrun 사용)
model = MyModel().to(local_rank)
model = DDP(model, device_ids=[local_rank])

어떤 상황에 무엇을 쓸까?

상황 추천 방식
연구용, 단순 실험 DataParallel (비추천이나 간단함)
대규모 학습, 실전 학습 ✅ DDP
정확한 시간 측정, 성능 최적화 ✅ DDP
다중 노드 (멀티 서버) 반드시 DDP 사용

결론

"DataParallel은 입문자용 구조이며, 실전에서는 반드시 DistributedDataParallel을 사용해야 합니다."
DDP는 성능 최적화뿐만 아니라 정확한 결과 재현성메모리 효율성 측면에서도 훨씬 우수합니다.



Model Parallelism이란?

  • Data Parallel: 같은 모델 복사본을 각 GPU에 두고 데이터를 나눠서 학습
  • Model Parallel: 하나의 모델을 여러 GPU에 나눠서 배치하고, 순차적으로 연산을 수행

주 용도

  • 거대한 모델(GPT, LLaMA 등)을 GPU 여러 개에 나누어 학습
  • GPU 하나에 전체 모델이 올라가지 않을 때 필수

간단한 예제 구조 (2개 GPU에 모델 분산)

import torch
import torch.nn as nn

# 두 개 GPU가 필요
device0 = torch.device("cuda:0")
device1 = torch.device("cuda:1")

# 모델 정의: 두 layer를 다른 GPU에 올림
class ModelParallelNN(nn.Module):
    def __init__(self):
        super(ModelParallelNN, self).__init__()
        self.seq1 = nn.Sequential(
            nn.Linear(1024, 2048),
            nn.ReLU()
        ).to(device0)

        self.seq2 = nn.Sequential(
            nn.Linear(2048, 1024),
            nn.ReLU()
        ).to(device1)

    def forward(self, x):
        # 입력은 device0으로 전달됨
        x = x.to(device0)
        x = self.seq1(x)

        # seq2가 있는 device1로 이동
        x = x.to(device1)
        x = self.seq2(x)
        return x

학습 루프 예시

model = ModelParallelNN()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.MSELoss()

# dummy input/output
x = torch.randn(64, 1024).to("cuda:0")  # 입력은 첫 번째 GPU로
y = torch.randn(64, 1024).to("cuda:1")  # 출력은 마지막 GPU 기준

for epoch in range(5):
    optimizer.zero_grad()
    out = model(x)
    loss = criterion(out, y)
    loss.backward()  # 역전파는 자동으로 GPU 간 통신 처리
    optimizer.step()
    print(f"Epoch {epoch+1}: Loss = {loss.item():.4f}")

주의사항 및 팁

항목 내용
.to(device) 각 연산 전/후 데이터를 해당 GPU로 이동해야 함
backward() 자동으로 GPU 간 통신 수행 (Autograd 엔진)
속도 Layer 간 이동 시 GPU 간 통신 오버헤드 존재
PyTorch 지원 수동으로 .to(device) 처리 필요, 자동화는 없음 (vs. DataParallel)

정리

항목 설명
목적 모델이 GPU 메모리 하나에 안 들어갈 때
구조 Layer 단위로 서로 다른 GPU에 배치
장점 대규모 모델 처리 가능
단점 구현 복잡, GPU 간 통신 속도에 민감
실사용 예 GPT-3, LLaMA, T5 등 수십억 파라미터 모델


1. DDP의 핵심 철학

GPU 당 하나의 프로세스” 원칙

  • 각 GPU는 자기만의 프로세스와 모델 복사본을 가짐
  • Forward + Backward 계산을 독립적으로 처리
  • Backward 단계에서만 통신 (gradient all-reduce)

2. DDP의 내부 동작 순서

1. torchrun 또는 launch로 N개 프로세스 실행
2. 각 프로세스는 모델과 데이터를 자기 GPU에 로드
3. forward: 각 GPU가 자기 미니배치로 연산
4. backward: gradient를 all-reduce (NCCL)로 통신
5. optimizer step: 각 GPU가 동일한 업데이트 수행

3. DDP에서 꼭 필요한 설정 요소

항목 설명 예시
init_process_group DDP 통신 그룹 초기화 dist.init_process_group("nccl", ...)
local_rank 각 프로세스의 GPU ID torchrun에서 자동 전달됨
DistributedSampler 각 GPU에 고유한 데이터 샘플링 DataLoader(..., sampler=DistributedSampler(...))
device_ids GPU 지정 DDP(model, device_ids=[local_rank])

4. 실전 코드 구조 예시 (단일 노드 N GPU)

import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def setup(rank, world_size):
    dist.init_process_group("nccl", rank=rank, world_size=world_size)
    torch.cuda.set_device(rank)

def cleanup():
    dist.destroy_process_group()

def train(rank, world_size):
    setup(rank, world_size)

    model = MyModel().to(rank)
    model = DDP(model, device_ids=[rank])

    sampler = DistributedSampler(train_dataset, num_replicas=world_size, rank=rank)
    dataloader = DataLoader(train_dataset, sampler=sampler, ...)

    # 학습 루프
    for epoch in range(num_epochs):
        sampler.set_epoch(epoch)  # epoch별 셔플 고정
        for batch in dataloader:
            ...

    cleanup()

실행:

torchrun --nproc_per_node=4 train.py

5. DDP 통신 방식: All-Reduce

  • gradient 동기화 방식: All-Reduce (각 GPU가 gradient를 공유 후 평균)
  • 기반 통신 백엔드: NCCL (NVIDIA Collective Communication Library)
  • 효율성 팁:
    • Mixed Precision (fp16, bf16) + Gradient Accumulation
    • Gradient Bucketing (PyTorch 자동 처리)
    • CUDA-aware 통신 → CPU로 안 거치고 바로 GPU-GPU 간 통신

6. 성능 최적화를 위한 팁

전략 설명
pin_memory=True CPU→GPU 데이터 전송 속도 향상
num_workers 최적화 데이터 로딩 병렬화
AMP (torch.cuda.amp) Mixed precision으로 메모리/속도 이득
Gradient Accumulation GPU 메모리 절약 + batch 크기 증가
find_unused_parameters=False 비활성 파라미터 추적 안 함 → 속도 증가
gradient_checkpointing 메모리 절약 (forward 계산 다시 수행)

7. Multi-Node DDP 확장

항목 설명
init_method env://로 환경변수로 IP/PORT 설정
MASTER_ADDR, MASTER_PORT 마스터 노드 주소
WORLD_SIZE, RANK 전체 프로세스 수, 현재 프로세스 인덱스

예시:

MASTER_ADDR=10.0.0.1 MASTER_PORT=29500 \
WORLD_SIZE=8 RANK=2 \
torchrun --nproc_per_node=4 --nnodes=2 --node_rank=1 train.py

8. DDP 관련 문제 상황 및 디버깅

문제 원인 해결 방법
일부 GPU만 사용됨 rank / device 설정 오류 torch.cuda.set_device(local_rank) 필수
학습 멈춤, 무한 대기 drop_last=False로 마지막 배치 크기 불일치 drop_last=True 강력 추천
gradient sync 안 됨 파라미터 미사용 또는 mask find_unused_parameters=True
느림, 병목 발생 CPU 병목 또는 통신 충돌 DataLoader 튜닝, CUDA 버전 확인

핵심 요약

항목 내용
구조 GPU당 하나의 프로세스, 모델 복사본
통신 NCCL 기반 all-reduce (backward 시만 통신)
데이터 DistributedSampler 필수, set_epoch(epoch) 필요
실행 torchrun 또는 torch.distributed.launch 사용
확장성 단일 노드 ↔ 다중 노드 모두 지원
성능 향상 AMP, Accumulation, Efficient Sampler 등 활용

 

728x90

'인공지능 > 공부' 카테고리의 다른 글

딥러닝 응용 - 2주차  (0) 2025.09.08
mteb 한글 평가하기  (0) 2025.08.29
인공지능 기초 학습 코드 + 개념  (4) 2025.07.29
Collator란?  (4) 2025.07.29
딥러닝 총 정리  (2) 2025.07.04