fix: h3_av_decouple was a 0-1 dial over a guessed conversion constant

No way to read H3's actual trained attention-logit scale from this machine
(checkpoint isn't here) -- so normalizing the knob to 0-1 against a hidden
made-up multiplier (12.0) was a second guess wearing a calibrated costume,
and exactly the kind of thing that produces another flat live-tested curve.
The knob is now the raw softmax logit penalty directly (0-50, step 0.5), so
a live sweep searches the actual unit instead of my conversion factor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-09-05 02:29:26 +03:00
co-authored by Claude Sonnet 5
parent 1e4553e658
commit a60b0e981f
3 changed files with 23 additions and 20 deletions
+3 -3
View File
@@ -334,9 +334,9 @@
{ name: "h3_repr_steering_block", label: "REINS steer at block(s)", kind: "text", default: "25", dependsOn: "h3_repr_steering",
hint: "TEST-ONLY: which block(s) actually get steered, instead of always block 25 — every block 0-49 is captured read-only regardless. A single block, a range, or a comma list: 25 | 31-40 | 4,5,6 — same syntax as Repeat block(s). Each named block steers with its OWN learned direction from its OWN liked/disliked history at that block; a block without enough data just captures, same as any unnamed candidate block.",
detail: "Blocks 40-49 carry the most residual movement (48+49 alone about a third of it), so a steering push has the most to act on there. Blocks 0-1 are structurally distinct rather than just 'quiet early blocks' — 0 is the raw input projection, 1 does the first cross-patch mixing — expect steering there to behave differently in kind, not just 'weaker', than the middle of the stack." },
{ name: "h3_av_decouple", label: "Video/audio attention decoupling (H3, experimental)", kind: "float", default: 0.0, min: 0.0, max: 1.0, step: 0.01,
hint: "EXPERIMENTAL, unvalidated, H3 only. Weakens how much video and audio attend to each other, for the failure pattern where one comes out clean and the other doesn't. 0 = off. Not rating-driven — a manual dial, same as Repeat block(s).",
detail: "H3 has no separate cross-attention to mask — video, text and audio share one joint self-attention. Instead of building a mask over that whole sequence (unaffordable at H3's token count), this runs the same attention as separate video-query and audio-query passes, each carrying a penalty on the other modality's attention weight. Same total cost as the ordinary single pass. 1.0 cuts cross-modal attention entirely, which likely also kills legitimate audio/video sync — search down from a small value, not up to it. (v2: the first version tried this by shrinking the other modality's keys instead of penalizing attention directly, and live testing showed almost no effect across the whole 0-1 range — fixed to actually penalize.)" },
{ name: "h3_av_decouple", label: "Video/audio attention decoupling (H3, experimental)", kind: "float", default: 0.0, min: 0.0, max: 50.0, step: 0.5,
hint: "EXPERIMENTAL, unvalidated, H3 only. Weakens how much video and audio attend to each other, for the failure pattern where one comes out clean and the other doesn't. 0 = off. Not rating-driven — a manual dial, same as Repeat block(s). This is a raw attention-logit penalty, not a 0-1 fraction — try 1-5 first, then 10-20.",
detail: "H3 has no separate cross-attention to mask — video, text and audio share one joint self-attention. Instead of building a mask over that whole sequence (unaffordable at H3's token count), this runs the same attention as separate video-query and audio-query passes, each carrying a penalty on the other modality's attention weight. Same total cost as the ordinary single pass. There's no way to read H3's actual trained attention scale from here (the checkpoint isn't on this machine), so this number can't be pre-calibrated — v1 tried normalizing it to 0-1 against a guessed constant and live testing came back flat across the whole range, which is why it's now the raw penalty for you to search directly. Large enough to fully exclude the other modality likely also kills legitimate audio/video sync — search up from a small value, not down from a large one." },
{ name: "identity_transfer_enabled", label: "Best-FaceID compatibility", kind: "bool", default: false,
hint: "Feeds the identity pin image the way Best-FaceID identity LoRAs expect it. Needs an Identity pin set.",
detail: "Replaces Continuity's identity-pin guide with separate, non-rendered reference tokens plus an optional ArcFace projector below. Load the LoRA itself in Models. No effect without an identity pin." },
+19 -16
View File
@@ -2807,8 +2807,8 @@ class FunPackLTXAVSceneChainSampler:
"tooltip": "How much of the learned direction to add, as a fraction of that block's own hidden-state norm. Unlike the attention-bias channel, there is no architectural ceiling here -- push too far and coherence breaks with no warning. Start low; the way to tell it is doing anything at all is to push this deliberately high on a same-seed test and see if the video visibly moves, then back off.",
}),
"h3_av_decouple": ("FLOAT", {
"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01,
"tooltip": "EXPERIMENTAL, unvalidated (H3 only). Damps how much video and audio attend to EACH OTHER inside H3's joint self-attention, without touching any hidden state -- for the failure pattern where one comes out clean and the other doesn't. 0 = off (installs nothing, zero cost). Higher = weaker cross-modal influence via a negative bias on the other modality's attention logits (v2: the first version scaled key magnitude instead and measured flat 0-1 live -- fixed). 1.0 fully cuts it, which likely also kills legitimate AV sync, so this is a knob to search down from, not up to.",
"default": 0.0, "min": 0.0, "max": 50.0, "step": 0.5,
"tooltip": "EXPERIMENTAL, unvalidated (H3 only). Damps how much video and audio attend to EACH OTHER inside H3's joint self-attention, without touching any hidden state -- for the failure pattern where one comes out clean and the other doesn't. 0 = off (installs nothing, zero cost). This is a RAW softmax logit penalty, not a 0-1 fraction -- there's no way to read H3's actual trained attention-logit scale from here (the checkpoint isn't on this machine), so this can't be pre-calibrated. Try single digits (1-5) first, then 10-20; v1 normalized this to 0-1 against a guessed constant and measured completely flat live, which is why it's raw now. Large enough to fully exclude the other modality likely also kills legitimate AV sync, so search up from a small value.",
}),
"alg_guide_blur_strength": ("FLOAT", {
"default": 2.0, "min": 1.0, "max": 4.0, "step": 0.1,
@@ -6846,8 +6846,15 @@ class FunPackLTXAVSceneChainSampler:
attention causes the clean-video/corrupted-audio split) is even correct -- this only
fixes the mechanism not doing what the strength dial implied.
`strength` in [0, 1]; 0 or blank installs nothing (exact pass-through, not just a
no-op override) so an unset knob costs literally zero.
`strength` is the RAW softmax logit penalty subtracted from the targeted modality's
attention score, not a normalized 0-1 fraction. attention_head_dim=128 and no `scale`
kwarg is ever passed into optimized_attention here, so SDPA's default 1/sqrt(128)
applies -- with an untrained (gain=1) RMSNorm that puts a "typical" logit std around
1, so single digits should already be a meaningful penalty and double digits close to
exclusion. But RMSNorm's per-channel gain is LEARNED, and the checkpoint that would
say what it actually trained to isn't on this machine -- so that estimate is an
analytic starting point, not a calibrated constant. Search it empirically instead of
trusting the estimate: 0 installs nothing (exact pass-through, zero cost).
"""
try:
_strength = float(strength or 0.0)
@@ -6855,7 +6862,6 @@ class FunPackLTXAVSceneChainSampler:
_strength = 0.0
if _strength <= 0.0:
return model
_strength = min(_strength, 1.0)
try:
try:
from . import h3_repr_steering as _rs
@@ -6884,14 +6890,6 @@ class FunPackLTXAVSceneChainSampler:
to["patches_replace"] = patches_replace
_mask_cache = {} # seq_len -> (video_mask, audio_mask); one live resolution per gen
# Logit bias scale, not a key-magnitude scale (see the method docstring for why
# the first cut of this used K-scaling and measured flat across the whole 0-1
# range live). After H3's own per-head RMSNorm on Q/K, attention logits already
# sit close to 0 -- an additive penalty pushes a targeted key toward genuinely
# excluded, where a multiplicative one only pushes it toward "average".
# ponytail: fixed heuristic scale, unvalidated against H3's real logit spread --
# revisit if the live strength sweep is still flat, or saturates immediately.
_BIAS_SCALE = 12.0
def _override(func, q, k, v, heads, mask=None, skip_reshape=True,
transformer_options=None, **kwargs):
@@ -6908,7 +6906,12 @@ class FunPackLTXAVSceneChainSampler:
if vmask is None or amask is None or k.shape[-2] != seq_len:
return func(q, k, v, heads, mask=mask, skip_reshape=skip_reshape, **kwargs)
other = ~(vmask | amask)
bias_val = -_strength * _BIAS_SCALE
# `strength` IS the raw softmax logit penalty, not a normalized 0-1 fraction --
# there's no local way to know H3's actual trained attention-logit scale (the
# checkpoint isn't on this machine, see the method docstring), so a 0-1 dial
# over a guessed conversion constant would just be a second guess wearing a
# calibrated-looking costume. This way the live sweep searches the real unit.
bias_val = -_strength
out = torch.empty(q.shape[0], seq_len, heads * q.shape[-1],
dtype=q.dtype, device=q.device)
if amask.any():
@@ -6930,8 +6933,8 @@ class FunPackLTXAVSceneChainSampler:
to["optimized_attention_override"] = _override
patched.model_options["transformer_options"] = to
print(f"[FunPackSceneChain] H3 attention decoupling: damping cross-modal "
f"video<->audio attention by {_strength:g}.")
print(f"[FunPackSceneChain] H3 attention decoupling: cross-modal "
f"video<->audio attention logit penalty {_strength:g}.")
return patched
except Exception as _e: # noqa: BLE001
_log.failed("FunPackSceneChain", "H3 attention decoupling", _e,
+1 -1
View File
@@ -112,7 +112,7 @@ def test_splits_into_three_groups_with_a_bias_penalizing_the_other_modality():
by_len = {c["q_len"]: c for c in calls}
assert set(by_len) == {len(_VIDEO_IDX), len(_AUDIO_IDX), len(_OTHER_IDX)}
bias_val = -0.5 * 12.0 # strength * _BIAS_SCALE
bias_val = -0.5 # strength IS the raw logit penalty now, no hidden conversion constant
audio_call = by_len[len(_AUDIO_IDX)]
audio_bias = audio_call["mask"].view(-1)