返回 JoyAI-Echo
transformer.py
根目录 / echo_wm / ltx-core / src / ltx_core / model / transformer / transformer.py
1 from dataclasses import dataclass, field, replace
2
3 import torch
4 import torch.nn.functional as F
5
6 from ltx_core.guidance.perturbations import BatchedPerturbationConfig, PerturbationType
7 from ltx_core.model.transformer.adaln import adaln_embedding_coefficient
8 from ltx_core.model.transformer.attention import Attention, AttentionCallable, AttentionFunction, update_kv_cache
9 from ltx_core.model.transformer.feed_forward import FeedForward
10 from ltx_core.model.transformer.rope import LTXRopeType
11 from ltx_core.model.transformer.transformer_args import TransformerArgs
12 from ltx_core.model.transformer.ucpe_prope import _prepare_apply_fns
13 from ltx_core.utils import rms_norm
14
15
16 @dataclass
17 class TransformerConfig:
18 dim: int
19 heads: int
20 d_head: int
21 context_dim: int
22 apply_gated_attention: bool = False
23 cross_attention_adaln: bool = False
24
25
26 @dataclass
27 class ActionBlockConfig:
28 """Configuration for the optional pure-UCPE camera branch."""
29
30 enabled: bool = False
31 block_indices: list[int] = field(default_factory=list)
32 ucpe: bool = True
33 ucpe_attn_dim: int | None = None
34 ucpe_num_heads: int | None = None
35 ucpe_patches_x: int = 40
36 ucpe_patches_y: int = 22
37 ucpe_image_width: int = 1280
38 ucpe_image_height: int = 704
39 ucpe_freq_base: float = 100.0
40 ucpe_freq_scale: float = 1.0
41
42 def owns(self, block_idx: int) -> bool:
43 return self.enabled and self.ucpe and block_idx in self.block_indices
44
45
46 def active_sink_fifo_indices(
47 current_end: int, local_size: int, sink_size: int, device: torch.device
48 ) -> tuple[torch.Tensor, int]:
49 """Indices represented by a bounded ``sink + recent FIFO`` cache."""
50 if local_size <= 0 or sink_size < 0 or sink_size >= local_size:
51 raise ValueError(f"invalid sink/FIFO layout: local={local_size}, sink={sink_size}")
52 if current_end <= local_size:
53 return torch.arange(current_end, device=device), 0
54 recent_start = max(sink_size, current_end - (local_size - sink_size))
55 return torch.cat((torch.arange(sink_size, device=device), torch.arange(recent_start, current_end, device=device))), recent_start
56
57
58 def rebase_viewmat_translation(viewmats: torch.Tensor, anchor: torch.Tensor) -> torch.Tensor:
59 """Apply one common right-side translation, preserving relative cameras."""
60 with torch.autocast(device_type=viewmats.device.type, enabled=False):
61 matrices = viewmats.float()
62 anchor = anchor.float()
63 shift = -(anchor[..., :3, :3].transpose(-1, -2) @ anchor[..., :3, 3:4])
64 result = matrices.clone()
65 result[..., :3, 3:4] += result[..., :3, :3] @ shift
66 return result
67
68
69 def _ucpe_transform(apply_fn, value: torch.Tensor) -> torch.Tensor:
70 dtype = value.dtype
71 with torch.autocast(device_type=value.device.type, enabled=False):
72 return apply_fn(value.float()).to(dtype)
73
74
75 def _ucpe_cache_attend(cache: dict, start: int, k: torch.Tensor, v: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
76 batch, heads, seq, dim = k.shape
77 flat_k = k.transpose(1, 2).reshape(batch, seq, heads * dim)
78 flat_v = v.transpose(1, 2).reshape(batch, seq, heads * dim)
79 flat_k, flat_v = update_kv_cache(cache, start, flat_k, flat_v)
80 active = flat_k.shape[1]
81 return (
82 flat_k.view(batch, active, heads, dim).transpose(1, 2),
83 flat_v.view(batch, active, heads, dim).transpose(1, 2),
84 )
85
86
87 class BasicAVTransformerBlock(torch.nn.Module):
88 def __init__(
89 self,
90 idx: int,
91 num_layers: int,
92 video: TransformerConfig | None = None,
93 audio: TransformerConfig | None = None,
94 rope_type: LTXRopeType = LTXRopeType.INTERLEAVED,
95 norm_eps: float = 1e-6,
96 attention_function: AttentionFunction | AttentionCallable = AttentionFunction.DEFAULT,
97 ):
98 super().__init__()
99
100 self.idx = idx
101 self.num_layers = num_layers
102 if video is not None:
103 self.attn1 = Attention(
104 query_dim=video.dim,
105 heads=video.heads,
106 dim_head=video.d_head,
107 context_dim=None,
108 rope_type=rope_type,
109 norm_eps=norm_eps,
110 attention_function=attention_function,
111 apply_gated_attention=video.apply_gated_attention,
112 )
113 self.attn2 = Attention(
114 query_dim=video.dim,
115 context_dim=video.context_dim,
116 heads=video.heads,
117 dim_head=video.d_head,
118 rope_type=rope_type,
119 norm_eps=norm_eps,
120 attention_function=attention_function,
121 apply_gated_attention=video.apply_gated_attention,
122 )
123 self.ff = FeedForward(video.dim, dim_out=video.dim)
124 video_sst_size = adaln_embedding_coefficient(video.cross_attention_adaln)
125 self.scale_shift_table = torch.nn.Parameter(torch.empty(video_sst_size, video.dim))
126
127 if audio is not None:
128 self.audio_attn1 = Attention(
129 query_dim=audio.dim,
130 heads=audio.heads,
131 dim_head=audio.d_head,
132 context_dim=None,
133 rope_type=rope_type,
134 norm_eps=norm_eps,
135 attention_function=attention_function,
136 apply_gated_attention=audio.apply_gated_attention,
137 )
138 self.audio_attn2 = Attention(
139 query_dim=audio.dim,
140 context_dim=audio.context_dim,
141 heads=audio.heads,
142 dim_head=audio.d_head,
143 rope_type=rope_type,
144 norm_eps=norm_eps,
145 attention_function=attention_function,
146 apply_gated_attention=audio.apply_gated_attention,
147 )
148 self.audio_ff = FeedForward(audio.dim, dim_out=audio.dim)
149 audio_sst_size = adaln_embedding_coefficient(audio.cross_attention_adaln)
150 self.audio_scale_shift_table = torch.nn.Parameter(torch.empty(audio_sst_size, audio.dim))
151
152 if audio is not None and video is not None:
153 # Q: Video, K,V: Audio
154 self.audio_to_video_attn = Attention(
155 query_dim=video.dim,
156 context_dim=audio.dim,
157 heads=audio.heads,
158 dim_head=audio.d_head,
159 rope_type=rope_type,
160 norm_eps=norm_eps,
161 attention_function=attention_function,
162 apply_gated_attention=video.apply_gated_attention,
163 )
164
165 # Q: Audio, K,V: Video
166 self.video_to_audio_attn = Attention(
167 query_dim=audio.dim,
168 context_dim=video.dim,
169 heads=audio.heads,
170 dim_head=audio.d_head,
171 rope_type=rope_type,
172 norm_eps=norm_eps,
173 attention_function=attention_function,
174 apply_gated_attention=audio.apply_gated_attention,
175 )
176
177 self.scale_shift_table_a2v_ca_audio = torch.nn.Parameter(torch.empty(5, audio.dim))
178 self.scale_shift_table_a2v_ca_video = torch.nn.Parameter(torch.empty(5, video.dim))
179
180 self.cross_attention_adaln = (video is not None and video.cross_attention_adaln) or (
181 audio is not None and audio.cross_attention_adaln
182 )
183
184 if self.cross_attention_adaln and video is not None:
185 self.prompt_scale_shift_table = torch.nn.Parameter(torch.empty(2, video.dim))
186 if self.cross_attention_adaln and audio is not None:
187 self.audio_prompt_scale_shift_table = torch.nn.Parameter(torch.empty(2, audio.dim))
188
189 self.norm_eps = norm_eps
190 self.action_owns = False
191 self.action_ucpe_enabled = False
192
193 def _init_action_params(self, video: TransformerConfig, action_config: ActionBlockConfig) -> None:
194 """Attach the zero-initialized pure-UCPE branch to this block."""
195 if self.action_owns:
196 raise RuntimeError(f"Action params already initialized for block idx={self.idx}")
197 if not action_config.owns(self.idx):
198 return
199 from ltx_core.model.transformer.ucpe_prope import PropeDotProductAttention
200
201 self.action_owns = True
202 self.action_ucpe_enabled = True
203 vdim = video.dim
204 attn_dim = action_config.ucpe_attn_dim or vdim
205 num_heads = action_config.ucpe_num_heads or video.heads
206 if attn_dim % num_heads != 0 or (attn_dim // num_heads) % 4 != 0:
207 raise ValueError("UCPE attention dimension must be divisible by heads and by 4")
208 self.ucpe_num_heads = num_heads
209 self.ucpe_head_dim = attn_dim // num_heads
210 self.ucpe_q_proj = torch.nn.Linear(vdim, attn_dim, bias=False)
211 self.ucpe_k_proj = torch.nn.Linear(vdim, attn_dim, bias=False)
212 self.ucpe_v_proj = torch.nn.Linear(vdim, attn_dim, bias=False)
213 self.ucpe_out_proj = torch.nn.Linear(attn_dim, vdim, bias=True)
214 torch.nn.init.xavier_uniform_(self.ucpe_q_proj.weight)
215 torch.nn.init.xavier_uniform_(self.ucpe_k_proj.weight)
216 torch.nn.init.xavier_uniform_(self.ucpe_v_proj.weight)
217 torch.nn.init.zeros_(self.ucpe_out_proj.weight)
218 torch.nn.init.zeros_(self.ucpe_out_proj.bias)
219 self.ucpe_prope = PropeDotProductAttention(
220 head_dim=self.ucpe_head_dim,
221 patches_x=action_config.ucpe_patches_x,
222 patches_y=action_config.ucpe_patches_y,
223 image_width=action_config.ucpe_image_width,
224 image_height=action_config.ucpe_image_height,
225 freq_base=action_config.ucpe_freq_base,
226 freq_scale=action_config.ucpe_freq_scale,
227 )
228
229 def _apply_ucpe_attention(
230 self,
231 norm_vx: torch.Tensor,
232 viewmats: torch.Tensor,
233 Ks: torch.Tensor,
234 kv_cache: dict | None = None,
235 kv_cache_start: int = 0,
236 ) -> torch.Tensor:
237 batch, seq_len, _ = norm_vx.shape
238 heads, head_dim = self.ucpe_num_heads, self.ucpe_head_dim
239 q = self.ucpe_q_proj(norm_vx).view(batch, seq_len, heads, head_dim).transpose(1, 2)
240 k = self.ucpe_k_proj(norm_vx).view(batch, seq_len, heads, head_dim).transpose(1, 2)
241 v = self.ucpe_v_proj(norm_vx).view(batch, seq_len, heads, head_dim).transpose(1, 2)
242 if kv_cache is not None and kv_cache.get("bounded_anchor_translation", False):
243 ppf = int(kv_cache["patches_per_frame"])
244 k, v = _ucpe_cache_attend(kv_cache, kv_cache_start, k, v)
245 current_start = kv_cache_start // ppf
246 current_end = current_start + seq_len // ppf
247 indices, anchor_index = active_sink_fifo_indices(
248 current_end,
249 int(kv_cache["local_attn_size"]) // ppf,
250 int(kv_cache["sink_tokens"]) // ppf,
251 kv_cache["full_ucpe_viewmats"].device,
252 )
253 all_viewmats = kv_cache["full_ucpe_viewmats"]
254 all_Ks = kv_cache["full_ucpe_Ks"]
255 anchor = all_viewmats[:, anchor_index : anchor_index + 1]
256 q_viewmats = rebase_viewmat_translation(all_viewmats[:, current_start:current_end], anchor)
257 k_viewmats = rebase_viewmat_translation(all_viewmats.index_select(1, indices), anchor)
258 kwargs = dict(
259 head_dim=self.ucpe_prope.head_dim,
260 patches_x=self.ucpe_prope.patches_x,
261 patches_y=self.ucpe_prope.patches_y,
262 image_width=self.ucpe_prope.image_width,
263 image_height=self.ucpe_prope.image_height,
264 coeffs_x=None if self.ucpe_prope.coeffs_x_0 is None else (self.ucpe_prope.coeffs_x_0, self.ucpe_prope.coeffs_x_1),
265 coeffs_y=None if self.ucpe_prope.coeffs_y_0 is None else (self.ucpe_prope.coeffs_y_0, self.ucpe_prope.coeffs_y_1),
266 )
267 apply_q, _, apply_out = _prepare_apply_fns(viewmats=q_viewmats, Ks=all_Ks[:, current_start:current_end].float(), **kwargs)
268 _, apply_kv, _ = _prepare_apply_fns(viewmats=k_viewmats, Ks=all_Ks.index_select(1, indices).float(), **kwargs)
269 q = _ucpe_transform(apply_q, q)
270 k = _ucpe_transform(apply_kv, k)
271 v = _ucpe_transform(apply_kv, v)
272 out = F.scaled_dot_product_attention(q, k, v, is_causal=False)
273 out = _ucpe_transform(apply_out, out)
274 else:
275 # Preserve the original base-inference dtype/autocast behavior.
276 self.ucpe_prope._precompute_and_cache_apply_fns(viewmats=viewmats, Ks=Ks)
277 q = self.ucpe_prope._apply_to_q(q)
278 k = self.ucpe_prope._apply_to_kv(k)
279 v = self.ucpe_prope._apply_to_kv(v)
280 if kv_cache is not None:
281 k, v = _ucpe_cache_attend(kv_cache, kv_cache_start, k, v)
282 out = F.scaled_dot_product_attention(q, k, v, is_causal=False)
283 out = self.ucpe_prope._apply_to_o(out)
284 out = out.to(dtype=self.ucpe_out_proj.weight.dtype)
285 return self.ucpe_out_proj(out.transpose(1, 2).reshape(batch, seq_len, heads * head_dim))
286
287 def get_ada_values(
288 self, scale_shift_table: torch.Tensor, batch_size: int, timestep: torch.Tensor, indices: slice
289 ) -> tuple[torch.Tensor, ...]:
290 num_ada_params = scale_shift_table.shape[0]
291
292 ada_values = (
293 scale_shift_table[indices].unsqueeze(0).unsqueeze(0).to(device=timestep.device, dtype=timestep.dtype)
294 + timestep.reshape(batch_size, timestep.shape[1], num_ada_params, -1)[:, :, indices, :]
295 ).unbind(dim=2)
296 return ada_values
297
298 def get_av_ca_ada_values(
299 self,
300 scale_shift_table: torch.Tensor,
301 batch_size: int,
302 scale_shift_timestep: torch.Tensor,
303 gate_timestep: torch.Tensor,
304 scale_shift_indices: slice,
305 num_scale_shift_values: int = 4,
306 ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
307 scale_shift_ada_values = self.get_ada_values(
308 scale_shift_table[:num_scale_shift_values, :], batch_size, scale_shift_timestep, scale_shift_indices
309 )
310 gate_ada_values = self.get_ada_values(
311 scale_shift_table[num_scale_shift_values:, :], batch_size, gate_timestep, slice(None, None)
312 )
313
314 scale, shift = (t.squeeze(2) for t in scale_shift_ada_values)
315 (gate,) = (t.squeeze(2) for t in gate_ada_values)
316
317 return scale, shift, gate
318
319 def _apply_text_cross_attention(
320 self,
321 x: torch.Tensor,
322 context: torch.Tensor,
323 attn: AttentionCallable,
324 scale_shift_table: torch.Tensor,
325 prompt_scale_shift_table: torch.Tensor | None,
326 timestep: torch.Tensor,
327 prompt_timestep: torch.Tensor | None,
328 context_mask: torch.Tensor | None,
329 cross_attention_adaln: bool = False,
330 crossattn_cache: dict | None = None,
331 ) -> torch.Tensor:
332 """Apply text cross-attention, with optional AdaLN modulation."""
333 if cross_attention_adaln:
334 shift_q, scale_q, gate = self.get_ada_values(scale_shift_table, x.shape[0], timestep, slice(6, 9))
335 return apply_cross_attention_adaln(
336 x,
337 context,
338 attn,
339 shift_q,
340 scale_q,
341 gate,
342 prompt_scale_shift_table,
343 prompt_timestep,
344 context_mask,
345 self.norm_eps,
346 crossattn_cache,
347 )
348 return attn(
349 rms_norm(x, eps=self.norm_eps), context=context, mask=context_mask,
350 crossattn_cache=crossattn_cache,
351 )
352
353 def forward( # noqa: PLR0915
354 self,
355 video: TransformerArgs | None,
356 audio: TransformerArgs | None,
357 perturbations: BatchedPerturbationConfig | None = None,
358 ucpe_viewmats: torch.Tensor | None = None,
359 ucpe_Ks: torch.Tensor | None = None,
360 kv_cache: dict | None = None,
361 current_video_token_start: int = 0,
362 current_audio_token_start: int = 0,
363 ) -> tuple[TransformerArgs | None, TransformerArgs | None]:
364 if video is None and audio is None:
365 raise ValueError("At least one of video or audio must be provided")
366
367 batch_size = (video or audio).x.shape[0]
368
369 if perturbations is None:
370 perturbations = BatchedPerturbationConfig.empty(batch_size)
371
372 vx = video.x if video is not None else None
373 ax = audio.x if audio is not None else None
374
375 run_vx = video is not None and video.enabled and vx.numel() > 0
376 run_ax = audio is not None and audio.enabled and ax.numel() > 0
377
378 run_a2v = run_vx and (audio is not None and ax.numel() > 0)
379 run_v2a = run_ax and (video is not None and vx.numel() > 0)
380
381 if run_vx:
382 vshift_msa, vscale_msa, vgate_msa = self.get_ada_values(
383 self.scale_shift_table, vx.shape[0], video.timesteps, slice(0, 3)
384 )
385 norm_vx = rms_norm(vx, eps=self.norm_eps) * (1 + vscale_msa) + vshift_msa
386 del vshift_msa, vscale_msa
387
388 all_perturbed = perturbations.all_in_batch(PerturbationType.SKIP_VIDEO_SELF_ATTN, self.idx)
389 none_perturbed = not perturbations.any_in_batch(PerturbationType.SKIP_VIDEO_SELF_ATTN, self.idx)
390 v_mask = (
391 perturbations.mask_like(PerturbationType.SKIP_VIDEO_SELF_ATTN, self.idx, vx)
392 if not all_perturbed and not none_perturbed
393 else None
394 )
395 attn_out = self.attn1(
396 norm_vx,
397 pe=video.positional_embeddings,
398 mask=video.self_attention_mask,
399 perturbation_mask=v_mask,
400 all_perturbed=all_perturbed,
401 kv_cache=kv_cache.get("video_self") if kv_cache else None,
402 kv_cache_start=current_video_token_start,
403 )
404 if self.action_ucpe_enabled and ucpe_viewmats is not None and ucpe_Ks is not None:
405 attn_out = attn_out + self._apply_ucpe_attention(
406 norm_vx, ucpe_viewmats, ucpe_Ks,
407 kv_cache=kv_cache.get("video_ucpe") if kv_cache else None,
408 kv_cache_start=current_video_token_start,
409 )
410 vx = vx + attn_out * vgate_msa
411 del vgate_msa, norm_vx, v_mask, attn_out
412 vx = vx + self._apply_text_cross_attention(
413 vx,
414 video.context,
415 self.attn2,
416 self.scale_shift_table,
417 getattr(self, "prompt_scale_shift_table", None),
418 video.timesteps,
419 video.prompt_timestep,
420 video.context_mask,
421 cross_attention_adaln=self.cross_attention_adaln,
422 crossattn_cache=kv_cache.get("video_text") if kv_cache else None,
423 )
424
425 if run_ax:
426 ashift_msa, ascale_msa, agate_msa = self.get_ada_values(
427 self.audio_scale_shift_table, ax.shape[0], audio.timesteps, slice(0, 3)
428 )
429
430 norm_ax = rms_norm(ax, eps=self.norm_eps) * (1 + ascale_msa) + ashift_msa
431 del ashift_msa, ascale_msa
432 all_perturbed = perturbations.all_in_batch(PerturbationType.SKIP_AUDIO_SELF_ATTN, self.idx)
433 none_perturbed = not perturbations.any_in_batch(PerturbationType.SKIP_AUDIO_SELF_ATTN, self.idx)
434 a_mask = (
435 perturbations.mask_like(PerturbationType.SKIP_AUDIO_SELF_ATTN, self.idx, ax)
436 if not all_perturbed and not none_perturbed
437 else None
438 )
439 audio_self_attention_mask = audio.self_attention_mask
440 if self.idx >= int(self.num_layers * 0.7):
441 audio_self_attention_mask = audio.late_self_attention_mask
442 ax = (
443 ax
444 + self.audio_attn1(
445 norm_ax,
446 pe=audio.positional_embeddings,
447 mask=audio_self_attention_mask,
448 perturbation_mask=a_mask,
449 all_perturbed=all_perturbed,
450 kv_cache=kv_cache.get("audio_self") if kv_cache else None,
451 kv_cache_start=current_audio_token_start,
452 )
453 * agate_msa
454 )
455 del agate_msa, norm_ax, a_mask
456 ax = ax + self._apply_text_cross_attention(
457 ax,
458 audio.context,
459 self.audio_attn2,
460 self.audio_scale_shift_table,
461 getattr(self, "audio_prompt_scale_shift_table", None),
462 audio.timesteps,
463 audio.prompt_timestep,
464 audio.context_mask,
465 cross_attention_adaln=self.cross_attention_adaln,
466 crossattn_cache=kv_cache.get("audio_text") if kv_cache else None,
467 )
468
469 # Audio - Video cross attention.
470 if run_a2v or run_v2a:
471 vx_norm3 = rms_norm(vx, eps=self.norm_eps)
472 ax_norm3 = rms_norm(ax, eps=self.norm_eps)
473
474 if run_a2v and not perturbations.all_in_batch(PerturbationType.SKIP_A2V_CROSS_ATTN, self.idx):
475 scale_ca_video_a2v, shift_ca_video_a2v, gate_out_a2v = self.get_av_ca_ada_values(
476 self.scale_shift_table_a2v_ca_video,
477 vx.shape[0],
478 video.cross_scale_shift_timestep,
479 video.cross_gate_timestep,
480 slice(0, 2),
481 )
482 vx_scaled = vx_norm3 * (1 + scale_ca_video_a2v) + shift_ca_video_a2v
483 del scale_ca_video_a2v, shift_ca_video_a2v
484
485 scale_ca_audio_a2v, shift_ca_audio_a2v, _ = self.get_av_ca_ada_values(
486 self.scale_shift_table_a2v_ca_audio,
487 ax.shape[0],
488 audio.cross_scale_shift_timestep,
489 audio.cross_gate_timestep,
490 slice(0, 2),
491 )
492 ax_scaled = ax_norm3 * (1 + scale_ca_audio_a2v) + shift_ca_audio_a2v
493 del scale_ca_audio_a2v, shift_ca_audio_a2v
494 a2v_mask = perturbations.mask_like(PerturbationType.SKIP_A2V_CROSS_ATTN, self.idx, vx)
495 cross_attention_mask = video.cross_attention_mask
496 cross_output_mask = video.cross_output_mask
497 if self.idx >= int(self.num_layers * 0.7):
498 if video.late_cross_attention_mask is not None:
499 cross_attention_mask = video.late_cross_attention_mask
500 if video.late_cross_output_mask is not None:
501 cross_output_mask = video.late_cross_output_mask
502 cross_output_mask = cross_output_mask if cross_output_mask is not None else 1.0
503 vx = vx + (
504 self.audio_to_video_attn(
505 vx_scaled,
506 context=ax_scaled,
507 mask=cross_attention_mask,
508 pe=video.cross_positional_embeddings,
509 k_pe=audio.cross_positional_embeddings,
510 kv_cache=kv_cache.get("a2v") if kv_cache else None,
511 kv_cache_start=current_audio_token_start,
512 )
513 * gate_out_a2v
514 * a2v_mask
515 * cross_output_mask
516 )
517 del gate_out_a2v, a2v_mask, vx_scaled, ax_scaled, cross_output_mask
518
519 if run_v2a and not perturbations.all_in_batch(PerturbationType.SKIP_V2A_CROSS_ATTN, self.idx):
520 scale_ca_audio_v2a, shift_ca_audio_v2a, gate_out_v2a = self.get_av_ca_ada_values(
521 self.scale_shift_table_a2v_ca_audio,
522 ax.shape[0],
523 audio.cross_scale_shift_timestep,
524 audio.cross_gate_timestep,
525 slice(2, 4),
526 )
527 ax_scaled = ax_norm3 * (1 + scale_ca_audio_v2a) + shift_ca_audio_v2a
528 del scale_ca_audio_v2a, shift_ca_audio_v2a
529 scale_ca_video_v2a, shift_ca_video_v2a, _ = self.get_av_ca_ada_values(
530 self.scale_shift_table_a2v_ca_video,
531 vx.shape[0],
532 video.cross_scale_shift_timestep,
533 video.cross_gate_timestep,
534 slice(2, 4),
535 )
536 vx_scaled = vx_norm3 * (1 + scale_ca_video_v2a) + shift_ca_video_v2a
537 del scale_ca_video_v2a, shift_ca_video_v2a
538 v2a_mask = perturbations.mask_like(PerturbationType.SKIP_V2A_CROSS_ATTN, self.idx, ax)
539 cross_attention_mask = audio.cross_attention_mask
540 cross_output_mask = audio.cross_output_mask
541 if self.idx >= int(self.num_layers * 0.7):
542 if audio.late_cross_attention_mask is not None:
543 cross_attention_mask = audio.late_cross_attention_mask
544 if audio.late_cross_output_mask is not None:
545 cross_output_mask = audio.late_cross_output_mask
546 cross_output_mask = cross_output_mask if cross_output_mask is not None else 1.0
547 v2a_update = (
548 self.video_to_audio_attn(
549 ax_scaled,
550 context=vx_scaled,
551 mask=cross_attention_mask,
552 pe=audio.cross_positional_embeddings,
553 k_pe=video.cross_positional_embeddings,
554 kv_cache=kv_cache.get("v2a") if kv_cache else None,
555 kv_cache_start=current_video_token_start,
556 )
557 * gate_out_v2a
558 * v2a_mask
559 * cross_output_mask
560 )
561 v2a_grad_scale = float(getattr(audio, "v2a_grad_scale", 1.0))
562 if v2a_grad_scale != 1.0 and torch.is_grad_enabled():
563 v2a_update = v2a_update.detach() + v2a_grad_scale * (v2a_update - v2a_update.detach())
564 ax = ax + v2a_update
565 del gate_out_v2a, v2a_mask, ax_scaled, vx_scaled, cross_output_mask, v2a_update
566
567 del vx_norm3, ax_norm3
568
569 if run_vx:
570 vshift_mlp, vscale_mlp, vgate_mlp = self.get_ada_values(
571 self.scale_shift_table, vx.shape[0], video.timesteps, slice(3, 6)
572 )
573 vx_scaled = rms_norm(vx, eps=self.norm_eps) * (1 + vscale_mlp) + vshift_mlp
574 vx = vx + self.ff(vx_scaled) * vgate_mlp
575
576 del vshift_mlp, vscale_mlp, vgate_mlp, vx_scaled
577
578 if run_ax:
579 ashift_mlp, ascale_mlp, agate_mlp = self.get_ada_values(
580 self.audio_scale_shift_table, ax.shape[0], audio.timesteps, slice(3, 6)
581 )
582 ax_scaled = rms_norm(ax, eps=self.norm_eps) * (1 + ascale_mlp) + ashift_mlp
583 ax = ax + self.audio_ff(ax_scaled) * agate_mlp
584
585 del ashift_mlp, ascale_mlp, agate_mlp, ax_scaled
586
587 return replace(video, x=vx) if video is not None else None, replace(audio, x=ax) if audio is not None else None
588
589
590 def apply_cross_attention_adaln(
591 x: torch.Tensor,
592 context: torch.Tensor,
593 attn: AttentionCallable,
594 q_shift: torch.Tensor,
595 q_scale: torch.Tensor,
596 q_gate: torch.Tensor,
597 prompt_scale_shift_table: torch.Tensor,
598 prompt_timestep: torch.Tensor,
599 context_mask: torch.Tensor | None = None,
600 norm_eps: float = 1e-6,
601 crossattn_cache: dict | None = None,
602 ) -> torch.Tensor:
603 batch_size = x.shape[0]
604 shift_kv, scale_kv = (
605 prompt_scale_shift_table[None, None].to(device=x.device, dtype=x.dtype)
606 + prompt_timestep.reshape(batch_size, prompt_timestep.shape[1], 2, -1)
607 ).unbind(dim=2)
608 attn_input = rms_norm(x, eps=norm_eps) * (1 + q_scale) + q_shift
609 encoder_hidden_states = context * (1 + scale_kv) + shift_kv
610 return attn(
611 attn_input,
612 context=encoder_hidden_states,
613 mask=context_mask,
614 crossattn_cache=crossattn_cache,
615 ) * q_gate
616
616 lines PYTHON