Skip to content

Refine semantic inner-loop early stop#74

Merged
scraed merged 34 commits into
scraed:masterfrom
godnight10061:fix/semantic-stop-tail
Jan 30, 2026
Merged

Refine semantic inner-loop early stop#74
scraed merged 34 commits into
scraed:masterfrom
godnight10061:fix/semantic-stop-tail

Conversation

@godnight10061

@godnight10061 godnight10061 commented Jan 17, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR refines LanPaint semantic inner-loop early stop to reduce tuning surface area, addressing feedback in #74 (comment: #74 (comment)).

What changed

  • Semantic early stop exposes only threshold + patience.
  • Effective threshold is derived from ABT (4abt(1-abt)).
  • Boundary guard is parameter-free (4-neighbor adjacency) plus a drift guard.
  • Backward compatibility: legacy min_steps is mapped into the patience floor (patience = max(patience, min_steps - 1)); older Langevin state tuples still work.

Testing

Local:

  • ruff check .
  • pytest tests/

Fork CI:

@godnight10061 godnight10061 marked this pull request as draft January 17, 2026 03:49
@scraed

scraed commented Jan 17, 2026

Copy link
Copy Markdown
Owner

Hi, thanks a lot for the contribution!

I think a time adaptive (based on abt) early stop is a very promising direction!

Note that there is already a simple early-stop mechanism that simply disables iteration for the last several steps. It would be better to merge them together and keep the number of hyperparameters to a minimum.

I have several questions before we merge the early stop mechanism.

  1. Could you plot & sanity check the distance change during iterations? Does x₀ show converging behavior in terms of distance? If it is converging, that is good! If not converging, maybe we could switch to adjust the number of iterations directly.

  2. Where does the new early stopping happen most often? At the beginning or at the end of sampling?

  3. How it performs compared to the old cut-off mechanism (LanPaint EarlyStop in LanPaint KSampler Advanced) under same runtime budget?

Additional note:

Personally, I prefer stopping iteration entirely instead of freezing x₀ and continuing to iterate. The intuition is the following:

  • Freezing and keep iterating is equivalent to “using a large step size”. Suppose you freeze x₀ and perform three extra iterations with step size Δt. This basically amounts to taking one step with step size 3Δt. With a large step size, the iteration may become unstable.
    Breaking iteration entirely might be a better choice. You could also try and compare them.

@godnight10061 godnight10061 changed the title Add semantic early-stop freeze to skip redundant score calls Refine semantic inner-loop early stop Jan 18, 2026
@godnight10061 godnight10061 force-pushed the fix/semantic-stop-tail branch from 98d5128 to daeaabe Compare January 19, 2026 03:30
@godnight10061

Copy link
Copy Markdown
Contributor Author

I added optional per-inner-step tracing (--trace-inner) and on an ImageNet-256 subset=50 run the stop metric usually drops a lot over inner steps (median dist(last)/dist(first) ~0.03-0.16 for outer steps 11-18), but it is not strictly monotonic every step.
It triggers mostly in mid-late outer steps (e.g. outer 15-17 in that run; abt ~0.63-0.89). It is disabled for very noisy steps and the extreme tail.
Under a similar call budget, --outer-cutoff-steps 1 was worse in my run (LPIPS ~0.2220) vs semantic-stop (~0.2163) and baseline (~0.2168). I’d appreciate an independent LanPaintBench run to confirm across setups.

@scraed

scraed commented Jan 22, 2026

Copy link
Copy Markdown
Owner

I have cleaned the code by adding an early stopper file to the code.

@godnight10061 godnight10061 force-pushed the fix/semantic-stop-tail branch from add138f to 34ded92 Compare January 24, 2026 02:20
@godnight10061

godnight10061 commented Jan 26, 2026

Copy link
Copy Markdown
Contributor Author

@scraed

I agreed with the concern about tuning surface, so I removed the extra internal knobs (ABT min/max thresholds, patience boosts, threshold floors, and the dilation ring kernel/padding) and kept the user-facing controls to just threshold + patience.

For backward compatibility, I also mapped the legacy min_steps into the patience floor (patience = max(patience, min_steps - 1)), so it is not an independent third knob, but it still prevents stopping too early when older configs pass it.

The boundary check has no parameter now(4-neighbor adjacency) and the ABT behavior is derived from 4*abt*(1-abt).

@scraed

scraed commented Jan 28, 2026

Copy link
Copy Markdown
Owner

Hi, Thanks a lot for the refactor! I'm checking the code and doing some ablation on LanPaintBench

@scraed scraed marked this pull request as ready for review January 30, 2026 03:43
@scraed scraed merged commit 95b65ed into scraed:master Jan 30, 2026
2 checks passed
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.

2 participants