Files
EnragedAntelope-comfy-modelopt/docs/TECHNICAL_GUIDE.md
T
EnragedAntelopeandSisyphus e215a52149 v0.6.0 - Update documentation for standalone loading
Document standalone loading workflow in README. Add CLAUDE.md project context file. Update technical guide and research notes with v0.6.0 architecture changes.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-25 23:57:30 -04:00

1015 lines
34 KiB
Markdown

> **⚠️ OUTDATED**: This technical guide was written for ModelOpt v0.27-v0.33 (November 2025). ModelOpt is now at v0.43 (April 2026) with significant changes. **The project has been rewritten (v0.5.0) to use native PyTorch quantization with real quantized weights instead of ModelOpt fake quantization.** See RESEARCH_NOTES.md for the rewrite details and updated information.
>
# NVIDIA Model Optimizer (ModelOpt) for ComfyUI: Complete Technical Guide
## Executive Summary (Updated April 2026)
NVIDIA Model Optimizer (ModelOpt, formerly TensorRT Model Optimizer) v0.43 is now **open source under Apache 2.0** with **extensive diffusion model support** including FLUX.1, SD3.5, SDXL, SD1.5, LTX-2, and Wan2.2. The library supports FP8, INT8 SmoothQuant, INT4 AWQ, W4A8, and NVFP4 quantization formats. For ComfyUI integration, the native PyTorch path remains blocked by compatibility issues, but **three viable alternatives now exist**: Diffusers wrapper, TensorRT export, and pre-quantized checkpoint distribution.
## 1. ModelOpt Current Capabilities (April 2026 — Updated from November 2025)
### Supported Model Architectures
**Diffusion Models (Extensive Support — Updated):**
- **SDXL**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4, Cache Diffusion
- **SD1.5**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4
- **SD 2.1**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4
- **FLUX.1-dev**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4 (NEW — was ❌)
- **FLUX.1-schnell**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4 (NEW — was ❌)
- **SD 3.5**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4, Cache Diffusion (NEW)
- **LTX-2**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4, Cache Diffusion (NEW)
- **Wan 2.2 (T2V)**: ✅ FP8, INT8, INT4 AWQ, W4A8, NVFP4, Cache Diffusion (NEW)
### Supported Model Architectures
**Diffusion Models (Limited Support):**
- **SDXL (Stable Diffusion XL)**: ✅ Fully supported via NeMo framework with INT8 quantization achieving ~2x speedup
- **SD1.5 (Stable Diffusion 1.5)**: ✅ Supported via NeMo with INT8 quantization
- **Stable Diffusion 3**: ✅ Mentioned in v0.15 release with INT8 support
- **FLUX.1**: ❌ **NOT officially supported** (community FP8/NF4 alternatives exist)
- **Qwen Image**: ❌ **NOT officially supported** (20B parameter model, community FP8 available)
- **WAN 2.2**: ❌ **NOT officially supported** (video generation model)
**LLM Architectures (Primary Focus):**
ModelOpt has extensive LLM support including Llama 3.1/3.2/3.3/4, DeepSeek-R1, Qwen 2.5/3, Mistral, Mixtral, Phi-3/3.5, Gemma 2, Nemotron, Arctic 2, and DBRX.
### Quantization Formats Supported
**Linux Platform:**
| Format | Precision | Requirements | Best Use Case | Quality Loss |
|--------|-----------|--------------|---------------|--------------|
| **NVFP4** | 4-bit float | Blackwell GPUs (SM 12.0) | Maximum compression on latest hardware | \<1% from FP8 |
| **FP8** | 8-bit float | Hopper/Ada GPUs (SM 8.9+) | Best quality/performance balance | \<1% |
| **INT8** | 8-bit integer | SM 7.5+ (Turing+) | Broad hardware compatibility, production-ready | 1-3% |
| **INT4 AWQ** | 4-bit integer | SM 7.5+ | Maximum VRAM savings, small-batch inference | 2-5% |
| **W4A8** | Mixed 4/8-bit | SM 8.9+ | Balanced memory and performance | Low |
**Key Details:**
- **FP8 formats**: E4M3 (better precision) and E5M2 (larger range)
- **INT4 AWQ**: Activation-Aware Weight Quantization with block-wise quantization (e.g., block size 128)
- **NVFP4**: Added January 28, 2025 when ModelOpt became open source
- **Windows platform**: Limited to INT4, FP8, INT8 via ONNX export for DirectML
### Model Types Supported
**For LLMs:**
- ✅ Base checkpoints (full pre-trained models)
- ✅ Fine-tuned models (SFT, instruction-tuned)
- ✅ LoRAs via QLoRA workflow with NF4 (NeMo integration)
- ✅ Multi-GPU trained models (tensor/pipeline parallelism)
**For Diffusion Models:**
- ✅ Base checkpoints (SDXL, SD1.5 only)
- ❌ LoRAs: NOT directly supported (must be applied at framework level post-quantization)
- ❌ ControlNets: NOT supported for quantization
- ❌ Other adapters (IP-Adapter, T2I-Adapter): NOT supported
**Critical Limitation**: ModelOpt only quantizes the UNet for diffusion models. VAE and text encoders remain FP16.
### Output Formats
**1. TensorRT-LLM Checkpoint Format:**
```python
from modelopt.torch.export import export_tensorrt_llm_checkpoint
export_tensorrt_llm_checkpoint(
model, # Quantized model
decoder_type="llama", # Model architecture type
dtype=torch.float16, # Dtype for unquantized layers
export_dir="./checkpoint",
inference_tensor_parallel=2, # Target TP for inference
inference_pipeline_parallel=1 # Target PP for inference
)
```
**Output structure**: `config.json` + `rank{N}.safetensors` per GPU
**2. Unified HuggingFace Checkpoint:**
```python
from modelopt.torch.export import export_hf_checkpoint
export_hf_checkpoint(model, export_dir="./quantized_model")
tokenizer.save_pretrained("./quantized_model")
```
**Compatible with**: TensorRT-LLM v0.17.0+, vLLM v0.6.5+, SGLang (main branch since Jan 6, 2025)
**3. ONNX Format:**
Optimized ONNX models for DirectML (Windows), compatible with ONNX Runtime and Microsoft Olive.
**4. PyTorch Checkpoint:**
Native PyTorch state dict with quantization metadata when `mto.enable_huggingface_checkpointing()` is enabled.
### Current Limitations
**Diffusion Model Limitations:**
- Only SDXL and SD1.5 officially supported (no FLUX, Qwen, WAN 2.2)
- Only INT8 quantization available (no FP8 or FP4 despite LLM support)
- NeMo framework dependency (complex conversion workflow)
- No LoRA, ControlNet, or adapter quantization support
- Only UNet quantization (VAE and text encoders remain FP16)
**Platform Limitations:**
- Windows support deprecated for TensorRT-LLM (v0.18.0+)
- SBSA (ARM) has limited ONNX PTQ support
- FP8 only on SM 8.9+ GPUs (Hopper, Ada architectures)
- NVFP4 only on Blackwell architecture
**Deployment Constraints:**
- vLLM: FP8 without FP8 KV cache only, requires `quantization="modelopt"` flag
- SGLang: Must build from main branch (January 2025+)
- TensorRT-LLM version requirements vary by format (NVFP4 needs v0.17.0+)
## 2. PyTorch Requirements
### Minimum PyTorch Version
**Core Requirements:**
- **PyTorch**: >= 2.6 (recommended: latest stable)
- **Python**: >=3.10, <3.13
- **Architecture**: x86_64, aarch64 (SBSA)
- **OS**: Linux (primary), Windows via ModelOpt-Windows
**Installation:**
```bash
# Default installation (installs latest PyTorch)
pip install -U "nvidia-modelopt[all]"
# For specific CUDA version, install PyTorch first
pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cu124
pip install -U "nvidia-modelopt[all]"
```
**Version Compatibility:**
- ModelOpt v0.31+ deprecates torch<2.4
- TensorRT-LLM 1.2.0 requires ModelOpt v0.33
- ONNX Runtime 1.22+ optional for ONNX deployment
### CUDA Version Dependencies
**Minimum Requirements:**
- **CUDA**: >= 12.0
- **Compute Capability**: >= 5.3 (TensorRT requirement)
- **Recommended**: >= 8.9 for accelerated Triton kernels (~40% faster quantization)
**Special Features by Architecture:**
- **Hopper (H100, H200)**: FP8 with Transformer Engine, SM 9.0
- **Ada Lovelace (RTX 4000)**: FP8 support, SM 8.9
- **Ampere (A100, RTX 3000)**: TF32/BF16 optimizations, SM 8.0-8.6
- **Turing (T4, RTX 2000)**: Minimum support, SM 7.5
- **Blackwell (B200, GB200)**: NVFP4 support, SM 12.0
**Accelerated Quantization with Triton:**
```bash
pip install triton # For SM 8.9+ devices
```
Automatically used for NVFP4 format when available.
**First-Time Compilation:**
ModelOpt compiles CUDA kernels on first use (takes a few minutes). Pre-compile with:
```bash
python -c "import modelopt.torch.quantization.extensions as ext; ext.precompile()"
```
### Mixed Precision Support
**Mixed precision is OPTIONAL but RECOMMENDED:**
- Works with any base dtype: FP32, FP16, BF16
- BF16 preferred over FP16 (same dynamic range as FP32, prevents overflow)
- Quantized inference typically uses: quantized weights (INT4/INT8/FP8) + BF16/FP16 activations
**Usage:**
```python
import torch
from torch.cuda.amp import autocast
# BF16 recommended (Ampere+ GPUs)
model = model.to(torch.bfloat16)
# Inference with autocast
with torch.inference_mode():
with autocast(dtype=torch.bfloat16):
outputs = model(inputs)
```
**Hardware Requirements:**
- BF16: Ampere or newer
- FP16: Volta or newer
- TF32: Automatically enabled on Ampere+ for matmul
### torch.compile Requirements
**Compatibility:**
- Generally compatible with PyTorch 2.0+ `torch.compile`
- Status: Some edge cases may fail (feature still maturing)
- Test thoroughly before production use
**Usage:**
```python
# Quantize first, then compile
model = mtq.quantize(model, config, forward_loop)
compiled_model = torch.compile(model, mode='max-autotune')
```
**Known Issues:**
- Some quantizer operations may not optimize well
- BF16/FP16 conversion can cause compilation errors (PyTorch issue #123830)
- Workaround: Use export modes or skip compile for problematic layers
**Recommended Optimizations:**
```python
# Backend optimizations
torch.backends.cudnn.benchmark = True
torch.backends.cuda.matmul.allow_tf32 = True # Ampere+
# Weight folding for faster inference
mtq.fold_weight(quantized_model) # Note: Cannot export to ONNX after folding
# Compression for real speedup (v0.31+)
compressed_model = mtq.compress(quantized_model) # Enables real INT8/FP8 GEMM
```
## 3. Loading Mechanisms
### Native PyTorch Loading for Quantized Models
ModelOpt uses "fake quantization" - it simulates low-precision computation within PyTorch. The quantized model remains a PyTorch model with TensorQuantizer modules inserted.
**Basic Quantization Workflow:**
```python
import modelopt.torch.quantization as mtq
import modelopt.torch.opt as mto
from transformers import AutoModelForCausalLM
# 1. Load model
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
# 2. Select quantization config
config = mtq.FP8_DEFAULT_CFG # or INT8_DEFAULT_CFG, INT4_AWQ_CFG, NVFP4_DEFAULT_CFG
# 3. Prepare calibration dataset
from modelopt.torch.utils import dataset_utils
calib_dataloader = dataset_utils.get_dataset_dataloader(
"cnn_dailymail",
tokenizer,
batch_size=1,
num_samples=512
)
# 4. Define forward loop
def forward_loop(model):
for data in calib_dataloader:
model(**data)
# 5. Quantize model (in-place)
model = mtq.quantize(model, config, forward_loop)
# 6. Save quantized model
mto.save(model, "quantized_model.pt")
# 7. Load quantized model
model = mto.restore(model, "quantized_model.pt")
```
### Safetensors vs Diffusers Format
**Safetensors Format (Primary for LLMs):**
- Used by TensorRT-LLM, vLLM, SGLang deployment
- Structure:
```
checkpoint_dir/
├── config.json # Model config with quantization info
├── rank0.safetensors # Quantized weights + scaling factors
├── rank1.safetensors # (for multi-GPU models)
└── tokenizer files
```
**Diffusers Format (for Diffusion Models):**
```python
from diffusers import AutoModel, NVIDIAModelOptConfig
import torch
# Quantize during loading
quantization_config = NVIDIAModelOptConfig(
quant_type="FP8",
quant_method="modelopt",
modules_to_not_convert=["pos_embed.proj.weight"], # Skip specific layers
disable_conv_quantization=True # Optional: disable conv layers
)
transformer = AutoModel.from_pretrained(
"model_id",
subfolder="transformer",
quantization_config=quantization_config,
torch_dtype=torch.bfloat16
)
# Save quantized model
model.save_pretrained('path/to/saved_model', safe_serialization=False)
```
### ComfyUI Model Loading Infrastructure
**Key Challenge**: ComfyUI's standard model loading assumes PyTorch .safetensors or .ckpt formats. ModelOpt quantized models require special handling.
**Integration Strategy:**
1. **Custom loader nodes** that handle ModelOpt-specific formats
2. **Wrapper classes** that adapt quantized models to ComfyUI's expected model interface
3. **Lazy loading** with caching to avoid repeated quantization overhead
**Pattern from TensorRT loader:**
```python
import folder_paths
def get_model_list():
"""Scan directory for quantized model files"""
model_dir = folder_paths.get_folder_paths("tensorrt")
models = []
for root, dirs, files in os.walk(model_dir[0]):
for file in files:
if file.endswith(('.plan', '.engine', '.safetensors')):
models.append(file)
return models
class ModelOptModelLoader:
def __init__(self):
self.loaded_model = None
self.model_hash = None
def load_if_changed(self, model_path):
new_hash = hash_file(model_path)
if new_hash != self.model_hash:
self.loaded_model = self._load_engine(model_path)
self.model_hash = new_hash
return self.loaded_model
```
### Tensor Format Requirements
**TensorQuantizer Modules:**
ModelOpt inserts TensorQuantizer modules into nn.Linear and nn.Conv layers:
- `input_quantizer`: Quantizes input activations
- `weight_quantizer`: Quantizes weights
- `output_quantizer`: Typically disabled
**Quantizer States:**
- Stored in module's `extra_state` (v0.31+)
- Includes scaling factors and calibration parameters
- Parallel state tracking for distributed models
**Special Handling:**
```python
# For distributed models
# Parallel state: data_parallel_group, tensor_parallel_group
# Loading requires same GPU count for tensor-parallel models
# Example: Model trained on 8 GPUs needs 8 GPUs for PTQ calibration
```
### Memory Requirements
**Memory Footprint:**
- Quantized models require **both half and full precision copies** during mixed precision training
- Actual memory savings only in forward activations
- Real memory reduction requires deployment to TensorRT-LLM/TensorRT/vLLM
**Memory Optimization Techniques:**
1. **Low Memory Mode (PTQ):**
```bash
python llm_ptq.py --low_memory_mode # Reduces peak memory during PTQ
```
2. **CPU Offloading:**
```python
from accelerate import init_empty_weights, load_checkpoint_and_dispatch
with init_empty_weights():
model = AutoModelForCausalLM.from_pretrained("model_path")
model = load_checkpoint_and_dispatch(
model,
checkpoint="path/to/checkpoint",
device_map="auto" # Automatic CPU/GPU memory management
)
```
3. **Gradient Checkpointing:**
```python
from modelopt.torch.opt import enable_huggingface_checkpointing
enable_huggingface_checkpointing()
```
**Quantization Memory Reduction:**
- **FP8**: ~2x memory reduction (8-bit vs 16-bit)
- **INT8**: ~2x reduction with better performance
- **INT4**: ~4x reduction (4-bit vs 16-bit)
- **NVFP4**: ~4x reduction with improved quality vs INT4
## 4. ComfyUI V3 Integration
### V3 Node Schema Implementation
**Key Breaking Changes from V1/V2:**
- Inputs/outputs defined by objects instead of dictionaries
- Execution method fixed to `execute()` as class method
- Node registration uses `comfy_entrypoint()` returning `ComfyExtension`
- All nodes must inherit from `ComfyNode`
- Stateless design: class methods (receive `cls`) instead of instance methods
**V3 Node Template:**
```python
from comfy_api.latest import ComfyExtension, io
class ModelOptLoader(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id="ModelOptLoader",
display_name="ModelOpt TensorRT Loader",
category="loaders/tensorrt",
description="Load quantized models optimized with NVIDIA ModelOpt",
inputs=[
io.String.Input(
"model_path",
default="",
tooltip="Path to the quantized model file"
),
io.Combo.Input(
"precision",
options=["fp16", "fp8", "int8", "int4"],
default="fp16",
tooltip="Quantization precision of the model"
),
io.Int.Input(
"batch_size",
default=1,
min=1,
max=8,
display_mode=io.NumberDisplay.number
),
],
outputs=[
io.Model.Output(display_name="MODEL"),
io.String.Output(display_name="model_info")
],
is_output_node=False
)
@classmethod
def execute(cls, model_path, precision, batch_size) -> io.NodeOutput:
model = load_model(model_path, precision, batch_size)
info = f"Loaded {model_path} with {precision} precision"
return io.NodeOutput(model, info)
class MyExtension(ComfyExtension):
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [ModelOptLoader]
async def comfy_entrypoint() -> ComfyExtension:
return MyExtension()
```
### Custom Model Loaders for Non-Standard Formats
**Pattern 1: Directory-Based Model Discovery**
```python
import folder_paths
def scan_model_directory(base_path, extensions):
"""Recursively scan for models with specific extensions"""
models = {}
for root, dirs, files in os.walk(base_path):
for file in files:
if any(file.endswith(ext) for ext in extensions):
rel_path = os.path.relpath(os.path.join(root, file), base_path)
models[rel_path] = os.path.join(root, file)
return models
```
**Pattern 2: Metadata Parsing**
```python
def parse_engine_metadata(engine_path):
"""Extract model type, batch size, resolution from filename"""
# Example: "dyn-b-1-4-2-h-512-1024-768-w-512-1024-768.engine"
parts = os.path.basename(engine_path).split('-')
metadata = {
'type': parts[0], # 'dyn' or 'stat'
'batch_range': parse_range(parts[2:5]),
'height_range': parse_range(parts[7:10]),
'width_range': parse_range(parts[12:15])
}
return metadata
```
**Pattern 3: Lazy Loading with Caching**
```python
class ModelOptModelLoader:
def __init__(self):
self.loaded_model = None
self.model_hash = None
def load_if_changed(self, model_path):
new_hash = hash_file(model_path)
if new_hash != self.model_hash:
self.loaded_model = self._load_engine(model_path)
self.model_hash = new_hash
return self.loaded_model
```
### Input Validation Patterns
**V3 Validation Method:**
```python
@classmethod
def validate_inputs(cls, model_path, precision):
"""Validate inputs before execution"""
# File path validation
if not os.path.exists(model_path):
return f"Model file not found: {model_path}"
# File extension validation
valid_extensions = ['.plan', '.engine', '.onnx', '.safetensors']
if not any(model_path.endswith(ext) for ext in valid_extensions):
return f"Invalid file format. Expected: {', '.join(valid_extensions)}"
# File size check
file_size = os.path.getsize(model_path) / (1024**3) # GB
if file_size > 10:
print(f"Warning: Large model file ({file_size:.2f}GB)")
# Precision compatibility check
if precision == "fp8":
gpu_arch = get_compute_capability()
if gpu_arch < 8.9:
return (
f"FP8 requires Compute Capability 8.9+ (Ada Lovelace or newer)\n"
f"Your GPU: Compute Capability {gpu_arch}"
)
return True
```
**Dynamic Combo Options:**
```python
@classmethod
def INPUT_TYPES(cls):
"""Generate dropdown options dynamically"""
return {
"required": {
"model_name": (cls.get_available_models(), {
"tooltip": "Select a quantized model"
}),
"precision": (["fp16", "fp8", "int8", "int4"], {
"default": "fp16",
"tooltip": "Model quantization precision"
})
}
}
@classmethod
def get_available_models(cls):
"""Scan and return available models"""
model_dir = folder_paths.get_folder_paths("tensorrt")[0]
models = [f for f in os.listdir(model_dir) if f.endswith(('.plan', '.engine'))]
return sorted(models) if models else ["No models found"]
```
### Error Handling Patterns
**Graceful Failure with User-Friendly Messages:**
```python
@classmethod
def execute(cls, model_path, precision, batch_size):
"""Execute with comprehensive error handling"""
try:
if not os.path.exists(model_path):
raise FileNotFoundError(
f"Model file not found at: {model_path}\n"
f"Please ensure the file exists in the models/tensorrt directory"
)
try:
model = cls._load_engine(model_path)
except RuntimeError as e:
raise RuntimeError(
f"Failed to load TensorRT engine:\n{str(e)}\n\n"
f"Common causes:\n"
f"- GPU mismatch (engine built for different GPU)\n"
f"- TensorRT version mismatch\n"
f"- Corrupted model file"
)
try:
result = cls._run_inference(model, batch_size)
except torch.cuda.OutOfMemoryError:
raise RuntimeError(
f"Out of VRAM! Model requires more memory.\n"
f"Try:\n"
f"- Reducing batch_size (current: {batch_size})\n"
f"- Using lower precision (current: {precision})\n"
f"- Closing other applications"
)
return io.NodeOutput(result)
except Exception as e:
import traceback
print(f"ModelOpt Error: {traceback.format_exc()}")
raise RuntimeError(
f"ModelOpt Loader Error: {str(e)}\n"
f"Check the console for detailed error information."
)
```
**Progress Reporting:**
```python
from comfy.utils import ProgressBar
@classmethod
def execute(cls, model_path):
"""Execute with progress updates"""
pbar = ProgressBar(3)
pbar.update_absolute(0, 3, "Loading model...")
model = load_model(model_path)
pbar.update_absolute(1, 3, "Initializing inference engine...")
engine = initialize_engine(model)
pbar.update_absolute(2, 3, "Warming up...")
warmup(engine)
pbar.update_absolute(3, 3, "Complete!")
return io.NodeOutput(engine)
```
### UI Helper Text Implementation
**Comprehensive Tooltips:**
```python
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id="ModelOptQuantizer",
display_name="ModelOpt Quantizer",
description="Quantize models using NVIDIA ModelOpt for optimized inference",
inputs=[
io.String.Input(
"model_path",
default="",
tooltip="Path to the source model file (safetensors or checkpoint)"
),
io.Combo.Input(
"quant_format",
options=["fp8", "int8", "int4", "w4a8"],
default="fp8",
tooltip=(
"Quantization format:\n"
"• fp8: Best quality, requires Ada Lovelace+\n"
"• int8: Good balance of speed and quality\n"
"• int4: Maximum speed, lower quality\n"
"• w4a8: Weights in INT4, activations in INT8"
)
),
io.Int.Input(
"calibration_steps",
default=512,
min=1,
max=2048,
step=64,
tooltip=(
"Number of calibration samples for quantization.\n"
"Higher values = better accuracy but slower conversion.\n"
"Recommended: 512 for testing, 1024+ for production"
)
),
],
outputs=[
io.Model.Output(
display_name="Quantized Model",
tooltip="Quantized model ready for inference"
),
io.String.Output(
display_name="Statistics",
tooltip="Quantization statistics and metrics"
)
]
)
```
**Node-Level Documentation:**
```python
class ModelOptLoader(io.ComfyNode):
"""
# ModelOpt TensorRT Loader
Load models quantized with NVIDIA ModelOpt for optimized inference on NVIDIA GPUs.
## Features
- Support for FP8, INT8, INT4 quantization
- Dynamic and static batch sizes
- Automatic GPU architecture detection
## Requirements
- NVIDIA GPU with Compute Capability 8.0+
- TensorRT 8.6+
- For FP8: Ada Lovelace architecture (RTX 40-series)
## Usage
1. Place quantized models in `models/tensorrt/`
2. Select model from dropdown
3. Choose precision matching your model
4. Set batch size for your workflow
"""
```
## 5. Conversion Process
### Programmatic Conversion
**Basic PTQ Workflow:**
```python
import torch
import modelopt.torch.quantization as mtq
from transformers import AutoModelForCausalLM
from modelopt.torch.utils import dataset_utils
# 1. Load model
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf")
# 2. Select quantization config
config = mtq.FP8_DEFAULT_CFG # or INT8_DEFAULT_CFG, INT4_AWQ_CFG, NVFP4_DEFAULT_CFG
# 3. Prepare calibration dataset
calib_dataloader = dataset_utils.get_dataset_dataloader(
"cnn_dailymail",
tokenizer,
batch_size=1,
num_samples=512
)
# 4. Define forward loop
def forward_loop(model):
for data in calib_dataloader:
model(**data)
# 5. Quantize model (in-place)
model = mtq.quantize(model, config, forward_loop)
# 6. Export
from modelopt.torch.export import export_hf_checkpoint
export_hf_checkpoint(model, export_dir="./quantized_model")
```
### Conversion Options and Parameters
**Available Quantization Configs:**
- `FP8_DEFAULT_CFG`: Best quality, requires SM 8.9+
- `NVFP4_DEFAULT_CFG`: Maximum compression, Blackwell only
- `INT8_DEFAULT_CFG`: Broad compatibility
- `INT4_AWQ_CFG`: Weight-only, small-batch inference
- `W4A8_AWQ_BETA_CFG`: Mixed precision
**Custom Quantization Config:**
```python
import copy
# Create custom config
CUSTOM_INT4_AWQ_CFG = copy.deepcopy(mtq.INT4_AWQ_CFG)
# Skip quantizing specific layers
CUSTOM_INT4_AWQ_CFG["quant_cfg"]["*lm_head*"] = {"enable": False}
# Apply custom config
model = mtq.quantize(model, CUSTOM_INT4_AWQ_CFG, forward_loop)
```
**AutoQuantize (Mixed Precision):**
```python
from modelopt.torch.quantization import auto_quantize
# Define constraints
constraints = {"effective_bits": 4.8} # Target effective precision
# Specify quantization formats to search
quantization_formats = [mtq.NVFP4_DEFAULT_CFG, mtq.FP8_DEFAULT_CFG]
# Auto-search optimal per-layer quantization
model, state_dict = auto_quantize(
model,
constraints=constraints,
quantization_formats=quantization_formats,
data_loader=calib_dataloader,
forward_step=forward_step
)
```
### Batch Conversion Best Practices
1. **Use representative calibration datasets**:
- CNN/DailyMail for summarization models
- Pile for general language models
- Domain-specific data for specialized models
- Size: 128-512 samples typical (512+ for production)
2. **Multi-GPU considerations**:
- If model trained with tensor parallelism, calibration needs same GPU count
- Single GPU calibration possible for most models
3. **Memory optimization**:
- Use `accelerate` package for large models
- Adjust `device_map` settings
- Can use CPU for quantization (very slow, not recommended)
4. **Error prevention**:
- Clean output directory before each run
- Ensure CUDA/cuDNN version compatibility
- Verify compute capability meets format requirements
### Conversion Time Estimates
**PTQ Calibration Duration (128-512 samples):**
- **Small models (7B)**: 5-15 minutes on H100
- **Medium models (13B-30B)**: 15-30 minutes
- **Large models (70B)**: 30-60 minutes
- **Very large models (405B)**: 1-2 hours
- **Diffusion models (SD3.5)**: 10-25 minutes
- **SVD/SVD-XT**: Up to 1 hour
**Engine Building Time (TensorRT compilation):**
- **First time**: 3-10 minutes for LLMs, 10-25 minutes for diffusion
- **Subsequent builds**: Much faster (engine cached)
### Resource Requirements During Conversion
**Peak VRAM Usage:**
- Calibration: Model size + 20-30% overhead
- Example: 7B FP16 model needs ~18-20GB during calibration
- **Must fit entire model in GPU memory** for efficient calibration
**Temporary Storage:**
- Calibration cache: 1-5GB
- Intermediate files: 2-10GB
- Clean output directory to avoid corruption
### Metadata Preservation
ModelOpt exports preserve:
- Model configuration (JSON)
- Tokenizer configuration (YAML)
- Quantized weights (safetensors format)
- Scaling factors and calibration parameters
**Export formats maintain full metadata:**
```python
# Hugging Face checkpoint format
export_hf_checkpoint(model, export_dir)
# TensorRT-LLM checkpoint
export_tensorrt_llm_checkpoint(model, decoder_type, dtype, export_dir)
# ONNX (for Windows/DirectML)
torch.onnx.export(model, ...)
```
## 6. Hardware Requirements
### GPU Requirements
**Minimum Compute Capability: SM 7.5+ (Turing and newer)**
**Compute Capability by Quantization Format:**
- **INT8/INT4**: SM 7.5+ (T4, RTX 2000+)
- **FP8**: SM 8.9+ (Ada Lovelace, Hopper)
- **FP4**: SM 9.0+ (Hopper) or SM 12.0 (Blackwell)
**Specific GPU Models:**
| GPU | Architecture | SM | VRAM | Best For |
|-----|--------------|-----|------|----------|
| H100 | Hopper | 9.0 | 80GB | FP8, NVFP4, large models |
| H200 | Hopper | 9.0 | 480GB variant | Massive models, highest performance |
| RTX 4090 | Ada Lovelace | 8.9 | 24GB | FP8, consumer high-end |
| RTX 4080 | Ada Lovelace | 8.9 | 16GB | FP8, consumer mid-range |
| A100 | Ampere | 8.0 | 40/80GB | INT8, datacenter |
| RTX A6000 | Ampere | 8.6 | 48GB | INT8, workstation |
| T4 | Turing | 7.5 | 16GB | INT8, minimum support |
**VRAM Requirements by Model Size (without quantization):**
- **7B parameters**: ~14GB FP16, requires 24GB GPU minimum
- **13B parameters**: ~26GB FP16, requires 40-48GB GPU
- **70B parameters**: ~140GB FP16, requires multiple GPUs or quantization
- **SDXL**: FP16 ~6GB, FP8 ~3GB
**PTQ Calibration Memory:**
Quantization process needs 20-24GB VRAM for calibration on top of model size.
### CPU Requirements
- **Core count**: Equal to or greater than number of GPUs
- **RAM**: 64GB minimum, 512GB+ recommended for multi-GPU
- CPU primarily used for data loading during calibration
### Disk Space Requirements
**Storage by Model Type:**
- **FP32**: ~4 bytes/parameter (7B = 28GB, 70B = 280GB)
- **FP16**: ~2 bytes/parameter (7B = 14GB, 70B = 140GB)
- **FP8**: ~1 byte/parameter (7B = 7GB, 70B = 70GB)
- **INT8**: ~1 byte/parameter
- **INT4**: ~0.5 bytes/parameter (7B = 3.5GB, 70B = 35GB)
**Total Workflow Storage:**
Original model + quantized checkpoint + TensorRT engine + calibration cache
- Example 7B workflow: ~40-50GB total
### Performance Differences by Hardware
**Quantization Performance Improvements (vs FP16 baseline):**
| Model | GPU | Quantization | Batch | Speedup | VRAM Reduction |
|-------|-----|--------------|-------|---------|----------------|
| Llama 3 7B | H100 | FP8 | 1 | 1.5x | ~50% |
| Llama 3 7B | H100 | FP8 | 32 | 1.8x | ~50% |
| Llama 3 7B | H100 | INT4 AWQ | 1 | 2.5x | ~75% |
| Llama 3 70B | H100 | FP8 | 32 | 2.0x | ~50% |
| Llama 3 70B | H100 | INT4 AWQ | 1 | 3.0x | ~75% |
| SD3.5 Large | RTX | FP8 | 1 | 2.3x | 40% (19GB→11GB) |
**Batch Size Impact:**
- **Small batch (≤4)**: Memory-bound, INT4 AWQ best
- **Large batch (≥16)**: Compute-bound, FP8 best
## 7. Implementation References
### Official NVIDIA TensorRT Model Optimizer Repository
**Primary Resource:** https://github.com/NVIDIA/TensorRT-Model-Optimizer
**Key Examples in Repository:**
- `examples/llm_ptq/notebooks/`: Jupyter notebooks for PTQ workflows
- `1_FP4-FP8_PTQ_Min-Max_Calibration.ipynb`
- `2_PTQ_AWQ_Calibration.ipynb`
- `3_PTQ_AutoQuantization.ipynb`
- `examples/diffusers/`: Stable Diffusion quantization
- `examples/vlm_ptq/`: Vision-language models
- `examples/onnx_ptq/`: ONNX model quantization
- `examples/llm_qat/`: QAT workflows
**Python Scripts:**
- `hf_ptq.py`: HuggingFace model PTQ
- `modelopt_to_tensorrt_llm.py`: Build TensorRT-LLM engines
- `nemo_example.sh`: NeMo PTQ workflow
## Key Recommendations for Your ComfyUI Node Pack
### Critical Findings
1. **Limited Diffusion Support**: ModelOpt only officially supports SDXL and SD1.5 with INT8 quantization. **FLUX, Qwen Image, and WAN 2.2 are NOT supported.**
2. **NeMo Dependency**: Diffusion model quantization requires NeMo framework conversion, adding complexity to your workflow.
3. **No Adapter Support**: LoRAs, ControlNets, and other adapters cannot be quantized directly. They must be applied at inference time after loading the quantized base model.
4. **Format Limitations**: For diffusion models, only INT8 is supported (no FP8 or FP4 despite these formats being available for LLMs).
### Implementation Strategy
**For SDXL/SD1.5:**
- Use official ModelOpt INT8 quantization via NeMo
- Achieve ~2x speedup with minimal quality loss
- Deploy via TensorRT for optimal performance
**For FLUX/Qwen/WAN:**
- ModelOpt is not an option
- Consider community FP8 checkpoints
- Implement GGUF loader nodes instead
- Document limitations clearly to users
**For ComfyUI Integration:**
- Follow V3 schema patterns strictly
- Implement robust validation with GPU capability checks
- Provide clear error messages with troubleshooting steps
- Use progress bars for long operations
- Cache loaded models to avoid repeated loading overhead
### Version Compatibility Matrix
| Component | Minimum Version | Recommended Version |
|-----------|----------------|---------------------|
| PyTorch | 2.6+ | Latest stable |
| CUDA | 12.0+ | 12.4+ |
| ModelOpt | 0.27.0+ | 0.33+ |
| TensorRT-LLM | 0.13.0+ | 1.2.0+ |
| Python | 3.10+ | 3.11 |
**GPU Requirements:**
- FP8: Ada Lovelace (SM 8.9+) or Hopper (SM 9.0+)
- INT8: Turing (SM 7.5+) or newer
- FP4: Blackwell (SM 12.0) only
---
This comprehensive guide provides the foundation for building a robust ModelOpt integration for ComfyUI. Focus on SDXL/SD1.5 support where ModelOpt excels, and clearly document the lack of support for modern models like FLUX and Qwen Image.