alex commited on
Commit
7787c31
·
1 Parent(s): e3cddce

tea_cache

Browse files
Files changed (3) hide show
  1. app.py +14 -9
  2. wan/animate.py +60 -1
  3. wan/modules/animate/model_animate.py +47 -15
app.py CHANGED
@@ -258,23 +258,23 @@ def calculate_time_required(max_duration_s, rc_bool):
258
  if max_duration_s == -1:
259
  return 75
260
  if max_duration_s == 2:
261
- return 110
262
  elif max_duration_s == 3:
263
- return 170
264
  elif max_duration_s == 4:
265
- return 180
266
  elif max_duration_s == 5:
267
- return 190
268
  elif max_duration_s == 6:
269
- return 250
270
  elif max_duration_s == 7:
271
- return 260
272
  elif max_duration_s == 8:
273
- return 320
274
  elif max_duration_s == 9:
275
- return 330
276
  elif max_duration_s == 10:
277
- return 340
278
 
279
  def get_display_time_required(max_duration_s, rc_bool):
280
  # the 30 seconds extra is just for saftey in case of a unexpected slow down
@@ -484,6 +484,8 @@ def get_sam_mask(prompt_state, frames, frame_idx, input_points, input_labels):
484
  def animate_scene(input_video, max_duration_s, edited_frame, rc_str,
485
  prompt_state=None, session_id=None,
486
  progress=gr.Progress(track_tqdm=True)):
 
 
487
 
488
  # Build dicts keyed by frame index, or empty if none
489
  if not prompt_state or "selections" not in prompt_state:
@@ -546,6 +548,9 @@ def animate_scene(input_video, max_duration_s, edited_frame, rc_str,
546
  else:
547
  final_video_path = output_video_path
548
 
 
 
 
549
  print(f"task for {session_id} finalised")
550
  return final_video_path, pose_video, bg_video, mask_video, face_video
551
 
 
258
  if max_duration_s == -1:
259
  return 75
260
  if max_duration_s == 2:
261
+ return 90
262
  elif max_duration_s == 3:
263
+ return 150
264
  elif max_duration_s == 4:
265
+ return 150
266
  elif max_duration_s == 5:
267
+ return 150
268
  elif max_duration_s == 6:
269
+ return 210
270
  elif max_duration_s == 7:
271
+ return 220
272
  elif max_duration_s == 8:
273
+ return 260
274
  elif max_duration_s == 9:
275
+ return 270
276
  elif max_duration_s == 10:
277
+ return 270
278
 
279
  def get_display_time_required(max_duration_s, rc_bool):
280
  # the 30 seconds extra is just for saftey in case of a unexpected slow down
 
484
  def animate_scene(input_video, max_duration_s, edited_frame, rc_str,
485
  prompt_state=None, session_id=None,
486
  progress=gr.Progress(track_tqdm=True)):
487
+
488
+ start_preprocess = time.time()
489
 
490
  # Build dicts keyed by frame index, or empty if none
491
  if not prompt_state or "selections" not in prompt_state:
 
548
  else:
549
  final_video_path = output_video_path
550
 
551
+ preprocess_time = time.time() - start_preprocess
552
+ print(f"total time for duration {max_duration_s} took {preprocess_time:.2f} seconds")
553
+
554
  print(f"task for {session_id} finalised")
555
  return final_video_path, pose_video, bg_video, mask_video, face_video
556
 
wan/animate.py CHANGED
@@ -611,6 +611,15 @@ class WanAnimate:
611
  "face_pixel_values": face_pixel_values_uncond,
612
  }
613
 
 
 
 
 
 
 
 
 
 
614
  for i, t in enumerate(timesteps):
615
  latent_model_input = latents
616
  timestep = [t]
@@ -618,7 +627,7 @@ class WanAnimate:
618
  timestep = torch.stack(timestep)
619
 
620
  noise_pred_cond = TensorList(
621
- self.noise_model(TensorList(latent_model_input), t=timestep, **arg_c)
622
  )
623
 
624
  if guide_scale > 1:
@@ -661,3 +670,53 @@ class WanAnimate:
661
 
662
  videos = torch.cat(all_out_frames, dim=2)[:, :, :real_frame_len]
663
  return videos[0] if self.rank == 0 else None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  "face_pixel_values": face_pixel_values_uncond,
612
  }
613
 
614
+
615
+ tea_cache_posi = {
616
+ "tea_cache": TeaCache(
617
+ num_inference_steps=sampling_steps,
618
+ rel_l1_thresh=0.3,
619
+ timestep_scale=float(self.num_train_timesteps),
620
+ )
621
+ }
622
+
623
  for i, t in enumerate(timesteps):
624
  latent_model_input = latents
625
  timestep = [t]
 
627
  timestep = torch.stack(timestep)
628
 
629
  noise_pred_cond = TensorList(
630
+ self.noise_model(TensorList(latent_model_input), t=timestep, **tea_cache_posi, **arg_c)
631
  )
632
 
633
  if guide_scale > 1:
 
670
 
671
  videos = torch.cat(all_out_frames, dim=2)[:, :, :real_frame_len]
672
  return videos[0] if self.rank == 0 else None
673
+
674
+ class TeaCache:
675
+ def __init__(self, num_inference_steps, rel_l1_thresh, timestep_scale=1000.0):
676
+ self.num_inference_steps = num_inference_steps
677
+ self.rel_l1_thresh = rel_l1_thresh
678
+ self.timestep_scale = timestep_scale
679
+
680
+ self.step = 0
681
+ self.accumulated_rel_l1_distance = 0.0
682
+ self.previous_modulated_input = None
683
+ self.previous_residual = None
684
+ self.previous_hidden_states = None
685
+
686
+ def check(self, x, t_scalar):
687
+ # scheduler-agnostic timestep-aware weighting
688
+ # works for UniPC / flow schedulers that only expose t
689
+ w = float(t_scalar) / max(self.timestep_scale, 1.0)
690
+ modulated_input = x * w
691
+
692
+ if self.step == 0 or self.step == self.num_inference_steps - 1:
693
+ should_calc = True
694
+ self.accumulated_rel_l1_distance = 0.0
695
+ else:
696
+ diff = torch.abs(modulated_input - self.previous_modulated_input)
697
+ ref = torch.abs(self.previous_modulated_input)
698
+ relative_l1 = diff.sum() / (ref.sum() + 1e-8)
699
+
700
+ self.accumulated_rel_l1_distance += relative_l1.item()
701
+ should_calc = self.accumulated_rel_l1_distance >= self.rel_l1_thresh
702
+
703
+ if should_calc:
704
+ self.accumulated_rel_l1_distance = 0.0
705
+
706
+ self.previous_modulated_input = modulated_input.detach()
707
+
708
+ self.step += 1
709
+ if self.step == self.num_inference_steps:
710
+ self.step = 0
711
+
712
+ if should_calc:
713
+ self.previous_hidden_states = x.clone()
714
+
715
+ return not should_calc
716
+
717
+ def store(self, hidden_states):
718
+ self.previous_residual = hidden_states - self.previous_hidden_states
719
+ self.previous_hidden_states = None
720
+
721
+ def update(self, hidden_states):
722
+ return hidden_states + self.previous_residual
wan/modules/animate/model_animate.py CHANGED
@@ -375,6 +375,7 @@ class WanAnimateModel(ModelMixin, ConfigMixin, PeftAdapterMixin):
375
  x,
376
  t,
377
  clip_fea,
 
378
  context,
379
  seq_len,
380
  y=None,
@@ -391,14 +392,17 @@ class WanAnimateModel(ModelMixin, ConfigMixin, PeftAdapterMixin):
391
  x = [self.patch_embedding(u.unsqueeze(0)) for u in x]
392
  x, motion_vec = self.after_patch_embedding(x, pose_latents, face_pixel_values)
393
 
 
394
  grid_sizes = torch.stack(
395
- [torch.tensor(u.shape[2:], dtype=torch.long) for u in x])
 
396
  x = [u.flatten(2).transpose(1, 2) for u in x]
397
- seq_lens = torch.tensor([u.size(1) for u in x], dtype=torch.long)
398
  assert seq_lens.max() <= seq_len
 
399
  x = torch.cat([
400
- torch.cat([u, u.new_zeros(1, seq_len - u.size(1), u.size(2))],
401
- dim=1) for u in x
402
  ])
403
 
404
  # time embeddings
@@ -413,36 +417,64 @@ class WanAnimateModel(ModelMixin, ConfigMixin, PeftAdapterMixin):
413
  context_lens = None
414
  context = self.text_embedding(
415
  torch.stack([
416
- torch.cat(
417
- [u, u.new_zeros(self.text_len - u.size(0), u.size(1))])
418
  for u in context
419
- ]))
 
420
 
421
  if self.use_img_emb:
422
- context_clip = self.img_emb(clip_fea) # bs x 257 x dim
423
  context = torch.concat([context_clip, context], dim=1)
424
 
425
- # arguments
426
  kwargs = dict(
427
  e=e0,
428
  seq_lens=seq_lens,
429
  grid_sizes=grid_sizes,
430
  freqs=freqs,
431
  context=context,
432
- context_lens=context_lens)
 
433
 
434
- if self.use_context_parallel:
435
- x = torch.chunk(x, get_world_size(), dim=1)[get_rank()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
- for idx, block in enumerate(self.blocks):
438
- x = block(x, **kwargs)
439
- x = self.after_transformer_block(idx, x, motion_vec)
 
440
 
441
  # head
442
  x = self.head(x, e)
443
 
444
  if self.use_context_parallel:
445
  x = gather_forward(x, dim=1)
 
446
 
447
  # unpatchify
448
  x = self.unpatchify(x, grid_sizes)
 
375
  x,
376
  t,
377
  clip_fea,
378
+ tea_cache,
379
  context,
380
  seq_len,
381
  y=None,
 
392
  x = [self.patch_embedding(u.unsqueeze(0)) for u in x]
393
  x, motion_vec = self.after_patch_embedding(x, pose_latents, face_pixel_values)
394
 
395
+ device = x[0].device
396
  grid_sizes = torch.stack(
397
+ [torch.tensor(u.shape[2:], dtype=torch.long, device=device) for u in x]
398
+ )
399
  x = [u.flatten(2).transpose(1, 2) for u in x]
400
+ seq_lens = torch.tensor([u.size(1) for u in x], dtype=torch.long, device=device)
401
  assert seq_lens.max() <= seq_len
402
+
403
  x = torch.cat([
404
+ torch.cat([u, u.new_zeros(1, seq_len - u.size(1), u.size(2))], dim=1)
405
+ for u in x
406
  ])
407
 
408
  # time embeddings
 
417
  context_lens = None
418
  context = self.text_embedding(
419
  torch.stack([
420
+ torch.cat([u, u.new_zeros(self.text_len - u.size(0), u.size(1))])
 
421
  for u in context
422
+ ])
423
+ )
424
 
425
  if self.use_img_emb:
426
+ context_clip = self.img_emb(clip_fea)
427
  context = torch.concat([context_clip, context], dim=1)
428
 
 
429
  kwargs = dict(
430
  e=e0,
431
  seq_lens=seq_lens,
432
  grid_sizes=grid_sizes,
433
  freqs=freqs,
434
  context=context,
435
+ context_lens=context_lens,
436
+ )
437
 
438
+ # tea cache check must happen after e0 exists
439
+ ori_x_len = x.shape[1]
440
+ if tea_cache is not None:
441
+ t_scalar = int(t.flatten()[0].item())
442
+ tea_cache_update = tea_cache.check(x, t_scalar)
443
+ else:
444
+ tea_cache_update = False
445
+
446
+ if tea_cache_update:
447
+ x = tea_cache.update(x)
448
+ else:
449
+ if self.use_context_parallel:
450
+ world_size = get_world_size()
451
+ rank = get_rank()
452
+
453
+ pad_size = 0
454
+ if x.shape[1] % world_size != 0:
455
+ pad_size = world_size - (x.shape[1] % world_size)
456
+ x = torch.cat(
457
+ [x, x.new_zeros(x.shape[0], pad_size, x.shape[2])],
458
+ dim=1
459
+ )
460
+
461
+ x = torch.chunk(x, world_size, dim=1)[rank]
462
+
463
+ for idx, block in enumerate(self.blocks):
464
+ x = block(x, **kwargs)
465
+ x = self.after_transformer_block(idx, x, motion_vec)
466
 
467
+ if tea_cache is not None:
468
+ x_cache = gather_forward(x, dim=1) if self.use_context_parallel else x
469
+ x_cache = x_cache[:, :ori_x_len]
470
+ tea_cache.store(x_cache)
471
 
472
  # head
473
  x = self.head(x, e)
474
 
475
  if self.use_context_parallel:
476
  x = gather_forward(x, dim=1)
477
+ x = x[:, :ori_x_len]
478
 
479
  # unpatchify
480
  x = self.unpatchify(x, grid_sizes)