Files
hao-ai-lab-FastVideo/fastvideo/tests/train/utils/test_moduleloader_attention_backend.py
Zhang PeiyuanandClaude Opus 5.5 c520878fd2 [perf]: QAT/QAD add safe regional compile for modular training (#1718)
Adds opt-in regional torch.compile to the YAML-driven modular Wan trainer (training.model.enable_torch_compile / torch_compile_kwargs) with FA4 and Wan modulation forward/backward as opaque custom ops, compiled repeated DiT blocks after FSDP setup, and activation checkpointing applied before FSDP for trainable Wan transformers.

Merged with main's #1716 (WanModel resolves the checkpointing type in __init__; the PR's pre-FSDP wrap uses it and the post-load wrap is gone) and #1757 (pre_fsdp_model_transform runs before this PR's pre_fsdp_transform). Maintainer review (Swarm order #776): AdaLN skip names are canonicalized through checkpoint-wrapper prefixes, test_inference_regional_compile.py joins the unit lane, the Wan modulation custom-op test moved to the transformer lane, regional_compile is classified in the schema inventory, and a 1/2-GPU training smoke covers fully_shard(CheckpointWrapper) in eager and regional-compile modes. Validated on GB200: the FA4 CuTe forward/backward parity and fullgraph-backward tests and all four smoke cases pass.

Also exempts MMAudio recipes, whose student builds its pipeline config at init, from the fine-tuning recipe construction check that #1648 left failing on main.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 05:24:24 -07:00

168 lines
5.5 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Verify construction-scoped attention backends in the training loader."""
from __future__ import annotations
import torch
import pytest
from fastvideo.attention.selector import _active_component_attention_backend_scope
from fastvideo.configs.pipelines.base import PipelineConfig
from fastvideo.platforms import AttentionBackendEnum
from fastvideo.train.utils import moduleloader
from fastvideo.train.utils.training_config import (
DistributedConfig,
ModelTrainingConfig,
TrainingConfig,
)
def test_load_transformer_scopes_attention_backend(monkeypatch, tmp_path) -> None:
"""Apply one backend while accepting trailing modular-manifest metadata."""
training_config = TrainingConfig(
distributed=DistributedConfig(hsdp_shard_dim=1),
pipeline_config=PipelineConfig(),
)
captured: list[tuple[AttentionBackendEnum | None, str | None]] = []
monkeypatch.setattr(moduleloader, "maybe_download_model", lambda path: str(tmp_path))
monkeypatch.setattr(
moduleloader,
"verify_model_config_and_directory",
lambda path: {"transformer": ("diffusers", "FakeTransformer", {
"subfolder": "transformer"
})},
)
def _fake_load_module(**kwargs):
del kwargs
scope = _active_component_attention_backend_scope()
captured.append((scope.backend, scope.component) if scope else (None, None))
return torch.nn.Linear(1, 1)
monkeypatch.setattr(
moduleloader.PipelineComponentLoader,
"load_module",
_fake_load_module,
)
result = moduleloader.load_module_from_path(
model_path="fake/model",
module_type="transformer",
training_config=training_config,
attention_backend="ATTN_QAT_TRAIN",
)
assert isinstance(result, torch.nn.Module)
assert captured == [(AttentionBackendEnum.ATTN_QAT_TRAIN, "transformer")]
assert _active_component_attention_backend_scope() is None
def test_load_transformer_restores_backend_when_loading_fails(
monkeypatch,
tmp_path,
) -> None:
training_config = TrainingConfig(
distributed=DistributedConfig(hsdp_shard_dim=1),
pipeline_config=PipelineConfig(),
)
monkeypatch.setattr(moduleloader, "maybe_download_model", lambda path: str(tmp_path))
monkeypatch.setattr(
moduleloader,
"verify_model_config_and_directory",
lambda path: {"transformer": ("diffusers", "FakeTransformer")},
)
def _raise_during_load(**kwargs):
del kwargs
scope = _active_component_attention_backend_scope()
assert scope is not None and scope.backend is AttentionBackendEnum.ATTN_QAT_TRAIN
raise RuntimeError("load failed")
monkeypatch.setattr(
moduleloader.PipelineComponentLoader,
"load_module",
_raise_during_load,
)
with pytest.raises(RuntimeError, match="load failed"):
moduleloader.load_module_from_path(
model_path="fake/model",
module_type="transformer",
training_config=training_config,
attention_backend="ATTN_QAT_TRAIN",
)
assert _active_component_attention_backend_scope() is None
def test_training_args_propagate_compile_settings() -> None:
training_config = TrainingConfig(
distributed=DistributedConfig(hsdp_shard_dim=1),
model=ModelTrainingConfig(
enable_torch_compile=True,
torch_compile_kwargs={"dynamic": False},
),
pipeline_config=PipelineConfig(),
)
args = moduleloader._make_training_args(training_config, model_path="fake/model")
assert args.enable_torch_compile is True
assert args.torch_compile_kwargs == {"dynamic": False}
def test_load_transformer_forwards_pre_fsdp_transform(monkeypatch, tmp_path) -> None:
training_config = TrainingConfig(
distributed=DistributedConfig(hsdp_shard_dim=1),
pipeline_config=PipelineConfig(),
)
def transform(module):
return module
captured = None
monkeypatch.setattr(moduleloader, "maybe_download_model", lambda path: str(tmp_path))
monkeypatch.setattr(
moduleloader,
"verify_model_config_and_directory",
lambda path: {"transformer": ("diffusers", "FakeTransformer")},
)
def _fake_load_module(**kwargs):
nonlocal captured
captured = getattr(kwargs["fastvideo_args"], "_pre_fsdp_transform", None)
return torch.nn.Linear(1, 1)
monkeypatch.setattr(moduleloader.PipelineComponentLoader, "load_module", _fake_load_module)
moduleloader.load_module_from_path(
model_path="fake/model",
module_type="transformer",
training_config=training_config,
pre_fsdp_transform=transform,
)
assert captured is transform
def test_pre_fsdp_transform_rejects_non_transformer(monkeypatch, tmp_path) -> None:
training_config = TrainingConfig(
distributed=DistributedConfig(hsdp_shard_dim=1),
pipeline_config=PipelineConfig(),
)
monkeypatch.setattr(moduleloader, "maybe_download_model", lambda path: str(tmp_path))
monkeypatch.setattr(
moduleloader,
"verify_model_config_and_directory",
lambda path: {"vae": ("diffusers", "FakeVAE")},
)
with pytest.raises(ValueError, match="only be set when loading a transformer"):
moduleloader.load_module_from_path(
model_path="fake/model",
module_type="vae",
training_config=training_config,
pre_fsdp_transform=lambda module: module,
)