Compare commits
46
Commits
main
...
refactor-config
@@ -80,26 +80,36 @@ def _run_fastvideo_pipeline(model_path: Path, params: dict[str, Any]) -> Any:
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
str(model_path),
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
try:
|
||||
return generator.generate_video(
|
||||
prompt=params["prompt"],
|
||||
negative_prompt=params.get("negative_prompt"),
|
||||
output_path=f"outputs_{_MODEL_FAMILY}/pipeline_parity",
|
||||
save_video=False,
|
||||
height=params.get("height"),
|
||||
width=params.get("width"),
|
||||
num_frames=params.get("num_frames"),
|
||||
fps=params.get("fps"),
|
||||
num_inference_steps=params["num_inference_steps"],
|
||||
guidance_scale=params.get("guidance_scale"),
|
||||
seed=params["seed"],
|
||||
)
|
||||
return generator.generate({
|
||||
"prompt": params["prompt"],
|
||||
"negative_prompt": params.get("negative_prompt"),
|
||||
"sampling": {
|
||||
"height": params.get("height"),
|
||||
"width": params.get("width"),
|
||||
"num_frames": params.get("num_frames"),
|
||||
"fps": params.get("fps"),
|
||||
"num_inference_steps": params["num_inference_steps"],
|
||||
"guidance_scale": params.get("guidance_scale"),
|
||||
"seed": params["seed"],
|
||||
},
|
||||
"output": {
|
||||
"output_path": f"outputs_{_MODEL_FAMILY}/pipeline_parity",
|
||||
"save_video": False,
|
||||
},
|
||||
})
|
||||
finally:
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -84,7 +84,9 @@ fastvideo/configs/models/dits/__init__.py
|
||||
fastvideo/configs/models/encoders/__init__.py
|
||||
fastvideo/configs/models/vaes/__init__.py
|
||||
fastvideo/envs.py
|
||||
fastvideo/fastvideo_args.py
|
||||
fastvideo/api/schema.py
|
||||
fastvideo/api/resolution.py
|
||||
fastvideo/api/inference_resolution.py
|
||||
fastvideo/distributed/**
|
||||
fastvideo/layers/**
|
||||
fastvideo/attention/**
|
||||
|
||||
@@ -20,7 +20,8 @@ on a summary here.
|
||||
- Read `docs/contributing/env_vars.md` in full.
|
||||
- Decide whether the setting belongs in an environment variable or an argument
|
||||
(rule 5 in the policy doc). Settings that users change per deployment are
|
||||
arguments; add them through `fastvideo/fastvideo_args.py` instead.
|
||||
arguments; add them as typed config fields in `fastvideo/api/schema.py`
|
||||
instead.
|
||||
|
||||
## Inputs
|
||||
|
||||
|
||||
@@ -489,7 +489,6 @@ def classify_paths(paths: list[str]) -> MergePlan:
|
||||
_select_output_coverage(plan, path)
|
||||
continue
|
||||
if path in {
|
||||
"fastvideo/fastvideo_args.py",
|
||||
"fastvideo/forward_context.py",
|
||||
"fastvideo/image_processor.py",
|
||||
"fastvideo/registry.py",
|
||||
|
||||
@@ -135,18 +135,20 @@ def main():
|
||||
# Create a video generator with a pre-trained model
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/FastWan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=1, # Adjust based on your hardware
|
||||
{"engine": {"num_gpus": 1}}, # Adjust based on your hardware
|
||||
)
|
||||
|
||||
# Define a prompt for your video
|
||||
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes wide with interest."
|
||||
|
||||
# Generate the video
|
||||
video = generator.generate_video(
|
||||
prompt,
|
||||
output_path="my_videos/", # Controls where videos are saved
|
||||
save_video=True
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"output": {
|
||||
"output_path": "my_videos/", # Controls where videos are saved
|
||||
"save_video": True,
|
||||
},
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -42,7 +42,7 @@ Near-term OSS note:
|
||||
- `apps/dreamverse/dreamverse/main.py`: websocket endpoint, request handling,
|
||||
session state machine, rewrite orchestration, REST routes, and stream relay
|
||||
- `apps/dreamverse/dreamverse/gpu_pool.py`: GPU worker processes, warmup, model
|
||||
loading, and `generate_video()` calls through FastVideo
|
||||
loading, and `generate()` calls through FastVideo
|
||||
- `apps/dreamverse/dreamverse/prompt_enhancer.py`: prompt enhancement, rollout
|
||||
rewrite execution, provider selection, and timeout/fallback behavior
|
||||
- `apps/dreamverse/dreamverse/rewrite_prompt_payload.py`: canonical rewrite request payload
|
||||
|
||||
@@ -52,7 +52,8 @@ import torch # noqa: E402
|
||||
|
||||
from fastvideo import VideoGenerator # noqa: E402
|
||||
from fastvideo.api import ( # noqa: E402
|
||||
ComponentConfig, CompileConfig, EngineConfig, GeneratorConfig, OffloadConfig, PipelineSelection, QuantizationConfig,
|
||||
ComponentConfig, CompileConfig, EngineConfig, GenerationResult, GeneratorConfig, OffloadConfig, PipelineSelection,
|
||||
QuantizationConfig,
|
||||
)
|
||||
|
||||
DEFAULT_PROMPT = ("A cinematic drone shot over coastal cliffs at sunrise, golden "
|
||||
@@ -128,9 +129,9 @@ def _build_generator_config(model_path: str, enable_compile: bool, num_gpus: int
|
||||
)
|
||||
|
||||
|
||||
def _extract_stage_times(result: dict) -> OrderedDict[str, float]:
|
||||
def _extract_stage_times(result: GenerationResult) -> OrderedDict[str, float]:
|
||||
out: OrderedDict[str, float] = OrderedDict()
|
||||
info = result.get("logging_info") if isinstance(result, dict) else None
|
||||
info = result.logging_info if isinstance(result, GenerationResult) else None
|
||||
if info is None:
|
||||
return out
|
||||
stages = getattr(info, "stages", None)
|
||||
@@ -162,19 +163,25 @@ def _do_one_run(generator: VideoGenerator, prompt: str, *, height: int, width: i
|
||||
_reset_peak_gpu()
|
||||
t0 = time.perf_counter()
|
||||
try:
|
||||
result = generator.generate_video(
|
||||
prompt=prompt,
|
||||
negative_prompt="",
|
||||
save_video=False,
|
||||
height=height,
|
||||
width=width,
|
||||
num_frames=num_frames,
|
||||
fps=24,
|
||||
num_inference_steps=num_inference_steps,
|
||||
guidance_scale=1.0,
|
||||
seed=seed,
|
||||
ltx2_image_crf=0.0,
|
||||
)
|
||||
result = generator.generate({
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"sampling": {
|
||||
"height": height,
|
||||
"width": width,
|
||||
"num_frames": num_frames,
|
||||
"fps": 24,
|
||||
"num_inference_steps": num_inference_steps,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": seed,
|
||||
},
|
||||
"output": {
|
||||
"save_video": False
|
||||
},
|
||||
"extensions": {
|
||||
"ltx2_image_crf": 0.0
|
||||
},
|
||||
})
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.synchronize()
|
||||
except Exception as exc:
|
||||
|
||||
@@ -14,6 +14,7 @@ import gc
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
@@ -34,6 +35,9 @@ from dreamverse.config import (
|
||||
)
|
||||
from dreamverse.generation_contracts import StepResult
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastvideo.api import GenerationResult
|
||||
|
||||
# Multi-frame decoded continuation defaults from
|
||||
# examples/inference/basic/basic_ltx2_distilled_video_continuation.py.
|
||||
# Overridable via environment variables.
|
||||
@@ -95,8 +99,8 @@ class ContinuationState:
|
||||
self.video_images = None
|
||||
self.audio_latents = None
|
||||
|
||||
def apply_video(self, request_kwargs: dict, segment_idx: int) -> None:
|
||||
"""Seed next-segment kwargs with the cached tail frames."""
|
||||
def apply_video(self, request: dict, segment_idx: int) -> None:
|
||||
"""Seed the next-segment request with the cached tail frames."""
|
||||
if segment_idx <= 1 or not self.video_images:
|
||||
return
|
||||
from PIL import Image
|
||||
@@ -110,21 +114,21 @@ class ContinuationState:
|
||||
arr = np.clip(arr, 0, 255).astype(np.uint8)
|
||||
noisy.append(Image.fromarray(arr))
|
||||
cond_images = noisy
|
||||
request_kwargs["ltx2_video_conditions"] = [(
|
||||
request["extensions"]["ltx2_video_conditions"] = [(
|
||||
cond_images,
|
||||
LTX2_VIDEO_CONDITIONING_FRAME_IDX,
|
||||
LTX2_VIDEO_CONDITIONING_STRENGTH,
|
||||
)]
|
||||
request_kwargs["ltx2_images"] = None
|
||||
request_kwargs["image_path"] = None
|
||||
request["extensions"]["ltx2_images"] = None
|
||||
request["inputs"]["image_path"] = None
|
||||
|
||||
def apply_audio(
|
||||
self,
|
||||
request_kwargs: dict,
|
||||
request: dict,
|
||||
segment_idx: int,
|
||||
audio_lps: float,
|
||||
) -> None:
|
||||
"""Seed next-segment kwargs with clean audio latents + denoise mask.
|
||||
"""Seed the next-segment request with clean audio latents + denoise mask.
|
||||
|
||||
When audio conditioning is longer than video, extend audio
|
||||
generation and shift video RoPE forward so the audio prefix
|
||||
@@ -141,9 +145,9 @@ class ContinuationState:
|
||||
audio_extra = max(0, AUDIO_CONDITIONING_NUM_FRAMES - LTX2_VIDEO_CONDITIONING_NUM_FRAMES)
|
||||
if audio_extra > 0:
|
||||
audio_num_frames = NUM_FRAMES + audio_extra
|
||||
request_kwargs["audio_num_frames"] = (audio_num_frames)
|
||||
request["extensions"]["audio_num_frames"] = (audio_num_frames)
|
||||
prefix_sec = float(audio_extra) / 24.0
|
||||
request_kwargs["video_position_offset_sec"] = prefix_sec
|
||||
request["extensions"]["video_position_offset_sec"] = prefix_sec
|
||||
|
||||
new_duration = float(NUM_FRAMES + audio_extra) / 24.0
|
||||
total_T = max(
|
||||
@@ -161,8 +165,8 @@ class ContinuationState:
|
||||
mask = torch.ones((B, 1, total_T, 1), dtype=torch.float32)
|
||||
mask[:, :, :audio_cond_T, :] = (1.0 - AUDIO_CONDITIONING_STRENGTH)
|
||||
|
||||
request_kwargs["ltx2_audio_clean_latent"] = clean
|
||||
request_kwargs["ltx2_audio_denoise_mask"] = mask
|
||||
request["extensions"]["ltx2_audio_clean_latent"] = clean
|
||||
request["extensions"]["ltx2_audio_denoise_mask"] = mask
|
||||
|
||||
def save_video(self, frames: list) -> None:
|
||||
"""Snapshot trailing N frames as PIL images for next-segment conditioning."""
|
||||
@@ -306,7 +310,7 @@ class LTX2GenerationBackend:
|
||||
),
|
||||
)
|
||||
|
||||
self.generator = VideoGenerator.from_pretrained(config=generator_config)
|
||||
self.generator = VideoGenerator.from_config(generator_config)
|
||||
print(f"[GPU {self.gpu_id}] After model load: {self._gpu_mem()}")
|
||||
|
||||
lora_stack = DREAMVERSE_LORA_STACK or ([(DREAMVERSE_LORA_PATH,
|
||||
@@ -403,7 +407,7 @@ class LTX2GenerationBackend:
|
||||
return
|
||||
|
||||
loader = ComponentLoader.for_module_type("audio_encoder", "diffusers")
|
||||
enc = loader.load(audio_vae_path, self.generator.fastvideo_args)
|
||||
enc = loader.load(audio_vae_path, self.generator.resolved_config)
|
||||
target = getattr(enc, "model", enc)
|
||||
|
||||
proc = AudioProcessor(
|
||||
@@ -460,51 +464,59 @@ class LTX2GenerationBackend:
|
||||
|
||||
prompt = self._inject_style_trigger(prompt)
|
||||
|
||||
request_kwargs = dict(
|
||||
prompt=prompt,
|
||||
negative_prompt="",
|
||||
save_video=False,
|
||||
height=FRAME_HEIGHT,
|
||||
width=FRAME_WIDTH,
|
||||
num_frames=NUM_FRAMES,
|
||||
fps=24,
|
||||
num_inference_steps=NUM_INFERENCE_STEPS,
|
||||
guidance_scale=1.0,
|
||||
seed=10,
|
||||
ltx2_image_crf=0.0,
|
||||
image_path=image_path if segment_idx == 1 else None,
|
||||
return_continuation_state=False,
|
||||
)
|
||||
request = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"inputs": {
|
||||
"image_path": image_path if segment_idx == 1 else None
|
||||
},
|
||||
"sampling": {
|
||||
"height": FRAME_HEIGHT,
|
||||
"width": FRAME_WIDTH,
|
||||
"num_frames": NUM_FRAMES,
|
||||
"fps": 24,
|
||||
"num_inference_steps": NUM_INFERENCE_STEPS,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": 10,
|
||||
},
|
||||
"output": {
|
||||
"save_video": False
|
||||
},
|
||||
"extensions": {
|
||||
"ltx2_image_crf": 0.0,
|
||||
"return_continuation_state": False,
|
||||
},
|
||||
}
|
||||
|
||||
if reset_conditioning:
|
||||
self.continuation.clear()
|
||||
|
||||
audio_lps = (DEFAULT_LTX2_AUDIO_SAMPLE_RATE / DEFAULT_LTX2_AUDIO_HOP_LENGTH / DEFAULT_LTX2_AUDIO_DOWNSAMPLE)
|
||||
|
||||
# Phase 1: seed kwargs with prior-segment conditioning.
|
||||
self.continuation.apply_video(request_kwargs, segment_idx)
|
||||
self.continuation.apply_audio(request_kwargs, segment_idx, audio_lps)
|
||||
# Phase 1: seed the request with prior-segment conditioning.
|
||||
self.continuation.apply_video(request, segment_idx)
|
||||
self.continuation.apply_audio(request, segment_idx, audio_lps)
|
||||
|
||||
# Phase 2: generate.
|
||||
t0 = time.perf_counter()
|
||||
result = self.generator.generate_video(**request_kwargs)
|
||||
result = self.generator.generate(request)
|
||||
torch.cuda.synchronize()
|
||||
timings["generation_ms"] = (time.perf_counter() - t0) * 1000
|
||||
|
||||
if not isinstance(result, dict):
|
||||
raise RuntimeError("Expected dictionary output from generate_video.")
|
||||
frames = result.get("frames")
|
||||
if isinstance(result, list):
|
||||
raise RuntimeError("Expected a single GenerationResult from generate.")
|
||||
frames = result.frames
|
||||
if not isinstance(frames, list) or len(frames) == 0:
|
||||
raise RuntimeError("Generation did not return frames.")
|
||||
audio = result.get("audio")
|
||||
audio_sample_rate = result.get("audio_sample_rate")
|
||||
audio = result.audio
|
||||
audio_sample_rate = result.audio_sample_rate
|
||||
if audio is not None and audio_sample_rate is None:
|
||||
# LTX2 audio decoding stage uses 24kHz output by default.
|
||||
audio_sample_rate = 24000
|
||||
print(f"[GPU {self.gpu_id}] audio_sample_rate missing from result; "
|
||||
f"defaulting to {audio_sample_rate}Hz")
|
||||
|
||||
timings["generation_time_ms"] = result.get("generation_time", 0.0) * 1000
|
||||
timings["generation_time_ms"] = (result.generation_time or 0.0) * 1000
|
||||
|
||||
# Phase 3: snapshot continuation state for the next segment.
|
||||
t_save_start = time.perf_counter()
|
||||
@@ -542,7 +554,7 @@ class LTX2GenerationBackend:
|
||||
self,
|
||||
audio: object,
|
||||
audio_sample_rate: int | None,
|
||||
result: dict,
|
||||
result: "GenerationResult",
|
||||
segment_idx: int,
|
||||
) -> torch.Tensor | None:
|
||||
"""Pick which tensor to cache for next-segment audio conditioning."""
|
||||
@@ -557,7 +569,7 @@ class LTX2GenerationBackend:
|
||||
f"for segment {segment_idx + 1}")
|
||||
return re_encoded
|
||||
return None
|
||||
audio_latents = result.get("ltx2_audio_latents")
|
||||
audio_latents = result.extra.get("ltx2_audio_latents")
|
||||
if audio_latents is not None:
|
||||
print(f"[GPU {self.gpu_id}] Cached audio latents "
|
||||
f"shape={tuple(audio_latents.shape)} "
|
||||
|
||||
@@ -79,32 +79,24 @@ class MiniMaxH3GenerationBackend:
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import (
|
||||
AttentionConfig,
|
||||
CompileConfig,
|
||||
ComponentConfig,
|
||||
EngineConfig,
|
||||
GeneratorConfig,
|
||||
MiniMaxH3Options,
|
||||
OffloadConfig,
|
||||
ParallelismConfig,
|
||||
PipelineSelection,
|
||||
)
|
||||
|
||||
adapter_path = hf_hub_download(repo_id=adapter_repo, filename=adapter_filename)
|
||||
experimental = {
|
||||
"attention_backend": attention_backend,
|
||||
"inference_torch_compile": attention_backend == "FLASH_ATTN",
|
||||
"vae_parallel_decode": True,
|
||||
"vae_parallel_decode_strategy": "gather",
|
||||
}
|
||||
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
|
||||
experimental.update({
|
||||
"VSA_sparsity": 0.9,
|
||||
"VSA_tile_size": 64,
|
||||
})
|
||||
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
|
||||
generator_config = GeneratorConfig(
|
||||
model_path=model_path,
|
||||
pipeline=PipelineSelection(
|
||||
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
|
||||
experimental=experimental,
|
||||
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
|
||||
),
|
||||
engine=EngineConfig(
|
||||
num_gpus=DREAMVERSE_SP_SIZE,
|
||||
@@ -117,7 +109,12 @@ class MiniMaxH3GenerationBackend:
|
||||
vae=True,
|
||||
pin_cpu_memory=True,
|
||||
),
|
||||
compile=CompileConfig(enabled=False, vae_enabled=True),
|
||||
compile=CompileConfig(enabled=False, vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
|
||||
attention=AttentionConfig(
|
||||
backend=attention_backend,
|
||||
vsa_sparsity=0.9 if use_vsa else None,
|
||||
vsa_tile_size=64 if use_vsa else None,
|
||||
),
|
||||
use_fsdp_inference=False,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -13,7 +13,6 @@ FORBIDDEN_PREFIXES = (
|
||||
"fastvideo.models",
|
||||
"fastvideo.layers",
|
||||
"fastvideo.worker",
|
||||
"fastvideo.fastvideo_args",
|
||||
)
|
||||
ALLOWED_INTERNAL_IMPORTS = {
|
||||
(
|
||||
|
||||
@@ -88,14 +88,13 @@ def test_initialize_builds_vsa_datafree_fasth3_generator(monkeypatch):
|
||||
assert config.model_path == "MiniMaxAI/MiniMax-H3"
|
||||
assert config.pipeline.components.lora_path.endswith("vsa-datafree/adapter_model.safetensors")
|
||||
assert config.pipeline.components.lora_strength == 1.0
|
||||
assert config.pipeline.experimental == {
|
||||
"attention_backend": "VIDEO_SPARSE_ATTN_H3",
|
||||
"inference_torch_compile": False,
|
||||
"vae_parallel_decode": True,
|
||||
"vae_parallel_decode_strategy": "gather",
|
||||
"VSA_sparsity": 0.9,
|
||||
"VSA_tile_size": 64,
|
||||
}
|
||||
assert config.pipeline.experimental == {}
|
||||
assert config.engine.attention.backend == "VIDEO_SPARSE_ATTN_H3"
|
||||
assert config.engine.attention.vsa_sparsity == 0.9
|
||||
assert config.engine.attention.vsa_tile_size == 64
|
||||
assert config.engine.compile.regional is False
|
||||
assert config.pipeline.model.vae_parallel_decode is True
|
||||
assert config.pipeline.model.vae_parallel_decode_strategy == "gather"
|
||||
assert config.engine.num_gpus == 4
|
||||
assert config.engine.parallelism.tp_size == 1
|
||||
assert config.engine.parallelism.sp_size == 4
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="e_dsg" value="generator.generate_video()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
|
||||
<mxCell id="e_dsg" value="generator.generate()" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#9673a6;endArrow=classic;fontSize=10;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="do_step" target="generator" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="e_dscache" value="read / write" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;strokeColor=#d6b656;endArrow=classic;startArrow=classic;fontSize=10;exitX=0;exitY=0.8;exitDx=0;exitDy=0;entryX=1;entryY=0.2;entryDx=0;entryDy=0;" parent="1" source="do_step" target="caches" edge="1">
|
||||
@@ -534,7 +534,7 @@
|
||||
<mxPoint x="1040" y="1610" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dm11a" value="10a. worker runs:
VideoGenerationWorker.generate_step()
 (ltx2_generation.py:380)
 → generator.generate_video()
 → updates ContinuationState
then stream_fmp4() (av_streaming.py:121)
 → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
|
||||
<mxCell id="dm11a" value="10a. worker runs:
VideoGenerationWorker.generate_step()
 (ltx2_generation.py:380)
 → generator.generate()
 → updates ContinuationState
then stream_fmp4() (av_streaming.py:121)
 → ffmpeg (rawvideo+wav → fmp4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0b2;strokeColor=#d79b00;fontSize=10;align=left;spacingLeft=8;fontFamily=monospace;" parent="1" vertex="1">
|
||||
<mxGeometry x="955" y="1640" width="180" height="70" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dm11" value="10b. resp_q.put(MediaInit / MediaChunk / MediaComplete / StepComplete)" style="endArrow=classic;html=1;strokeColor=#b85450;fontSize=10;labelBackgroundColor=#ffffff;" parent="1" edge="1">
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
@@ -64,8 +64,8 @@ generator:
|
||||
# internal: pipeline_config.dit_config.quant_config = FP4Config()
|
||||
# set in gpu_pool.py:280 (via the legacy in-place mutation). The
|
||||
# public typed surface resolves "NVFP4" to NVFP4Config() and pins
|
||||
# it on dit_config in FastVideoArgs.__post_init__. Comment this
|
||||
# block out on hosts without flashinfer / NVFP4 hardware.
|
||||
# it on dit_config when resolution materializes the PipelineConfig.
|
||||
# Comment this block out on hosts without flashinfer / NVFP4 hardware.
|
||||
quantization:
|
||||
transformer_quant: NVFP4
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ test.describe('preset prompt generation', () => {
|
||||
// on a B200 plus encode/transfer time. The "Continuation flipped
|
||||
// to Generating + Leave button rendered" pair above is the proof
|
||||
// the integration works: FE → /readyz → /curated-presets → WS
|
||||
// /ws → BE → GPU pool → VideoGenerator.generate_video, all green.
|
||||
// /ws → BE → GPU pool → VideoGenerator.generate, all green.
|
||||
const video = page.locator('video').first();
|
||||
await expect(video).toHaveCount(1);
|
||||
});
|
||||
|
||||
@@ -862,23 +862,40 @@ class JobRunner:
|
||||
sp_size,
|
||||
)
|
||||
|
||||
gen = VideoGenerator.from_pretrained(
|
||||
model_id,
|
||||
workload_type=workload_type,
|
||||
num_gpus=num_gpus,
|
||||
dit_layerwise_offload=dit_layerwise_offload,
|
||||
**({
|
||||
"override_pipeline_cls_name": override_pipeline_cls_name
|
||||
} if override_pipeline_cls_name else {}),
|
||||
dit_cpu_offload=dit_cpu_offload,
|
||||
text_encoder_cpu_offload=text_encoder_cpu_offload,
|
||||
vae_cpu_offload=vae_cpu_offload,
|
||||
image_encoder_cpu_offload=image_encoder_cpu_offload,
|
||||
use_fsdp_inference=use_fsdp_inference,
|
||||
enable_torch_compile=enable_torch_compile,
|
||||
VSA_sparsity=vsa_sparsity,
|
||||
tp_size=tp_size,
|
||||
sp_size=sp_size,
|
||||
gen = VideoGenerator.from_config(
|
||||
{
|
||||
"model_path": model_id,
|
||||
"engine": {
|
||||
"num_gpus": num_gpus,
|
||||
"parallelism": {
|
||||
"tp_size": tp_size,
|
||||
"sp_size": sp_size,
|
||||
},
|
||||
"offload": {
|
||||
"dit": dit_cpu_offload,
|
||||
"dit_layerwise": dit_layerwise_offload,
|
||||
"text_encoder": text_encoder_cpu_offload,
|
||||
"image_encoder": image_encoder_cpu_offload,
|
||||
"vae": vae_cpu_offload,
|
||||
},
|
||||
"compile": {
|
||||
"enabled": enable_torch_compile
|
||||
},
|
||||
"attention": {
|
||||
"vsa_sparsity": vsa_sparsity
|
||||
},
|
||||
"use_fsdp_inference": use_fsdp_inference,
|
||||
},
|
||||
"pipeline": {
|
||||
"workload_type":
|
||||
workload_type,
|
||||
**({
|
||||
"components": {
|
||||
"override_pipeline_cls_name": override_pipeline_cls_name
|
||||
}
|
||||
} if override_pipeline_cls_name else {}),
|
||||
},
|
||||
},
|
||||
log_queue=log_queue,
|
||||
)
|
||||
|
||||
@@ -1103,30 +1120,33 @@ class JobRunner:
|
||||
# Without a name FastVideo derives the filename from the prompt.
|
||||
safe_name = re.sub(r'[\\/:*?"<>|]+', "", job.name).strip().strip(".")
|
||||
output_target = (os.path.join(job_output_dir, f"{safe_name[:80]}.mp4") if safe_name else job_output_dir)
|
||||
gen_kwargs: dict[str, Any] = {
|
||||
request: dict[str, Any] = {
|
||||
"prompt": job.prompt,
|
||||
"output_path": output_target,
|
||||
"save_video": True,
|
||||
"num_inference_steps": job.num_inference_steps,
|
||||
"num_frames": job.num_frames,
|
||||
"height": job.height,
|
||||
"width": job.width,
|
||||
"guidance_scale": job.guidance_scale,
|
||||
"guidance_rescale": job.guidance_rescale,
|
||||
"fps": job.fps,
|
||||
"seed": job.seed,
|
||||
"negative_prompt": job.negative_prompt or "",
|
||||
"log_queue": log_queue,
|
||||
"sampling": {
|
||||
"num_inference_steps": job.num_inference_steps,
|
||||
"num_frames": job.num_frames,
|
||||
"height": job.height,
|
||||
"width": job.width,
|
||||
"guidance_scale": job.guidance_scale,
|
||||
"guidance_rescale": job.guidance_rescale,
|
||||
"fps": job.fps,
|
||||
"seed": job.seed,
|
||||
},
|
||||
"output": {
|
||||
"output_path": output_target,
|
||||
"save_video": True,
|
||||
},
|
||||
}
|
||||
if job.image_path:
|
||||
gen_kwargs["image_path"] = job.image_path
|
||||
request.setdefault("inputs", {})["image_path"] = job.image_path
|
||||
if job.references:
|
||||
gen_kwargs["references"] = _build_h3_references(job.references)
|
||||
request.setdefault("inputs", {})["references"] = _build_h3_references(job.references)
|
||||
if job.last_image_path:
|
||||
# _prepare_fl2va requires a PIL image, not a path.
|
||||
from PIL import Image as _PILImage
|
||||
gen_kwargs["last_image"] = _PILImage.open(job.last_image_path)
|
||||
generator.generate_video(**gen_kwargs)
|
||||
request.setdefault("inputs", {})["last_image"] = _PILImage.open(job.last_image_path)
|
||||
generator.generate(request, log_queue=log_queue)
|
||||
|
||||
buf.phase = "saving"
|
||||
logger.info("Generation completed, searching for output file...")
|
||||
|
||||
@@ -131,8 +131,9 @@ export default function CreateJobModal({
|
||||
const editingJobId = editingJob?.id ?? null;
|
||||
const editingJobModelId = editingJob?.model_id ?? null;
|
||||
|
||||
// Layerwise offload and FSDP compete for the DiT weights and FastVideoArgs
|
||||
// silently picks a winner (fastvideo_args.py:859); resolve it visibly here.
|
||||
// Layerwise offload and FSDP compete for the DiT weights and the device offload
|
||||
// policy (resolve_device_offload_conflicts in fastvideo/api/device_policy.py)
|
||||
// silently picks a winner; resolve it visibly here.
|
||||
// dit_cpu_offload is deliberately not interlocked -- it is a modifier, not a
|
||||
// competing strategy.
|
||||
const handleDitLayerwiseOffloadChange = React.useCallback((next: boolean) => {
|
||||
|
||||
@@ -15,6 +15,9 @@ from fastvideo import VideoGenerator as FastVideoGenerator
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))))
|
||||
|
||||
# InferenceArgs keys that are SamplingConfig fields of a GenerationRequest.
|
||||
_SAMPLING_INFERENCE_ARGS = ("height", "width", "num_frames", "num_inference_steps", "guidance_scale", "seed", "fps")
|
||||
|
||||
|
||||
# Custom exception for interruption
|
||||
class GenerationInterruptedException(Exception):
|
||||
@@ -154,7 +157,17 @@ class VideoGenerator:
|
||||
"""Thread function to run the generation"""
|
||||
try:
|
||||
if self.generator is not None:
|
||||
self.generator.generate_video(prompt=prompt, output_path=output_path, **inference_args)
|
||||
# Place each InferenceArgs value in the GenerationRequest section that owns it.
|
||||
request: dict[str, Any] = {"prompt": prompt, "output": {"output_path": output_path}}
|
||||
for key, value in inference_args.items():
|
||||
if key == "image_path":
|
||||
section = "inputs"
|
||||
elif key in _SAMPLING_INFERENCE_ARGS:
|
||||
section = "sampling"
|
||||
else:
|
||||
section = "extensions"
|
||||
request.setdefault(section, {})[key] = value
|
||||
self.generator.generate(request)
|
||||
self._generation_result = os.path.join(output_path, f"{prompt[:100]}.mp4")
|
||||
else:
|
||||
raise RuntimeError("Generator is not initialized")
|
||||
@@ -253,9 +266,24 @@ class VideoGenerator:
|
||||
if self.generator is None:
|
||||
print('generation_args', generation_args)
|
||||
print('pipeline_config', pipeline_config)
|
||||
self.generator = FastVideoGenerator.from_pretrained(model_path=model_path,
|
||||
**generation_args,
|
||||
pipeline_config=pipeline_config)
|
||||
# Place each generation argument at its GeneratorConfig engine path.
|
||||
engine_config: dict[str, Any] = {}
|
||||
if "num_gpus" in generation_args:
|
||||
engine_config["num_gpus"] = generation_args["num_gpus"]
|
||||
for parallelism_key in ("tp_size", "sp_size"):
|
||||
if parallelism_key in generation_args:
|
||||
engine_config.setdefault("parallelism", {})[parallelism_key] = generation_args[parallelism_key]
|
||||
if "dit_cpu_offload" in generation_args:
|
||||
engine_config["offload"] = {"dit": generation_args["dit_cpu_offload"]}
|
||||
self.generator = FastVideoGenerator.from_config({
|
||||
"model_path": model_path,
|
||||
"engine": engine_config,
|
||||
"pipeline": {
|
||||
"experimental": {
|
||||
"pipeline_config": pipeline_config
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
print('inference_args', inference_args)
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
"limitations": [
|
||||
"Install from the DGX Spark guide, not the generic CUDA extra. GB10 has no FA4 / sm_100a VSA kernel; keep FASTVIDEO_FA4=0 and FASTVIDEO_VSA_SM100A=0.",
|
||||
"Legal num_frames values are 17n+5, capped at 345 (15 s). Native 16:9 sizes include 832x480 and 1344x768.",
|
||||
"Lazy module load reloads Qwen3-VL and the DiT between phases of each request. Do not pass --no-lazy-module-load on this box.",
|
||||
"Lazy module load reloads Qwen3-VL and the DiT between phases of each request. Do not set engine.offload.lazy_module_load to false on this box.",
|
||||
"A 345-frame request on one Spark can OOM. Prefer 124 or 243 frames, TAEH3 decode, or two Sparks over QSFP."
|
||||
]
|
||||
},
|
||||
|
||||
@@ -57,19 +57,15 @@ Minimal usage example (based on `examples/inference/basic/basic.py`):
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
model_id = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers" # or official_weights/<model_name>/
|
||||
generator = VideoGenerator.from_pretrained(model_id, num_gpus=1)
|
||||
generator = VideoGenerator.from_pretrained(model_id, {"engine": {"num_gpus": 1}})
|
||||
|
||||
sampling = SamplingParam.from_pretrained(model_id)
|
||||
sampling.num_frames = 45
|
||||
video = generator.generate_video(
|
||||
"A vibrant city street at sunset.",
|
||||
sampling_param=sampling,
|
||||
output_path="video_samples",
|
||||
save_video=True,
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": "A vibrant city street at sunset.",
|
||||
"sampling": {"num_frames": 45},
|
||||
"output": {"output_path": "video_samples", "save_video": True},
|
||||
})
|
||||
```
|
||||
|
||||
## Some questions to ask yourself before starting
|
||||
|
||||
@@ -122,8 +122,9 @@ in `examples/`, `scripts/`, `docs/`, `apps/`, and the tests in the same pull req
|
||||
minor release.
|
||||
|
||||
To remove a variable that no code reads, delete its entry and add the name to `DEPRECATED_VARIABLES` in
|
||||
`fastvideo/envs.py` with a reason. `FastVideoArgs` calls `envs.warn_deprecated_variables()`, which logs a warning for
|
||||
each listed variable that is set. Delete the entry in the next minor release.
|
||||
`fastvideo/envs.py` with a reason. The config resolution step `warn_deprecated_environment_variables` in
|
||||
`fastvideo/api/inference_resolution.py` calls `envs.warn_deprecated_variables()`, which logs a warning for each listed
|
||||
variable that is set. Delete the entry in the next minor release.
|
||||
|
||||
## What the contract test checks
|
||||
|
||||
@@ -188,7 +189,7 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
|
||||
| `FASTVIDEO_LOGGING_LEVEL` | str | `INFO` | logging | Default logging level. |
|
||||
| `FASTVIDEO_LOGGING_PREFIX` | str | `""` | logging | Prefix prepended to every log message. |
|
||||
| `FASTVIDEO_STAGE_LOGGING` | bool | `0` | logging | Log the time that each pipeline stage takes. |
|
||||
| `FASTVIDEO_ATTENTION_BACKEND` | str | unset | attention | Attention backend, as an AttentionBackendEnum name such as TORCH_SDPA, FLASH_ATTN, VIDEO_SPARSE_ATTN, SAGE_ATTN, or SAGE_ATTN_THREE. FastVideoArgs uses it when FastVideoArgs.attention_backend is unset. |
|
||||
| `FASTVIDEO_ATTENTION_BACKEND` | str | unset | attention | Attention backend, as an AttentionBackendEnum name such as TORCH_SDPA, FLASH_ATTN, VIDEO_SPARSE_ATTN, SAGE_ATTN, or SAGE_ATTN_THREE. Config resolution uses it when engine.attention.backend is unset. An unsupported name raises an error. |
|
||||
| `FASTVIDEO_FA4` | bool | `0` | attention | The FLASH_ATTN backend uses FlashAttention-4 (flash_attn.cute) instead of FA3 or FA2. |
|
||||
| `FASTVIDEO_MINIMAX_H3_FA4_PACKED_VARLEN` | bool | `0` | attention | MiniMax-H3 dense DiT self-attention uses the FlashAttention-4 packed-varlen entry point. This changes the floating-point reduction order, so it is an inference-only opt-in. |
|
||||
| `FASTVIDEO_VSA_SM100A` | bool | `0` | attention | VIDEO_SPARSE_ATTN_H3 sends no-grad tile-64 forwards to the data-center Blackwell (sm_100a) kernel. fastvideo-kernel reads the same variable with the same rule. |
|
||||
@@ -196,9 +197,9 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
|
||||
| `FASTVIDEO_DISABLE_ATTENTION_COMPILE` | bool | `1` | attention | Keep attention forward out of torch.compile graphs (torch.compiler.disable). Set it to 0 to let attention constructed under that setting be traced. Setting it explicitly to true also blocks regional compile. |
|
||||
| `FASTVIDEO_MLX_WINDOW` | int | `0` | attention | MLX FastWan windowed attention size in tokens. 0 uses full attention. |
|
||||
| `FASTVIDEO_MLX_WINDOW_SINK` | int | `0` | attention | Number of sink tokens that MLX windowed attention always attends to. |
|
||||
| `FASTVIDEO_INFERENCE_TORCH_COMPILE` | bool | `0` | performance | Compile each DiT transformer block with fullgraph torch.compile at inference. Same as FastVideoArgs.inference_torch_compile=True. |
|
||||
| `FASTVIDEO_VAE_PARALLEL_DECODE` | bool | `0` | performance | MiniMax-H3 VAE decode splits its temporal chunks across the sequence-parallel ranks instead of running serially on the output rank. Same as FastVideoArgs.vae_parallel_decode=True. |
|
||||
| `FASTVIDEO_VAE_PARALLEL_ENCODE` | bool | `0` | performance | MiniMax-H3 reference-video VAE encode splits its temporal chunks across the sequence-parallel ranks. Same as FastVideoArgs.vae_parallel_encode=True. |
|
||||
| `FASTVIDEO_INFERENCE_TORCH_COMPILE` | bool | `0` | performance | Compile each DiT transformer block with fullgraph torch.compile at inference. Same as engine.compile.regional=True. |
|
||||
| `FASTVIDEO_VAE_PARALLEL_DECODE` | bool | `0` | performance | MiniMax-H3 VAE decode splits its temporal chunks across the sequence-parallel ranks instead of running serially on the output rank. Same as pipeline.model.minimax_h3.vae_parallel_decode=True. |
|
||||
| `FASTVIDEO_VAE_PARALLEL_ENCODE` | bool | `0` | performance | MiniMax-H3 reference-video VAE encode splits its temporal chunks across the sequence-parallel ranks. Same as pipeline.model.minimax_h3.vae_parallel_encode=True. |
|
||||
| `FASTVIDEO_VAE_PARALLEL_DECODE_STRATEGY` | str | unset | performance | Collective that moves chunks in parallel VAE decode: gather (used when unset) or all_gather. |
|
||||
| `FASTVIDEO_MINIMAX_H3_FUSIONS` | str | `""` | performance | MiniMax-H3 inference-only Triton fusions: all, 1, or a comma-separated subset of modulate,qknorm_rope,swiglu. Empty, 0, or none keeps the eager implementation. |
|
||||
| `FASTVIDEO_FSDP2_AUTOWRAP` | bool | `0` | performance | FSDP2 shards modules by parameter count instead of the model's shard conditions. Not supported by self-forcing distillation. |
|
||||
@@ -234,7 +235,7 @@ longer exists also fails the test, so the fixing pull request deletes its entry.
|
||||
| `FASTVIDEO_LTX2_GEMMA_LOG` | str | `""` | debug | Log file for LTX-2 Gemma text-encoder hidden states, used by parity tests. Deprecated names: `LTX2_FASTVIDEO_GEMMA_LOG`. |
|
||||
| `FASTVIDEO_COSMOS25_LOG_KNOBS` | bool | `0` | debug | Log the Cosmos 2.5 latent-preparation conditioning inputs. |
|
||||
| `FASTVIDEO_CFG_GATE_STEP` | float | `1.0` | sampling | CFG gating fraction in [0, 1]. Steps before len(timesteps) \* X run the conditional and unconditional forwards; later steps reuse the cached difference. 1.0 disables gating. |
|
||||
| `FASTVIDEO_LTX2_USE_DISTILLED_SIGMAS` | bool | `1` | sampling | LTX-2 uses the distilled sigma schedule when FastVideoArgs.ltx2_use_distilled_sigmas is also true. Deprecated names: `LTX2_USE_DISTILLED_SIGMAS`. |
|
||||
| `FASTVIDEO_LTX2_USE_DISTILLED_SIGMAS` | bool | `1` | sampling | LTX-2 uses the distilled sigma schedule when pipeline.model.ltx2.use_distilled_sigmas is also true. Deprecated names: `LTX2_USE_DISTILLED_SIGMAS`. |
|
||||
| `FASTVIDEO_EVAL_CACHE` | path | computed | eval | Cache directory for evaluation models and datasets. Defaults to $FASTVIDEO_CACHE_ROOT/eval. |
|
||||
| `FASTVIDEO_PHYSICS_IQ_BUCKET_URL` | str | `https://storage.googleapis.com/physics-iq-benchmark` | eval | Base URL of the Physics-IQ benchmark bucket. |
|
||||
| `FASTVIDEO_VBENCH_FULL_INFO_JSON` | str | unset | eval | Path to VBench_full_info.json, used instead of the vendored copy. Deprecated names: `VBENCH_FULL_INFO_JSON`. |
|
||||
|
||||
@@ -112,7 +112,7 @@ per-metric policy with direction, percent threshold, absolute threshold, and a
|
||||
|
||||
`test_inference_performance.py` temporarily sets `FASTVIDEO_STAGE_LOGGING=1`
|
||||
while it runs so pipeline stage execution times are available in
|
||||
`generate_video(...).logging_info`. Stage logs use pipeline-unique keys such as
|
||||
`generate(...).logging_info`. Stage logs use pipeline-unique keys such as
|
||||
`prompt_encoding_stage` so duplicate stage classes do not collide. For
|
||||
`PipelineStage` entries, shared component stage bases emit a stable
|
||||
`component_metric`: text encoding stages map to `text_encoder_time_s`,
|
||||
|
||||
@@ -2,136 +2,158 @@ status_definitions:
|
||||
kept: "Public field remains on a public adapter surface with the same meaning."
|
||||
moved: "Public field remains supported but normalizes into a different nested path."
|
||||
preset_owned: "Public field remains supported only through a model/preset-specific surface."
|
||||
compatibility_only: "Legacy public field remains adapter-only during migration and is not part of the canonical typed schema."
|
||||
compatibility_only: "Public field that an adapter or an open mapping accepts outside the typed fields of the canonical schema."
|
||||
private_only: "Field should only be handled by private adapters and is not a public FastVideo compatibility promise."
|
||||
internal_only: "Field is runtime/config plumbing and should not be part of the new public typed inference API."
|
||||
internal_only: "Field is runtime/config plumbing that model code, config resolution, or the runtime fills; it is not a public input."
|
||||
unsupported: "Typed config field that no runtime code reads; resolution rejects a value."
|
||||
|
||||
surfaces:
|
||||
fastvideo_args:
|
||||
generator_config:
|
||||
kept:
|
||||
- model_path
|
||||
- mode
|
||||
- revision
|
||||
- trust_remote_code
|
||||
- engine.num_gpus
|
||||
- engine.execution_backend
|
||||
- engine.parallelism.tp_size
|
||||
- engine.parallelism.sp_size
|
||||
- engine.parallelism.hsdp_replicate_dim
|
||||
- engine.parallelism.hsdp_shard_dim
|
||||
- engine.parallelism.dist_timeout
|
||||
- engine.offload.dit
|
||||
- engine.offload.dit_layerwise
|
||||
- engine.offload.text_encoder
|
||||
- engine.offload.image_encoder
|
||||
- engine.offload.vae
|
||||
- engine.offload.pin_cpu_memory
|
||||
- engine.compile.enabled
|
||||
- engine.compile.backend
|
||||
- engine.compile.fullgraph
|
||||
- engine.compile.mode
|
||||
- engine.compile.dynamic
|
||||
- engine.compile.extras
|
||||
- engine.enable_stage_verification
|
||||
- engine.use_fsdp_inference
|
||||
- engine.disable_autocast
|
||||
- engine.attention.nvfp4_fa4
|
||||
- pipeline.components.lora_path
|
||||
- pipeline.components.lora_strength
|
||||
- pipeline.output_type
|
||||
- engine.parallelism.master_port
|
||||
- engine.offload.lazy_module_load
|
||||
- engine.compile.text_encoder_enabled
|
||||
- engine.compile.vae_enabled
|
||||
- engine.compile.audio_vae_enabled
|
||||
- engine.compile.regional
|
||||
- engine.compile.dit_kwargs
|
||||
- engine.compile.text_encoder_kwargs
|
||||
- engine.compile.vae_kwargs
|
||||
- engine.compile.audio_vae_kwargs
|
||||
- engine.attention.backend
|
||||
- engine.attention.vsa_sparsity
|
||||
- engine.attention.vsa_tile_size
|
||||
- engine.attention.moba_config_path
|
||||
- engine.precision.dit
|
||||
- engine.precision.vae
|
||||
- engine.precision.vae_decode
|
||||
- engine.precision.image_encoder
|
||||
- engine.precision.text_encoders
|
||||
- engine.quantization.text_encoder_quant
|
||||
- engine.quantization.transformer_quant
|
||||
- pipeline.workload_type
|
||||
- pipeline.components.config_root
|
||||
- pipeline.components.pipeline_config_path
|
||||
- pipeline.components.text_encoder_weights
|
||||
- pipeline.components.transformer_weights
|
||||
- pipeline.components.transformer_2_weights
|
||||
- pipeline.components.upsampler_weights
|
||||
- pipeline.components.lora_nickname
|
||||
- pipeline.components.lora_target_modules
|
||||
- pipeline.components.override_pipeline_cls_name
|
||||
- pipeline.components.override_transformer_cls_name
|
||||
- pipeline.vae_tiling
|
||||
- pipeline.vae_sp
|
||||
- pipeline.flow_shift
|
||||
- pipeline.embedded_cfg_scale
|
||||
- pipeline.dmd_denoising_steps
|
||||
- pipeline.boundary_ratio
|
||||
- pipeline.model.generic.dit
|
||||
- pipeline.model.generic.vae
|
||||
- pipeline.model.ltx2.dit
|
||||
- pipeline.model.ltx2.vae
|
||||
- pipeline.model.minimax_h3.dit
|
||||
- pipeline.model.minimax_h3.vae
|
||||
- pipeline.model.longcat.dit
|
||||
- pipeline.model.longcat.vae
|
||||
unsupported:
|
||||
- pipeline.preset
|
||||
- pipeline.preset_version
|
||||
- pipeline.components.vae_weights
|
||||
moved:
|
||||
model_path: generator.model_path
|
||||
workload_type: generator.pipeline.workload_type
|
||||
distributed_executor_backend: generator.engine.execution_backend
|
||||
trust_remote_code: generator.trust_remote_code
|
||||
revision: generator.revision
|
||||
num_gpus: generator.engine.num_gpus
|
||||
tp_size: generator.engine.parallelism.tp_size
|
||||
sp_size: generator.engine.parallelism.sp_size
|
||||
hsdp_replicate_dim: generator.engine.parallelism.hsdp_replicate_dim
|
||||
hsdp_shard_dim: generator.engine.parallelism.hsdp_shard_dim
|
||||
dist_timeout: generator.engine.parallelism.dist_timeout
|
||||
lora_path: generator.pipeline.components.lora_path
|
||||
lora_nickname: generator.pipeline.components.lora_nickname
|
||||
lora_strength: generator.pipeline.components.lora_strength
|
||||
dit_cpu_offload: generator.engine.offload.dit
|
||||
use_fsdp_inference: generator.engine.use_fsdp_inference
|
||||
dit_layerwise_offload: generator.engine.offload.dit_layerwise
|
||||
text_encoder_cpu_offload: generator.engine.offload.text_encoder
|
||||
image_encoder_cpu_offload: generator.engine.offload.image_encoder
|
||||
vae_cpu_offload: generator.engine.offload.vae
|
||||
pin_cpu_memory: generator.engine.offload.pin_cpu_memory
|
||||
lazy_module_load: generator.engine.offload.lazy_module_load
|
||||
enable_torch_compile: generator.engine.compile.enabled
|
||||
enable_torch_compile_text_encoder: generator.engine.compile.text_encoder_enabled
|
||||
enable_torch_compile_vae: generator.engine.compile.vae_enabled
|
||||
enable_torch_compile_audio_vae: generator.engine.compile.audio_vae_enabled
|
||||
torch_compile_kwargs: generator.engine.compile.backend,fullgraph,mode,dynamic,extras
|
||||
torch_compile_kwargs_dit: generator.engine.compile.dit_kwargs
|
||||
torch_compile_kwargs_text_encoder: generator.engine.compile.text_encoder_kwargs
|
||||
torch_compile_kwargs_vae: generator.engine.compile.vae_kwargs
|
||||
torch_compile_kwargs_audio_vae: generator.engine.compile.audio_vae_kwargs
|
||||
transformer_quant: generator.engine.quantization.transformer_quant
|
||||
disable_autocast: generator.engine.disable_autocast
|
||||
enable_stage_verification: generator.engine.enable_stage_verification
|
||||
prompt_txt: request.inputs.prompt_path
|
||||
override_text_encoder_safetensors: generator.pipeline.components.text_encoder_weights
|
||||
override_text_encoder_quant: generator.engine.quantization.text_encoder_quant
|
||||
transformer_quant: generator.engine.quantization.transformer_quant
|
||||
override_transformer_cls_name: generator.pipeline.components.override_transformer_cls_name
|
||||
init_weights_from_safetensors: generator.pipeline.components.transformer_weights
|
||||
init_weights_from_safetensors_2: generator.pipeline.components.transformer_2_weights
|
||||
override_pipeline_cls_name: generator.pipeline.components.override_pipeline_cls_name
|
||||
boundary_ratio: request.sampling.boundary_ratio
|
||||
ltx2_vae_tiling: generator.pipeline.vae_tiling
|
||||
refine_enabled: generator.pipeline.preset_overrides.refine.enabled
|
||||
refine_upsampler_path: generator.pipeline.components.upsampler_weights
|
||||
refine_lora_path: generator.pipeline.components.lora_path
|
||||
refine_num_inference_steps: request.stage_overrides.refine.num_inference_steps
|
||||
refine_guidance_scale: request.stage_overrides.refine.guidance_scale
|
||||
refine_add_noise: generator.pipeline.preset_overrides.refine.add_noise
|
||||
ltx2_refine_enabled: generator.pipeline.preset_overrides.refine.enabled
|
||||
ltx2_refine_upsampler_path: generator.pipeline.components.upsampler_weights
|
||||
ltx2_refine_lora_path: generator.pipeline.components.lora_path
|
||||
ltx2_refine_num_inference_steps: request.stage_overrides.refine.num_inference_steps
|
||||
ltx2_refine_guidance_scale: request.stage_overrides.refine.guidance_scale
|
||||
ltx2_refine_add_noise: generator.pipeline.preset_overrides.refine.add_noise
|
||||
pipeline.preset_overrides:
|
||||
target: generator.pipeline.model.ltx2.refine
|
||||
note: "Only the refine mapping applies: resolution copies pipeline.preset_overrides.refine into the pipeline.model.ltx2.refine fields of the same names when the model is LTX-2. Other keys have no effect."
|
||||
preset_owned:
|
||||
ltx2_vae_spatial_tile_size_in_pixels: generator.pipeline.preset_overrides.ltx2.vae.spatial_tile_size_in_pixels
|
||||
ltx2_vae_spatial_tile_overlap_in_pixels: generator.pipeline.preset_overrides.ltx2.vae.spatial_tile_overlap_in_pixels
|
||||
ltx2_vae_temporal_tile_size_in_frames: generator.pipeline.preset_overrides.ltx2.vae.temporal_tile_size_in_frames
|
||||
ltx2_vae_temporal_tile_overlap_in_frames: generator.pipeline.preset_overrides.ltx2.vae.temporal_tile_overlap_in_frames
|
||||
ltx2_initial_latent_path: request.extensions.ltx2.initial_latent_path
|
||||
ltx2_audio_latent_path: request.extensions.ltx2.audio_latent_path
|
||||
- pipeline.model.ltx2.vae_spatial_tile_size_in_pixels
|
||||
- pipeline.model.ltx2.vae_spatial_tile_overlap_in_pixels
|
||||
- pipeline.model.ltx2.vae_temporal_tile_size_in_frames
|
||||
- pipeline.model.ltx2.vae_temporal_tile_overlap_in_frames
|
||||
- pipeline.model.ltx2.initial_latent_path
|
||||
- pipeline.model.ltx2.audio_latent_path
|
||||
- pipeline.model.ltx2.legacy_native_noise_order
|
||||
- pipeline.model.ltx2.use_distilled_sigmas
|
||||
- pipeline.model.ltx2.refine.enabled
|
||||
- pipeline.model.ltx2.refine.num_inference_steps
|
||||
- pipeline.model.ltx2.refine.guidance_scale
|
||||
- pipeline.model.ltx2.refine.add_noise
|
||||
- pipeline.model.ltx2.refine.image_crf
|
||||
- pipeline.model.ltx2.refine.video_position_offset_sec
|
||||
- pipeline.model.ltx2.refine.transformer_path
|
||||
- pipeline.model.ltx2.refine.lora_path
|
||||
- pipeline.model.ltx2.refine.noise_path
|
||||
- pipeline.model.ltx2.refine.audio_noise_path
|
||||
- pipeline.model.minimax_h3.sequential_load
|
||||
- pipeline.model.minimax_h3.video_decode_backend
|
||||
- pipeline.model.minimax_h3.taeh3_checkpoint
|
||||
- pipeline.model.minimax_h3.taeh3_chunk_size
|
||||
- pipeline.model.minimax_h3.vae_parallel_decode
|
||||
- pipeline.model.minimax_h3.vae_parallel_encode
|
||||
- pipeline.model.minimax_h3.vae_parallel_decode_strategy
|
||||
- pipeline.model.longcat.enable_bsa
|
||||
- pipeline.model.longcat.bsa_sparsity
|
||||
- pipeline.model.longcat.bsa_cdf_threshold
|
||||
- pipeline.model.longcat.bsa_chunk_q
|
||||
- pipeline.model.longcat.bsa_chunk_k
|
||||
compatibility_only:
|
||||
mode: "Legacy multi-mode FastVideoArgs switch; typed inference config should not expose execution mode."
|
||||
inference_mode: "Legacy boolean mirror of mode; kept only through adapters while FastVideoArgs remains."
|
||||
lora_target_modules: "Legacy LoRA configuration surface pending dedicated component API."
|
||||
output_type: "Legacy output formatting surface pending GenerationResult cleanup."
|
||||
VSA_sparsity: "Model-specific inference optimization not yet represented in the typed public schema."
|
||||
VSA_tile_size: "VSA-H3 tile geometry request; model-specific optimization not yet represented in the typed public schema."
|
||||
inference_torch_compile: "Regional inference compile opt-in currently carried through PipelineSelection.experimental rather than CompileConfig."
|
||||
vae_parallel_decode: "MiniMax-H3 sequence-parallel VAE decode opt-in; model-specific optimization not yet represented in the typed public schema."
|
||||
h3_sequential_load: "MiniMax-H3 sequential text-encoder then DiT/VAE load; model-specific optimization not yet represented in the typed public schema."
|
||||
video_decode_backend: "MiniMax-H3 video decoder selection (full VAE vs TAEH3 preview); model-specific optimization not yet represented in the typed public schema."
|
||||
taeh3_checkpoint: "Optional local TAEH3 safetensors path; model-specific optimization not yet represented in the typed public schema."
|
||||
taeh3_chunk_size: "TAEH3 temporal chunk length; model-specific optimization not yet represented in the typed public schema."
|
||||
vae_parallel_encode: "MiniMax-H3 sequence-parallel reference VAE encode opt-in; model-specific optimization not yet represented in the typed public schema."
|
||||
vae_parallel_decode_strategy: "Chunk-transport collective for vae_parallel_decode; model-specific optimization not yet represented in the typed public schema."
|
||||
attention_backend: "Process-wide default attention-backend request applied per component at load time; kernel-selection knob not yet represented in the typed public schema."
|
||||
moba_config_path: "Model-specific MoBA optimization surface not yet represented in the typed public schema."
|
||||
master_port: "Executor/bootstrap compatibility field; not part of the canonical inference schema."
|
||||
refine_transformer_path: "Generic stage-2 refine transformer override; no typed equivalent yet."
|
||||
refine_noise_path: "Generic stage-2 refine noise override; no typed equivalent yet."
|
||||
refine_audio_noise_path: "Generic stage-2 refine audio noise override; no typed equivalent yet."
|
||||
ltx2_refine_transformer_path: "LTX-2 refine transformer carrier; no typed equivalent yet."
|
||||
ltx2_refine_noise_path: "LTX-2 refine noise carrier; no typed equivalent yet."
|
||||
ltx2_refine_audio_noise_path: "LTX-2 refine audio noise carrier; no typed equivalent yet."
|
||||
ltx2_legacy_native_noise_order: "LTX-2 SSIM compatibility knob preserving legacy native latent noise ordering."
|
||||
ltx2_use_distilled_sigmas: "LTX-2 compatibility knob gating use of distilled sigma schedule."
|
||||
private_only:
|
||||
ray_placement_group: "Ray deployment-only field."
|
||||
ray_runtime_env: "Ray deployment-only field."
|
||||
pipeline.experimental: "Open mapping for settings without a typed path: the pipeline_config source (a JSON path, a mapping, or a PipelineConfig), keys that runtime code reads by name (for example ray_runtime_env), and PipelineConfig attribute overrides for model-only fields (for example flow_shift_sr). Resolution rejects a key whose PipelineConfig attribute has a typed path."
|
||||
internal_only:
|
||||
pipeline_config: "Legacy internal carrier object."
|
||||
preprocess_config: "Legacy preprocess carrier object."
|
||||
moba_config: "Derived runtime config loaded from moba_config_path."
|
||||
model_paths: "Runtime bookkeeping."
|
||||
model_loaded: "Runtime bookkeeping."
|
||||
engine.attention.moba_config: "V-MoBA attention settings that resolution loads from engine.attention.moba_config_path."
|
||||
|
||||
pipeline_config_base:
|
||||
moved:
|
||||
model_path: generator.model_path
|
||||
pipeline_config_path: generator.pipeline.components.pipeline_config_path
|
||||
embedded_cfg_scale: generator.pipeline.embedded_cfg_scale
|
||||
flow_shift: generator.pipeline.flow_shift
|
||||
disable_autocast: generator.engine.disable_autocast
|
||||
vae_tiling: generator.pipeline.vae_tiling
|
||||
vae_sp: generator.pipeline.vae_sp
|
||||
dmd_denoising_steps: generator.pipeline.dmd_denoising_steps
|
||||
boundary_ratio: generator.pipeline.boundary_ratio
|
||||
dit_precision: generator.engine.precision.dit
|
||||
vae_precision: generator.engine.precision.vae
|
||||
vae_decode_precision: generator.engine.precision.vae_decode
|
||||
image_encoder_precision: generator.engine.precision.image_encoder
|
||||
text_encoder_precisions: generator.engine.precision.text_encoders
|
||||
preset_owned:
|
||||
embedded_cfg_scale: generator.pipeline.preset_overrides.embedded_cfg_scale
|
||||
flow_shift: generator.pipeline.preset_overrides.flow_shift
|
||||
flow_shift_sr: generator.pipeline.preset_overrides.flow_shift_sr
|
||||
is_causal: generator.pipeline.preset_overrides.is_causal
|
||||
vae_tiling: generator.pipeline.preset_overrides.vae_tiling
|
||||
vae_sp: generator.pipeline.preset_overrides.vae_sp
|
||||
dmd_denoising_steps: generator.pipeline.preset_overrides.dmd_denoising_steps
|
||||
ti2v_task: generator.pipeline.preset_overrides.ti2v_task
|
||||
lucy_edit_task: generator.pipeline.preset_overrides.lucy_edit_task
|
||||
boundary_ratio: generator.pipeline.preset_overrides.boundary_ratio
|
||||
flow_shift_sr: generator.pipeline.experimental.flow_shift_sr
|
||||
is_causal: generator.pipeline.experimental.is_causal
|
||||
ti2v_task: generator.pipeline.experimental.ti2v_task
|
||||
lucy_edit_task: generator.pipeline.experimental.lucy_edit_task
|
||||
compatibility_only:
|
||||
model_path: "Redundant with generator.model_path."
|
||||
disable_autocast: "Duplicated by generator.engine.disable_autocast during migration."
|
||||
dit_precision: "Precision override pending dedicated typed component precision design."
|
||||
upsampler_precision: "Precision override pending dedicated typed component precision design."
|
||||
vae_precision: "Precision override pending dedicated typed component precision design."
|
||||
vae_decode_precision: "Decode-only precision override pending dedicated typed component precision design."
|
||||
image_encoder_precision: "Precision override pending dedicated typed component precision design."
|
||||
image_encoder_precisions: "Precision overrides pending dedicated typed component precision design."
|
||||
text_encoder_precisions: "Precision override pending dedicated typed component precision design."
|
||||
internal_only:
|
||||
dit_config: "Legacy internal component config object."
|
||||
upsampler_config: "Legacy internal component config object."
|
||||
@@ -144,6 +166,22 @@ surfaces:
|
||||
scheduler_step_in_fp32: "Runtime scheduler precision toggle; not part of the public typed inference API."
|
||||
|
||||
pipeline_config_extensions:
|
||||
moved:
|
||||
enable_bsa:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
target: generator.pipeline.model.longcat.enable_bsa
|
||||
bsa_sparsity:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
target: generator.pipeline.model.longcat.bsa_sparsity
|
||||
bsa_cdf_threshold:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
target: generator.pipeline.model.longcat.bsa_cdf_threshold
|
||||
bsa_chunk_q:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
target: generator.pipeline.model.longcat.bsa_chunk_q
|
||||
bsa_chunk_k:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
target: generator.pipeline.model.longcat.bsa_chunk_k
|
||||
preset_owned:
|
||||
flux2_text_encoder_type:
|
||||
sources:
|
||||
@@ -324,18 +362,8 @@ surfaces:
|
||||
- fastvideo.configs.pipelines.wan.WanI2V720PConfig
|
||||
- fastvideo.configs.pipelines.wan.WanT2V480PConfig
|
||||
- fastvideo.configs.pipelines.wan.WanT2V720PConfig
|
||||
bsa_cdf_threshold:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
bsa_chunk_k:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
bsa_chunk_q:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
bsa_params:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
bsa_sparsity:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
enable_bsa:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
enable_kv_cache:
|
||||
sources: [fastvideo.configs.pipelines.longcat.LongCatT2V480PConfig, fastvideo.configs.pipelines.longcat.LongCatT2V704PConfig]
|
||||
enhance_hf:
|
||||
@@ -520,6 +548,7 @@ surfaces:
|
||||
cfg_truncation: request.sampling.cfg_truncation
|
||||
guidance_rescale: request.sampling.guidance_rescale
|
||||
use_embedded_guidance: request.sampling.use_embedded_guidance
|
||||
embedded_cfg_scale: request.sampling.embedded_cfg_scale
|
||||
true_cfg_scale: request.sampling.true_cfg_scale
|
||||
boundary_ratio: request.sampling.boundary_ratio
|
||||
sigmas: request.sampling.sigmas
|
||||
|
||||
+11
-12
@@ -28,19 +28,15 @@ Minimal usage (from `examples/inference/basic/basic.py`):
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
model_id = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers" # or official_weights/<model_name>/
|
||||
generator = VideoGenerator.from_pretrained(model_id, num_gpus=1)
|
||||
generator = VideoGenerator.from_pretrained(model_id, {"engine": {"num_gpus": 1}})
|
||||
|
||||
sampling = SamplingParam.from_pretrained(model_id)
|
||||
sampling.num_frames = 45
|
||||
video = generator.generate_video(
|
||||
"A vibrant city street at sunset.",
|
||||
sampling_param=sampling,
|
||||
output_path="video_samples",
|
||||
save_video=True,
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": "A vibrant city street at sunset.",
|
||||
"sampling": {"num_frames": 45},
|
||||
"output": {"output_path": "video_samples", "save_video": True},
|
||||
})
|
||||
```
|
||||
|
||||
## Configuration system
|
||||
@@ -74,8 +70,11 @@ not override checkpoint manifests, user pipeline overrides, or component
|
||||
precision settings. HF IDs, local checkpoints, and old config imports retain
|
||||
their existing resolution behavior, including first-match detector ordering.
|
||||
|
||||
`FastVideoArgs` (in `fastvideo/fastvideo_args.py`) provides runtime settings and
|
||||
is passed into pipeline construction and stages.
|
||||
`ResolvedGeneratorConfig` (in `fastvideo/api/resolution.py`) provides runtime
|
||||
settings and is passed into pipeline construction and stages as `resolved_config`.
|
||||
`resolve_inference_config` (in `fastvideo/api/inference_resolution.py`) builds it
|
||||
from the typed config in `fastvideo/api/schema.py` and attaches the model's
|
||||
frozen `PipelineConfig` as `resolved_config.pipeline_config`.
|
||||
|
||||
## Weights and Diffusers format
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ from fastvideo.api import (
|
||||
|
||||
| Surface | Availability | Notes |
|
||||
| --- | --- | --- |
|
||||
| `VideoGenerator.from_pretrained(model_path, **typed_kwargs)` | Today | `typed_kwargs` is a stable subset from `GeneratorConfig` — no flat legacy LTX-2 kwargs (guaranteed after PR 6) |
|
||||
| `VideoGenerator.from_pretrained(model_path, config)` | Today | `config` is a nested `GeneratorConfig` mapping without `model_path`; no flat keywords |
|
||||
| `VideoGenerator.generate(request: GenerationRequest) -> GenerationResult` | Today | Aggregated; Dynamo wraps in `asyncio.to_thread` under `asyncio.Lock` |
|
||||
| `VideoGenerator.generate_async(request) -> AsyncGenerator[VideoEvent, None]` | **PR 7.10** | Canonical execution substrate; sync wrapper reroutes through this |
|
||||
| `VideoGenerator.default_health_check_request() -> GenerationRequest` | **PR 7.10** | 256x256 / 8 frames / 1 step; lets Dynamo build its health payload without knowing any FastVideo internals |
|
||||
@@ -225,7 +225,7 @@ async def init_video_generation(runtime, config, shutdown_endpoints):
|
||||
from fastvideo.api import config_to_dict
|
||||
|
||||
server_args, dynamo_args = config.server_args, config.dynamo_args
|
||||
generator = VideoGenerator.from_pretrained(**config.fastvideo_kwargs())
|
||||
generator = VideoGenerator.from_config(build_generator_config(server_args))
|
||||
|
||||
dump_config(dynamo_args.dump_config_to, config)
|
||||
|
||||
@@ -262,7 +262,8 @@ this adapter can build the config purely from the public typed schema:
|
||||
def build_generator_config(args) -> "GeneratorConfig":
|
||||
from fastvideo.api import (
|
||||
CompileConfig, ComponentConfig, EngineConfig, GeneratorConfig,
|
||||
OffloadConfig, ParallelismConfig, PipelineSelection,
|
||||
LTX2Options, LTX2RefineOptions, OffloadConfig, ParallelismConfig,
|
||||
PipelineSelection,
|
||||
)
|
||||
return GeneratorConfig(
|
||||
model_path=args.model_path,
|
||||
@@ -275,10 +276,8 @@ def build_generator_config(args) -> "GeneratorConfig":
|
||||
pipeline=PipelineSelection(
|
||||
workload_type=args.workload or "t2v",
|
||||
preset=args.preset, # e.g. "ltx2_two_stage"
|
||||
components=ComponentConfig(
|
||||
upsampler_weights=args.refine_upsampler,
|
||||
lora_path=args.refine_lora,
|
||||
),
|
||||
components=ComponentConfig(upsampler_weights=args.refine_upsampler),
|
||||
model=LTX2Options(refine=LTX2RefineOptions(lora_path=args.refine_lora)),
|
||||
),
|
||||
)
|
||||
```
|
||||
@@ -310,8 +309,11 @@ re-chase FastVideo drift:
|
||||
2. `ContinuationState.payload` is JSON-serializable or references
|
||||
opaque blob ids. Dynamo can round-trip it through RPC without
|
||||
special-casing torch tensors.
|
||||
3. `VideoGenerator.from_pretrained` accepts a typed `GeneratorConfig`;
|
||||
legacy flat kwargs are compatibility-only and deprecate in PR 13.
|
||||
3. `VideoGenerator.from_pretrained(model_path, config)` takes a typed
|
||||
`GeneratorConfig` or its nested mapping; any flat keyword raises
|
||||
`TypeError` that points to the nested config.
|
||||
`VideoGenerator.from_config(...)` takes the same settings with
|
||||
`model_path` inside.
|
||||
4. `generate_async` (PR 7.10+) emits events in order
|
||||
`Progress* → Partial* → Final`; the final event always has exactly
|
||||
one occurrence per request.
|
||||
@@ -329,7 +331,6 @@ at FastVideo's CI — before the Dynamo-side integration even knows.
|
||||
* Anything under `fastvideo.pipelines.*` directly (pipelines are
|
||||
internal; presets identify them by name on
|
||||
`PipelineSelection.preset`).
|
||||
* `fastvideo.fastvideo_args.FastVideoArgs` (legacy compat type).
|
||||
* `fastvideo.api.compat.*` private helpers
|
||||
(`_validate_continuation_state` etc.) — the public boundary is
|
||||
`VideoGenerator` + `fastvideo.api`.
|
||||
|
||||
@@ -17,7 +17,7 @@ xDiT vendor. Do not install xDiT for this path.
|
||||
|
||||
| Goal | How | Use two Sparks? |
|
||||
|---|---|---|
|
||||
| Two independent videos at once | One process per box, `num_gpus=1` | Throughput only. Each clip still takes the 1-GPU time for that size. |
|
||||
| Two independent videos at once | One process per box, `engine.num_gpus: 1` | Throughput only. Each clip still takes the 1-GPU time for that size. |
|
||||
| One clip, faster | Ray + `sp_size=2` + parallel VAE | **Yes.** One 768×1344×124 recipe was 292 s vs 374 s on one GB10. |
|
||||
| One clip, longer | Same, more frames | **Yes.** 345 frames (~14.4 s at 24 fps) finished in 587 s at 768×1344. |
|
||||
|
||||
@@ -35,7 +35,7 @@ over ~21 GB/s RoCE.
|
||||
QSFP; do not download 100+ GB twice over Wi-Fi.
|
||||
- Ray in the FastVideo venv (`uv pip install ray` if it is not already there).
|
||||
|
||||
Each Spark has **one** GPU. `num_gpus=2` therefore means two nodes, which is
|
||||
Each Spark has **one** GPU. `engine.num_gpus: 2` therefore means two nodes, which is
|
||||
why the executor must be Ray (`mp` only works inside one process tree).
|
||||
|
||||
## 1. Put IPv4 on the QSFP NICs
|
||||
@@ -197,8 +197,8 @@ sm_100a VSA kernel is not on this chip, so denoise is slower than a GB200
|
||||
| NCCL hangs or uses Wi-Fi | `source spark_pair_env.sh`. Confirm `NCCL_SOCKET_IFNAME` is the QSFP NIC. |
|
||||
| Gloo `connectFullMesh` / `remote=[127.0.0.1]` | Two 1-GPU nodes must not use loopback as the Gloo store. Source `spark_pair_env.sh` so `GLOO_SOCKET_IFNAME` is the QSFP NIC on **each** box. FastVideo no longer copies that NIC name from the driver onto workers. |
|
||||
| Second `generate()` crashes `NoneType.parameters` | Sequential load used to drop the text encoder without reloading it. This branch reloads Qwen for later requests so `--warmup --repeats N` works. |
|
||||
| OOM / `earlyoom` prefers Python | Lazy module load must stay on (do not pass `--no-lazy-module-load`). Peak GPU during 345-frame denoise is ~90 GiB/node. |
|
||||
| `num_gpus=2` on one Spark | Each Spark has one GPU. Use Ray across two nodes, or `num_gpus=1` on one box. |
|
||||
| OOM / `earlyoom` prefers Python | Lazy module load must stay on (do not pass `--no-lazy-module-load` to `basic_fasth3.py` or set `engine.offload.lazy_module_load: false`). Peak GPU during 345-frame denoise is ~90 GiB/node. |
|
||||
| `engine.num_gpus: 2` on one Spark | Each Spark has one GPU. Use Ray across two nodes, or `engine.num_gpus: 1` on one box. |
|
||||
|
||||
## What we are not claiming
|
||||
|
||||
|
||||
@@ -62,10 +62,12 @@ nothing to set. If you run a model that still defaults to an fp32 decode, set th
|
||||
decode-only override yourself:
|
||||
|
||||
```python
|
||||
from fastvideo.configs.pipelines.base import PipelineConfig
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
pipeline_config = PipelineConfig.from_pretrained(model_id)
|
||||
pipeline_config.vae_decode_precision = "bf16" # decode-only; leaves encode precision alone
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_id,
|
||||
"engine": {"precision": {"vae_decode": "bf16"}}, # decode-only; leaves encode precision alone
|
||||
})
|
||||
```
|
||||
|
||||
Decode is output-only, so lowering its precision is safe. (Encode seeds the
|
||||
@@ -166,8 +168,9 @@ is power-cycled. To avoid it:
|
||||
encoder is still resident, the process is a typical `earlyoom` kill (Python is
|
||||
preferred). On unified memory, `lazy_module_load` auto-enables and owns that
|
||||
split (encoder, then DiT, then VAE; DiT can drop before decode). Sequential
|
||||
load is the H3-only fallback when lazy is off; do not pass
|
||||
`--no-lazy-module-load` here. Geometry scalars come from checkpoint
|
||||
load is the H3-only fallback when lazy is off; do not set
|
||||
`engine.offload.lazy_module_load` to false here (`--no-lazy-module-load` in
|
||||
`basic_fasth3.py` and `basic_minimax_h3_t2v.py`). Geometry scalars come from checkpoint
|
||||
`config.json`, not live weights. See [Offloading](../../inference/offloading.md).
|
||||
- **FastH3 TAEH3** (`--video-decode-backend taeh3`) is an opt-in preview decoder.
|
||||
T2VA never materializes the 9.7 GiB video VAE (DiT still loads after Qwen via
|
||||
@@ -201,7 +204,7 @@ A few things that surprise people on this box (beyond the memory notes above):
|
||||
build recent enough to include its `transformers`-compatibility handling before
|
||||
running it.
|
||||
- **MiniMax H3 worker init can look healthy and still die on the first generate**
|
||||
if deferred loading is off (`--no-lazy-module-load` and sequential also off)
|
||||
if deferred loading is off (`engine.offload.lazy_module_load: false` and sequential also off)
|
||||
and encoder, VAE, and DiT load together. On GB10 the log should show
|
||||
`lazy_module_load owns deferral` (or, if lazy is off, sequential
|
||||
`Released MiniMax-H3 text encoder after conditioning` before
|
||||
|
||||
@@ -210,7 +210,7 @@ from fastvideo.pipelines.stages import (
|
||||
InputValidationStage, CLIPTextEncodingStage, TimestepPreparationStage,
|
||||
LatentPreparationStage, DenoisingStage, DecodingStage
|
||||
)
|
||||
from fastvideo.fastvideo_args import FastVideoArgs
|
||||
from fastvideo.api.resolution import ResolvedGeneratorConfig
|
||||
from fastvideo.pipelines.pipeline_batch_info import ForwardBatch
|
||||
import torch
|
||||
|
||||
@@ -226,11 +226,11 @@ class MyCustomPipeline(ComposedPipelineBase):
|
||||
def required_config_modules(self) -> List[str]:
|
||||
return self._required_config_modules
|
||||
|
||||
def initialize_pipeline(self, fastvideo_args: FastVideoArgs):
|
||||
def initialize_pipeline(self, resolved_config: ResolvedGeneratorConfig):
|
||||
"""Initialize pipeline-specific components."""
|
||||
pass
|
||||
|
||||
def create_pipeline_stages(self, fastvideo_args: FastVideoArgs):
|
||||
def create_pipeline_stages(self, resolved_config: ResolvedGeneratorConfig):
|
||||
"""Set up pipeline stages with proper dependency injection."""
|
||||
self.add_stage(
|
||||
stage_name="input_validation_stage",
|
||||
@@ -294,7 +294,7 @@ class MyCustomStage(PipelineStage):
|
||||
self.custom_module = custom_module
|
||||
self.other_param = other_param
|
||||
|
||||
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
|
||||
def forward(self, batch: ForwardBatch, resolved_config: ResolvedGeneratorConfig) -> ForwardBatch:
|
||||
# Access input data
|
||||
input_data = batch.some_attribute
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ PipelineConfig (fastvideo/configs/pipelines/base.py)
|
||||
- Precision settings: `dit_precision`, `vae_precision`,
|
||||
`text_encoder_precisions`.
|
||||
|
||||
These generation and precision attributes hold the model defaults. Resolution
|
||||
copies them into the typed fields of the resolved config (`pipeline.flow_shift`,
|
||||
`engine.precision.dit`, ...), and runtime code reads the typed fields.
|
||||
|
||||
Model-specific subclasses override defaults. For example,
|
||||
`WanT2V480PConfig` sets `flow_shift=3.0` and uses `WanVideoConfig` as
|
||||
its DiT config.
|
||||
@@ -127,9 +131,11 @@ Concrete hierarchy: `DiTConfig` → `DiTArchConfig`, `VAEConfig` →
|
||||
|
||||
- `PipelineConfig.from_pretrained(model_path)` — resolves config class
|
||||
via `get_pipeline_config_cls_from_name()`, instantiates with defaults.
|
||||
- `PipelineConfig.from_kwargs(kwargs)` — resolves class, optionally loads
|
||||
JSON via `load_from_json()`, then applies CLI overrides via
|
||||
`update_config_from_dict()`.
|
||||
- `PipelineConfig.from_source(model_path, source)` — resolves the registry
|
||||
class of `model_path`, then updates it from `source`: a JSON path loaded
|
||||
via `load_from_json()`, a mapping of field values applied via
|
||||
`update_pipeline_config()`, or a `PipelineConfig` that replaces the
|
||||
registry instance.
|
||||
- `dump_to_json()` / `load_from_json()` — JSON persistence. Callable
|
||||
fields and `arch_config` are excluded from dumps.
|
||||
|
||||
@@ -147,7 +153,7 @@ sp = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
|
||||
|
||||
### ComponentLoader (`fastvideo/models/loader/component_loader.py`)
|
||||
|
||||
Abstract base with a `load(model_path, fastvideo_args)` method.
|
||||
Abstract base with a `load(model_path, resolved_config)` method.
|
||||
`ComponentLoader.for_module_type(module_type, library)` is a factory
|
||||
that dispatches to specialized loaders via a `module_loaders` dict:
|
||||
|
||||
@@ -167,7 +173,7 @@ that dispatches to specialized loaders via a `module_loaders` dict:
|
||||
`TransformerLoader` reads `config.json` from the component directory,
|
||||
resolves the class via `ModelRegistry.resolve_model_cls()`, instantiates
|
||||
the model, and loads safetensors weights. CPU offload and layerwise
|
||||
offload are applied based on `FastVideoArgs`.
|
||||
offload are applied based on `resolved_config.engine.offload`.
|
||||
|
||||
Unknown module types fall back to `GenericComponentLoader`.
|
||||
|
||||
@@ -205,14 +211,14 @@ loading by calling `ComponentLoader.for_module_type()` then `.load()`.
|
||||
|
||||
Abstract base class using the Template Method pattern:
|
||||
|
||||
- `__call__(batch, fastvideo_args)` — orchestrates verification, timing,
|
||||
- `__call__(batch, resolved_config)` — orchestrates verification, timing,
|
||||
and error handling. Not overridden by subclasses.
|
||||
- `forward(batch, fastvideo_args) -> ForwardBatch` — abstract, contains
|
||||
- `forward(batch, resolved_config) -> ForwardBatch` — abstract, contains
|
||||
the stage logic.
|
||||
- `verify_input()` / `verify_output()` — optional hooks returning
|
||||
`VerificationResult`. Default: no checks.
|
||||
|
||||
When `fastvideo_args.enable_stage_verification` is `True`, `__call__`
|
||||
When `resolved_config.engine.enable_stage_verification` is `True`, `__call__`
|
||||
runs input verification before `forward()` and output verification after.
|
||||
When `envs.FASTVIDEO_STAGE_LOGGING` is set, execution time is measured
|
||||
with `torch.cuda.synchronize()` and logged.
|
||||
@@ -295,7 +301,7 @@ provides detailed error messages. Failed verification raises
|
||||
|
||||
Abstract base for all inference pipelines. Lifecycle:
|
||||
|
||||
1. **`__init__(model_path, fastvideo_args)`** — initializes distributed
|
||||
1. **`__init__(model_path, resolved_config)`** — initializes distributed
|
||||
environment via `maybe_init_distributed_environment_and_model_parallel
|
||||
(tp_size, sp_size)`, then calls `load_modules()` to populate
|
||||
`self.modules`.
|
||||
@@ -303,7 +309,7 @@ Abstract base for all inference pipelines. Lifecycle:
|
||||
setup), `create_pipeline_stages()` (abstract — subclasses wire stages),
|
||||
optionally applies `torch.compile` to transformers, and calls
|
||||
`warmup_sequence_parallel_communication()`.
|
||||
3. **`forward(batch, fastvideo_args)`** — iterates `self.stages` calling
|
||||
3. **`forward(batch, resolved_config)`** — iterates `self.stages` calling
|
||||
each stage in order. Decorated with `@torch.no_grad()`.
|
||||
|
||||
Key class attributes:
|
||||
@@ -316,8 +322,9 @@ Key methods:
|
||||
- `add_stage(name, stage)` — appends to `_stages` list and
|
||||
`_stage_name_mapping` dict, also sets attribute on `self`.
|
||||
- `get_module(name, default)` — retrieves a loaded module.
|
||||
- `from_pretrained(model_path, **kwargs)` — class method constructing
|
||||
`FastVideoArgs` and calling `cls(...)` then `post_init()`.
|
||||
- `from_pretrained(model_path, *, resolved_config)` — class method that
|
||||
builds the pipeline from a resolved config (from
|
||||
`resolve_inference_config({...})`) by calling `cls(...)` then `post_init()`.
|
||||
|
||||
### LoRAPipeline (`fastvideo/pipelines/lora_pipeline.py`)
|
||||
|
||||
@@ -352,12 +359,12 @@ Key APIs: `get_tp_rank()`, `get_tp_world_size()`, `get_sp_rank()`,
|
||||
`warmup_sequence_parallel_communication()` pre-warms NCCL communicators
|
||||
to avoid slow first forward passes.
|
||||
|
||||
Usage: `torchrun --nproc-per-node=N -m fastvideo.entrypoints.cli.main
|
||||
generate --model-path ... --tp-size N --sp-size M`.
|
||||
Usage: `fastvideo generate --config run.yaml
|
||||
--generator.engine.parallelism.tp_size N --generator.engine.parallelism.sp_size M`.
|
||||
|
||||
### torch.compile Integration
|
||||
|
||||
When `fastvideo_args.enable_torch_compile` is `True`,
|
||||
When `resolved_config.engine.compile.enabled` is `True`,
|
||||
`_maybe_compile_pipeline_module()` checks for a `_compile_conditions`
|
||||
attribute on the module. If present, only matching submodules are
|
||||
compiled. Otherwise, the entire module is compiled. FSDP-wrapped
|
||||
@@ -369,47 +376,53 @@ modules are skipped.
|
||||
|
||||
```python
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_path="Wan-AI/Wan2.1-T2V-14B-Diffusers",
|
||||
num_gpus=1, tp_size=1, sp_size=1,
|
||||
)
|
||||
result = generator.generate_video(
|
||||
prompt="A cat dancing",
|
||||
height=720, width=1280, num_frames=81,
|
||||
"Wan-AI/Wan2.1-T2V-14B-Diffusers",
|
||||
{"engine": {"num_gpus": 1, "parallelism": {"tp_size": 1, "sp_size": 1}}},
|
||||
)
|
||||
result = generator.generate({
|
||||
"prompt": "A cat dancing",
|
||||
"sampling": {"height": 720, "width": 1280, "num_frames": 81},
|
||||
})
|
||||
```
|
||||
|
||||
**CLI** (`fastvideo/entrypoints/cli/`):
|
||||
|
||||
```bash
|
||||
# run.yaml holds `generator: {model_path: Wan-AI/Wan2.1-T2V-14B-Diffusers}`.
|
||||
fastvideo generate \
|
||||
--model-path "Wan-AI/Wan2.1-T2V-14B-Diffusers" \
|
||||
--prompt "A cat dancing" \
|
||||
--num-gpus 1
|
||||
--config run.yaml \
|
||||
--request.prompt "A cat dancing" \
|
||||
--generator.engine.num_gpus 1
|
||||
```
|
||||
|
||||
**FastVideoArgs** (`fastvideo/fastvideo_args.py`): Central args dataclass.
|
||||
Key fields: `model_path`, `mode` (`ExecutionMode`), `workload_type`
|
||||
(`WorkloadType`), `pipeline_config` (`PipelineConfig`), `num_gpus`,
|
||||
`tp_size`, `sp_size`, `lora_path`, `dit_cpu_offload`,
|
||||
`dit_layerwise_offload`, `enable_torch_compile`,
|
||||
`enable_stage_verification`.
|
||||
**ResolvedGeneratorConfig** (`fastvideo/api/resolution.py`): The frozen
|
||||
runtime config that the executor, workers, pipelines, stages, and loaders
|
||||
read. Key paths: `model_path`, `mode` (`ExecutionMode`),
|
||||
`pipeline.workload_type` (`WorkloadType`), `engine.num_gpus`,
|
||||
`engine.parallelism.tp_size`, `engine.parallelism.sp_size`,
|
||||
`pipeline.components.lora_path`, `engine.offload.dit`,
|
||||
`engine.offload.dit_layerwise`, `engine.compile.enabled`,
|
||||
`engine.enable_stage_verification`, and `pipeline_config` (the frozen
|
||||
`PipelineConfig`).
|
||||
|
||||
Constructed via `FastVideoArgs.from_kwargs(**kwargs)` which resolves the
|
||||
`PipelineConfig` from the registry, applies JSON config if provided, and
|
||||
merges CLI overrides.
|
||||
Built by `resolve_inference_config(config)`
|
||||
(`fastvideo/api/inference_resolution.py`), which runs the named resolution
|
||||
steps (environment variables, model defaults, derived values, validation) in
|
||||
order, records each decision, and then builds the `PipelineConfig` from the
|
||||
registry, applies a JSON config if provided, and freezes it.
|
||||
|
||||
## End-to-End Inference Flow
|
||||
|
||||
```
|
||||
User: VideoGenerator.from_pretrained(model_path, **kwargs)
|
||||
User: VideoGenerator.from_pretrained(model_path, config)
|
||||
│
|
||||
├─ FastVideoArgs.from_kwargs() → PipelineConfig resolved via registry
|
||||
├─ resolve_inference_config() → PipelineConfig resolved via registry
|
||||
├─ get_model_info() → ModelInfo(pipeline_cls, sampling_param_cls, ...)
|
||||
│ ├─ model_index.json read → _class_name extracted
|
||||
│ ├─ pipeline_registry resolves pipeline_cls from _class_name
|
||||
│ └─ config_registry resolves config classes from model_path
|
||||
│
|
||||
├─ pipeline_cls.__init__(model_path, fastvideo_args)
|
||||
├─ pipeline_cls.__init__(model_path, resolved_config)
|
||||
│ ├─ maybe_init_distributed(tp_size, sp_size)
|
||||
│ └─ load_modules() → reads model_index.json, loads each component
|
||||
│ ├─ ComponentLoader.for_module_type() → specialized loader
|
||||
@@ -421,10 +434,10 @@ User: VideoGenerator.from_pretrained(model_path, **kwargs)
|
||||
├─ torch.compile (if enabled)
|
||||
└─ warmup_sequence_parallel_communication()
|
||||
|
||||
User: generator.generate_video(prompt, ...)
|
||||
User: generator.generate(request)
|
||||
│
|
||||
├─ ForwardBatch constructed from SamplingParam + user args
|
||||
└─ pipeline.forward(batch, fastvideo_args)
|
||||
└─ pipeline.forward(batch, resolved_config)
|
||||
├─ InputValidationStage → validates dims
|
||||
├─ TextEncodingStage → prompt → embeddings
|
||||
├─ ConditioningStage → prepares conditioning
|
||||
|
||||
@@ -8,7 +8,7 @@ FastVideo automatically distributes the generation process when multiple GPUs ar
|
||||
# Will use 4 GPUs in parallel for faster generation
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=4,
|
||||
{"engine": {"num_gpus": 4}},
|
||||
)
|
||||
```
|
||||
|
||||
@@ -30,58 +30,53 @@ bring-up: [Pair two NVIDIA DGX Sparks](../getting_started/installation/spark_pai
|
||||
|
||||
## Customizing Generation
|
||||
|
||||
- `PipelineConfig`: Initialization time parameters
|
||||
- `SamplingParam`: Generation time parameters
|
||||
|
||||
You can customize generation behavior using `PipelineConfig` and
|
||||
`SamplingParam`:
|
||||
`VideoGenerator.from_pretrained(model_path, config)` takes the startup
|
||||
settings as a nested mapping at their typed config paths, such as
|
||||
`{"engine": {"num_gpus": 2, "offload": {"dit": False}}}`; it is
|
||||
`VideoGenerator.from_config` with `model_path` added to the mapping. Pass
|
||||
generation settings to `VideoGenerator.generate` as a request:
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator, SamplingParam, PipelineConfig
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
def main():
|
||||
model_name = "Wan-AI/Wan2.1-T2V-1.3B-Diffusers"
|
||||
config = PipelineConfig.from_pretrained(model_name)
|
||||
config.vae_precision = "fp16"
|
||||
|
||||
# Create the generator
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_name,
|
||||
num_gpus=1,
|
||||
dit_layerwise_offload=True, # FastVideoArgs option
|
||||
pipeline_config=config
|
||||
)
|
||||
|
||||
# Create and customize sampling parameters
|
||||
sampling_param = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
|
||||
|
||||
# How many frames to generate
|
||||
sampling_param.num_frames = 45
|
||||
|
||||
# Video resolution (width, height)
|
||||
sampling_param.width = 1024
|
||||
sampling_param.height = 576
|
||||
|
||||
# How many steps we denoise the video (higher = better quality, slower generation)
|
||||
sampling_param.num_inference_steps = 30
|
||||
|
||||
# How strongly the video conforms to the prompt (higher = more faithful to prompt)
|
||||
sampling_param.guidance_scale = 7.5
|
||||
|
||||
# Random seed for reproducibility
|
||||
sampling_param.seed = 42 # Optional, leave unset for random results
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_name,
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"offload": {"dit_layerwise": True},
|
||||
"precision": {"vae": "fp16"},
|
||||
},
|
||||
})
|
||||
|
||||
# Generate video with custom parameters
|
||||
prompt = "A beautiful sunset over a calm ocean, with gentle waves."
|
||||
video = generator.generate_video(
|
||||
prompt,
|
||||
sampling_param=sampling_param,
|
||||
output_path="my_videos/", # Controls where videos are saved
|
||||
save_video=True
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"sampling": {
|
||||
# How many frames to generate
|
||||
"num_frames": 45,
|
||||
# Video resolution (width, height)
|
||||
"width": 1024,
|
||||
"height": 576,
|
||||
# How many steps we denoise the video (higher = better quality, slower generation)
|
||||
"num_inference_steps": 30,
|
||||
# How strongly the video conforms to the prompt (higher = more faithful to prompt)
|
||||
"guidance_scale": 7.5,
|
||||
# Random seed for reproducibility
|
||||
"seed": 42, # Optional, leave unset for random results
|
||||
},
|
||||
"output": {
|
||||
"output_path": "my_videos/", # Controls where videos are saved
|
||||
"save_video": True,
|
||||
},
|
||||
})
|
||||
|
||||
# If return_frames=True, frames are available in video["frames"]
|
||||
print(f"Generated {len(video['frames'])} frames")
|
||||
# If return_frames=True, frames are available in video.frames
|
||||
print(f"Generated {len(video.frames)} frames")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -125,6 +120,26 @@ Override individual values from the CLI with dotted paths:
|
||||
fastvideo generate --config config.yaml --request.sampling.seed 42
|
||||
```
|
||||
|
||||
## Where a Value Came From
|
||||
|
||||
FastVideo resolves the generator config once at startup and records the source of every value: the input config
|
||||
(`input`; `explicit` tells whether you wrote the value or it is the schema default), a `FASTVIDEO_*` environment
|
||||
variable, the model's defaults, or a derived value. A worker's device policy and values read from checkpoint files
|
||||
are recorded too.
|
||||
|
||||
```python
|
||||
generator = VideoGenerator.from_config(config)
|
||||
generator.resolved_config.provenance("engine.parallelism.sp_size")
|
||||
# PathProvenance(path='engine.parallelism.sp_size', value=2, source='derive_parallel_sizes', ...)
|
||||
|
||||
result = generator.generate(request)
|
||||
result.resolved_request.provenance("sampling.num_frames")
|
||||
# PathProvenance(..., value=81, source='fill_sampling_defaults[preset wan_t2v_1_3b]', explicit=False)
|
||||
```
|
||||
|
||||
`resolved_config.provenance_table()` lists every path. Every value is decided before resolution ends, including the
|
||||
device offload policy and the checkpoint defaults; after that, `resolved_config` is read-only.
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
For configuring optimizations, please see our [optimizations guide](optimizations.md)
|
||||
|
||||
@@ -66,7 +66,7 @@ as above.
|
||||
|
||||
For exports without this sidecar, an explicit ladder is supported via
|
||||
`MiniMaxH3PipelineConfig.dmd_denoising_steps`, or through the typed API's
|
||||
`PipelineSelection(experimental={"dmd_denoising_steps": [...]})`. The shifts
|
||||
`PipelineSelection(dmd_denoising_steps=[...])`. The shifts
|
||||
still come from the checkpoint scheduler configs. Keep generic `flow_shift`
|
||||
unset: H3 has separate video and audio shifts, not one shared shift.
|
||||
|
||||
|
||||
@@ -38,18 +38,20 @@ def main():
|
||||
# Create a video generator with a pre-trained model
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=1, # Adjust based on your hardware
|
||||
{"engine": {"num_gpus": 1}}, # Adjust based on your hardware
|
||||
)
|
||||
|
||||
# Define a prompt for your video
|
||||
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes wide with interest."
|
||||
|
||||
# Generate the video
|
||||
video = generator.generate_video(
|
||||
prompt,
|
||||
output_path="my_videos/", # Controls where videos are saved
|
||||
save_video=True
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"output": {
|
||||
"output_path": "my_videos/", # Controls where videos are saved
|
||||
"save_video": True,
|
||||
},
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -75,23 +77,24 @@ Please see the [support matrix](support_matrix.md) for the list of supported mod
|
||||
You can generate a video starting from an initial image:
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator, SamplingParam
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
def main():
|
||||
# Create the generator
|
||||
model_name = "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers"
|
||||
generator = VideoGenerator.from_pretrained(model_name, num_gpus=1)
|
||||
|
||||
# Set up parameters with an initial image
|
||||
sampling_param = SamplingParam.from_pretrained(model_name)
|
||||
sampling_param.image_path = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"
|
||||
sampling_param.num_frames = 107
|
||||
generator = VideoGenerator.from_pretrained(model_name, {"engine": {"num_gpus": 1}})
|
||||
|
||||
# Generate video based on the image
|
||||
prompt = "A photograph coming to life with gentle movement"
|
||||
generator.generate_video(prompt, sampling_param=sampling_param,
|
||||
output_path="my_videos/",
|
||||
save_video=True)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
# Set up parameters with an initial image
|
||||
"inputs": {
|
||||
"image_path": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg",
|
||||
},
|
||||
"sampling": {"num_frames": 107},
|
||||
"output": {"output_path": "my_videos/", "save_video": True},
|
||||
})
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -106,12 +109,12 @@ Common issues and their solutions:
|
||||
If you encounter CUDA out of memory errors:
|
||||
|
||||
- Reduce `num_frames` or video resolution
|
||||
- Enable FastVideo offloading options such as `dit_layerwise_offload=True`
|
||||
(single GPU) or `use_fsdp_inference=True` (multi-GPU)
|
||||
- Enable FastVideo offloading options such as `engine.offload.dit_layerwise: true`
|
||||
(single GPU) or `engine.use_fsdp_inference: true` (multi-GPU)
|
||||
- Try a smaller model or use distilled versions
|
||||
- Use `num_gpus` > 1 if multiple GPUs are available
|
||||
- Try enabling FSDP inference with `use_fsdp_inference=True` (may slow down generation)
|
||||
- Try enabling DiT layerwise offload with `dit_layerwise_offload=True` (now only a few models support this, but may introduce less overhead than FSDP)
|
||||
- Use `engine.num_gpus` > 1 if multiple GPUs are available
|
||||
- Try enabling FSDP inference with `engine.use_fsdp_inference: true` (may slow down generation)
|
||||
- Try enabling DiT layerwise offload with `engine.offload.dit_layerwise: true` (now only a few models support this, but may introduce less overhead than FSDP)
|
||||
|
||||
### Slow Generation
|
||||
|
||||
|
||||
@@ -4,15 +4,17 @@ This page describes how to use offloading techniques for inference to reduce GPU
|
||||
|
||||
## Default Behavior
|
||||
|
||||
```python
|
||||
dit_cpu_offload: bool = True
|
||||
use_fsdp_inference: bool = False
|
||||
dit_layerwise_offload: bool = True
|
||||
text_encoder_cpu_offload: bool = True
|
||||
image_encoder_cpu_offload: bool = True
|
||||
vae_cpu_offload: bool = True
|
||||
pin_cpu_memory: bool = True
|
||||
lazy_module_load: bool | None = None
|
||||
```yaml
|
||||
engine:
|
||||
use_fsdp_inference: false
|
||||
offload:
|
||||
dit: true # dit_cpu_offload
|
||||
dit_layerwise: true # dit_layerwise_offload
|
||||
text_encoder: true # text_encoder_cpu_offload
|
||||
image_encoder: true # image_encoder_cpu_offload
|
||||
vae: true # vae_cpu_offload
|
||||
pin_cpu_memory: true
|
||||
lazy_module_load: null # auto
|
||||
```
|
||||
|
||||
On unified-memory accelerators such as NVIDIA GB10 and Apple silicon, FastVideo
|
||||
@@ -35,8 +37,8 @@ channels, DiT patch size) so those stages do not materialize weights just to
|
||||
read two integers. The MLX FastH3 runtime always uses this phase order. When
|
||||
host offload is off, DiT safetensors are read onto the accelerator instead of
|
||||
CPU-then-copy. Both flags default to auto (`None`) and turn on for
|
||||
unified-memory devices such as GB10; lazy then disables sequential. Pass
|
||||
`--no-lazy-module-load` to keep every component resident (sequential may still
|
||||
unified-memory devices such as GB10; lazy then disables sequential. Set
|
||||
`engine.offload.lazy_module_load: false` to keep every component resident (sequential may still
|
||||
auto-arm). Two-node Spark
|
||||
jobs still need this split: sequence parallel replicates the DiT on each GB10
|
||||
(~66 GiB of weights plus activations). See
|
||||
@@ -45,7 +47,10 @@ jobs still need this split: sequence parallel replicates the DiT on each GB10
|
||||
## Behavior Explanation
|
||||
|
||||
!!! note
|
||||
For CLI usage, replace underscores (`_`) with hyphens (`-`).
|
||||
`VideoGenerator.from_pretrained` accepts the option names below as keywords, except `lazy_module_load` and
|
||||
`h3_sequential_load`. In a YAML config or a dotted override, each option is a typed field:
|
||||
`engine.use_fsdp_inference`, `engine.offload.<field>` as listed in the defaults above, and
|
||||
`pipeline.model.minimax_h3.sequential_load` for `h3_sequential_load`.
|
||||
|
||||
### `use_fsdp_inference`
|
||||
|
||||
@@ -104,8 +109,8 @@ because the encoder has been released.
|
||||
Leave the default on Spark / DGX Spark when `lazy_module_load` is off. When
|
||||
both would arm (the GB10 auto case), lazy owns deferral and sequential stands
|
||||
down so VAE `torch.compile` can attach to the lazy proxy. Force
|
||||
`--h3-sequential-load` only when you need the split on a discrete GPU without
|
||||
lazy load. Use `--no-h3-sequential-load` when you need more than one prompt per
|
||||
`pipeline.model.minimax_h3.sequential_load: true` only when you need the split on a discrete GPU without
|
||||
lazy load. Set `pipeline.model.minimax_h3.sequential_load: false` when you need more than one prompt per
|
||||
worker and have enough memory to keep the encoder.
|
||||
|
||||
### `text_encoder_cpu_offload`
|
||||
@@ -160,7 +165,7 @@ options above cannot help with because they act after loading. It is
|
||||
particularly relevant on unified-memory devices, where host and device draw on
|
||||
the same pool and moving weights to the host frees nothing. FastVideo
|
||||
auto-enables it there (`lazy_module_load=None`). Leave it off when the model
|
||||
already fits, or pass `--no-lazy-module-load` to keep components resident for
|
||||
already fits, or set `engine.offload.lazy_module_load: false` to keep components resident for
|
||||
later `generate()` calls.
|
||||
|
||||
This option applies to inference only. Training keeps every component resident
|
||||
@@ -203,19 +208,25 @@ from fastvideo import VideoGenerator
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=1,
|
||||
# Recommended for single GPU
|
||||
dit_layerwise_offload=True,
|
||||
# Enable if OOM happens
|
||||
vae_cpu_offload=True,
|
||||
image_encoder_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
# Speeds up CPU-GPU transfer
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"offload": {
|
||||
# Recommended for single GPU
|
||||
"dit_layerwise": True,
|
||||
# Enable if OOM happens
|
||||
"vae": True,
|
||||
"image_encoder": True,
|
||||
"text_encoder": True,
|
||||
# Speeds up CPU-GPU transfer
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = "A curious raccoon peers through a vibrant field of yellow sunflowers."
|
||||
video = generator.generate_video(prompt, output_path="output/", save_video=True)
|
||||
video = generator.generate({"prompt": prompt, "output": {"output_path": "output/", "save_video": True}})
|
||||
```
|
||||
|
||||
### Multi-GPU with FSDP
|
||||
@@ -225,18 +236,24 @@ from fastvideo import VideoGenerator
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=2,
|
||||
# Recommended for multi-GPU
|
||||
use_fsdp_inference=True,
|
||||
dit_layerwise_offload=False,
|
||||
dit_cpu_offload=False,
|
||||
# Enable if OOM happens
|
||||
vae_cpu_offload=True,
|
||||
image_encoder_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 2,
|
||||
# Recommended for multi-GPU
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit_layerwise": False,
|
||||
"dit": False,
|
||||
# Enable if OOM happens
|
||||
"vae": True,
|
||||
"image_encoder": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = "A majestic lion strides across the golden savanna."
|
||||
video = generator.generate_video(prompt, output_path="output/", save_video=True)
|
||||
video = generator.generate({"prompt": prompt, "output": {"output_path": "output/", "save_video": True}})
|
||||
```
|
||||
|
||||
@@ -166,22 +166,26 @@ Enable FP4 attention via the `--nvfp4_fa4` flag:
|
||||
python examples/inference/optimizations/fp4_attn_wan2_1_1_3b.py --nvfp4_fa4
|
||||
```
|
||||
|
||||
Or in Python via the `nvfp4_fa4` kwarg (sets env vars automatically):
|
||||
Or in Python via the `engine.attention.nvfp4_fa4` field (resolution sets the env vars):
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator
|
||||
gen = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
nvfp4_fa4=True,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # FSDP is incompatible with FP4 pointer path
|
||||
{
|
||||
"engine": {
|
||||
"attention": {"nvfp4_fa4": True},
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # FSDP is incompatible with FP4 pointer path
|
||||
},
|
||||
},
|
||||
)
|
||||
gen.generate_video(prompt="A raccoon in sunflowers", save_video=True)
|
||||
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
|
||||
```
|
||||
|
||||
#### Known Limitations
|
||||
|
||||
- `use_fsdp_inference=True` is incompatible with the FP4 path (FSDP shards invalidate tensor pointers)
|
||||
- `engine.use_fsdp_inference: true` is incompatible with the FP4 path (FSDP shards invalidate tensor pointers)
|
||||
- Per-call cosine similarity vs BF16: ~0.99 (slight quantization error accumulates over denoising steps)
|
||||
- Only supports `headdim >= 128`
|
||||
|
||||
@@ -205,15 +209,15 @@ import os
|
||||
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "ATTN_QAT_INFER"
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.layers.quantization import get_quantization_config
|
||||
gen = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=1,
|
||||
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific). Pass an
|
||||
# instance — the bare string is not resolved on the from_pretrained path.
|
||||
transformer_quant=get_quantization_config("nvfp4_qat")(),
|
||||
use_fsdp_inference=False, # FSDP shards invalidate the FP4 tensor pointers
|
||||
)
|
||||
gen = VideoGenerator.from_config({
|
||||
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # FSDP shards invalidate the FP4 tensor pointers
|
||||
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific).
|
||||
"quantization": {"transformer_quant": "nvfp4_qat"},
|
||||
},
|
||||
})
|
||||
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
|
||||
```
|
||||
|
||||
@@ -301,17 +305,34 @@ automatically.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
gen = VideoGenerator.from_config({
|
||||
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
"engine": {"quantization": {"transformer_quant": "FP8"}}, # per-tensor (default)
|
||||
})
|
||||
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
|
||||
```
|
||||
|
||||
`engine.quantization.transformer_quant` takes a quantization registry name and builds that config with its default
|
||||
arguments. To pass constructor arguments, such as per-channel granularity, set the config instance on the DiT config
|
||||
through `pipeline.model.generic.dit` instead:
|
||||
|
||||
```python
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.layers.quantization import get_quantization_config
|
||||
|
||||
gen = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
# Pass an instance — the bare string is not resolved on the from_pretrained path.
|
||||
transformer_quant=get_quantization_config("FP8")(), # per-tensor (default)
|
||||
# transformer_quant=get_quantization_config("FP8")(granularity="channel"), # slower, higher accuracy
|
||||
)
|
||||
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
|
||||
gen = VideoGenerator.from_config({
|
||||
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
"pipeline": {
|
||||
"model": {
|
||||
"generic": {
|
||||
"dit": {"quant_config": get_quantization_config("FP8")(granularity="channel")}, # slower, higher accuracy
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Or run the example script:
|
||||
@@ -341,7 +362,7 @@ end-to-end speedup. It is **off by default** and enabled per-run.
|
||||
```python
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
enable_torch_compile=True,
|
||||
{"engine": {"compile": {"enabled": True}}},
|
||||
)
|
||||
```
|
||||
|
||||
@@ -376,12 +397,14 @@ device is unsupported. Legacy VSA, MiniMax-H3 tile-256 VSA, and the explicit
|
||||
eager with one warning instead of failing mid-denoise.
|
||||
|
||||
```python
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"MiniMaxAI/MiniMax-H3",
|
||||
inference_torch_compile=True, # or FASTVIDEO_INFERENCE_TORCH_COMPILE=1
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "MiniMaxAI/MiniMax-H3",
|
||||
"engine": {"compile": {"regional": True}}, # or FASTVIDEO_INFERENCE_TORCH_COMPILE=1
|
||||
})
|
||||
```
|
||||
|
||||
In a YAML config, set `generator.engine.compile.regional: true`.
|
||||
|
||||
Do not combine it with `torch_compile_kwargs['mode']` (the loader injects
|
||||
inductor options, and torch.compile forbids mode+options); it is
|
||||
independent of `enable_torch_compile`, and when both are set the regional
|
||||
@@ -390,7 +413,7 @@ compile wins for the DiT.
|
||||
### What to expect from generic compile
|
||||
|
||||
The Wan result below measures the existing generic
|
||||
`enable_torch_compile=True` path. It is useful evidence that compile can help,
|
||||
`engine.compile.enabled: true` path. It is useful evidence that compile can help,
|
||||
but it is **not** a benchmark or numerical gate for the stricter regional
|
||||
fullgraph path above.
|
||||
|
||||
@@ -433,12 +456,12 @@ not asserted by any standing SSIM regression here — the SSIM tests in
|
||||
run with `enable_torch_compile` disabled. If you depend on compile
|
||||
output staying close to eager (or your previous compiled run), run an
|
||||
MS-SSIM gate on *your* config, especially when combining
|
||||
`enable_torch_compile=True` with other numerics-affecting flags
|
||||
`engine.compile.enabled: true` with other numerics-affecting flags
|
||||
(quantized attention backends, FP4, layerwise offload edge cases).
|
||||
|
||||
### Known interactions
|
||||
|
||||
- **Layerwise CPU offload** (`dit_layerwise_offload=True`, the default):
|
||||
- **Layerwise CPU offload** (`engine.offload.dit_layerwise: true`, the default):
|
||||
the offload hook previously caused an implicit graph break once per
|
||||
transformer layer, fragmenting the compiled region. Addressed in
|
||||
hao-ai-lab/FastVideo#1365 — keep that fix to get a clean compiled
|
||||
@@ -453,16 +476,17 @@ MS-SSIM gate on *your* config, especially when combining
|
||||
grad-enabled path remain outside it. Use the default inductor mode shown
|
||||
above unless your exact configuration has its own gate.
|
||||
|
||||
Extra `torch.compile` options are passed through `torch_compile_kwargs`
|
||||
(a dict), accepted by `VideoGenerator.from_pretrained(...)` and by the
|
||||
CLI as a JSON string via `--torch-compile-kwargs`. Example (currently
|
||||
Extra `torch.compile` options live at `engine.compile.backend`,
|
||||
`fullgraph`, `mode`, and `dynamic`; any other `torch.compile` kwargs go in
|
||||
`engine.compile.extras`. Set them in the nested config, in a config file,
|
||||
or as a CLI dotted override (for example
|
||||
`--generator.engine.compile.mode reduce-overhead`). Example (currently
|
||||
**not** recommended — see the CUDA-graphs caveat above):
|
||||
|
||||
```python
|
||||
VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
enable_torch_compile=True,
|
||||
torch_compile_kwargs={"mode": "reduce-overhead"}, # may error today
|
||||
{"engine": {"compile": {"enabled": True, "mode": "reduce-overhead"}}}, # may error today
|
||||
)
|
||||
```
|
||||
|
||||
@@ -475,7 +499,7 @@ config; **discard the first generation** (graph build):
|
||||
import time
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
gen = VideoGenerator.from_pretrained("your-model-id", enable_torch_compile=True)
|
||||
gen = VideoGenerator.from_pretrained("your-model-id", {"engine": {"compile": {"enabled": True}}})
|
||||
req = {"prompt": "Your prompt", "sampling": {"seed": 1024},
|
||||
"output": {"save_video": False}}
|
||||
gen.generate(req) # warmup: graph build, discard
|
||||
@@ -499,10 +523,10 @@ for backend in ["TORCH_SDPA", "FLASH_ATTN", "SAGE_ATTN"]:
|
||||
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = backend
|
||||
generator = VideoGenerator.from_pretrained("your-model-id")
|
||||
start_time = time.perf_counter()
|
||||
generator.generate_video(
|
||||
prompt="Your prompt",
|
||||
seed=1024,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": "Your prompt",
|
||||
"sampling": {"seed": 1024},
|
||||
})
|
||||
elapsed = time.perf_counter() - start_time
|
||||
print(f"{backend}: {elapsed:.2f}s")
|
||||
```
|
||||
|
||||
@@ -19,46 +19,56 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/preprocess_wan_data_t2v_
|
||||
|
||||
## Preprocessing Pipeline
|
||||
|
||||
The new preprocessing pipeline supports multiple dataset formats and video loaders:
|
||||
The preprocessing pipeline supports multiple dataset formats and video loaders. It reads a `PreprocessRunConfig`
|
||||
YAML file (`fastvideo/api/training_schema.py`): the model and workload type at the top level, and the preprocessing
|
||||
settings in the `preprocess` section (the fields of `PreprocessConfig` in `fastvideo/configs/configs.py`):
|
||||
|
||||
```yaml
|
||||
# preprocess_t2v.yaml
|
||||
model_path: Wan-AI/Wan2.1-T2V-1.3B-Diffusers
|
||||
pipeline:
|
||||
workload_type: t2v
|
||||
preprocess:
|
||||
video_loader_type: torchvision
|
||||
dataset_type: merged
|
||||
preprocess_video_batch_size: 2
|
||||
dataloader_num_workers: 0
|
||||
max_height: 480
|
||||
max_width: 832
|
||||
num_frames: 77
|
||||
train_fps: 16
|
||||
samples_per_file: 8
|
||||
flush_frequency: 8
|
||||
video_length_tolerance_range: 5
|
||||
```
|
||||
|
||||
Pass the file with `--config`. Each dotted override after it sets one field, for example the values that come from
|
||||
shell variables:
|
||||
|
||||
```bash
|
||||
GPU_NUM=2
|
||||
MODEL_PATH="Wan-AI/Wan2.1-T2V-1.3B-Diffusers"
|
||||
DATASET_PATH="data/crush-smol/"
|
||||
OUTPUT_DIR="data/crush-smol_processed_t2v/"
|
||||
|
||||
torchrun --nproc_per_node=$GPU_NUM \
|
||||
-m fastvideo.pipelines.preprocess.v1_preprocessing_new \
|
||||
--model_path $MODEL_PATH \
|
||||
--mode preprocess \
|
||||
--workload_type t2v \
|
||||
--preprocess.video_loader_type torchvision \
|
||||
--preprocess.dataset_type merged \
|
||||
--preprocess.dataset_path $DATASET_PATH \
|
||||
--preprocess.dataset_output_dir $OUTPUT_DIR \
|
||||
--preprocess.preprocess_video_batch_size 2 \
|
||||
--preprocess.dataloader_num_workers 0 \
|
||||
--preprocess.max_height 480 \
|
||||
--preprocess.max_width 832 \
|
||||
--preprocess.num_frames 77 \
|
||||
--preprocess.train_fps 16 \
|
||||
--preprocess.samples_per_file 8 \
|
||||
--preprocess.flush_frequency 8 \
|
||||
--preprocess.video_length_tolerance_range 5
|
||||
--config preprocess_t2v.yaml \
|
||||
--preprocess.dataset_path "$DATASET_PATH" \
|
||||
--preprocess.dataset_output_dir "$OUTPUT_DIR"
|
||||
```
|
||||
|
||||
### Key Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `--workload_type` | Task type: `t2v` (text-to-video) or `i2v` (image-to-video) |
|
||||
| `--preprocess.dataset_type` | Input format: `hf` (HuggingFace) or `merged` (local folder) |
|
||||
| `--preprocess.dataset_path` | Path to dataset (HF repo ID or local folder) |
|
||||
| `--preprocess.dataset_output_dir` | Output directory for Parquet files |
|
||||
| `--preprocess.video_loader_type` | Video decoder: `torchcodec` or `torchvision` |
|
||||
| `--preprocess.max_height` / `max_width` | Target resolution for videos |
|
||||
| `--preprocess.num_frames` | Number of frames to extract per video |
|
||||
| `--preprocess.train_fps` | Target FPS for frame extraction |
|
||||
| Parameter | Description |
|
||||
| ------------------------------------- | ----------------------------------------------------------- |
|
||||
| `pipeline.workload_type` | Task type: `t2v` (text-to-video) or `i2v` (image-to-video) |
|
||||
| `preprocess.dataset_type` | Input format: `hf` (HuggingFace) or `merged` (local folder) |
|
||||
| `preprocess.dataset_path` | Path to dataset (HF repo ID or local folder) |
|
||||
| `preprocess.dataset_output_dir` | Output directory for Parquet files |
|
||||
| `preprocess.video_loader_type` | Video decoder: `torchcodec` or `torchvision` |
|
||||
| `preprocess.max_height` / `max_width` | Target resolution for videos |
|
||||
| `preprocess.num_frames` | Number of frames to extract per video |
|
||||
| `preprocess.train_fps` | Target FPS for frame extraction |
|
||||
|
||||
## Dataset Formats
|
||||
|
||||
|
||||
+48
-37
@@ -4,48 +4,58 @@ This guide covers finetuning video diffusion models with FastVideo, including fu
|
||||
|
||||
## Training Arguments
|
||||
|
||||
FastVideo training scripts use several argument groups:
|
||||
Each training launcher passes a `TrainingRunConfig` YAML file to its entry point with `--config` (the schema is in
|
||||
`fastvideo/api/training_schema.py`). A dotted override after `--config` sets one field, for example
|
||||
`--training.optimizer.learning_rate 1e-5` or `--engine.num_gpus "$NUM_GPUS"`:
|
||||
|
||||
```bash
|
||||
torchrun --nnodes 1 --nproc_per_node 4 \
|
||||
fastvideo/training/wan_training_pipeline.py \
|
||||
--config finetune_t2v.yaml \
|
||||
--engine.num_gpus 4
|
||||
```
|
||||
|
||||
The settings are grouped as follows:
|
||||
|
||||
### Training Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `--max_train_steps` | Total training steps |
|
||||
| `--train_batch_size` | Batch size per GPU |
|
||||
| `--gradient_accumulation_steps` | Steps to accumulate before optimizer update |
|
||||
| `--num_latent_t` | Temporal latent dimension (reduce to save memory) |
|
||||
| `--num_height` / `--num_width` | Video resolution |
|
||||
| `--num_frames` | Number of frames per video |
|
||||
| `--output_dir` | Directory for checkpoints |
|
||||
| Config path | Description |
|
||||
| ------------------------------------------------------ | ------------------------------------------------- |
|
||||
| `training.loop.max_train_steps` | Total training steps |
|
||||
| `training.data.train_batch_size` | Batch size per GPU |
|
||||
| `training.loop.gradient_accumulation_steps` | Steps to accumulate before optimizer update |
|
||||
| `training.data.num_latent_t` | Temporal latent dimension (reduce to save memory) |
|
||||
| `training.data.num_height` / `training.data.num_width` | Video resolution |
|
||||
| `training.data.num_frames` | Number of frames per video |
|
||||
| `training.checkpoint.output_dir` | Directory for checkpoints |
|
||||
|
||||
### Parallelism Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `--num_gpus` | Total number of GPUs |
|
||||
| `--sp_size` | Sequence parallel size (increase to reduce memory per GPU) |
|
||||
| `--tp_size` | Tensor parallel size |
|
||||
| `--hsdp_replicate_dim` | HSDP replication dimension |
|
||||
| `--hsdp_shard_dim` | HSDP sharding dimension |
|
||||
| Config path | Description |
|
||||
| --------------------------------------- | ---------------------------------------------------------- |
|
||||
| `engine.num_gpus` | Total number of GPUs |
|
||||
| `engine.parallelism.sp_size` | Sequence parallel size (increase to reduce memory per GPU) |
|
||||
| `engine.parallelism.tp_size` | Tensor parallel size |
|
||||
| `engine.parallelism.hsdp_replicate_dim` | HSDP replication dimension |
|
||||
| `engine.parallelism.hsdp_shard_dim` | HSDP sharding dimension |
|
||||
|
||||
### Optimizer Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `--learning_rate` | Base learning rate |
|
||||
| `--mixed_precision` | Precision mode (`bf16` recommended) |
|
||||
| `--weight_decay` | Weight decay for regularization |
|
||||
| `--max_grad_norm` | Gradient clipping threshold |
|
||||
| Config path | Description |
|
||||
| ---------------------------------- | ------------------------------- |
|
||||
| `training.optimizer.learning_rate` | Base learning rate |
|
||||
| `training.optimizer.weight_decay` | Weight decay for regularization |
|
||||
| `training.optimizer.max_grad_norm` | Gradient clipping threshold |
|
||||
|
||||
### Validation Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `--log_validation` | Enable validation logging |
|
||||
| `--validation_dataset_file` | JSON file with validation prompts |
|
||||
| `--validation_steps` | Run validation every N steps |
|
||||
| `--validation_sampling_steps` | Inference steps for validation |
|
||||
| `--validation_guidance_scale` | CFG scale for validation |
|
||||
| Config path | Description |
|
||||
| ------------------------------------ | ----------------------------------------------------------- |
|
||||
| `training.validation.enabled` | Enable validation logging |
|
||||
| `training.validation.dataset_file` | JSON file with validation prompts |
|
||||
| `training.validation.every_steps` | Run validation every N steps |
|
||||
| `training.validation.sampling_steps` | Inference steps for validation (a list, for example `[50]`) |
|
||||
| `training.validation.guidance_scale` | CFG scale for validation |
|
||||
|
||||
## Full Finetuning
|
||||
|
||||
@@ -59,8 +69,8 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/finetune_t2v.sh
|
||||
**Typical settings:**
|
||||
|
||||
- Learning rate: `1e-5` to `5e-5`
|
||||
- Gradient checkpointing: `--enable_gradient_checkpointing_type "full"`
|
||||
- Memory scaling: Increase `--sp_size` or reduce `--num_latent_t` to fit in memory
|
||||
- Gradient checkpointing: `training.model.enable_gradient_checkpointing_type: full`
|
||||
- Memory scaling: Increase `engine.parallelism.sp_size` or reduce `training.data.num_latent_t` to fit in memory
|
||||
|
||||
## Attention Quantization-Aware Training
|
||||
|
||||
@@ -80,10 +90,10 @@ LoRA (Low-Rank Adaptation) trains lightweight adapters while keeping the base mo
|
||||
|
||||
### LoRA-Specific Arguments
|
||||
|
||||
| Argument | Description |
|
||||
|----------|-------------|
|
||||
| `--lora_training True` | Enable LoRA mode |
|
||||
| `--lora_rank` | Rank of LoRA adapters (16, 32, 64, 128) |
|
||||
| Config path | Description |
|
||||
| ----------------------------- | --------------------------------------- |
|
||||
| `training.lora.enabled: true` | Enable LoRA mode |
|
||||
| `training.lora.rank` | Rank of LoRA adapters (16, 32, 64, 128) |
|
||||
|
||||
### Learning Rate for LoRA
|
||||
|
||||
@@ -103,7 +113,7 @@ bash examples/training/finetune/wan_t2v_1.3B/crush_smol/finetune_t2v_lora.sh
|
||||
|
||||
Key differences from full finetune:
|
||||
|
||||
- Add `--lora_training True --lora_rank 32`
|
||||
- Set `training.lora.enabled: true` and `training.lora.rank: 32`
|
||||
- Use higher learning rate (10–20× full finetune)
|
||||
- Can run on fewer GPUs (even single GPU)
|
||||
- Outputs adapter weights instead of full model
|
||||
@@ -186,4 +196,5 @@ Each example includes:
|
||||
- `preprocess_*.sh` — run preprocessing
|
||||
- `finetune_*.sh` — full finetune launcher
|
||||
- `finetune_*_lora.sh` — LoRA finetune launcher
|
||||
- a YAML file next to each launcher — the `TrainingRunConfig` that the launcher passes with `--config`
|
||||
- `validation.json` — validation prompts
|
||||
|
||||
@@ -16,111 +16,17 @@ DATA_DIR="data/matrixgame2"
|
||||
VALIDATION_DATASET_FILE="examples/distill/MatrixGame2.0/validation.json"
|
||||
NUM_GPUS=1
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name "matrixgame2_sf"
|
||||
--output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}"
|
||||
--wandb_run_name "${RUN_NAME}_test"
|
||||
--max_train_steps 5
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 352
|
||||
--num_width 640
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
--simulate_generator_forward
|
||||
--num_frames 81
|
||||
--num_frame_per_block 3 # Frame generation block size for self-forcing
|
||||
# --enable_gradient_masking
|
||||
# --gradient_mask_last_n_frames 21
|
||||
# --init_weights_from_safetensors "path/to/generator_ema.safetensors"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus $NUM_GPUS
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1
|
||||
--hsdp_shard_dim $NUM_GPUS
|
||||
)
|
||||
|
||||
model_args=(
|
||||
--model_path $GENERATOR_MODEL_PATH # TODO: check if you can remove this in this script
|
||||
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--log_visualization
|
||||
--visualization-steps 100
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 100
|
||||
--validation_sampling_steps "4"
|
||||
--validation_guidance_scale "6.0"
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 3e-6
|
||||
--mixed_precision "bf16"
|
||||
--weight_only_checkpointing_steps 400
|
||||
--training_state_checkpointing_steps 400
|
||||
--weight_decay 0
|
||||
--betas "0.9,0.95"
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
--use_ema True
|
||||
--ema_decay 0.99
|
||||
--ema_start_step 200
|
||||
)
|
||||
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,750,500,250'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--dfake_gen_update_ratio 5
|
||||
--real_score_guidance_scale 3.0
|
||||
--fake_score_learning_rate 3e-7
|
||||
--fake_score_betas "0.9,0.95"
|
||||
--warp_denoising_step
|
||||
)
|
||||
|
||||
self_forcing_args=(
|
||||
--independent_first_frame False # Whether to treat first frame independently
|
||||
--same_step_across_blocks True # Whether to use same denoising step across all blocks
|
||||
--last_step_only False # Whether to only use the last denoising step
|
||||
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
)
|
||||
|
||||
torchrun \
|
||||
--nnodes 1 \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
fastvideo/training/matrixgame2_self_forcing_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}" \
|
||||
"${self_forcing_args[@]}"
|
||||
--config examples/distill/MatrixGame2.0/distill_dmd.yaml \
|
||||
--model_path "$GENERATOR_MODEL_PATH" \
|
||||
--engine.num_gpus "$NUM_GPUS" \
|
||||
--engine.parallelism.hsdp_shard_dim "$NUM_GPUS" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}" \
|
||||
--training.tracker.run_name "${RUN_NAME}_test" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
@@ -63,93 +63,6 @@ FAKE_SCORE_MODEL_PATH="FastVideo/Matrix-Game-2.0-Base-Diffusers"
|
||||
DATA_DIR="data/matrixgame2"
|
||||
VALIDATION_DATASET_FILE="examples/distill/MatrixGame2.0/validation.json"
|
||||
|
||||
training_args=(
|
||||
--tracker_project_name "matrixgame2_sf"
|
||||
--output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}"
|
||||
--wandb_run_name "${RUN_NAME}_test"
|
||||
--max_train_steps 1200
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 2
|
||||
--num_latent_t 21
|
||||
--num_height 352
|
||||
--num_width 640
|
||||
--simulate_generator_forward
|
||||
--num_frames 81
|
||||
--num_frame_per_block 3
|
||||
# --init_weights_from_safetensors "path/to/generator_ema.safetensors"
|
||||
)
|
||||
|
||||
parallel_args=(
|
||||
--num_gpus "${TOTAL_GPUS}"
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1
|
||||
--hsdp_shard_dim "${TOTAL_GPUS}"
|
||||
)
|
||||
|
||||
model_args=(
|
||||
--model_path "${GENERATOR_MODEL_PATH}"
|
||||
--pretrained_model_name_or_path "${GENERATOR_MODEL_PATH}"
|
||||
--real_score_model_path "${REAL_SCORE_MODEL_PATH}"
|
||||
--fake_score_model_path "${FAKE_SCORE_MODEL_PATH}"
|
||||
)
|
||||
|
||||
dataset_args=(
|
||||
--data_path "${DATA_DIR}"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--log_visualization
|
||||
--visualization-steps 100
|
||||
--validation_dataset_file "${VALIDATION_DATASET_FILE}"
|
||||
--validation_steps 100
|
||||
--validation_sampling_steps "4"
|
||||
--validation_guidance_scale "6.0"
|
||||
)
|
||||
|
||||
optimizer_args=(
|
||||
--learning_rate 3e-6
|
||||
--mixed_precision "bf16"
|
||||
--weight_only_checkpointing_steps 400
|
||||
--training_state_checkpointing_steps 400
|
||||
--weight_decay 0
|
||||
--betas "0.9,0.95"
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
--use_ema True
|
||||
--ema_decay 0.99
|
||||
--ema_start_step 200
|
||||
)
|
||||
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps "1000,750,500,250"
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--dfake_gen_update_ratio 5
|
||||
--real_score_guidance_scale 3.0
|
||||
--fake_score_learning_rate 3e-7
|
||||
--fake_score_betas "0.9,0.95"
|
||||
--warp_denoising_step
|
||||
)
|
||||
|
||||
self_forcing_args=(
|
||||
--independent_first_frame False
|
||||
--same_step_across_blocks True
|
||||
--last_step_only False
|
||||
--context_noise 0
|
||||
)
|
||||
|
||||
srun python -m torch.distributed.run \
|
||||
--nnodes "${SLURM_JOB_NUM_NODES}" \
|
||||
--nproc_per_node "${GPUS_PER_NODE}" \
|
||||
@@ -157,12 +70,13 @@ srun python -m torch.distributed.run \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="${MASTER_ADDR}:${MASTER_PORT}" \
|
||||
fastvideo/training/matrixgame2_self_forcing_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}" \
|
||||
"${self_forcing_args[@]}"
|
||||
--config examples/distill/MatrixGame2.0/distill_dmd_slurm.yaml \
|
||||
--model_path "${GENERATOR_MODEL_PATH}" \
|
||||
--engine.num_gpus "${TOTAL_GPUS}" \
|
||||
--engine.parallelism.hsdp_shard_dim "${TOTAL_GPUS}" \
|
||||
--training.distillation.real_score_model_path "${REAL_SCORE_MODEL_PATH}" \
|
||||
--training.distillation.fake_score_model_path "${FAKE_SCORE_MODEL_PATH}" \
|
||||
--training.data.data_path "${DATA_DIR}" \
|
||||
--training.checkpoint.output_dir "checkpoints/matrixgame2_sf_${RUN_NAME}" \
|
||||
--training.tracker.run_name "${RUN_NAME}_test" \
|
||||
--training.validation.dataset_file "${VALIDATION_DATASET_FILE}"
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Read by distill_dmd.sh, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
|
||||
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path,
|
||||
# training.checkpoint.output_dir, training.tracker.run_name, and training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
# components:
|
||||
# transformer_weights: path/to/generator_ema.safetensors
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 750, 500, 250]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 352
|
||||
num_width: 640
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 3.0e-06
|
||||
betas: [0.9, 0.95]
|
||||
weight_decay: 0.0
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 5
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 400
|
||||
weight_only_checkpointing_steps: 400
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: matrixgame2_sf
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [4]
|
||||
guidance_scale: 6.0
|
||||
every_steps: 100
|
||||
log_visualization: true
|
||||
visualization_steps: 100
|
||||
distillation:
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 3.0e-07
|
||||
fake_score_betas: [0.9, 0.95]
|
||||
simulate_generator_forward: true
|
||||
warp_denoising_step: true
|
||||
ema:
|
||||
enabled: true
|
||||
decay: 0.99
|
||||
start_step: 200
|
||||
self_forcing:
|
||||
dfake_gen_update_ratio: 5
|
||||
num_frame_per_block: 3 # Frame generation block size for self-forcing
|
||||
independent_first_frame: false # Whether to treat first frame independently
|
||||
same_step_across_blocks: true # Whether to use same denoising step across all blocks
|
||||
last_step_only: false # Whether to only use the last denoising step
|
||||
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -0,0 +1,66 @@
|
||||
# Read by distill_dmd.slurm, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
|
||||
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path,
|
||||
# training.checkpoint.output_dir, training.tracker.run_name, and training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
# components:
|
||||
# transformer_weights: path/to/generator_ema.safetensors
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 750, 500, 250]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 352
|
||||
num_width: 640
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 3.0e-06
|
||||
betas: [0.9, 0.95]
|
||||
weight_decay: 0.0
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 1200
|
||||
gradient_accumulation_steps: 2
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 400
|
||||
weight_only_checkpointing_steps: 400
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: matrixgame2_sf
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [4]
|
||||
guidance_scale: 6.0
|
||||
every_steps: 100
|
||||
log_visualization: true
|
||||
visualization_steps: 100
|
||||
distillation:
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 3.0e-07
|
||||
fake_score_betas: [0.9, 0.95]
|
||||
simulate_generator_forward: true
|
||||
warp_denoising_step: true
|
||||
ema:
|
||||
enabled: true
|
||||
decay: 0.99
|
||||
start_step: 200
|
||||
self_forcing:
|
||||
dfake_gen_update_ratio: 5
|
||||
num_frame_per_block: 3
|
||||
independent_first_frame: false
|
||||
same_step_across_blocks: true
|
||||
last_step_only: false
|
||||
context_noise: 0
|
||||
@@ -36,105 +36,16 @@ VALIDATION_DATASET_FILE=your_validation_data_dir
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
training_args=(
|
||||
--tracker_project_name SFwan_t2v_distill_self_forcing_dmd
|
||||
--output_dir your_output_dir
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 480
|
||||
--num_width 832
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
--log_visualization
|
||||
--simulate_generator_forward
|
||||
--num_frames 81
|
||||
--num_frame_per_block 3 # Frame generation block size for self-forcing
|
||||
--enable_gradient_masking
|
||||
--gradient_mask_last_n_frames 21
|
||||
)
|
||||
|
||||
parallel_args=(
|
||||
--num_gpus $NUM_GPUS # 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1 # 64
|
||||
--hsdp_shard_dim $NUM_GPUS
|
||||
)
|
||||
|
||||
model_args=(
|
||||
--model_path $GENERATOR_MODEL_PATH # TODO: check if you can remove this in this script
|
||||
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 50
|
||||
--validation_sampling_steps "4"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
optimizer_args=(
|
||||
--learning_rate 1e-5
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
--weight_decay 0.01
|
||||
--betas '0.0,0.999'
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
--use_ema True
|
||||
--ema_decay 0.99
|
||||
--ema_start_step 100
|
||||
--init_weights_from_safetensors your_ode_init_weights_path
|
||||
)
|
||||
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,750,500,250'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--dfake_gen_update_ratio 5
|
||||
--real_score_guidance_scale 3.0
|
||||
--fake_score_learning_rate 8e-6
|
||||
--fake_score_betas '0.0,0.999'
|
||||
--warp_denoising_step
|
||||
)
|
||||
|
||||
self_forcing_args=(
|
||||
--independent_first_frame False # Whether to treat first frame independently
|
||||
--same_step_across_blocks True # Whether to use same denoising step across all blocks
|
||||
--last_step_only False # Whether to only use the last denoising step
|
||||
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
)
|
||||
|
||||
torchrun \
|
||||
--nnodes 1 \
|
||||
--master_port $MASTER_PORT \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
fastvideo/training/wan_self_forcing_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}" \
|
||||
"${self_forcing_args[@]}"
|
||||
--config examples/distill/SFWan2.1-T2V/distill_dmd_t2v_1.3B.yaml \
|
||||
--model_path "$GENERATOR_MODEL_PATH" \
|
||||
--engine.num_gpus "$NUM_GPUS" \
|
||||
--engine.parallelism.hsdp_shard_dim "$NUM_GPUS" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Read by distill_dmd_t2v_1.3B.sh, which passes model_path, engine.num_gpus, engine.parallelism.hsdp_shard_dim,
|
||||
# training.distillation.real_score_model_path, training.distillation.fake_score_model_path, training.data.data_path, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1 # 64
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
components:
|
||||
transformer_weights: your_ode_init_weights_path
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 750, 500, 250]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 480
|
||||
num_width: 832
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 1.0e-05
|
||||
betas: [0.0, 0.999]
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
output_dir: your_output_dir
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: SFwan_t2v_distill_self_forcing_dmd
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [4]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 50
|
||||
log_visualization: true
|
||||
distillation:
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 8.0e-06
|
||||
fake_score_betas: [0.0, 0.999]
|
||||
simulate_generator_forward: true
|
||||
warp_denoising_step: true
|
||||
ema:
|
||||
enabled: true
|
||||
decay: 0.99
|
||||
start_step: 100
|
||||
self_forcing:
|
||||
dfake_gen_update_ratio: 5
|
||||
num_frame_per_block: 3 # Frame generation block size for self-forcing
|
||||
independent_first_frame: false # Whether to treat first frame independently
|
||||
same_step_across_blocks: true # Whether to use same denoising step across all blocks
|
||||
last_step_only: false # Whether to only use the last denoising step
|
||||
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -8,17 +8,7 @@ OUTPUT_DIR="data/crush-smol_processed_t2v/"
|
||||
|
||||
torchrun --nproc_per_node=$GPU_NUM \
|
||||
fastvideo/pipelines/preprocess/v1_preprocess.py \
|
||||
--config examples/distill/SFWan2.1-T2V/preprocess_data.yaml \
|
||||
--model_path $MODEL_PATH \
|
||||
--data_merge_path $DATA_MERGE_PATH \
|
||||
--preprocess_video_batch_size 8 \
|
||||
--seed 42 \
|
||||
--max_height 480 \
|
||||
--max_width 832 \
|
||||
--num_frames 81 \
|
||||
--dataloader_num_workers 0 \
|
||||
--output_dir=$OUTPUT_DIR \
|
||||
--train_fps 16 \
|
||||
--samples_per_file 8 \
|
||||
--flush_frequency 8 \
|
||||
--video_length_tolerance_range 5 \
|
||||
--preprocess_task "t2v"
|
||||
--preprocess.data_merge_path $DATA_MERGE_PATH \
|
||||
--preprocess.dataset_output_dir=$OUTPUT_DIR
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Read by preprocess_data.sh, which passes model_path, preprocess.data_merge_path, and preprocess.dataset_output_dir.
|
||||
# Offload settings of the per-task preprocessing pipelines (v1_preprocess.py).
|
||||
engine:
|
||||
offload:
|
||||
dit_layerwise: true
|
||||
image_encoder: true
|
||||
pin_cpu_memory: true
|
||||
preprocess:
|
||||
preprocess_video_batch_size: 8
|
||||
seed: 42
|
||||
max_height: 480
|
||||
max_width: 832
|
||||
num_frames: 81
|
||||
dataloader_num_workers: 0
|
||||
train_fps: 16
|
||||
samples_per_file: 8
|
||||
flush_frequency: 8
|
||||
video_length_tolerance_range: 5
|
||||
preprocess_task: t2v
|
||||
@@ -49,96 +49,6 @@ VALIDATION_DATASET_FILE="/mnt/weka/home/hao.zhang/wl/FastVideo/examples/distill/
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
training_args=(
|
||||
--tracker_project_name SFwan2.2_t2v_distill_self_forcing_dmd # Updated for Wan2.2
|
||||
--output_dir "/mnt/sharefs/users/hao.zhang/SFwan2.2_t2v_finetune"
|
||||
--override_transformer_cls_name "CausalWanTransformer3DModel"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 448 # Updated to match Wan2.2 config
|
||||
--num_width 832 # Updated to match Wan2.2 config
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
--simulate_generator_forward
|
||||
# --log_visualization
|
||||
--num_frames 81
|
||||
--num_frame_per_block 3 # Frame generation block size for self-forcing
|
||||
--enable_gradient_masking
|
||||
--gradient_mask_last_n_frames 21
|
||||
# --init_weights_from_safetensors /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/high/3k/
|
||||
# --init_weights_from_safetensors_2 /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/low/3k/
|
||||
)
|
||||
|
||||
parallel_args=(
|
||||
--num_gpus 32 # 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1 # 64
|
||||
--hsdp_shard_dim 32
|
||||
)
|
||||
|
||||
model_args=(
|
||||
--model_path $GENERATOR_MODEL_PATH
|
||||
--pretrained_model_name_or_path $GENERATOR_MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 20
|
||||
--validation_sampling_steps "4"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
optimizer_args=(
|
||||
--learning_rate 1e-5
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
--weight_decay 0.01
|
||||
--betas '0.0,0.999'
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
--use_ema True
|
||||
--ema_decay 0.99
|
||||
--ema_start_step 100
|
||||
)
|
||||
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,750,500,250'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--dfake_gen_update_ratio 5
|
||||
--real_score_guidance_scale 3.0
|
||||
--fake_score_learning_rate 8e-6
|
||||
--fake_score_betas '0.0,0.999'
|
||||
--warp_denoising_step
|
||||
)
|
||||
|
||||
self_forcing_args=(
|
||||
--independent_first_frame False # Whether to treat first frame independently
|
||||
--same_step_across_blocks True # Whether to use same denoising step across all blocks
|
||||
--last_step_only False # Whether to only use the last denoising step
|
||||
--context_noise 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -146,12 +56,9 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_self_forcing_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}" \
|
||||
"${self_forcing_args[@]}"
|
||||
--config examples/distill/SFWan2.2-A14B/distill_dmd.yaml \
|
||||
--model_path "$GENERATOR_MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
@@ -0,0 +1,72 @@
|
||||
# Read by distill_dmd.sh, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
|
||||
# command line.
|
||||
engine:
|
||||
num_gpus: 32 # 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1 # 64
|
||||
hsdp_shard_dim: 32
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
components:
|
||||
# transformer_weights: /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/high/3k/
|
||||
# transformer_2_weights: /mnt/sharefs/users/hao.zhang/wl/models/sf_ode_init_wan22_checkpoints/low/3k/
|
||||
override_transformer_cls_name: CausalWanTransformer3DModel
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 750, 500, 250]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 448 # Updated to match Wan2.2 config
|
||||
num_width: 832 # Updated to match Wan2.2 config
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 1.0e-05
|
||||
betas: [0.0, 0.999]
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
output_dir: /mnt/sharefs/users/hao.zhang/SFwan2.2_t2v_finetune
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: SFwan2.2_t2v_distill_self_forcing_dmd # Updated for Wan2.2
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [4]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 20
|
||||
# log_visualization: true
|
||||
distillation:
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 8.0e-06
|
||||
fake_score_betas: [0.0, 0.999]
|
||||
simulate_generator_forward: true
|
||||
warp_denoising_step: true
|
||||
ema:
|
||||
enabled: true
|
||||
decay: 0.99
|
||||
start_step: 100
|
||||
self_forcing:
|
||||
dfake_gen_update_ratio: 5
|
||||
num_frame_per_block: 3 # Frame generation block size for self-forcing
|
||||
independent_first_frame: false # Whether to treat first frame independently
|
||||
same_step_across_blocks: true # Whether to use same denoising step across all blocks
|
||||
last_step_only: false # Whether to only use the last denoising step
|
||||
context_noise: 0 # Amount of noise to add during context caching (0 = no noise)
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -47,84 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name wan_t2v_distill_dmd_VSA
|
||||
--output_dir $OUTPUT_DIR
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 480
|
||||
--num_width 832
|
||||
--num_frames 81
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 64
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 2e-6
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 8
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3.5
|
||||
--VSA_sparsity 0.8
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -132,11 +54,10 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_VSA_t2v_1.3B.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "$OUTPUT_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Read by distill_dmd_VSA_t2v_1.3B.slurm, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
num_gpus: 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 64
|
||||
hsdp_shard_dim: 1
|
||||
attention:
|
||||
vsa_sparsity: 0.8
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 8.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 480
|
||||
num_width: 832
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 2.0e-06
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: wan_t2v_distill_dmd_VSA
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.5
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -47,84 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name wan_t2v_distill_dmd_VSA
|
||||
--output_dir "$OUTPUT_DIR"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 480
|
||||
--num_width 832
|
||||
--num_frames 81
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 64
|
||||
--sp_size 4
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 8
|
||||
--hsdp_shard_dim 8
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 2e-6
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 3
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3.5
|
||||
--VSA_sparsity 0.9
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -132,11 +54,10 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_VSA_t2v_14B.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "$OUTPUT_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Read by distill_dmd_VSA_t2v_14B.slurm, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
num_gpus: 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 4
|
||||
hsdp_replicate_dim: 8
|
||||
hsdp_shard_dim: 8
|
||||
attention:
|
||||
vsa_sparsity: 0.9
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 3.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 480
|
||||
num_width: 832
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 2.0e-06
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: wan_t2v_distill_dmd_VSA
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.5
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -47,83 +47,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name wan_t2v_distill_dmd
|
||||
--output_dir "$OUTPUT_DIR"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 21
|
||||
--num_height 480
|
||||
--num_width 832
|
||||
--num_frames 81
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 64
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 2e-6
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 8
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3.5
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -131,11 +54,10 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.1-T2V/Wan-Syn-Data-480P/distill_dmd_t2v_1.3B.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "$OUTPUT_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Read by distill_dmd_t2v_1.3B.slurm, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
num_gpus: 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 64
|
||||
hsdp_shard_dim: 1
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 8.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 480
|
||||
num_width: 832
|
||||
num_frames: 81
|
||||
train_batch_size: 1
|
||||
num_latent_t: 21
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 2.0e-06
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: wan_t2v_distill_dmd
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.5
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -8,4 +8,4 @@ uv pip install vsa
|
||||
```
|
||||
|
||||
### Data-free Distillation
|
||||
When `--simulate_generator_forward` is enabled, distillation becomes data-free by simulating intermediate steps through forward inference of the generator. This helps avoid training–inference mismatch. See Section 4.5 of [DMD2](https://arxiv.org/pdf/2405.14867) for details.
|
||||
When `training.distillation.simulate_generator_forward` is enabled, distillation becomes data-free by simulating intermediate steps through forward inference of the generator. This helps avoid training–inference mismatch. See Section 4.5 of [DMD2](https://arxiv.org/pdf/2405.14867) for details.
|
||||
@@ -48,90 +48,6 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name Wan_distillation
|
||||
--output_dir "$OUTPUT_DIR"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 31
|
||||
--num_height 704
|
||||
--num_width 1280
|
||||
--num_frames 121
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 64
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DIR"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 4e-6
|
||||
--lr_scheduler "cosine_with_min_lr"
|
||||
--min_lr_ratio 0.5
|
||||
--lr_warmup_steps 100
|
||||
--fake_score_learning_rate 2e-6
|
||||
--fake_score_lr_scheduler "cosine_with_min_lr"
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 200
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3
|
||||
--simulate_generator_forward
|
||||
--log_visualization # disable if oom
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -139,11 +55,10 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/Data-free/distill_dmd_t2v_5B.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "$OUTPUT_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DIR"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Read by distill_dmd_t2v_5B.sh, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
num_gpus: 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 64
|
||||
hsdp_shard_dim: 1
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 704
|
||||
num_width: 1280
|
||||
num_frames: 121
|
||||
train_batch_size: 1
|
||||
num_latent_t: 31
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 4.0e-06
|
||||
weight_decay: 0.01
|
||||
lr_scheduler: cosine_with_min_lr
|
||||
lr_warmup_steps: 100
|
||||
min_lr_ratio: 0.5
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 200
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: Wan_distillation
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
log_visualization: true # disable if oom
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 2.0e-06
|
||||
fake_score_lr_scheduler: cosine_with_min_lr
|
||||
simulate_generator_forward: true
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -47,91 +47,6 @@ VALIDATION_DIR=your_validation_path #(example:validation_64.json)
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name Wan_distillation
|
||||
--output_dir "your_output_dir"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 31
|
||||
--num_height 704
|
||||
--num_width 1280
|
||||
--num_frames 121
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 64
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 64
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DIR"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 4e-6
|
||||
--lr_scheduler "cosine_with_min_lr"
|
||||
--min_lr_ratio 0.5
|
||||
--lr_warmup_steps 100
|
||||
--fake_score_learning_rate 2e-6
|
||||
--fake_score_lr_scheduler "cosine_with_min_lr"
|
||||
--mixed_precision "bf16"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 200
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 5
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3
|
||||
--simulate_generator_forward
|
||||
--log_visualization # disable if oom
|
||||
--VSA_sparsity 0.8
|
||||
)
|
||||
|
||||
srun torchrun \
|
||||
--nnodes $SLURM_JOB_NUM_NODES \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
@@ -139,11 +54,9 @@ srun torchrun \
|
||||
--rdzv_backend=c10d \
|
||||
--rdzv_endpoint="$MASTER_ADDR:$MASTER_PORT" \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/Data-free/distill_dmd_t2v_5B_VSA.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DIR"
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Read by distill_dmd_t2v_5B_VSA.sh, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
|
||||
# command line.
|
||||
engine:
|
||||
num_gpus: 64
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 64
|
||||
hsdp_shard_dim: 1
|
||||
attention:
|
||||
vsa_sparsity: 0.8
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 5.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 704
|
||||
num_width: 1280
|
||||
num_frames: 121
|
||||
train_batch_size: 1
|
||||
num_latent_t: 31
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 4.0e-06
|
||||
weight_decay: 0.01
|
||||
lr_scheduler: cosine_with_min_lr
|
||||
lr_warmup_steps: 100
|
||||
min_lr_ratio: 0.5
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
output_dir: your_output_dir
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 200
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: Wan_distillation
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
log_visualization: true # disable if oom
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.0
|
||||
fake_score_learning_rate: 2.0e-06
|
||||
fake_score_lr_scheduler: cosine_with_min_lr
|
||||
simulate_generator_forward: true
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
@@ -22,94 +22,15 @@ OUTPUT_DIR="checkpoints/wan_t2v_finetune"
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name wan_t2v_distill_dmd_VSA
|
||||
--output_dir "$OUTPUT_DIR"
|
||||
--max_train_steps 4000
|
||||
--train_batch_size 1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps 1
|
||||
--num_latent_t 31
|
||||
--num_height 704
|
||||
--num_width 1280
|
||||
--num_frames 121
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
--training_state_checkpointing_steps 500
|
||||
--weight_only_checkpointing_steps 500
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 1
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate 2e-6
|
||||
--mixed_precision "bf16"
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 8
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3.5
|
||||
--VSA_sparsity 0.8
|
||||
)
|
||||
|
||||
torchrun \
|
||||
--nnodes 1 \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
--master_port $MASTER_PORT \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/distill_dmd_VSA_t2v_5B.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.checkpoint.output_dir "$OUTPUT_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Read by distill_dmd_VSA_t2v_5B.sh, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, training.checkpoint.output_dir, and
|
||||
# training.validation.dataset_file on the command line.
|
||||
engine:
|
||||
num_gpus: 1
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1
|
||||
hsdp_shard_dim: 1
|
||||
attention:
|
||||
vsa_sparsity: 0.8
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 8.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 704
|
||||
num_width: 1280
|
||||
num_frames: 121
|
||||
train_batch_size: 1
|
||||
num_latent_t: 31
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 2.0e-06
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: wan_t2v_distill_dmd_VSA
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.5
|
||||
ema:
|
||||
start_step: 0
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
+6
-88
@@ -21,96 +21,14 @@ VALIDATION_DATASET_FILE="examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/va
|
||||
# export CUDA_VISIBLE_DEVICES=4,5
|
||||
# IP=[MASTER NODE IP]
|
||||
|
||||
# Training arguments
|
||||
training_args=(
|
||||
--tracker_project_name wan_t2v_distill_dmd_VSA
|
||||
--output_dir="checkpoints/wan_t2v_finetune"
|
||||
--max_train_steps=4000
|
||||
--train_batch_size=1
|
||||
--train_sp_batch_size 1
|
||||
--gradient_accumulation_steps=1
|
||||
--num_latent_t 31
|
||||
--num_height 704
|
||||
--num_width 1280
|
||||
--num_frames 121
|
||||
--enable_gradient_checkpointing_type "full"
|
||||
--training_state_checkpointing_steps=500
|
||||
--weight_only_checkpointing_steps=500
|
||||
--lora_rank 32
|
||||
--lora_training True
|
||||
)
|
||||
|
||||
# Parallel arguments
|
||||
parallel_args=(
|
||||
--num_gpus 1
|
||||
--sp_size 1
|
||||
--tp_size 1
|
||||
--hsdp_replicate_dim 1
|
||||
--hsdp_shard_dim 1
|
||||
)
|
||||
|
||||
# Model arguments
|
||||
model_args=(
|
||||
--model_path $MODEL_PATH
|
||||
--pretrained_model_name_or_path $MODEL_PATH
|
||||
--real_score_model_path $REAL_SCORE_MODEL_PATH
|
||||
--fake_score_model_path $FAKE_SCORE_MODEL_PATH
|
||||
)
|
||||
|
||||
# Dataset arguments
|
||||
dataset_args=(
|
||||
--data_path "$DATA_DIR"
|
||||
--dataloader_num_workers 4
|
||||
)
|
||||
|
||||
# Validation arguments
|
||||
validation_args=(
|
||||
--log_validation
|
||||
--validation_dataset_file "$VALIDATION_DATASET_FILE"
|
||||
--validation_steps 200
|
||||
--validation_sampling_steps "3"
|
||||
--validation_guidance_scale "6.0" # not used for dmd inference
|
||||
)
|
||||
|
||||
# Optimizer arguments
|
||||
optimizer_args=(
|
||||
--learning_rate=1e-4
|
||||
--mixed_precision="bf16"
|
||||
--weight_decay 0.01
|
||||
--max_grad_norm 1.0
|
||||
)
|
||||
|
||||
# Miscellaneous arguments
|
||||
miscellaneous_args=(
|
||||
--inference_mode False
|
||||
--checkpoints_total_limit 3
|
||||
--training_cfg_rate 0.0
|
||||
--dit_precision "fp32"
|
||||
--ema_start_step 0
|
||||
--flow_shift 8
|
||||
--seed 1000
|
||||
)
|
||||
|
||||
# DMD arguments
|
||||
dmd_args=(
|
||||
--dmd_denoising_steps '1000,757,522'
|
||||
--min_timestep_ratio 0.02
|
||||
--max_timestep_ratio 0.98
|
||||
--generator_update_interval 5
|
||||
--real_score_guidance_scale 3.5
|
||||
--VSA_sparsity 0.8
|
||||
)
|
||||
|
||||
torchrun \
|
||||
--nnodes 1 \
|
||||
--nproc_per_node $NUM_GPUS \
|
||||
--master_port $MASTER_PORT \
|
||||
fastvideo/training/wan_distillation_pipeline.py \
|
||||
"${parallel_args[@]}" \
|
||||
"${model_args[@]}" \
|
||||
"${dataset_args[@]}" \
|
||||
"${training_args[@]}" \
|
||||
"${optimizer_args[@]}" \
|
||||
"${validation_args[@]}" \
|
||||
"${miscellaneous_args[@]}" \
|
||||
"${dmd_args[@]}"
|
||||
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/distill_dmd_VSA_t2v_5B_lora.yaml \
|
||||
--model_path "$MODEL_PATH" \
|
||||
--training.distillation.real_score_model_path "$REAL_SCORE_MODEL_PATH" \
|
||||
--training.distillation.fake_score_model_path "$FAKE_SCORE_MODEL_PATH" \
|
||||
--training.data.data_path "$DATA_DIR" \
|
||||
--training.validation.dataset_file "$VALIDATION_DATASET_FILE"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Read by distill_dmd_VSA_t2v_5B_lora.sh, which passes model_path, training.distillation.real_score_model_path,
|
||||
# training.distillation.fake_score_model_path, training.data.data_path, and training.validation.dataset_file on the
|
||||
# command line.
|
||||
engine:
|
||||
num_gpus: 1
|
||||
parallelism:
|
||||
tp_size: 1
|
||||
sp_size: 1
|
||||
hsdp_replicate_dim: 1
|
||||
hsdp_shard_dim: 1
|
||||
attention:
|
||||
vsa_sparsity: 0.8
|
||||
precision:
|
||||
dit: fp32
|
||||
pipeline:
|
||||
flow_shift: 8.0
|
||||
dmd_denoising_steps: [1000, 757, 522]
|
||||
training:
|
||||
data:
|
||||
dataloader_num_workers: 4
|
||||
num_height: 704
|
||||
num_width: 1280
|
||||
num_frames: 121
|
||||
train_batch_size: 1
|
||||
num_latent_t: 31
|
||||
training_cfg_rate: 0.0
|
||||
seed: 1000
|
||||
train_sp_batch_size: 1
|
||||
optimizer:
|
||||
learning_rate: 0.0001
|
||||
weight_decay: 0.01
|
||||
max_grad_norm: 1.0
|
||||
loop:
|
||||
max_train_steps: 4000
|
||||
gradient_accumulation_steps: 1
|
||||
checkpoint:
|
||||
output_dir: checkpoints/wan_t2v_finetune
|
||||
training_state_checkpointing_steps: 500
|
||||
weight_only_checkpointing_steps: 500
|
||||
checkpoints_total_limit: 3
|
||||
tracker:
|
||||
project_name: wan_t2v_distill_dmd_VSA
|
||||
validation:
|
||||
enabled: true
|
||||
sampling_steps: [3]
|
||||
guidance_scale: 6.0 # not used for dmd inference
|
||||
every_steps: 200
|
||||
distillation:
|
||||
generator_update_interval: 5
|
||||
min_timestep_ratio: 0.02
|
||||
max_timestep_ratio: 0.98
|
||||
real_score_guidance_scale: 3.5
|
||||
ema:
|
||||
start_step: 0
|
||||
lora:
|
||||
enabled: true
|
||||
rank: 32
|
||||
model:
|
||||
enable_gradient_checkpointing_type: full
|
||||
+3
-13
@@ -8,17 +8,7 @@ OUTPUT_DIR="data/crush-smol_processed_ti2v/"
|
||||
|
||||
torchrun --nproc_per_node=$GPU_NUM \
|
||||
fastvideo/pipelines/preprocess/v1_preprocess.py \
|
||||
--config examples/distill/Wan2.2-TI2V-5B-Diffusers/crush_smol/preprocess_wan_data_ti2v_5b.yaml \
|
||||
--model_path $MODEL_PATH \
|
||||
--data_merge_path $DATA_MERGE_PATH \
|
||||
--preprocess_video_batch_size 8 \
|
||||
--seed 42 \
|
||||
--max_height 704 \
|
||||
--max_width 1280 \
|
||||
--num_frames 121 \
|
||||
--dataloader_num_workers 0 \
|
||||
--output_dir=$OUTPUT_DIR \
|
||||
--train_fps 24 \
|
||||
--samples_per_file 8 \
|
||||
--flush_frequency 8 \
|
||||
--video_length_tolerance_range 5 \
|
||||
--preprocess_task "t2v"
|
||||
--preprocess.data_merge_path $DATA_MERGE_PATH \
|
||||
--preprocess.dataset_output_dir=$OUTPUT_DIR
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Read by preprocess_wan_data_ti2v_5b.sh, which passes model_path, preprocess.data_merge_path, and
|
||||
# preprocess.dataset_output_dir.
|
||||
# Offload settings of the per-task preprocessing pipelines (v1_preprocess.py).
|
||||
engine:
|
||||
offload:
|
||||
dit_layerwise: true
|
||||
image_encoder: true
|
||||
pin_cpu_memory: true
|
||||
preprocess:
|
||||
preprocess_video_batch_size: 8
|
||||
seed: 42
|
||||
max_height: 704
|
||||
max_width: 1280
|
||||
num_frames: 121
|
||||
dataloader_num_workers: 0
|
||||
train_fps: 24
|
||||
samples_per_file: 8
|
||||
flush_frequency: 8
|
||||
video_length_tolerance_range: 5
|
||||
preprocess_task: t2v
|
||||
@@ -177,13 +177,13 @@ from fastvideo import VideoGenerator
|
||||
def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
num_gpus=1,
|
||||
{"engine": {"num_gpus": 1}},
|
||||
)
|
||||
|
||||
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
|
||||
"wide with interest. The playful yet serene atmosphere is complemented by soft "
|
||||
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
|
||||
video = generator.generate_video(prompt)
|
||||
video = generator.generate({"prompt": prompt})
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
# from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
OUTPUT_PATH = "video_samples"
|
||||
|
||||
|
||||
@@ -13,24 +11,35 @@ def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# sampling_param = SamplingParam.from_pretrained("Wan-AI/Wan2.1-T2V-1.3B-Diffusers")
|
||||
# sampling_param.num_frames = 45
|
||||
# sampling_param.image_path = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"
|
||||
# Generate videos with the same simple API, regardless of GPU count
|
||||
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
|
||||
"wide with interest. The playful yet serene atmosphere is complemented by soft "
|
||||
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
|
||||
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True)
|
||||
# video = generator.generate_video(prompt, sampling_param=sampling_param, output_path="wan_t2v_videos/")
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
# video = generator.generate({
|
||||
# "prompt": prompt,
|
||||
# "inputs": {"image_path": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/astronaut.jpg"},
|
||||
# "sampling": {"num_frames": 45},
|
||||
# "output": {"output_path": "wan_t2v_videos/"},
|
||||
# })
|
||||
|
||||
# Generate another video with a different prompt, without reloading the
|
||||
# model!
|
||||
@@ -39,7 +48,10 @@ def main():
|
||||
"the breeze, enhancing the lion's commanding presence. The tone is vibrant, "
|
||||
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
|
||||
"cinematic.")
|
||||
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True)
|
||||
video2 = generator.generate({
|
||||
"prompt": prompt2,
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
|
||||
def main():
|
||||
@@ -9,16 +8,20 @@ def main():
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_path,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
sampling_param = SamplingParam.from_pretrained(model_path)
|
||||
|
||||
# image2world example from official repo
|
||||
image_path = "assets/images/bus_terminal.jpg"
|
||||
|
||||
@@ -32,14 +35,12 @@ def main():
|
||||
"It then comes to a smooth stop, resuming its position in line. "
|
||||
"Overhead signage in Chinese characters remains illuminated, enhancing the vibrant, urban night scene.")
|
||||
|
||||
generator.generate_video(
|
||||
prompt,
|
||||
sampling_param=sampling_param,
|
||||
image_path=str(image_path),
|
||||
num_cond_frames=1,
|
||||
output_path="outputs_video/cosmos2_5_i2w.mp4",
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
"inputs": {"image_path": str(image_path)},
|
||||
"output": {"output_path": "outputs_video/cosmos2_5_i2w.mp4", "save_video": True},
|
||||
"extensions": {"num_cond_frames": 1},
|
||||
})
|
||||
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
|
||||
def main():
|
||||
@@ -8,17 +7,20 @@ def main():
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_path,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# Load default sampling parameters (negative_prompt, resolution, steps, etc.)
|
||||
sampling_param = SamplingParam.from_pretrained(model_path)
|
||||
|
||||
prompt = (
|
||||
"A high-definition video captures the precision of robotic welding in an industrial setting. "
|
||||
"The first frame showcases a robotic arm, equipped with a welding torch, positioned over a large metal structure. "
|
||||
@@ -33,12 +35,10 @@ def main():
|
||||
"The scene retains its industrial ambiance, with the welding sparks and smoke dominating the visual field, "
|
||||
"underscoring the ongoing nature of the welding operation.")
|
||||
|
||||
generator.generate_video(
|
||||
prompt,
|
||||
sampling_param=sampling_param,
|
||||
output_path="outputs_video/cosmos2_5_t2w.mp4",
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
"output": {"output_path": "outputs_video/cosmos2_5_t2w.mp4", "save_video": True},
|
||||
})
|
||||
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
|
||||
def main():
|
||||
@@ -9,16 +8,20 @@ def main():
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_path,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
sampling_param = SamplingParam.from_pretrained(model_path)
|
||||
|
||||
# video2world example from official repo
|
||||
video_path = "assets/videos/robot_pouring.mp4"
|
||||
|
||||
@@ -36,14 +39,12 @@ def main():
|
||||
"The final frame captures the robotic arm with the pitcher finishing the pour, with the glass now filled to a higher level, while the pitcher is slightly tilted but still held securely by the gripper."
|
||||
)
|
||||
|
||||
generator.generate_video(
|
||||
prompt,
|
||||
sampling_param=sampling_param,
|
||||
video_path=str(video_path),
|
||||
num_cond_frames=1,
|
||||
output_path="outputs_video/cosmos2_5_v2w.mp4",
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
"inputs": {"video_path": str(video_path)},
|
||||
"output": {"output_path": "outputs_video/cosmos2_5_v2w.mp4", "save_video": True},
|
||||
"extensions": {"num_cond_frames": 1},
|
||||
})
|
||||
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import os
|
||||
import time
|
||||
from fastvideo import VideoGenerator
|
||||
|
||||
from fastvideo.api.sampling_param import SamplingParam
|
||||
|
||||
OUTPUT_PATH = "video_samples_dmd2"
|
||||
|
||||
|
||||
@@ -12,29 +10,34 @@ def main():
|
||||
|
||||
load_start_time = time.perf_counter()
|
||||
model_name = "FastVideo/FastWan2.1-T2V-1.3B-Diffusers"
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_name,
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
# Adjust these offload parameters if you have < 32GB of VRAM
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
VSA_sparsity=0.8,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_name,
|
||||
"engine": {
|
||||
# FastVideo will automatically handle distributed setup
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
# Adjust these offload parameters if you have < 32GB of VRAM
|
||||
"offload": {
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
},
|
||||
"attention": {"vsa_sparsity": 0.8},
|
||||
},
|
||||
})
|
||||
load_end_time = time.perf_counter()
|
||||
load_time = load_end_time - load_start_time
|
||||
|
||||
sampling_param = SamplingParam.from_pretrained(model_name)
|
||||
sampling_param.num_frames = 81
|
||||
|
||||
prompt = (
|
||||
"A neon-lit alley in futuristic Tokyo during a heavy rainstorm at night. The puddles reflect glowing signs in kanji, advertising ramen, karaoke, and VR arcades. A woman in a translucent raincoat walks briskly with an LED umbrella. Steam rises from a street food cart, and a cat darts across the screen. Raindrops are visible on the camera lens, creating a cinematic bokeh effect."
|
||||
)
|
||||
start_time = time.perf_counter()
|
||||
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True, sampling_param=sampling_param)
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"sampling": {"num_frames": 81},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
end_time = time.perf_counter()
|
||||
gen_time = end_time - start_time
|
||||
|
||||
@@ -46,7 +49,11 @@ def main():
|
||||
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
|
||||
"cinematic.")
|
||||
start_time = time.perf_counter()
|
||||
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True, num_frames=81)
|
||||
video2 = generator.generate({
|
||||
"prompt": prompt2,
|
||||
"sampling": {"num_frames": 81},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
end_time = time.perf_counter()
|
||||
gen_time2 = end_time - start_time
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import time
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import (
|
||||
AttentionConfig,
|
||||
EngineConfig,
|
||||
GenerationRequest,
|
||||
GeneratorConfig,
|
||||
OffloadConfig,
|
||||
OutputConfig,
|
||||
PipelineSelection,
|
||||
)
|
||||
|
||||
OUTPUT_PATH = "video_samples_dmd2_typed"
|
||||
@@ -29,12 +29,8 @@ def main():
|
||||
dit=False,
|
||||
vae=False,
|
||||
),
|
||||
attention=AttentionConfig(vsa_sparsity=0.8),
|
||||
),
|
||||
# PR 2 still routes a few advanced inference knobs through the
|
||||
# compatibility bridge until they get first-class typed fields.
|
||||
pipeline=PipelineSelection(experimental={
|
||||
"VSA_sparsity": 0.8,
|
||||
}, ),
|
||||
)
|
||||
|
||||
load_start_time = time.perf_counter()
|
||||
|
||||
@@ -15,16 +15,20 @@ def _env_float(name: str, default: float) -> float:
|
||||
|
||||
def main():
|
||||
model_name = os.getenv("DREAMX_WORLD_MODEL_DIR", "FastVideo/DreamX-World-5B-Cam-Diffusers")
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_name,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
override_pipeline_cls_name="DreamXWorldPipeline",
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_name,
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {"components": {"override_pipeline_cls_name": "DreamXWorldPipeline"}},
|
||||
})
|
||||
|
||||
prompt = os.getenv(
|
||||
"DREAMX_WORLD_PROMPT",
|
||||
@@ -36,23 +40,30 @@ def main():
|
||||
"https://huggingface.co/datasets/YiYiXu/testing-images/resolve/main/wan_i2v_input.JPG",
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
"output_path": OUTPUT_PATH,
|
||||
"save_video": os.getenv("DREAMX_WORLD_SAVE_VIDEO", "1") != "0",
|
||||
"height": _env_int("DREAMX_WORLD_HEIGHT", 480),
|
||||
"width": _env_int("DREAMX_WORLD_WIDTH", 832),
|
||||
"num_frames": _env_int("DREAMX_WORLD_NUM_FRAMES", 161),
|
||||
"num_inference_steps": _env_int("DREAMX_WORLD_STEPS", 30),
|
||||
"guidance_scale": _env_float("DREAMX_WORLD_GUIDANCE", 5.0),
|
||||
"action_list": os.getenv("DREAMX_WORLD_ACTIONS", "w,d,w").split(","),
|
||||
"action_speed_list":
|
||||
[float(value) for value in os.getenv("DREAMX_WORLD_ACTION_SPEEDS", "4.0,2.0,4.0").split(",")],
|
||||
request = {
|
||||
"prompt": prompt,
|
||||
"sampling": {
|
||||
"height": _env_int("DREAMX_WORLD_HEIGHT", 480),
|
||||
"width": _env_int("DREAMX_WORLD_WIDTH", 832),
|
||||
"num_frames": _env_int("DREAMX_WORLD_NUM_FRAMES", 161),
|
||||
"num_inference_steps": _env_int("DREAMX_WORLD_STEPS", 30),
|
||||
"guidance_scale": _env_float("DREAMX_WORLD_GUIDANCE", 5.0),
|
||||
},
|
||||
"output": {
|
||||
"output_path": OUTPUT_PATH,
|
||||
"save_video": os.getenv("DREAMX_WORLD_SAVE_VIDEO", "1") != "0",
|
||||
},
|
||||
"extensions": {
|
||||
"action_list": os.getenv("DREAMX_WORLD_ACTIONS", "w,d,w").split(","),
|
||||
"action_speed_list":
|
||||
[float(value) for value in os.getenv("DREAMX_WORLD_ACTION_SPEEDS", "4.0,2.0,4.0").split(",")],
|
||||
},
|
||||
}
|
||||
if image_path:
|
||||
kwargs["image_path"] = image_path
|
||||
request["inputs"] = {"image_path": image_path}
|
||||
|
||||
try:
|
||||
generator.generate_video(prompt, **kwargs)
|
||||
generator.generate(request)
|
||||
finally:
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -25,11 +25,13 @@ from pathlib import Path
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import (
|
||||
AttentionConfig,
|
||||
CompileConfig,
|
||||
ComponentConfig,
|
||||
EngineConfig,
|
||||
GenerationRequest,
|
||||
GeneratorConfig,
|
||||
MiniMaxH3Options,
|
||||
OffloadConfig,
|
||||
OutputConfig,
|
||||
ParallelismConfig,
|
||||
@@ -254,23 +256,6 @@ def _execution_backend(args: argparse.Namespace) -> str:
|
||||
|
||||
def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
|
||||
use_vsa = _uses_vsa(args)
|
||||
experimental: dict[str, object] = {
|
||||
"attention_backend": "VIDEO_SPARSE_ATTN_H3" if use_vsa else "FLASH_ATTN",
|
||||
"inference_torch_compile": args.inference_torch_compile,
|
||||
"vae_parallel_decode": args.parallel_vae,
|
||||
"vae_parallel_decode_strategy": "gather",
|
||||
}
|
||||
if args.h3_sequential_load is not None:
|
||||
experimental["h3_sequential_load"] = args.h3_sequential_load
|
||||
if args.video_decode_backend != "h3-vae":
|
||||
experimental["video_decode_backend"] = args.video_decode_backend
|
||||
if args.taeh3_checkpoint is not None:
|
||||
experimental["taeh3_checkpoint"] = args.taeh3_checkpoint
|
||||
if use_vsa:
|
||||
experimental.update({
|
||||
"VSA_sparsity": args.vsa_sparsity,
|
||||
"VSA_tile_size": args.vsa_tile_size,
|
||||
})
|
||||
return GeneratorConfig(
|
||||
model_path=args.model_path,
|
||||
pipeline=PipelineSelection(
|
||||
@@ -278,7 +263,13 @@ def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
|
||||
lora_path=getattr(args, "lora_path", None),
|
||||
lora_strength=float(getattr(args, "lora_strength", 1.0)),
|
||||
),
|
||||
experimental=experimental,
|
||||
model=MiniMaxH3Options(
|
||||
sequential_load=args.h3_sequential_load,
|
||||
video_decode_backend=None if args.video_decode_backend == "h3-vae" else args.video_decode_backend,
|
||||
taeh3_checkpoint=args.taeh3_checkpoint,
|
||||
vae_parallel_decode=args.parallel_vae,
|
||||
vae_parallel_decode_strategy="gather",
|
||||
),
|
||||
),
|
||||
engine=EngineConfig(
|
||||
num_gpus=args.num_gpus,
|
||||
@@ -297,6 +288,12 @@ def build_generator_config(args: argparse.Namespace) -> GeneratorConfig:
|
||||
enabled=args.torch_compile,
|
||||
mode=args.compile_mode,
|
||||
vae_enabled=args.compile_vae,
|
||||
regional=args.inference_torch_compile,
|
||||
),
|
||||
attention=AttentionConfig(
|
||||
backend="VIDEO_SPARSE_ATTN_H3" if use_vsa else "FLASH_ATTN",
|
||||
vsa_sparsity=args.vsa_sparsity if use_vsa else None,
|
||||
vsa_tile_size=args.vsa_tile_size if use_vsa else None,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -17,11 +17,13 @@ sys.path.insert(0, str(FASTVIDEO_ROOT))
|
||||
|
||||
from fastvideo import VideoGenerator # noqa: E402
|
||||
from fastvideo.api import ( # noqa: E402
|
||||
AttentionConfig,
|
||||
CompileConfig,
|
||||
ComponentConfig,
|
||||
EngineConfig,
|
||||
GenerationRequest,
|
||||
GeneratorConfig,
|
||||
MiniMaxH3Options,
|
||||
OffloadConfig,
|
||||
OutputConfig,
|
||||
ParallelismConfig,
|
||||
@@ -57,17 +59,7 @@ def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
attention_backend = VARIANT_BACKENDS[args.variant]
|
||||
experimental = {
|
||||
"attention_backend": attention_backend,
|
||||
"inference_torch_compile": attention_backend == "FLASH_ATTN",
|
||||
"vae_parallel_decode": True,
|
||||
"vae_parallel_decode_strategy": "gather",
|
||||
}
|
||||
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
|
||||
experimental.update({
|
||||
"VSA_sparsity": 0.9,
|
||||
"VSA_tile_size": 64,
|
||||
})
|
||||
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
|
||||
|
||||
adapter_path = hf_hub_download(
|
||||
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
|
||||
@@ -81,13 +73,18 @@ def main() -> None:
|
||||
model_path="MiniMaxAI/MiniMax-H3",
|
||||
pipeline=PipelineSelection(
|
||||
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
|
||||
experimental=experimental,
|
||||
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
|
||||
),
|
||||
engine=EngineConfig(
|
||||
num_gpus=4,
|
||||
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
|
||||
offload=OffloadConfig(dit=False, dit_layerwise=False),
|
||||
compile=CompileConfig(vae_enabled=True),
|
||||
compile=CompileConfig(vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
|
||||
attention=AttentionConfig(
|
||||
backend=attention_backend,
|
||||
vsa_sparsity=0.9 if use_vsa else None,
|
||||
vsa_tile_size=64 if use_vsa else None,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -11,11 +11,13 @@ from huggingface_hub import hf_hub_download
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import (
|
||||
AttentionConfig,
|
||||
CompileConfig,
|
||||
ComponentConfig,
|
||||
EngineConfig,
|
||||
GenerationRequest,
|
||||
GeneratorConfig,
|
||||
MiniMaxH3Options,
|
||||
OffloadConfig,
|
||||
OutputConfig,
|
||||
ParallelismConfig,
|
||||
@@ -50,17 +52,7 @@ def main() -> None:
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
attention_backend = VARIANT_BACKENDS[args.variant]
|
||||
experimental = {
|
||||
"attention_backend": attention_backend,
|
||||
"inference_torch_compile": attention_backend == "FLASH_ATTN",
|
||||
"vae_parallel_decode": True,
|
||||
"vae_parallel_decode_strategy": "gather",
|
||||
}
|
||||
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
|
||||
experimental.update({
|
||||
"VSA_sparsity": 0.9,
|
||||
"VSA_tile_size": 64,
|
||||
})
|
||||
use_vsa = attention_backend == "VIDEO_SPARSE_ATTN_H3"
|
||||
|
||||
adapter_path = hf_hub_download(
|
||||
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
|
||||
@@ -71,13 +63,18 @@ def main() -> None:
|
||||
model_path="MiniMaxAI/MiniMax-H3",
|
||||
pipeline=PipelineSelection(
|
||||
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
|
||||
experimental=experimental,
|
||||
model=MiniMaxH3Options(vae_parallel_decode=True, vae_parallel_decode_strategy="gather"),
|
||||
),
|
||||
engine=EngineConfig(
|
||||
num_gpus=4,
|
||||
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
|
||||
offload=OffloadConfig(dit=False, dit_layerwise=False),
|
||||
compile=CompileConfig(vae_enabled=True),
|
||||
compile=CompileConfig(vae_enabled=True, regional=attention_backend == "FLASH_ATTN"),
|
||||
attention=AttentionConfig(
|
||||
backend=attention_backend,
|
||||
vsa_sparsity=0.9 if use_vsa else None,
|
||||
vsa_tile_size=64 if use_vsa else None,
|
||||
),
|
||||
quantization=QuantizationConfig(transformer_quant="MXFP8"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -30,14 +30,16 @@ generator:
|
||||
compile:
|
||||
enabled: false
|
||||
vae_enabled: true
|
||||
regional: true
|
||||
attention:
|
||||
backend: VIDEO_SPARSE_ATTN_H3
|
||||
vsa_sparsity: 0.9
|
||||
vsa_tile_size: 64
|
||||
pipeline:
|
||||
experimental:
|
||||
attention_backend: VIDEO_SPARSE_ATTN_H3
|
||||
VSA_sparsity: 0.9
|
||||
VSA_tile_size: 64
|
||||
inference_torch_compile: true
|
||||
vae_parallel_decode: true
|
||||
vae_parallel_decode_strategy: gather
|
||||
model:
|
||||
minimax_h3:
|
||||
vae_parallel_decode: true
|
||||
vae_parallel_decode_strategy: gather
|
||||
request:
|
||||
prompt: >-
|
||||
A wide cinematic shot of an alpine meadow at sunrise, pale pink mountain
|
||||
|
||||
@@ -30,14 +30,16 @@ generator:
|
||||
compile:
|
||||
enabled: false
|
||||
vae_enabled: true
|
||||
regional: true
|
||||
attention:
|
||||
backend: VIDEO_SPARSE_ATTN_H3
|
||||
vsa_sparsity: 0.9
|
||||
vsa_tile_size: 64
|
||||
pipeline:
|
||||
experimental:
|
||||
attention_backend: VIDEO_SPARSE_ATTN_H3
|
||||
VSA_sparsity: 0.9
|
||||
VSA_tile_size: 64
|
||||
inference_torch_compile: true
|
||||
vae_parallel_decode: true
|
||||
vae_parallel_decode_strategy: gather
|
||||
model:
|
||||
minimax_h3:
|
||||
vae_parallel_decode: true
|
||||
vae_parallel_decode_strategy: gather
|
||||
request:
|
||||
prompt: >-
|
||||
A wide cinematic shot of an alpine meadow at sunrise, pale pink mountain
|
||||
|
||||
@@ -83,24 +83,23 @@ def main() -> None:
|
||||
|
||||
os.makedirs(args.out_dir, exist_ok=True)
|
||||
|
||||
init_kwargs = {
|
||||
"num_gpus": args.num_gpus,
|
||||
"workload_type": "t2i",
|
||||
"sp_size": 1,
|
||||
"tp_size": 1,
|
||||
"dit_cpu_offload": False,
|
||||
"dit_layerwise_offload": False,
|
||||
"text_encoder_cpu_offload": False,
|
||||
"vae_cpu_offload": False,
|
||||
"image_encoder_cpu_offload": False,
|
||||
"pin_cpu_memory": False,
|
||||
"use_fsdp_inference": False,
|
||||
}
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_path=args.model_path,
|
||||
**init_kwargs,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": args.model_path,
|
||||
"engine": {
|
||||
"num_gpus": args.num_gpus,
|
||||
"parallelism": {"sp_size": 1, "tp_size": 1},
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"dit_layerwise": False,
|
||||
"text_encoder": False,
|
||||
"vae": False,
|
||||
"image_encoder": False,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
"use_fsdp_inference": False,
|
||||
},
|
||||
"pipeline": {"workload_type": "t2i"},
|
||||
})
|
||||
try:
|
||||
for i, prompt in enumerate(prompts):
|
||||
seed = args.seed + i
|
||||
@@ -109,21 +108,23 @@ def main() -> None:
|
||||
output_path = os.path.join(args.out_dir, f"{filename_base}.png")
|
||||
print(f"[flux] prompt_idx={i} seed={seed} output_path={output_path}")
|
||||
|
||||
generation_kwargs = {
|
||||
"output_path": output_path,
|
||||
"height": args.height,
|
||||
"width": args.width,
|
||||
"num_frames": 1,
|
||||
"fps": 1,
|
||||
"num_inference_steps": args.steps,
|
||||
"guidance_scale": args.guidance,
|
||||
"use_embedded_guidance": True,
|
||||
"true_cfg_scale": 1.0,
|
||||
"seed": seed,
|
||||
"save_video": True,
|
||||
request = {
|
||||
"prompt": prompt,
|
||||
"sampling": {
|
||||
"height": args.height,
|
||||
"width": args.width,
|
||||
"num_frames": 1,
|
||||
"fps": 1,
|
||||
"num_inference_steps": args.steps,
|
||||
"guidance_scale": args.guidance,
|
||||
"use_embedded_guidance": True,
|
||||
"true_cfg_scale": 1.0,
|
||||
"seed": seed,
|
||||
},
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
}
|
||||
|
||||
generator.generate_video(prompt, **generation_kwargs)
|
||||
generator.generate(request)
|
||||
|
||||
print(f"[flux] done. outputs written to: {args.out_dir}")
|
||||
finally:
|
||||
|
||||
@@ -56,12 +56,18 @@ def main():
|
||||
# FastVideo will automatically download weights from HuggingFace
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
MODEL_PATH,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# Video parameters
|
||||
@@ -95,23 +101,24 @@ def main():
|
||||
prompt = DEFAULT_I2V_PROMPT if is_i2v else DEFAULT_PROMPTS["temple"]
|
||||
print(f"Mode: {'I2V' if is_i2v else 'T2V'}, prompt: {prompt[:60]}...")
|
||||
|
||||
gen_kw = dict(
|
||||
prompt=prompt,
|
||||
negative_prompt="",
|
||||
camera_states=camera_states,
|
||||
height=height,
|
||||
width=width,
|
||||
num_frames=num_frames,
|
||||
num_inference_steps=50,
|
||||
guidance_scale=6.0,
|
||||
seed=42,
|
||||
fps=24,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
)
|
||||
request = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"sampling": {
|
||||
"height": height,
|
||||
"width": width,
|
||||
"num_frames": num_frames,
|
||||
"num_inference_steps": 50,
|
||||
"guidance_scale": 6.0,
|
||||
"seed": 42,
|
||||
"fps": 24,
|
||||
},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
"extensions": {"camera_states": camera_states},
|
||||
}
|
||||
if is_i2v:
|
||||
gen_kw["image_path"] = image_path
|
||||
generator.generate_video(**gen_kw)
|
||||
request["inputs"] = {"image_path": image_path}
|
||||
generator.generate(request)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -60,31 +60,40 @@ def main():
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
args.model_path,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
video = generator.generate_video(
|
||||
args.prompt,
|
||||
negative_prompt=args.negative_prompt,
|
||||
image_path=args.image_path,
|
||||
trajectory_type=args.trajectory,
|
||||
movement_distance=args.movement_distance,
|
||||
camera_rotation=args.camera_rotation,
|
||||
height=args.height,
|
||||
width=args.width,
|
||||
num_frames=args.num_frames,
|
||||
num_inference_steps=args.num_inference_steps,
|
||||
guidance_scale=args.guidance_scale,
|
||||
fps=24,
|
||||
seed=args.seed,
|
||||
output_path=args.output_path,
|
||||
save_video=True,
|
||||
)
|
||||
video = generator.generate({
|
||||
"prompt": args.prompt,
|
||||
"negative_prompt": args.negative_prompt,
|
||||
"inputs": {"image_path": args.image_path},
|
||||
"sampling": {
|
||||
"height": args.height,
|
||||
"width": args.width,
|
||||
"num_frames": args.num_frames,
|
||||
"num_inference_steps": args.num_inference_steps,
|
||||
"guidance_scale": args.guidance_scale,
|
||||
"fps": 24,
|
||||
"seed": args.seed,
|
||||
},
|
||||
"output": {"output_path": args.output_path, "save_video": True},
|
||||
"extensions": {
|
||||
"trajectory_type": args.trajectory,
|
||||
"movement_distance": args.movement_distance,
|
||||
"camera_rotation": args.camera_rotation,
|
||||
},
|
||||
})
|
||||
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -13,25 +13,31 @@ def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"hunyuanvideo-community/HunyuanVideo-1.5-Diffusers-480p_t2v",
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
|
||||
"wide with interest. The playful yet serene atmosphere is complemented by soft "
|
||||
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
|
||||
|
||||
video = generator.generate_video(prompt,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
negative_prompt="",
|
||||
num_frames=81,
|
||||
fps=16)
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"sampling": {"num_frames": 81, "fps": 16},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
|
||||
"glistening under the warm afternoon sun. The tall grass ripples gently in "
|
||||
@@ -39,12 +45,12 @@ def main():
|
||||
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
|
||||
"cinematic.")
|
||||
|
||||
video2 = generator.generate_video(prompt2,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
negative_prompt="",
|
||||
num_frames=81,
|
||||
fps=16)
|
||||
video2 = generator.generate({
|
||||
"prompt": prompt2,
|
||||
"negative_prompt": "",
|
||||
"sampling": {"num_frames": 81, "fps": 16},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -14,20 +14,30 @@ def main():
|
||||
"weizhou03/HunyuanVideo-1.5-Diffusers-1080p-2SR", # 480p -> 720p -> 1080p
|
||||
# or "weizhou03/HunyuanVideo-1.5-Diffusers-1080p" # 720p -> 1080p
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True, # set to false if low CPU RAM or hit obscure "CUDA error: Invalid argument"
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
|
||||
"wide with interest. The playful yet serene atmosphere is complemented by soft "
|
||||
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
|
||||
|
||||
video = generator.generate_video(prompt, output_path=OUTPUT_PATH, save_video=True, negative_prompt="")
|
||||
video = generator.generate({
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
|
||||
"glistening under the warm afternoon sun. The tall grass ripples gently in "
|
||||
@@ -35,7 +45,11 @@ def main():
|
||||
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
|
||||
"cinematic.")
|
||||
|
||||
video2 = generator.generate_video(prompt2, output_path=OUTPUT_PATH, save_video=True, negative_prompt="")
|
||||
video2 = generator.generate({
|
||||
"prompt": prompt2,
|
||||
"negative_prompt": "",
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -35,31 +35,40 @@ def main():
|
||||
print("\nInitializing VideoGenerator for HYWorld...")
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/HY-WorldPlay-Bidirectional-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
image_encoder_cpu_offload=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
"image_encoder": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
# Generate video
|
||||
# The pose string is automatically converted to camera matrices by the pipeline
|
||||
print("\nGenerating video...")
|
||||
generator.generate_video(
|
||||
prompt=args.prompt,
|
||||
image_path=args.image,
|
||||
pose=args.pose, # Camera trajectory control
|
||||
output_path=args.output_path,
|
||||
save_video=True,
|
||||
negative_prompt="",
|
||||
num_frames=args.num_frames,
|
||||
fps=24,
|
||||
height=HEIGHT,
|
||||
width=WIDTH,
|
||||
seed=args.seed,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": args.prompt,
|
||||
"negative_prompt": "",
|
||||
"inputs": {
|
||||
"image_path": args.image,
|
||||
"pose": args.pose, # Camera trajectory control
|
||||
},
|
||||
"sampling": {
|
||||
"num_frames": args.num_frames,
|
||||
"fps": 24,
|
||||
"height": HEIGHT,
|
||||
"width": WIDTH,
|
||||
"seed": args.seed,
|
||||
},
|
||||
"output": {"output_path": args.output_path, "save_video": True},
|
||||
})
|
||||
|
||||
print(f"\nVideo saved to: {args.output_path}")
|
||||
|
||||
|
||||
@@ -10,25 +10,28 @@ def main():
|
||||
"kandinskylab/Kandinsky-5.0-I2V-Pro-distilled-5s-Diffusers",
|
||||
# "kandinskylab/Kandinsky-5.0-I2V-Pro-sft-5s-Diffusers"
|
||||
# "kandinskylab/Kandinsky-5.0-I2V-Lite-5s-Diffusers"
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = ("A woman stands up and walks away")
|
||||
_ = generator.generate_video(
|
||||
prompt,
|
||||
image_path=IMAGE_PATH,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
height=1024,
|
||||
width=1024,
|
||||
num_frames=121,
|
||||
)
|
||||
_ = generator.generate({
|
||||
"prompt": prompt,
|
||||
"inputs": {"image_path": IMAGE_PATH},
|
||||
"sampling": {"height": 1024, "width": 1024, "num_frames": 121},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -9,36 +9,40 @@ def main():
|
||||
# "kandinskylab/Kandinsky-5.0-T2V-Pro-sft-5s-Diffusers"
|
||||
# "kandinskylab/Kandinsky-5.0-T2V-Lite-distilled16steps-5s-Diffusers"
|
||||
# "kandinskylab/Kandinsky-5.0-T2V-Pro-distilled-5s-Diffusers"
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = ("A curious raccoon peers through a vibrant field of yellow sunflowers, its eyes "
|
||||
"wide with interest. The playful yet serene atmosphere is complemented by soft "
|
||||
"natural light filtering through the petals. Mid-shot, warm and cheerful tones.")
|
||||
_ = generator.generate_video(prompt,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
height=512,
|
||||
width=768,
|
||||
num_frames=121)
|
||||
_ = generator.generate({
|
||||
"prompt": prompt,
|
||||
"sampling": {"height": 512, "width": 768, "num_frames": 121},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
prompt2 = ("A majestic lion strides across the golden savanna, its powerful frame "
|
||||
"glistening under the warm afternoon sun. The tall grass ripples gently in "
|
||||
"the breeze, enhancing the lion's commanding presence. The tone is vibrant, "
|
||||
"embodying the raw energy of the wild. Low angle, steady tracking shot, "
|
||||
"cinematic.")
|
||||
_ = generator.generate_video(prompt2,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
height=512,
|
||||
width=768,
|
||||
num_frames=121)
|
||||
_ = generator.generate({
|
||||
"prompt": prompt2,
|
||||
"sampling": {"height": 512, "width": 768, "num_frames": 121},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -29,11 +29,17 @@ def main() -> None:
|
||||
args = parse_args()
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
str(args.model_path),
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
vae_cpu_offload=False,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"text_encoder": True,
|
||||
"vae": False,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
try:
|
||||
generator.generate({
|
||||
|
||||
@@ -13,36 +13,43 @@ OUTPUT_PATH = REPO_ROOT / "outputs" / "lingbotworld2_causal_fast.mp4"
|
||||
|
||||
def main() -> None:
|
||||
"""Load the native FastVideo LingBot World 2 causal-fast pipeline and generate one video."""
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
os.environ["LINGBOTWORLD2_MODEL_PATH"],
|
||||
num_gpus=8,
|
||||
sp_size=8,
|
||||
hsdp_shard_dim=8,
|
||||
use_fsdp_inference=True,
|
||||
dit_layerwise_offload=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=False,
|
||||
pin_cpu_memory=True,
|
||||
override_pipeline_cls_name="LingBotWorld2CausalFastPipeline",
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": os.environ["LINGBOTWORLD2_MODEL_PATH"],
|
||||
"engine": {
|
||||
"num_gpus": 8,
|
||||
"parallelism": {"sp_size": 8, "hsdp_shard_dim": 8},
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit_layerwise": False,
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": False,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
"pipeline": {"components": {"override_pipeline_cls_name": "LingBotWorld2CausalFastPipeline"}},
|
||||
})
|
||||
|
||||
try:
|
||||
generator.generate_video(
|
||||
generator.generate({
|
||||
"prompt":
|
||||
"A serene lakeside scene with a lone tree standing in calm water, surrounded by distant snow-capped mountains under a bright blue sky with drifting white clouds; gentle ripples reflect the tree and sky, creating a tranquil, meditative atmosphere.",
|
||||
image_path=str(DATASET_DIR / "image.jpg"),
|
||||
action_path=str(DATASET_DIR),
|
||||
output_path=str(OUTPUT_PATH),
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=65,
|
||||
num_inference_steps=4,
|
||||
guidance_scale=1.0,
|
||||
negative_prompt="",
|
||||
fps=16,
|
||||
seed=42,
|
||||
)
|
||||
"negative_prompt": "",
|
||||
"inputs": {
|
||||
"image_path": str(DATASET_DIR / "image.jpg"),
|
||||
"action_path": str(DATASET_DIR),
|
||||
},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 65,
|
||||
"num_inference_steps": 4,
|
||||
"guidance_scale": 1.0,
|
||||
"fps": 16,
|
||||
"seed": 42,
|
||||
},
|
||||
"output": {"output_path": str(OUTPUT_PATH), "save_video": True},
|
||||
})
|
||||
finally:
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
@@ -13,14 +13,20 @@ def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LingBot-World-Base-Cam-Diffusers",
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=True, # DiT need to be offloaded for MoE
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
pin_cpu_memory=True,
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": True, # DiT need to be offloaded for MoE
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
"pin_cpu_memory": True,
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
num_frames = 81
|
||||
@@ -35,16 +41,12 @@ def main():
|
||||
spatial_scale=8,
|
||||
)
|
||||
|
||||
generator.generate_video(
|
||||
prompt,
|
||||
image_path=image_path,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
num_frames=num_frames,
|
||||
height=480,
|
||||
width=832,
|
||||
c2ws_plucker_emb=c2ws_plucker_emb,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
"inputs": {"image_path": image_path, "c2ws_plucker_emb": c2ws_plucker_emb},
|
||||
"sampling": {"num_frames": num_frames, "height": 480, "width": 832},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -48,33 +48,38 @@ def basic_generation():
|
||||
print("LongCat I2V: Basic Generation (50 steps, 480p)")
|
||||
print("=" * 60)
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-I2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-I2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
|
||||
})
|
||||
|
||||
output_path = "outputs_video/longcat_i2v_basic"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
image_path=IMAGE_PATH,
|
||||
output_path=output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=480, # Square
|
||||
num_frames=93,
|
||||
num_inference_steps=50,
|
||||
fps=15,
|
||||
guidance_scale=4.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"image_path": IMAGE_PATH},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 480, # Square
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 15,
|
||||
"guidance_scale": 4.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
})
|
||||
|
||||
print(f"\nBasic generation complete! Video saved to: {output_path}")
|
||||
generator.shutdown()
|
||||
@@ -95,35 +100,44 @@ def distill_refine_generation():
|
||||
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
|
||||
print("-" * 40)
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-I2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
lora_nickname="distilled",
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-I2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {"longcat": {"enable_bsa": False}},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
"lora_nickname": "distilled",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
distill_output_path = "outputs_video/longcat_i2v_distill"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
image_path=IMAGE_PATH,
|
||||
output_path=distill_output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=480, # Square
|
||||
num_frames=93,
|
||||
num_inference_steps=16,
|
||||
fps=15,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"image_path": IMAGE_PATH},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 480, # Square
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 16,
|
||||
"fps": 15,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": distill_output_path, "save_video": True},
|
||||
})
|
||||
|
||||
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
|
||||
generator.shutdown()
|
||||
@@ -144,40 +158,55 @@ def distill_refine_generation():
|
||||
# Note: Refinement uses the T2V model (not I2V) since it's upscaling the generated video
|
||||
# For BSA [4, 4, 8]: latent must be divisible by 8
|
||||
# 768x768: latent 48x48, 48%8=0 ✓
|
||||
refine_generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=True,
|
||||
bsa_sparsity=0.875,
|
||||
bsa_chunk_q=[4, 4, 4],
|
||||
bsa_chunk_k=[4, 4, 4],
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
lora_nickname="refinement",
|
||||
)
|
||||
refine_generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {
|
||||
"longcat": {
|
||||
"enable_bsa": True,
|
||||
"bsa_sparsity": 0.875,
|
||||
"bsa_chunk_q": [4, 4, 4],
|
||||
"bsa_chunk_k": [4, 4, 4],
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
"lora_nickname": "refinement",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
refine_output_path = "outputs_video/longcat_i2v_refine_720p"
|
||||
|
||||
refine_generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
output_path=refine_output_path,
|
||||
save_video=True,
|
||||
refine_from=distill_video_path,
|
||||
t_thresh=0.5,
|
||||
spatial_refine_only=False,
|
||||
num_cond_frames=0,
|
||||
height=720,
|
||||
width=720,
|
||||
num_inference_steps=50,
|
||||
fps=30,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
refine_generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"refine_from": distill_video_path},
|
||||
"sampling": {
|
||||
"height": 720,
|
||||
"width": 720,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 30,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": refine_output_path, "save_video": True},
|
||||
"extensions": {
|
||||
"t_thresh": 0.5,
|
||||
"spatial_refine_only": False,
|
||||
"num_cond_frames": 0,
|
||||
},
|
||||
})
|
||||
|
||||
print(f"Refinement complete! Video saved to: {refine_output_path}")
|
||||
refine_generator.shutdown()
|
||||
|
||||
@@ -41,32 +41,37 @@ def basic_generation():
|
||||
print("LongCat T2V: Basic Generation (50 steps, 480p)")
|
||||
print("=" * 60)
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
|
||||
})
|
||||
|
||||
output_path = "outputs_video/longcat_t2v_basic"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
output_path=output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=93,
|
||||
num_inference_steps=50,
|
||||
fps=15,
|
||||
guidance_scale=4.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 15,
|
||||
"guidance_scale": 4.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
})
|
||||
|
||||
print(f"\nBasic generation complete! Video saved to: {output_path}")
|
||||
generator.shutdown()
|
||||
@@ -87,34 +92,43 @@ def distill_refine_generation():
|
||||
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
|
||||
print("-" * 40)
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
lora_nickname="distilled",
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {"longcat": {"enable_bsa": False}},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
"lora_nickname": "distilled",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
distill_output_path = "outputs_video/longcat_t2v_distill"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
output_path=distill_output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=93,
|
||||
num_inference_steps=16,
|
||||
fps=15,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 16,
|
||||
"fps": 15,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": distill_output_path, "save_video": True},
|
||||
})
|
||||
|
||||
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
|
||||
generator.shutdown()
|
||||
@@ -132,40 +146,55 @@ def distill_refine_generation():
|
||||
print(f"Using stage 1 video: {distill_video_path}")
|
||||
|
||||
# Create a new generator with refinement LoRA and BSA enabled
|
||||
refine_generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=True,
|
||||
bsa_sparsity=0.875,
|
||||
bsa_chunk_q=[4, 4, 8],
|
||||
bsa_chunk_k=[4, 4, 8],
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
lora_nickname="refinement",
|
||||
)
|
||||
refine_generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {
|
||||
"longcat": {
|
||||
"enable_bsa": True,
|
||||
"bsa_sparsity": 0.875,
|
||||
"bsa_chunk_q": [4, 4, 8],
|
||||
"bsa_chunk_k": [4, 4, 8],
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
"lora_nickname": "refinement",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
refine_output_path = "outputs_video/longcat_t2v_refine_720p"
|
||||
|
||||
refine_generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
output_path=refine_output_path,
|
||||
save_video=True,
|
||||
refine_from=distill_video_path,
|
||||
t_thresh=0.5,
|
||||
spatial_refine_only=False,
|
||||
num_cond_frames=0,
|
||||
height=720,
|
||||
width=1280,
|
||||
num_inference_steps=50,
|
||||
fps=30,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
refine_generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"refine_from": distill_video_path},
|
||||
"sampling": {
|
||||
"height": 720,
|
||||
"width": 1280,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 30,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": refine_output_path, "save_video": True},
|
||||
"extensions": {
|
||||
"t_thresh": 0.5,
|
||||
"spatial_refine_only": False,
|
||||
"num_cond_frames": 0,
|
||||
},
|
||||
})
|
||||
|
||||
print(f"Refinement complete! Video saved to: {refine_output_path}")
|
||||
refine_generator.shutdown()
|
||||
|
||||
@@ -57,34 +57,39 @@ def basic_generation():
|
||||
raise FileNotFoundError(f"Video not found at {VIDEO_PATH}. "
|
||||
"Please provide a valid video path.")
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-VC-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-VC-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {"model": {"longcat": {"enable_bsa": False}}},
|
||||
})
|
||||
|
||||
output_path = "outputs_video/longcat_vc_basic"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
video_path=VIDEO_PATH,
|
||||
num_cond_frames=NUM_COND_FRAMES,
|
||||
output_path=output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=93,
|
||||
num_inference_steps=50,
|
||||
fps=15,
|
||||
guidance_scale=4.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"video_path": VIDEO_PATH},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 15,
|
||||
"guidance_scale": 4.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
"extensions": {"num_cond_frames": NUM_COND_FRAMES},
|
||||
})
|
||||
|
||||
print(f"\nBasic generation complete! Video saved to: {output_path}")
|
||||
generator.shutdown()
|
||||
@@ -110,36 +115,45 @@ def distill_refine_generation():
|
||||
print("\n[Stage 1] Distilled generation (16 steps, 480p)")
|
||||
print("-" * 40)
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-VC-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=False,
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
lora_nickname="distilled",
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-VC-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {"longcat": {"enable_bsa": False}},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Distilled-LoRA",
|
||||
"lora_nickname": "distilled",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
distill_output_path = "outputs_video/longcat_vc_distill"
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
video_path=VIDEO_PATH,
|
||||
num_cond_frames=NUM_COND_FRAMES,
|
||||
output_path=distill_output_path,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=93,
|
||||
num_inference_steps=16,
|
||||
fps=15,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"video_path": VIDEO_PATH},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 93,
|
||||
"num_inference_steps": 16,
|
||||
"fps": 15,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": distill_output_path, "save_video": True},
|
||||
"extensions": {"num_cond_frames": NUM_COND_FRAMES},
|
||||
})
|
||||
|
||||
print(f"Distilled generation complete! Video saved to: {distill_output_path}")
|
||||
generator.shutdown()
|
||||
@@ -158,40 +172,55 @@ def distill_refine_generation():
|
||||
|
||||
# Create a new generator with refinement LoRA and BSA enabled
|
||||
# Note: Refinement uses the T2V model (not VC) since it's upscaling the generated video
|
||||
refine_generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=True,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=True,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=False,
|
||||
enable_bsa=True,
|
||||
bsa_sparsity=0.875,
|
||||
bsa_chunk_q=[4, 4, 8],
|
||||
bsa_chunk_k=[4, 4, 8],
|
||||
lora_path="FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
lora_nickname="refinement",
|
||||
)
|
||||
refine_generator = VideoGenerator.from_config({
|
||||
"model_path": "FastVideo/LongCat-Video-T2V-Diffusers",
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": True,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": True,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"model": {
|
||||
"longcat": {
|
||||
"enable_bsa": True,
|
||||
"bsa_sparsity": 0.875,
|
||||
"bsa_chunk_q": [4, 4, 8],
|
||||
"bsa_chunk_k": [4, 4, 8],
|
||||
},
|
||||
},
|
||||
"components": {
|
||||
"lora_path": "FastVideo/LongCat-Video-T2V-Refinement-LoRA",
|
||||
"lora_nickname": "refinement",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
refine_output_path = "outputs_video/longcat_vc_refine_720p"
|
||||
|
||||
refine_generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
negative_prompt=NEGATIVE_PROMPT,
|
||||
output_path=refine_output_path,
|
||||
save_video=True,
|
||||
refine_from=distill_video_path,
|
||||
t_thresh=0.5,
|
||||
spatial_refine_only=False,
|
||||
num_cond_frames=0, # For refinement, no conditioning frames
|
||||
height=720,
|
||||
width=1280,
|
||||
num_inference_steps=50,
|
||||
fps=30,
|
||||
guidance_scale=1.0,
|
||||
seed=SEED,
|
||||
)
|
||||
refine_generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": NEGATIVE_PROMPT,
|
||||
"inputs": {"refine_from": distill_video_path},
|
||||
"sampling": {
|
||||
"height": 720,
|
||||
"width": 1280,
|
||||
"num_inference_steps": 50,
|
||||
"fps": 30,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": SEED,
|
||||
},
|
||||
"output": {"output_path": refine_output_path, "save_video": True},
|
||||
"extensions": {
|
||||
"t_thresh": 0.5,
|
||||
"spatial_refine_only": False,
|
||||
"num_cond_frames": 0, # For refinement, no conditioning frames
|
||||
},
|
||||
})
|
||||
|
||||
print(f"Refinement complete! Video saved to: {refine_output_path}")
|
||||
refine_generator.shutdown()
|
||||
|
||||
@@ -17,18 +17,19 @@ def main() -> None:
|
||||
# Uses FastVideo default sampling settings for LTX2 base.
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"Davids048/LTX2-Base-Diffusers",
|
||||
num_gpus=1,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
output_path = "outputs_video/ltx2_basic/output_ltx2_base_t2v_1088_1920_1.1.mp4"
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
output_path=output_path,
|
||||
save_video=True,
|
||||
num_frames=121,
|
||||
height=1088,
|
||||
width=1920,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"sampling": {"num_frames": 121, "height": 1088, "width": 1920},
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
})
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ from pathlib import Path
|
||||
import torch._inductor.config as _inductor
|
||||
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import GenerationResult
|
||||
from fastvideo.configs.pipelines.base import PipelineConfig
|
||||
from fastvideo.utils import maybe_download_model
|
||||
|
||||
@@ -80,9 +81,9 @@ PROMPT = os.getenv("LTX23_I2V_PROMPT", DEFAULT_PROMPT)
|
||||
# Per-stage timing helpers --------------------------------------------------
|
||||
|
||||
|
||||
def _print_stage_breakdown(result: dict, label: str) -> float | None:
|
||||
def _print_stage_breakdown(result: GenerationResult, label: str) -> float | None:
|
||||
"""Print stage execution times and return the sum, or None if missing."""
|
||||
logging_info = result.get("logging_info")
|
||||
logging_info = result.logging_info
|
||||
stages = getattr(logging_info, "stages", None) if logging_info else None
|
||||
if not stages:
|
||||
print(f" [{label}] stage breakdown unavailable")
|
||||
@@ -98,11 +99,11 @@ def _print_stage_breakdown(result: dict, label: str) -> float | None:
|
||||
|
||||
|
||||
def _collect_stage_times(
|
||||
result: dict,
|
||||
result: GenerationResult,
|
||||
stage_times: dict[str, list[float]],
|
||||
stage_order: OrderedDict[str, None],
|
||||
) -> None:
|
||||
logging_info = result.get("logging_info")
|
||||
logging_info = result.logging_info
|
||||
stages = getattr(logging_info, "stages", None) if logging_info else None
|
||||
if not stages:
|
||||
return
|
||||
@@ -158,52 +159,68 @@ def main() -> None:
|
||||
pipeline_config = PipelineConfig.from_pretrained(model_root)
|
||||
pipeline_config.dit_config.quant_config = None
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_root,
|
||||
num_gpus=1,
|
||||
# LTX-2.3 distilled uses the two-stage refine pipeline; the refine
|
||||
# LoRA is intentionally empty for the distilled student.
|
||||
ltx2_refine_enabled=True,
|
||||
ltx2_refine_upsampler_path=str(refine_upsampler_path),
|
||||
ltx2_refine_lora_path="",
|
||||
ltx2_refine_num_inference_steps=3,
|
||||
ltx2_refine_guidance_scale=1.0,
|
||||
ltx2_refine_add_noise=True,
|
||||
pipeline_config=pipeline_config,
|
||||
enable_torch_compile=True,
|
||||
enable_torch_compile_text_encoder=True,
|
||||
# Compile the VAE codec submodules (encoder / decoder) too. The
|
||||
# `LTX2CausalVideoAutoencoder` declares `_compile_conditions` so
|
||||
# `_compile_with_conditions` targets just those submodules and
|
||||
# leaves the surrounding tiling control flow eager — needed for
|
||||
# fullgraph + dynamic=False to succeed. VAE eager decode is
|
||||
# ~1.0s; compiling it brings the stage to ~0.3s.
|
||||
enable_torch_compile_vae=True,
|
||||
torch_compile_kwargs=torch_compile_kwargs,
|
||||
torch_compile_kwargs_vae=torch_compile_kwargs,
|
||||
# Keep everything resident — no CPU offload for serving-style runs.
|
||||
dit_cpu_offload=False,
|
||||
text_encoder_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
ltx2_vae_tiling=False,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_root,
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"compile": {
|
||||
"enabled": True,
|
||||
"text_encoder_enabled": True,
|
||||
# Compile the VAE codec submodules (encoder / decoder) too. The
|
||||
# `LTX2CausalVideoAutoencoder` declares `_compile_conditions` so
|
||||
# `_compile_with_conditions` targets just those submodules and
|
||||
# leaves the surrounding tiling control flow eager — needed for
|
||||
# fullgraph + dynamic=False to succeed. VAE eager decode is
|
||||
# ~1.0s; compiling it brings the stage to ~0.3s.
|
||||
"vae_enabled": True,
|
||||
**torch_compile_kwargs,
|
||||
"vae_kwargs": torch_compile_kwargs,
|
||||
},
|
||||
# Keep everything resident — no CPU offload for serving-style runs.
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"text_encoder": False,
|
||||
"vae": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
# LTX-2.3 distilled uses the two-stage refine pipeline; the refine
|
||||
# LoRA is intentionally empty for the distilled student.
|
||||
"preset_overrides": {
|
||||
"refine": {
|
||||
"enabled": True,
|
||||
"num_inference_steps": 3,
|
||||
"guidance_scale": 1.0,
|
||||
"add_noise": True,
|
||||
},
|
||||
},
|
||||
"components": {"upsampler_weights": str(refine_upsampler_path)},
|
||||
"model": {"ltx2": {"refine": {"lora_path": ""}}},
|
||||
"experimental": {"pipeline_config": pipeline_config},
|
||||
"vae_tiling": False,
|
||||
},
|
||||
})
|
||||
|
||||
common_kwargs = dict(
|
||||
prompt=PROMPT,
|
||||
negative_prompt="", # distilled is CFG-free; no negative needed
|
||||
guidance_scale=1.0, # CFG=1 for distilled
|
||||
height=1280,
|
||||
width=832, # portrait runway aspect
|
||||
num_frames=121,
|
||||
fps=24, # ~5s clip
|
||||
num_inference_steps=8, # distilled denoise steps
|
||||
common_request = {
|
||||
"prompt": PROMPT,
|
||||
"negative_prompt": "", # distilled is CFG-free; no negative needed
|
||||
"sampling": {
|
||||
"guidance_scale": 1.0, # CFG=1 for distilled
|
||||
"height": 1280,
|
||||
"width": 832, # portrait runway aspect
|
||||
"num_frames": 121,
|
||||
"fps": 24, # ~5s clip
|
||||
"num_inference_steps": 8, # distilled denoise steps
|
||||
},
|
||||
"output": {"save_video": True},
|
||||
# i2v: anchor the input image at frame 0 with full strength.
|
||||
# `ltx2_image_crf=0.0` skips an extra JPEG re-encode of an already
|
||||
# JPEG conditioning image.
|
||||
ltx2_images=[(I2V_IMAGE, 0, 1.0)],
|
||||
ltx2_image_crf=0.0,
|
||||
save_video=True,
|
||||
)
|
||||
"extensions": {
|
||||
"ltx2_images": [(I2V_IMAGE, 0, 1.0)],
|
||||
"ltx2_image_crf": 0.0,
|
||||
},
|
||||
}
|
||||
|
||||
warmup_runs = 2
|
||||
measured_runs = 2
|
||||
@@ -218,11 +235,11 @@ def main() -> None:
|
||||
for w in range(warmup_runs):
|
||||
t0 = time.perf_counter()
|
||||
print(f"\n[warmup {w + 1}/{warmup_runs}] compiling + generating…")
|
||||
generator.generate_video(
|
||||
output_path=str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4"),
|
||||
seed=7,
|
||||
**common_kwargs,
|
||||
)
|
||||
generator.generate({
|
||||
**common_request,
|
||||
"sampling": {**common_request["sampling"], "seed": 7},
|
||||
"output": {**common_request["output"], "output_path": str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4")},
|
||||
})
|
||||
dt = time.perf_counter() - t0
|
||||
warmup_secs.append(dt)
|
||||
print(f"[warmup {w + 1}/{warmup_runs}] wall={dt:.1f}s")
|
||||
@@ -236,16 +253,16 @@ def main() -> None:
|
||||
out_path = OUTPUT_DIR / f"output_ltx2_3_distilled_i2v_run_{m + 1}.mp4"
|
||||
print(f"\n[measured {m + 1}/{measured_runs}] generating: {out_path}")
|
||||
t0 = time.perf_counter()
|
||||
result = generator.generate_video(
|
||||
output_path=str(out_path),
|
||||
seed=2002 + m,
|
||||
**common_kwargs,
|
||||
)
|
||||
result = generator.generate({
|
||||
**common_request,
|
||||
"sampling": {**common_request["sampling"], "seed": 2002 + m},
|
||||
"output": {**common_request["output"], "output_path": str(out_path)},
|
||||
})
|
||||
wall = time.perf_counter() - t0
|
||||
e2e = (result.get("e2e_latency") if isinstance(result, dict) else None) or wall
|
||||
e2e = (result.extra.get("e2e_latency") if isinstance(result, GenerationResult) else None) or wall
|
||||
measured_secs.append(e2e)
|
||||
print(f"[measured {m + 1}/{measured_runs}] e2e={e2e:.2f}s wall={wall:.2f}s")
|
||||
if isinstance(result, dict):
|
||||
if isinstance(result, GenerationResult):
|
||||
_print_stage_breakdown(result, f"measured {m + 1}")
|
||||
_collect_stage_times(result, stage_times, stage_order)
|
||||
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
"""LTX-2.3 distilled image-to-video — typed API (``from_config`` / ``generate``).
|
||||
|
||||
Identical generation behavior to ``basic_ltx2_3_distilled_i2v.py``, but
|
||||
expressed through the newer typed surface (``GeneratorConfig`` /
|
||||
``GenerationRequest``) instead of the ``from_pretrained(**legacy_kwargs)``
|
||||
bridge. The typed API is now the preferred entry point — the legacy
|
||||
example still works but emits a ``DeprecationWarning`` for the LTX-2.3
|
||||
specific knobs.
|
||||
expressed with the typed dataclasses (``GeneratorConfig`` /
|
||||
``GenerationRequest``) instead of nested dicts and a ``PipelineConfig``
|
||||
object.
|
||||
|
||||
Quick start
|
||||
-----------
|
||||
@@ -42,28 +40,28 @@ Hardware notes
|
||||
The ``_inductor.shape_padding = False`` line below also avoids a
|
||||
``pad_mm`` landmine on the same generation of cards.
|
||||
|
||||
Typed-API mapping (legacy kwarg ↔ typed field)
|
||||
----------------------------------------------
|
||||
- ``num_gpus`` ↔ ``engine.num_gpus``
|
||||
- ``enable_torch_compile`` ↔ ``engine.compile.enabled``
|
||||
- ``enable_torch_compile_text_encoder`` ↔ ``engine.compile.text_encoder_enabled``
|
||||
- ``enable_torch_compile_vae`` ↔ ``engine.compile.vae_enabled``
|
||||
- ``torch_compile_kwargs`` ↔ ``engine.compile.backend/fullgraph/mode/dynamic``
|
||||
- ``torch_compile_kwargs_vae`` ↔ empty ``compile.vae_kwargs`` (inherits master)
|
||||
- ``dit_cpu_offload`` ↔ ``engine.offload.dit``
|
||||
- ``text_encoder_cpu_offload`` ↔ ``engine.offload.text_encoder``
|
||||
- ``vae_cpu_offload`` ↔ ``engine.offload.vae``
|
||||
- ``ltx2_vae_tiling`` ↔ ``pipeline.vae_tiling``
|
||||
- ``ltx2_refine_enabled`` ↔ ``pipeline.preset_overrides["refine"]["enabled"]``
|
||||
- ``ltx2_refine_upsampler_path`` ↔ ``pipeline.components.upsampler_weights``
|
||||
- ``ltx2_refine_lora_path`` ↔ ``pipeline.components.lora_path``
|
||||
- ``ltx2_refine_num_inference_steps`` ↔ ``pipeline.preset_overrides["refine"]["num_inference_steps"]``
|
||||
- ``ltx2_refine_guidance_scale`` ↔ ``pipeline.preset_overrides["refine"]["guidance_scale"]``
|
||||
- ``ltx2_refine_add_noise`` ↔ ``pipeline.preset_overrides["refine"]["add_noise"]``
|
||||
- ``pipeline_config=PipelineConfig.from_pretrained(model_root)`` ↔ (no-op — ``PipelineConfig.from_kwargs`` already resolves the model-specific class from ``model_path``)
|
||||
- ``pipeline_config.dit_config.quant_config = None`` ↔ leave ``engine.quantization`` unset
|
||||
- ``ltx2_images`` / ``ltx2_image_crf`` ↔ ``request.extensions`` (LTX-2 specific, no
|
||||
first-class typed field yet)
|
||||
Recipe setting ↔ typed field
|
||||
----------------------------
|
||||
- GPU count ↔ ``engine.num_gpus``
|
||||
- DiT compile ↔ ``engine.compile.enabled``
|
||||
- Text encoder compile ↔ ``engine.compile.text_encoder_enabled``
|
||||
- VAE compile ↔ ``engine.compile.vae_enabled``
|
||||
- ``torch.compile`` kwargs ↔ ``engine.compile.backend/fullgraph/mode/dynamic``
|
||||
- VAE ``torch.compile`` kwargs ↔ empty ``engine.compile.vae_kwargs`` (inherits the master kwargs)
|
||||
- DiT CPU offload ↔ ``engine.offload.dit``
|
||||
- Text encoder CPU offload ↔ ``engine.offload.text_encoder``
|
||||
- VAE CPU offload ↔ ``engine.offload.vae``
|
||||
- VAE tiling ↔ ``pipeline.vae_tiling``
|
||||
- Refine stage on ↔ ``pipeline.preset_overrides["refine"]["enabled"]``
|
||||
- Refine upsampler ↔ ``pipeline.components.upsampler_weights``
|
||||
- Refine LoRA ↔ ``pipeline.model.ltx2.refine.lora_path``
|
||||
- Refine denoising steps ↔ ``pipeline.preset_overrides["refine"]["num_inference_steps"]``
|
||||
- Refine guidance scale ↔ ``pipeline.preset_overrides["refine"]["guidance_scale"]``
|
||||
- Refine noise injection ↔ ``pipeline.preset_overrides["refine"]["add_noise"]``
|
||||
- Model-specific ``PipelineConfig`` ↔ nothing to set: ``PipelineConfig.from_source`` builds the
|
||||
registry class of ``model_path``
|
||||
- No DiT quantization ↔ leave ``engine.quantization`` unset
|
||||
- Conditioning image and its CRF ↔ ``request.extensions["ltx2_images"]`` / ``["ltx2_image_crf"]`` (LTX-2 specific)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -177,7 +175,7 @@ def main() -> None:
|
||||
# Keep DiT / text encoder / VAE resident on GPU — no CPU offload
|
||||
# for serving-style runs. ``image_encoder`` and
|
||||
# ``pin_cpu_memory`` are left at their schema defaults
|
||||
# (matches the legacy example, which only set these three).
|
||||
# (matches ``basic_ltx2_3_distilled_i2v.py``, which sets only these three).
|
||||
offload=OffloadConfig(
|
||||
dit=False,
|
||||
text_encoder=False,
|
||||
@@ -199,12 +197,12 @@ def main() -> None:
|
||||
),
|
||||
),
|
||||
pipeline=PipelineSelection(
|
||||
# ``PipelineConfig.from_kwargs`` resolves the model-specific
|
||||
# ``PipelineConfig.from_source`` resolves the model-specific
|
||||
# pipeline-config class from ``model_path`` automatically, so we
|
||||
# don't need to set ``components.pipeline_config_path`` — the
|
||||
# model-specific VAE precision / decoder defaults are picked up
|
||||
# the same way the legacy example's
|
||||
# ``PipelineConfig.from_pretrained(model_root)`` did them.
|
||||
# the same way ``basic_ltx2_3_distilled_i2v.py``'s
|
||||
# ``PipelineConfig.from_pretrained(model_root)`` picks them up.
|
||||
components=ComponentConfig(upsampler_weights=str(refine_upsampler_path),
|
||||
# Distilled has no refine LoRA — omit ``lora_path``.
|
||||
),
|
||||
|
||||
@@ -19,15 +19,18 @@ os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "FLASH_ATTN"
|
||||
def main() -> None:
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"FastVideo/LTX2-Distilled-Diffusers",
|
||||
num_gpus=4,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 4,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
output_path = "outputs_video/ltx2_basic/output_ltx2_distilled_t2v.mp4"
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
output_path=output_path,
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"output": {"output_path": output_path, "save_video": True},
|
||||
})
|
||||
generator.shutdown()
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
||||
import torch
|
||||
import torch._inductor.config
|
||||
from fastvideo import VideoGenerator
|
||||
from fastvideo.api import GenerationResult
|
||||
from fastvideo.configs.pipelines.base import PipelineConfig
|
||||
from fastvideo.layers.quantization.nvfp4_config import NVFP4Config
|
||||
from fastvideo.utils import maybe_download_model
|
||||
@@ -41,11 +42,11 @@ def load_validation_entries(path: Path) -> list[dict]:
|
||||
|
||||
|
||||
def print_stage_breakdown(
|
||||
result: dict,
|
||||
result: GenerationResult,
|
||||
run_idx: int,
|
||||
num_runs: int,
|
||||
) -> float | None:
|
||||
logging_info = result.get("logging_info")
|
||||
logging_info = result.logging_info
|
||||
if logging_info is None:
|
||||
print(f"[{run_idx}/{num_runs}] Stage breakdown unavailable: no logging_info")
|
||||
return None
|
||||
@@ -65,8 +66,8 @@ def print_stage_breakdown(
|
||||
return total
|
||||
|
||||
|
||||
def extract_sr_forward_latency(result: dict, ) -> tuple[float | None, list[tuple[str, float]], list[str]]:
|
||||
logging_info = result.get("logging_info")
|
||||
def extract_sr_forward_latency(result: GenerationResult, ) -> tuple[float | None, list[tuple[str, float]], list[str]]:
|
||||
logging_info = result.logging_info
|
||||
if logging_info is None:
|
||||
return None, [], []
|
||||
|
||||
@@ -96,11 +97,11 @@ def extract_sr_forward_latency(result: dict, ) -> tuple[float | None, list[tuple
|
||||
|
||||
|
||||
def collect_stage_times(
|
||||
result: dict,
|
||||
result: GenerationResult,
|
||||
stage_times: dict[str, list[float]],
|
||||
stage_order: OrderedDict[str, None],
|
||||
) -> None:
|
||||
logging_info = result.get("logging_info")
|
||||
logging_info = result.logging_info
|
||||
if logging_info is None:
|
||||
return
|
||||
stages = getattr(logging_info, "stages", None)
|
||||
@@ -206,27 +207,40 @@ def main() -> None:
|
||||
"dynamic": False,
|
||||
}
|
||||
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
model_root,
|
||||
num_gpus=1,
|
||||
ltx2_refine_enabled=True,
|
||||
ltx2_refine_upsampler_path=str(refine_upsampler_path),
|
||||
refine_lora_path="", # keep refine LoRA disabled in this repo's typed adapter
|
||||
ltx2_refine_lora_path="", # keep refine LoRA disabled for distilled model
|
||||
ltx2_refine_num_inference_steps=2,
|
||||
ltx2_refine_guidance_scale=1.0,
|
||||
ltx2_refine_add_noise=True,
|
||||
pipeline_config=pipeline_config,
|
||||
enable_torch_compile=True,
|
||||
enable_torch_compile_text_encoder=True,
|
||||
enable_torch_compile_vae=True,
|
||||
torch_compile_kwargs=torch_compile_kwargs,
|
||||
torch_compile_kwargs_vae=torch_compile_kwargs,
|
||||
dit_cpu_offload=False,
|
||||
text_encoder_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
ltx2_vae_tiling=False,
|
||||
)
|
||||
generator = VideoGenerator.from_config({
|
||||
"model_path": model_root,
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"compile": {
|
||||
"enabled": True,
|
||||
"text_encoder_enabled": True,
|
||||
"vae_enabled": True,
|
||||
**torch_compile_kwargs,
|
||||
"vae_kwargs": torch_compile_kwargs,
|
||||
},
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"text_encoder": False,
|
||||
"vae": False,
|
||||
},
|
||||
},
|
||||
"pipeline": {
|
||||
"preset_overrides": {
|
||||
"refine": {
|
||||
"enabled": True,
|
||||
"num_inference_steps": 2,
|
||||
"guidance_scale": 1.0,
|
||||
"add_noise": True,
|
||||
},
|
||||
},
|
||||
"components": {"upsampler_weights": str(refine_upsampler_path)},
|
||||
"model": {"ltx2": {"refine": {"lora_path": ""}}}, # keep refine LoRA disabled for distilled model
|
||||
"experimental": {
|
||||
"pipeline_config": pipeline_config,
|
||||
},
|
||||
"vae_tiling": False,
|
||||
},
|
||||
})
|
||||
|
||||
run_times: list[float] = []
|
||||
e2e_times: list[float] = []
|
||||
@@ -247,25 +261,26 @@ def main() -> None:
|
||||
torch.cuda.synchronize()
|
||||
|
||||
start = time.perf_counter()
|
||||
result = generator.generate_video(
|
||||
prompt=prompt,
|
||||
output_path=str(output_path),
|
||||
fps=24,
|
||||
seed=10,
|
||||
save_video=True,
|
||||
guidance_scale=1.0,
|
||||
height=benchmark_entry.get("height", 1088),
|
||||
width=benchmark_entry.get("width", 1920),
|
||||
num_frames=121,
|
||||
num_inference_steps=5,
|
||||
# image_path="examples/inference/basic/prompt1.png",
|
||||
# ltx2_image_crf=0.0
|
||||
)
|
||||
result = generator.generate({
|
||||
"prompt": prompt,
|
||||
"sampling": {
|
||||
"fps": 24,
|
||||
"seed": 10,
|
||||
"guidance_scale": 1.0,
|
||||
"height": benchmark_entry.get("height", 1088),
|
||||
"width": benchmark_entry.get("width", 1920),
|
||||
"num_frames": 121,
|
||||
"num_inference_steps": 5,
|
||||
},
|
||||
"output": {"output_path": str(output_path), "save_video": True},
|
||||
# "inputs": {"image_path": "examples/inference/basic/prompt1.png"},
|
||||
# "extensions": {"ltx2_image_crf": 0.0},
|
||||
})
|
||||
if os.environ.get("FASTVIDEO_STAGE_LOGGING") == "0":
|
||||
torch.cuda.synchronize()
|
||||
|
||||
elapsed = result.get("generation_time") if isinstance(result, dict) else None
|
||||
e2e_elapsed = result.get("e2e_latency") if isinstance(result, dict) else None
|
||||
elapsed = result.generation_time if isinstance(result, GenerationResult) else None
|
||||
e2e_elapsed = result.extra.get("e2e_latency") if isinstance(result, GenerationResult) else None
|
||||
if elapsed is None:
|
||||
elapsed = time.perf_counter() - start
|
||||
if e2e_elapsed is None:
|
||||
@@ -276,7 +291,7 @@ def main() -> None:
|
||||
print(f"[{i + 1}/{num_runs}] Generation time: {elapsed:.2f}s")
|
||||
print(f"[{i + 1}/{num_runs}] End-to-end latency: {e2e_elapsed:.2f}s")
|
||||
|
||||
if isinstance(result, dict):
|
||||
if isinstance(result, GenerationResult):
|
||||
stage_sum = print_stage_breakdown(result, i + 1, num_runs)
|
||||
if stage_sum is not None:
|
||||
non_stage_overhead = e2e_elapsed - stage_sum
|
||||
|
||||
@@ -6,12 +6,18 @@ OUTPUT_PATH = "video_samples_lucy_edit"
|
||||
def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
"decart-ai/Lucy-Edit-Dev",
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=True,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": True,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
prompt = ("Change the apron and blouse to a classic clown costume: satin "
|
||||
@@ -20,18 +26,19 @@ def main():
|
||||
"foam nose; soft window light from left, eye-level medium shot.")
|
||||
video_path = "https://d2drjpuinn46lb.cloudfront.net/painter_original_edit.mp4"
|
||||
|
||||
generator.generate_video(
|
||||
prompt,
|
||||
negative_prompt="",
|
||||
video_path=video_path,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
height=480,
|
||||
width=832,
|
||||
num_frames=81,
|
||||
fps=24,
|
||||
guidance_scale=5.0,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": prompt,
|
||||
"negative_prompt": "",
|
||||
"inputs": {"video_path": video_path},
|
||||
"sampling": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 81,
|
||||
"fps": 24,
|
||||
"guidance_scale": 5.0,
|
||||
},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -51,33 +51,42 @@ def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
config["model_path"],
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=True, # DiT need to be offloaded for MoE
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
pin_cpu_memory=True,
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": True, # DiT need to be offloaded for MoE
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
"pin_cpu_memory": True,
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
num_frames = 597
|
||||
actions = create_action_presets(num_frames, keyboard_dim=config["keyboard_dim"])
|
||||
grid_sizes = torch.tensor([150, 44, 80])
|
||||
|
||||
generator.generate_video(
|
||||
prompt="",
|
||||
image_path=config["image_url"],
|
||||
mouse_cond=actions["mouse"].unsqueeze(0),
|
||||
keyboard_cond=actions["keyboard"].unsqueeze(0),
|
||||
grid_sizes=grid_sizes,
|
||||
num_frames=num_frames,
|
||||
height=352,
|
||||
width=640,
|
||||
num_inference_steps=50,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": "",
|
||||
"inputs": {
|
||||
"image_path": config["image_url"],
|
||||
"mouse_cond": actions["mouse"].unsqueeze(0),
|
||||
"keyboard_cond": actions["keyboard"].unsqueeze(0),
|
||||
"grid_sizes": grid_sizes,
|
||||
},
|
||||
"sampling": {
|
||||
"num_frames": num_frames,
|
||||
"height": 352,
|
||||
"width": 640,
|
||||
"num_inference_steps": 50,
|
||||
},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -58,14 +58,20 @@ async def main():
|
||||
generator = StreamingVideoGenerator.from_pretrained(
|
||||
config["model_path"],
|
||||
# FastVideo will automatically handle distributed setup
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False, # set to True if GPU is out of memory
|
||||
dit_cpu_offload=True, # DiT need to be offloaded for MoE
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
pin_cpu_memory=True,
|
||||
# image_encoder_cpu_offload=False,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False, # set to True if GPU is out of memory
|
||||
"offload": {
|
||||
"dit": True, # DiT need to be offloaded for MoE
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
# Set pin_cpu_memory to false if CPU RAM is limited and there're no frequent CPU-GPU transfer
|
||||
"pin_cpu_memory": True,
|
||||
# "image_encoder": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
max_blocks = 50
|
||||
|
||||
@@ -9,26 +9,33 @@ OUTPUT_PATH = "video_samples_matrixgame3"
|
||||
def main():
|
||||
generator = VideoGenerator.from_pretrained(
|
||||
MODEL_PATH,
|
||||
num_gpus=1,
|
||||
use_fsdp_inference=False,
|
||||
dit_cpu_offload=False,
|
||||
vae_cpu_offload=False,
|
||||
text_encoder_cpu_offload=True,
|
||||
pin_cpu_memory=True,
|
||||
{
|
||||
"engine": {
|
||||
"num_gpus": 1,
|
||||
"use_fsdp_inference": False,
|
||||
"offload": {
|
||||
"dit": False,
|
||||
"vae": False,
|
||||
"text_encoder": True,
|
||||
"pin_cpu_memory": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
generator.generate_video(
|
||||
prompt=PROMPT,
|
||||
image_path=IMAGE_URL,
|
||||
height=720,
|
||||
width=1280,
|
||||
num_frames=57,
|
||||
num_inference_steps=3,
|
||||
guidance_scale=1.0,
|
||||
seed=42,
|
||||
output_path=OUTPUT_PATH,
|
||||
save_video=True,
|
||||
)
|
||||
generator.generate({
|
||||
"prompt": PROMPT,
|
||||
"inputs": {"image_path": IMAGE_URL},
|
||||
"sampling": {
|
||||
"height": 720,
|
||||
"width": 1280,
|
||||
"num_frames": 57,
|
||||
"num_inference_steps": 3,
|
||||
"guidance_scale": 1.0,
|
||||
"seed": 42,
|
||||
},
|
||||
"output": {"output_path": OUTPUT_PATH, "save_video": True},
|
||||
})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -16,7 +16,6 @@ from fastvideo.api import (
|
||||
OffloadConfig,
|
||||
OutputConfig,
|
||||
ParallelismConfig,
|
||||
PipelineSelection,
|
||||
SamplingConfig,
|
||||
)
|
||||
|
||||
@@ -82,17 +81,10 @@ def main() -> None:
|
||||
output_dir = Path(args.output)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Boot-time run configuration folded into FastVideoArgs (the same
|
||||
# experimental-dict route basic_fasth3.py uses for the VSA knobs).
|
||||
experimental: dict[str, object] = {}
|
||||
if args.inference_torch_compile:
|
||||
experimental["inference_torch_compile"] = True
|
||||
|
||||
execution_backend = args.execution_backend or ("ray" if os.environ.get("RAY_ADDRESS") else "mp")
|
||||
generator = VideoGenerator.from_config(
|
||||
GeneratorConfig(
|
||||
model_path=args.model_path,
|
||||
pipeline=PipelineSelection(experimental=experimental),
|
||||
engine=EngineConfig(
|
||||
num_gpus=args.num_gpus,
|
||||
execution_backend=execution_backend,
|
||||
@@ -110,6 +102,7 @@ def main() -> None:
|
||||
enabled=args.torch_compile,
|
||||
mode=args.compile_mode,
|
||||
vae_enabled=args.compile_vae,
|
||||
regional=True if args.inference_torch_compile else None,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user