Replace absolute reward regression with Bradley-Terry pairwise ranking
(DPO-style) so the value function learns relative preference between
sampled pairs rather than fitting noisy absolute reward floats.
Add a 3-member bootstrap ensemble; gradient() and ascend() scale by
inter-member agreement so guidance backs off in regions the ensemble
hasn't seen data for, instead of confidently extrapolating (the
static-character collapse this was meant to guard against).
Loosen the "Wrong but loved" exclusion in conditioning.py so a strong
loved_modifier signal is no longer filtered out by skip_value_function
- "Wrong but loved" and "Missing but loved" now train the value
function, per the stronger-signal precedence requested.
Legacy single-net checkpoints migrate transparently on load (weights
remap into all ensemble members, rating buffer preserved intact).
New LatentValueFunction (value_function.py) mirrors OnlineValueFunction but is
trained on a pooled snapshot of the sampler's own predicted output (x0_hat)
instead of the input conditioning, borrowing NoiseTilt's core idea (reward
gradient from the model's own prediction) without its SDE noise-term trick,
which has no analogue in this deterministic-ODE sampler.
New output_guidance / output_guidance_strength toggle on the Chain Sampler
(off by default): per-step correction installed as the outermost
model_function_wrapper (on top of embed_guidance/score_slider), same
near-zero-forward-pass shape as embed_guidance. End of run writes a small
pooled snapshot per refinement key; the next rating cycle trains the value
function on it via a new call site in conditioning.py.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Route the conditioning variant from per-config outcome history, via the one
charter-safe surface: the INTERNAL value-function ascend+search (not user knobs,
so no WYSIWYG / hidden-settings violation). Sampler-owned visual ops stay on the
sampler (boundary law kept).
Directly serves the original complaint (Perfect not locking, Awful reproducing):
- _v2_path_conditioning_plan: matching-arm Beta posteriors -> liked-rate mean ->
"lock" (>=0.6: hold the liked conditioning, skip the stochastic search),
"explore" (<=0.4: widen the search to flee a disliked region), else "normal"
- value_function.search gains eps_scale to widen/tighten the search radius
- threaded conditioning_plan through ascend -> apply_scene_refinement_keys ->
finalize; computed once per run from path_outcomes + prompt concepts; logged
Only active once the per-key VF is ready, since it rides the existing ascend path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
search() generates 16 random unit-direction perturbations (2.5% of
conditioning norm each), scores each with the VF, returns the best.
Runs after ascend() in _v2_ascend_conditioning — ascent exploits the
known gradient direction, search explores the neighbourhood around it
for any better nearby region. Total cost: 16 VF forward passes ≈ free.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LTX packs all spatial/temporal dims into a single token [1, 1, C*T*H*W]
during sampling. Per-step latent steering requires hooking into the model
internals, not the k-diffusion callback. Removed: compress_latent(),
_load_latent_value_function(), latent VF training, motion floor callback,
latent_value_guidance toggle in Studio.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unconstrained ascent was stripping action content from conditioning to reach
a "safe" static-character attractor. Added 8% displacement cap relative to
original conditioning norm — quality direction is still steered but the
prompt content can't be erased. Also raised MIN_SAMPLES from 5 to 10 to
reduce overfitting risk before the value function activates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Trains a second OnlineValueFunction on rated video latents (compressed via
channel mean-pool). At each denoising step, computes ∂reward/∂latent through
the lightweight MLP and applies it as an in-place drift to the noisy latent —
gradients never touch the 22B transformer. Controlled by embed_guidance_strength
alongside the existing conditioning guidance. Studio toggle: "Latent guidance".
Cleared on session reset alongside the conditioning VF.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before each denoising run, when the value function is ready, gradient-ascend
the text conditioning until predicted reward plateaus (self-terminating: step
size = 0.5% of conditioning norm, stops when delta < 1e-3, caps at 50 steps).
No user-facing params — fully automated. Per-step wrapper still runs on top.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.clone() on an inference tensor inherits the inference flag even inside
inference_mode(False). torch.empty() inside that context creates a true
normal tensor; copy_() fills it from the inference tensor without flag
inheritance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MLP lives on CPU, conditioning on GPU — device mismatch silently fell back
to fixed direction. Now moves conditioning to MLP device for the forward/
backward, then returns gradient to original device.
Also surface value function fallback errors instead of swallowing them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Buffer tensors were created inside inference_mode — detach().cpu() doesn't
escape the inference flag. Wrapping the whole method ensures the clone and
all subsequent tensor ops produce normal autograd-tracked tensors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
torch.enable_grad() cannot override torch.inference_mode() in older PyTorch.
Add inference_mode(False) as outer wrapper in both train_on and gradient.
Also .clone() tensors from buffer/input before grad tracking to avoid
inference_mode tensor view restrictions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ComfyUI execution context may have torch.no_grad() active, which kills
gradient tracking needed for training and gradient computation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
value_function.py: OnlineValueFunction MLP that trains incrementally on
(conditioning, reward) pairs. Uses replay buffer of 100 samples, 20
gradient steps per update. save/load to .pt file via refinement_state_path.
Studio popup: new "Value guidance" toggle. When on, trains the value function
after every rated generation (good and bad). Falls back to no-op if conditioning
deserialization fails.
chain sampler: _load_value_function reads the trained MLP if ready (5+
samples). _build_embed_guidance_wrapper uses value function gradient
(∂reward/∂conditioning) when available, fixed liked direction as fallback.
Log reports which mode is active and sample count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>