Skip to content

[Cosmos3] Mixed W8A8/W8A16 denoising for ModelOpt FP8 checkpoints - #14664

Open
ConstBob wants to merge 4 commits into
huggingface:mainfrom
ConstBob:feat/cosmos3-mixed-w8a8-w8a16
Open

[Cosmos3] Mixed W8A8/W8A16 denoising for ModelOpt FP8 checkpoints#14664
ConstBob wants to merge 4 commits into
huggingface:mainfrom
ConstBob:feat/cosmos3-mixed-w8a8-w8a16

Conversation

@ConstBob

@ConstBob ConstBob commented Aug 31, 2026

Copy link
Copy Markdown

What does this PR do?

Adds denoising-step-aware mixed W8A8/W8A16 for Cosmos3 ModelOpt FP8 checkpoints, so official Hub revision=fp8 is not W8A8 on every step.

This is the Diffusers counterpart of the FP8 schedule contract in vllm-omni#6560 (not a port of NVFP4, tensor parallel, caches, or vllm serve).

Behavior

  • Checkpoint weights stay serialized static W8A8 (weight_only: false). Mixing is runtime.
  • Video Nano / Super / Super-I2V fp8: first 3 + last 3 scheduler steps W8A16 (dequant + F.linear); middle steps native ModelOpt W8A8.
  • Precision is chosen once per scheduler step (CFG cond/uncond match). Reasoner defaults to A16 when the checkpoint says reasoner: a16.
  • Distilled 4-step and Super-T2I fp8 have runtime: null → mixed off, all steps native W8A8.
  • mixed_precision_format=None (default) reads quantization_config.runtime.diffusion_step_policy from the transformer (including on-disk transformer/config.json when the live ModelOpt config omits runtime). "none" disables only the schedule. "fp8" forces it.

Code

  • src/diffusers/pipelines/cosmos/mixed_precision.py — policy parse (fail-closed), linear discovery, W8A16 dispatch.
  • pipeline_cosmos3_omni.py and modular denoise.py — apply/reset once per step.
  • docs/source/en/api/pipelines/cosmos3.md — user-facing FP8 mixed section.
  • Out of scope: NVFP4, custom kernels, dense-weight cache, cookbook/Hub cards (follow-up after this lands).

Tests

Unit (CPU, in-tree): tests/pipelines/cosmos/test_cosmos3_mixed_precision.py17 passed (schedule boundaries, 1-step stays native, reasoner vs generation, overlay of live runtime: null + on-disk policy, FP32 W8A16, malformed policy / NVFP4 reject).

pytest tests/pipelines/cosmos/test_cosmos3_mixed_precision.py
# 17 passed, 5 subtests passed

GPU, not in CI (official Hub revision=fp8):

  • Cosmos3-Nano @fp8: auto-enable 3+3; 8-step T2I E2E with exact trace W8A16×3 / W8A8×2 / W8A16×3; 35-step latent W8A16×3 / W8A8×29 / W8A16×3.
  • Super-Text2Image-4Step @fp8 transformer: auto-off; GEMM stays native W8A8.

Not claimed in this PR: Super 64B generate, 189-frame I2V, vs-BF16 hashes, or the vLLM-Omni H100 throughput table.

Depends on NVIDIA ModelOpt restore for serialized FP8 (same as existing Cosmos3 FP8 loading).

Self-review (final)

Ran the self-review skill on huggingface/main...HEAD against .ai/references/review-rules.md (plus pipelines.md, modular.md, testing.md, code_style.md, pitfalls.md).

Diff files: mixed_precision.py, pipeline_cosmos3_omni.py, modular_pipelines/cosmos/denoise.py, test_cosmos3_mixed_precision.py, docs/source/en/api/pipelines/cosmos3.md.

Blocking issues: none.

Non-blocking — left for review (deliberate):

  1. Transformer forward wrappers stay installed after __call__ (mixed_precision.py). Inactive wrappers call the original ModelOpt forward. Per pipelines.md gotcha 7 we could uninstall on exit (PAG-style); we kept wrappers to avoid re-wrapping hundreds of linears each generate.
  2. W8A16 reads layer.weight then F.linear. Per testing.md, that bypasses group-offload leaf hooks. Same dense A16 reference as vLLM-Omni; group offload + serialized FP8 was not a target.
  3. Focused tests use fake ModelOpt linears + unittest.TestCase. Per testing.md, new pipeline tests prefer real tiny classes and pytest mixins. Existing Cosmos3 pipeline tests are still unittest; ModelOpt restore is impractical in CI.
  4. mixed_precision_overlap is parsed from the checkpoint / resolve(), not exposed on Cosmos3OmniPipeline.__call__.
  5. Docs revision="fp8" example does not repeat full ModelOpt from_pretrained kwargs; loading still follows the ModelOpt guide.

Dead code: FIRST_LAST_N_FP8_POLICY, from_kwargs, and quantization_config_from_module are used (tests + resolve). No unused runtime methods.

Verdict: READY.

Before submitting

  • Did you use an AI agent (Claude Code, Codex, Cursor, etc.) to help with this PR? If so:
    • Did you read the Coding with AI agents guide?
    • Did you run the self-review skill on the diff?
    • Did you share the final self-review notes in the PR description or a comment?
  • Did you read the contributor guideline?
  • Did you read our philosophy doc? (important for complex PRs)
  • Was this discussed/approved via a GitHub issue or the forum? Please add a link to it if that's the case.
  • Did you make sure to update the documentation with your changes? Here are the
    documentation guidelines, and
    here are tips on formatting docstrings.
  • Did you write any new necessary tests?
  • Are you the author (or part of the team) of the model/pipeline (only applicable for model/pipeline related PRs)?

Who can review?

@yiyixuxu @asomoza

ConstBob and others added 4 commits August 24, 2026 16:42
…oints.

Keep native ModelOpt GEMM on middle steps and dequant-linear W8A16 on the first/last steps so CFG cond/uncond share one precision per scheduler step.
Enable first/last W8A16 only when transformer/config.json declares diffusion_step_policy, so distilled FP8 stays native W8A8 instead of inheriting a hardcoded 3+3 window.
…schedules.

Read the checkpoint runtime policy from on-disk transformer/config.json when the live ModelOpt config omits it, fail closed on incomplete policies, and allow FP32 activations on the W8A16 path.
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests modular-pipelines pipelines size/L PR with diff > 200 LOC labels Aug 31, 2026
@yiyixuxu
yiyixuxu requested a review from sayakpaul September 2, 2026 01:00

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR. I think it's a bit intrusive in terms of what it is doing. Could you provide runtime numbers (latency, throughput) and also how does it compare in terms of visual results (with and without comparisons would be great).

Comment on lines +65 to +71
Call-site overrides:

- `mixed_precision_format="none"` disables the schedule only (quantized W8A8 remains).
- `mixed_precision_format="fp8"` forces the first/last-N schedule even if the checkpoint has no policy.
- `mixed_precision_first_steps` / `mixed_precision_last_steps` / `mixed_precision_reasoner_policy` override the checkpoint counts and reasoner path (`"high_precision"` = W8A16, `"base_precision"` = native W8A8).

These kwargs are not Accelerate `mixed_precision`. They only select W8A8 vs W8A16 on Cosmos3 ModelOpt linears.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we need to mention things like these. If we want the users to control them, we could simplify the text.


## FP8 mixed W8A8/W8A16 denoising

Official ModelOpt FP8 checkpoints live on the Hub `fp8` revision (for example [`nvidia/Cosmos3-Nano`](https://huggingface.co/nvidia/Cosmos3-Nano) with `revision="fp8"`). The serialized weights are static W8A8. Video Nano / Super / Super-I2V checkpoints also store a `quantization_config.runtime.diffusion_step_policy` on the transformer: the **first 3 and last 3** denoising steps run **W8A16** (dequantized FP8 weights, `torch.nn.functional.linear`), and the middle steps keep native **W8A8**. Precision is chosen once per scheduler step so CFG cond/uncond calls match. Distilled 4-step and Super-T2I FP8 checkpoints omit that policy (`runtime` is `null`) and stay native W8A8 on every step.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Users may not be familiar with the convention of W8A8. It would make sense to elaborate on that.

@@ -0,0 +1,310 @@
# Copyright 2026 The NVIDIA Team and The HuggingFace Team. All rights reserved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We don't need these tests yet. Let's first gauge if this checkpoint is used much.

Comment on lines +1711 to +1717
mixed_precision = Cosmos3MixedPrecisionConfig.resolve(
self.transformer,
mixed_precision_format=mixed_precision_format,
mixed_precision_first_steps=mixed_precision_first_steps,
mixed_precision_last_steps=mixed_precision_last_steps,
mixed_precision_reasoner_policy=mixed_precision_reasoner_policy,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How does it work out when people would use a different quantization backend like TorchAO, for example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation modular-pipelines pipelines size/L PR with diff > 200 LOC tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants