Skip to content

feat(minimax-h3): replace the t2va Flow-GRPO recipe with the measured configuration - #403

Open
celve wants to merge 12 commits into
Tencent-Hunyuan:mainfrom
celve:LIN-883/flowgrpo-retune
Open

feat(minimax-h3): replace the t2va Flow-GRPO recipe with the measured configuration#403
celve wants to merge 12 commits into
Tencent-Hunyuan:mainfrom
celve:LIN-883/flowgrpo-retune

Conversation

@celve

@celve celve commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Replaces the H3 t2va Flow-GRPO recipe with the configuration that was actually run, and deletes the sibling I first proposed adding.

Why replace rather than add

The values currently on main have never been run by anyone: 768x768, sde_indices: [0, 3, 6], samples_per_prompt: 4, learning_rate: 3e-4, num_updates_per_batch: 2. They arrived in 9e3e4b13, and the author of the branch that commit landed on states those four knobs match none of theirs and that they have no measurements behind them. The nearest thing anyone did run at 768 — a larger group and a different reward — produced four null arms.

So I cannot say the old configuration fails; I can say nobody has tried it, and that at 768 only 3 of 10 SDE steps fit in replay. Shipping it beside a measured recipe invites running the unrun one, which is why this replaces instead of adding a variant. The route back to 768 stays in the header.

on main this PR
canvas 768x768 256x384
reward videopickscore + clap imagebind + clap
SDE steps trained 3 of 10 all 10
group 4 16
learning rate 3e-4 1e-4
updates per batch 2 1

Two fields this drops

Upstream's file carries master_dtype: fp32 and cast_forward_inputs: false, and neither appears here, because neither was present in the runs below. fsdp_wrap casts params only when master_dtype is set, so its absence leaves the trainable LoRA adapters at bf16 rather than fp32 masters — a real difference, and I am not claiming bf16 is better, only that it is what was measured. Flipping it blind would recreate the problem this PR is about, so it is in the test plan.

The measurement

Two pods, 1x8 H20, same commit and recipe, differing only in seed. Pooled fit with per-seed intercepts and one shared slope, pre-registered before either seed reached the read point:

+0.00234 reward/step, t=+6.67, p<0.0001, 45 rollouts per seed, seeds agreeing at p=0.598. OLS and Newey-West HAC both p<0.0001. Levels move 0.205→0.288 and 0.173→0.313.

The canvas is what pays for the rest: 3966 packed rows against ~22k means every SDE step is replayable and the group can be 16 wide. All four knobs move together and no run separates them, so the result belongs to the bundle.

What this does not establish

  • The rate. It decelerates within 1-45 (halves +0.00427 vs +0.00047, p=0.007 on the disjoint windows, and the plateau appears on each seed separately), then seed 42 resumes: rollouts 35-69 fit +0.00243. Non-monotonic, so do not extrapolate in either direction.
  • That the video improves. See the warning below. This is the one I would push on hardest as a reviewer.
  • Which knob matters. Canvas and reward are perfectly collinear across every run anyone has done. On 8x80GB the canvas cannot be varied alone, since ten replayed steps at 22k rows need >100 GB/rank — any 768 arm also changes the step count. The separable comparison is the reward at this canvas, and it is in the test plan.

⚠️ The reward has no prompt→video term

ImageBindSpec.mode defaults to "audio_video", and T2AVCompositeScorer builds inner specs with inner_spec_cls() while propagating only ("device", "batch_size", "frame_selection") — so mode is unreachable from a recipe and imagebind is exactly cos(audio_emb, video_emb). clap sees text but never a pixel. Nothing in this objective connects the prompt to the video.

That is not hypothetical. minimax_h3_t2va_nft.yaml on the same reward and canvas collapsed at rollout 100 to luma 2.01 / 2.14 of 255, 99.4% of pixels below 16, audio RMS down 9x and 28x — while imagebind rose 0.158 → 0.316. Not a clamp artifact: of nonzero pixels p90=8, p99=16, max=47, so a tensor merely shifted out of the writer's clamp(0, 1) window would have left a broad tail.

Scoring those clips back through ImageBindRewardScorer, both collapsed clips outscore both real ones (0.215 / 0.262 against 0.038 / 0.203), and constant black with silence scores 0.153 while constant gray with silence scores −0.0265. Direction established; magnitude not, at n=2 per condition.

#404 fixes the reachability. This recipe does not set mode — it cannot until #404 lands — so merging both still ships a reward with no prompt→video term. It also cannot simply be flipped afterwards: mode: "all" changes the reward, so the numbers above would stop describing the file. That needs its own run, and there is a note beside the weights saying so.

Test plan

  • Every _target_ resolves against main, including imagebind in the reward registry; YAML parses
  • All 10 SDE steps replay — cuda_alloc_gb 25.24, stable to ±0.02
  • Advantages healthy — advantage_std 0.62–0.90, zero_std_group_ratio 0 throughout
  • Host RAM flat at 154 GB of 2265; zero errors on either run
  • Two-seed pooled verdict, seeds agreeing, OLS and HAC both p<0.0001
  • Config values identical to what was measured, asserted by diffing the parsed YAML
  • Frames through a run — the assumption everything else rests on. Neither component scores artifacts, and the sibling arm shows what that permits
  • 256x384 + videopickscore 1.0 / clap 0.0 — the only clean de-confound available, and a mechanism test: videopickscore is a weak but real prompt→video anchor, so if anchoring is what separates flat from degenerate it should not collapse
  • master_dtype: fp32 restored — plausibly right, but it changes the trainable master dtype and no run here had it
  • eta 0.7 against 0.25 within Flow-GRPO — the only comparison that isolates eta; 0.25 has in-repo precedent in the hunyuan_video15 recipes

Also corrected

The old header claimed H3 "was released for a 768px SHORT EDGE only" and that 768x768 is "the FLOOR". No such floor exists — MiniMaxH3Geometry.resolve enforces multiple-of-32, aspect ratio, an area cap and the frame round-trip, and nothing else. num_frames: 124 is a real floor: legal counts are 17n+5 and the 5 s minimum at 24 fps makes 124 the smallest, so frames cannot be traded for row budget the way resolution can.

The 768x768 Flow-GRPO recipe does not learn. At ~22k packed rows only 3 of the
10 SDE steps fit in replay and the group has to shrink, which is where the
signal goes. On a 256x384 canvas the sequence is 3966 rows, so every step
replays and the group can be 16 wide.

Measured on two independent seeds against a decision rule fixed before either
could be read: pooled +0.00234 reward/step, t=6.67, p<0.0001, with the seeds
agreeing on the slope at p=0.598 and each significant alone.

Two limits are written into the recipe header rather than left implicit. The
rate decelerates inside the window (first half +0.00427, second half +0.00047,
difference p=0.007), so it is not a linear rate to extrapolate. And the reward
is imagebind+clap, neither of which scores artifacts, so a rising number is not
evidence of better-looking video.

Also corrects the sibling recipe's geometry comment, which asserted a 768px
short-edge floor. No such floor exists in the resolver; it was inferred from an
aspect-ratio helper's default, and it is what made the low-resolution setting
look unavailable.
Copilot AI lite review requested due to automatic review settings August 28, 2026 02:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

celve added 7 commits August 28, 2026 10:34
The header claimed the deceleration was what a bounded reward has to do. The
sibling NFT recipe reaches 0.461 on the identical reward and prompt set and is
still climbing, so ~0.29 is nowhere near the achievable ceiling and saturation
explains nothing. The suspect is eta 0.7, whose stochastic rollouts score lower
than the NFT recipe's deterministic ones, but that is untested.

Also narrows the post-45 reading to one seed, where nothing is established.
The header named eta 0.7 as the suspect behind the gap to the NFT arm. The two
recipes differ in objective, and eta is collinear with that choice: DiffusionNFT
needs no log-prob and every NFT recipe here runs eta 0.0, while Flow-GRPO's loss
is built on SDE log-probs and raises without them. The gap therefore supports
only that NFT outperforms Flow-GRPO on this task.

Isolating eta requires 0.7 against 0.3-0.5 within Flow-GRPO, which is noted.
0.7 is not a settled default for video: all three hunyuan_video15 recipes and
wan21_t2v_videoalign run Flow-GRPO at 0.25, so that is the value with in-repo
precedent for an isolating test.

Also states that 124 frames cannot be traded for packed-row budget. Legal counts
are 17n+5 and the 5s minimum duration puts the smallest legal value at 124, so
unlike resolution this axis is closed.
Said neither reward component scores artifacts, which reads as a coverage gap a
better reward would close. imagebind's coverage is in fact reasonable -- 10 of
124 frames as 5 adjacent pairs, three 224px crops each, against
videopickscore's single frame. The gap is that it scores embedding alignment
rather than quality, so a fringed clip that stays recognisable is aligned as
well as a clean one at any coverage.

That makes looking at frames the only thing that settles it.
Four knobs differ from the sibling and no run separates them; canvas and reward
are perfectly collinear across every configuration anyone has tried. Also record
that the plateau is a trough on seed 42 rather than a ceiling.
A 768 arm cannot hold the step count fixed: ten replayed steps need >100 GB/rank
at 22k rows, which is why the sibling trains three of ten. The separable axis is
the reward, at the cheap canvas.
imagebind's mode is unreachable through the composite, so it reduces to
audio-video agreement with no text term, and the sibling NFT arm reached the
degenerate solution that permits.
@celve celve changed the title feat(minimax-h3): Flow-GRPO recipe at 256x384 whose reward curve climbs feat(minimax-h3): Flow-GRPO recipe at 256x384 with all 10 SDE steps and group 16 Aug 28, 2026
celve added 2 commits August 28, 2026 12:15
A PR body does not survive into the working tree, and the header documents the
defect without putting the fix where someone editing this file will see it.
…measured

The values on main -- 768x768, 3 of 10 SDE steps, group 4, lr 3e-4, two updates
per batch -- have never been run by anyone; they arrived in 9e3e4b1 and the
author of the branch it landed on disowns them. Keeping them beside a measured
recipe invites running the unrun one, so this replaces rather than adds, and the
path back to 768 stays in the header.

Comments cut from 191 lines to 44, keeping only those naming a failure the reader
cannot see from the file.
@celve celve changed the title feat(minimax-h3): Flow-GRPO recipe at 256x384 with all 10 SDE steps and group 16 feat(minimax-h3): replace the t2va Flow-GRPO recipe with the measured configuration Aug 28, 2026
celve and others added 2 commits August 28, 2026 12:56
… replacement

Upstream's recipe set master_dtype: fp32 and cast_forward_inputs: false; neither
was present in the runs this file records, so restoring them would describe a
configuration nobody measured. fsdp_wrap only casts params when master_dtype is
set, so its absence leaves the trainable LoRA adapters at bf16.

Co-authored-by: Cursor <cursoragent@cursor.com>
191 comment lines against 143 of config was more comment than config. What is
left names a failure that is invisible from the file: the unsharded fp32 modules,
the token_refiner LoRA collision, the small grad_norm, the omitted sde_indices,
the frame-count floor, the host-RAM ceiling and the reward's missing text term.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

need review Ready and waiting for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants