초거대 모델의 탄생 · Huang et al. (NeurIPS 2019)

GPipe — 층을 나누고, 배치를 쪼개고, 갱신은 한 번에

모델이 한 가속기의 메모리를 넘어서면 층을 여러 장치에 나눠 실을 수밖에 없습니다. 그런데 층은 앞 층의 출력을 기다려야 하므로, 그냥 나누기만 하면 한 번에 한 장치만 일하고 나머지는 놉니다. GPipe는 미니배치를 마이크로배치로 잘게 쪼개 장치들이 서로 다른 조각을 동시에 처리하게 만들고, 기울기는 모든 조각에서 모아 한 번에 적용합니다. 이 페이지는 그 타임라인을 직접 돌려 보고, 유휴 시간(버블)과 활성 메모리를 계산하고, 쪼개서 학습해도 결과가 정말 같은지 브라우저에서 학습시켜 확인합니다.

이 자료의 축

모델은 층 단위로 잘라 장치에 나눠 싣고, 미니배치는 마이크로배치로 쪼개 파이프라인을 채운다. 기울기는 모든 마이크로배치에서 모아 한 번에 적용하므로, 장치를 몇 대로 나누든 학습되는 수학은 한 장치에서 돌린 것과 같다. 쪼개서 생기는 대가는 두 가지, 파이프라인의 앞뒤에 남는 유휴 시간과 마이크로배치마다 쌓이는 활성 메모리이고, GPipe는 앞의 것을 M을 키워서, 뒤의 것을 재계산으로 갚습니다.

1. 한 장치에 안 들어가는 모델

논문은 그림 1로 시작합니다. ImageNet 분류 모델은 몇 해 사이 용량이 36배 커졌고 정확도도 그만큼 따라 올랐습니다. 번역에서도 Transformer를 키울수록 BLEU가 올랐습니다. 문제는 모델이 커지는 속도를 가속기 메모리가 따라가지 못한다는 것입니다. 학습할 때 장치 메모리에는 가중치만 올라가는 게 아닙니다. 기울기와 옵티마이저 상태가 함께 올라가고, 역전파에 쓸 순전파 중간 결과(활성)도 남겨 둬야 합니다.

논문 표 1의 Transformer 설정으로 계산해 봅니다. 층마다 모델 차원 2048, 피드포워드 차원 8192, 어휘 32k, 시퀀스 길이 1024, 배치 32이고, RMSProp을 써서 파라미터 하나에 12바이트가 든다고 표 1 설명에 적혀 있습니다(가중치·기울기·RMSProp 누적값이 4바이트씩이라고 보면 맞는 숫자입니다).

$$P(L) \approx \underbrace{2\,V d}_{\text{입력 임베딩 + 출력 소프트맥스}} \;+\; L\,\big(\underbrace{4d^2}_{\text{어텐션 Q, K, V, O}} + \underbrace{2dh}_{\text{피드포워드}} + \text{편향, LN}\big),\qquad d=2048,\ h=8192,\ V=32000$$ 논문은 이 식을 적지 않았습니다. 표 1의 파라미터 수 다섯 개(3층 282.2M ~ 1663층 83.9B)가 층마다 약 50.36M, 고정분 약 131M으로 거의 정확히 한 직선 위에 놓여서, 그 값에 맞게 구성 요소를 맞춘 재구성입니다. 다섯 값 모두 표에 적힌 자릿수 안에서 일치합니다(3층 282.1M 대 282.2M, 1663층 83.88B 대 83.9B).

데모 1 · 이 Transformer는 장치 몇 대에 들어가나?

층 수 L과 장치 수 K를 바꾸면 파라미터 수와 장치당 파라미터 메모리(12바이트 × P ÷ K)를 계산합니다. 막대 위의 점선은 Cloud TPUv3 코어 하나의 메모리 16GB입니다. 버튼은 표 1의 다섯 열입니다. 활성 메모리는 계산하지 않고, 논문이 잰 값을 그대로 보여 줍니다.

가중치기울기RMSProp 누적값논문이 잰 활성(표 1 열일 때만)

파라미터만 놓고 보면 16GB 한 장에는 약 14억 개, 25층까지 들어갑니다. 그런데 표 1에서 GPipe 없이 한 장치로 학습할 수 있었던 건 3층뿐이었습니다. 나머지 자리를 활성이 차지했기 때문입니다1. 128장으로 늘려도 같은 일이 일어납니다. 128 × 16GB = 2,048GB면 파라미터만으로는 1,833억 개가 들어가지만, 논문이 실제로 올린 것은 839억 개였고 그때 활성이 796.1GB를 차지했습니다. 모델을 장치에 나눠 싣는 것만큼 활성을 줄이는 것이 중요하다는 뜻이고, 이건 6절에서 다룹니다.

2. 층으로 자르기: 단순 모델 병렬과 유휴

GPipe가 모델에 요구하는 것은 하나입니다. 층을 순서대로 나열할 수 있을 것. L개 층의 순서열을 K개의 연속된 덩어리(논문은 셀이라고 부릅니다)로 자르고, k번째 셀을 k번째 가속기에 올립니다.

$$F_k = f_j \circ \cdots \circ f_{i+1} \circ f_i, \qquad C_k = \sum_{l=i}^{j} c_l$$ 2.1절. 셀 pk가 층 i부터 j까지를 담으면 그 순전파는 층 함수의 합성 Fk이고, 역전파 Bk는 자동 미분으로 Fk에서 얻습니다. cl은 사용자가 줄 수 있는 층별 비용 추정치이고, 셀 비용 Ck는 그 합입니다. 사용자가 정하는 것은 셀 수 K, 마이크로배치 수 M, 층의 순서열 세 가지뿐입니다.
입력→장치 0 · F₀→장치 1 · F₁→ … →장치 K−1 · FK−1→ 손실 →BK−1→ … →B₀

장치 사이에는 셀 경계의 활성만 오갑니다. 순전파는 장치 0에서 K−1로, 역전파는 거꾸로 흐릅니다. Bk는 위쪽 셀의 Bk+1과 자기 셀의 Fk 결과가 둘 다 있어야 계산할 수 있습니다. 이 의존 때문에 미니배치 하나를 통째로 흘리면 장치들이 차례로 일하고 차례로 놉니다(그림 2b).

데모 2 · 미니배치를 통째로 흘리는 단순 모델 병렬

위에서부터 장치 0, 1, …입니다. 파란 칸은 순전파 F, 주황 칸은 역전파 B, 끝의 초록 칸은 가중치 갱신입니다. 역전파는 보통 순전파의 두 배쯤 걸리므로(행렬곱이 입력에 대한 기울기와 가중치에 대한 기울기로 두 번) 기본값을 2로 두었습니다.

K를 올려도 한 스텝에 걸리는 시간은 그대로이고, 각 장치가 일하는 비율만 1/K로 줄어듭니다. 이 방식이 쓸모없는 건 아닙니다. 한 장치에 안 들어가던 모델이 들어가게는 되니까요. 다만 장치를 K배 써도 빨라지지는 않습니다. 논문 표 2의 M = 1 행이 이것을 잽니다. Transformer의 처리량이 K = 2, 4, 8에서 1, 1.07, 1.3으로 거의 그대로였고, 저자들은 “어느 순간에든 한 장치만 계산하고 있다는 뜻”이라고 적었습니다.

3. 배치를 쪼개기: 마이크로배치 파이프라인 (그림 2c)

GPipe의 해법은 미니배치를 쪼개는 것입니다. 크기 N인 미니배치를 크기 N/M인 마이크로배치 M개로 나눠 차례로 흘려보냅니다. 장치 0이 첫 조각의 F를 끝내고 장치 1에 넘기면, 장치 1이 그 조각을 처리하는 동안 장치 0은 둘째 조각을 시작할 수 있습니다. 조립 라인과 같습니다.

역전파도 같은 식으로 흐릅니다. 각 마이크로배치의 기울기는 순전파 때와 같은 파라미터로 계산하고, 미니배치 끝에서 M개 조각의 기울기를 모두 더해 모든 장치가 동시에 한 번 갱신합니다(2.2절). 갱신이 한 번뿐이므로 그 사이에 가중치가 바뀌는 일이 없습니다.

데모 3 · K개 장치, M개 마이크로배치의 타임라인

칸 안의 숫자는 마이크로배치 번호 m입니다(논문 그림의 Fk,m, Bk,m). 각 칸은 바로 앞 의존이 끝나야 시작합니다. Fk,m은 Fk−1,m을, Bk,m은 Bk+1,m을 기다립니다. ▶ 재생을 누르면 시간이 흐르고, 세로선 위치에서 일하는 장치 수가 아래에 나옵니다. K = 4, M = 4가 논문 그림 2c입니다.

순전파 F역전파 B동기식 갱신아래: 그 순간 일하는 장치 수

M = 1에서는 데모 2와 똑같이 계단 하나가 올라갔다 내려옵니다. M을 키우면 계단의 경사는 그대로인데 가운데 모든 장치가 동시에 일하는 구간이 길어집니다. 비어 있는 칸은 두 삼각형, 순전파가 파이프라인을 채우는 동안의 왼쪽 위와 역전파가 비워 나가는 오른쪽 아래, 그리고 순전파와 역전파 사이에 끼인 가운데 부분에만 남습니다. 논문은 이 빈 공간을 버블이라고 부릅니다.

4. 버블은 얼마나 남는가

모든 셀의 계산 시간이 같다고 두고 세어 봅니다. 한 장치가 마이크로배치 하나의 F에 tF, B에 tB를 씁니다. 장치 k의 첫 F는 앞 장치 k개가 첫 조각을 넘겨줄 때까지 기다려야 하므로 k·tF 늦게 시작하고, 마지막 장치는 (K−1)·tF 늦게 시작합니다. 순전파가 모두 끝나는 데 (M + K − 1)·tF, 역전파도 같은 이유로 (M + K − 1)·tB입니다. 그동안 장치 하나가 실제로 일한 시간은 M·(tF + tB)입니다.

$$\text{버블 비율} = 1 - \frac{M\,(t_F+t_B)}{(M+K-1)\,(t_F+t_B)} = \frac{K-1}{M+K-1}$$ 2.3절은 이것을 O((K−1)/(M+K−1))로 적습니다. tB/tF 비율이 지워지므로 역전파가 순전파보다 몇 배 느리든 상관없습니다. 데모 3에서 역전파 비율 슬라이더를 움직여도 버블 비율이 바뀌지 않는 것을 확인할 수 있습니다.

논문은 실험에서 M ≥ 4K이면 버블이 무시할 만했다고 보고합니다. 다만 위 식을 그대로 넣으면 M = 4K일 때 (K−1)/(5K−1)이라 K = 8에서 18%, K가 커지면 20%에 다가가서 “무시할 만하다”고 하기에는 큰 값입니다. 논문은 그 이유의 일부로, 재계산(6절)을 쓰면 역전파 앞부분의 순전파 재계산은 위쪽 셀의 기울기를 기다릴 필요가 없어 버블 안으로 미리 당겨 할 수 있다는 점을 듭니다. 실제 측정은 아래 표 2처럼 식보다 나은 경우가 있습니다.

데모 4 · M과 K에 따른 버블, 그리고 표 2

곡선은 K = 2, 4, 8, 16에서 버블 비율 (K−1)/(M+K−1)입니다(가로축 M은 로그 눈금). 점선은 M = 4K 지점입니다. 아래 표는 논문 표 2의 Transformer-48 처리량(K = 2, M = 1을 1로 둔 상대값)과, 같은 기준으로 맞춘 이상적인 모형 K·M/(M+K−1)을 나란히 둡니다.

Transformer-48K = 2K = 4K = 8

각 칸: 논문 측정값 / 모형. 논문 표 2 주석대로 배치 크기는 메모리에 맞게 조정되었을 수 있습니다.

M = 32 행은 모형과 8% 안에서 맞습니다. K를 2에서 8로 4배 늘렸을 때 1.8 → 6.3, 즉 3.5배라는 논문의 말이 여기서 나옵니다. M = 4 행은 측정이 모형보다 훨씬 높은데, 위에서 말한 재계산의 앞당김과 배치 크기 조정이 섞였을 것으로 보입니다. M = 1 행의 K = 8 값 1.3은 모형(1.0)보다 30% 높은데, 논문에는 이 차이에 대한 설명이 없습니다. 3절은 이 행을 두고 “M = 1이면 사실상 파이프라인 병렬이 없고, 장치 수와 상관없이 처리량이 비교적 일정해 한 번에 한 장치만 계산하고 있음을 보여 준다”고만 적었습니다. 표 2 주석의 “필요하면 메모리에 맞게 배치 크기를 조정했다”는 문장이 원인일 수는 있지만, 어느 칸의 배치를 얼마로 바꿨는지는 밝히지 않아 확인할 수 없습니다.

이후 논문들(Megatron-LM 등)은 버블을 전체 시간에 대한 비율이 아니라 이상적인 계산 시간에 대한 비율로 (K−1)/M이라고 적습니다. 분모가 다를 뿐 같은 양입니다. 위 readout에 둘 다 표시했습니다.

5. 고르게 자르기: 분할과 불균형

4절의 식은 모든 셀이 똑같이 오래 걸린다는 가정 위에 있습니다. 실제로는 그렇지 않습니다. 파이프라인은 가장 느린 셀의 속도로 흐르기 때문에, 다른 셀들은 그 셀을 기다리며 놉니다. 그래서 GPipe는 층별 비용 추정치 cl로 셀 비용 Ck를 구하고, 셀 비용들의 분산이 최소가 되도록 자릅니다(2.2절). 셀 비용의 합은 자르는 방식과 상관없이 같으므로, 분산을 최소화하는 것은 제곱합 Σ Ck²를 최소화하는 것과 같고, 연속 구간 분할이라 동적 계획법으로 정확히 풀 수 있습니다.

데모 5 · 24개 층을 K개 셀로 자르기

막대는 층별 계산 비용입니다(색 = 셀). 비용 모양 버튼 중 균등은 모든 층이 같은 Transformer형이고, 나머지 둘은 설명을 위해 만든 가상의 프로파일입니다(AmoebaNet의 실제 층별 비용이 아닙니다). 아래 막대는 셀별 비용 합이고, 점선이 가장 느린 셀입니다.

“층 수로 똑같이”에 뒤로 갈수록 무거운 비용을 주면 마지막 셀이 다른 셀의 몇 배가 되고, 이상적인 가속이 K보다 한참 낮아집니다. 분산 최소화로 바꾸면 앞쪽 셀은 층을 많이, 뒤쪽 셀은 적게 가져가 다시 고르게 됩니다. 그래도 층 하나보다 잘게 자를 수는 없어서, 무거운 층이 하나 있으면 그 층이 상한이 됩니다. 논문은 AmoebaNet의 가속이 선형에 못 미친 이유(표 2에서 K = 8, M = 32일 때 Transformer 6.3, AmoebaNet 3.48)와 최대 모델 크기가 장치 수에 비례해 늘지 않은 이유(표 1)를 모두 층마다 파라미터와 계산이 고르지 않은 것에서 찾고, 더 나은 분할 알고리즘이 성능을 올릴 여지가 있다고 적었습니다.

6. 재계산으로 활성 메모리 갚기

역전파에서 층 i의 기울기 bi를 구하려면 위층에서 내려온 기울기 bi+1과 함께 순전파 때의 활성 fi(x)가 필요합니다. 그래서 보통은 순전파 동안 모든 층의 활성을 남겨 두고, 그 메모리는 O(N × L)입니다. 파이프라인에서는 사정이 더 나쁩니다. 장치 0은 첫 마이크로배치의 역전파가 돌아올 때까지 M개 조각의 활성을 모두 들고 있어야 합니다.

GPipe는 Chen et al.(2016)의 재계산(re-materialization, gradient checkpointing이라고도 부릅니다)을 장치마다 적용합니다. 순전파 때는 셀 경계의 출력만 저장하고 셀 내부 활성은 버립니다. 역전파 차례가 오면 저장해 둔 경계 입력으로 Fk를 다시 계산해 내부 활성을 복원하고, 그 마이크로배치의 역전파가 끝나면 다시 버립니다.

$$\underbrace{O(N \times L)}_{\text{재계산, 분할 없음}} \quad\longrightarrow\quad O\!\Big(N + \frac{L}{K}\times\frac{N}{M}\Big)$$ 2.3절. 앞의 N은 M개 마이크로배치 각각의 경계 입력(N/M × M), 뒤의 항은 지금 역전파 중인 마이크로배치 하나(N/M개 예제)의 셀 내부 활성(L/K개 층)입니다. 이 데모에서는 “예제 하나가 층 하나에서 남기는 활성”을 1칸으로 셉니다.

데모 6 · 한 장치가 들고 있는 활성, 시간에 따라

데모 3과 같은 GPipe 스케줄을 돌리면서 고른 장치의 활성 메모리를 시간에 따라 그립니다. 재계산을 켜면 역전파마다 순전파를 한 번 더 하므로(보라색) 한 스텝이 길어집니다.

저장된 활성재계산으로 잠깐 복원한 활성재계산 없이 (비교용)

재계산이 없으면 M을 키워도 정점은 N·L/K로 그대로입니다. 마이크로배치를 쪼개도 결국 M개 조각을 전부 들고 있어야 하니까요. 재계산을 켜면 정점이 N + (L/K)(N/M)으로 내려가고, 이제 M을 키울수록 둘째 항이 줄어듭니다. 버블을 줄이려고 키운 M이 메모리까지 줄여 주는 셈입니다. 이것이 6절 끝에서 논문이 “재계산과 결합하면 많은 수의 마이크로배치로 확장할 수 있다”고 말하는 이유입니다. 대가는 시간입니다. 역전파가 순전파의 두 배라면 재계산은 계산량을 3 → 4, 약 33% 늘립니다.

이 효과는 장치 하나에서도 나타납니다. 표 1의 Pipeline-1 열은 장치 1대에 재계산과 배치 쪼개기만 적용한 경우인데, AmoebaNet의 활성 메모리가 6.26GB에서 3.46GB로 줄어 82M 대신 318M 모델이 들어갔고, Transformer는 2.7배 큰 모델(3층 → 13층)이 들어갔습니다.

7. 쪼개도 같은 기울기: 동기식 누적

마이크로배치로 쪼갠 뒤 기울기를 더하는 것이 왜 원래 미니배치의 기울기와 같은지는 손실의 정의에서 바로 나옵니다. 미니배치 손실은 예제별 손실의 평균이고, 미분은 덧셈을 지나갑니다.

$$\nabla_\theta \frac{1}{N}\sum_{n=1}^{N}\ell_n(\theta) \;=\; \sum_{m=1}^{M}\;\underbrace{\frac{1}{N}\sum_{n\in\text{조각 }m}\nabla_\theta\,\ell_n(\theta)}_{\text{마이크로배치 } m \text{의 기여}}$$ 조건은 하나, 모든 조각의 기울기를 같은 θ에서 계산하는 것입니다. GPipe는 미니배치가 끝날 때까지 갱신하지 않으므로 이 조건이 지켜집니다. 그래서 논문은 “분할 수와 상관없이 기울기 갱신이 일관된다”고 말합니다(1절, 7절).

예외는 배치 전체를 보는 층입니다. BatchNorm은 학습 중 통계를 미니배치가 아니라 마이크로배치마다 계산하게 되므로 결과가 달라집니다. 논문은 이 경우 통계를 마이크로배치(필요하면 복제본들)에 대해 계산하고, 평가용 이동 평균은 미니배치 전체에 대해 따로 추적한다고 밝힙니다(2.2절).

데모 7 · 브라우저에서 학습시켜 비교하기

2 → 16 → 16 → 16 → 1 MLP(tanh, 마지막은 시그모이드)를 두 개의 반달 데이터 64점으로 학습합니다. 층 4개를 “장치” 4개에 하나씩 두었다고 보고, 같은 초기값에서 네 방법을 나란히 돌립니다. ① 한 장치·미니배치 전체, ② GPipe(마이크로배치 M개를 순전파 → 역순 역전파 → 기울기 누적 → 한 번 갱신), ③ 마이크로배치마다 바로 갱신(학습률 ÷ M), ④ d스텝 전의 가중치로 계산한 기울기로 갱신(비동기 파이프라인의 지연). 학습률 0.3, 400스텝.

① 한 장치② GPipe③ 조각마다 갱신④ 지연된 기울기격자 그림: ② 모델의 결정 경계

②의 손실 곡선은 ①과 정확히 겹치고, 두 모델 가중치의 최대 차이는 10−15 근처입니다. 덧셈 순서가 달라서 생기는 부동소수점 반올림 차이일 뿐입니다. M을 바꿔 다시 돌려도 마찬가지입니다. ③은 M에 따라 다른 경로로 학습됩니다. 더 빨리 내려갈 때도 있지만 그건 “다른 알고리즘”이라는 뜻이고, 장치 수를 바꿀 때마다 하이퍼파라미터를 다시 맞춰야 한다는 뜻이기도 합니다. ④는 9절의 PipeDream이 피하려고 애쓴 문제를 보여 줍니다. 지연 d를 키우면 손실이 출렁이거나 잘 내려가지 않습니다.

이 계산이 정확히 같으려면 층에 배치 통계가 없어야 합니다. 이 MLP에는 BatchNorm이 없습니다. 또 학습률을 크게 올리면 학습이 혼돈스러워져 10−15의 반올림 차이도 수백 스텝 뒤 눈에 보이게 커질 수 있습니다. 수학은 같아도 부동소수점 결과까지 비트 단위로 같다는 보장은 아닙니다.

8. 논문의 결과

얼마나 큰 모델이 들어갔나 (표 1)

AmoebaNet은 224 × 224 입력, 미니배치 128에서 장치당 8GB, Transformer는 앞의 설정으로 장치당 16GB에서 잰 값입니다. Naive-1은 GPipe 없는 한 장치, Pipeline-k는 GPipe로 k개 장치에 나눈 경우입니다.

데모 8 · 표 1을 장치당 메모리로 다시 보기

막대는 (파라미터 메모리 + 정점 활성 메모리) ÷ 장치 수, 즉 장치 한 대가 평균적으로 들고 있는 양입니다. 점선은 장치 하나의 메모리입니다. 막대 위 숫자는 그 설정에서 올린 최대 파라미터 수입니다.

파라미터 메모리 ÷ K정점 활성 메모리 ÷ K

모든 열이 장치 메모리를 80~95%쯤 채운 곳에서 멈춰 있습니다. 표가 “들어가는 한 최대로 키운 모델”을 잰 것이니 당연한 모양입니다. Transformer는 층마다 크기가 같아서 장치 수에 정확히 비례해 커졌고(8 → 32 → 128장에서 103 → 415 → 1663층), 128장에서 한 장치 대비 298배인 839억 파라미터에 도달했습니다. AmoebaNet은 8장에서 18억으로, 논문 표현으로 GPipe 없이 가능한 것의 25배에 그쳤습니다. 층마다 파라미터가 고르지 않아 분할이 완벽하지 않았기 때문입니다(5절).

고속 연결 없이도 (표 3)

NVLink 없이 PCI-E로만 연결된 P100 GPU 여러 장에서 M = 32로 잰 처리량입니다. 장치 사이에 오가는 것이 셀 경계의 활성뿐이라 통신 대역폭이 병목이 되지 않았고, 가속은 TPU에서와 비슷했습니다.

GPU, M = 32K = 2K = 4K = 8
AmoebaNet-D (18, 128)11.72.7
Transformer-2411.83.3

키운 모델로 얻은 것: 이미지 분류 (4절, 표 4)

AmoebaNet-B(18, 512)를 채널 수를 늘리고 입력을 480 × 480으로 키워 5억 5,700만 파라미터로 만들고, 4개 파티션으로 나눠 ImageNet을 학습했습니다. 단일 크롭 top-1 84.4%, top-5 97%입니다. 이 모델을 다른 데이터셋에 미세 조정한 결과(5회 평균)는 다음과 같습니다.

데이터셋학습클래스GPipe (%)이전 최고 (%)
ImageNet-20121,281,167100084.483.9 (비공개 데이터로 85.4*)
CIFAR-1050,0001099.098.5
CIFAR-10050,00010091.389.3
Stanford Cars8,14419694.694.8*
Oxford Pets3,6803795.993.8*
Food-10175,75010193.090.4*
FGVC Aircraft6,66710092.792.9*
Birdsnap47,38650083.680.2*

표 4. 별표(*)는 논문 표기를 그대로 옮긴 것입니다. ImageNet의 85.4%는 비공개 Instagram 데이터로 사전학습한 Mahajan et al.의 결과입니다.

키운 모델로 얻은 것: 다국어 번역 (5절, 표 5)

102개 언어 ↔ 영어, 언어당 104~109개, 총 250억 개의 문장 쌍으로 Transformer 하나를 학습했습니다. 4억 파라미터의 T(6, 8192, 16)에서 시작해 깊게(T(24, 8192, 16), 13억), 넓게(T(12, 16384, 32), 13억), 그리고 30억(T(32, 16384, 32))과 60억(T(64, 16384, 32))까지 키웠고, 큰 모델들은 2·4·8·16개 가속기에 나눴습니다. T(L, H, A)는 인코더·디코더 각 L층, 피드포워드 차원 H, 어텐션 헤드 A이고 모델 차원은 1024로 고정입니다.

9. 다른 병렬화와 비교: 데이터·텐서 병렬, PipeDream, 1F1B

논문 6절은 대규모 모델 병렬의 여러 접근을 “각자 다른 절충을 골랐다”는 관점에서 비교합니다. 여기에 논문 이후에 자리 잡은 1F1B 스케줄까지 더해 정리합니다.

방식무엇을 나누나장치 사이 통신한계
데이터 병렬배치 (모델은 장치마다 통째로 복제)스텝마다 기울기 전체를 AllReduce모델 전체가 한 장치에 들어가야 함. 단순해서 가장 널리 쓰임
텐서 병렬 (SPMD, Mesh-TensorFlow)층 하나의 행렬곱층마다 AllReduce 계열 연산이 많음고속 연결이 필요. 합성곱처럼 나누기 까다로운 연산이 있음. 깊이를 키우려면 층마다 더 잘게 나눠야 해서 통신이 더 늘어남
GPipe층의 순서열 (+ 배치를 마이크로배치로)셀 경계의 활성만, 마이크로배치마다층 하나는 한 장치에 들어가야 함. 배치 통계를 쓰는 층은 따로 처리. 버블
PipeDream층의 순서열셀 경계의 활성순전파와 역전파를 섞어 비동기로 갱신해 가중치가 낡음. 장치마다 여러 버전의 가중치를 보관해야 해서 큰 모델로 키우기 어려움

GPipe가 셋째 줄에서 내세우는 것은 두 가지입니다. 통신이 셀 경계에서만 일어나 고속 연결이 없어도 되고(표 3), 갱신이 동기식이라 7절처럼 결과가 분할 방식과 무관합니다. 각주 3에서는 층 하나가 한 장치에 안 들어가는 한계를 피하는 방법으로 큰 행렬곱을 작은 것들로 쪼개 여러 층에 나눠 두는 것을 제안합니다. 실제로 이후의 초거대 모델 학습은 데이터·텐서·파이프라인 병렬을 함께 쓰는 쪽으로 갔습니다(Megatron-LM, 2021).

1F1B: 같은 버블, 더 적은 활성

GPipe 스케줄은 M개 조각의 순전파를 모두 끝낸 뒤에야 역전파를 시작하므로, 장치 0은 M개 조각의 경계 활성을 한꺼번에 들고 있습니다. PipeDream이 쓴 1F1B(one-forward-one-backward) 순서는 파이프라인이 찬 뒤로 순전파 하나, 역전파 하나를 번갈아 합니다. 역전파가 일찍 시작되니 끝난 조각의 활성을 일찍 버릴 수 있습니다. PipeDream은 이것을 비동기로 돌렸지만, 미니배치 끝에서 파이프라인을 비우고(flush) 한 번에 갱신하면 GPipe와 같은 동기식 학습이 됩니다(PipeDream-Flush, Narayanan et al. 2021).

데모 9 · GPipe 순서와 1F1B 순서

위는 GPipe(F 전부 → B 전부), 아래는 1F1B(장치 k는 K−1−k개의 F로 파이프라인을 채운 뒤 F·B 교대). 두 스케줄 모두 미니배치 끝에 동기식으로 갱신합니다. 오른쪽 숫자는 그 장치가 동시에 활성을 들고 있던 마이크로배치의 최대 개수입니다.

GPipe
1F1B (동기식, flush)

두 타임라인의 길이는 같습니다. 버블은 둘 다 (K−1)/(M+K−1)입니다. 다른 것은 오른쪽 숫자입니다. GPipe는 모든 장치가 M개를, 1F1B는 장치 k가 K−k개 이하를 들고 있습니다. M을 24까지 올려도 1F1B의 장치 0은 K개에서 멈춥니다. 그래서 버블을 줄이려고 M을 크게 잡을 때 1F1B는 활성 메모리가 M과 무관해지고, 이후의 파이프라인 병렬 구현은 대부분 이 순서를 씁니다. 반대로 GPipe는 이 부담을 재계산(6절)으로 줄였습니다. 두 방법은 함께 쓸 수도 있습니다.

같은 시리즈의 How SGD Works는 미니배치 기울기를, How Adam Works는 옵티마이저 상태가 학습 메모리에서 차지하는 몫을, How Transformer Works는 이 페이지가 파라미터를 센 Transformer 블록을 다룹니다.

10. 확인 퀴즈

각주

  1. 표 1의 두 군데 어긋남. Transformer Naive-1 열은 파라미터 메모리 11.7G, 정점 활성 3.15G로 적혀 있습니다. 그런데 282.2M × 12바이트 = 3.15GiB이고, 다른 네 열은 모두 “파라미터 수 × 12바이트”가 파라미터 메모리 칸과 맞습니다(785.8M → 8.8G, 5.3B → 59.5G …). 이 열만 두 행이 뒤바뀐 것으로 보이고, 이 페이지는 파라미터 3.15G, 활성 11.7G로 읽었습니다. 합(14.85G)은 어느 쪽으로 읽어도 같아서 데모 8의 막대 높이에는 영향이 없습니다.

    또 표 1 머리에는 AmoebaNet 쪽이 “NVIDIA GPUs (8GB each)”로 적혀 있지만 본문 3절은 같은 실험을 “8GB 메모리의 Cloud TPUv2”에서 했다고 적습니다. 어느 쪽이든 장치당 8GB라는 점은 같습니다. ↩