cookbook(diffusers): add FP8 quantized checkpoint support - #304
Conversation
Enable Nano/Super and Super 4-Step FP8 via COSMOS3_DIFFUSERS_PRECISION without duplicating request cells. Load public MODEL_IDS with revision=fp8 or a local COSMOS3_FP8_MODEL_PATH.
| " pretrained_source,\n", | ||
| " **from_kwargs,\n", | ||
| " )\n", | ||
| " pipe.load_components(torch_dtype=torch.bfloat16)\n", |
There was a problem hiding this comment.
Not sure what the final model-card/repository structure will be, but we need important fix for the current nvidia/Cosmos3-Experimental subfolders to fix Distilled FP8. Because Distilled models can only be used with Modular pipeline, and how weights and quantization state get loaded for regular and modular pipelines differ.
Regular (called internally by Diffusers):
Cosmos3OmniTransformer.from_pretrained(
".../cosmos3-nano-fp8-14072026/transformer"
)
ModelOpt finds:
".../cosmos3-nano-fp8-14072026/transformer/modelopt_state.pth" ✓
Distilled (called internally by load_components):
Cosmos3OmniTransformer.from_pretrained(
"nvidia/Cosmos3-Experimental",
revision="refs/pr/17",
subfolder="cosmos3-super-t2i-4step-fp8-14072026/transformer",
)
ModelOpt checks:
"nvidia/Cosmos3-Experimental/modelopt_state.pth" ✗
Actual state:
"nvidia/Cosmos3-Experimental/
cosmos3-super-t2i-4step-fp8-14072026/transformer/modelopt_state.pth"
The regular loader joins the component path first, while the modular loader keeps the subfolder separate, which ModelOpt ignores during state lookup.
Fixing it here is safer than changing generic diffusers loading or depending fixing in ModelOpt, which will only land in 0.46+ version and will be incompatible with 0.44 used for quantization already.
So the fix is -- load the transformer from its full path, pin it, then load the remaining components normally:
| " pipe.load_components(torch_dtype=torch.bfloat16)\n", | |
| " if fp8:\n", | |
| " # Pre-load the transformer from its full path so ModelOpt 0.44 finds\n", | |
| " # transformer/modelopt_state.pth and restores the FP8 quantizer graph.\n", | |
| " from diffusers import AutoModel\n", | |
| "\n", | |
| " transformer_dir = Path(pretrained_source) / \"transformer\"\n", | |
| " if not (transformer_dir / \"modelopt_state.pth\").is_file():\n", | |
| " raise RuntimeError(\n", | |
| " f\"FP8 requested but no ModelOpt state at {transformer_dir / 'modelopt_state.pth'}. \"\n", | |
| " \"Point COSMOS3_FP8_MODEL_PATH at a pre-quantized distilled export directory.\"\n", | |
| " )\n", | |
| " transformer = AutoModel.from_pretrained(str(transformer_dir), torch_dtype=torch.bfloat16)\n", | |
| " pipe.update_components(transformer=transformer)\n", | |
| " pipe.load_components(\n", | |
| " pretrained_model_name_or_path=distilled_components_root(pretrained_source),\n", | |
| " torch_dtype=torch.bfloat16,\n", | |
| " )\n", | |
| " else:\n", | |
| " pipe.load_components(torch_dtype=torch.bfloat16)\n", |
There was a problem hiding this comment.
There might be a better solution, so feel free to handle it other way. This is also something to fix for the HF model cards examples, although this is dependent on the final HF repo structure.
Some helper that I used to verify correctness:
def verify_fp8(model: str) -> None:
transformer = get_pipe(model).transformer
tensors = list(transformer.named_parameters()) + list(transformer.named_buffers())
fp8 = sum(p.dtype == torch.float8_e4m3fn for p in transformer.parameters())
quantizers = sum("quantizer" in name.lower() for name, _ in transformer.named_modules())
meta = sum(t.is_meta for _, t in tensors)
# Float8 weights alone are insufficient; quantizers prove ModelOpt restored the scales.
if not fp8 or not quantizers or meta:
raise RuntimeError(f"Invalid FP8 load: weights={fp8}, quantizers={quantizers}, meta={meta}")
print(f"FP8 verified: weights={fp8}, quantizers={quantizers}, meta=0")
There was a problem hiding this comment.
Thanks for the review and suggestions! I implemented the Distilled FP8 preload approach you suggested (full-path transformer load → update_components → load_components, plus a small helper to materialize Hub checkpoints locally) and added your verify_fp8 helper: f4878b6
Below testing has been conducted: On A100-80GB cluster (nvidia-modelopt==0.44.0), I loaded Experimental Distilled T2I 4-Step FP8 through that path and ran verify_fp8("Cosmos3-Super-Text2Image-4Step"); ModelOpt restored transformer/modelopt_state.pth and verification passed (weights=896, quantizers=2709, meta=0).
There was a problem hiding this comment.
Thanks! Let's wait till checkpoints are released in the final HF model repos to update paths in the cookbook before merging it
…er preload Modular load_components passes repo+subfolder, which ModelOpt 0.44 ignores when looking up modelopt_state.pth. Pre-load transformer from its full local path, pin it, then load remaining components. Add verify_fp8 helper.
1831c57 to
f4878b6
Compare
Adds FP8 quantized checkpoint support to the Cosmos3 Diffusers audiovisual cookbook for Nano, Super, and Super 4-Step models. FP8 is enabled with
COSMOS3_DIFFUSERS_PRECISION=fp8(publicrevision=fp8or localCOSMOS3_FP8_MODEL_PATH); use-case request cells stay the same, andnvidia-modeloptis pinned for ModelOpt restore.