Model Quantization
모델 양자화는 가중치와 활성화를 INT8 같은 저정밀도로 저장해 모델 크기를 줄이고 추론 속도를 높입니다. 작동 방식과 PTQ 및 QAT를 살펴봅니다.
모델 양자화는 신경망의 가중치와 종종 활성화 값을 32비트 부동소수점(FP32)에서 16비트 부동소수점(FP16), 8비트 정수(INT8), 4비트 정수와 같은 저정밀도 형식으로 변환합니다. 그러면 각 값을 저장하고 이동하는 데 필요한 비트 수가 줄어들며, 대부분의 프로세서에서 정수 연산은 더 빠르고 에너지 효율적입니다. INT8 모델은 FP32 원본의 4분의 1 크기입니다. 덕분에 객체 탐지 모델을 휴대폰, 카메라 및 기타 엣지 AI 장치에서 실시간으로 실행할 수 있고, 대규모 언어 모델(LLM)을 단일 GPU에 적재할 수 있습니다.
양자화의 작동 방식#
양자화는 실수 값의 범위를 소수의 정수 단계에 대응시킵니다. 일반적인 아핀 방식에서는 각 부동소수점 값 x을 정수 q = round(x / scale) + zero_point로 저장한 뒤 x ≈ (q - zero_point) × scale로 복원합니다. 스케일은 단계 간 간격을 정하고, 제로 포인트는 부동소수점 값 0이 정확한 정수에 대응하도록 합니다. INT8에는 256개 단계가 있으므로 범위를 신중하게 선택해야 합니다. 범위가 너무 넓으면 실제로 나타나지 않는 값에 단계가 낭비되고, 너무 좁으면 이상치가 잘립니다. 가중치의 범위는 미리 알 수 있으므로 학습된 모델에서 바로 가져오며, 출력 채널별로 따로 계산하는 경우가 많습니다. 활성화 값은 입력에 따라 달라지므로, 대표 이미지나 텍스트 샘플의 소규모 집합을 모델에 통과시켜 보정 과정에서 범위를 측정합니다.
import numpy as np
rng = np.random.default_rng(0)
weights = rng.normal(0, 0.05, size=1000).astype(np.float32)
# Affine INT8 quantization maps the observed float range onto 256 integer levels
scale = (weights.max() - weights.min()) / 255
zero_point = np.round(-128 - weights.min() / scale)
q = np.clip(np.round(weights / scale + zero_point), -128, 127).astype(np.int8)
# Dequantize to measure the rounding error the model has to tolerate
restored = (q.astype(np.float32) - zero_point) * scale
print(f"Size: {weights.nbytes} -> {q.nbytes} bytes")
print(f"Max error: {np.abs(weights - restored).max():.5f} (half a step: {scale / 2:.5f})")Size: 4000 -> 1000 bytes와 0.00068의 최대 오차를 출력합니다. 이 오차는 양자화 단계 하나의 절반에 해당합니다. 가중치 크기는 4분의 1로 줄어들며, 값이 원래 값에서 단계 절반보다 더 많이 이동하지 않습니다.
학습 후 양자화와 양자화 인식 학습 비교#
- 학습 후 양자화(PTQ): 이미 학습된 모델을 변환하고 소규모 보정 세트로 활성화 범위를 설정합니다. 몇 분이면 완료되며 해당 샘플 외의 학습 데이터는 필요하지 않습니다. 동적 양자화는 추론 시 활성화 범위를 즉석에서 계산하는 PTQ 변형이며, 가중치 전용 양자화는 활성화를 부동소수점으로 유지합니다.
- 양자화 인식 학습(QAT): 순전파에서 값을 반올림해 학습 또는 파인튜닝 중 양자화를 시뮬레이션하므로, 가중치가 반올림에 견디도록 학습됩니다. 추가 학습이 필요하지만 PTQ에서 손실된 정확도를 대부분 회복합니다.
일반적으로 팀은 PTQ부터 시작하고 정확도 저하가 지나치게 클 때만 다른 방법으로 전환합니다.
LLM에는 고유한 방법도 있습니다. GPTQ와 AWQ 같은 가중치 전용 4비트 방식은 활성화를 16비트로 유지하면서 수십억 개의 파라미터를 압축합니다. GGUF 형식은 로컬 추론을 위해 양자화된 모델을 저장하며, 최신 GPU는 FP8과 같은 저정밀도 부동소수점 형식으로 연산을 직접 수행합니다.
양자화와 관련 기법 비교#
- 혼합 정밀도: 학습 속도를 높이기 위해 대부분의 연산은 FP16 또는 BF16으로 실행하면서 가중치의 마스터 사본은 FP32로 유지하는 학습 기법입니다. 양자화는 추론을 대상으로 하며, 일반적으로 정수까지 사용해 더 낮은 정밀도를 적용합니다.
- 모델 가지치기: 연산 횟수를 줄이기 위해 가중치 또는 채널 전체를 제거합니다. 남은 각 연산의 비용을 줄이는 양자화와 함께 사용하는 경우가 많습니다.
- 지식 증류: 대형 교사 모델을 모방하도록 소형 학생 모델을 학습합니다. 배포를 위해 학생 모델을 이후에 양자화하는 경우가 많습니다.
실제 환경에서의 양자화#
속도 향상은 하드웨어 지원에 따라 달라집니다. INT8 모델은 NVIDIA GPU의 TensorRT, Intel CPU의 OpenVINO, 휴대폰의 신경망 처리 장치(NPU)처럼 INT8 커널을 지원하는 프로세서에서만 더 빠르게 실행됩니다. Ultralytics YOLO26은 quantize=8를 사용해 내보내기 과정에서 PTQ를 적용하고 data 데이터셋의 이미지로 보정합니다. 반면 model.train(quantize=8)은 QAT로 체크포인트를 파인튜닝합니다. 다음 예제에서는 Android 및 임베디드 장치를 위한 Google 런타임인 LiteRT용 INT8 모델을 내보냅니다.
from ultralytics import YOLO
# 사전 학습된 YOLO26 nano 모델을 불러옵니다
model = YOLO("yolo26n.pt")
# INT8 학습 후 양자화. coco8.yaml의 이미지로 활성화 범위를 보정합니다.
model.export(format="litert", quantize=8, data="coco8.yaml")COCO에서 TensorRT로 측정한 결과, INT8 PTQ를 적용하면 YOLO26n의 mAP50-95는 0.403에서 0.393으로만 낮아집니다. 그러나 YOLO26s에서는 0.479에서 0.441로 더 크게 낮아지며, QAT를 적용하면 YOLO26s의 mAP50-95가 0.471로 회복됩니다.










