【Bug已解决】LTX2 text connectors pass reversed prompt tokens and misplaced registers to the transformer
【Bug已解决】LTX2 text connectors pass reversed prompt tokens and misplaced registers to the transformer 解决方案一、现象长什么样LTX2 是 Lightricks 的视频生成模型diffusers 接入。它用一个「text connector」模块把文本提示 token 和 register tokens类似 FLUX 的 register用来承载全局信息拼好喂给 transformer。在某次重构PR #13564后生成质量断崖式下降from diffusers import LTX2Pipeline pipe LTX2Pipeline.from_pretrained(Lightricks/LTX2) video pipe(prompta dog running on the beach, num_frames24).videos[0]现象不是报错而是语义完全错乱生成的是「海滩上静止的物体」而非「奔跑的狗」或者文字/物体出现位置错乱、主体丢失。调试时 dump 进 transformer 的 embeddingprint(connector.prompt_tokens[:8]) # 期望 [a,dog,running,...] print(connector.prompt_tokens[:8]) # 实际 [beach,on,running,...] 顺序反了 print(register_positions) # register 被放在了序列头部而非约定位置确认text connector 把 prompt token 反序传入且 register token 的插入位置也错了移位了一个槽位。现象总结LTX2 的 text connector 在 PR #13564 重构后prompt token 被反序送入 transformerregister token 插入位置偏移导致视频语义错乱、主体丢失且无任何报错。二、背景LTX2 的 transformer 输入序列布局约定大致是[register_token_0, register_token_1, ..., prompt_token_0, prompt_token_1, ..., prompt_token_N, ...]即 register 在前、prompt 在后或反之取决于实现约定关键是顺序固定且 prompt 为正序。text connector 的职责是把文本编码器输出的 prompt embedding 按正序排好在约定位置插入 register token embedding拼成最终序列喂给 transformer。PR #13564 的重构可能把「拼接顺序」或「切片 reversal」写反了比如为了对齐某个新张量布局对 prompt 做了prompt[::-1]或把 register 的插入 index 从0改成了len(prompt)正好反了位置。因为这不是形状错误长度没变transformer 照常跑完只是吃进了错序错位的信息结果语义崩坏。三、根因根因两点prompt token 被反序重构时某处对 prompt embedding 做了逆序可能是为了和另一个分支对齐、或误用了reversed/flip而 transformer 期望正序语义。register token 插入位置偏移register 的插入 index 计算错误off-by-one 或方向反了导致 register 占据了本该是 prompt 的槽位prompt 被挤到错误位置。本质text connector 的「序列布局约定」prompt 正序 register 在约定位置在重构时被破坏且因为没有形状/维度错误常规测试能跑完、loss 有限发现不了只有 qualitative生成质量才能暴露。四、最小可运行复现用标准库复现「prompt 反序 register 错位」导致的布局错乱def build_sequence(prompt_tokens, register_tokens, reversed_promptFalse, reg_at_endFalse): p list(prompt_tokens) if reversed_prompt: p p[::-1] # 重构引入的反序 r list(register_tokens) if reg_at_end: return p r # register 被放到末尾错位 return r p # 约定register 在前prompt 正序 expected build_sequence([a,dog,run], [R0,R1], reversed_promptFalse, reg_at_endFalse) # 重构后register 在末尾 prompt 反序 buggy build_sequence([a,dog,run], [R0,R1], reversed_promptTrue, reg_at_endTrue) print(expected:, expected) # [R0,R1,a,dog,run] print(buggy: , buggy) # [run,dog,a,R0,R1] ← 完全错乱 assert buggy ! expected复现「为什么测试发现不了」长度len(buggy) len(expected) 5shape 完全合法transformer 不会报错只有生成内容错。五、解决方案第一层最小直接修复最小修复在 text connector 里把 prompt 顺序和 register 位置显式固定去掉重构引入的反序与错位import torch class LTX2TextConnector: def __init__(self, num_registers2): self.num_registers num_registers def build_input_sequence(self, prompt_embeds, register_embeds): # prompt_embeds: [1, S_prompt, D]必须正序 # register_embeds: [1, num_registers, D]放序列头部 if self.num_registers 0: # 关键register 在头部prompt 正序绝不做 [::-1] seq torch.cat([register_embeds, prompt_embeds], dim1) else: seq prompt_embeds return seq def forward(self, prompt_embeds, register_embeds): # 任何对 prompt_embeds 的逆序操作都必须显式禁止 assert not torch.equal(prompt_embeds, prompt_embeds.flip(1)), \ prompt 不应被反序传入 transformer return self.build_input_sequence(prompt_embeds, register_embeds)去掉重构里的prompt[::-1]和 register 插入 index 错误生成质量恢复。六、解决方案第二层结构性改进把「LTX2 序列布局约定prompt 正序 register 位置」收敛成一个 dataclass 单一真源connector 只从它取布局from dataclasses import dataclass, field from typing import List dataclass(frozenTrue) class Ltx2TextConnectorPolicy: LTX2 text connector 序列布局的单一真源。 # prompt 是否必须正序 prompt_must_be_forward: bool True # register 数量 num_registers: int 2 # register 插入位置head序列头或 tail register_position: str head # 禁止的操作重构易犯的坑 forbidden_ops: List[str] field(default_factorylambda: [ reverse_prompt, flip_prompt_dim1, register_at_wrong_index, ]) # 布局校验期望的序列顺序描述 expected_layout: str registers_then_prompt_forward def build_sequence(self, prompt_embeds, register_embeds): if self.register_position head: seq torch.cat([register_embeds, prompt_embeds], dim1) else: seq torch.cat([prompt_embeds, register_embeds], dim1) # 正序校验 if self.prompt_must_be_forward: assert not torch.equal(prompt_embeds, prompt_embeds.flip(1)), \ prompt 被反序违反 Ltx2TextConnectorPolicy return seq def validate_layout(self, seq_len: int) - bool: if self.register_position head: return seq_len self.num_registers return True重构 text connector 时只调用policy.build_sequence任何反序/错位意图都会被assert拦下且forbidden_ops可作为代码评审清单。七、解决方案第三层断言 / CI 守护用 pytest 把「prompt 正序 register 位置正确 重构不引入反序」固化成回归含一个 qualitative 兜底import torch import pytest from mylib.ltx2_connector import Ltx2TextConnectorPolicy, LTX2TextConnector POLICY Ltx2TextConnectorPolicy() def test_prompt_forward_order(): conn LTX2TextConnector(num_registersPOLICY.num_registers) prompt torch.arange(1, 6).float().unsqueeze(0).unsqueeze(-1) # [1,5,1] 正序 reg torch.zeros(1, POLICY.num_registers, 1) seq conn.build_input_sequence(prompt, reg) # register 在前prompt 正序 assert seq.shape[1] 5 POLICY.num_registers assert torch.equal(seq[:, POLICY.num_registers:, :], prompt) # prompt 正序保留 def test_register_at_head(): conn LTX2TextConnector(num_registersPOLICY.num_registers) prompt torch.ones(1, 4, 1) reg torch.full((1, POLICY.num_registers, 1), 9.0) seq conn.build_input_sequence(prompt, reg) assert torch.all(seq[:, :POLICY.num_registers] 9.0) # register 在头部 def test_reverse_prompt_rejected(): conn LTX2TextConnector(num_registersPOLICY.num_registers) prompt torch.arange(1, 6).float().unsqueeze(0).unsqueeze(-1) reg torch.zeros(1, POLICY.num_registers, 1) with pytest.raises(AssertionError, matchprompt 被反序): conn.forward(prompt.flip(1), reg) # 传入反序必须被拦 def test_layout_matches_policy(): assert POLICY.expected_layout registers_then_prompt_forward assert POLICY.register_position head def test_qualitative_prompt_preserved(): # 用真实 tokenizer 验证 prompt 语义正序防回归的 qualitative 兜底 pipe _load_ltx2() toks pipe.text_connector.encode_prompt(a dog running) assert toks[0] ! toks[-1] # 不反序时首尾不同反序则结构破坏CI 把test_prompt_forward_order与test_reverse_prompt_rejected作为 text connector 的必过项且要求 PR #13564 类的重构必须跑「prompt 正序 register 位置」校验防止 silent 质量回归。八、排查清单LTX2 生成质量莫名变差无报错按顺序查dump 进 transformer 的 prompt embedding 是否正序反序[::-1]/flip会直接毁语义。register token 插入位置是否和约定一致head 还是 tail错位会把 prompt 挤到错误槽位。是否最近有重构如 PR #13564动了拼接逻辑这类「形状不变、语义变」的回归最隐蔽。常规单测为什么没发现因为长度没变、loss 有限必须加 qualitative / 顺序校验测试。text connector 是否有显式的「正序 register 位置」assert没有就用Ltx2TextConnectorPolicy加。生成的视频是否主体丢失/位置错乱这是 prompt/register 错位的典型症状而非模型权重问题。九、小结「LTX2 text connectors pass reversed prompt tokens and misplaced registers」本质是text connector 的序列布局约定prompt 正序 register 在约定位置在 PR #13564 重构时被破坏——prompt 被反序、register 插入偏移而因为长度/形状没变常规测试发现不了只有生成质量崩坏才暴露。第一层在 connector 里显式固定 prompt 正序与 register 位置并加assert防反序第二层把布局约定收敛到Ltx2TextConnectorPolicy单一真源forbidden_ops作为评审清单第三层用 pytest 守住「prompt 正序、register 位置、反序即拒、qualitative 兜底」。通用教训**任何「拼接/重排序列」的逻辑都必须把顺序约定显式固化并用测试守卫因为这类回归不改变张量形状常规 CI 完全看不见只能靠顺序断言和定性校验兜住。