Files
hao-ai-lab-FastVideo/fastvideo/api/training_schema.py
T
Davids048andClaude Opus 5.5 6c776a219b [refactor]: run the inference path on the resolved config
ResolvedGeneratorConfig becomes the runtime config of every mode. Resolution
builds the model's PipelineConfig once, freezes it, and attaches it as
resolved_config.pipeline_config; it also exposes mode, training_mode, and
inference_mode. VideoGenerator, fastvideo serve, the OpenAI server state,
the executors, the worker, and ComposedPipelineBase receive the resolved
config instead of a FastVideoArgs, and generator_config_to_fastvideo_args
is no longer on the inference path.

- Schema: ExecutionMode and WorkloadType move to fastvideo/api/schema.py;
  typed homes for mode, output_type, boundary_ratio, master_port,
  moba_config, and the LTX-2 refine settings; Enum parsing.
- Resolution: steps for the refine preset overrides, flat experimental
  keys, the V-MoBA config, the validation that check_fastvideo_args did,
  the num_gpus bump, the model's vae_tiling default, and a final
  fill_runtime_defaults step for typed fields no earlier step decides.
- fastvideo/api/training_schema.py: TrainingRunConfig and
  PreprocessRunConfig roots, their resolvers, and a --config plus dotted
  override loader.
- fastvideo/api/device_policy.py: the device offload policy as functions
  that return an overridden config; the worker keeps the result. The LTX-2
  and MiniMax-H3 checkpoint defaults rebind the pipeline's config.
- fastvideo/api/flat_name_fallback.py answers the flat FastVideoArgs names
  on the resolved config until the readers move to typed paths.

Golden changes: the decisions of the steps above are recorded, and
FastVideoArgs.ltx2_vae_tiling holds the model's vae_tiling (it held None;
no runtime code reads it). Against the 3f6893a0 goldens every
FastVideoArgs field, PipelineConfig, and SamplingParam matches, except
disable_autocast and boundary_ratio, which FastVideoArgs held at their
defaults because flattening routed them only to PipelineConfig.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 05:35:14 +00:00

381 lines
15 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Typed roots of the legacy training stack (``fastvideo/training/``) and of the preprocessing entry points.
``TrainingRunConfig`` and ``PreprocessRunConfig`` extend ``GeneratorConfig`` with a ``training`` or a ``preprocess``
section. Both resolve to a ``ResolvedGeneratorConfig`` through the generator resolution steps plus their own, so the
pipelines, loaders, and stages read one runtime config type in every mode.
The field defaults are the effective values of the command line that these runs used: the argparse defaults of the
``TrainingArgs`` and ``FastVideoArgs`` flags, including the engine offload settings, which default to off there
(``CommandLineEngineConfig``). Comma strings of the command line (``betas``, ``validation_sampling_steps``) are typed
tuples and lists here.
``load_resolved_run_config`` is the one loader of the entry points: ``--config <yaml>`` plus dotted overrides such as
``--training.optimizer.learning_rate 1e-5``.
"""
from __future__ import annotations
import argparse
from collections.abc import Callable, Mapping, Sequence
from dataclasses import dataclass, field
from typing import Any, Literal
from fastvideo.api.inference_resolution import generator_resolution_steps, resolve_config
from fastvideo.api.resolution import ResolutionStep, ResolutionView, ResolvedGeneratorConfig
from fastvideo.api.schema import EngineConfig, ExecutionMode, GeneratorConfig, OffloadConfig
from fastvideo.configs.configs import PreprocessConfig
from fastvideo.logger import init_logger
logger = init_logger(__name__)
@dataclass
class TrainingDataOptions:
"""Training data and its loader."""
data_path: str = ""
dataloader_num_workers: int = 0
num_height: int = 0
num_width: int = 0
num_frames: int = 0
train_batch_size: int = 0
num_latent_t: int = 0
training_cfg_rate: float | None = None
"""Probability of dropping the text condition of a sample. ``None`` keeps the dataset default."""
seed: int = 42
train_sp_batch_size: int | None = None
@dataclass
class TrainingOptimizerOptions:
"""Optimizer and learning-rate schedule of the trained transformer."""
learning_rate: float = 0.0
betas: tuple[float, float] = (0.9, 0.999)
weight_decay: float | None = None
lr_scheduler: str = "constant"
lr_warmup_steps: int = 10
lr_num_cycles: int | None = None
lr_power: float | None = None
min_lr_ratio: float = 0.5
"""Minimum learning rate ratio of the ``cosine_with_min_lr`` scheduler."""
max_grad_norm: float | None = None
@dataclass
class TrainingLoopOptions:
max_train_steps: int | None = None
gradient_accumulation_steps: int | None = None
@dataclass
class TrainingCheckpointOptions:
output_dir: str = ""
resume_from_checkpoint: str | None = None
training_state_checkpointing_steps: int | None = None
"""Steps between training-state checkpoints, which resume a run."""
weight_only_checkpointing_steps: int | None = None
"""Steps between weight-only checkpoints, which inference loads."""
checkpoints_total_limit: int | None = None
@dataclass
class TrainingTrackerOptions:
trackers: list[str] = field(default_factory=list)
project_name: str | None = None
run_name: str | None = None
@dataclass
class TrainingValidationOptions:
"""Validation runs during training, and the intermediate-latent visualizations logged with them."""
enabled: bool = False
dataset_file: str | None = None
sampling_steps: list[int] | None = None
"""Denoising step counts of the validation runs; each count runs once."""
guidance_scale: float | None = None
"""Guidance scale of the validation runs. ``None`` keeps the model's sampling default."""
every_steps: int | None = None
log_visualization: bool = False
visualization_steps: int | None = None
@dataclass
class TrainingDistillationOptions:
"""DMD distillation: the teacher (real score) and critic (fake score) models and their updates."""
real_score_model_path: str | None = None
fake_score_model_path: str | None = None
generator_update_interval: int = 5
"""Critic updates per student update."""
min_timestep_ratio: float = 0.2
max_timestep_ratio: float = 0.98
real_score_guidance_scale: float = 3.5
"""Teacher CFG scale in the parameterization ``x_cond + w * (x_cond - x_uncond)``; 3.5 is a standard scale of
4.5."""
fake_score_learning_rate: float = 0.0
"""Learning rate of the critic; 0.0 uses ``optimizer.learning_rate``."""
fake_score_lr_scheduler: str = "constant"
fake_score_betas: tuple[float, float] = (0.9, 0.999)
simulate_generator_forward: bool = False
warp_denoising_step: bool = False
@dataclass
class TrainingEMAOptions:
enabled: bool = False
decay: float = 0.999
start_step: int = 0
@dataclass
class TrainingLoRAOptions:
enabled: bool = False
rank: int | None = None
alpha: int | None = None
"""LoRA alpha. ``None`` uses ``rank``."""
@dataclass
class TrainingVSAOptions:
"""Video sparse attention schedule during training; ``engine.attention.vsa_sparsity`` is the target."""
decay_rate: float = 0.01
decay_interval_steps: int = 1
cache_tile_buf: bool = False
"""Reuse the per-step padded VSA tile buffer across attention layers. Off by default because, under full
activation checkpointing, the cached buffer survives into the backward recompute and raises peak memory."""
@dataclass
class TrainingSelfForcingOptions:
dfake_gen_update_ratio: int = 5
"""Train the generator every this many steps."""
num_frame_per_block: int = 3
independent_first_frame: bool = False
same_step_across_blocks: bool = False
last_step_only: bool = False
context_noise: int = 0
@dataclass
class TrainingModelOptions:
"""Loss weighting and memory settings of the trained transformer."""
weighting_scheme: Literal["sigma_sqrt", "logit_normal", "mode", "cosmap", "uniform"] = "uniform"
logit_mean: float = 0.0
logit_std: float = 1.0
mode_scale: float = 1.29
precondition_outputs: bool = False
enable_gradient_checkpointing_type: Literal["full", "ops", "block_skip"] | None = None
ltx2_first_frame_conditioning_p: float = 0.1
"""Probability of conditioning an LTX-2 sample on its first frame."""
@dataclass
class TrainingOptions:
data: TrainingDataOptions = field(default_factory=TrainingDataOptions)
optimizer: TrainingOptimizerOptions = field(default_factory=TrainingOptimizerOptions)
loop: TrainingLoopOptions = field(default_factory=TrainingLoopOptions)
checkpoint: TrainingCheckpointOptions = field(default_factory=TrainingCheckpointOptions)
tracker: TrainingTrackerOptions = field(default_factory=TrainingTrackerOptions)
validation: TrainingValidationOptions = field(default_factory=TrainingValidationOptions)
distillation: TrainingDistillationOptions = field(default_factory=TrainingDistillationOptions)
ema: TrainingEMAOptions = field(default_factory=TrainingEMAOptions)
lora: TrainingLoRAOptions = field(default_factory=TrainingLoRAOptions)
vsa: TrainingVSAOptions = field(default_factory=TrainingVSAOptions)
self_forcing: TrainingSelfForcingOptions = field(default_factory=TrainingSelfForcingOptions)
model: TrainingModelOptions = field(default_factory=TrainingModelOptions)
@dataclass
class CommandLineOffloadConfig(OffloadConfig):
"""Offload settings with the defaults of the ``FastVideoArgs`` command line: every offload and pinned host memory
off."""
dit: bool = False
dit_layerwise: bool = False
text_encoder: bool = False
image_encoder: bool = False
vae: bool = False
pin_cpu_memory: bool = False
@dataclass
class CommandLineEngineConfig(EngineConfig):
"""Engine settings with the offload defaults of the ``FastVideoArgs`` command line."""
offload: CommandLineOffloadConfig = field(default_factory=CommandLineOffloadConfig)
@dataclass
class TrainingRunConfig(GeneratorConfig):
"""Root config of a ``fastvideo/training/`` run. ``model_path`` is the pretrained model that training starts
from."""
mode: ExecutionMode = ExecutionMode.FINETUNING
engine: CommandLineEngineConfig = field(default_factory=CommandLineEngineConfig)
training: TrainingOptions = field(default_factory=TrainingOptions)
@dataclass
class PreprocessRunConfig(GeneratorConfig):
"""Root config of a preprocessing run, which encodes a dataset into training inputs."""
mode: ExecutionMode = ExecutionMode.PREPROCESS
engine: CommandLineEngineConfig = field(default_factory=CommandLineEngineConfig)
preprocess: PreprocessConfig = field(default_factory=PreprocessConfig)
def resolve_training_offload_conflicts(view: ResolutionView) -> dict[str, Any]:
"""Settle offload modes that cannot run together in training, before any device is bound.
On MPS, FSDP inference and layerwise offload turn off. With layerwise offload on, FSDP inference and DiT CPU
offload turn off.
"""
from fastvideo.platforms import current_platform
values: dict[str, Any] = {}
if current_platform.is_mps():
values.update({"engine.use_fsdp_inference": False, "engine.offload.dit_layerwise": False})
if values.get("engine.offload.dit_layerwise", view.get("engine.offload.dit_layerwise")):
if view.get("engine.use_fsdp_inference"):
logger.warning("dit_layerwise_offload is enabled, automatically disabling use_fsdp_inference.")
values["engine.use_fsdp_inference"] = False
if view.get("engine.offload.dit"):
logger.warning("dit_layerwise_offload is enabled, automatically disabling dit_cpu_offload.")
values["engine.offload.dit"] = False
return values
def validate_training_parallel_sizes(view: ResolutionView) -> dict[str, Any]:
"""Training needs ``hsdp_replicate_dim``, ``hsdp_shard_dim``, and ``sp_size`` set; -1 is not allowed."""
for name in ("hsdp_replicate_dim", "hsdp_shard_dim", "sp_size"):
if view.get(f"engine.parallelism.{name}") == -1:
raise ValueError(f"{name} must be set for training")
return {}
def derive_lora_alpha_from_rank(view: ResolutionView) -> dict[str, Any]:
"""``training.lora.alpha`` takes ``training.lora.rank`` while it is unset."""
rank = view.get("training.lora.rank")
if view.get("training.lora.alpha") is not None or rank is None:
return {}
return {"training.lora.alpha": rank}
def fill_preprocess_model_path(view: ResolutionView) -> dict[str, Any]:
"""``preprocess.model_path`` takes the top-level ``model_path`` while it is empty."""
if view.get("preprocess.model_path"):
return {}
return {"preprocess.model_path": view.get("model_path")}
def validate_preprocess_options(view: ResolutionView) -> dict[str, Any]:
"""A dataset preprocessing run needs ``preprocess.dataset_path`` and positive output sizes.
The per-task pipelines, which set ``preprocess.data_merge_path`` instead, are not checked.
"""
if view.get("preprocess.data_merge_path"):
return {}
if view.get("preprocess.dataset_path") == "":
raise ValueError("dataset_path must be set for preprocess mode")
if view.get("preprocess.samples_per_file") <= 0:
raise ValueError("samples_per_file must be greater than 0")
if view.get("preprocess.flush_frequency") <= 0:
raise ValueError("flush_frequency must be greater than 0")
return {}
def training_resolution_steps(config: GeneratorConfig, defaults: Any = None) -> tuple[ResolutionStep, ...]:
"""The resolution steps of a ``TrainingRunConfig``, in the order that they run.
The offload conflicts and the parallel-size check run on the placeholders, before ``derive_parallel_sizes``
replaces them.
"""
return generator_resolution_steps(config,
defaults,
before_placeholders=(resolve_training_offload_conflicts,
validate_training_parallel_sizes),
after=(derive_lora_alpha_from_rank, ))
def preprocess_resolution_steps(config: GeneratorConfig, defaults: Any = None) -> tuple[ResolutionStep, ...]:
"""The resolution steps of a ``PreprocessRunConfig``, in the order that they run."""
return generator_resolution_steps(config, defaults, after=(fill_preprocess_model_path, validate_preprocess_options))
def resolve_training_config(config: TrainingRunConfig | Mapping[str, Any]) -> ResolvedGeneratorConfig:
"""Resolve a ``TrainingRunConfig`` and freeze the result, with its ``PipelineConfig`` as ``pipeline_config``."""
return resolve_config(config, TrainingRunConfig, training_resolution_steps)
def resolve_preprocess_config(config: PreprocessRunConfig | Mapping[str, Any]) -> ResolvedGeneratorConfig:
"""Resolve a ``PreprocessRunConfig`` and freeze the result, with its ``PipelineConfig`` as ``pipeline_config``."""
return resolve_config(config, PreprocessRunConfig, preprocess_resolution_steps)
_RESOLVERS: dict[type[GeneratorConfig], Callable[[Any], ResolvedGeneratorConfig]] = {
TrainingRunConfig: resolve_training_config,
PreprocessRunConfig: resolve_preprocess_config,
}
def load_resolved_run_config(
config_class: type[TrainingRunConfig] | type[PreprocessRunConfig],
argv: Sequence[str] | None = None,
*,
mode: ExecutionMode | None = None,
) -> ResolvedGeneratorConfig:
"""Load ``--config <yaml>`` and its dotted overrides from ``argv`` and resolve them as ``config_class``.
``argv`` defaults to ``sys.argv[1:]``. Every token after the known ``--config`` option must be a dotted override
(``--engine.num_gpus 2`` or ``--engine.num_gpus=2``). ``mode`` is the entry point's mode; the file can set
``mode`` itself.
"""
from fastvideo.api.overrides import apply_overrides, parse_cli_overrides
from fastvideo.api.parser import load_raw_config
parser = argparse.ArgumentParser(description=f"Run with a {config_class.__name__} YAML file")
parser.add_argument("--config", required=True, help="Path to the YAML or JSON config file.")
args, override_tokens = parser.parse_known_args(argv)
raw = load_raw_config(args.config)
if mode is not None:
raw.setdefault("mode", mode.value)
overrides = parse_cli_overrides(list(override_tokens))
if overrides:
raw = apply_overrides(raw, overrides)
return _RESOLVERS[config_class](raw)
__all__ = [
"PreprocessRunConfig",
"TrainingCheckpointOptions",
"TrainingDataOptions",
"TrainingDistillationOptions",
"TrainingEMAOptions",
"TrainingLoRAOptions",
"TrainingLoopOptions",
"TrainingModelOptions",
"TrainingOptimizerOptions",
"TrainingOptions",
"TrainingRunConfig",
"TrainingSelfForcingOptions",
"TrainingTrackerOptions",
"TrainingVSAOptions",
"TrainingValidationOptions",
"CommandLineEngineConfig",
"CommandLineOffloadConfig",
"derive_lora_alpha_from_rank",
"fill_preprocess_model_path",
"load_resolved_run_config",
"preprocess_resolution_steps",
"resolve_preprocess_config",
"resolve_training_config",
"resolve_training_offload_conflicts",
"training_resolution_steps",
"validate_preprocess_options",
"validate_training_parallel_sizes",
]