Tag: production-pipeline

  • The Production Pipeline I Built to Run MiniMax H3 on a 16GB GPU: Every Design Decision, Explained

    The Production Pipeline I Built to Run MiniMax H3 on a 16GB GPU (and the Design Decisions That Make It Work)

    The Gap Between Tutorials and Production

    Most H3 guides show you the official workflow graph and say “load this, click run.” What they don’t show you is the production engineering — the node chain, the quantization choice, the audio handling, and the verification loops that turn a model that technically runs into a pipeline that reliably produces deliverable video on a 16GB card.

    This is that pipeline. I built it on an RTX 5060 Ti 16GB (AMD Ryzen 7, 30GB RAM + 43GB swap, Ubuntu 22.04) and it has been running as the production path for months. Here’s the whole design, with the reasoning behind every piece.


    The Core Insight: A 14B-Class Joint Audio-Video Model CAN Fit 16GB — With the Right Chain

    The official full-precision model is 118GB. The production model that actually runs daily is minimax_h3_fl2va_pruned_int8_convrot.safetensors at 20GB, paired with a NVFP4-AWQ text encoder. The chain that makes this fit:

    UNETLoader (int8 20GB)
      → PathchSageAttentionKJ (sage_attention: auto)
      → LoraLoaderBypassModelOnly (h3-realism-people LoRA, strength 1.0)
      → MiniMaxH3SigmaShift (shift_video 5.0, shift_audio 5.0)
      → TESpeedMiniMaxH3 (processing_control_value 0.12, cache_depth 0.75)
      → MiniMaxChunkFeedForward (chunks: 4)
      → MiniMaxLowVRAMAttention (head_chunks: 4)
      → KSampler (euler/simple, cfg 1.0)
    

    Every node in this chain is load-bearing:

    • Sage Attention auto — FlashAttention-style kernel on the 14B DiT. Without it, attention alone would thrash the 16GB window.
    • TE-Speed with processing_control_value: 0.12 — the text-encoder optimization that gave a measured 45.6% speedup. The 0.12 control value means only 12% of the timesteps get the full-fidelity path; the rest use the cached KV path (cache_depth: 0.75).
    • Chunked FFN (chunks: 4) — splits the feed-forward layer into 4 chunks so peak activation memory stays inside VRAM instead of spilling.
    • Low-VRAM Attention (head_chunks: 4) — the attention equivalent: process attention heads in chunks. Together with chunked FFN this is what lets a 20GB model run inside 16GB of VRAM with offload.
    • SigmaShift at 5.0/5.0 — flow-matching noise schedule for video and audio. Non-negotiable for H3; without it the sampler produces garbage.

    Why int8 (20GB) Is the Production Default, Not NVFP4 or FP8

    I benchmarked every practical tier on this exact card — same keyframe, same prompt, same seed, same resolution:

    Model File size Staged VRAM Sample speed (768p) Verdict
    bf16 full precision 118 GB 63+49 GB staged ~16 s/it (swap thrash) Runs only with 43GB swap; 17 min for 15s
    FP8 scaled 20 GB 19,983 MB 66.9 s/it Bigger AND slower than NVFP4 — no win
    NVFP4 12 GB 11,944 MB 60.4 s/it Theoretical sweet spot, benchmarked
    int8_convrot (production) 20 GB ~15 GB 16.28 s/it at 480p The battle-tested default

    The interesting data point: the int8 convrot model is faster per iteration than every other quantized tier because of the chunking strategy, and it has logged the most production hours. NVFP4 looks better on paper (smaller file), but int8 is what has actually shipped deliverable after deliverable.

    The Audio Problem Nobody Warns You About

    H3 is a joint audio-video model. Every KSampler pass denoises two streams — video latent [B, 24, latent_t, H//16, W//16] and audio latent [B, 32, 2, T40] (at 40Hz audio latent fps). EmptyMiniMaxH3LatentAV returns a NestedTensor((video, audio)).

    Here’s the trap: VAEDecode only takes unbind()[0] — the video. The audio latent is silently discarded. Your video comes out mute and nothing errors.

    The production chain fixes it with a dedicated audio branch:

    KSampler ─┬─> VAEDecode       (video VAE fp16) ──> IMAGE ─┐
              └─> VAEDecodeAudio  (audio VAE fp32) ──> AUDIO ─┴─> CreateVideo(fps=24, audio=AUDIO) → SaveVideo
    

    CreateVideo has a built-in optional audio input, so no ffmpeg merging is needed — and because both streams come from the same sampling pass, audio and video are frame-aligned by construction.

    And because “aac stream present” ≠ “has sound”, the CLI runs a real verification:

    ffmpeg -hide_banner -i out.mp4 -map 0:a -af volumedetect -f null /dev/null 2>&1 | grep volume
    # good:   mean_volume around -25 to -30 dB
    # silent: mean_volume: -91.0 dB  ← fail
    

    (That -hide_banner is load-bearing — volumedetect output is info-level, and -v error swallows it, making you wrongly conclude there’s no audio track at all.)

    Frame Counting: The 5+17n Constraint

    H3 requires length % 17 == 5. Valid lengths: 5, 22, 39, 56, 73, 90, 107, 124 (≈5.17s at 24fps), 141. The CLI auto-aligns: align(n) bumps any input up to the next valid length. At 24fps (hardcoded in nodes_minimax_h3.py — don’t set 12 or your video plays at half speed), 107 frames = 4.46s and 124 = 5.17s.

    The Canvas Rule: Adapt, Don’t Guess

    Native canvas is short-side 768 with an area cap of 768×1344, each axis rounded to 32. For fl2va (image-to-video), the pipeline keeps 768×1344 as the production canvas. Counterintuitive finding from the benchmarks: the 768×1344 canvas peaks at ~9.9GB VRAM while 512×512 peaks at 15.1GB — because the larger canvas triggers more aggressive offload, so the model is no longer fully resident. The real risk at large canvas isn’t OOM, it’s time (60 s/it vs 2 s/it).

    The Production Toolbox (What I Actually Run)

    The pipeline is wrapped in four CLI tools that talk to ComfyUI’s API on :18189:

    1. h3make.py — One-shot stable render

    python h3make.py --image keyframe.png --action "gently turns head and smiles" --out deliverable
    

    Frozen for reliability: small-motion prompt, int8 model, chunked, auto audio merge. Optional --speech "台词" --voice "年轻撩人的中国女声" triggers H3’s own TTS + mixing.

    2. h3av-cli.py — Flexible generation (fl2va / ref2va / motion transfer)

    python h3av-cli.py fl2va  --first a.png --prompt "..." --length 124
    python h3av-cli.py ref2va --ref_video dance.mp4 --prompt "..." --length 73   # R2V motion transfer
    python h3av-cli.py fl2va  --first a.png --prompt "..." --turbo                 # 4-step Turbo LoRA
    

    --model int8|bf16 switches UNet; --turbo switches to the 4-step Turbo LoRA with shift 12/3; --realism_strength scales the realism LoRA.

    3. h3upscale.py — FlashVSR v1.1 video upscale

    python h3upscale.py --video gen.mp4 --scale 2
    

    2× upscale with audio passthrough, quality_locked profile, bf16 precision, offload_after release policy.

    4. h3speech.py — Dialogue dubbing

    python h3speech.py --text "相公你来啊" --voice "年轻撩人的中国女声, 温柔妩媚" --prefix out
    

    Uses MiniMaxH3VoiceProfileT8 (described_voice mode, speaker_id nv_ling) → MiniMaxH3SpeechPlanT8 → MiniMaxH3SpeechStudioT8 with dual_clock_euler sampler.

    Why This Pipeline Design Is the Optimal Answer for 16GB

    1. One model, one config, frozen — h3make bakes in the known-good settings (small motion, int8, chunks=4) so daily production never drifts into a broken combination.
    2. Audio is first-class — the dual-branch decode + CreateVideo merge + volumedetect verification means “silent video” cannot ship silently.
    3. Every optimization is a node, not a fork — Sage Attention, TE-Speed, chunked FFN, low-VRAM attention are all composable nodes in the graph. You can A/B any single node without touching the rest.
    4. Verification is built in — the CLI checks file existence, ffprobe stream layout, and audio level on every render. If it says “DONE”, the file is deliverable.
    5. The whole thing is API-driven — submit(graph) posts to /prompt, wait(pid) polls /history/{pid}. No clicking in the UI, no manual graph editing, fully scriptable for batch work.

    What This Costs (Real Numbers)

    Operation Time Notes
    512×512, 56 frames, 20 steps ~75s end-to-end 2.0 s/it, peak 15.1GB
    768×1344, 124 frames, 20 steps ~23 min 60 s/it, peak 9.9GB
    768×1344, 107 frames (h3make default) ~4-5 min render production sweet spot
    480p, 362 frames (15s), 20 steps 17 min 28 s 20 s/it, peak 12.9GB

    The production choice is deliberate: 768×1344 native canvas at 107-124 frames gives 4-5s clips with full quality, and the pipeline handles upscale + dubbing as separate stages.


    Everything above is the actual production system on a T2 workstation (RTX 5060 Ti 16GB / AMD Ryzen 7 / 30GB RAM + 43GB swap / Ubuntu 22.04 / ComfyUI-H3 on :18189). All timings measured, all model files verified by byte count, all audio levels measured with volumedetect.


    More in the MiniMax H3 on 16GB series