Skip to content

Phase 4: flow matching, min-SNR, EDM2 defaults, interval CFG, FID - #14

Closed
AshishKumar4 wants to merge 5 commits into
revamp/phase-2-restorationsfrom
revamp/phase-4-flow-matching
Closed

Phase 4: flow matching, min-SNR, EDM2 defaults, interval CFG, FID#14
AshishKumar4 wants to merge 5 commits into
revamp/phase-2-restorationsfrom
revamp/phase-4-flow-matching

Conversation

@AshishKumar4

Copy link
Copy Markdown
Owner

Fifth PR of the revamp series — stacked on #13.

Flow matching

FlowMatchingScheduler (subclassing the existing ContinuousNoiseScheduler) + FlowMatchPredictionTransform slot into the existing scheduler/predictor seams — no new machinery. Linear path x_t = (1−t)x₀ + tε, logit-normal timesteps (SD3's m=0, s=1), SD3 resolution shift t' = st/(1+(s−1)t) with Flux-style compute_resolution_shift, and c_in = 1.

The existing samplers work unmodified — on a linear path the DDIM/Euler update is the FM Euler step, and test_sampler_step_is_the_flow_euler_step proves that algebraically to <1e-5 for both.

A toy 2-D test proves it actually learns, not merely runs: 1500 CPU steps on a two-mode Gaussian mixture, then 2048 samples drawn through the real EulerSampler, asserting mode balance and per-mode statistics (untrained σ≈0.65 with wrong means → trained means within 0.006, σ within 0.008). Runs in ~4s.

min-SNR-γ — integrated, not bolted on

The subtle part: min(SNR,γ) is defined on the x₀ loss, but the loss is computed in each transform's target space. Rather than add a parallel weighting system, get_weights became a template over get_schedule_weights, and each transform gained target_error_scale(snr) = ‖target error‖²/‖x₀ error‖². This reproduces the paper formulas exactly (eps → min/SNR, v → min/(SNR+1)) and falls out correctly for Karras and FM.

I verified all four derivations by hand and numerically: FM's (1+√SNR)² is exactly 1/t²; Karras's 1/σ_d² + SNR is exactly 1/c_out². And a good independent signal that the abstraction is right: KarrasPredictionTransform.target_error_scale turns out to equal EDM's existing weighting, i.e. EDM is x₀-space weight 1.

EDM's audited-correct weighting is bit-identical after the refactor (max relative error 0.0) when min-SNR is off.

Also

  • EDM2 defaults: P_mean/P_std exposed and defaulted to EDM2's −0.4/1.0 (was EDM1's −1.2/1.2 hardcoded).
  • Interval-limited CFG (2404.07724): guidance_start/guidance_stop step-fractions on DiffusionSampler; default is full range, so no behavior change.
  • FID: the vendored InceptionV3 in metrics/inception.py had been sitting completely unused — now wired as an EvaluationMetric (--val_metrics fid).
  • Latent normalization: SD3-style shift+scale on the AutoEncoder seam, with the VAE's scaling_factor folded into it rather than stacked on top. Defaults reproduce prior behavior; verified by the network-marked VAE roundtrip test.

Verification

112 passed, 4 deselected (from 73), independently re-run by me, not just reported. Network-marked tests (FID weights, SD VAE roundtrip) pass too.

Known scope note: no helper to compute a dataset's latent mean/std — the seam takes them as input; say the word if you want the measurement pass.

🤖 Generated with Claude Code

AshishKumar4 and others added 5 commits July 27, 2026 01:45
Three changes to the training objective layer, sharing one contract.

Flow matching slots into the existing scheduler/predictor seams rather than
next to them: FlowMatchingScheduler is the linear path alpha = 1 - t,
sigma = t with SD3's logit-normal timesteps and Flux's resolution shift, and
FlowMatchPredictionTransform predicts the velocity u = eps - x0. Because the
path is linear, the unmodified DDIM and Euler updates already are the flow
Euler step; there is a test proving the identity exactly.

min-SNR-gamma (Hang et al. 2023) is defined on the x_0 loss, so converting it
into the space the trainer actually computes the loss in needs the
parameterization. get_weights becomes a template over get_schedule_weights and
asks the transform for that conversion, which also makes the schedule/transform
pairing explicit instead of implicit. No second weighting system.

EDMNoiseScheduler's P_mean/P_std were hardcoded to EDM1's -1.2/1.2; they are
now constructor arguments defaulting to EDM2's -0.4/1.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guidance costs diversity at high noise and buys nothing at low noise
(Kynkaanniemi et al. 2024). guidance_start/guidance_stop bound the fraction of
the trajectory over which it applies; outside the interval the scale drops to
1, which is exactly the plain conditional prediction. The default interval is
the full range, so existing samplers are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The jax-fid port in metrics/inception.py has been sitting unused. Wire it into
an EvaluationMetric the same way metrics/images.py builds the CLIP ones,
sharing a single cached copy of the weights. Per-batch FID is noisy and only
meaningful as a trend across checkpoints, which the docstring says outright.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Latents are normalized as (z - latent_shift) * latent_scale and inverted on
decode, the SD3 convention, so a caller can hand in a dataset's own mean and
1/std instead of relying on the VAE's single fixed constant. The SD VAE's
scaling_factor now rides on that same seam rather than being applied inside
its jitted frame functions, so there is one normalization path and the
defaults reproduce the previous behavior exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
--noise_schedule gains flow/flow_matching, plus --min_snr_gamma,
--flow_shift, and 'fid' as a validation metric.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AshishKumar4

Copy link
Copy Markdown
Owner Author

Landed on main as part of the full revamp stack (main fast-forwarded to bc9d06b). GitHub reports no new commits between this head and main, so every commit here is merged; closing as superseded by the stack merge rather than leaving it open.

@AshishKumar4
AshishKumar4 deleted the revamp/phase-4-flow-matching branch July 27, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant