No jobs
Advanced 11 min 4 of 4

Why 25 steps and not 1000

Training defines a loss over a thousand timesteps. Inference visits twenty-five of them, and which twenty-five is a config key with consequences.

What this repairs

Confusing the training schedule with the inference schedule, and assuming more steps is strictly better.

The thousand timesteps are a property of the loss, not of inference. Nothing in the derivation of the objective says a sampler has to visit all of them, and the shipped model visits twenty-five.

What licenses that is a change of sampler. DDPM's ancestral step draws fresh noise at every timestep, so the chain is a genuine stochastic process and skipping links in it changes the distribution being sampled. DDIM removes the noise draw, and what is left is a deterministic map from xtx_t to xtx_{t'} that happens to agree with the DDPM marginals — a map you can compose in any strides you like.

The DDIM step, and why a stride is exact

  1. x^0=cxtsv^,ε^=sxt+cv^\hat{x}_0 = c\,x_t - s\,\hat{v}, \qquad \hat{\varepsilon} = s\,x_t + c\,\hat{v}

    Start by converting whatever the network emitted into both endpoints, using the rotation from the previous concept. Every sampler in this family begins here, whatever its prediction type.

  2. σt=η1αˉt1αˉt1αˉtαˉt\sigma_t = \eta\,\sqrt{\frac{1-\bar{\alpha}_{t'}}{1-\bar{\alpha}_t}}\,\sqrt{1 - \frac{\bar{\alpha}_t}{\bar{\alpha}_{t'}}}

    The noise budget for this jump, as a fraction η of the DDPM posterior standard deviation. Setting η = 1 reproduces the ancestral sampler exactly; η = 0 is DDIM proper.

  3. xt=αˉtx^0  +  1αˉtσt2ε^  +  σtzx_{t'} = \sqrt{\bar{\alpha}_{t'}}\,\hat{x}_0 \;+\; \sqrt{1 - \bar{\alpha}_{t'} - \sigma_t^{2}}\,\hat{\varepsilon} \;+\; \sigma_t z

    The step. Read the three terms as a budget: the next timestep needs total variance 1 − ᾱ_{t’}, σ² of it is spent on fresh randomness, and whatever is left is filled by pointing along the estimated noise direction rather than by drawing.

  4. η=0, ε^=ε    xt=αˉtx0+1αˉtε\eta = 0,\ \hat{\varepsilon} = \varepsilon \;\Longrightarrow\; x_{t'} = \sqrt{\bar{\alpha}_{t'}}\,x_0 + \sqrt{1-\bar{\alpha}_{t'}}\,\varepsilon

    And that is the closed form of the forward process at t’, exactly. With a perfect network the step lands on the right point for a stride of any length — nothing about skipping is approximate, which is what makes 25 steps a defensible number rather than a shortcut.

The rescale, and the spacing that has to go with it

  1. αˉT1=0.0683SNR(T1)=0.004680\sqrt{\bar{\alpha}_{T-1}} = 0.0683 \quad\Longrightarrow\quad \mathrm{SNR}(T-1) = 0.00468 \neq 0

    The defect, as a number. The terminal signal-to-noise ratio is small but not zero, and inference assumes it is zero.

  2. αˉt=(αˉtαˉT1)αˉ0αˉ0αˉT1\sqrt{\bar{\alpha}'_t} = \left(\sqrt{\bar{\alpha}_t} - \sqrt{\bar{\alpha}_{T-1}}\right)\cdot\frac{\sqrt{\bar{\alpha}_0}}{\sqrt{\bar{\alpha}_0} - \sqrt{\bar{\alpha}_{T-1}}}

    The whole fix: a two-point affine map on √ᾱ. Shift the tail to zero, then stretch what is left so that √ᾱ₀ comes back to where it was. Nothing is retrained and nothing is approximated — it is a rescaling of a lookup table.

  3. αˉT1=0xT1=εexactly\bar{\alpha}'_{T-1} = 0 \quad\Longrightarrow\quad x_{T-1} = \varepsilon \quad \text{exactly}

    Now the last training input really is pure noise, so training and inference agree about what the first sampler step is looking at. This is also the timestep at which ε-prediction went degenerate in the previous concept — the rescale is why that topic mattered.

  4. αˉ961=0.01830\sqrt{\bar{\alpha}'_{961}} = 0.0183 \neq 0

    And this is why the spacing is not a free choice. The diffusers default, "leading", starts a 25-step sampler at t = 961 for this model — 960 from the stride, plus the steps_offset: 1 the shipped scheduler_config.json carries — where the rescaled schedule still expects 1.8 % signal. A sampler fed pure noise there reintroduces exactly the mismatch the rescale was added to remove. Without the offset the timestep is 960 and √ᾱ′ is 0.0188; the argument is the same number to two figures either way, but 960 is not what the library prints for this configuration.

The shipped numbers are 25 steps for the dehazing branch and 20 for depth, from the training config's validation block.

It is worth being honest about what those numbers are. Nothing in the configuration says why, and neither is derived from anything on this page: they are the result of somebody sweeping the step count and finding the knee. The useful thing is knowing what to measure to defend or move them — run the same input at 5, 10, 25 and 50 steps and look at where the outputs stop differing from each other, not at where a metric stops improving, because a per-pixel metric against a single reference will keep improving past the point where the samples have converged.

That the depth branch runs fewer steps than the RGB branch is the kind of asymmetry worth noticing and not worth explaining away. The plausible reading is that a depth field is a smoother target with less trajectory curvature, so it tolerates a coarser stride — but that is a hypothesis about the model, and the config records a decision rather than a reason.

Check yourself

Someone reports that raising a sampler from 25 to 100 steps made their outputs worse. Is that possible, or did they make a mistake?

Show answer

Entirely possible, and there are three separate mechanisms to check before calling it a mistake.

The trajectory changed. More steps is a different discretisation of the same ODE, so with η=0\eta = 0 you land somewhere else — near the old answer, not on it. "Worse" may mean "different, and they preferred the old one".

More noise went in. If η>0\eta > 0 then step count and injected randomness are the same knob, and 100 steps means 100 draws instead of 25.

The spacing moved. Most libraries recompute the subsequence from the step count, so a schedule that started at t=961t = 961 with 25 leading steps starts at t=991t = 991 with 100 — both including this model's steps_offset: 1 — which, on a zero-terminal-SNR schedule, changes how much mismatch the first step is carrying. The step count and the spacing are not independent, even though they are two arguments.

Euler View - ML Experiment Monitor