Skip to content

cookbook(diffusers): add FP8 quantized checkpoint support - #304

Open
ConstBob wants to merge 3 commits into
NVIDIA:mainfrom
ConstBob:feat/diffusers-fp8
Open

cookbook(diffusers): add FP8 quantized checkpoint support#304
ConstBob wants to merge 3 commits into
NVIDIA:mainfrom
ConstBob:feat/diffusers-fp8

Conversation

@ConstBob

@ConstBob ConstBob commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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 (public revision=fp8 or local COSMOS3_FP8_MODEL_PATH); use-case request cells stay the same, and nvidia-modelopt is pinned for ModelOpt restore.

ConstBob and others added 2 commits August 5, 2026 14:02
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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
" 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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review and suggestions! I implemented the Distilled FP8 preload approach you suggested (full-path transformer load → update_componentsload_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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@ConstBob
ConstBob force-pushed the feat/diffusers-fp8 branch from 1831c57 to f4878b6 Compare August 6, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants