Skip to content

hy_worldplay: Clean Forcing drift corrector (training recipe + content-keyed deploy)#396

Open
wenqingw-nv wants to merge 1 commit into
NVIDIA:mainfrom
wenqingw-nv:clean-forcing-hy-worldplay
Open

hy_worldplay: Clean Forcing drift corrector (training recipe + content-keyed deploy)#396
wenqingw-nv wants to merge 1 commit into
NVIDIA:mainfrom
wenqingw-nv:clean-forcing-hy-worldplay

Conversation

@wenqingw-nv

@wenqingw-nv wenqingw-nv commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Long autoregressive HY-WorldPlay rollouts drift: each chunk conditions on self-generated history and small errors compound into saturation/contrast runaway. This PR adds a trained drift corrector (Clean Forcing), the zero-real-data recipe that produced it, and a content-keyed deploy hook. drift_corrector=None (default) is byte-identical to current behavior.

Contents

  • hy_worldplay/_drift_corrector.py + two runner-config fields (drift_corrector, drift_corrector_gain) — content-keyed per job: static (locked-off camera) trajectories run untouched base weights (static scenes measure negative drift on this host — correction there is pure artifact cost); commanded-motion trajectories apply the LoRA at alpha*(t) × gain per denoise step.
  • integrations/hy_worldplay/drift_correction/ — full recipe, zero real videos: strafe-loop pair construction, step-0 alpha*(t) systematicity gate (go/no-go + the deploy gain profile), v1 counterfactual-teacher training, v2 DAgger + drift-contraction, gain-sweep eval, drift/dynamics/progression/seam scoring, static-scene demo suite. README documents the ~2.5 GPU-day reproduction.
  • tests/test_drift_corrector.py (ci_cpu, 6 tests) — LoRA wrapper is a strict identity until a checkpoint is loaded and gated on; content-keyed selection triggers on action labels.

Docs

Measured (704×1280, 24-chunk ≈19 s rollouts, held-out trajectories, GB300)

config MUSIQ ↑ Δ-drift ↓ dynamic degree
base 68.0 +1.70 19.3
shipped alpha*(t) × 0.5 70.4 −48% 20.3 (≈ base)
flat gain 1.0 72.1 −133% (negative drift) 11.7 (−40%)

Shipped config cuts the drift metric ~50% with no dynamics loss and no added chunk-boundary pulse; commanded trajectories keep advancing (owner-verified side-by-sides). The method/reproduction details: drift_correction/README.md (quick start)

Deploy notes

  • Per-step alpha*(t) gating cannot merge into one weight set → motion jobs run the LoRA unfused (~0.3% extra matmul); static jobs run the base at zero overhead.
  • v2 LoRA checkpoint (~59 MB) hosting: currently a fork release asset (https://github.com/wenqingw-nv/flashdreams-wq/releases/tag/clean-forcing-hy-v2); release asset here vs HF — maintainer preference welcome.
  • Correctors encode a checkpoint-specific error map: re-instantiation on a new base checkpoint is ~1 GPU-day with the included recipe (prompts only).

Video Comparison

Left: FlashDreams-HY (base), Right: w/ Clean Forcing LoRA drift corrector (better details/color preservation across time):

sbs_base_LEFT_vs_corrgate050_RIGHT_p0_i0_s5042.mp4
fsbs_base_LEFT_vs_corrgate050_RIGHT_p0_i0_s5042.mp4
sbs_base_LEFT_vs_corrgate050_RIGHT_p0_i0_s5042mix.mp4

VBench Ablation:

config subj cons bg cons aesthetic imaging motion smooth dyn degree mean
base 86.4 88.6 65.3 77.6 91.4 100.0 84.9
corr050 92.0 92.9 67.1 77.0 91.8 100.0 86.8
corrgate 94.6 94.2 68.3 77.2 93.9 100.0 88.1
corrgate050 (shipped) 88.7 90.2 64.5 76.5 93.0 100.0 85.5

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a trained Clean Forcing drift corrector (LoRA) for HY-WorldPlay's long autoregressive rollouts, plus the full zero-real-data training recipe. The corrector is content-keyed at deploy time: static-camera jobs bypass it entirely, commanded-motion jobs apply the LoRA at alpha*(t) × gain per denoise step; drift_corrector=None (default) is byte-identical to current behavior.

  • Adds _drift_corrector.py + two HyWorldPlayWanI2VRunnerConfig fields (drift_corrector, drift_corrector_gain) wired into runner.run() with a content-keyed dispatch via is_static_trajectory.
  • Adds the full drift_correction/ training recipe: strafe-loop pair construction, step-0 alpha*(t) systematicity gate, v1 counterfactual-teacher training, v2 DAgger + drift-contraction, and gain-sweep eval scripts.
  • Adds 6 ci_cpu unit tests covering the LoRA identity invariant, action-label selection, and static-pose bypass.

Confidence Score: 5/5

Safe to merge. The default path (drift_corrector=None) is a no-op that leaves existing behavior byte-identical, and the corrector path is gated behind an explicit config field.

The core deploy hook, LoRA wrapper, and content-keyed dispatch are all correctly implemented. Training recipe scripts are standalone tools that do not affect production inference. The only issues found are minor diagnostic quality nits that do not affect correctness of the shipped corrector or the default no-corrector path.

No files require special attention for merge safety. The previously flagged _drift_corrector.py concerns are the items most worth resolving before any follow-on work that reuses the runner across multiple jobs.

Important Files Changed

Filename Overview
integrations/hy_worldplay/hy_worldplay/_drift_corrector.py Deploy hook: content-keyed LoRA inject + per-step alpha*(t) gating. Previously flagged: weights_only=False, duplicate GATE_ALPHA, gated_pf stacking on repeated run() calls.
integrations/hy_worldplay/hy_worldplay/runner.py Adds drift_corrector / drift_corrector_gain config fields and calls maybe_apply_drift_corrector inside run(). Previously flagged: gated_pf stacks on each run() call if runner is reused.
integrations/hy_worldplay/drift_correction/_lora.py Training-side LoRALinear, apply/save/load helpers. Previously flagged: weights_only=False in load_lora.
integrations/hy_worldplay/drift_correction/_pairs.py Clip loading and clean_counterfactual history substitution. TOKENS_PER_FRAME/PATCH_DIM hardcoded for 704x1280 without runtime guard.
integrations/hy_worldplay/drift_correction/_rollout.py Runner construction, rollout capture, counterfactual probe helpers. Previously flagged: weights_only=False in load_rollout.
integrations/hy_worldplay/drift_correction/train_v1.py v1 counterfactual-teacher trainer with per-block gradient checkpointing. Logic is sound.
integrations/hy_worldplay/drift_correction/train_v2.py v2 DAgger + drift-contraction trainer. Two-phase backward correctly documented and implemented.
integrations/hy_worldplay/drift_correction/_train_attn.py Functional dual-branch attention patch for training. Correctly asserts single-forward-per-chunk invariant.
integrations/hy_worldplay/drift_correction/build_pairs.py Strafe-loop pair collection. Runner reuse across clips correctly updates config fields.
integrations/hy_worldplay/tests/test_drift_corrector.py 6 CI-CPU unit tests covering LoRA identity invariants and content-keyed selection.

Reviews (4): Last reviewed commit: "hy_worldplay: add Clean Forcing drift co..." | Re-trigger Greptile

network = network._orig_mod
params = _apply_lora(network)

sd = torch.load(checkpoint, map_location="cpu", weights_only=False)["lora"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security weights_only=False allows arbitrary code execution

torch.load(..., weights_only=False) uses Python's pickle deserializer, which can execute arbitrary code embedded in the file. checkpoint is a user-controlled path via HyWorldPlayWanI2VRunnerConfig.drift_corrector, so a replaced or tampered .pt file would run attacker code on load. The save_lora format stores only {"lora": {int: Tensor}} — all primitive Python types and tensors — so weights_only=True loads it without issue.

Comment on lines +29 to +33
GATE_ALPHA = {1000.0: 0.81, 960.0: 0.53, 888.8889: 0.53, 727.2728: 0.58}
"""Unbiased alpha*(t) from the step-0 systematicity gate (drift_correction's
``outputs/gate/gate_faithful.json``): the systematic fraction of the
drift-induced error at each of the distilled solver's timesteps. The
shipped config deploys the LoRA at ``alpha*(t) * gain`` per denoise step."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Duplicate GATE_ALPHA dict — deploy and training scripts diverge silently

GATE_ALPHA is defined with identical values in both this file and drift_correction/_rollout.py. If a future gate rerun changes the alpha*(t) profile, only one copy is likely to be updated, causing the deployed per-step gain profile to silently mismatch the training configuration.

Suggested change
GATE_ALPHA = {1000.0: 0.81, 960.0: 0.53, 888.8889: 0.53, 727.2728: 0.58}
"""Unbiased alpha*(t) from the step-0 systematicity gate (drift_correction's
``outputs/gate/gate_faithful.json``): the systematic fraction of the
drift-induced error at each of the distilled solver's timesteps. The
shipped config deploys the LoRA at ``alpha*(t) * gain`` per denoise step."""
GATE_ALPHA = {1000.0: 0.81, 960.0: 0.53, 888.8889: 0.53, 727.2728: 0.58}
"""Unbiased alpha*(t) from the step-0 systematicity gate (drift_correction's
``outputs/gate/gate_faithful.json``): the systematic fraction of the
drift-induced error at each of the distilled solver's timesteps. The
shipped config deploys the LoRA at ``alpha*(t) * gain`` per denoise step.
NOTE: This dict is also defined in ``drift_correction/_rollout.py``. Both copies
must be kept in sync; prefer sourcing from a single shared location."""

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@wenqingw-nv
wenqingw-nv force-pushed the clean-forcing-hy-worldplay branch from 58702aa to d91feb2 Compare July 22, 2026 06:22
@wenqingw-nv

Copy link
Copy Markdown
Collaborator Author

Checkpoint availability: the trained v2 corrector LoRA (59 MB) is published as a release asset on the contributing fork for review/testing: https://github.com/wenqingw-nv/flashdreams-wq/releases/tag/clean-forcing-hy-v2 — happy to rehome it (release asset here / HF / internal artifact store) per maintainer preference. Note the deploy dial is inference-time: this single checkpoint serves all configurations (flat gain, alpha*(t) gate, gate x gain); the shipped setting is alpha*(t) x 0.5 with content-keyed selection.

@wenqingw-nv

Copy link
Copy Markdown
Collaborator Author

/ok to test d91feb2

@wenqingw-nv
wenqingw-nv force-pushed the clean-forcing-hy-worldplay branch from d91feb2 to ada922d Compare July 22, 2026 07:36
Comment on lines +356 to +362
if cfg.drift_corrector is not None:
from hy_worldplay._drift_corrector import maybe_apply_drift_corrector

mode = maybe_apply_drift_corrector(
self, cfg.drift_corrector, cfg.drift_corrector_gain
)
logger.info(f"[{cfg.runner_name}] drift corrector: {mode}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Drift corrector re-applied on every run() call, stale gain overrides the new one

maybe_apply_drift_corrector installs a gated_pf closure that captures gain from the call site. Each subsequent run() wraps the previous gated_pf in a new one: the outermost closure (latest call's gain) sets the scale, then returns via orig_pf, which is the previous gated_pf — which overrides the scale again with an older captured gain before calling the real predict_flow. If drift_corrector_gain is mutated between runs (e.g. runner.config.drift_corrector_gain = 0.3 after a first run at 0.7), the network executes every subsequent step at the first-call gain (0.7), not the current one, with no error raised.

Moving the application into __init__ (or adding a _drift_corrector_applied guard) would prevent the stacking entirely.

@wenqingw-nv
wenqingw-nv enabled auto-merge July 22, 2026 08:00
@wenqingw-nv

Copy link
Copy Markdown
Collaborator Author

Domain scope note: the shipped corrector was trained on urban/outdoor content without people; person-heavy or stylized domains may need a pairs refresh (recipe included, ~1 GPU-day). We validated the refresh path: a 200-step continue-train with ~10% person/scene-diverse pairs improves the walking-person stress scene (drift −47%, best seam profile) while keeping — in fact improving — the original commanded-motion profile (bridge drift cut −58%). Checkpoint to follow with the hosting decision.

@wenqingw-nv

Copy link
Copy Markdown
Collaborator Author

Checkpoint update: the shipped LoRA is now v2c2 — a content-diversified refresh (200-step continue-train, 10% person/scene-diverse pairs) that improves BOTH axes: bridge drift cut −58% (was −48%) and the walking-person stress scene fixed (drift −47%, owner-verified side-by-sides). Release asset: https://github.com/wenqingw-nv/flashdreams-wq/releases/tag/clean-forcing-hy-v2c2 (md5 1de6023c...; loads via the identical deploy path, no code change). The earlier v2 asset remains available for reproducing the PR's tables.

…ntent-keyed deploy)

Adds a trained drift corrector for the HY-WorldPlay WAN-5B integration:
a frozen-base LoRA (r16 q/k/v/o, ~0.3% params) trained from the model's
own strafe-loop rollouts with a counterfactual clean-history teacher --
zero real videos end-to-end.

Deploy: HyWorldPlayWanI2VRunnerConfig.drift_corrector (checkpoint path)
+ drift_corrector_gain. Selection is content-keyed per job: static
(locked-off camera) poses run untouched base weights; commanded-motion
poses apply the LoRA at alpha*(t) x gain per denoise step, where
alpha*(t) is the measured systematic fraction of the drift-induced
error at each solver timestep.

Measured on 24-chunk (~19 s) rollouts: -48% delta-drift vs base with
dynamics ~= base and no added seam pulse; the base's late-horizon
saturation runaway is eliminated. Training/eval scripts and the step-0
diagnostic live under integrations/hy_worldplay/drift_correction/ with
a README covering the method and the full reproduction recipe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Wenqing Wang <wenqingw@nvidia.com>
@wenqingw-nv
wenqingw-nv force-pushed the clean-forcing-hy-worldplay branch from ada922d to 8afdaf1 Compare July 23, 2026 03: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