Quintus / docs /training_playbook.md
iamrahulreddy's picture
release: publish Quintus project files
4fc1bb9 verified
|
Raw
History Blame Contribute Delete
8.03 kB
# Training Playbook
This page captures the practical training lessons behind Quintus. It focuses on the engineering decisions that made the final online-KD run stable, reproducible, and fast enough to complete on large single-GPU hardware.
## Core Objective
The training objective combines assistant-token cross entropy with teacher-student KL divergence:
$$
\mathcal{L}_{\text{total}}
= \alpha \mathcal{L}_{\text{CE}}
+ (1 - \alpha)\mathcal{L}_{\text{KD}}
$$
For the final Qwen3 run:
$$
\alpha = 0.3,\quad
T = 2.0,\quad
C_{\text{KD}} = 2048,\quad
S_{\max} = 4096
$$
In this codebase, $\alpha$ is the cross-entropy weight. Lower $\alpha$ gives the teacher distribution more influence. Higher $\alpha$ gives hard assistant targets more influence.
## Why Online KD Replaced Offline Top-K KD
The early pipeline precomputed only a small top-k slice of the teacher distribution. That made storage and training cheaper, but it created a hard information ceiling.
With a Qwen vocabulary around 151K tokens:
$$
\frac{k}{|V|}
= \frac{8}{151{,}665}
\approx 5.3 \times 10^{-5}
= 0.0053\%
$$
That sparse signal was enough to disturb student weights, but not enough to reliably transfer deeper reasoning behavior. Several development probes changed alpha, epochs, and student initialization; the same ceiling remained.
The final online path removes that bottleneck. Teacher and student run together, and the KL term is computed from the live full-vocabulary teacher distribution.
## Memory Shape To Respect
Full-vocabulary KD is dominated by logits:
$$
\text{student\_logits},\ \text{teacher\_logits}
\in \mathbb{R}^{B \times S \times |V|}
$$
At Qwen vocabulary scale, increasing micro-batch size by one can add many GiB of temporary memory pressure. Effective batch size is not the same as memory cost. Peak memory is mostly driven by micro-batch size, sequence length, vocabulary width, activation storage, and the backward pass.
Useful rule:
$$
B_{\text{eff}} = B_{\mu} \times A
$$
Keeping $B_{\mu}$ lower and $A$ higher is often safer than a large micro-batch with the same effective batch size.
## Token Chunking
A naive full-vocabulary KL implementation materializes too much temporary state. Quintus computes KD over token chunks:
$$
C_{\text{KD}} = 2048
$$
Larger chunks reduce loop overhead but increase temporary memory. Smaller chunks save memory but can add kernel-launch and Python overhead. The final value is a B200-oriented balance for the 8B -> 1.7B workload.
## Sequence Packing
Sequence packing was the largest throughput win in development probes.
The packing strategy:
- Sort samples by length descending.
- Pack samples with deterministic first-fit decreasing binning.
- Insert EOS separators between samples.
- Set separator `loss_mask = 0`.
- Optionally mask the first token after each separator.
- Build `attention_mask` from true packed length, not from token identity.
The attention-mask detail matters because Qwen tokenizers can share EOS-like IDs with padding behavior. Deriving attention from `input_ids != pad_token_id` can accidentally mask real EOS separators inside packed rows.
Packing probes showed an unpacked B200 online-KD baseline around the low-20K tokens/sec range. Packed training reached roughly the mid-40K tokens/sec range after warmup. The final Qwen3 profile uses the same design principle with a conservative 8B -> 1.7B batch shape.
## B200-Oriented Final Shape
The Qwen3 config is intentionally conservative:
$$
B_{\mu}=4,\quad
A=2,\quad
B_{\text{eff}}=8,\quad
L_{\text{pack}}=4096
$$
Runtime choices:
- `gradient_checkpointing = false`
- `compile_model = false`
- `fused_adamw = true`
- `sequence_packing.enabled = true`
- FlashAttention-2 when available
- Liger kernels for compatible Qwen-family operators
The main reason is the 8B teacher plus 1.7B student online-KD footprint. A smaller teacher/student pair can use larger micro-batches, but the release workload reserves more headroom.
## Kernel Choices
FlashAttention-2 is the preferred stable attention path when available.
Liger kernels are useful for Qwen-family training, but KD places an important constraint on fusion:
- Safe to fuse: RMSNorm, RoPE, SwiGLU.
- Avoid for KD: fused linear cross entropy that hides raw student logits.
The KD loss needs raw student logits to compute teacher-student KL. Any optimization that bypasses logits entirely can break the objective.
## Why `torch.compile` Stayed Off
`torch.compile` can be useful for some SFT paths, but it was not the production choice for final KD.
Observed risks:
- Large Inductor memory overhead.
- Warmup cost on short-lived cloud instances.
- Dynamic-shape graph breaks from variable sequence lengths.
- Recompile overhead that reduced cumulative throughput in probes.
- `_orig_mod.` prefixes in saved checkpoints if compiled modules are not unwrapped before saving.
- Limited benefit after FlashAttention and Liger already fuse the major kernels.
For this workload, stable eager execution with targeted kernels was more predictable than compiler-driven fusion.
## DataLoader And Cloud Stability
Large worker counts can improve throughput on local systems, but notebook and cloud environments can deadlock through multiprocessing queues, IPC limits, or shared-memory pressure.
Practical policy:
- Start with conservative worker and prefetch settings.
- Treat a silent training hang as a DataLoader candidate, even when GPU utilization remains high.
- For some cloud notebook runs, `dataloader_workers = 0` was the most stable choice.
- For the release config, `dataloader_workers = 8` and `prefetch_factor = 2` are a controlled default, not a universal rule.
## Checkpointing And Resume
Cloud GPUs are preemptible and notebook sessions disappear. The training loop therefore treats checkpointing as a core training feature, not an afterthought.
Important design points:
- `best` is selected from validation loss where available.
- `last` is saved for final-state inspection.
- Step checkpoints can resume mid-epoch.
- Scheduler state is saved.
- Optimizer state may be intentionally omitted for very large runs to avoid massive checkpoint overhead.
- Resume semantics distinguish initialization from a completed checkpoint and continuation from an interrupted checkpoint.
This avoids the common trap where `resume_from_checkpoint` silently starts from the wrong phase or stale state.
## Provenance Rules
The pipeline is strict about artifact compatibility:
- Tokenizer vocabulary sizes must match the model contract.
- Teacher-logit metadata must match expected temperature, sample count, max sequence length, and tokenizer/model identity.
- Dataset fingerprints are preferred over path equality because paths are machine-local.
- Tokenizer fingerprints can drift across library versions, so hard checks should focus on vocab-size and schema invariants.
The principle is simple: train only when artifacts prove they belong together.
## Dataset Sampling
Taking the first N valid streamed examples can bias a run if the upstream dataset is ordered by source, task, difficulty, or language. Later configs added stream shuffling before selection.
The config uses a non-default seed:
```text
stream_shuffle_seed = 25
split_seed = 25
```
The number is intentionally explicit. Reproducibility needs stable seeds; it does not require the overused value `42`.
## Practical Watchpoints
During a run, these signals matter more than a single loss number:
- Loss stays finite from the first logging window.
- CE and KD move in plausible ranges.
- Rolling throughput remains stable after warmup.
- GPU memory is high but not near an unpredictable OOM edge.
- Validation loss is computed on the intended holdout.
- Saved checkpoints load in standard Transformers and vLLM paths.
- Downstream benchmark results agree with the training story.
Held-out KD loss is useful, but it is not the release gate. Standardized benchmarks and qualitative checks must decide whether the checkpoint improved the target behavior.