Spaces:
Paused
Paused
perf: optional First-Block-Cache "Faster" toggle (~1.4x, A/B-verified near-clean); default off
Browse files
app.py
CHANGED
|
@@ -55,6 +55,10 @@ REWRITER_MODEL = os.environ.get("REWRITER_MODEL", "Qwen/Qwen3.6-27B:deepinfra")
|
|
| 55 |
# forward per step on the low-noise tail (~15% fewer transformer forwards).
|
| 56 |
# 1.0 disables the optimization (standard CFG on every step).
|
| 57 |
CFG_STOP = float(os.environ.get("LINGBOT_CFG_STOP", "0.7"))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
# (height, width), multiples of 16 — official 480p buckets.
|
| 60 |
VIDEO_SIZES = {
|
|
@@ -292,9 +296,10 @@ def _run_pipe(target, **call_kwargs):
|
|
| 292 |
|
| 293 |
@spaces.GPU(duration=_video_gpu_duration, size="xlarge")
|
| 294 |
def _gpu_generate_video(caption, height, width, num_frames, steps, guidance_scale, shift,
|
| 295 |
-
negative_prompt, seed, image=None):
|
| 296 |
target = pipe_i2v if image is not None else pipe
|
| 297 |
kwargs = {"image": image} if image is not None else {}
|
|
|
|
| 298 |
t0 = time.perf_counter()
|
| 299 |
output, cfg_mode = _run_pipe(
|
| 300 |
target,
|
|
@@ -307,6 +312,9 @@ def _gpu_generate_video(caption, height, width, num_frames, steps, guidance_scal
|
|
| 307 |
guidance_scale=float(guidance_scale),
|
| 308 |
shift=float(shift),
|
| 309 |
guidance_stop=CFG_STOP,
|
|
|
|
|
|
|
|
|
|
| 310 |
generator=torch.Generator().manual_seed(seed),
|
| 311 |
**kwargs,
|
| 312 |
)
|
|
@@ -325,7 +333,9 @@ def _image_gpu_duration(caption, height, width, steps, *args, **kwargs) -> int:
|
|
| 325 |
|
| 326 |
|
| 327 |
@spaces.GPU(duration=_image_gpu_duration, size="xlarge")
|
| 328 |
-
def _gpu_generate_image(caption, height, width, steps, guidance_scale, shift, negative_prompt,
|
|
|
|
|
|
|
| 329 |
t0 = time.perf_counter()
|
| 330 |
output, cfg_mode = _run_pipe(
|
| 331 |
pipe,
|
|
@@ -338,6 +348,9 @@ def _gpu_generate_image(caption, height, width, steps, guidance_scale, shift, ne
|
|
| 338 |
guidance_scale=float(guidance_scale),
|
| 339 |
shift=float(shift),
|
| 340 |
guidance_stop=CFG_STOP,
|
|
|
|
|
|
|
|
|
|
| 341 |
generator=torch.Generator().manual_seed(seed),
|
| 342 |
)
|
| 343 |
torch.cuda.synchronize()
|
|
@@ -354,6 +367,7 @@ def generate_t2v(
|
|
| 354 |
steps,
|
| 355 |
guidance_scale,
|
| 356 |
shift,
|
|
|
|
| 357 |
negative_prompt,
|
| 358 |
seed,
|
| 359 |
randomize_seed,
|
|
@@ -367,7 +381,8 @@ def generate_t2v(
|
|
| 367 |
seed = _resolve_seed(seed, randomize_seed)
|
| 368 |
caption = _prepare_caption(prompt, "t2v", duration_s, enhance)
|
| 369 |
video = _gpu_generate_video(
|
| 370 |
-
caption, height, width, num_frames, steps, guidance_scale, shift, negative_prompt, seed
|
|
|
|
| 371 |
)
|
| 372 |
return video, seed, caption
|
| 373 |
|
|
@@ -380,6 +395,7 @@ def generate_i2v(
|
|
| 380 |
steps,
|
| 381 |
guidance_scale,
|
| 382 |
shift,
|
|
|
|
| 383 |
negative_prompt,
|
| 384 |
seed,
|
| 385 |
randomize_seed,
|
|
@@ -396,7 +412,7 @@ def generate_i2v(
|
|
| 396 |
caption = _prepare_caption(prompt, "ti2v", duration_s, enhance, image=image)
|
| 397 |
video = _gpu_generate_video(
|
| 398 |
caption, height, width, num_frames, steps, guidance_scale, shift, negative_prompt, seed,
|
| 399 |
-
image=image,
|
| 400 |
)
|
| 401 |
return video, seed, caption
|
| 402 |
|
|
@@ -407,6 +423,7 @@ def generate_t2i(
|
|
| 407 |
steps,
|
| 408 |
guidance_scale,
|
| 409 |
shift,
|
|
|
|
| 410 |
negative_prompt,
|
| 411 |
seed,
|
| 412 |
randomize_seed,
|
|
@@ -417,7 +434,8 @@ def generate_t2i(
|
|
| 417 |
seed = _resolve_seed(seed, randomize_seed)
|
| 418 |
caption = _prepare_caption(prompt, "t2i", None, enhance)
|
| 419 |
image = _gpu_generate_image(
|
| 420 |
-
caption, height, width, steps, guidance_scale, shift, negative_prompt, seed
|
|
|
|
| 421 |
)
|
| 422 |
return image, seed, caption
|
| 423 |
|
|
@@ -535,26 +553,30 @@ def _advanced(negative_default: str, steps_value: int):
|
|
| 535 |
steps = gr.Slider(label="Inference steps", minimum=4, maximum=40, step=1, value=steps_value)
|
| 536 |
guidance = gr.Slider(label="Guidance scale", minimum=1.0, maximum=10.0, step=0.5, value=3.0)
|
| 537 |
shift = gr.Slider(label="Flow shift", minimum=1.0, maximum=9.0, step=0.5, value=3.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
negative = gr.Textbox(label="Negative prompt", value=negative_default, lines=3)
|
| 539 |
seed, randomize = _seed_row()
|
| 540 |
-
return steps, guidance, shift, negative, seed, randomize
|
| 541 |
|
| 542 |
|
| 543 |
# Example runners: examples carry only the natural-language prompt (and first
|
| 544 |
# frame for I2V) — enhancement is on and every other control keeps its default,
|
| 545 |
# so the examples table stays to one or two clean columns instead of ten.
|
| 546 |
def _example_t2v(prompt):
|
| 547 |
-
return generate_t2v(prompt, "832 × 480 (16:9)", 2.0, 24, 3.0, 3.0,
|
| 548 |
DEFAULT_NEGATIVE_PROMPT, 42, False, True)
|
| 549 |
|
| 550 |
|
| 551 |
def _example_i2v(image, prompt):
|
| 552 |
-
return generate_i2v(image, prompt, "832 × 480 (16:9)", 2.0, 24, 3.0, 3.0,
|
| 553 |
DEFAULT_NEGATIVE_PROMPT, 42, False, True)
|
| 554 |
|
| 555 |
|
| 556 |
def _example_t2i(prompt):
|
| 557 |
-
return generate_t2i(prompt, "1280 × 736 (16:9, 720p)", 30, 3.0, 3.0,
|
| 558 |
DEFAULT_NEGATIVE_PROMPT_IMAGE, 42, False, True)
|
| 559 |
|
| 560 |
|
|
@@ -609,7 +631,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 609 |
t2v_enhance = gr.Checkbox(
|
| 610 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 611 |
)
|
| 612 |
-
t2v_steps, t2v_guidance, t2v_shift, t2v_negative, t2v_seed, t2v_rand = _advanced(
|
| 613 |
DEFAULT_NEGATIVE_PROMPT, 24
|
| 614 |
)
|
| 615 |
t2v_btn = gr.Button("Generate video", variant="primary")
|
|
@@ -620,7 +642,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 620 |
t2v_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 621 |
|
| 622 |
t2v_inputs = [
|
| 623 |
-
t2v_prompt, t2v_size, t2v_dur, t2v_steps, t2v_guidance, t2v_shift,
|
| 624 |
t2v_negative, t2v_seed, t2v_rand, t2v_enhance,
|
| 625 |
]
|
| 626 |
t2v_outputs = [t2v_out, t2v_seed_out, t2v_caption_out]
|
|
@@ -657,7 +679,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 657 |
i2v_enhance = gr.Checkbox(
|
| 658 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 659 |
)
|
| 660 |
-
i2v_steps, i2v_guidance, i2v_shift, i2v_negative, i2v_seed, i2v_rand = _advanced(
|
| 661 |
DEFAULT_NEGATIVE_PROMPT, 24
|
| 662 |
)
|
| 663 |
i2v_btn = gr.Button("Generate video", variant="primary")
|
|
@@ -668,7 +690,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 668 |
i2v_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 669 |
|
| 670 |
i2v_inputs = [
|
| 671 |
-
i2v_image, i2v_prompt, i2v_size, i2v_dur, i2v_steps, i2v_guidance, i2v_shift,
|
| 672 |
i2v_negative, i2v_seed, i2v_rand, i2v_enhance,
|
| 673 |
]
|
| 674 |
i2v_outputs = [i2v_out, i2v_seed_out, i2v_caption_out]
|
|
@@ -700,7 +722,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 700 |
t2i_enhance = gr.Checkbox(
|
| 701 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 702 |
)
|
| 703 |
-
t2i_steps, t2i_guidance, t2i_shift, t2i_negative, t2i_seed, t2i_rand = _advanced(
|
| 704 |
DEFAULT_NEGATIVE_PROMPT_IMAGE, 30
|
| 705 |
)
|
| 706 |
t2i_btn = gr.Button("Generate image", variant="primary")
|
|
@@ -711,7 +733,7 @@ with gr.Blocks(title="LingBot-Video MoE 30B-A3B") as demo:
|
|
| 711 |
t2i_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 712 |
|
| 713 |
t2i_inputs = [
|
| 714 |
-
t2i_prompt, t2i_size, t2i_steps, t2i_guidance, t2i_shift,
|
| 715 |
t2i_negative, t2i_seed, t2i_rand, t2i_enhance,
|
| 716 |
]
|
| 717 |
t2i_outputs = [t2i_out, t2i_seed_out, t2i_caption_out]
|
|
|
|
| 55 |
# forward per step on the low-noise tail (~15% fewer transformer forwards).
|
| 56 |
# 1.0 disables the optimization (standard CFG on every step).
|
| 57 |
CFG_STOP = float(os.environ.get("LINGBOT_CFG_STOP", "0.7"))
|
| 58 |
+
# "Faster" mode: First-Block-Cache (reuse the block-loop residual on steps whose
|
| 59 |
+
# first-block residual barely changes). (threshold, warmup, max_consecutive).
|
| 60 |
+
# A/B-verified at ~1.4x with near-identical quality and no temporal flicker.
|
| 61 |
+
FAST_CACHE = (0.03, 4, 1)
|
| 62 |
|
| 63 |
# (height, width), multiples of 16 — official 480p buckets.
|
| 64 |
VIDEO_SIZES = {
|
|
|
|
| 296 |
|
| 297 |
@spaces.GPU(duration=_video_gpu_duration, size="xlarge")
|
| 298 |
def _gpu_generate_video(caption, height, width, num_frames, steps, guidance_scale, shift,
|
| 299 |
+
negative_prompt, seed, cache=None, image=None):
|
| 300 |
target = pipe_i2v if image is not None else pipe
|
| 301 |
kwargs = {"image": image} if image is not None else {}
|
| 302 |
+
thr, warmup, maxc = cache if cache else (None, 4, 1)
|
| 303 |
t0 = time.perf_counter()
|
| 304 |
output, cfg_mode = _run_pipe(
|
| 305 |
target,
|
|
|
|
| 312 |
guidance_scale=float(guidance_scale),
|
| 313 |
shift=float(shift),
|
| 314 |
guidance_stop=CFG_STOP,
|
| 315 |
+
fbcache_threshold=thr,
|
| 316 |
+
fbcache_warmup=warmup,
|
| 317 |
+
fbcache_max_consecutive=maxc,
|
| 318 |
generator=torch.Generator().manual_seed(seed),
|
| 319 |
**kwargs,
|
| 320 |
)
|
|
|
|
| 333 |
|
| 334 |
|
| 335 |
@spaces.GPU(duration=_image_gpu_duration, size="xlarge")
|
| 336 |
+
def _gpu_generate_image(caption, height, width, steps, guidance_scale, shift, negative_prompt,
|
| 337 |
+
seed, cache=None):
|
| 338 |
+
thr, warmup, maxc = cache if cache else (None, 4, 1)
|
| 339 |
t0 = time.perf_counter()
|
| 340 |
output, cfg_mode = _run_pipe(
|
| 341 |
pipe,
|
|
|
|
| 348 |
guidance_scale=float(guidance_scale),
|
| 349 |
shift=float(shift),
|
| 350 |
guidance_stop=CFG_STOP,
|
| 351 |
+
fbcache_threshold=thr,
|
| 352 |
+
fbcache_warmup=warmup,
|
| 353 |
+
fbcache_max_consecutive=maxc,
|
| 354 |
generator=torch.Generator().manual_seed(seed),
|
| 355 |
)
|
| 356 |
torch.cuda.synchronize()
|
|
|
|
| 367 |
steps,
|
| 368 |
guidance_scale,
|
| 369 |
shift,
|
| 370 |
+
fast,
|
| 371 |
negative_prompt,
|
| 372 |
seed,
|
| 373 |
randomize_seed,
|
|
|
|
| 381 |
seed = _resolve_seed(seed, randomize_seed)
|
| 382 |
caption = _prepare_caption(prompt, "t2v", duration_s, enhance)
|
| 383 |
video = _gpu_generate_video(
|
| 384 |
+
caption, height, width, num_frames, steps, guidance_scale, shift, negative_prompt, seed,
|
| 385 |
+
cache=FAST_CACHE if fast else None,
|
| 386 |
)
|
| 387 |
return video, seed, caption
|
| 388 |
|
|
|
|
| 395 |
steps,
|
| 396 |
guidance_scale,
|
| 397 |
shift,
|
| 398 |
+
fast,
|
| 399 |
negative_prompt,
|
| 400 |
seed,
|
| 401 |
randomize_seed,
|
|
|
|
| 412 |
caption = _prepare_caption(prompt, "ti2v", duration_s, enhance, image=image)
|
| 413 |
video = _gpu_generate_video(
|
| 414 |
caption, height, width, num_frames, steps, guidance_scale, shift, negative_prompt, seed,
|
| 415 |
+
cache=FAST_CACHE if fast else None, image=image,
|
| 416 |
)
|
| 417 |
return video, seed, caption
|
| 418 |
|
|
|
|
| 423 |
steps,
|
| 424 |
guidance_scale,
|
| 425 |
shift,
|
| 426 |
+
fast,
|
| 427 |
negative_prompt,
|
| 428 |
seed,
|
| 429 |
randomize_seed,
|
|
|
|
| 434 |
seed = _resolve_seed(seed, randomize_seed)
|
| 435 |
caption = _prepare_caption(prompt, "t2i", None, enhance)
|
| 436 |
image = _gpu_generate_image(
|
| 437 |
+
caption, height, width, steps, guidance_scale, shift, negative_prompt, seed,
|
| 438 |
+
cache=FAST_CACHE if fast else None,
|
| 439 |
)
|
| 440 |
return image, seed, caption
|
| 441 |
|
|
|
|
| 553 |
steps = gr.Slider(label="Inference steps", minimum=4, maximum=40, step=1, value=steps_value)
|
| 554 |
guidance = gr.Slider(label="Guidance scale", minimum=1.0, maximum=10.0, step=0.5, value=3.0)
|
| 555 |
shift = gr.Slider(label="Flow shift", minimum=1.0, maximum=9.0, step=0.5, value=3.0)
|
| 556 |
+
fast = gr.Checkbox(
|
| 557 |
+
label="⚡ Faster generation (feature caching, ~1.4× — slightly softer detail)",
|
| 558 |
+
value=False,
|
| 559 |
+
)
|
| 560 |
negative = gr.Textbox(label="Negative prompt", value=negative_default, lines=3)
|
| 561 |
seed, randomize = _seed_row()
|
| 562 |
+
return steps, guidance, shift, fast, negative, seed, randomize
|
| 563 |
|
| 564 |
|
| 565 |
# Example runners: examples carry only the natural-language prompt (and first
|
| 566 |
# frame for I2V) — enhancement is on and every other control keeps its default,
|
| 567 |
# so the examples table stays to one or two clean columns instead of ten.
|
| 568 |
def _example_t2v(prompt):
|
| 569 |
+
return generate_t2v(prompt, "832 × 480 (16:9)", 2.0, 24, 3.0, 3.0, False,
|
| 570 |
DEFAULT_NEGATIVE_PROMPT, 42, False, True)
|
| 571 |
|
| 572 |
|
| 573 |
def _example_i2v(image, prompt):
|
| 574 |
+
return generate_i2v(image, prompt, "832 × 480 (16:9)", 2.0, 24, 3.0, 3.0, False,
|
| 575 |
DEFAULT_NEGATIVE_PROMPT, 42, False, True)
|
| 576 |
|
| 577 |
|
| 578 |
def _example_t2i(prompt):
|
| 579 |
+
return generate_t2i(prompt, "1280 × 736 (16:9, 720p)", 30, 3.0, 3.0, False,
|
| 580 |
DEFAULT_NEGATIVE_PROMPT_IMAGE, 42, False, True)
|
| 581 |
|
| 582 |
|
|
|
|
| 631 |
t2v_enhance = gr.Checkbox(
|
| 632 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 633 |
)
|
| 634 |
+
t2v_steps, t2v_guidance, t2v_shift, t2v_fast, t2v_negative, t2v_seed, t2v_rand = _advanced(
|
| 635 |
DEFAULT_NEGATIVE_PROMPT, 24
|
| 636 |
)
|
| 637 |
t2v_btn = gr.Button("Generate video", variant="primary")
|
|
|
|
| 642 |
t2v_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 643 |
|
| 644 |
t2v_inputs = [
|
| 645 |
+
t2v_prompt, t2v_size, t2v_dur, t2v_steps, t2v_guidance, t2v_shift, t2v_fast,
|
| 646 |
t2v_negative, t2v_seed, t2v_rand, t2v_enhance,
|
| 647 |
]
|
| 648 |
t2v_outputs = [t2v_out, t2v_seed_out, t2v_caption_out]
|
|
|
|
| 679 |
i2v_enhance = gr.Checkbox(
|
| 680 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 681 |
)
|
| 682 |
+
i2v_steps, i2v_guidance, i2v_shift, i2v_fast, i2v_negative, i2v_seed, i2v_rand = _advanced(
|
| 683 |
DEFAULT_NEGATIVE_PROMPT, 24
|
| 684 |
)
|
| 685 |
i2v_btn = gr.Button("Generate video", variant="primary")
|
|
|
|
| 690 |
i2v_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 691 |
|
| 692 |
i2v_inputs = [
|
| 693 |
+
i2v_image, i2v_prompt, i2v_size, i2v_dur, i2v_steps, i2v_guidance, i2v_shift, i2v_fast,
|
| 694 |
i2v_negative, i2v_seed, i2v_rand, i2v_enhance,
|
| 695 |
]
|
| 696 |
i2v_outputs = [i2v_out, i2v_seed_out, i2v_caption_out]
|
|
|
|
| 722 |
t2i_enhance = gr.Checkbox(
|
| 723 |
label="Enhance prompt (official rewriter — required for plain prompts)", value=True
|
| 724 |
)
|
| 725 |
+
t2i_steps, t2i_guidance, t2i_shift, t2i_fast, t2i_negative, t2i_seed, t2i_rand = _advanced(
|
| 726 |
DEFAULT_NEGATIVE_PROMPT_IMAGE, 30
|
| 727 |
)
|
| 728 |
t2i_btn = gr.Button("Generate image", variant="primary")
|
|
|
|
| 733 |
t2i_caption_out = gr.Textbox(label="Caption", lines=4)
|
| 734 |
|
| 735 |
t2i_inputs = [
|
| 736 |
+
t2i_prompt, t2i_size, t2i_steps, t2i_guidance, t2i_shift, t2i_fast,
|
| 737 |
t2i_negative, t2i_seed, t2i_rand, t2i_enhance,
|
| 738 |
]
|
| 739 |
t2i_outputs = [t2i_out, t2i_seed_out, t2i_caption_out]
|
lingbot_video/pipeline_lingbot_video.py
CHANGED
|
@@ -375,6 +375,9 @@ class LingBotVideoPipeline(DiffusionPipeline):
|
|
| 375 |
batch_cfg: bool = False,
|
| 376 |
null_cond_clone_zero: bool = False,
|
| 377 |
guidance_stop: float = 1.0,
|
|
|
|
|
|
|
|
|
|
| 378 |
t_thresh: Optional[float] = None,
|
| 379 |
refiner_sigma_tail_steps: int = LOW_NOISE_TAIL_V1_DEFAULT_STEPS,
|
| 380 |
offload_vae_during_denoise: bool = False,
|
|
@@ -487,6 +490,10 @@ class LingBotVideoPipeline(DiffusionPipeline):
|
|
| 487 |
total_steps if guidance_stop >= 1.0
|
| 488 |
else max(1, int(round(float(guidance_stop) * total_steps)))
|
| 489 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 490 |
for i, timestep in enumerate(self.progress_bar(self.scheduler.timesteps)):
|
| 491 |
if cfg_parallel:
|
| 492 |
dist.broadcast(latents, src=cfg_latent_src, group=cfg_parallel_group)
|
|
@@ -552,6 +559,7 @@ class LingBotVideoPipeline(DiffusionPipeline):
|
|
| 552 |
timestep_batch,
|
| 553 |
prompt_model_input,
|
| 554 |
encoder_attention_mask=prompt_mask,
|
|
|
|
| 555 |
return_dict=False,
|
| 556 |
)[0].float()
|
| 557 |
|
|
@@ -563,6 +571,7 @@ class LingBotVideoPipeline(DiffusionPipeline):
|
|
| 563 |
timestep_batch,
|
| 564 |
negative_model_input,
|
| 565 |
encoder_attention_mask=negative_mask,
|
|
|
|
| 566 |
return_dict=False,
|
| 567 |
)[0].float()
|
| 568 |
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
|
|
|
|
| 375 |
batch_cfg: bool = False,
|
| 376 |
null_cond_clone_zero: bool = False,
|
| 377 |
guidance_stop: float = 1.0,
|
| 378 |
+
fbcache_threshold: Optional[float] = None,
|
| 379 |
+
fbcache_warmup: int = 3,
|
| 380 |
+
fbcache_max_consecutive: int = 3,
|
| 381 |
t_thresh: Optional[float] = None,
|
| 382 |
refiner_sigma_tail_steps: int = LOW_NOISE_TAIL_V1_DEFAULT_STEPS,
|
| 383 |
offload_vae_during_denoise: bool = False,
|
|
|
|
| 490 |
total_steps if guidance_stop >= 1.0
|
| 491 |
else max(1, int(round(float(guidance_stop) * total_steps)))
|
| 492 |
)
|
| 493 |
+
if hasattr(self.transformer, "reset_fbcache"):
|
| 494 |
+
self.transformer.reset_fbcache(
|
| 495 |
+
fbcache_threshold, fbcache_warmup, fbcache_max_consecutive
|
| 496 |
+
)
|
| 497 |
for i, timestep in enumerate(self.progress_bar(self.scheduler.timesteps)):
|
| 498 |
if cfg_parallel:
|
| 499 |
dist.broadcast(latents, src=cfg_latent_src, group=cfg_parallel_group)
|
|
|
|
| 559 |
timestep_batch,
|
| 560 |
prompt_model_input,
|
| 561 |
encoder_attention_mask=prompt_mask,
|
| 562 |
+
fb_cache_branch="cond",
|
| 563 |
return_dict=False,
|
| 564 |
)[0].float()
|
| 565 |
|
|
|
|
| 571 |
timestep_batch,
|
| 572 |
negative_model_input,
|
| 573 |
encoder_attention_mask=negative_mask,
|
| 574 |
+
fb_cache_branch="uncond",
|
| 575 |
return_dict=False,
|
| 576 |
)[0].float()
|
| 577 |
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
|
lingbot_video/pipeline_lingbot_video_i2v.py
CHANGED
|
@@ -162,6 +162,9 @@ class LingBotVideoImageToVideoPipeline(LingBotVideoPipeline):
|
|
| 162 |
batch_cfg: bool = False,
|
| 163 |
null_cond_clone_zero: bool = False,
|
| 164 |
guidance_stop: float = 1.0,
|
|
|
|
|
|
|
|
|
|
| 165 |
return_dict: bool = True,
|
| 166 |
) -> Union[LingBotVideoPipelineOutput, Tuple[Union[list, torch.Tensor]]]:
|
| 167 |
self.check_inputs(height, width, num_frames)
|
|
@@ -249,6 +252,10 @@ class LingBotVideoImageToVideoPipeline(LingBotVideoPipeline):
|
|
| 249 |
total_steps if guidance_stop >= 1.0
|
| 250 |
else max(1, int(round(float(guidance_stop) * total_steps)))
|
| 251 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
|
| 253 |
for i, timestep in enumerate(self.progress_bar(self.scheduler.timesteps)):
|
| 254 |
if cfg_parallel:
|
|
@@ -313,6 +320,7 @@ class LingBotVideoImageToVideoPipeline(LingBotVideoPipeline):
|
|
| 313 |
timestep_batch,
|
| 314 |
prompt_model_input,
|
| 315 |
encoder_attention_mask=prompt_mask,
|
|
|
|
| 316 |
return_dict=False,
|
| 317 |
)[0].float()
|
| 318 |
|
|
@@ -324,6 +332,7 @@ class LingBotVideoImageToVideoPipeline(LingBotVideoPipeline):
|
|
| 324 |
timestep_batch,
|
| 325 |
negative_model_input,
|
| 326 |
encoder_attention_mask=negative_mask,
|
|
|
|
| 327 |
return_dict=False,
|
| 328 |
)[0].float()
|
| 329 |
noise_pred = noise_pred_uncond + guidance_scale * (
|
|
|
|
| 162 |
batch_cfg: bool = False,
|
| 163 |
null_cond_clone_zero: bool = False,
|
| 164 |
guidance_stop: float = 1.0,
|
| 165 |
+
fbcache_threshold: Optional[float] = None,
|
| 166 |
+
fbcache_warmup: int = 3,
|
| 167 |
+
fbcache_max_consecutive: int = 3,
|
| 168 |
return_dict: bool = True,
|
| 169 |
) -> Union[LingBotVideoPipelineOutput, Tuple[Union[list, torch.Tensor]]]:
|
| 170 |
self.check_inputs(height, width, num_frames)
|
|
|
|
| 252 |
total_steps if guidance_stop >= 1.0
|
| 253 |
else max(1, int(round(float(guidance_stop) * total_steps)))
|
| 254 |
)
|
| 255 |
+
if hasattr(self.transformer, "reset_fbcache"):
|
| 256 |
+
self.transformer.reset_fbcache(
|
| 257 |
+
fbcache_threshold, fbcache_warmup, fbcache_max_consecutive
|
| 258 |
+
)
|
| 259 |
|
| 260 |
for i, timestep in enumerate(self.progress_bar(self.scheduler.timesteps)):
|
| 261 |
if cfg_parallel:
|
|
|
|
| 320 |
timestep_batch,
|
| 321 |
prompt_model_input,
|
| 322 |
encoder_attention_mask=prompt_mask,
|
| 323 |
+
fb_cache_branch="cond",
|
| 324 |
return_dict=False,
|
| 325 |
)[0].float()
|
| 326 |
|
|
|
|
| 332 |
timestep_batch,
|
| 333 |
negative_model_input,
|
| 334 |
encoder_attention_mask=negative_mask,
|
| 335 |
+
fb_cache_branch="uncond",
|
| 336 |
return_dict=False,
|
| 337 |
)[0].float()
|
| 338 |
noise_pred = noise_pred_uncond + guidance_scale * (
|
lingbot_video/transformer_lingbot_video.py
CHANGED
|
@@ -965,11 +965,58 @@ class LingBotVideoBlock(nn.Module):
|
|
| 965 |
return x
|
| 966 |
|
| 967 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 968 |
class LingBotVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
| 969 |
_supports_gradient_checkpointing = False
|
| 970 |
_no_split_modules = ["LingBotVideoBlock"]
|
| 971 |
_keep_in_fp32_modules = list(LINGBOT_VIDEO_FP32_MODULES)
|
| 972 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 973 |
def to(self, *args, **kwargs):
|
| 974 |
device, dtype, non_blocking, _ = torch._C._nn._parse_to(*args, **kwargs)
|
| 975 |
if dtype is None or dtype == torch.float32:
|
|
@@ -1100,6 +1147,7 @@ class LingBotVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
|
| 1100 |
timestep: torch.Tensor, # (B,) ∈ [0, 1000](= sigma*1000)
|
| 1101 |
encoder_hidden_states: torch.Tensor, # (B, L, text_dim)
|
| 1102 |
encoder_attention_mask: Optional[torch.Tensor] = None, # (B, L) 1=valid
|
|
|
|
| 1103 |
return_dict: bool = True,
|
| 1104 |
):
|
| 1105 |
B, C, T, H, W = hidden_states.shape
|
|
@@ -1272,16 +1320,47 @@ class LingBotVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
|
| 1272 |
temb6 = self.cp_temb6(temb6)
|
| 1273 |
temb6 = temb6.reshape(temb6.shape[0] * temb6.shape[1], -1)
|
| 1274 |
|
| 1275 |
-
|
| 1276 |
-
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
|
| 1280 |
-
|
| 1281 |
-
|
| 1282 |
-
|
| 1283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1284 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1285 |
if not packed_cp:
|
| 1286 |
joint = self.cp_out(joint)
|
| 1287 |
|
|
|
|
| 965 |
return x
|
| 966 |
|
| 967 |
|
| 968 |
+
class _FBCacheState:
|
| 969 |
+
"""First-Block-Cache state for a single CFG branch.
|
| 970 |
+
|
| 971 |
+
Reuses the accumulated residual of blocks 1..N-1 on denoising steps whose
|
| 972 |
+
first-block residual barely changes relative to the previous step (the
|
| 973 |
+
signal is the first transformer block's output delta, not the timestep
|
| 974 |
+
embedding). `warmup` forces full compute early; `max_consecutive` bounds
|
| 975 |
+
error drift by refreshing the cache after a run of skips.
|
| 976 |
+
"""
|
| 977 |
+
|
| 978 |
+
__slots__ = ("threshold", "warmup", "max_consecutive", "step", "consecutive",
|
| 979 |
+
"prev_first", "rest_residual", "skips")
|
| 980 |
+
|
| 981 |
+
def __init__(self, threshold: float, warmup: int, max_consecutive: int):
|
| 982 |
+
self.threshold = float(threshold)
|
| 983 |
+
self.warmup = int(warmup)
|
| 984 |
+
self.max_consecutive = int(max_consecutive)
|
| 985 |
+
self.step = 0
|
| 986 |
+
self.consecutive = 0
|
| 987 |
+
self.skips = 0
|
| 988 |
+
self.prev_first = None
|
| 989 |
+
self.rest_residual = None
|
| 990 |
+
|
| 991 |
+
def decide(self, first_residual: torch.Tensor) -> bool:
|
| 992 |
+
if (
|
| 993 |
+
self.step < self.warmup
|
| 994 |
+
or self.rest_residual is None
|
| 995 |
+
or self.prev_first is None
|
| 996 |
+
or self.consecutive >= self.max_consecutive
|
| 997 |
+
):
|
| 998 |
+
return False
|
| 999 |
+
diff = (first_residual - self.prev_first).abs().mean()
|
| 1000 |
+
base = self.prev_first.abs().mean() + 1e-8
|
| 1001 |
+
return bool((diff / base) < self.threshold)
|
| 1002 |
+
|
| 1003 |
+
|
| 1004 |
class LingBotVideoTransformer3DModel(ModelMixin, ConfigMixin):
|
| 1005 |
_supports_gradient_checkpointing = False
|
| 1006 |
_no_split_modules = ["LingBotVideoBlock"]
|
| 1007 |
_keep_in_fp32_modules = list(LINGBOT_VIDEO_FP32_MODULES)
|
| 1008 |
|
| 1009 |
+
def reset_fbcache(self, threshold=None, warmup: int = 3, max_consecutive: int = 3) -> None:
|
| 1010 |
+
"""(Re)initialize First-Block-Cache for a generation. threshold=None/<=0
|
| 1011 |
+
disables caching (standard full-block compute)."""
|
| 1012 |
+
if threshold is None or float(threshold) <= 0.0:
|
| 1013 |
+
self._fbcache = None
|
| 1014 |
+
else:
|
| 1015 |
+
self._fbcache = {
|
| 1016 |
+
"cond": _FBCacheState(threshold, warmup, max_consecutive),
|
| 1017 |
+
"uncond": _FBCacheState(threshold, warmup, max_consecutive),
|
| 1018 |
+
}
|
| 1019 |
+
|
| 1020 |
def to(self, *args, **kwargs):
|
| 1021 |
device, dtype, non_blocking, _ = torch._C._nn._parse_to(*args, **kwargs)
|
| 1022 |
if dtype is None or dtype == torch.float32:
|
|
|
|
| 1147 |
timestep: torch.Tensor, # (B,) ∈ [0, 1000](= sigma*1000)
|
| 1148 |
encoder_hidden_states: torch.Tensor, # (B, L, text_dim)
|
| 1149 |
encoder_attention_mask: Optional[torch.Tensor] = None, # (B, L) 1=valid
|
| 1150 |
+
fb_cache_branch: Optional[str] = None, # "cond"/"uncond" enables First-Block-Cache
|
| 1151 |
return_dict: bool = True,
|
| 1152 |
):
|
| 1153 |
B, C, T, H, W = hidden_states.shape
|
|
|
|
| 1320 |
temb6 = self.cp_temb6(temb6)
|
| 1321 |
temb6 = temb6.reshape(temb6.shape[0] * temb6.shape[1], -1)
|
| 1322 |
|
| 1323 |
+
cache = None
|
| 1324 |
+
if fb_cache_branch is not None:
|
| 1325 |
+
caches = getattr(self, "_fbcache", None)
|
| 1326 |
+
if caches is not None:
|
| 1327 |
+
cache = caches.get(fb_cache_branch)
|
| 1328 |
+
|
| 1329 |
+
if cache is None:
|
| 1330 |
+
for block in self.blocks:
|
| 1331 |
+
joint = block(
|
| 1332 |
+
joint,
|
| 1333 |
+
temb6,
|
| 1334 |
+
rotary,
|
| 1335 |
+
attention_mask,
|
| 1336 |
+
moe_padding_mask,
|
| 1337 |
+
packed_indices=packed_indices,
|
| 1338 |
+
parallel_config=parallel_config,
|
| 1339 |
+
)
|
| 1340 |
+
else:
|
| 1341 |
+
# First-Block-Cache: run block 0, then either reuse the cached
|
| 1342 |
+
# residual of blocks 1..N-1 or recompute it.
|
| 1343 |
+
original = joint
|
| 1344 |
+
joint = self.blocks[0](
|
| 1345 |
+
original, temb6, rotary, attention_mask, moe_padding_mask,
|
| 1346 |
+
packed_indices=packed_indices, parallel_config=parallel_config,
|
| 1347 |
)
|
| 1348 |
+
first_residual = joint - original
|
| 1349 |
+
if cache.decide(first_residual):
|
| 1350 |
+
joint = joint + cache.rest_residual
|
| 1351 |
+
cache.consecutive += 1
|
| 1352 |
+
cache.skips += 1
|
| 1353 |
+
else:
|
| 1354 |
+
h0 = joint
|
| 1355 |
+
for block in self.blocks[1:]:
|
| 1356 |
+
joint = block(
|
| 1357 |
+
joint, temb6, rotary, attention_mask, moe_padding_mask,
|
| 1358 |
+
packed_indices=packed_indices, parallel_config=parallel_config,
|
| 1359 |
+
)
|
| 1360 |
+
cache.rest_residual = joint - h0
|
| 1361 |
+
cache.consecutive = 0
|
| 1362 |
+
cache.prev_first = first_residual
|
| 1363 |
+
cache.step += 1
|
| 1364 |
if not packed_cp:
|
| 1365 |
joint = self.cp_out(joint)
|
| 1366 |
|