返回 JoyAI-Echo
memory_bidirectional_pipeline.py
根目录 / ltx-distillation / src / ltx_distillation / inference / memory_bidirectional_pipeline.py
1 """
2 Bidirectional pipelines for memory-conditioned DMD.
3 """
4
5 from __future__ import annotations
6
7 from typing import Any, Callable, Dict, Optional, Tuple
8
9 import torch
10 import torch.nn as nn
11
12
13 class BidirectionalMemoryVideoTrajectoryPipeline:
14 """
15 Few-step backward simulation for video-only memory-conditioned DMD.
16 """
17
18 def __init__(
19 self,
20 generator: nn.Module,
21 add_noise_fn,
22 denoising_sigmas: torch.Tensor,
23 memory_downscale_factor: int = 1,
24 audio_latent_clamp: float = 0.0,
25 ) -> None:
26 self.generator = generator
27 self.add_noise_fn = add_noise_fn
28 self.denoising_sigmas = denoising_sigmas
29 self.memory_downscale_factor = int(memory_downscale_factor)
30 self.audio_latent_clamp = float(audio_latent_clamp)
31
32 @torch.no_grad()
33 def inference_with_trajectory(
34 self,
35 video_noise: torch.Tensor,
36 conditional_dict: Dict[str, Any],
37 memory_video: torch.Tensor,
38 ) -> torch.Tensor:
39 batch_size = video_noise.shape[0]
40 num_frames = video_noise.shape[1]
41 device = video_noise.device
42 dtype = video_noise.dtype
43 memory_video = memory_video.to(device=device, dtype=dtype)
44
45 trajectory = [video_noise]
46 noisy_video = video_noise
47
48 for idx, sigma in enumerate(self.denoising_sigmas[:-1]):
49 video_sigma = sigma * torch.ones([batch_size, num_frames], device=device, dtype=dtype)
50 pred_video, _ = self.generator(
51 noisy_image_or_video=noisy_video,
52 conditional_dict=conditional_dict,
53 timestep=video_sigma,
54 noisy_audio=None,
55 audio_timestep=None,
56 memory_video=memory_video,
57 memory_downscale_factor=self.memory_downscale_factor,
58 )
59 pred_video = pred_video.to(dtype=dtype)
60
61 next_sigma = self.denoising_sigmas[idx + 1]
62 if next_sigma > 0:
63 fresh_noise = torch.randn_like(video_noise)
64 next_video_sigma = next_sigma * torch.ones([batch_size, num_frames], device=device, dtype=dtype)
65 noisy_video = self.add_noise_fn(
66 pred_video.flatten(0, 1),
67 fresh_noise.flatten(0, 1),
68 next_video_sigma.flatten(0, 1),
69 ).unflatten(0, (batch_size, num_frames)).to(dtype=dtype)
70 else:
71 noisy_video = pred_video
72
73 trajectory.append(noisy_video)
74
75 return torch.stack(trajectory, dim=1)
76
77
78 class BidirectionalMemoryVideoInferencePipeline:
79 """
80 Few-step benchmark/inference pipeline for video-only memory-conditioned DMD.
81 """
82
83 def __init__(
84 self,
85 generator: nn.Module,
86 add_noise_fn,
87 denoising_sigmas: torch.Tensor,
88 memory_downscale_factor: int = 1,
89 trace_fn: Optional[Callable[[Dict[str, Any]], None]] = None,
90 ) -> None:
91 self.generator = generator
92 self.add_noise_fn = add_noise_fn
93 self.denoising_sigmas = denoising_sigmas
94 self.memory_downscale_factor = int(memory_downscale_factor)
95 self.trace_fn = trace_fn
96
97 def _emit_trace(
98 self,
99 event: str,
100 tensor: torch.Tensor,
101 *,
102 sigma_idx: Optional[int] = None,
103 sigma: Optional[torch.Tensor] = None,
104 ) -> None:
105 if self.trace_fn is None:
106 return
107 values = tensor.detach().float()
108 if values.numel() == 0:
109 stats = {
110 "mean": 0.0,
111 "std": 0.0,
112 "min": 0.0,
113 "max": 0.0,
114 "absmax": 0.0,
115 "nonzero_frac": 0.0,
116 }
117 else:
118 stats = {
119 "mean": values.mean().item(),
120 "std": values.std(unbiased=False).item() if values.numel() > 1 else 0.0,
121 "min": values.min().item(),
122 "max": values.max().item(),
123 "absmax": values.abs().max().item(),
124 "nonzero_frac": values.ne(0).float().mean().item(),
125 }
126 payload: Dict[str, Any] = {
127 "phase": "bootstrap",
128 "event": event,
129 "shape": list(tensor.shape),
130 **stats,
131 }
132 if sigma_idx is not None:
133 payload["sigma_idx"] = int(sigma_idx)
134 if sigma is not None:
135 payload["sigma"] = float(sigma.detach().float().item())
136 self.trace_fn(payload)
137
138 @torch.no_grad()
139 def generate(
140 self,
141 video_shape: Tuple[int, ...],
142 conditional_dict: Dict[str, Any],
143 memory_video: torch.Tensor,
144 seed: Optional[int] = None,
145 ) -> torch.Tensor:
146 batch_size = video_shape[0]
147 num_frames = video_shape[1]
148
149 if seed is not None:
150 torch.manual_seed(seed)
151
152 device = next(self.generator.parameters()).device
153 dtype = next(self.generator.parameters()).dtype
154
155 video = torch.randn(video_shape, device=device, dtype=dtype)
156 memory_video = memory_video.to(device=device, dtype=dtype)
157 self._emit_trace("initial_noise", video)
158
159 for idx, sigma in enumerate(self.denoising_sigmas[:-1]):
160 video_sigma = sigma * torch.ones([batch_size, num_frames], device=device, dtype=dtype)
161
162 pred_video, _ = self.generator(
163 noisy_image_or_video=video,
164 conditional_dict=conditional_dict,
165 timestep=video_sigma,
166 noisy_audio=None,
167 audio_timestep=None,
168 memory_video=memory_video,
169 memory_downscale_factor=self.memory_downscale_factor,
170 )
171 pred_video = pred_video.to(dtype=dtype)
172 self._emit_trace("pred_x0", pred_video, sigma_idx=idx, sigma=sigma)
173
174 next_sigma = self.denoising_sigmas[idx + 1]
175 if next_sigma > 0:
176 fresh_noise = torch.randn_like(video)
177 next_video_sigma = next_sigma * torch.ones([batch_size, num_frames], device=device, dtype=dtype)
178 video = self.add_noise_fn(
179 pred_video.flatten(0, 1),
180 fresh_noise.flatten(0, 1),
181 next_video_sigma.flatten(0, 1),
182 ).unflatten(0, (batch_size, num_frames)).to(dtype=dtype)
183 else:
184 video = pred_video
185 self._emit_trace("updated_video", video, sigma_idx=idx + 1, sigma=next_sigma)
186
187 return video
188
189
190 class BidirectionalMemoryAVTrajectoryPipeline:
191 """
192 Few-step backward simulation for video-memory-conditioned AV DMD.
193 """
194
195 def __init__(
196 self,
197 generator: nn.Module,
198 add_noise_fn,
199 denoising_sigmas: torch.Tensor,
200 memory_downscale_factor: int = 1,
201 audio_latent_clamp: float = 0.0,
202 ) -> None:
203 self.generator = generator
204 self.add_noise_fn = add_noise_fn
205 self.denoising_sigmas = denoising_sigmas
206 self.memory_downscale_factor = int(memory_downscale_factor)
207 self.audio_latent_clamp = float(audio_latent_clamp)
208
209 @torch.no_grad()
210 def inference_with_trajectory(
211 self,
212 video_noise: torch.Tensor,
213 audio_noise: torch.Tensor,
214 conditional_dict: Dict[str, Any],
215 memory_video: torch.Tensor,
216 memory_audio: Optional[torch.Tensor] = None,
217 memory_audio_timestep: Optional[torch.Tensor] = None,
218 memory_audio_segment_lengths: tuple[tuple[int, ...], ...] | None = None,
219 paired_audio_memory: bool = False,
220 v2a_grad_scale: float = 1.0,
221 memory_position_mode: str = "reference",
222 ) -> Tuple[torch.Tensor, torch.Tensor]:
223 batch_size = video_noise.shape[0]
224 num_video_frames = video_noise.shape[1]
225 num_audio_frames = audio_noise.shape[1]
226 device = video_noise.device
227 dtype = video_noise.dtype
228 memory_video = memory_video.to(device=device, dtype=dtype)
229 if memory_audio is not None:
230 memory_audio = memory_audio.to(device=device, dtype=dtype)
231 if memory_audio_timestep is not None:
232 memory_audio_timestep = memory_audio_timestep.to(device=device, dtype=dtype)
233
234 video_trajectory = [video_noise]
235 audio_trajectory = [audio_noise]
236 noisy_video = video_noise
237 noisy_audio = audio_noise
238 memory_audio_kwargs = (
239 {
240 "memory_audio": memory_audio,
241 "memory_audio_timestep": memory_audio_timestep,
242 }
243 if memory_audio is not None or memory_audio_timestep is not None
244 else {}
245 )
246 paired_memory_kwargs = (
247 {
248 "memory_audio_segment_lengths": memory_audio_segment_lengths,
249 "paired_audio_memory": True,
250 "v2a_grad_scale": v2a_grad_scale,
251 "memory_position_mode": memory_position_mode,
252 }
253 if paired_audio_memory
254 else {}
255 )
256
257 for idx, sigma in enumerate(self.denoising_sigmas[:-1]):
258 video_sigma = sigma * torch.ones([batch_size, num_video_frames], device=device, dtype=dtype)
259 audio_sigma = sigma * torch.ones([batch_size, num_audio_frames], device=device, dtype=dtype)
260
261 pred_video, pred_audio = self.generator(
262 noisy_image_or_video=noisy_video,
263 conditional_dict=conditional_dict,
264 timestep=video_sigma,
265 noisy_audio=noisy_audio,
266 audio_timestep=audio_sigma,
267 memory_video=memory_video,
268 memory_downscale_factor=self.memory_downscale_factor,
269 **memory_audio_kwargs,
270 **paired_memory_kwargs,
271 )
272 pred_video = pred_video.to(dtype=dtype)
273 pred_audio = pred_audio.to(dtype=dtype)
274 if self.audio_latent_clamp > 0:
275 pred_audio = pred_audio.clamp(-self.audio_latent_clamp, self.audio_latent_clamp)
276
277 next_sigma = self.denoising_sigmas[idx + 1]
278 if next_sigma > 0:
279 fresh_noise_video = torch.randn_like(video_noise)
280 fresh_noise_audio = torch.randn_like(audio_noise)
281 next_video_sigma = next_sigma * torch.ones([batch_size, num_video_frames], device=device, dtype=dtype)
282 next_audio_sigma = next_sigma * torch.ones([batch_size, num_audio_frames], device=device, dtype=dtype)
283 noisy_video = self.add_noise_fn(
284 pred_video.flatten(0, 1),
285 fresh_noise_video.flatten(0, 1),
286 next_video_sigma.flatten(0, 1),
287 ).unflatten(0, (batch_size, num_video_frames)).to(dtype=dtype)
288 noisy_audio = self.add_noise_fn(
289 pred_audio,
290 fresh_noise_audio,
291 next_audio_sigma,
292 ).to(dtype=dtype)
293 else:
294 noisy_video = pred_video
295 noisy_audio = pred_audio
296
297 video_trajectory.append(noisy_video)
298 audio_trajectory.append(noisy_audio)
299
300 return torch.stack(video_trajectory, dim=1), torch.stack(audio_trajectory, dim=1)
301
302
303 class BidirectionalMemoryAVInferencePipeline:
304 """
305 Few-step benchmark/inference pipeline for video-memory-conditioned AV generation.
306 """
307
308 def __init__(
309 self,
310 generator: nn.Module,
311 add_noise_fn,
312 denoising_sigmas: torch.Tensor,
313 memory_downscale_factor: int = 1,
314 ) -> None:
315 self.generator = generator
316 self.add_noise_fn = add_noise_fn
317 self.denoising_sigmas = denoising_sigmas
318 self.memory_downscale_factor = int(memory_downscale_factor)
319
320 @torch.no_grad()
321 def generate(
322 self,
323 video_shape: Tuple[int, ...],
324 audio_shape: Tuple[int, ...],
325 conditional_dict: Dict[str, Any],
326 memory_video: torch.Tensor,
327 memory_audio: Optional[torch.Tensor] = None,
328 memory_audio_timestep: Optional[torch.Tensor] = None,
329 memory_audio_segment_lengths: tuple[tuple[int, ...], ...] | None = None,
330 paired_audio_memory: bool = False,
331 v2a_grad_scale: float = 1.0,
332 memory_position_mode: str = "reference",
333 seed: Optional[int] = None,
334 ) -> Tuple[torch.Tensor, torch.Tensor]:
335 batch_size = video_shape[0]
336 num_video_frames = video_shape[1]
337 num_audio_frames = audio_shape[1]
338
339 if seed is not None:
340 torch.manual_seed(seed)
341
342 device = next(self.generator.parameters()).device
343 dtype = next(self.generator.parameters()).dtype
344
345 video = torch.randn(video_shape, device=device, dtype=dtype)
346 audio = torch.randn(audio_shape, device=device, dtype=dtype)
347 memory_video = memory_video.to(device=device, dtype=dtype)
348 if memory_audio is not None:
349 memory_audio = memory_audio.to(device=device, dtype=dtype)
350 if memory_audio_timestep is not None:
351 memory_audio_timestep = memory_audio_timestep.to(device=device, dtype=dtype)
352 memory_audio_kwargs = (
353 {
354 "memory_audio": memory_audio,
355 "memory_audio_timestep": memory_audio_timestep,
356 }
357 if memory_audio is not None or memory_audio_timestep is not None
358 else {}
359 )
360 paired_memory_kwargs = (
361 {
362 "memory_audio_segment_lengths": memory_audio_segment_lengths,
363 "paired_audio_memory": True,
364 "v2a_grad_scale": v2a_grad_scale,
365 "memory_position_mode": memory_position_mode,
366 }
367 if paired_audio_memory
368 else {}
369 )
370
371 for idx, sigma in enumerate(self.denoising_sigmas[:-1]):
372 video_sigma = sigma * torch.ones([batch_size, num_video_frames], device=device, dtype=dtype)
373 audio_sigma = sigma * torch.ones([batch_size, num_audio_frames], device=device, dtype=dtype)
374
375 pred_video, pred_audio = self.generator(
376 noisy_image_or_video=video,
377 conditional_dict=conditional_dict,
378 timestep=video_sigma,
379 noisy_audio=audio,
380 audio_timestep=audio_sigma,
381 memory_video=memory_video,
382 memory_downscale_factor=self.memory_downscale_factor,
383 **memory_audio_kwargs,
384 **paired_memory_kwargs,
385 )
386 pred_video = pred_video.to(dtype=dtype)
387 pred_audio = pred_audio.to(dtype=dtype)
388
389 next_sigma = self.denoising_sigmas[idx + 1]
390 if next_sigma > 0:
391 fresh_noise_video = torch.randn_like(video)
392 fresh_noise_audio = torch.randn_like(audio)
393 next_video_sigma = next_sigma * torch.ones([batch_size, num_video_frames], device=device, dtype=dtype)
394 next_audio_sigma = next_sigma * torch.ones([batch_size, num_audio_frames], device=device, dtype=dtype)
395 video = self.add_noise_fn(
396 pred_video.flatten(0, 1),
397 fresh_noise_video.flatten(0, 1),
398 next_video_sigma.flatten(0, 1),
399 ).unflatten(0, (batch_size, num_video_frames)).to(dtype=dtype)
400 audio = self.add_noise_fn(pred_audio, fresh_noise_audio, next_audio_sigma).to(dtype=dtype)
401 else:
402 video = pred_video
403 audio = pred_audio
404
405 return video, audio
406
406 lines PYTHON