-
Q4 양자화는 GPU 안에서 어떻게 동작하나 — Blackwell FP8 텐서코어와의 만남IT 2026. 5. 5. 23:30
들어가며 — "Q4는 그럼 어떻게 곱셈을 하지?"
직전 글에서 Blackwell이 FP8을 텐서코어로 네이티브 처리한다고 했다. 메모리에서 곧장 곱셈으로, 변환 단계 없이.
그렇다면 자연스럽게 떠오르는 의문이 있다. Q4 양자화로 4비트까지 줄이면 어떻게 되는가? 메모리는 분명히 4분의 1로 줄어든다. 그런데 텐서코어가 받아주는 형식은 FP16, BF16, FP8, FP4까지인데, Q4는 거기 안 끼어 있다. 그럼 곱셈은 어떻게 되는가?
답을 먼저 말하면, Q4는 메모리에는 4비트로 압축돼 있지만 GPU 안에서 곱셈 직전에 FP16(또는 FP8)으로 풀어준 뒤 텐서코어에 태운다. 압축 zip 파일을 디스크에 두고, 처리할 때만 풀어 읽는 것과 똑같다. 이 글은 그 흐름을 비유와 다이어그램으로 풀어쓴다.
1. Q4가 도대체 무엇인가
Q4는 가중치 한 개를 4비트 정수로 표현하는 양자화 형식이다. 정확히는 -8부터 +7까지(또는 0~15까지) 16개의 값 중 하나로 가중치를 매핑한다.
비트 수만 비교하면 다음과 같다.
FP16 대비 Q4는 4분의 1의 메모리만 쓴다. 70B 모델이라면 FP16에서 140GB가 필요하지만, Q4면 35GB. DGX Spark 같은 단일 GPU에 충분히 올릴 수 있는 크기가 된다.
대신 16개 값으로 가중치를 표현해야 하니 정밀도는 매우 거칠어진다. 그냥 단순히 4비트로 자르면 모델 품질이 무너진다. 그래서 Q4는 단순 4비트가 아니라 블록 단위 양자화라는 트릭을 쓴다.
2. Q4_K_M 블록 구조 — 단순 4비트가 아니다
가장 널리 쓰이는 GGUF의
Q4_K_M을 예로 들면, 256개의 가중치를 하나의 블록으로 묶고, 블록마다 별도로 scale과 min(혹은 zero-point)을 따로 저장한다.핵심 아이디어는 이렇다.
- 256개를 한 블록으로 묶는다. 그 안의 가중치들은 비슷한 분포를 가질 가능성이 높다.
- 블록마다 별도의 scale, min을 둔다. 이 두 값은 FP16으로 정밀하게 저장한다.
- 실제 가중치는 4비트 정수로 저장하고, 사용 시 위 공식으로 복원한다.
비유하자면 256개의 가중치가 모두 0.1과 0.2 사이에 몰려 있다고 해보자. 그러면 그 블록 안에서는 16단계로 잘게 쪼개도 정밀도가 충분하다. 다른 블록이 1000과 2000 사이에 몰려 있다면, 그 블록은 또 그 범위 안에서 16단계로 쪼갠다. 구간을 블록별로 다르게 잡는 것이 압축률은 유지하면서 정밀도를 살리는 비결이다.
즉 Q4_K_M의 4비트 정수 자체는 "16단계 중 어디"라는 인덱스에 가깝다. 진짜 숫자로 풀려면 블록의 scale과 min이 필요하다. 이 풀어주는 작업이 곧 dequantize다.
3. 텐서코어는 Q4를 직접 받을 수 있는가
여기가 핵심이다. 아니다, 못 받는다.
텐서코어가 한 사이클에 통째로 곱할 수 있는 입력 형식은 GPU 세대마다 정해져 있다.
왼쪽이 텐서코어가 곧장 받는 형식들. 오른쪽은 그대로는 못 받는 형식들이다. Q4_K_M, AWQ INT4, GPTQ INT4 모두 오른쪽에 들어간다.
이유는 두 가지다.
- 형식이 표준이 아니다. Q4_K_M은 NVIDIA의 텐서코어 명세에 들어 있지 않다. 회로가 그 형식을 모른다.
- 블록 메타데이터가 섞여 있다. Q4_K_M의 256개 가중치를 풀려면 그 블록의 scale·min을 같이 봐야 한다. 텐서코어 회로는 "두 행렬"을 받지, "행렬 + scale·min"을 받지 않는다.
그래서 Q4 가중치는 곱셈 직전에 풀려야 한다. 이 풀어주는 단계가 dequantize다.
4. dequantize — GPU 안에서 일어나는 압축 해제
실제 흐름을 그리면 이렇다.
그러니까 Q4의 진짜 모습은 이렇다. 가중치는 4비트로 압축돼 있지만, 곱셈 직전에는 결국 FP16(또는 FP8)으로 풀린다. 텐서코어가 본 적 없는 형식은 회로에 못 들어가니까.
이게 핵심 통찰이다. Q4는 "메모리에서는 압축된 채로 살지만, 연산 직전에 평문으로 돌아오는 형식"이다. zip 파일과 똑같다 — 디스크에서는 작지만 처리하려면 풀어야 한다.
5. 두 경로 비교 — Q4+dequantize vs FP8 네이티브
같은 모델을 Q4로 돌리는 경로와 FP8로 돌리는 경로를 나란히 놓고 보자.
비교를 정리하면 이렇다.
관점 Q4 + dequantize FP8 네이티브 VRAM 점유 최소 (가중치 4비트) 중간 (가중치 8비트) 메모리 → SM 트래픽 가장 적음 두 번째 변환 단계 매 GEMM마다 dequantize 없음 행렬곱 회로 FP16 (또는 BF16) FP8 (처리량 2배) 품질 손실 약간 있음 (블록 양자화로 완화) 거의 없음 그래서 결과적으로,
- 모델이 VRAM에 안 들어갈 때: Q4가 답이다. FP8로는 못 올리는데 Q4면 올라간다.
- 모델이 VRAM에 충분히 들어갈 때: FP8 네이티브가 거의 항상 더 빠르다. dequantize 비용이 없고, 텐서코어 처리량 자체가 두 배다.
직전 글에서 Qwen3.6 마이그레이션 시 Ollama Q4 → vLLM FP8로 옮기며 디코드 +8%, 동시 4요청 throughput +148%가 나왔다고 했다. 그게 바로 이 구조의 차이다 — VRAM 여유가 있는 환경(DGX Spark 128GB 통합 메모리)에서는 Q4의 메모리 이득이 의미가 없고, 변환 비용·회로 처리량이 결정한다.
6. 그래서 무엇을 골라야 하나
경험칙을 표로 정리하면 이렇다.
상황 추천 이유 모델이 VRAM에 안 들어감 Q4 (AWQ + Marlin) 유일한 선택지. 메모리 절감이 압도적 VRAM 충분, 단일 사용자 FP8 네이티브 (vLLM) 변환 비용 0, 텐서코어 처리량 2배 VRAM 여유, 동시 요청 많음 FP8 네이티브 처리량 격차가 prefill·동시 요청에서 폭발 품질 민감 (수학·코드 추론) FP8 또는 BF16 Q4는 일부 태스크에서 미세 손실 모바일·엣지 기기 Q4 또는 INT4 메모리·전력이 결정적 한마디로, Q4는 "안 들어갈 모델을 들어가게 만드는 기술"이다. 들어갈 만한 모델을 굳이 Q4로 돌릴 이유는 점점 사라지고 있다 — Blackwell이 FP8 네이티브를 가지면서 그 영역을 전부 가져갔기 때문이다.
7. 정리 — Q4의 진짜 위치
Q4 양자화의 동작을 한 줄로 줄이면 이렇다.
Q4 = "VRAM에서는 4비트로 살지만, 곱셈 직전에는 FP16 또는 FP8로 풀려 텐서코어에 들어가는 형식"
그래서 Q4의 본질은 두 가지다.
- 메모리 효율의 끝. 가중치 크기를 FP16의 4분의 1로 만든다. 큰 모델을 작은 GPU에 올릴 수 있게 해준다.
- 변환 비용을 받아들인다. 텐서코어가 4비트를 직접 처리하지 못하므로, 매 GEMM 직전에 푸는 단계가 들어간다. Marlin 같은 fused 커널이 그 비용을 거의 0으로 만들지만, 회로 자체는 결국 FP16/FP8을 쓴다.
FP8과 Q4는 사실 경쟁이 아니라 역할 분담에 가깝다. FP8은 "들어갈 모델을 가장 빠르게", Q4는 "안 들어갈 모델을 들어가게". 그 사이를 fused 커널과 양자화 알고리즘(AWQ, GPTQ)이 메우는 구조다.
다음에 Q4가 텐서코어에 직접 들어갈 수 있는 날이 올지도 모른다. NVIDIA가 INT4 텐서코어를 Turing 시절에 잠깐 추가했다가 잘 쓰이지 않은 전례가 있고, 최근에는 FP4가 Blackwell에서 1급 시민이 됐다. "네이티브로 처리한다"의 자리가 점점 작은 비트로 옮겨가는 흐름은 직전 글의 결론과 똑같다 — 다음은 FP4 양자화가 Q4의 자리를 위협할 차례다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
vLLM reasoning_parser — <think> 블록을 정규식 말고 구조로 받는 법 (0) 2026.05.06 KV cache FP8로 동시 요청 76배 수용하기 — LLM 메모리의 숨은 주범 정리 (0) 2026.05.06 Ollama에서 vLLM으로 백엔드를 바꿨더니 throughput이 148% 올랐다 — 같은 모델, 다른 엔진 (0) 2026.05.06 Gemma 4에서 Qwen 3.6으로 갈아탔다 — 두 모델을 모든 지표로 비교한 기록 (0) 2026.05.06 Fused 커널은 왜 3~4배 빠른가 — GPU 메모리 계층과 Marlin의 비밀 (0) 2026.05.05 FP8이 왜 FP16보다 45% 빠를까 — Blackwell GPU의 '텐서코어 네이티브 처리' 이해하기 (0) 2026.05.05 TFLOPS가 뭔데? — FLOPS의 정의부터 요즘 GPU의 실제 수치까지 (0) 2026.05.05 텐서코어, 한 명령에 행렬 곱을 끝낸다는 게 무슨 뜻인가 (0) 2026.05.05 context7 MCP, 설치만 하고 끝낼 뻔했다 — 도구 도입의 진짜 일은 트리거 설계 (2) 2026.05.04 GitHub MCP 서버 도입기 — gh CLI 대신 JSON-RPC로 깃헙과 대화하기 (0) 2026.05.04