feat(sft): single-card equivalent of the 8-GPU H3 SFT recipe - #210
Draft
Rockdu wants to merge 9 commits into
Draft
feat(sft): single-card equivalent of the 8-GPU H3 SFT recipe#210Rockdu wants to merge 9 commits into
Rockdu wants to merge 9 commits into
Conversation
Same batch schedule as the 8-GPU recipe -- 32 samples per rollout, num_steps_per_rollout=4, global_batch_size 8, micro_batch_size 1 -- with the 8 ranks' one micro-batch each folded into 8 gradient-accumulation micro-batches on the single rank. Adds --fsdp-cpu-offload, without which H3's ~134 GB fp32 master does not fit beside the activations on one card. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-step numbers are perf/actor_train_time on a fully warm cache; the single-worker encode cost lands in train_wait_time and was being read as if it were included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 35.3 GB figure came from one sample of an early rollout; the warm-cache run peaks at 39.5 GB, which is what a card has to be provisioned for. Also records that --fsdp-master-dtype moves host RAM, not GPU: under cpu-offload the gathered copy is bf16 either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Only the LoRA adapters train and PEFT keeps them in fp32 whatever the base dtype, while the frozen base is gathered as bf16 for the forward at either setting -- MixedPrecisionPolicy uses param_dtype=bf16 on every wrap and H3 declares no param_dtype_patterns. So fp32 master changes no math here and costs 66GB of host RAM. diffusers' _keep_in_fp32_modules still protects the tensors MiniMax shipped as fp32. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Peak 217GB / steady 99GB at bf16 master, sampled through init. The steady cost is CUDA pinned host memory for the offloaded shard, which is shmem-accounted, does not touch /dev/shm, and cannot be paged out; the peak is the mmap'd checkpoint briefly overlapping the state_dict copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rockdu
force-pushed
the
sft/h3-sft-1gpu
branch
from
August 26, 2026 22:19
67d87af to
40c09fe
Compare
It was a 70-line section against 4.1's ten, repeating the whole memory forensics the script docstring already carries. Both topologies are one recipe with one schedule, so they belong in one section: a two-row recipe table, both launch lines, what the extra two flags buy, and the cost table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One rank has no cross-rank reduction order to vary, so determinism is reachable here and makes the recipe a reproducible reference. No attention backend is pinned, so validate_attention_args takes the torch-native path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deterministic mode moves from a default to a documented opt-in: it buys bit-reproducibility within a topology, which the recipe does not need to be correct, and costs ~25% per step and ~3.6GB of GPU -- enough to push the peak past a 40GB card. The docstring and docs now lead with why the two recipes train the same thing (global_batch_size has no world-size term; the gradient divisor works out the same either way) rather than with the memory forensics. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
scripts/run_diffusion_sft_h3_t2va_1gpu.py—run_diffusion_sft_h3_t2va.pyon one card, training the same thing rather than a scaled-down approximation. Same automation level as the 8-GPU recipe:prepare()fetchesrockdu/WISA-80K-Practical-Dynamics-254, so a zero-argument launch is a complete run and--data-diris the only data knob.Below the docstring the two scripts differ in four lines: the run-name prefix,
--fsdp-cpu-offload,--fsdp-master-dtype bf16, and 8 → 1 GPUs. Every*_argsblock — optimizer, LoRA, batch, sigma grid, reduce dtype — is byte-identical.Why the two are equivalent
global_batch_sizeis 8 in both.arguments.pyderives it asrollout_batch_size × n_samples_per_prompt ÷ num_steps_per_rollout— no world-size term — so one card only changes the layout: 8 dp ranks × 1 micro-batch becomes 1 rank × 8 gradient-accumulation micro-batches atmicro_batch_size=1, still 4 optimizer steps per rollout.actor.pybackwardsloss_sum / num_local_pairs:/8on the single rank,/1on each of eight before FSDP2's mesh-mean divides by 8 again.--fsdp-master-dtype bf16changes no math under--fsdp-cpu-offload. Only the LoRA adapters train and PEFT keeps those in fp32 whatever the base dtype (measured: base bf16 →lora_A/lora_Bfloat32, requires_grad=True); the frozen base is gathered as bf16 for the forward at either setting, sinceMixedPrecisionPolicyappliesparam_dtype=bf16to every wrap and H3 declares noparam_dtype_patterns. Step 1 of an otherwise identical run reproduces the fp32 loss to every digit logged,3.276656e-01._keep_in_fp32_modulesstill holdsproj_in/proj_out/audio_proj_*/time_embedder/ropein fp32;--fsdp-reduce-dtypestays fp32 because the LoRA gradients do train.--fsdp-cpu-offloadis the one flag that is a requirement rather than a free choice: H3 is 33.5B parameters, so even a bf16 master is ~66 GB and leaves no room for activations on a 139.8 GB H200.What it costs
Against the 8-GPU reference run on the same dataset, warm cache. Per-step figures are
perf/actor_train_time, so encoding is excluded.The 9× gap is 8× rank count plus ~14% for the PCIe round trip the offload adds to every block, on the forward and the gradient-checkpoint recompute alike. A cold
.sft_cachecosts more and only here — one encode worker instead of eight, ~480–590 s per rollout through the first epoch, during which the ~67 GB encoder rather than the train step owns the GPU peak.Validation
A zero-argument run on a clean box, end to end:
prepare()downloaded the dataset (1.1 GB, 254 rows,clips/), and its relative media paths resolved — the path this recipe would otherwise never exercise, since every earlier run of mine used absolute paths.iter_0000011→scripts/export_lora.py→ a 624-tensor rank-64/alpha-128 adapter structurally identical to the 8-GPU one, which then served undersglang serve --lora-pathand reproduced thelora_sft_guidefigure prompts.--deterministic-modewas exercised and passes (no op lacks a deterministic implementation), but is not on by default: it costs ~25% per step and ~3.6 GB of GPU, enough to push the peak past a 40 GB card. Documented as--extra-args "--deterministic-mode".Limits
The two curves are comparable on trend and on the per-sigma buckets, not point by point.
prepare_sft_batchseeds the sigma and noise draw on(rollout_id, microbatch_id, dp_rank), so changing topology moves every sample to a different sigma by construction. A long-run trend comparison against the 8-GPU reference is not included here. The run that would have provided it reached 78 steps (19 rollouts) at fp32 master before I stopped it to switch the recipe to bf16; at that length both series still sit inside the 8-GPU run's own warm-up spread, so it would not have supported a conclusion either way.Status is registered NV: no complete training curve has been run at this topology.
Checklist
pre-commit run --files <touched>passespython3 scripts/run_diffusion_sft_h3_t2va_1gpu.py --helpparses--fsdp-cpu-offload,--fsdp-master-dtypeand--deterministic-modeall already exist and are documenteddocs/models/h3/h3.md§4.1,recipe-verification.md)