제6부 생성형 AI 플랫폼
6.3 NeMo II — 병렬화(TP·PP·DP·시퀀스)
6.3.1 이 장에서 배우는 것 / 선수 지식
6.2에서 NeMo 구조·데이터 경로를 준비했다. 이제 그 데이터로 거대 모델을 여러 GPU에 나눠 학습하는 병렬화를 판다. 4.14(NCCL·매핑)·5.9(추론 TP/PP)에서 병렬화의 통신·매핑을 봤지만, 학습은 추론보다 메모리 압박이 훨씬 크다 — 가중치뿐 아니라 그래디언트·옵티마이저 상태·역전파용 활성화까지 메모리에 있어야 한다. 그래서 학습은 추론에 없던 차원들 — ZeRO/분산 옵티마이저·시퀀스 병렬·활성화 재계산·파이프라인 스케줄 — 이 더 필요하다.
핵심 통찰: 학습 메모리의 적은 가중치가 아니라 옵티마이저 상태와 활성화다. Adam 혼합정밀 학습은 파라미터당 ~16~20바이트(가중치의 8~10배)가 들고, 역전파용 활성화는 배치·시퀀스에 비례해 폭증한다. 병렬화는 이 메모리를 GPU들에 나눠 담는 게 1차 목적이고, 통신·버블이 그 대가다.
배우는 것:
- 학습 메모리 분해 — 가중치·그래디언트·옵티마이저 상태(Adam m·v·master)·활성화, 그리고 왜 활성화가 핵심인가.
- 병렬 차원 — DP(+ZeRO/분산 옵티마이저)·TP·PP·SP(시퀀스 병렬)·CP(컨텍스트 병렬)·EP(전문가), 각각 무엇을 나누나.
- 활성화 재계산(checkpointing) — 컴퓨트로 활성화 메모리를 사는 거래.
- 파이프라인 스케줄 — 1F1B·인터리브로 버블을 줄이는 법.
- 3D 매핑 — TP×PP×DP(+SP/CP)를 인터커넥트에 정렬(4.14 원리의 학습판), 글로벌 배치 구성.
- NeMo config로 병렬도를 정하고 메모리를 수계산하는 흐름.
선수 지식: 4.14(NCCL·TP/PP/DP·ZeRO·매핑 원리), 5.9(추론 TP/PP — 이 장은 학습판), 6.2(NeMo config·데이터), 1.14(혼합정밀·BF16·master weight), 1.9(HBM). 환경은 6.2(NeMo 컨테이너).
정직: 병렬화는 Megatron-Core가 구현하고 NeMo가 config로 노출한다. config 키·기본값·스케줄 옵션은 버전 의존이다 — 이 장은 개념·메모리 산수·매핑을 가르치고 구체 인자는 "그 버전 문서로 확인". 메모리·성능 수치는 환경 의존 개략값, 실측.
6.3.2 학습 메모리 — 적은 가중치가 아니다
추론(5.7)은 가중치 + KV 캐시면 됐다. 학습은 훨씬 많다. Adam + 혼합정밀(1.14) 기준, 파라미터당 메모리(개략):
파라미터 1개당 (Adam, BF16 혼합정밀, 개략):
| 항목 | 바이트/파라미터 | 비고 |
|---|---|---|
| BF16 가중치 | 2 B | |
| BF16 그래디언트 | 2 B | |
| FP32 master 가중치 | 4 B | 옵티마이저 상태(가장 큼) |
| FP32 Adam m(모멘텀) | 4 B | 옵티마이저 상태(가장 큼) |
| FP32 Adam v(분산) | 4 B | 옵티마이저 상태(가장 큼) |
| 합계 | ≈ 16 B/파라미터 | 가중치만의 8배! |
예: 7B 모델 → 7e9 × 16 ≈ 112 GB — 80GB GPU 한 장에 '상태'만으로도 초과한다. 여기에 활성화(아래)가 더해져 더 커진다.
여기에 활성화(activation) — 역전파를 위해 forward의 중간 출력을 저장 — 가 더해진다:
함의(학습 병렬화의 동기):
- 가중치만 보면 안 된다. 7B도 옵티마이저 상태로 ~112GB → 단일 GPU 불가. 상태를 나눠 담아야(ZeRO/TP/PP).
- 활성화가 배치·시퀀스로 폭증 → 별도 대책(SP·재계산) 필요.
- 그래서 학습 병렬화는 추론보다 차원이 많다(DP에 ZeRO, TP에 SP, +재계산).
6.3.3 병렬 차원 — 무엇을 나누나
학습의 병렬 차원을 "무엇을 나누고 무엇을 통신하나"로 정리한다.
| 차원 | 나누는 것 | 통신 | 메모리 효과 |
|---|---|---|---|
| DP(데이터) | 배치(데이터) | grad all-reduce(스텝당) | 모델 복제 → 메모리 안 줄음(ZeRO 전) |
| ZeRO/분산옵티 | 옵티마이저 상태(±grad·param) | all-gather·reduce-scatter | 상태 메모리 1/DP |
| TP(텐서) | 레이어 가중치(행렬) | all-reduce 매 레이어 | 가중치·활성화 1/TP |
| PP(파이프라인) | 레이어를 스테이지로 | 경계 P2P(드묾) | 가중치 1/PP, 버블 발생 |
| SP(시퀀스) | 시퀀스 차원(TP 보완) | TP와 결합 | 활성화 추가 절감 |
| CP(컨텍스트) | 긴 시퀀스 자체 | 어텐션 통신 | 긴 문맥 활성화 분산 |
| EP(전문가) | MoE 전문가 | all-to-all | MoE 메모리 분산 |
① DP + ZeRO/분산 옵티마이저. 순수 DP는 각 복제가 전체 모델+옵티마이저를 들어 메모리가 안 준다(배치만 나눔). ZeRO(4.14)는 옵티마이저 상태(ZeRO-1)·그래디언트(ZeRO-2)·파라미터(ZeRO-3)를 DP 랭크에 분할해 메모리를 1/DP로 줄인다. NeMo/Megatron의 분산 옵티마이저가 ZeRO-1류(옵티마이저 상태 분할) — 가장 큰 메모리(옵티마이저 상태)를 친다.
② TP(텐서 병렬). 5.9처럼 각 레이어 가중치를 분할, 매 레이어 all-reduce → 가중치·활성화를 1/TP. 통신 잦아 NVLink 도메인 안.
③ PP(파이프라인 병렬). 레이어를 스테이지로, 경계 P2P. 가중치 1/PP, 노드 간 가능. 단 파이프라인 버블(6.3.7).
6.3.4 시퀀스 병렬(SP) — 활성화를 더 줄인다
TP는 어텐션·MLP의 가중치·활성화를 나누지만, LayerNorm·dropout 같은 부분은 TP로 안 나뉘어 각 TP 랭크가 전체 시퀀스의 활성화를 중복 보유한다. 시퀀스 병렬(SP) 은 그 부분을 시퀀스 차원으로 분할해 활성화 중복을 없앤다.
| 구성 | 활성화 처리 |
|---|---|
| TP만 | 어텐션/MLP는 분할되나, LayerNorm 등은 각 랭크가 전체 활성화 보유(중복) |
| TP+SP | LayerNorm 등의 활성화도 시퀀스로 분할 → 활성화 메모리 추가 절감. 전환 지점에 all-gather/reduce-scatter 통신(TP 통신과 결합되어 추가 비용 작음) |
SP는 TP와 짝으로 쓰며, 긴 시퀀스 학습의 활성화 메모리를 크게 줄인다.
- SP는 TP의 확장으로 거의 항상 TP와 함께 켠다(NeMo
sequence_parallel). - 추가 통신은 TP all-reduce를 all-gather+reduce-scatter로 분할하는 형태라 순 비용 증가가 작다.
- 컨텍스트 병렬(CP) 은 한 발 더 나아가 긴 시퀀스 자체를 GPU로 분할(어텐션을 분산 계산) — 32K·128K 같은 긴 문맥 학습의 활성화·연산을 나눈다.
6.3.5 활성화 재계산 — 컴퓨트로 메모리를 산다
활성화 메모리가 여전히 크면 재계산(activation recomputation / gradient checkpointing): forward의 활성화를 저장하지 않고, 역전파 때 다시 계산한다.
| 방식 | 동작 | 효과 |
|---|---|---|
| 기본 | forward 활성화를 전부 저장 → 역전파에 사용 | 활성화 메모리 큼 |
| 재계산 | 활성화를 (일부/전부) 버림 → 역전파 시 그 구간 forward 재실행해 복원 | 활성화 메모리 ↓↓, 대신 forward 부분 반복 → 컴퓨트 ↑(~20~30% 개략) |
- 선택적 재계산: 메모리 큰 부분(예: 어텐션)만 재계산해 메모리·컴퓨트 균형.
- 전체 재계산: 활성화 거의 안 저장 → 메모리 최소, 컴퓨트 최대.
- 트레이드오프: 활성화 메모리를 컴퓨트로 거래. 메모리가 부족하면(긴 시퀀스·큰 모델) 필수, 여유 있으면 끄는 것이 빠름.
핵심: SP·CP·재계산은 활성화 메모리를 공략하는 세 레버다. 가중치·옵티마이저는 TP/PP/ZeRO가, 활성화는 SP/CP/재계산이 나눈다 — 둘을 조합해 GPU 메모리 예산에 맞춘다.
6.3.6 워크스루 — 메모리 예산·병렬 config
모델 크기와 GPU에서 병렬 config을 정하는 워크스루(개략 산수).
NeMo config(개념):
model:
tensor_model_parallel_size: 8 # TP: 노드 내 NVLink 도메인(4.14)
pipeline_model_parallel_size: 2 # PP: 노드 경계
sequence_parallel: true # SP: TP와 함께(활성화↓)
# context_parallel_size: 2 # CP: 긴 시퀀스면
micro_batch_size: 1
global_batch_size: 512 # = micro × DP × grad_accum
activation_checkpointing: true # 활성화 재계산(메모리가 부족하면)
optim: { name: distributed_fused_adam } # 분산 옵티마이저(ZeRO-1류)
# 총 GPU = TP × PP × DP. DP = 총GPU / (TP×PP)
매핑 원리(4.14 학습판): TP(+SP)는 NVLink 도메인 안(매 레이어 통신), PP는 노드 경계(드문 P2P), DP는 가장 바깥(스텝당 grad 통신, ZeRO로 메모리도 분산). 어기면 통신이 병목.
6.3.7 파이프라인 버블과 스케줄
PP는 레이어를 스테이지로 나누지만 단일 마이크로배치는 한 번에 한 스테이지만 활성 → 나머지 유휴(버블, 5.9). 학습은 여러 마이크로배치를 파이프에 흘려 버블을 줄인다.
채움 전후로 큰 버블이 생긴다.
1F1B(one-forward-one-backward) 스케줄: 마이크로배치들을 파이프에 채워 forward·backward를 교차한다 → 버블↓, 활성화 메모리도 관리. 인터리브(interleaved) 1F1B: 각 GPU가 여러 비연속 레이어 청크를 맡아 버블을 더 줄인다(통신↑).
버블 비율(이상 compute 대비) ≈ (PP단계수 − 1) / 마이크로배치수. (전체 벽시계 대비 유휴 비율은 (p−1)/(m+p−1), 4.14 — 예: 0.75 → 약 43%.) 마이크로배치를 PP 단계보다 충분히 많이 두면 버블이 작아진다.
- 마이크로배치 수 ≫ PP 단계 수 면 버블이 작아진다(그래서 PP는 글로벌 배치가 커야 효율적).
- 1F1B: forward·backward를 교차해 활성화 메모리(동시에 살아있는 활성화)를 제한하며 버블 축소.
- 인터리브 1F1B: 버블을 더 줄이나 통신↑ — 노드 내 빠른 링크일 때 유리.
핵심: PP는 메모리(노드 간 확장)를 주지만 버블이라는 효율 비용이 있다. TP를 노드 내에서 최대한 쓰고(버블 없음), 부족분만 PP(노드 간)로 가는 것이 보통 — TP는 통신 잦아 NVLink 필요, PP는 통신 드물어 노드 간 OK라는 4.14 원리와 맞물린다.
6.3.8 중간 점검 (체크포인트)
- [ ] 학습 메모리가 가중치+그래디언트+옵티마이저 상태(Adam ~16B/param)+활성화임을, 옵티마이저·활성화가 핵심임을 설명한다.
- [ ] DP가 메모리를 안 줄이는 이유와 ZeRO/분산 옵티마이저가 상태를 1/DP로 줄이는 원리를 안다.
- [ ] TP·PP가 가중치를, SP·CP·재계산이 활성화를 줄임을 구분한다.
- [ ] 파이프라인 버블과 1F1B·마이크로배치로 줄이는 법, 버블 비율 공식을 안다.
- [ ] 3D 매핑(TP+SP=NVLink, PP=노드간, DP=바깥)과 글로벌 배치 구성(micro×DP×grad_accum)을 정한다.
- [ ] 모델·GPU에서 메모리 예산으로 병렬 config을 수계산한다.
증상→의심: OOM(상태 메모리) → 옵티마이저 상태 미분산 → 분산 옵티마이저(ZeRO)·TP·PP. OOM(활성화) → SP 미사용·긴 시퀀스 → SP·CP·활성화 재계산. PP인데 GPU 유휴 큼 → 버블 → 마이크로배치↑·1F1B. TP 늘렸는데 느림 → TP가 NVLink 초과(노드 경계) → 노드 내로. 확장 효율 낮음 → 매핑이 인터커넥트와 안 맞음(4.14) → TP=NVLink·PP/DP=노드간. 재계산 켰는데 느림 → 메모리 여유 있는데 과한 재계산 → 선택적·끄기.
6.3.9 트레이드오프·대안
병렬 차원 선택(학습).
| 차원 | 줄이는 것 | 비용 | 어디에 |
|---|---|---|---|
| 분산 옵티(ZeRO-1) | 옵티마이저 상태 | all-gather 통신 | 거의 항상(가장 큰 메모리) |
| TP(+SP) | 가중치·활성화 | 매 레이어 통신 | NVLink 도메인(4.14) |
| PP | 가중치 | 버블·P2P | 노드 간, 큰 모델 |
| DP | (배치 분산) | grad all-reduce | 처리량·바깥 차원 |
| CP | 긴 시퀀스 활성화 | 어텐션 통신 | 긴 문맥 |
| 활성화 재계산 | 활성화 | 컴퓨트 +20~30% | 메모리가 부족할 때 |
견해: 순서 — 분산 옵티마이저(무비용에 가까운 메모리 절감) → TP+SP(노드 내) → 활성화 재계산(메모리가 부족하면) → PP(노드 간, 모델이 노드 초과) → DP(처리량·확장). 가중치·옵티마이저는 TP/PP/ZeRO, 활성화는 SP/CP/재계산으로 나눠 GPU 메모리에 맞춘다. TP를 NVLink 도메인 안에서 최대한, 그 너머만 PP — 버블·통신 비용 때문(4.14·5.9).
비엔비디아 정직: 이 병렬 기법들(ZeRO·TP·PP·SP·1F1B)은 Megatron·DeepSpeed 등에서 나온 공개 연구다. DeepSpeed(ZeRO)·FSDP(PyTorch)·Megatron-LM도 같은 차원을 제공한다. NeMo/Megatron-Core의 강점은 이들의 통합·NVLink/NCCL 최적화·검증 레시피·엔비디아 스택이다. 다른 스택(FSDP·DeepSpeed)으로도 같은 원리로 학습 가능 — 규모·기존 스택으로 선택.
6.3.10 흔한 함정
- 가중치만 보고 메모리 계산. 학습은 옵티마이저 상태(~8×)·활성화가 지배 — Adam ~16B/param + 활성화로 계산.
- DP만으로 큰 모델. 순수 DP는 모델을 복제해 메모리가 안 준다 → 분산 옵티마이저/ZeRO·TP·PP로 상태·가중치 분산.
- TP를 NVLink 도메인 너머로. 매 레이어 all-reduce가 노드 경계를 타 병목(4.14) → TP는 노드 내, 초과는 PP.
- SP를 TP 없이·따로 생각. SP는 TP의 확장 — TP와 함께 켜 활성화 중복 제거. 긴 시퀀스면 거의 필수.
- PP 버블 무시. 마이크로배치가 PP 단계보다 적으면 버블 큼 → 마이크로배치↑·1F1B. 글로벌 배치가 작으면 PP 비효율.
- 활성화 재계산 과용/미용. 메모리 여유 있는데 켜면 컴퓨트만 낭비, 여유가 적은데 안 켜면 OOM. 선택적으로 균형.
- 글로벌 배치 잘못 계산. global = micro × DP × grad_accum. 병렬도 바꾸면 DP가 바뀌어 글로벌 배치·학습 동역학이 변함 — 의도한 배치 유지.
- config 키 버전 가정. Megatron-Core/NeMo 병렬 인자·기본값이 버전마다 다름 — 그 버전 문서.
6.3.11 연습문제
연습 1 (메모리 분해). 13B 모델을 BF16 Adam 혼합정밀로 학습한다. (a) 옵티마이저 상태 포함 파라미터 메모리(개략)? (b) 80GB GPU 한 장에 들어가나? (c) 활성화는 왜 별도 문제이고 무엇이 키우나?
정답
(a) Adam 혼합정밀 ≈ 16 B/param(BF16 가중치2 + BF16 grad2 + FP32 master4 + Adam m4 + v4). 13e9 × 16 ≈ 208 GB. (b) 불가 — 208GB ≫ 80GB. '상태'만으로도 한 장 초과 → 분산 옵티마이저(ZeRO)·TP·PP로 나눠야. (c) 활성화는 역전파를 위해 forward 중간 출력을 저장하는 것으로, 배치 × 시퀀스길이 × hidden × 레이어수에 비례 → 큰 배치·긴 시퀀스에서 폭증. 상태(208GB)에 더해지므로 별도 대책(SP·CP·활성화 재계산)이 필요. 추론엔 거의 없는 부담이라 학습이 메모리가 훨씬 큰 이유.
연습 2 (DP vs ZeRO). 순수 데이터 병렬(DP)이 메모리를 줄이지 못하는 이유와, ZeRO/분산 옵티마이저가 어떻게 푸는지, 어느 ZeRO 단계가 옵티마이저 상태를 분할하는지 설명하라.
정답
순수 DP: 각 DP 랭크가 전체 모델 + 전체 옵티마이저 상태를 복제하고 배치만 나눔 → 메모리는 랭크당 그대로(처리량만↑). ZeRO: 중복 보유하던 상태를 DP 랭크에 분할한다 — ZeRO-1: 옵티마이저 상태(가장 큼)를 1/DP로 분할, ZeRO-2: +그래디언트, ZeRO-3: +파라미터까지 분할(필요 시 all-gather). NeMo/Megatron의 분산 옵티마이저가 ZeRO-1류로 옵티마이저 상태(~12B/param 중 큰 몫)를 분산 → DP가 클수록 상태 메모리 1/DP. 통신: all-gather/reduce-scatter가 추가되나 메모리 절감이 큼. 즉 DP를 "메모리도 줄이는" 차원으로 바꾸는 것이 ZeRO.
연습 3 (활성화 레버). 긴 시퀀스(32K) 학습에서 활성화 OOM이 난다. 가중치·옵티마이저는 이미 TP/ZeRO로 분산했다. 활성화를 줄이는 레버 셋과 각 비용을 제시하라.
정답
① 시퀀스 병렬(SP) — TP와 함께 켜 LayerNorm/dropout 등의 활성화 중복(각 TP 랭크가 전체 시퀀스 보유)을 시퀀스로 분할해 제거. 비용: 약간의 추가 통신(TP 통신과 결합되어 작음). 거의 항상 켤 것. ② 컨텍스트 병렬(CP) — 긴 시퀀스 자체를 GPU로 분할해 어텐션·활성화를 분산. 32K 같은 긴 문맥에 효과적. 비용: 어텐션 통신. ③ 활성화 재계산(checkpointing) — 활성화를 저장 안 하고 역전파 때 재계산. 비용: 컴퓨트 +20~30%(forward 부분 반복). 선택적으로 메모리 큰 부분만. 조합: SP(기본) + CP(긴 문맥) + 필요시 재계산. 가중치는 이미 됐으니 활성화 전용 레버로.
연습 4 (파이프라인 버블). PP=4, 마이크로배치=4로 학습하니 GPU 유휴가 크다. (a) 버블 비율(개략)? (b) 어떻게 줄이나? (c) PP를 효율적으로 쓰려면 글로벌 배치는?
정답
(a) 버블 비율 ≈ (PP단계−1)/마이크로배치수 = (4−1)/4 = 0.75 → 약 43% 유휴(채움·비움 구간이 큼). 마이크로배치가 PP 단계와 비슷하면 버블이 매우 크다. (b) 마이크로배치 수를 PP 단계보다 훨씬 많이(예 16~32) → 버블 비율 (4−1)/16 ≈ 0.19로 감소. 1F1B 스케줄로 forward·backward 교차(활성화 메모리도 관리), 노드 내 빠른 링크면 인터리브 1F1B로 더↓(통신↑). (c) PP가 효율적이려면 마이크로배치가 충분히 많아야 하므로 글로벌 배치(=micro×DP×grad_accum)가 충분히 커야 한다 — 작은 글로벌 배치에선 PP가 버블로 비효율. 그래서 TP(버블 없음)를 노드 내에서 먼저 쓰고 PP는 모델이 노드 초과할 때.
연습 5 (3D 매핑). 32 GPU(노드 4×8, 노드 내 NVLink)에서 큰 모델을 학습한다. TP=8, PP=2를 정했다. (a) DP는? (b) 각 차원을 어느 인터커넥트에 매핑하고 왜? (c) 글로벌 배치가 micro=1, grad_accum=16이면?
정답
(a) 총 GPU = TP×PP×DP → 32 = 8×2×DP → DP=2. (b) TP=8 → 노드 내 NVLink 도메인(매 레이어 all-reduce, 잦고 큼 → NVLink 필수, SP도 함께). PP=2 → 노드 경계 IB(스테이지 경계 P2P, 드묾 → 노드 간 OK). DP=2 → 가장 바깥(스텝당 grad all-reduce, ZeRO로 옵티마이저 상태도 분산). 4.14 원리: 잦은 통신=NVLink, 드문=노드 간. (c) 글로벌 배치 = micro × DP × grad_accum = 1 × 2 × 16 = 32. (주의: 병렬도를 바꿔 DP가 변하면 글로벌 배치도 변하므로, 목표 글로벌 배치를 유지하려면 grad_accum으로 보정.)
6.3.12 요약 · 다음 장
- 학습 메모리의 적은 가중치가 아니라 옵티마이저 상태(Adam ~16B/param·가중치의 8배)와 활성화(배치·시퀀스로 폭증) 다. 병렬화의 1차 목적은 이를 나눠 담는 것.
- 차원별 분담: 옵티마이저 상태=ZeRO/분산 옵티마이저, 가중치=TP·PP, 활성화=SP·CP·활성화 재계산, 배치·확장=DP. 각각 다른 메모리를 친다.
- SP는 TP의 확장(LayerNorm 등 활성화 중복 제거), CP는 긴 시퀀스 분산, 재계산은 컴퓨트로 활성화 메모리를 사는 거래.
- PP는 버블이 비용 — 마이크로배치를 PP 단계보다 많이·1F1B로 줄인다. TP(+SP)는 NVLink 도메인, PP는 노드 간, DP는 바깥(4.14 매핑). 글로벌 배치 = micro × DP × grad_accum.
- 정직: 기법은 Megatron/DeepSpeed 공개 연구(FSDP·DeepSpeed도 가능), config는 버전 의존, 메모리는 수계산 후 실측.
다음 장: 6.4 NeMo III — SFT·PEFT(LoRA). 사전학습의 병렬화를 봤으니, 대부분의 팀이 실제로 하는 커스터마이즈 — 지도 미세조정(SFT)과 LoRA(PEFT) — 로 내려간다. 적응 스펙트럼(6.1)의 핵심 단계를, 데이터 형식·어댑터 구조·여러 어댑터 운영·메모리 이점과 함께 실습으로 본다.