mehdiPrunaAI commited on
Commit
dd9b435
·
verified ·
1 Parent(s): 4efb00a

Refining demo

Browse files
Files changed (1) hide show
  1. demo/demo_distilled_decode.py +42 -34
demo/demo_distilled_decode.py CHANGED
@@ -2,7 +2,8 @@
2
  """Quick demo: generate a short LTX-2.3 video latent, then decode it twice.
3
 
4
  Compares the stock LTX-2.3 VAE decoder with this repo's pruned decoder
5
- (PrunaVAED) on the *same* latent. Prints decode time (ms) and writes two mp4s.
 
6
 
7
  Requires a CUDA GPU. From the repo root:
8
 
@@ -28,24 +29,25 @@ from diffusers.pipelines.ltx2.utils import (
28
  STAGE_2_DISTILLED_SIGMA_VALUES,
29
  )
30
 
 
 
 
 
 
 
31
  torch.backends.cuda.enable_cudnn_sdp(False)
32
 
33
  # ---------------------------------------------------------------------------
34
  # Settings (edit these if you want)
35
  # ---------------------------------------------------------------------------
36
 
37
- REPO_ROOT = Path(__file__).resolve().parents[1]
38
- sys.path.insert(0, str(REPO_ROOT))
39
-
40
- from patch_diffusers import patch_pruna_ltx2_decoder # noqa: E402
41
-
42
  # Official Diffusers checkpoints for the 2-stage distilled recipe.
43
  DISTILLED_MODEL = "diffusers/LTX-2.3-Distilled-Diffusers"
44
  SPATIAL_UPSAMPLER = "dg845/LTX-2.3-Spatial-Upsampler-Diffusers"
45
  LTX23_VAE = "diffusers/LTX-2.3-Diffusers" # stock decoder (baseline)
46
- PRUNED_VAE = REPO_ROOT # this repo's vae/ folder
47
 
48
- # ~720p for 5 s @ 24 fps. Height/width must be multiples of 64 (2-stage).
49
  HEIGHT, WIDTH, NUM_FRAMES, FPS = 1088, 1920, 121, 24.0
50
  SEED = 42
51
  DECODE_WARMUP, DECODE_RUNS = 1, 3 # timing: 1 warm-up + median of 3 runs
@@ -63,46 +65,51 @@ OUTPUT_DIR = REPO_ROOT / "outputs" / "demo_distilled"
63
  # Helpers
64
  # ---------------------------------------------------------------------------
65
 
66
- def save_mp4(video: torch.Tensor, path: Path) -> None:
67
  """Save a BCTHW tensor in [-1, 1] as an H.264 mp4."""
68
- frames = video[0].permute(1, 2, 3, 0).clamp(-1, 1)
69
- frames = ((frames + 1) / 2 * 255).byte().cpu().numpy()
70
  path.parent.mkdir(parents=True, exist_ok=True)
71
- iio.imwrite(path, frames, fps=FPS, codec="libx264")
72
 
73
 
74
  def load_vae(model_id: str) -> AutoencoderKLLTX2Video:
75
- """Load a video VAE decoder and put it on GPU."""
76
  vae = AutoencoderKLLTX2Video.from_pretrained(
77
  model_id, subfolder="vae", torch_dtype=DTYPE
78
  )
79
- vae = vae.to(DEVICE).eval()
80
- vae.use_tiling = False
81
- if hasattr(vae, "disable_tiling"):
82
- vae.disable_tiling()
83
- return vae
84
 
85
 
86
  @torch.inference_mode()
87
- def timed_decode(vae: AutoencoderKLLTX2Video, latent: torch.Tensor) -> tuple[torch.Tensor, float]:
88
- """Decode once after warm-up; return (video on CPU, median latency in ms)."""
 
 
 
 
 
 
 
89
  latent = latent.to(DEVICE, DTYPE)
90
 
91
- with torch.autocast(DEVICE, dtype=DTYPE):
92
- for _ in range(DECODE_WARMUP):
93
- vae.decode(latent, return_dict=False)
94
 
 
 
95
  times_ms = []
96
- video = None
97
  for _ in range(DECODE_RUNS):
 
98
  torch.cuda.synchronize()
99
  t0 = time.perf_counter()
100
- with torch.autocast(DEVICE, dtype=DTYPE):
101
- video = vae.decode(latent, return_dict=False)[0]
102
  torch.cuda.synchronize()
103
  times_ms.append((time.perf_counter() - t0) * 1000)
104
 
105
- return video.cpu(), statistics.median(times_ms)
 
106
 
107
 
108
  @torch.inference_mode()
@@ -180,23 +187,24 @@ def generate_latent(prompt: str) -> torch.Tensor:
180
 
181
 
182
  def compare_decoders(latent: torch.Tensor, out_dir: Path) -> None:
183
- """Decode the same latent with LTX-2.3 and PrunaVAED; print ms and save mp4s."""
184
  # Needed so Diffusers can build the pruned decoder graph correctly.
185
  patch_pruna_ltx2_decoder()
186
 
187
  results = {}
188
- for name, model_id in (("ltx23", LTX23_VAE), ("prunavaed", str(PRUNED_VAE))):
189
  print(f"Decoding with {name} …")
190
  vae = load_vae(model_id)
191
- video, ms = timed_decode(vae, latent)
192
- results[name] = ms
193
  path = out_dir / f"{name}.mp4"
194
- save_mp4(video, path)
195
- print(f" {name}: {ms:.1f} ms → {path}")
196
  del vae, video
197
  torch.cuda.empty_cache()
198
 
199
- print(f"Speedup: {results['ltx23'] / results['prunavaed']:.2f}×")
 
200
 
201
 
202
  # ---------------------------------------------------------------------------
 
2
  """Quick demo: generate a short LTX-2.3 video latent, then decode it twice.
3
 
4
  Compares the stock LTX-2.3 VAE decoder with this repo's pruned decoder
5
+ (PrunaVAED) on the *same* latent. Prints decode time (ms) and peak VRAM (GiB),
6
+ and writes two mp4s.
7
 
8
  Requires a CUDA GPU. From the repo root:
9
 
 
29
  STAGE_2_DISTILLED_SIGMA_VALUES,
30
  )
31
 
32
+ REPO_ROOT = Path(__file__).resolve().parents[1]
33
+ sys.path.insert(0, str(REPO_ROOT))
34
+
35
+ from patch_diffusers import patch_pruna_ltx2_decoder # noqa: E402
36
+
37
+
38
  torch.backends.cuda.enable_cudnn_sdp(False)
39
 
40
  # ---------------------------------------------------------------------------
41
  # Settings (edit these if you want)
42
  # ---------------------------------------------------------------------------
43
 
 
 
 
 
 
44
  # Official Diffusers checkpoints for the 2-stage distilled recipe.
45
  DISTILLED_MODEL = "diffusers/LTX-2.3-Distilled-Diffusers"
46
  SPATIAL_UPSAMPLER = "dg845/LTX-2.3-Spatial-Upsampler-Diffusers"
47
  LTX23_VAE = "diffusers/LTX-2.3-Diffusers" # stock decoder (baseline)
48
+ PRUNED_VAE = str(REPO_ROOT) # this repo's vae/ folder
49
 
50
+ # ~1080p for 5 s @ 24 fps. Height/width must be multiples of 64 (2-stage).
51
  HEIGHT, WIDTH, NUM_FRAMES, FPS = 1088, 1920, 121, 24.0
52
  SEED = 42
53
  DECODE_WARMUP, DECODE_RUNS = 1, 3 # timing: 1 warm-up + median of 3 runs
 
65
  # Helpers
66
  # ---------------------------------------------------------------------------
67
 
68
+ def save_mp4(video: torch.Tensor, path: Path, fps: float) -> None:
69
  """Save a BCTHW tensor in [-1, 1] as an H.264 mp4."""
70
+ frames = video[0].permute(1, 2, 3, 0).clamp(-1, 1).float()
71
+ frames = ((frames + 1) / 2 * 255).round().byte().cpu().numpy()
72
  path.parent.mkdir(parents=True, exist_ok=True)
73
+ iio.imwrite(path, frames, fps=fps, codec="libx264")
74
 
75
 
76
  def load_vae(model_id: str) -> AutoencoderKLLTX2Video:
77
+ """Load a video VAE decoder on GPU, tiling off so we time full-frame decodes."""
78
  vae = AutoencoderKLLTX2Video.from_pretrained(
79
  model_id, subfolder="vae", torch_dtype=DTYPE
80
  )
81
+ vae.disable_tiling()
82
+ return vae.to(DEVICE).eval()
 
 
 
83
 
84
 
85
  @torch.inference_mode()
86
+ def timed_decode(
87
+ vae: AutoencoderKLLTX2Video, latent: torch.Tensor
88
+ ) -> tuple[torch.Tensor, float, float]:
89
+ """Decode DECODE_RUNS times after warm-up.
90
+
91
+ Returns the video on CPU, the median latency in ms and the peak VRAM in GiB.
92
+ Deliberately no autocast: weights and latent are already bfloat16, and autocast
93
+ would run every PerChannelRMSNorm (``x**2``) in float32, costing time and VRAM.
94
+ """
95
  latent = latent.to(DEVICE, DTYPE)
96
 
97
+ for _ in range(DECODE_WARMUP):
98
+ vae.decode(latent, return_dict=False)
 
99
 
100
+ torch.cuda.synchronize()
101
+ torch.cuda.reset_peak_memory_stats()
102
  times_ms = []
 
103
  for _ in range(DECODE_RUNS):
104
+ video = None # keep the previous output out of the peak VRAM measurement
105
  torch.cuda.synchronize()
106
  t0 = time.perf_counter()
107
+ video = vae.decode(latent, return_dict=False)[0]
 
108
  torch.cuda.synchronize()
109
  times_ms.append((time.perf_counter() - t0) * 1000)
110
 
111
+ peak_gib = torch.cuda.max_memory_allocated() / 2**30
112
+ return video.cpu(), statistics.median(times_ms), peak_gib
113
 
114
 
115
  @torch.inference_mode()
 
187
 
188
 
189
  def compare_decoders(latent: torch.Tensor, out_dir: Path) -> None:
190
+ """Decode the same latent with LTX-2.3 and PrunaVAED; print ms/VRAM and save mp4s."""
191
  # Needed so Diffusers can build the pruned decoder graph correctly.
192
  patch_pruna_ltx2_decoder()
193
 
194
  results = {}
195
+ for name, model_id in (("ltx23", LTX23_VAE), ("prunavaed", PRUNED_VAE)):
196
  print(f"Decoding with {name} …")
197
  vae = load_vae(model_id)
198
+ video, ms, peak_gib = timed_decode(vae, latent)
199
+ results[name] = (ms, peak_gib)
200
  path = out_dir / f"{name}.mp4"
201
+ save_mp4(video, path, FPS)
202
+ print(f" {name}: {ms:.1f} ms · {peak_gib:.2f} GiB peak → {path}")
203
  del vae, video
204
  torch.cuda.empty_cache()
205
 
206
+ (ltx_ms, ltx_gib), (pruna_ms, pruna_gib) = results["ltx23"], results["prunavaed"]
207
+ print(f"Speedup: {ltx_ms / pruna_ms:.2f}× Peak VRAM: {pruna_gib / ltx_gib:.0%} of LTX-2.3")
208
 
209
 
210
  # ---------------------------------------------------------------------------