모델 양자화는 NVIDIA GeForce RTX GPU와 같은 컨슈머 디바이스에서 VRAM 사용량을 줄이고 추론 성능을 끌어올리는 효과적인 기법입니다. 모델 품질을 유지하면서 연산·메모리 요구량을 낮추므로, AI 모델이 리소스가 제한된 환경에서도 더 효율적으로 동작하도록 돕습니다.
이 글에서는 NVIDIA Model Optimizer를 활용해 CLIP 모델을 학습 후 양자화(post-training quantization, PTQ) 방식으로 FP8 포맷으로 양자화하는 절차를 단계별로 살펴봅니다. 모델 양자화의 전반적인 개념은 Model Quantization: Concepts, Methods, and Why It Matters 글에서 확인할 수 있습니다.
NVIDIA Model Optimizer란?
NVIDIA Model Optimizer(ModelOpt) 라이브러리는 AI 모델을 압축하고 가속하기 위한 최신 모델 최적화 기법을 통합 제공합니다. 양자화, 디스틸레이션, 프루닝, 추측 디코딩(speculative decoding), 희소화(sparsity) 등이 핵심 기법에 해당합니다. ModelOpt는 Hugging Face, PyTorch, ONNX 포맷의 모델을 입력으로 받으며, 다양한 최적화 기법을 자유롭게 조합해 최적화된 체크포인트를 산출할 수 있도록 Python API를 제공합니다.
ModelOpt는 FP4, FP8, INT8, INT4와 같이 성능이 뛰어난 양자화 포맷을 지원하며, SmoothQuant, AWQ, SVDQuant, Double Quantization 등 고급 알고리즘도 함께 제공합니다. PTQ와 양자화 인식 학습(quantization-aware training, QAT) 양쪽 모두 지원합니다.
CLIP이란?
CLIP(Contrastive Language-Image Pretraining)은 2021년 OpenAI가 공개한 파운데이션 비전 언어 모델(VLM)로, 대규모 이미지-텍스트 쌍에 대한 대조 학습을 통해 이미지와 텍스트가 공유하는 임베딩 공간을 학습합니다. 의미적으로 정렬된 표현을 산출하는 능력 덕분에 현대 멀티모달 시스템 전반에서 핵심 구성 요소로 자리잡았습니다.
CLIP 텍스트 인코더는 텍스트-이미지(Stable Diffusion 등) 및 텍스트-비디오(AnimateDiff 등) 합성에서 컨디셔닝 모듈로 폭넓게 재사용됩니다. 비전 인코더는 LLaVA 같은 멀티모달 LLM과 OWL-ViT 같은 오픈 보캐뷸러리(open-vocabulary) 인지 모델의 시각 백본 역할을 담당합니다. OpenCLIP, SigLIP 같은 후속 모델은 데이터 규모를 확장하고 학습 목표를 정교화했지만, 듀얼 인코더 대조 학습 패러다임은 그대로 유지합니다.
양자화 레시피
이 글에서는 ModelOpt를 활용한 CLIP 모델 양자화 절차를 단계별 가이드 형태로 살펴보며, 양자화 과정의 동작 방식을 이해할 수 있도록 다음 레시피를 사용합니다.
먼저 다음과 같이 모델과 데이터셋을 준비합니다.
- 베이스 CLIP 모델: CLIP-ViT-L-14-laion2B-s32B-b82K
- 양자화 캘리브레이션 데이터셋: MS-COCO 10K 서브셋
- 모델 정확도 평가 태스크: CLIP_benchmark 중 다음 세 가지
- cifar100 (제로샷 분류)
- imagenet1k (제로샷 분류)
- mscoco_captions (제로샷 검색)
ModelOpt로 PTQ 실행하기
다음 코드 샘플은 ModelOpt를 사용해 CLIP 모델을 FP8로 PTQ 처리하는 방법을 보여줍니다.
import torch
from torch.utils.data import DataLoader, Subset
from transformers import CLIPModel, CLIPTokenizer, CLIPImageProcessor
from transformers.models.clip.modeling_clip import CLIPAttention
import modelopt.torch.opt as mto
import modelopt.torch.quantization as mtq
from modelopt.torch.quantization.plugins.diffusion.diffusers import _QuantAttention
# FP8 (E4M3) per-tensor static quantization
FP8_CFG = {
"quant_cfg": {
"*weight_quantizer": {"num_bits": (4, 3), "axis": None, "trt_high_precision_dtype": "Half"},
"*input_quantizer": {"num_bits": (4, 3), "axis": None, "trt_high_precision_dtype": "Half"},
"*[qkv]_bmm_quantizer": {"num_bits": (4, 3), "axis": None, "trt_high_precision_dtype": "Half"},
"*bmm2_output_quantizer": {"num_bits": (4, 3), "axis": None, "trt_high_precision_dtype": "Half"},
"default": {"enable": False},
},
"algorithm": "max",
}
mto.enable_huggingface_checkpointing()
mtq.QuantModuleRegistry.register({CLIPAttention: "CLIPAttention"})(_QuantAttention)
model = CLIPModel.from_pretrained(args.model_ckpt, attn_implementation="sdpa").half().eval().cuda()
tokenizer = CLIPTokenizer.from_pretrained(args.model_ckpt)
processor = CLIPImageProcessor.from_pretrained(args.model_ckpt)
calib_set = Subset(CLIP_COCO_dataset(ANN, IMG_DIR, tokenizer, processor), range(8192))
loader = DataLoader(calib_set, batch_size=512, num_workers=4)
# Calibration: 8k MS-COCO image-text pairs
def calibrate(m):
for img, txt in loader:
m.get_text_features(input_ids=txt.cuda())
m.get_image_features(pixel_values=img.cuda())
q_model = mtq.quantize(model, FP8_CFG, forward_loop=calibrate)
# Save quantized modelopt checkpoint
q_model.save_pretrained(ckpt_path)
mtq.print_quant_summary(q_model)
FP8_CFG는 하나의 예시 레시피에 불과합니다. W8A8(가중치와 활성값 모두 FP8), per-tensor, 정적 양자화, AbsMax 알고리즘 기반 캘리브레이션을 적용한 구성입니다. ModelOpt는 이 외에도 per-channel·블록 단위 granularity, 동적 활성값 양자화, AWQ·GPTQ 같은 고급 캘리브레이션 알고리즘 등 훨씬 다양한 선택지를 제공합니다.
상세 구성 스키마는 ModelOpt 양자화 가이드를 참조하시기 바랍니다. 양자화 구성의 하이퍼파라미터는 필요에 따라 언제든 조정할 수 있으며, 최적값을 찾기 위해서는 보통 몇 차례의 반복 실험이 필요합니다.
mtq.quantize 호출이 반환된 후에도 CLIP의 모든 Linear 레이어는 가중치·활성값 양자화기를 갖춘 상태가 되지만, 어텐션 블록은 여전히 양자화되지 않은 상태로 남습니다. 멀티 헤드 어텐션이 torch.nn.functional.scaled_dot_product_attention 함수형 API로 디스패치되므로, ModelOpt의 모듈 워커가 단독으로는 해당 호출을 가로챌 수 없기 때문입니다.
어텐션을 양자화 범위에 포함시키려면 CLIPAttention에 대해 양자화 버전을 등록합니다.
mtq.QuantModuleRegistry.register({CLIPAttention:
"CLIPAttention"})(_QuantAttention)
이렇게 하면 각 CLIPAttention 인스턴스가 ModelOpt diffusers 플러그인의 _QuantAttention으로 업그레이드됩니다. forward 패스 내부에서 _QuantAttention은 SDPA 호출을 투명하게 가로채고 fused 커널 주변에 네 개의 양자화기를 삽입합니다.
q_bmm_quantizer,k_bmm_quantizer,v_bmm_quantizer는 projected Q / K / V 텐서가 커널에 진입하기 전 단계에서 래핑합니다.bmm2_output_quantizer는 커널 출력(softmax @ V)이out_proj로 흘러 들어가기 전 단계에서 래핑합니다.
이 과정을 통해 어텐션 메커니즘 전반에 걸쳐 적절한 양자화가 적용됩니다.
정확도 일부를 회복하기 위해서는 mtq.disable_quantizer로 일부 양자화기를 비활성화하는 방법이 자주 권장됩니다. 이 함수는 모듈 이름을 입력으로 받는 함수를 인자로 사용하며, 정규식이나 문자열 매칭을 통해 비활성화할 레이어를 선택할 수 있습니다. 다음 예시는 CLIP 모델의 patch_embedding 레이어에서 양자화기를 비활성화합니다.
import re
def filter_func(name):
pattern = re.compile(
r".*(patch_embedding).*"
)
return pattern.match(name) is not None
mtq.disable_quantizer(q_model, filter)
CLIP 벤치마크 평가
저장된 ModelOpt 체크포인트는 어떤 다운스트림 평가 스크립트로도 복원해 사용할 수 있습니다. 자세한 내용은 Restoring ModelOpt Models 문서를 참고하세요. 양자화된 CLIP 체크포인트는 제로샷 분류(CIFAR-100, ImageNet-1k)와 제로샷 검색(MS-COCO Captions) 등 세 가지 벤치마크로 평가했으며, FP16 CLIP 모델을 베이스라인으로 사용합니다.

평가 결과에 따르면 CLIP-FP8 양자화 모델은 CLIP-FP16 모델과 견줄 만한 품질을 보여 줍니다. 특히 patch_embedding 레이어에서 양자화기를 비활성화한 경우, 양자화가 모델 품질에 미치는 영향이 거의 무시할 수 있는 수준으로 떨어집니다.
ModelOpt PTQ 흐름 내부 동작
이 단계는 “가짜 양자화(fake quantization)”를 다룬다는 점을 이해해 두는 것이 중요합니다. 모델의 실제 데이터 타입은 변경되지 않으며, 삽입된 양자화기들은 모델을 원래의 부동소수점 포맷에 유지한 채 양자화 효과만 시뮬레이션하는 관측자(observer) 역할을 수행합니다.
가짜 양자화 프로세스는 다음 두 가지 방식으로 작동합니다.
- 통계 수집: 양자화기는 데이터가 통과하는 동안 텐서 통계(예: 최소·최대값)를 수집합니다. 이 통계는 스케일링 팩터와 같은 최적 양자화 파라미터를 계산하는 데 활용됩니다.
- 양자화 시뮬레이션: 양자화기는 네트워크를 통과하는 텐서에 대해 양자화-역양자화(QDQ) 연산을 수행합니다. 어디까지나 저정밀도 연산을 시뮬레이션할 뿐이며, 실제 성능 향상과 메모리 절감은 모델을 NVIDIA TensorRT 같은 배포 프레임워크로 익스포트해야 확보됩니다.
이 시뮬레이션이 중요한 이유는 실제 양자화를 적용하기에 앞서 모델 정확도를 평가할 수 있게 해 주기 때문입니다. 양자화기는 다운스트림 추론 프레임워크에 배포된 양자화 모델에서 발생할 동일한 라운딩과 정밀도 제약을 적용하므로, 다음과 같은 작업이 가능합니다.
- 배포 전 정확도 영향 측정
- 다양한 양자화 구성 실험
- 별도 처리가 필요한 문제성 레이어 식별
일반적으로 ModelOpt PTQ 흐름은 다음 여섯 단계를 따릅니다.
- 준비(Prepare): 양자화 구성을 설정해 모델의 가중치 및/또는 활성값 주변에 양자화기 모듈을 삽입합니다.
- 캘리브레이션(Calibrate): 대표 데이터의 작은 배치를 모델에 통과시켜, 각 양자화기가 통계(예: 활성값 amax)를 수집하고 스케일링 팩터를 도출하도록 합니다.
- 가짜 양자화(Fake quantization): 양자화기가 부동소수점 상에서 Q → DQ 왕복을 적용해 타깃 포맷의 정밀도 손실을 충실히 시뮬레이션하는 동안, 모델은 여전히 FP16/BF16으로 실행됩니다.
- 평가(Evaluate): 별도 보유한 평가 세트에서 정확도를 측정하고 양자화하지 않은 베이스라인과 비교합니다.
- 반복(Iterate): 격차가 허용 범위를 벗어나면 양자화 구성(granularity, 알고리즘, 양자화 대상 레이어)을 조정하고, 민감한 레이어의 양자화를 비활성화한 뒤 재캘리브레이션을 수행합니다.
- 익스포트 및 배포(Export and deploy): 정확도가 허용 범위에 들어오면, 가짜 양자화된 가중치를 실제 저정밀도 형태로 압축하고 다운스트림 엔진용 체크포인트로 익스포트합니다. 본 예시에서는 PyTorch 체크포인트를 ONNX로 익스포트한 뒤 TensorRT로 추론을 실행하며, 성능 향상과 메모리 절감은 이 단계에서 실현됩니다.

QAT는 양자화기 상태를 고정한 채 모델 가중치를 파인튜닝해 양자화로 인한 품질 손실을 회복합니다. PTQ보다 컴퓨트 부담이 크지만, 양자화 모델 품질을 한층 더 끌어올릴 수 있습니다. 자세한 내용은 ModelOpt 예제를 참조하시기 바랍니다.
NVIDIA Model Optimizer 시작하기
이 글에서는 NVIDIA Model Optimizer를 소개하고, CLIP 모델을 FP8로 양자화하는 실용적인 코드 예제를 통해 전형적인 학습 후 양자화 워크플로를 살펴봤습니다. 세 가지 평가 데이터셋에서 산출된 결과는 FP8 양자화가 모델 품질을 유지하면서도 더 효율적인 배포 경로를 가능하게 한다는 점을 확인해 줍니다.
자체 모델에 ModelOpt를 적용할 준비가 되셨나요? 모델과 캘리브레이션 데이터를 준비하고, 양자화 구성을 설정하며, 캘리브레이션을 수행한 뒤, 태스크별 품질 지표로 양자화 모델을 검증하고, ModelOpt 체크포인트를 저장·복원하는 순서로 진행하시기 바랍니다.
추가 워크플로 탐색과 자체 활용 사례에 맞춘 ModelOpt 적용 방법은 ModelOpt 문서에서 확인할 수 있습니다.