Repository navigation
Compile the Laya encoder for decision model training - #12778
Conversation
…memory test on a real CUDA device
Clef and Clef-flash repos (config.json plus joint_head.*) are detected as decision models next to Laya. Training goes through FastDecisionModel with QLoRA allowed and the Qwen3.5 fast-path kernel setup the worker skips for Laya. The Decision API lists clef and clef-flash and Clef fine-tunes, and serves them from a spawn child that loads the backbone through Unsloth, so unloading returns the GPU memory. Clef waits for the GPU while a run trains instead of loading on CPU.
… on CPU when causal_conv1d is installed
…les, honour a cancel during calibration
# Conflicts: # studio/backend/core/training/worker.py
…ure into its head
…-op accumulation wrapper
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b37a35370
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # The decision trainer resolves its own Laya cache, which holds no config.json to pin. | ||
| if is_local_path(model_name) or config.get("is_decision"): | ||
| config["actual_model_repo_id"] = None | ||
| config["model_snapshot_path"] = None | ||
| config["model_revision"] = None |
There was a problem hiding this comment.
Retain validated snapshot pins for remote Clef models
When a remote Clef model is selected from a validated cached snapshot, this blanket decision-model exemption clears its snapshot path and revision even though Clef uses a normal root-level config.json and can be pinned like other Hugging Face models. The decision worker subsequently resolves model_name again, so a moved branch can train different weights than those inspected during preflight, and a cached private model may require a new Hub lookup instead of loading the authorized snapshot. Limit this exemption to Laya's subfolder layout and make the Clef download/load path consume the retained snapshot.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not changing: decision runs skip the cache-pin machinery on purpose and the loader resolves its own snapshot; pinning remote Clef snapshots through the decision path is a feature, not a defect here.
| start, end = config.get("dataset_slice_start"), config.get("dataset_slice_end") | ||
| if start is not None or end is not None: | ||
| rows = rows[start or 0 : (len(rows) if end is None else end + 1)] |
There was a problem hiding this comment.
Apply the requested dataset slice before materializing rows
For a Hub decision dataset that is not loaded through the exact-cache row limiter, _from_arrow converts every row into Python dictionaries before the requested dataset_slice_start/dataset_slice_end is applied here. Selecting a small slice of a multi-million-row dataset can therefore consume memory and time proportional to the full dataset, potentially exhausting the worker before training starts. Select the requested Arrow range before calling _from_arrow (and similarly avoid fully materializing other sliceable inputs).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not changing: slicing before materialising is a memory optimisation for very large Hub decision datasets; the slice applied today is correct, only its cost differs.
|
@codex review |
1 similar comment
|
@codex review |
|
@codex review |
1 similar comment
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6595226285
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Initiates training in the background and returns immediately. Use /status | ||
| to check progress. | ||
| """ | ||
| await asyncio.to_thread(_validate_decision_request, request, via_api_key) |
There was a problem hiding this comment.
Replay idempotent starts before decision validation
For a retry carrying an already-resolved start_request_id, this validation runs before the stored outcome is checked at lines 1624–1630. If the original request was an accepted Clef QLoRA start but the checkpoint metadata is now temporarily unavailable or the local checkpoint was moved, decision_layout() can fall back to Laya and reject the still-explicit load_in_4bit=true, so the retry returns a new 400 instead of replaying the accepted result as the idempotency comment promises. Peek and return the existing start record before performing decision-model validation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not changing this: a retry only reaches decision validation again if the checkpoint it validated against disappeared between the first start and the retry, and in that case refusing the retry is the right outcome rather than replaying a run whose model is gone.
…ke decision tests portable to multi-GPU, Windows and Apple Silicon hosts
…run's training record when loading a Clef checkpoint
…m/NilayYadav/unsloth into r12778 # Conflicts: # tests/test_decision_model.py
|
@codex review |
1 similar comment
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Builds on #12585 (decision model fine-tuning for Laya and Clef), which is now merged. It now also carries @Etherll's #12824 (merged here with its commits kept), so the Laya training speedups are one PR.
Laya training is launch bound: at the default recipe (LoRA, 8 decisions of about 300 tokens per micro-batch) the GPU is busy for about 0.14 s of a 1.6 s step on a B200, and the time goes to many small ModernBERT kernels. Compiling each encoder layer fuses them.
What changes
DecisionTrainer.train()compiles each Laya encoder layer in place (nn.Module.compile(dynamic = True)) for the run and puts the layers back to eager afterwards, so calibration, evaluation and saving run eagerly and parameter names, LoRA merging and saving are unchanged.UNSLOTH_DECISION_COMPILE=0turns it off,UNSLOTH_DECISION_COMPILE=1forces it on. Clef models are not affected (their head already compiles in Studio: fine-tune Laya and Cloudflare Clef decision models and serve them #12585).Also from #12824
torch.utils.checkpoint.checkpoint.On one B200, LoRA r64 and full fine-tune at 2 decisions x 16 accumulation, interleaved runs: #12824's additions are about 1.12x (LoRA, 1.25 to 1.08 s/step) and 1.10x (full, 0.71 to 0.65 s/step) faster than the dynamic-shape compile alone, same peak memory, 0 graph breaks.
Results
Colab L4, one card per run, arms interleaved ABBA, fresh process and compile cache per arm, 24 steps, median s/step (identical base runs differed by about 1%):
Merged with #12824: speed and accuracy per GPU
Pre-merge (7052f9d) vs merged (16c55e2), ABBA order, fresh process and compile cache per run, steady s/step from step 3:
Same-arm spread is at most 3% (9% on the RTX PRO 6000). Full fine-tune peak memory is unchanged; LoRA at 8x8 allocates 2.6 to 5% more from bucket padding.
Gradients vs an fp64 eager step, 30 to 40 batches per GPU: in bf16 the merged compile is level with eager (A100 0.99x, L4 1.10x, B200 tie; RTX PRO 6000 1.22x, CI [1.02, 1.51]); in fp32 compiled matches fp64 on all GPUs. A single batch can show a large bf16 gap in either direction (eager was the worst arm on L4 and A100 for one batch): it comes from bf16 rounding in layers 0 to 10, and each compiled layer alone is as accurate as eager. Holdout loss after 60 steps (3 seeds per arm, B200 and L4) moved by at most 0.015 CE, well inside the 0.03 to 0.05 seed spread.
Not included
fullgraph=True, no graph breaks) was another 1.13x over this, but takes 13 to 17 minutes to compile on an L4 and cannot trace Unsloth's offloaded gradient checkpointing, so it needs 3x the memory. Not worth it at Laya's typical run length.Tests
tests/test_decision_model.pycovers the run-length threshold, both overrides, the return to eager, and a compile that raises (training still finishes, eagerly). The decision suite passes on CPU and on GPU with the compile on auto and forced on.