AI 인프라
큰 모델을 학습하고 서빙하는 데 GPU가 얼마나, 왜 필요한지를 계산량·메모리·통신·비용의 숫자로 어림해 보고, 병렬화와 혼합 정밀도, 연속 배칭과 PagedAttention 같은 기법이 어느 병목을 푸는지 정리한다.
목차 (13)
GPU라는 부품 자체는 GPU 노트에, 이 노트에서 쓰는 KV 캐시·양자화·추측 디코딩의 원리는 LLM 노트에 있다. 제품 사양 숫자는 계산의 기준점으로 쓰려고 NVIDIA H100의 공식 사양표에서 가져왔고(2026년 10월 기준), 더 새 세대 GPU는 숫자가 더 크지만 계산 방법은 같다.
학습에 드는 계산량: 6·N·D
트랜스포머 학습의 계산량은 파라미터 수 N과 학습 토큰 수 D만으로 꽤 정확히 어림된다. 토큰 하나가 순전파에서 지나갈 때 가중치마다 곱셈·덧셈을 한 번씩 하므로 약 2N FLOPs, 역전파는 입력 쪽 기울기와 가중치 기울기를 둘 다 구하느라 그 두 배인 4N이다. 합치면 토큰당 6N, 전체는 C ≈ 6·N·D다(Kaplan 등 2020). 어텐션 계산은 컨텍스트가 아주 길지 않으면 작은 몫이라 뺀다.
이 5.88 × 10²³ FLOPs를 H100으로 돌려 보자. 사양표의 BF16 성능 1,979 TFLOPS는 희소성을 쓴 값이라 일반(밀집) 행렬 곱은 그 절반인 약 990 TFLOPS다. 실제 학습은 통신·메모리 대기 때문에 최고 성능을 다 쓰지 못한다. 실제로 쓴 비율을 MFU(model FLOPs utilization, PaLM 논문이 제안한 지표)라 하고, 여기서는 40%로 가정한다.
- GPU 시간: 5.88 × 10²³ ÷ (990 × 10¹² × 0.4) ≈ 412,666 GPU·시간
- GPU 1,024장이면 약 16.8일. 실제로는 장애로 멈췄다 다시 시작하는 시간, 실험과 실패한 학습이 더해진다.
GPU 수천 장을 몇 주씩 돌리면 장애는 예외가 아니라 일상이다. Llama 3 논문은 최대 H100 1만 6천 장으로 405B 모델을 사전 학습한 54일 동안 학습이 466번 멈췄고, 그중 419번이 예정에 없던 중단이었다고 밝혔다. 예상 못 한 중단의 58.7%가 GPU 문제였고, GPU의 HBM3 메모리만 17.2%를 차지했다. 그런데도 실제 학습에 쓴 시간은 90%를 넘었다. 그래서 큰 학습은 모델 상태를 주기적으로 저장하는 체크포인트, 고장 난 노드를 빼고 바로 이어 가는 자동 재시작, 느려진 GPU를 찾아내는 감시를 기본으로 갖춘다.
GPU 메모리: 학습과 추론
학습할 때 GPU 메모리에는 가중치만 있는 것이 아니다. 혼합 정밀도와 Adam 옵티마이저를 쓰는 흔한 설정에서 파라미터 하나가 차지하는 양은 다음과 같다(ZeRO 논문의 셈).
- 가중치 BF16 2바이트, 기울기 BF16 2바이트
- 옵티마이저 상태 12바이트: 정밀한 갱신을 위한 FP32 가중치 사본, Adam의 모멘텀과 분산(각 FP32 4바이트)
- 합계 파라미터당 16바이트. 7B 모델이면 112.0GB로, 80GB GPU 한 장에 모델 상태조차 들어가지 않는다.
여기에 역전파에 쓰려고 순전파 중간값을 저장하는 활성값이 더해진다. 층마다 s·b·h·(34 + 5·a·s/h) 바이트로 어림한다(s 시퀀스 길이, b 배치, h 은닉 크기, a 헤드 수, Korthikanti 등 2022). 7B 모양(층 32, h 4096, 헤드 32)에 시퀀스 4096·배치 1이면 104.2GB인데, 뒤의 5·a·s/h 항이 어텐션 점수 행렬이다. FlashAttention처럼 점수 행렬을 메모리에 쓰지 않고 칩 안에서 타일로 계산하는 커널을 쓰면 18.3GB로 준다. 남은 활성값은 일부를 버렸다가 역전파 때 다시 계산하는 활성값 재계산(체크포인팅)으로 더 줄인다.
추론은 훨씬 가볍다. BF16 가중치 14.0GB와 요청마다의 KV 캐시면 된다. 그래서 학습은 GPU 여러 장에 나눠야 하는 문제가 되고, 추론은 KV 캐시를 얼마나 잘 관리해 요청을 많이 받느냐의 문제가 된다.
병렬화: 데이터, 텐서, 파이프라인, ZeRO/FSDP
모델과 데이터가 GPU 한 장에 들어가지 않거나 너무 오래 걸리면 여러 장에 나눈다. 무엇을 나누느냐에 따라 네 가지가 있고, 큰 학습은 이들을 겹쳐 쓴다.
- 데이터 병렬: 모델을 GPU마다 통째로 복제하고 배치를 나눈다. 각자 기울기를 구한 뒤 all-reduce로 평균해 모두 같은 갱신을 한다. 가장 단순하지만 GPU마다 모델 상태 전체가 들어가야 한다.
- 텐서 병렬(Megatron-LM): 층 안의 큰 행렬을 열·행으로 나눠 GPU들이 한 행렬 곱을 함께 한다. 층마다 결과를 합치는 통신이 있어 아주 빠른 연결이 필요하므로 보통 한 서버(노드) 안에서만 쓴다.
- 파이프라인 병렬(GPipe): 층을 구간으로 나눠 GPU마다 맡긴다. 주고받는 것이 구간 경계의 활성값뿐이라 노드 사이에도 쓸 수 있지만, 앞 GPU가 일을 넘겨줄 때까지 뒤 GPU가 노는 거품이 생긴다.
- ZeRO / FSDP(ZeRO, PyTorch FSDP): 데이터 병렬인데, GPU마다 똑같이 들고 있던 상태를 1/N씩 나눠 가진다. 층을 계산할 차례가 되면 그 층의 가중치만 all-gather로 모아 쓰고 버린다. 통신은 늘지만 메모리가 GPU 수에 반비례해 준다.
거품 비율은 (p − 1) / (m + p − 1)이다(p 단계 수, m 마이크로배치 수). 4단계에 마이크로배치 6개면 33%, 32개면 8.6%다. 그래서 배치를 잘게 많이 쪼개고, 순전파와 역전파를 엇갈려 끼워 넣는 일정(1F1B 등)으로 거품과 활성값 메모리를 함께 줄인다.
GPU 8장이면 GPU 한 장의 모델 상태가 112.0GB → 38.5GB → 26.3GB → 14.0GB로 준다. 실제 큰 학습은 노드 안에서 텐서 병렬, 노드 사이에서 파이프라인 병렬, 그 바깥을 데이터 병렬(또는 FSDP)로 겹쳐 쓴다(Narayanan 등 2021). 통신이 많은 병렬일수록 빠른 연결 안쪽에 두는 것이 원칙이다.
혼합 정밀도: FP32, BF16, FP8
숫자 형식의 비트 수를 줄이면 메모리와 통신이 줄고, GPU의 텐서 코어도 더 빨리 계산한다. 부동소수점 수는 부호·지수·가수로 이루어지는데, 지수 비트가 표현 범위를, 가수 비트가 정밀도를 정한다.
- FP16 + 손실 배율: Micikevicius 등(2017)은 계산은 FP16으로 하고 FP32 가중치 사본을 두어 갱신했다. FP16은 최댓값이 65,504로 범위가 좁아 작은 기울기가 0으로 사라지므로, 손실에 큰 수를 곱해 계산한 뒤 나누는 손실 배율이 필요하다.
- BF16: 지수 8비트로 FP32와 범위가 같아 손실 배율 없이 쓰기 쉽다. 오늘날 LLM 학습의 기본 형식이다.
- FP8(FP8 Formats 논문): 순전파의 가중치·활성값은 정밀도가 중요해 E4M3(최댓값 448), 역전파의 기울기는 범위가 중요해 E5M2(최댓값 57,344)를 쓰자고 제안했다. 범위가 아주 좁아 텐서마다 배율을 두고 계속 조정해야 한다. H100부터 FP8 텐서 코어가 들어갔다.
GPU를 잇는 길: NVLink와 InfiniBand
병렬화는 GPU끼리 데이터를 주고받아야 성립하므로, 연결의 속도가 곧 어떤 병렬을 어디에 쓸지를 정한다. 연결은 두 층이다.
- 노드 안: NVLink는 GPU끼리 직접 잇는 연결이다. H100 SXM은 GPU당 900GB/s(양방향 합)다. 8장짜리 서버 안에서는 NVLink 스위치로 모든 GPU가 이 속도로 서로 통한다.
- 노드 사이: GPU마다 네트워크 카드를 하나씩 붙여 InfiniBand나 RDMA 이더넷으로 잇는다. ConnectX-7 카드가 400Gb/s, 곧 50GB/s다. NVLink보다 한 자릿수 이상 좁다.
7B 모델의 BF16 기울기 14.0GB를 GPU 8장이 링 all-reduce로 평균하면 GPU마다 크기의 2(n−1)/n배인 24.5GB를 보낸다. NVLink(한 방향 450GB/s)로는 약 54ms, 50GB/s 네트워크로는 약 490ms다. 실제로는 역전파가 끝난 층의 기울기부터 미리 보내 계산과 통신을 겹친다. 그래도 층마다 통신하는 텐서 병렬은 노드 안에, 덜 자주 통신하는 데이터·파이프라인 병렬은 노드 사이에 두는 이유가 이 차이다.
GPU 말고: TPU와 AI 가속기
AI 계산의 대부분은 큰 행렬 곱이라, 그것만 잘하도록 만든 전용 칩도 쓰인다. 대표가 Google의 TPU다. Cloud TPU 안내에 따르면 TPU는 머신러닝용 주문형 반도체(ASIC)로, 핵심인 행렬 장치(MXU)가 128×128 시스톨릭 배열이고 칩 옆에 HBM을 둔다. 여러 칩을 묶은 슬라이스로 키우는 점은 GPU 클러스터와 같다.
- 컴파일러가 핵심: 모델의 행렬 계산을 XLA 컴파일러가 TPU 명령으로 바꾼다. 행렬 크기가 MXU를 다 채우지 못하면 0으로 채워 계산을 버리므로, 배치나 특징 차원 하나를 128의 배수로 맞추라고 권한다.
- 맞는 일, 안 맞는 일: 행렬 곱 위주로 오래 도는 큰 학습, 큰 배치에 맞고, 분기가 잦거나 텐서 모양이 계속 바뀌거나 사용자 정의 연산이 많은 모델에는 덜 맞는다. 그런 모델과 빠른 실험에는 GPU가 유연하다.
- 클라우드 회사들의 자체 학습·추론 칩, 휴대폰·노트북의 NPU(아래 기기 안 추론 절)도 같은 발상이다. 칩마다 지원하는 연산과 소프트웨어가 달라, 하드웨어를 고를 때는 성능표만큼 프레임워크 지원을 함께 본다.
서빙 1: 프리필과 디코드
LLM 요청 하나의 처리는 성격이 전혀 다른 두 단계로 나뉜다. 이 차이를 알아야 서빙 성능 숫자를 읽을 수 있다.
요청이 도착했다. 프롬프트는 6토큰이다.
- 프리필: 프롬프트 토큰을 한꺼번에 넣어 KV 캐시를 채우고 첫 토큰을 낸다. 토큰 수만큼 큰 행렬 곱이라 계산 성능이 한계다. 7B 모델에서 프롬프트 2,000토큰은 2 × 7B × 2,000 FLOPs로, H100에서 성능의 절반을 쓴다고 보면 약 57ms다.
- 디코드: 실행마다 토큰 하나를 만든다. 계산은 적은데 가중치 14.0GB를 매번 다 읽어야 하므로 메모리 대역폭이 한계다. 3.35TB/s로 나누면 배치 1에서 한 단계에 약 4.3ms, 초당 235토큰이 상한이다.
- 그래서 프롬프트 2,000토큰의 프리필은 출력 약 13토큰을 디코드하는 시간과 비슷하다. 첫 토큰까지의 시간(TTFT)은 프롬프트 길이에, 토큰 사이 간격(TPOT)은 디코드 성능에 달린다.
두 단계가 한 GPU에서 섞이면 긴 프리필이 들어올 때마다 다른 요청의 디코드가 멈칫한다. 긴 프리필을 잘게 나눠 디코드 사이에 끼우거나, DistServe처럼 프리필과 디코드를 아예 다른 GPU에 나눠 돌리는 방법이 쓰인다.
서빙 2: 연속 배칭과 PagedAttention
디코드는 가중치를 읽는 시간이 대부분이라, 한 번 읽을 때 요청 여럿의 토큰을 함께 계산하면(배칭) 거의 공짜로 처리량이 는다. 문제는 요청마다 답 길이가 다르다는 것이다.
- 연속 배칭(Orca, 2022): 요청 단위가 아니라 디코드 한 단계마다 배치를 다시 짠다. 끝난 요청은 바로 빼고 기다리던 요청을 바로 넣는다. 오늘날 LLM 서빙 엔진의 기본이다.
- KV 캐시가 발목을 잡는다: 배치를 키우려면 요청마다 KV 캐시 자리가 있어야 한다. 예전 방식은 요청마다 최대 길이만큼 연속된 메모리를 미리 잡았다. 최대 2,048토큰 자리를 잡았는데 실제 답이 300토큰이면 85%가 빈자리로 버려진다.
- PagedAttention(vLLM, 2023): 운영체제의 가상 메모리 페이지처럼 KV 캐시를 작은 블록으로 나누고, 요청마다 블록 표로 흩어진 블록을 잇는다. 필요할 때 한 블록씩 받으므로 낭비는 마지막 블록의 빈칸뿐이다. 블록 16토큰이면 같은 예에서 1.3%다. 같은 프롬프트로 시작하는 요청끼리 블록을 함께 쓸 수도 있다.
vLLM 논문은 이렇게 아낀 메모리로 배치를 키워, 같은 지연에서 처리량을 2~4배 올렸다고 보고했다. 직접 서빙해 보려면 vLLM 문서가 출발점으로 좋다.
서빙 3: 처리량과 지연
배치를 키우면 GPU 전체가 초당 만드는 토큰(처리량)은 늘지만, 한 단계에 읽을 KV 캐시도 늘어 사용자 한 명이 보는 속도(지연)는 나빠진다. 서빙은 이 둘 사이에서 고르는 일이다.
- 배치 1에서 64로 키우면 전체 처리량은 초당 235에서 6,876토큰으로 약 29배가 되고, 사용자 한 명의 속도는 235에서 107토큰으로 준다.
- 배치 64에서도 계산 시간(2 × 7B × 64 FLOPs)은 메모리 읽기 시간보다 짧아 여전히 대역폭이 한계다. 배치를 더 키우면 언젠가 계산이 한계가 되고, 그 전에 KV 캐시가 메모리를 다 채운다.
- 서비스는 보통 ‘토큰 사이 간격 몇 ms 이하’ 같은 지연 목표를 먼저 정하고, 그 목표를 지키는 가장 큰 배치로 돌린다.
비용 어림하기
출력 토큰 100만 개당 비용은 GPU 시간당 가격 ÷ (초당 토큰 수 × 3,600) × 100만이다. GPU 가격은 계약·지역·시점마다 크게 달라 여기서는 시간당 3달러로 가정한다. 처리량은 앞 절의 어림값을 쓴다.
- 배치 1이면 100만 토큰에 약 3.55달러, 배치 64면 약 0.12달러다. 요청이 늘 몰려 있어야 싼값이 나온다. 밤에 요청이 적으면 GPU를 놀려도 값은 나간다.
- 빠진 것: 프롬프트 처리(입력 토큰), 요청이 고르지 않은 시간대, 장애 대비 여유 GPU, 엔진의 실제 효율. 실제 비용은 이 어림보다 높게 잡는다.
- 반대로 양자화(LLM 노트), MoE, 추측 디코딩, 같은 프롬프트 앞부분의 KV 캐시 재사용은 같은 GPU로 더 많은 토큰을 만들어 비용을 낮춘다.
기기 안 추론
노트북·휴대폰에서 모델을 직접 돌리면 네트워크가 없어도 되고, 데이터가 기기 밖으로 나가지 않으며, 서버 비용이 들지 않는다. 대신 메모리와 대역폭이 작고, 전력과 발열을 아껴야 한다.
- 크기: 기기에서는 보통 1~8B 모델을 4비트로 양자화해 쓴다. 8B를 4비트로 줄이면 약 4.1GB로, 기기 메모리를 다른 앱과 나눠 써야 하는 점을 생각하면 휴대폰에서는 이 정도가 한계에 가깝다.
- 속도: 서버와 마찬가지로 디코드는 메모리 대역폭이 한계다. 토큰마다 가중치를 한 번 다 읽는다고 보면 상한은 대역폭 ÷ 모델 크기다.
- 도구: 개인 컴퓨터에서 공개 모델을 돌릴 때 많이 쓰는 llama.cpp는 의존성 없는 C/C++ 추론 엔진으로, 1.5비트부터 8비트까지의 정수 양자화와 CPU·Apple Metal·CUDA·Vulkan 등 여러 장치를 지원한다. 모델은 이 엔진용 GGUF 파일로 바꿔 쓴다.
- 가속기: 휴대폰·노트북 칩의 NPU는 적은 전력으로 행렬 곱을 하도록 만든 장치다. 모델을 그 칩이 지원하는 형식과 연산으로 바꿔야 해서, 같은 모델이라도 기기용으로 다시 변환·최적화하는 과정이 따른다.
흔한 오해
- "TFLOPS가 높은 GPU일수록 답이 빨리 나온다" — 토큰을 하나씩 만드는 디코드는 계산보다 메모리 대역폭이 한계다(서빙 1 절). 사양표의 FLOPS는 희소성을 쓴 값인 경우도 있어 밀집 행렬 곱 성능은 그 절반일 수 있다.
- "가중치가 VRAM에 들어가면 돌릴 수 있다" — 요청마다 KV 캐시가 더 들고, 긴 컨텍스트나 큰 배치에서는 가중치보다 커진다.
- "GPU를 두 배로 늘리면 학습이 두 배 빨라진다" — 통신, 파이프라인 거품, 장애 복구 시간 때문에 늘 그보다 덜 빨라진다. 그래서 MFU 같은 실제 활용률을 따로 잰다.
- "AI 비용은 대부분 학습에서 든다" — 학습은 한 번이지만 추론은 모델이 쓰이는 내내 든다. 많이 쓰이는 모델일수록 추론 비용의 몫이 커져, 작은 모델에 데이터를 더 쓰고(LLM 노트의 스케일링 절) 양자화·배칭으로 서빙을 아끼는 이유가 된다.
핵심 논문
요지는 직접 정리한 한 줄이다. 더 자세한 요약은 논문 노트에 모은다.
| 논문 | 연도 | 한 줄 요지 |
|---|---|---|
| Mixed Precision Training (Micikevicius 등) | 2017 | FP16으로 계산하되 FP32 가중치 사본과 손실 배율(loss scaling)을 두어 정확도를 잃지 않고 메모리·시간을 줄였다. |
| GPipe (Huang 등) | 2018 | 층을 여러 장치에 나누고 배치를 마이크로배치로 쪼개 흘려 보내는 파이프라인 병렬을 정리했다. |
| Megatron-LM (Shoeybi 등) | 2019 | 어텐션과 MLP의 행렬을 GPU들에 나누는 텐서 병렬로, 코드 몇 줄의 통신만 더해 수십억 파라미터 모델을 학습했다. |
| ZeRO (Rajbhandari 등) | 2019 | 데이터 병렬의 GPU마다 똑같이 들고 있던 옵티마이저 상태·기울기·가중치를 나눠 가져 메모리 중복을 없앴다. |
| Efficient Large-Scale LM Training on GPU Clusters (Narayanan 등) | 2021 | 텐서(노드 안)·파이프라인(노드 사이)·데이터 병렬을 겹쳐 쓰는 3차원 병렬과 그 배치 원칙을 정리했다. |
| FlashAttention (Dao 등) | 2022 | 어텐션을 작은 타일로 나눠 GPU 칩 안 메모리에서 끝내, 큰 점수 행렬을 HBM에 쓰지 않고 정확한 어텐션을 빠르게 계산했다. |
| Reducing Activation Recomputation (Korthikanti 등) | 2022 | 트랜스포머 활성값 메모리를 식으로 정리하고, 시퀀스 병렬과 선택적 재계산으로 재계산 비용을 거의 없앴다. |
| PaLM (Chowdhery 등) | 2022 | 6천여 개 TPU로 540B 모델을 학습하며, 하드웨어를 얼마나 쓰는지 재는 지표 MFU를 제안했다. |
| FP8 Formats for Deep Learning (Micikevicius 등) | 2022 | 가중치·활성값용 E4M3과 기울기용 E5M2 두 FP8 형식을 제안하고 큰 모델 학습에서 16비트와 비슷한 결과를 보였다. |
| Orca (Yu 등, OSDI) | 2022 | 요청 단위가 아니라 반복(토큰) 단위로 배치를 다시 짜는 스케줄링, 곧 연속 배칭을 처음 제안했다. |
| PyTorch FSDP (Zhao 등) | 2023 | ZeRO-3식 완전 분할 데이터 병렬을 PyTorch에 넣으며 겪은 설계와 성능 문제를 정리했다. |
| PagedAttention / vLLM (Kwon 등) | 2023 | KV 캐시를 운영체제의 페이지처럼 블록으로 관리해 낭비를 거의 없애고, 같은 지연에서 처리량을 2~4배 올렸다. |
| DistServe (Zhong 등) | 2024 | 성격이 다른 프리필과 디코드를 다른 GPU에 나눠 돌려 서로 방해하지 않게 하고, 지연 목표를 지키는 처리량을 올렸다. |
- Mixed Precision Training (Micikevicius 등) (2017)
- GPipe (Huang 등) (2018)
- Megatron-LM (Shoeybi 등) (2019)
- ZeRO (Rajbhandari 등) (2019)
- Efficient Large-Scale LM Training on GPU Clusters (Narayanan 등) (2021)
- FlashAttention (Dao 등) (2022)
- Reducing Activation Recomputation (Korthikanti 등) (2022)
- PaLM (Chowdhery 등) (2022)
- FP8 Formats for Deep Learning (Micikevicius 등) (2022)
- Orca (Yu 등, OSDI) (2022)
- PyTorch FSDP (Zhao 등) (2023)
- PagedAttention / vLLM (Kwon 등) (2023)
- DistServe (Zhong 등) (2024)
- NVIDIA H100 제품 페이지 — 사양표
- NVIDIA NVLink와 NVLink 스위치
- NVIDIA InfiniBand 어댑터 (ConnectX)
- Scaling Laws for Neural Language Models (Kaplan 등, 2020)
- vLLM 문서
- The Llama 3 Herd of Models (Llama 팀, 2024) — 학습 중단 통계
- Google Cloud — Introduction to Cloud TPU
- llama.cpp — C/C++ LLM 추론