---
pipeline_tag: text-to-video
license: other
license_name: minimax-h3-community-license-agreement
license_link: https://huggingface.co/MiniMaxAI/MiniMax-H3/blob/main/LICENSE
library_name: minimax-h3
base_model: MiniMaxAI/MiniMax-H3
base_model_relation: quantized
tags:
- text-to-video
- text-to-audio-video
- image-to-video
- audio-video-generation
- multimodal
- mxfp8
- fp8
- quantized
- auto-round
- vllm
---
# MiniMax-H3 — MXFP8 (weight-quantized, vLLM-Omni native layout)
An **MXFP8 (OCP microscaling FP8)** weight-quantized build of
[`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3) — the omni-modal
(video + native stereo audio) generative DiT — produced with
[Intel auto-round](https://github.com/intel/auto-round) (zero-shot RTN) and converted to the
**native fused layout** that vLLM-Omni serves. Both diffusion transformers are quantized; the
large **AdaLN** branch is deliberately **kept in BF16**.
**TL;DR**
- Each DiT **~62 GB (bf16) → ~43.65 GB (≈0.71×, −29 %)**; AdaLN (≈13 B params/DiT) stays BF16 because inference caches it and it never hits a matmul.
- Quality is effectively unchanged: VBench `imaging_quality` **0.6306 (bf16) → 0.6313 (mxfp8)**, Δ **+0.12 %** over 93 prompts.
- Serves end-to-end on **2×H200 (TP2)** and produces video **with synchronized audio**. H200 runs the FP8 weights through a Marlin **dequant** kernel (correct, memory-saving, **not** faster).
> ⚠️ Research / evaluation artifact. H3 has native/diffusers dual packaging; this repo is the
> **native fused** checkpoint (serve-ready). A diffusers-layout quant output also exists at
> `MiniMax-H3-mxfp8/` (not directly servable — see below).
---
## Showcase — official T2VA example, bf16 vs MXFP8
Reproduction of the official MiniMax-H3 **Text-to-Audio-Video (T2VA)** showcase
([`assets/t2va.mp4`](https://huggingface.co/MiniMaxAI/MiniMax-H3/blob/main/assets/t2va.mp4)),
generated on **our own vLLM-Omni stack with identical settings** (1344×768, 24 fps, 32 steps,
10 s, seed 0, flow-shift 12 / audio-flow-shift 3.0, task `t2va`) so the **only** variable is the
quantization. Prompt = the official H3-Context-IR "starship captain" T2VA prompt.
| bf16 (reference) | MXFP8 (this model) |
|---|---|
| [`assets/t2va_official_bf16.mp4`](assets/t2va_official_bf16.mp4) | [`assets/t2va_official_mxfp8.mp4`](assets/t2va_official_mxfp8.mp4) |
Both clips: 1344×768, 24 fps, 243 frames (10.12 s), video **+ stereo audio**, matching the
official `t2va.mp4` geometry (1344×768 / 24 fps / 10.12 s).
| Metric (this single example) | bf16 | MXFP8 |
|---|---|---|
| VBench `imaging_quality` | 0.6671 | 0.6614 |
| PSNR (mxfp8 vs bf16) | — | 14.69 dB |
| SSIM (mxfp8 vs bf16) | — | 0.5589 |
| LPIPS (mxfp8 vs bf16) | — | 0.3804 |
**How to read the PSNR/SSIM/LPIPS.** They are *moderate*, not near-identical, because the two
videos are **not** pixel-for-pixel copies. A ~2–3 % MXFP8 weight perturbation, compounded over the
denoising steps, nudges the sampler onto a **different but equally valid trajectory** — same scene,
motion and soundscape, slightly different pixels. The matching VBench `imaging_quality` (and the
93-prompt result below) confirm there is **no systematic quality loss**; the reference-metric
distance is sampling chaos, not degradation. (The distance is a little larger than Wan2.2's here
because this clip is longer/higher-fps, giving more room for trajectory divergence.)
---
## What was quantized
H3 has **two** DiTs sharing one architecture:
| Component | Role | Quantized? |
|---|---|---|
| `transformer` | FL2VA / T2VA DiT (33.12 B) | ✅ MXFP8 (from **BF16**) |
| `transformer_ref` (diffusers layout) / `Ref2VA` | Ref2VA DiT (33.12 B) | ✅ MXFP8 (from **BF16**) |
| `text_encoder` (Qwen3-VL) | text/vision encoding | ❌ kept bf16 |
| `vae` / `audio_vae` | video / audio decode | ❌ unchanged |
| `adaln_proj.linear` (×52/DiT) | AdaLN modulation (~13 B) | ❌ **kept BF16 on purpose** |
**Quantized Linears** — 6 per block × 52 = **312 per DiT**:
`attn.{to_q,to_k,to_v,to_out.0}` + `ff.net.0.proj` (SwiGLU gate+up) + `ff.net.2` (down).
Per DiT: **312 × F8_E4M3** weights + **312 × U8** E8M0 block scales + **314 × BF16** (AdaLN,
norms, embedders, biases).
**Why AdaLN stays BF16.** The official card notes ~13 B params live in AdaLN, and because AdaLN
modulation can be **precomputed and cached**, pure inference never loads them into a matmul
(vLLM-Omni ships `adaln_cache.py` and caches by default). Quantizing them would cost accuracy for
zero runtime benefit, so they are pinned to BF16 via
`extra_config: {".*adaln_proj\\.linear.*": {bits: 16, data_type: float, act_bits: 16}}`.
**Quant format** (per `transformer/config.json`):
`quant_method=auto-round`, `data_type=mx_fp`, `bits=8`, `group_size=32`, `sym=True`;
activations `act_data_type=mx_fp`, `act_bits=8`, `act_dynamic=True`, `act_group_size=32`;
`block_name_to_quantize="token_refiner.blocks,blocks"`; `autoround_version=0.17.0`.
### Reproduce the quantization
Unlike Wan2.2, H3 is a **Modular Diffusers** pipeline, so the one-line CLI does **not** work — you
must build the pipeline programmatically and hand it to auto-round. Outline
(`scripts/quant_h3_mxfp8_v2.py`):
```python
pipe = MiniMaxH3ModularPipeline.from_pretrained(LOCAL) # offline
for name in ("transformer", "transformer_ref"): # both DiTs
t = MiniMaxH3Transformer3DModel.from_pretrained(f"{LOCAL}/{name}", torch_dtype=torch.bfloat16)
pipe.update_components(**{name: t})
ar = AutoRound(pipe, scheme="MXFP8", iters=0, ignore_layers="adaln_proj.linear")
ar.quantize_and_save("MiniMax-H3-mxfp8", format="auto_round") # RTN, both DiTs
```
- `iters=0` ⇒ zero-shot **RTN**; `ignore_layers="adaln_proj.linear"` pins all 52 AdaLN layers to BF16.
- Needs the dual-transformer zero-shot fix ([auto-round PR #2481](https://github.com/intel/auto-round/pull/2481)); `transformer_ref` is then auto-detected and quantized too.
- The diffusers-layout output (`MiniMax-H3-mxfp8/`) is converted to this **native fused layout** with `scripts/convert_h3_mxfp8_to_native.py` (head-grouped qkv fusion + fc1 gate/up + synchronized U8 scale reordering). See "Serving" for the three fixes that make it serve correctly.
---
## Size / compression (safetensors header scan, per DiT)
| dtype | #tensors | Size | Meaning |
|---|---:|---:|---|
| F8_E4M3 | 312 | 18.66 GiB | quantized weights (attn + ff) |
| U8 (E8M0) | 312 | 0.58 GiB | per-32 block scales |
| BF16 | 314 | 24.34 GiB | AdaLN (52) + norms + embedders + biases |
| F32 | 12 | 0.06 GiB | proj_in / audio_proj_in / time embedder |
| **Total** | 950 | **43.65 GiB** | vs ~62 GiB bf16 ⇒ **0.71× (−29 %)** |
Because the source is already **BF16** (not FP32 like Wan2.2) and AdaLN (~55 % of bytes) stays
BF16, the per-DiT ratio is a modest ~0.71× rather than ~4×.
---
## Directory layout — how this differs from the base `MiniMax-H3`
This repo is a **serve-ready, self-contained** slice of the base model: **233 GB vs ~465 GB**. The
reduction is **not** mostly from quantization — it is from dropping redundant copies that the base
ships for the modular/diffusers distribution but vLLM-Omni never loads.
The base `MiniMax-H3` is **double-packaged**: it stores both (a) a top-level *modular* component
library (`transformer/`, `transformer_ref/`, `text_encoder/`, `vae/`, `audio_vae/`, `tokenizer/`,
`processor/`, `scheduler/`, …) **and** (b) two already-self-contained pipelines (`FL2VA/`, `Ref2VA/`),
each bundling its *own* transformer + text_encoder + VAEs + tokenizer. The two sets are the **same
weights duplicated** — e.g. the 63 GiB text encoder exists **3×** and the DiT ~4×.
vLLM-Omni serves only from the two pipeline folders (combined mode loads `FL2VA` + `Ref2VA`), so
this repo keeps just that one copy and removes the rest.
| Top-level entry | base `MiniMax-H3` (~465 G) | this repo (233 G) | why |
|---|---|---|---|
| `FL2VA/`, `Ref2VA/` | 135 G each | **117 G each** | kept; transformer swapped to MXFP8 (62→44 G) |
| `transformer/` | 62 G (bf16, 14 shards) | **config.json only (~1 KB)** | shim: carries `quantization_config` so the quant is auto-detected; weights live under the pipelines |
| `transformer_ref/`, `text_encoder/`, `vae/`, `audio_vae/`, `tokenizer/`, `processor/`, `scheduler/`, `audio_scheduler/` | present | **removed** | redundant duplicates of what's already inside `FL2VA/` + `Ref2VA/` |
| `docs/`, `scripts/`, `modular_model_index.json` | present | **removed** | not used for inference |
| `assets/`, `README.md`, `model_index.json` | official | **our showcase + this card** | model card + bf16-vs-MXFP8 samples |
Inside each pipeline, only `transformer/` changes vs the base: **13 bf16 shards → 11 MXFP8 shards**,
tensors are **fused into the vLLM-Omni native naming** (`blocks.N.attn.qkv_proj.weight`, `fc1`
gate/up) each with a paired `.weight_scale`, plus a `quantization_config.json`. The non-quantized
components (`text_encoder/`, `video_vae/`, `audio_vae/`, `tokenizer/`, `processor/`) are **byte-identical
copies** of the base — materialized as real files (no symlinks) so the directory is upload-ready.
> One intrinsic redundancy remains: `FL2VA/` and `Ref2VA/` each carry their own 63 GiB text encoder
> (identical content). De-duplicating would need a symlink/shared path, which conflicts with being a
> self-contained, uploadable checkpoint, so both copies are kept on purpose.
---
## Quality — VBench `imaging_quality` (dense93, n = 93)
Both models generated the same 93 prompts on the **same code path** (the non-modular *combined*
runner, differing only in transformer weights) with the **same recipe** (512×288, 16:9, 24 fps,
16 steps, flow-shift 12, duration 4.0 s, audio-flow-shift 3.0, task `t2va`, `seed=1000+row`), then
scored with the same VBench `imaging_quality`.
| Model | `imaging_quality` | /100 | vs bf16 |
|---|---|---|---|
| bf16 (native) | **0.6306** | 63.06 | — |
| **MXFP8 (this model)** | **0.6313** | 63.13 | **+0.12 %** |
Mean difference is **+0.0007** (mxfp8 marginally higher). Per-video deltas are near-symmetric
(mxfp8 higher on 49/93, lower on 44/93; mean |Δ| ≈ 3.41 on the 0–100 scale) while per-video scores
span 26.8–80.0 — the jitter is diffusion sampling non-determinism (amplified by the low-res,
short, few-step recipe), not a quantization floor.
---
## Serving (vLLM-Omni, 2×H200 / TP2)
```bash
CUDA_VISIBLE_DEVICES=6,7 \
VLLM_WORKER_MULTIPROC_METHOD=spawn VLLM_OMNI_VIDEO_SYNC_TIMEOUT=14400 \
vllm serve /path/to/MiniMax-H3-mxfp8-native --omni \
--host 0.0.0.0 --port 8092 --trust-remote-code \
--num-gpus 2 --tensor-parallel-size 2 --text-encoder-tp-size 2 \
--enforce-eager --diffusion-attention-backend CUDNN_ATTN
# no --task-type ⇒ combined (loads FL2VA + Ref2VA; required for T2VA)
```
- **TP2 is mandatory**: `create_weights` allocates the BF16 skeleton first (two DiTs ≈ 176 GB), so a single 141 GB H200 OOMs; `--enable-cpu-offload` is incompatible (Marlin repack is CUDA-only).
- Ready in ~5–6 min; `GET /health` → 200; ~81 GB/GPU; **0 weight_scale skips** (quantization active).
- `duration` must be in **[4, 15] s**. Submit to `/v1/videos/sync`.
**Three fixes required for correct MXFP8 serving** (carried as local, un-upstreamed patches):
1. a top-level `transformer/config.json` carrying `quantization_config` so vLLM-Omni auto-detects the quant (native weights live under `FL2VA/Ref2VA`);
2. `load_weights` must apply the **same** grouped-qkv / fc1 gate-up reordering to `.weight_scale` as to `.weight` (otherwise scales misalign and output is noise);
3. TP2 + no cpu-offload (above).
---
## Hardware note (important)
H200 (Hopper) has **no native MXFP8 tensor core**; vLLM-Omni runs these weights through a Marlin
weight-only **dequant** GEMM — functionally correct and memory-saving, but ~2.5× slower than bf16
(~13 s vs ~5 s per 4 s/512×288 clip). Use on H200 to **validate accuracy**; native MXFP8 speedups
require Blackwell/B300. Dequant does not change stored values, so accuracy matches a native kernel.
**Runtime is W8A16, not W8A8.** The Marlin kernel is documented as `MXFP8 W8A16 GEMM via Marlin`:
it dequantizes the MXFP8 *weight* (E4M3 + E8M0 block scale) inside the GEMM and multiplies it
against **unquantized bf16 activations** (`a_scales=None`; a 1-byte/fp8 activation path explicitly
raises `"Marlin W8A8 is not supported."`). The checkpoint's `act_data_type=mx_fp / act_bits=8 /
act_dynamic=True` is **quantization-recipe metadata that is not exercised on H200** — activations
stay bf16. True W8A8 (FP8 activations on a native microscaling tensor core) requires Blackwell/B300.
---
## Limitations
- Not faster than bf16 on Hopper; needs 2 GPUs to serve.
- Only the two DiTs are quantized (text encoder, VAEs, AdaLN kept BF16).
- Evaluated with no-reference `imaging_quality` on short 512×288 clips; other dimensions not swept.
- Native/diffusers dual packaging means serving needs the conversion + three fixes above.
## License & attribution
Inherits the base model's license: **MiniMax-H3 Community License Agreement** (`license: other`,
see [LICENSE](https://huggingface.co/MiniMaxAI/MiniMax-H3/blob/main/LICENSE)). This is a derivative
(quantized) checkpoint of
[`MiniMaxAI/MiniMax-H3`](https://huggingface.co/MiniMaxAI/MiniMax-H3); all model credit belongs to
MiniMax. Quantization by Intel [auto-round](https://github.com/intel/auto-round) (zero-shot RTN,
MXFP8, AdaLN kept BF16) with [PR #2481](https://github.com/intel/auto-round/pull/2481). Please also
comply with the upstream MiniMax-H3 usage terms.