Skip to content

[ci] add per-method single-step training tests for fastvideo.train - #1343

Merged
SolitaryThinker merged 2 commits into
hao-ai-lab:mainfrom
FoundationResearch:feature/train/citest/phase2-5a-i
May 26, 2026
Merged

[ci] add per-method single-step training tests for fastvideo.train#1343
SolitaryThinker merged 2 commits into
hao-ai-lab:mainfrom
FoundationResearch:feature/train/citest/phase2-5a-i

Conversation

@alexzms

@alexzms alexzms commented May 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Phase 2 / PR 5/9 of the fastvideo.train CI plan, split into a 5a-i slice that establishes the per-method test pattern. A follow-up 5a-ii will layer a device-keyed grad-norm regression on top of the same harness.

Adds two GPU smoke tests under a new fastvideo/tests/train/methods/ directory:

  • test_wan_finetune.pyWanModel + FineTuneMethod
  • test_wan_causal_dfsft.pyWanCausalModel + DiffusionForcingSFTMethod

Both tests build the method via its public constructor, feed a tiny synthetic raw_batch (text embed + mask + vae latent), and run one end-to-end step:

method.on_train_start()
loss_map, outputs, _ = method.single_train_step(batch, 0)
method.backward(loss_map, outputs, grad_accum_rounds=1)

The harness deliberately avoids the full Trainer (no FSDP wrap, no data loader, no checkpointing) so the tests stay focused on the method-level wiring that the model-loading tests in tests/train/models don't already cover.

Asserts

  • loss_map["total_loss"] is finite.
  • model.transformer.blocks[0] has trainable params with finite, non-zero gradients.

The first transformer block's gradient is computed last during backprop, so a healthy grad there implies the full forward + chain-rule path is intact. Keeping the assertion surface to a single block keeps the reference data tiny for the follow-up regression PR.

CI plumbing

  • fastvideo/tests/modal/pr_test.py:
    • run_train_framework_tests pytest path now includes ./fastvideo/tests/train/methods.
    • run_unit_test adds a matching --ignore for the new dir.
  • docs/contributing/testing.md: mention the new methods/ subdirectory in the Train Framework Tests entry.

Fixtures

Two new YAMLs under fastvideo/tests/train/fixtures/, mirroring the existing 4/9 fixtures but with trainable: true and the minimum training.distributed / training.optimizer / training.loop keys needed by the method's __init__ and on_train_start:

  • wan_t2v_finetune_min.yaml
  • wan_causal_t2v_dfsft_min.yaml (with chunk_size: 3, num_latent_t: 6)

Test plan

  • python -m pytest --collect-only fastvideo/tests/train/methods/ collects both tests cleanly.
  • pre-commit run on changed files (codespell + PyMarkdown checked; yapf/ruff/mypy are intentionally excluded under fastvideo/tests/).
  • /test train-framework — first full GPU CI run.

Plan context

Followup PRs will incrementally fill the (model, method) matrix:

Slice Scope
5a-i (this PR) Per-method test framework + 2 reference tests (Wan/finetune, WanCausal/dfsft)
5a-ii Layer-0 grad-norm regression on top, ref JSON in HF dataset
5b Complex methods: dmd2, self_forcing, kd (needs tiny teacher/fake-score fixtures)
5c Cross-coverage: Hunyuan + finetune, more (model, method) combos

@mergify mergify Bot added type: ci CI/CD infrastructure scope: infra CI, tests, Docker, build scope: docs Documentation labels May 13, 2026
@mergify

mergify Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🔴 PR merge requirements

Waiting for

  • #approved-reviews-by>=1
  • check-success=full-suite-passed
This rule is failing.
  • #approved-reviews-by>=1
  • check-success=full-suite-passed
  • check-success=fastcheck-passed
  • check-success~=pre-commit
  • title~=(?i)^\[(feat|feature|bugfix|fix|refactor|perf|ci|doc|docs|misc|chore|kernel|new.?model|skill|skills|infra)\]

@alexzms alexzms changed the title [ci] add per-method single-step training tests for fastvideo.train (PR 5a-i/9) [ci] add per-method single-step training tests for fastvideo.train May 13, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces per-method GPU smoke tests for the training framework, specifically targeting the WanModel and WanCausalModel with FineTuneMethod and DiffusionForcingSFTMethod respectively. The changes include new test fixtures for minimal training configurations, dedicated test scripts that verify loss finiteness and gradient propagation, and updates to the Modal CI configuration and documentation to include these new tests in the training framework suite. I have no feedback to provide as there were no review comments.

@alexzms alexzms added ready PR is ready to merge labels May 13, 2026
@alexzms
alexzms force-pushed the feature/train/citest/phase2-5a-i branch from 9fdb29f to 2767c85 Compare May 18, 2026 20:03
…R 5a-i/9)

Phase 2 / PR 5/9 of the fastvideo.train CI plan, split into a
first-half ``5a-i`` slice that establishes the per-method test
pattern.  A follow-up ``5a-ii`` will layer a device-keyed
grad-norm regression on top of the same harness.

Adds two GPU smoke tests under a new
``fastvideo/tests/train/methods/`` directory:

* ``test_wan_finetune.py`` — ``WanModel`` + ``FineTuneMethod``
* ``test_wan_causal_dfsft.py`` — ``WanCausalModel`` +
  ``DiffusionForcingSFTMethod``

Both tests construct the method via its public constructor
(``method = FineTuneMethod(cfg=cfg.method, role_models=...)``),
build a tiny synthetic ``raw_batch`` (text embed + mask + vae
latent), and run one end-to-end step:

    method.on_train_start()
    loss_map, outputs, _ = method.single_train_step(batch, 0)
    method.backward(loss_map, outputs, grad_accum_rounds=1)

Asserts that ``loss_map["total_loss"]`` is finite and that the
first transformer block (``model.transformer.blocks[0]``) has
trainable parameters with finite, non-zero gradients.  The first
block's grad is computed *last* during backprop, so a healthy
grad there implies the full forward + chain-rule path is intact —
keeping the assertion surface to a single block keeps the
reference data tiny for the follow-up regression PR.

The harness deliberately avoids the full ``Trainer`` (no FSDP
wrap, no data loader, no checkpointing) so the tests stay focused
on the method-level wiring that the model-loading tests in
``tests/train/models`` don't cover.

CI plumbing:

* ``fastvideo/tests/modal/pr_test.py``: add
  ``./fastvideo/tests/train/methods`` to the
  ``run_train_framework_tests`` pytest path, and add a matching
  ``--ignore`` entry in ``run_unit_test`` so the CPU runner doesn't
  pick it up.
* ``docs/contributing/testing.md``: mention the new ``methods/``
  subdirectory in the Train Framework Tests entry.

Two new fixture YAMLs under ``fastvideo/tests/train/fixtures/``
mirror the structure of the existing 4/9 fixtures but flip
``trainable`` to ``true`` and include the minimum
``training.distributed`` + ``training.optimizer`` + ``training.loop``
keys needed by the method's ``__init__`` and ``on_train_start``.
@alexzms
alexzms force-pushed the feature/train/citest/phase2-5a-i branch from 2767c85 to 8dfc13a Compare May 18, 2026 20:39
@SolitaryThinker

Copy link
Copy Markdown
Collaborator

Hi @alexzms — this is a code review from one of @SolitaryThinker's AI reviewer agents (Gob). I run these to help triage PRs but @SolitaryThinker hasn't personally verified every finding — please ping @SolitaryThinker if anything below is off.

TL;DR

Per-method test template is clean (public constructors, synthetic raw_batch, layer-0 grad assertion) and the Modal/pr_test.py wiring is symmetric. The blocker: both fixtures omit training.data.data_path, which init_preprocessors (called inside FineTuneMethod.__init__ and DiffusionForcingSFTMethod.__init__) needs to build the parquet dataloader — so both tests will raise FileNotFoundError at construction on Modal before single_train_step ever runs. The PR's own test plan flags this with the unchecked /test train-framework gate.

Verdict: ship-with-fixes

  • S0 (blockers): 0
  • S1 (must-fix): 1
  • S2 (should-fix; persistent or important): 2
  • S3 (discussion): not shown here; see review.md

Findings (must-fix + persistent should-fix only)

[S1] Both new tests will fail at construction because training.data.data_path is empty

What: FineTuneMethod.__init__ and DiffusionForcingSFTMethod.__init__ unconditionally call self.student.init_preprocessors(self.training_config) (fastvideo/train/methods/fine_tuning/finetune.py:36, fastvideo/train/methods/fine_tuning/dfsft.py:41). For Wan models, that builds a parquet dataloader (fastvideo/train/models/wan/wan.py:170build_parquet_t2v_train_dataloaderLatentsParquetMapStyleDataset(path=data_config.data_path, ...)). Neither new fixture sets training.data.data_path, and DataConfig.data_path defaults to "" (fastvideo/train/utils/training_config.py:25). The dataset then calls get_parquet_files_and_length(""), which os.walks the process CWD looking for *.parquet and raises FileNotFoundError("No parquet files found under dataset path: ...") when none exist (fastvideo/dataset/parquet_dataset_map_style.py:163-168).

Why it matters: On the Modal run_train_framework_tests job (which mounts hf-model-weights, not a parquet dataset), both test_wan_finetune_single_train_step and test_wan_causal_dfsft_single_train_step will raise at FineTuneMethod(cfg=..., role_models=...) / DiffusionForcingSFTMethod(cfg=..., role_models=...) — before on_train_start(), before single_train_step(), before the grad-norm assertion this PR exists to land. The local pytest --collect-only you ran in the test plan doesn't run setup, so it does not exercise this path. Locally, if a developer happens to have parquet files anywhere under CWD, the test silently passes against unrelated data — which is strictly worse than failing.

Suggested fix (pick one):

  1. (Preferred) Check in a tiny parquet stub under fastvideo/tests/train/fixtures/ and point training.data.data_path at it.
  2. Make WanModel.init_preprocessors tolerate an empty data_path (skip dataloader build; still load VAE + init timestep mechanics). This is a one-line surface change and probably deserves a separate co-shipped PR.
  3. Monkeypatch WanModel.init_preprocessors in the two new tests to set self.vae, self.world_group, self.sp_group, and call self._init_timestep_mechanics() without touching the dataloader.

Evidence: fastvideo/tests/train/fixtures/wan_t2v_finetune_min.yaml (no training.data.data_path) · fastvideo/tests/train/fixtures/wan_causal_t2v_dfsft_min.yaml (no training.data.data_path) · fastvideo/train/methods/fine_tuning/finetune.py:36 · fastvideo/train/methods/fine_tuning/dfsft.py:41 · fastvideo/train/models/wan/wan.py:150-175 · fastvideo/train/utils/dataloader.py:12-33 · fastvideo/dataset/parquet_dataset_map_style.py:163-168, 269-303 · fastvideo/train/utils/training_config.py:25.


[S2] Test plan's only end-to-end gate is unchecked

What: PR body has [ ] /test train-framework — first full GPU CI run unchecked. The two boxes that are checked (--collect-only and pre-commit) don't exercise the init_preprocessors → dataloader path that S1 hits.

Why it matters: This is the persistent CI-PR pattern of claiming verification when the failing gate is the one not run. Project convention asks CI-wiring PRs to run the GPU gate they wire.

Suggested fix: After resolving S1, run /test train-framework and paste the green log / Modal run link into the PR body.

Evidence: PR body (gh pr view 1343 --json body).


[S2] text_attention_mask dtype drift vs production

What: Both tests build the synthetic mask as bfloat16. Production (fastvideo/dataset/utils.py:19, 21) emits the mask via torch.ones(L) / torch.zeros(L) — float32. WanModel.prepare_batch casts to training dtype either way (wan.py:246), so this is functionally equivalent on the current path.

Why it matters: Cheap-to-fix drift between test and prod data. If a future change keys behavior off the mask dtype (e.g. mask.bool(), -inf * mask additive construction), production would diverge from these tests silently.

Suggested fix: Drop dtype=dtype on the mask:

"text_attention_mask": torch.ones(batch_size, 16, device=device),  # float32, matches dataloader

in both test_wan_finetune.py:323-324 and test_wan_causal_dfsft.py:201-202.

Evidence: fastvideo/tests/train/methods/test_wan_finetune.py:323-324 · fastvideo/tests/train/methods/test_wan_causal_dfsft.py:201-202 · fastvideo/dataset/utils.py:8-21.


Reviews above are from @SolitaryThinker's agent Gob (an AI reviewer). Ping @SolitaryThinker if any finding is off, contradicts your intent, or applies to a stale rebase. Full review (including S3 / discussion items) is archived locally and available on request.

Both fixtures intentionally omit training.data.data_path, so
FineTuneMethod/DiffusionForcingSFTMethod construction hit a
FileNotFoundError in init_preprocessors before the train step ran.
The tests feed a synthetic raw_batch, so the dataloader is unused —
stub build_parquet_t2v_train_dataloader in each test. Also align the
synthetic text_attention_mask dtype (float32) with the production
dataloader.
@alexzms

alexzms commented May 25, 2026

Copy link
Copy Markdown
Collaborator Author

/test train-framework

@SolitaryThinker

Copy link
Copy Markdown
Collaborator

Hi @alexzms — automated review from Gob, one of @SolitaryThinker's AI reviewers. Findings aren't all human-verified; ping @SolitaryThinker if anything looks off.

TL;DR

Differential re-review at c3bbb381a4. The address-commit takes option 3 (monkeypatch build_parquet_t2v_train_dataloader in each test) and also lands the F3 mask-dtype fix. buildkite/ci/test-tube-train-framework-tests (build #3258) is green at this SHA — empirically validates F1 + F2. All three actionable prior findings are addressed.

Verdict: approve

  • S1 (must-fix): 0
  • S2 (should-fix): 0
  • S3 new: 0

Prior findings status at c3bbb38

Prior finding Sev Status at c3bbb38 Note
F1: Both new tests fail at construction (empty training.data.data_path) S1 Option 3 (monkeypatch). Both tests now monkeypatch fastvideo.train.utils.dataloader.build_parquet_t2v_train_dataloader → lambda: None before the WanModel(...) / WanCausalModel(...) and method-constructor calls. Empirically validated: test-tube-train-framework-tests (build #3258) PASSED in 7m30s.
F2: Test plan's only end-to-end gate unchecked S2 /test train-framework ran on 2026-05-25 → Buildkite build #3258 green at c3bbb381a4.
F3: text_attention_mask dtype drift vs production S2 Both files now use torch.ones(batch_size, 16, device=device) (no dtype=), with an explanatory comment citing fastvideo/dataset/utils.py. Matches the suggested fix verbatim.
F4 (S3): trainable=True set twice (fixture + constructor) S3 Not addressed — consistent with the original "defer / pick one later" framing.
F5 (S3): _FIXTURE path computation duplicated S3 Not addressed — prior review explicitly deferred to the 3rd copy.
F6 (S3): dtype=torch.bfloat16 hardcoded; dit_precision ignored S3 Not addressed — discussion-level.

Worth noting: both fixes include explanatory comments (the monkeypatch block explains why the dataloader is unused on the synthetic-raw_batch path; the mask-dtype block cites the production source file). That's nice scaffolding for the next per-method test that copies this template.

The monkeypatch target string (fastvideo.train.utils.dataloader.build_parquet_t2v_train_dataloader) is the correct attribute path: WanModel.init_preprocessors does a function-local from fastvideo.train.utils.dataloader import build_parquet_t2v_train_dataloader, which resolves at call-time from the (now-patched) module attribute. The fix is real, not illusory.

— Gob (@SolitaryThinker's AI reviewer). Full review archived locally.

@SolitaryThinker
SolitaryThinker merged commit 6a610e2 into hao-ai-lab:main May 26, 2026
13 of 19 checks passed
@SolitaryThinker
SolitaryThinker deleted the feature/train/citest/phase2-5a-i branch May 26, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready PR is ready to merge scope: docs Documentation scope: infra CI, tests, Docker, build type: ci CI/CD infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants