한 줄 정의
INT4는 숫자 하나를 4비트 정수 칸에 넣도록 줄이는 양자화 선택지야. LLM 추론에서는 보통 가중치를 작게 저장하고 읽기 위해 먼저 등장해.
정확히는 “4비트라서 모두 같은 포맷”이 아니야. INT4는 정수 매핑이고, FP4는 4비트 부동소수점 계열이야. BF16은 16비트 부동소수점 기준선에 가깝고, INT8은 같은 정수 양자화라도 8비트를 써서 품질과 절감 폭이 다르게 움직여.
ONNX의 4 bit integer 문서는 INT4를 2의 보수 signed integer로 두고 값 범위를 [-8, 7]로 설명해. 한 바이트에는 4비트 값 두 개가 들어가고, 원소 수가 홀수면 4비트 padding이 붙어.
어떻게 작동하나
INT4 양자화는 원래 높은 정밀도 값 x를 scale s로 나누고, 가장 가까운 정수로 반올림한 뒤 [-8, 7] 안에 clamp하는 식으로 움직여. 다시 계산할 때는 xq * s로 높은 정밀도 쪽 값을 복원해서 행렬곱에 넣어.
문제는 scale을 어떻게 잡느냐야. 텐서 전체에 scale 하나만 두면 간단하지만 값 분포가 넓은 어텐션이나 MLP 가중치에서 오차가 커질 수 있어. 그래서 실제 런타임은 group 또는 block 단위 scale을 둬. TensorRT 문서는 INT4에 per-block scale을 요구하고, block size를 {64, 128}로 적어.
PyTorch 쪽도 비슷하게 읽으면 돼. TorchAO API 문서는 4비트 가중치 양자화가 지금은 그룹 단위만 지원한다고 설명하고, group size 선택지를 256, 128, 64, 32로 둬. TorchAO Quantized Inference 문서의 의사코드도 입력은 BF16, 가중치는 INT4로 두고 output_bf16 = input_bf16 @ weight_int4.t()처럼 설명해.
그러니까 INT4는 대개 “모든 값을 4비트로 계산한다”가 아니야. 큰 Linear/MatMul 가중치를 4비트로 읽고, scale로 복원하면서 높은 정밀도 입력과 곱하는 가중치 전용 경로부터 봐야 해.
왜 중요한가
INT4가 중요한 이유는 GPU VRAM과 메모리 대역폭 병목을 강하게 줄일 수 있어서야. raw 값 폭만 보면 4비트 가중치는 16비트 BF16 가중치의 1/4이고, 8비트 INT8 가중치의 1/2이야.
하지만 그 숫자를 서비스 절감률로 바로 옮기면 위험해. 실제 모델 파일과 엔진에는 scale, zero point, block metadata, packing layout, padding, dequantize 연산, 커널 선택 비용이 붙어. 그래서 “모델이 4비트니까 VRAM이 정확히 1/4”이라고 말하면 틀리기 쉬워.
체감 이득은 큰 행렬곱이 메모리 대역폭에 막혀 있을 때 더 잘 보여. 반대로 batch가 작고 커널이 INT4 경로를 제대로 못 타거나, dequantize 비용이 더 크게 보이면 p95 지연시간이 기대만큼 줄지 않을 수 있어. 토큰 처리량 하나만 보지 말고 최대 VRAM, p50·p95 지연시간, tokens/sec, perplexity나 task score, 긴 문맥 답변 품질을 같은 표에서 봐야 해.
품질도 따로 봐야 해. 코드 생성, 수학, 랭킹, tool call 인자처럼 작은 logits 차이가 결과를 뒤집는 작업에서는 INT4가 생각보다 빨리 흔들릴 수 있어. 특히 에이전트 작업처럼 긴 멀티턴 컨텍스트와 KV 캐시가 같이 붙는 경우에는 짧은 프롬프트 20개보다 실제 작업 로그로 회귀 테스트를 하는 편이 낫다.
INT4를 시험할 조건
먼저 적용 대상부터 좁혀. INT4가 가중치 전용인지, activation까지 4비트로 줄이는 W4A4인지, 아니면 파일 포맷 이름에 들어간 4비트 계열인지 갈라야 해. 일반적인 LLM 추론에서는 가중치 전용 INT4부터 보는 게 현실적이야.
- 가중치 전용 INT4: 모델이 VRAM에 간당간당하게 올라가거나, 같은 GPU에서 더 큰 batch를 처리하고 싶을 때 먼저 시험할 만해. TorchAO처럼 Linear 계층 가중치를 groupwise로 줄이는 경로가 여기에 가까워.
- ONNX 변환 경로: ONNX Runtime 문서는 Int4/UInt4 양자화를 특정 연산에만 적용해. 예를 들어
MatMul의B입력이 constant인 그래프는 후보가 되지만, 매 요청마다 바뀌는 오른쪽 입력이면 같은 규칙으로 줄일 수 없어.Gather도data입력이 constant일 때가 조건이야. Int4/UInt4 타입은 ONNX opset21에서 도입됐고,GatherBlockQuantized노드는 ONNX Runtime1.20이 필요해. - TensorRT 경로: TensorRT는 INT4를 Q/DQ가 들어간 explicit quantization 쪽에서 다뤄. 같은 문서는 INT4 activation quantization을 지원하지 않는다고 적으니, TensorRT에서 INT4를 W4A4처럼 읽으면 안 돼.
실험 순서는 단순하게 잡아. 먼저 BF16 또는 FP16 기준선을 만들고, 같은 모델·같은 prompt 묶음·같은 batch·같은 sampling·같은 컨텍스트 윈도우에서 INT4 결과를 붙여. group size를 바꿀 때도 한 번에 하나씩만 바꾸는 게 좋아. 품질 회귀가 보이면 group을 키우거나 민감한 계층을 높은 정밀도로 남기는 쪽을 봐야 해.
FP4·INT8·BF16과 경계
FP4와 INT4는 둘 다 4비트라는 숫자를 공유하지만 다른 계열이야. FP4는 E2M1 같은 부동소수점 구조와 스케일을 쓰고, Blackwell 문맥에서는 NVFP4처럼 hardware path가 따로 나와. INT4는 정수값 [-8, 7]과 scale을 쓰는 양자화 경로라서 FP4 커널 지원 소식을 INT4 지원으로 읽으면 안 돼.
INT8은 더 보수적인 정수 양자화야. raw 폭은 INT4보다 두 배지만, 품질 회귀와 kernel coverage가 나은 경우가 많아. 보정 데이터가 적거나 평가셋이 약하면 INT4보다 INT8을 먼저 붙이고, 그 다음에 INT4로 내려가는 편이 판단이 쉬워.
BF16은 아예 다른 기준선이야. Google Cloud TPU 문서는 BF16이 FP32와 같은 dynamic range를 갖고 메모리 공간은 절반이라고 설명해. BF16은 여전히 16비트 부동소수점이고, INT4는 4비트 정수와 scale로 값을 다시 맞추는 방식이야.
정리하면 INT4는 “가중치를 더 작게 읽기 위한 공격적인 배포 선택지”야. 모델이 안 올라가거나 대역폭이 막힐 때는 볼 만하지만, backend 지원과 품질 평가 없이 켜는 스위치는 아니야.