Files
hao-ai-lab-FastVideo/docs/inference/mlx_taeh3.md
T

160 lines
7.8 KiB
Markdown

# Decode H3 previews with TAEH3 on MLX
TAEH3 is an optional tiny video decoder for MiniMax H3. It replaces only
video reconstruction. The denoiser, sampler, resolution, frame count, and
audio decoder stay unchanged. The full H3 VAE remains the default.
TAEH3 produces a different reconstruction. Fine fur, fabric, vegetation,
and surface textures can look softer. Use it for previews or when you accept
that tradeoff. It is not a lossless acceleration of the full VAE.
## Generate a video
Use your existing MLX FastH3 environment and converted checkpoint. The same
`--video-decode-backend taeh3` flag works on `mlx_fasth3.py` (V1) and
`mlx_fasth3_8step.py` (V2):
```bash
python examples/inference/basic/mlx_fasth3.py \
--model-root ~/models/FastH3-Preview-v0.2 \
--mlx-checkpoint ~/models/FastH3-MLX/int6 \
--prompt 'A red panda beside a mountain lake at sunrise.' \
--height 480 --width 832 --num-frames 124 --steps 4 --seed 2027 \
--video-decode-backend taeh3 --vae-dtype fp32 \
--output-path video_samples/taeh3_preview.mp4
```
The first run downloads a 22.7 MB safetensors checkpoint from an immutable
upstream revision and verifies its SHA-256 digest. No remote Python code runs.
The cache is `~/.cache/fastvideo/taehv/taeh3.safetensors`.
Use `--taeh3-checkpoint /path/to/taeh3.safetensors` for offline use or a custom
trained checkpoint. Custom files must match the decoder architecture; they
are not required to match the upstream digest.
| Option | Default | Behavior |
| --- | --- | --- |
| `--video-decode-backend` | `h3-vae` | Select `taeh3` for approximate decoding. |
| `--taeh3-checkpoint` | Unset | Use the pinned upstream checkpoint from cache. |
| `--taeh3-chunk-size` | `5` | Latent frames per execution chunk. Smaller chunks reduce feature memory. |
| `--vae-dtype` | `fp32` | Decoder computation dtype. FP16 and BF16 are separate numerical tradeoffs. |
`--tiled-video-decode` controls the full VAE only. TAEH3 uses the whole spatial
canvas and bounded temporal chunks. Its memory blocks carry state across
chunks. The pipeline reports `video_decode_backend`, decode timing, and MLX
peak memory alongside the existing generation metrics.
The mode composes with `--fast-spatial` and temporal `--fast`. Those options
change the denoising workload and have additional quality costs. A TAEH3-only
measurement does not establish the quality or speed of a combined mode.
## Latent contract
The native decoder reads normalized diffusion latents in NCTHW layout through
`decode_latents_taeh3_mlx`. Do not apply the full VAE's mean, standard deviation,
or pixel denormalization. Its 24 latent channels reconstruct RGB at 16 times
the latent spatial dimensions.
H3 uses latent lengths `5*k-3`, such as 2, 7, and 37. TAEH3 removes three raw
frames from each group of 20 decoder outputs. Thus 37 latent frames produce
124 RGB frames. The port validates that contract before returning output.
## Provenance and validation
Architecture and weights come from Ollin Boer Bohan's MIT-licensed
[TAEHV H3 implementation](https://github.com/madebyollin/taehv/commit/62f7591f59dfbb4c3c02b7a621d180a9eeaba26c).
The [Aryan fork](https://github.com/aryan5v/taehv/tree/aryan/first-class-taeh3)
adds an explicit `TAEH3` API and checkpoint-loading tests. The fork is not an
official MiniMax release and does not contain newly trained H3 weights.
Run the numerical tests against a local TAEHV checkout containing the released
weights:
```bash
FASTVIDEO_TEST_TAEH3_REFERENCE_DIR=/path/to/taehv \
python -m pytest fastvideo/tests/mlx/test_mlx_taeh3.py -q
```
Tests compare MLX FP32 with upstream sequential FP32 and parallel FP64 at
`atol=1e-5, rtol=1e-5`. The initial parallel CPU FP32 comparison failed that
strict gate, reaching about `4e-5` maximum error on a 37-latent small fixture.
CPU convolution rounding changes with its batch size. The FP64 reference
and sequential FP32 checks distinguish this from a temporal chunking error.
The original failed comparison is not reported as a pass.
Passing these tests means the MLX port agrees with the tiny decoder within
the specified tolerance. It does not mean TAEH3 matches the full H3 VAE.
## Compare decoders without another denoising run
Save the normalized packed `video_rows` returned by `pipeline.denoise` as
`np.savez("latents.npz", video=video_rows)`. Then run:
```bash
python examples/inference/basic/mlx_h3_decode_benchmark.py \
--latents latents.npz \
--model-root ~/models/FastH3-Preview-v0.2 \
--mlx-checkpoint ~/models/FastH3-MLX/int6 \
--height 480 --width 832 --num-frames 124 \
--output-dir outputs/taeh3_comparison
```
Use a fresh output directory. The benchmark writes the first decoded frame
arrays and a JSON report with the input digest, MLX version, device, per-trial
latency, MLX peak active memory, lifetime process peak RSS, and swap snapshots.
Decoder loading is included; first-time checkpoint downloading is excluded.
Only run one MLX workload at a time. `--repeats` reverses the decoder order on
alternate trials. Do not treat two memory counters as additive or infer zero
page-outs from unchanged swap snapshots.
## Measured decoder results
On an Apple M4 Max with 36 GB unified memory, MLX 0.32.2, FP32 decoding,
and five-latent execution chunks:
| Workload | Full H3 VAE | TAEH3 |
| --- | --- | --- |
| Saved 37-frame latents to 124 RGB frames, 832x480 | 107.90 s | 1.44 s |
| MLX peak active memory for that decode | 11.03 GiB | 3.62 GiB |
These are one matched pair using the same seed-2027 production latent file,
including decoder loading. Both swap snapshots stayed unchanged. The decoded
images differ: PSNR against the full VAE was 29.86 dB, and inspected frames
showed softer fine detail. The approximately 75x ratio applies only to this
decoder comparison, not the entire generation pipeline.
Eight additional TAEH3-only decodes measured 0.96 s on first use and a 0.98 s median across seven warm trials, ranging from 0.96 to 0.99 s. Decoder construction and weight loading were included in each trial. The full VAE was not repeated eight times.
### Native resolution with temporal fast
A separate uncached run with `--fast --video-decode-backend taeh3`, without
spatial fast, completed in **205.47 s wall time**. It kept the native 832x480
canvas, denoised 73 source frames, and used RIFE to produce 124 output frames
with full-duration audio. Seed 2027, four steps, dense attention, INT6.
Prompt encoding took 16.66 s, denoising 181.14 s, TAEH3 decoding 0.64 s, RIFE
5.23 s, audio decoding 0.73 s, and muxing 0.40 s. Peak denoise MLX allocation
was 17.87 GiB. System swap use rose from 1231.19 to 2753.75 MiB.
The output has 124 H.264 frames at 832x480 and stereo AAC at 32 kHz.
This is one combined-mode measurement. It preserves spatial resolution but
still combines frame interpolation with approximate decoding. Frame samples
retain more fine detail than the spatial fast experiment below. Motion and
speech need human review; do not infer native dense generation parity.
### Spatial fast experiment, not the preferred quality path
A separate uncached generation with `--fast-spatial --video-decode-backend
taeh3` produced a 124-frame, 832x480 MP4 with stereo audio in **95.75 s wall
time**. Seed 2028, four denoising calls, INT6 weights, dense attention, and no
temporal fast mode were used. The internal canvas was 416x256, then cropped
and upscaled to the requested output size.
That run spent 16.66 s encoding the prompt, 77.05 s denoising, 0.34 s decoding
video, 0.30 s upscaling, 0.73 s decoding audio, and 0.40 s muxing. Peak denoise
MLX allocation was 16.85 GiB. System swap use rose from 1041.94 to 1247.19 MiB;
this measurement does not attribute that increase to a particular process.
It is one end-to-end result, not a repeated benchmark or native-resolution
quality comparison. The reduced canvas visibly loses detail, especially in
the opening frames. Speech intelligibility and motion quality need human
review before treating this combination as a final-output preset.