개요
NVIDIA가 출시한 통합 모델 최적화 라이브러리로, 양자화, 프루닝, 증류, 신경 아키텍처 탐색, 투기적 디코딩 등의 기술을 통합합니다. Hugging Face, PyTorch, ONNX 모델 입력을 지원하며, 압축된 체크포인트를 내보내어 TensorRT-LLM, TensorRT, vLLM, SGLang 등의 추론 프레임워크에 원활하게 배포할 수 있습니다.
README 미리보기
\n\n\n\n# NVIDIA Model Optimizer\n\n[](https://nvidia.github.io/Model-Optimizer)\n[](https://pypi.org/project/nvidia-modelopt/)\n[](./LICENSE)\n\n[Documentation](https://nvidia.github.io/Model-Optimizer) |\n[Roadmap](https://github.com/NVIDIA/Model-Optimizer/issues/1699) |\n[Announcement Blogs](https://nvidia.github.io/Model-Optimizer/#announcements)\n\n\n\n______________________________________________________________________\n\n**NVIDIA Model Optimizer** (referred to as **Model Optimizer**, or **ModelOpt**) is a library comprising state-of-the-art model optimization [techniques](#techniques) including quantization, pruning, Neural Architecture Search (NAS), distillation, speculative decoding and sparsity to accelerate models.\n\n**[Input]** Model Optimizer currently supports inputs of a [Hugging Face](https://huggingface.co/), [PyTorch](https://github.com/pytorch/pytorch) or [ONNX](https://github.com/onnx/onnx) model.\n\n**[Optimize]** Model Optimizer provides Python APIs for users to easily compose the above model optimization techniques and export an optimized quantized checkpoint.\nModel Optimizer is also integrated with [NVIDIA Megatron-Bridge](https://github.com/NVIDIA-NeMo/Megatron-Bridge), [Megatron-LM](https://github.com/NVIDIA/Megatron-LM) and [Hugging Face Accelerate](https://github.com/huggingface/accelerate) for training required inference optimization techniques.\n\n**[Export for deployment]** Seamlessly integrated within the NVIDIA AI software ecosystem, the quantized checkpoint generated from Model Optimizer is ready for deployment in downstream inference frameworks like [SGLang](https://github.com/sgl-project/sglang), [TensorRT-LLM](https://github.com/NVIDIA/TensorRT-LLM/tree/main/examples/quantization), [TensorRT](https://github.com/NVIDIA/TensorRT), or [vLLM](https://github.com/vllm-project/vllm). The unified Hugging Face export API now supports both transformers and diffusers models.\n\n## Latest News\n\n- [2026/09/16] [**End-to-end W4A4 NVFP4 +
FAQ (5)
문제 해결왜 megatron_generate는 enable_kv_cache=False일 때 VLM 모델에서 시각적으로 근거 없는 출력을 생성하며, 이를 어떻게 해결할 수 있습니까?
근본 원인: ModelOpt의 modelopt.torch.utils.plugins.megatron_generate.megatron_generate(0.46.0rc1 및 main에 영향)에서 비전 입력 pixel_values, image_grid_thw, image_sizes는 오직 step == 0 조건으로만 게이트됩니다. enable_kv_cache=False인 경우, 모든 디코딩 단계에서 계속 늘어나는 전체 토큰 접두사를 다시 계산하지만, 1단계 이후부터는 비전 임베딩 없이 실행되므로 이미지 플레이스홀더 토큰이 교체되지 않으며, 생성이 아무런 오류 없이 시각적 근거가 없는 상태로 조용히 진행됩니다. 이는 model.config.sequence_parallel=True일 때도 발생하는데, 이 경우 megatron_generate가 자동으로 캐시 없는(no-cache) 디코딩으로 폴백하기 때문입니다.
수정: 세 개의 시각 텐서에 대한 게이팅 조건을 step == 0에서 다음으로 변경하세요:
replay_vision_inputs = inference_context is None or step == 0
이 조건을 pixel_values, image_grid_thw, image_sizes에 적용하여, 추론 컨텍스트(KV 캐시)가 없을 때는 전체 접두사 재계산 시마다 비전 입력이 전달되고, KV 캐시가 활성화된 경우에는 프리필(prefill) 중에만 전달되도록 하세요.
검증된 결과: 패치 적용 후 접두사 로짓이 단계 불변(step-invariant)이며(max|diff| = 0.0, argmax 일치율 1.000, 패치 전 0.529), Qwen3-VL-8B-Instruct가 관련 없는 내용을 지어내는 대신 이미지를 올바르게 설명합니다.
패치할 수 없는 경우의 우회 방법: (1) KV 캐시를 활성화(enable_kv_cache=True)하여 시각 텐서가 프리필에서만 필요하도록 합니다. 단, sequence_parallel에서는 불가능합니다. 또는 (2) 생성기 함수를 호출하기 전에 수정된 게이팅 조건으로 메모리 내에서 몽키 패치(monkey-patch)합니다.
문제 해결tied embeddings를 사용하는 Megatron 모델에서 mtq.fold_weight를 호출할 때 발생하는 "AttributeError: 'NoneType' object has no attribute 'data'" 오류를 해결하는 방법은?
이 크래시는 share_embeddings_and_output_weights=True로 설정된 Megatron-Core 모델(예: Qwen3-0.6B GPTModel)에서 발생합니다. output_layer가 weight = None을 저장하고(순전파 시점에 임베딩 가중치를 빌려 사용함) 있는 반면, mtq.fold_weight는 None 가드 없이 저장된 가중치를 역참조하기 때문입니다.
해결 방법: PR #2140이 포함된 버전으로 TensorRT-Model-Optimizer를 업그레이드하세요. 해당 PR은 fold_weight가 가중치가 텐서가 아닌 쌍을 건너뛰도록 수정합니다. 빌려온 텐서는 임베딩 모듈에 속하며 해당 모듈에서 폴드되므로, 건너뛰는 것이 올바른 동작입니다.
임시 해결 방법(수정 적용 전): mtq.fold_weight를 호출하는 대신 None 가드를 포함하여 쌍별 가중치 폴드를 수동으로 재구현하세요. 즉, getattr(module, weight_name)이 torch.Tensor가 아닌 경우 건너뛰는 방식입니다. 이는 NeMo-RL PR #3441에서 사용된 접근 방식입니다. 일반적인 QAT 순전파에서는 이 버그가 발생하지 않는다는 점에 유의하세요. Megatron 플러그인이 저장된 module.weight가 아니라 linear functional에 전달되는 가중치 인수를 양자화하기 때문이며, 저장된 속성을 읽는 것은 fold_weight뿐입니다.
문제 해결NVIDIA ModelOpt Puzzletron이 Mistral-Small-24B, gpt-oss-20b 또는 Gemma-3 모델의 KV-cache 메모리를 잘못 추정하는 이유는 무엇인가요?
Puzzletron의 modelopt/torch/puzzletron/utils/misc.py에 있는 calculate_kv_dim은 모델 config에서 head_dim을 읽어오는 대신 head_size = n_embd // n_head를 계산합니다. head_dim != hidden_size / num_attention_heads인 모델에서는 모든 어텐션 레이어의 KV-cache 비용이 잘못 계산됩니다: Mistral-Small-24B(head_dim 128 vs 계산값 160)는 25% 과대 추정되고, gpt-oss-20b(head_dim 64 vs 계산값 45)는 30% 과소 추정되며, Gemma-3-12B(256 vs 240)도 영향을 받습니다. target_memory 제약 조건 하에서는 이로 인해 solver가 어텐션 블록을 잘못 가지치기(pruning)하는 쪽으로 편향됩니다. 수정 사항(PR #2484): head dimension을 qk_head_dim(설정된 경우) → 모델 config head_dim → 폴백 n_embd // n_head의 우선순위로 결정합니다. 예: head_dim = getattr(config, 'head_dim', n_embd // n_head) 후 kv_dim = 2 * num_kv_heads * head_dim. 이렇게 결정된 head dimension을 calculate_additive_metrics의 어텐션 FLOP 추정에도 동일하게 적용합니다(이전에는 qk_head_dim or (n_embd // n_head)를 사용했습니다). Llama-3.x, Qwen2.5-7B 및 Qwen3-8B는 head_dim이 hidden_size/num_attention_heads와 같으므로 영향을 받지 않습니다.
문제 해결NVIDIA Model Optimizer에서 QAD (Quantization-Aware Distillation) 학습이 정확도를 회복하지 못하는 이유는 무엇이며, arXiv:2601.20088 결과를 재현하는 학습 설정은 무엇인가요?
QAD 정확도 복원이 잘되지 않는 가장 흔한 원인은 글로벌 배치 크기가 너무 작고 시퀀스 길이가 너무 짧기 때문입니다(예: gbs=16, seq_len=8192는 개선이 거의 나타나지 않습니다). NVIDIA 메인테이너에 따르면, 논문 설정(예: AceReason Nemotron 1.1 7B)은 다음을 사용합니다:
- 시퀀스 패킹을 활성화한 상태에서 seq_len = 32768
- global_batch_size = 100(패킹된 샘플 약 1024개에 해당)
- warmup_steps = 10
- 200~600 스텝만 학습(총 약 1B 토큰). 데이터가 충분하다면 샘플을 재사용하지 마세요
- 총 토큰 수: 약 1B
- 손실: forward KL, 온도 T=1
핵심 사항:
1. 모델의 원래 SFT 데이터셋을 증류 데이터로 사용하세요. 이는 필수 사항으로 강조됩니다.
2. 검증 손실만이 아니라 실제 다운스트림 벤치마크를 실행하여 최적의 체크포인트를 선택하세요.
3. 논문에서는 Hugging Face/FSDP2 경로(examples/llm_qat)보다 더 활발히 유지 관리되는 NeMo/Megatron-LM 경로(examples/llm_qad)를 사용했습니다. 재현에는 Megatron-LM을 사용하세요.
4. 표준 옵티마이저 설정(AdamW, lr ~1e-5, 코사인 스케줄, 그래디언트 클립 1.0, BF16)으로 충분합니다. 중요한 것은 데이터와 배치/시퀀스 규모입니다.
문제 해결TensorRT Model Optimizer에서 FP8 양자화 텐서가 ±448이 아닌 ±36 범위만 갖는 이유는 무엇인가요 (INT8에서 FP8 스케일 변환)?
modelopt/onnx/quantization/fp8.py에서 INT8 스케일은 np_fp8_scale = (np_scale * 448.0) / 127.0으로 변환됩니다. int8_scale = amax / 127이고 fp8_scale = amax / 448이므로, 수학적으로 일관된 변환은 fp8_scale = int8_scale * (127.0 / 448.0)입니다. 현재의 승수는 스케일을 약 3.5배 부풀리므로, 양자화된 텐서가 FP8 ±448 범위 중 약 ±36만 차지하게 되어 activation blow-up, saturation/clipping, 레이어 불균형을 유발할 수 있습니다. NVIDIA가 권장하는 우회 방법: INT8 스케일을 변환하지 말고 대신 FP8에 대해 직접 amax 캘리브레이션을 실행하세요. 이렇게 하면 부풀려진 값(현재 코드)과 0으로 언더플로되는 값(단순한 역비율)을 모두 피할 수 있습니다. 참고: 소스에는 여전히 scale * 448 / 127이 포함되어 있으므로, 공식 수정이 적용될 때까지 네이티브 FP8 amax 캘리브레이션을 사용하는 것이 좋습니다.