분산 구성 간 성능 비교 — 시드·배치·학습률

분산 구성 간 성능 비교 — 시드·배치·학습률

TPU, 멀티 GPU, 단일 GPU는 같은 스크립트를 같은 배치 크기로 돌려도 결과가 정확히 맞아떨어지지는 않아요. 왜 그런지, 그리고 비교할 때 무엇을 맞춰야 하는지 세 가지 축으로 정리해볼게요.

출처: https://huggingface.co/docs/accelerate/concept_guides/performance

시드 설정

재현 가능한 훈련을 위해 모든 분산 케이스에서 시드를 완전히 설정하는 게 좋아요. utils.set_seed()가 이를 대신해줍니다.

from accelerate.utils import set_seed
set_seed(42)

이 함수는 내부적으로 5가지 시드 설정을 하나로 묶어줘요.

random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)  # or torch.xpu.manual_seed_all, etc
# ^^ safe to call this function even if cuda is not available
if is_torch_xla_available():
    xm.set_rng_state(seed)

random·numpy·torch·torch 디바이스, 그리고 TPU가 있으면 torch_xla의 cuda 상태까지 함께 설정됩니다.

관측 배치 크기(Observed Batch Size)

Accelerate로 훈련할 때 DataLoader에 넘기는 배치 크기는 GPU당 배치 크기예요. 즉 두 GPU에서 배치 64는 실제로는 배치 128이 됩니다. 단일 GPU에서 비교 테스트를 할 때는 이 점을 반드시 반영해야 해요.

Single GPU Batch Size Multi-GPU Equivalent Batch Size TPU Equivalent Batch Size
256 128 32
128 64 16
64 32 8
32 16 4

이 표는 "Multi-GPU"가 두 GPU, TPU pod가 8 워커인 경우의 예시예요.

학습률

문서들이 공통으로 지적하듯 학습률은 디바이스 수에 따라 선형적으로 스케일해야 해요. Accelerate로는 이렇게 적용합니다.

learning_rate = 1e-3
accelerator = Accelerator()
learning_rate *= accelerator.num_processes
optimizer = AdamW(params=model.parameters(), lr=learning_rate)

학습률 스케일 여부는 사용자 판단에 맡기고 Accelerate가 강제하지 않아요. 또 accelerate는 학습 중인 프로세스 수에 따라 학습률 스텝을 밟습니다. 앞서 말한 관측 배치 크기 때문인데, 두 GPU면 단일 GPU보다 학습률이 두 배 자주 스텝되어 더 큰 배치를 보정합니다.

그라디언트 누적과 혼합 정밀도

그라디언트 누적과 혼합 정밀도를 쓸 때, 그라디언트 평균 방식과 정밀도 손실 때문에 배치 단위 loss는 구성 간에 어느 정도 저하가 생기는 게 정상이에요. 하지만 훈련이 끝난 뒤 전체 loss·메트릭·일반 성능은 대략적으로 같아야 합니다.

더 알아보기