someone-in-the-world's picture
Quantize text encoder/transformers to fix ZeroGPU per-call duration overrun
8edf057
Raw
History Blame
4.92 kB
"""WAMU_v3 image-to-video pipeline loading and sizing helpers.
Baseline (non-AOT) loading per the SRS's "baseline first, AOT after" sequencing: FR-6's
AOT-compiled-transformer requirement is deferred until AOT-package compatibility with WAMU_v3
is confirmed (open item C-2, tracked in issue #3). Weight quantization (below) is independent
of AOT compilation and is applied now: the full bf16 pipe packs to ~69GB, which made ZeroGPU's
per-call GPU init (loading the packed weights onto the GPU) exceed the requested call duration.
"""
from __future__ import annotations
import torch
import torch._dynamo
from diffusers.pipelines.wan.pipeline_wan_i2v import WanImageToVideoPipeline
from PIL import Image
from torchao.quantization import Float8DynamicActivationFloat8WeightConfig, Int8WeightOnlyConfig, quantize_
MODEL_ID = "thornmaze/WAMU_v3_WAN2.2_I2V_LIGHTNING"
MAX_DIM = 832
MIN_DIM = 480
SQUARE_DIM = 640
MULTIPLE_OF = 16
FIXED_FPS = 16
MIN_FRAMES_MODEL = 8
MAX_FRAMES_MODEL = 321
MIN_DURATION = round(MIN_FRAMES_MODEL / FIXED_FPS, 1)
MAX_DURATION = round(MAX_FRAMES_MODEL / FIXED_FPS, 1)
DEFAULT_PROMPT = "make this image come alive, cinematic motion, smooth animation"
DEFAULT_NEGATIVE_PROMPT = (
"色调艳丽, 过曝, 静态, 细节模糊不清, 字幕, 风格, 作品, 画作, 画面, 静止, 整体发灰, "
"最差质量, 低质量, JPEG压缩残留, 丑陋的, 残缺的, 多余的手指, 画得不好的手部, "
"画得不好的脸部, 畸形的, 毁容的, 形态畸形的肢体, 手指融合, 静止不动的画面, "
"杂乱的背景, 三条腿, 背景人很多, 倒着走"
)
def load_pipeline() -> WanImageToVideoPipeline:
pipe = WanImageToVideoPipeline.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16).to("cuda")
# Quantize to shrink the ~69GB bf16 pipe (drops per-call ZeroGPU init time and peak memory).
# Independent of AOT compilation (FR-6/C-2, deferred) — same recipe as the reference project.
quantize_(pipe.text_encoder, Int8WeightOnlyConfig())
torch._dynamo.reset()
quantize_(pipe.transformer, Float8DynamicActivationFloat8WeightConfig())
torch._dynamo.reset()
quantize_(pipe.transformer_2, Float8DynamicActivationFloat8WeightConfig())
torch._dynamo.reset()
return pipe
def resize_image(image: Image.Image) -> Image.Image:
"""Resize/crop to the model's supported dimension range, keeping a multiple-of-16 size."""
width, height = image.size
if width == height:
return image.resize((SQUARE_DIM, SQUARE_DIM), Image.LANCZOS)
aspect_ratio = width / height
max_ar = MAX_DIM / MIN_DIM
min_ar = MIN_DIM / MAX_DIM
image_to_resize = image
if aspect_ratio > max_ar:
crop_width = int(round(height * max_ar))
left = (width - crop_width) // 2
image_to_resize = image.crop((left, 0, left + crop_width, height))
target_w, target_h = MAX_DIM, MIN_DIM
elif aspect_ratio < min_ar:
crop_height = int(round(width / min_ar))
top = (height - crop_height) // 2
image_to_resize = image.crop((0, top, width, top + crop_height))
target_w, target_h = MIN_DIM, MAX_DIM
else:
if width > height:
target_w = MAX_DIM
target_h = int(round(target_w / aspect_ratio))
else:
target_h = MAX_DIM
target_w = int(round(target_h * aspect_ratio))
final_w = max(MIN_DIM, min(MAX_DIM, round(target_w / MULTIPLE_OF) * MULTIPLE_OF))
final_h = max(MIN_DIM, min(MAX_DIM, round(target_h / MULTIPLE_OF) * MULTIPLE_OF))
return image_to_resize.resize((final_w, final_h), Image.LANCZOS)
def get_num_frames(duration_seconds: float) -> int:
raw = int(round(duration_seconds * FIXED_FPS))
raw = max(MIN_FRAMES_MODEL, min(MAX_FRAMES_MODEL, raw))
return ((raw - 1) // 4) * 4 + 1
def estimate_generation_seconds(
width: int, height: int, num_frames: int, steps: int, guidance_scale: float
) -> float:
"""Rough @spaces.GPU duration budget, calibrated from the reference wan2-2-i2v-v3 Space.
Exact limits are still an open item (SRS Section 4, #3) pending empirical testing on the
dev Space; this heuristic is a starting point, not a final calibration.
"""
base_frames_height_width = 81 * 832 * 624
base_step_duration = 5.0
factor = num_frames * width * height / base_frames_height_width
step_duration = base_step_duration * factor**1.5
gen_time = steps * step_duration
if guidance_scale > 1:
gen_time *= 2.4
# The reference project's "15" was calibrated with AOT-compiled transformers (FR-6, deferred
# here per C-2/issue #3). Without AOT, per-call ZeroGPU init (loading the quantized-but-not-
# compiled pipe onto the GPU) plus eager-mode execution both take longer, so pad generously
# until this can be measured empirically on the dev Space.
init_overhead = 60
return init_overhead + gen_time