QLoRA training path: fine-tune GLM-5.2 (744B) in 64 GB RAM#626
QLoRA training path: fine-tune GLM-5.2 (744B) in 64 GB RAM#626pavolbauer wants to merge 5 commits into
Conversation
Ported from the qlora-train branch (pre-v1.0 base): safetensors adapter format with base-fingerprint gating (LORA_UNSAFE=1 to override), atomic writer, CPU residual application via ADAPTER=<dir>. Without an adapter, inference output is bit-identical (tiny oracle TF 32/32 on this base). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The full training path from the qlora-train branch, ported to colibri.c: manual backward through the GLM block with activation checkpointing (routing replay; recompute bitwise-equal to retention), expert-grouped streaming with row-batched fwd/bwd, budget manager (shed expert cache before the OS swaps), masked-CE SFT dataset reader, atomic checkpoints with bitwise resume, coli_train CLI. Gates: test_train_linear (PyTorch f64 parity), test_train_tiny (trajectory vs torch, rel 2.4e-6), test_train_resume (bitwise) — all wired into make test. Real-model probe on this base reproduces the M7 baseline exactly (loss 3.5764, 239s/step, identical expert I/O). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merged around the new r_top8_par work; metal-test green for both suites. Dispatch is opt-in (COLI_METAL=1, COLI_TRAIN_METAL_MIN rows) and off by default: no wall-clock win yet (experts are fmt=4 grouped on CPU) and the f32 GEMM forward numerics differ from the CPU idot path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
m7_smoke.sh (build -> doctor -> 1+10 steps -> adapter-in-inference) and m8_overfit.sh (overfit + on/off eval), adapted to the colibri binary name; persona + dolly tokenized datasets; the M7 runbook and the implementation brief the milestones were built against. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4729e09 to
0cd9b99
Compare
|
This is genuinely exciting — fine-tuning the real 744B on 64GB by carrying the streamed-experts idea into the backward pass is exactly the kind of thing that fits colibrì's mission (frontier AI on consumer hardware), and the numbers you quote (loss 5.05 → 0.37, 34.9GB peak, adapter learned + base restored on unset) are compelling. We'd like to evaluate it seriously as a direction, not rush it in, so a few things would help us dig in: Reproducible evidence
Quality / correctness Scope / integration No rush — we'd rather land this well than fast. If you can add the minimal example + the before/after transcripts + the "inference unchanged when off" confirmation, that gives us what we need to decide on the direction. Really nice work. |
Tiny-oracle end-to-end repro for reviewers: base greedy matches the PyTorch reference 20/20 -> coli_train overfits a rank-16 adapter onto a chosen alternative continuation (~90s CPU) -> ADAPTER set reproduces the trained continuation 20/20 -> ADAPTER unset matches the reference 20/20 again. Needs torch once (tiny model generation); training and inference are pure C. tools/make_tiny_sft.py builds the coli-sft-v1 set from the oracle's own prompt ids, no tokenizer involved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Hi there, thank you very much for the kind words and the thorough look — "land it well rather than fast" is exactly the spirit this was built in, so the five asks are a pleasure to address. First things referenced are on the branch 1. Minimal runnable example (~2–3 minutes, CPU only)cd c && ./scripts/quickstart_tiny.shIt builds, generates the tiny oracle model ( Mechanism, adapter effect, and reversibility — each token-exact. 2. Before/after transcripts (real 744B, held-out prompt)System prompt in rows marked ✦:
The adapter-OFF generations are the base-restored-on-unset case: they were 3. Inference path unchanged when off — token-exact vs current devStrongest check I could construct: built pristine Byte-identical. Corroborating: tiny oracle TF is 32/32 and greedy 20/20 on this 4. Catastrophic forgettingFair ask — coming as a follow-up comment later: base-vs-adapter delta on 5. Blast radius (vs merge-base
|
| Area | Lines | Reaches a normal inference build? |
|---|---|---|
c/train/*, train tests, tools, scripts, data, docs |
+4 077 (31 files) | No — compiled only into coli_train and the four tests/test_train_*/test_lora binaries |
c/lora.h + c/colibri.c |
+288 / −3 (colibri.c itself: +54/−3) | Yes, but env-gated: adapter loads only when ADAPTER is set; off-path is a NULL check (see #3) |
Metal (backend_metal.mm/.h, its test) |
+328 | Kernels linked into the engine build but dispatched only from trainer code; inference dispatch sites untouched |
| Makefile + gitignores | +34 | New targets/test wiring only |
Happy to split the PR along exactly those lines (adapter runtime / trainer /
metal / scripts) if that's easier to review — and to relocate AGENTS.md and
the README material wherever you prefer docs to live.
Summary
This branch adds a memory-bounded LoRA/SFT training path to colibrì and has
fine-tuned the real 744B GLM-5.2 end-to-end on a 64 GB M4 Max: 100
optimizer steps in 27.8 h, peak RSS 34.9 GB, loss 5.05 → 0.37, adapter
demonstrably changes generation (persona learned, held-out prompt generalizes),
and unsetting
ADAPTERrestores base behavior. The same streamed-experts ideathat makes colibrì's inference possible carries over to training: dense state
resident, experts streamed per layer pass, no gradients or optimizer state for
any frozen weight, activation checkpointing with routing replay.
Mergeable against current
dev: the work was developed on pre-v1.0devand has been ported to the
colibri.c+ modules layout — 4 clean commits ontop of
devHEAD. All gates re-run on this base: fullmake test(197 OK),metal-test(incl. the new r_top8_par suites), tiny oracle TF 32/32, PyTorchfixture parity, and a real-model probe that reproduces the original run's
first-step loss exactly (3.5764, 239 s/step, identical expert I/O counts).
Opening as a draft to ask how you'd prefer it landed before flipping to
ready-for-review.
Evidence (M8 acceptance run, 2026-07-26)
o_proj, lr 1e-3, RAM budget 44 GBthree times and recovered; 104 TB total expert I/O, zero crashes
Kolibrík. Small footprint, big model. 🐦 …Kolibrík. Small footprint, big model. 🐦 …Bratislava. 🐦 🐦 …Full logs + generations are attached to the fork's release. A single fwd+bwd
pass reproduces the same loss to 4 decimals before/after the perf work
(784 s → 239 s per pass on the M4 Max).
What's in the diff (+11k lines, almost entirely additive)
c/lora.hcolibri-lora-v1, fingerprint-gated load,ADAPTER=<dir>in inference; without it output is bit-identical to upstreamc/train/qlora_ops.hdx = Q(W)ᵀ dy(int8/int4/int4-grouped, never materializes W), AdamW; validated vs PyTorch float64c/train/train_model.hc/train/budget.hc/train/dataset.h,c/train/checkpoint.h,c/tools/prepare_sft.pyc/train/train_main.c,c/scripts/m7_smoke.sh,c/scripts/m8_overfit.shc/backend_metal.mm/.hCOLI_METAL=1)c/tests/test_lora.c,test_train_linear.c,test_train_tiny.c,test_train_resume.cmake check; PyTorch fixtures viac/tools/make_train_oracle.pyEverything was built milestone-gated per the brief in
AGENTS.md(M0–M8, eachwith tests/oracle parity; commit history follows the milestones).
Review pointers
c/train/train_model.h(the backward + checkpointing core) andc/train/qlora_ops.h(the frozen-weight transpose kernel).make checkruns every training gate incl. PyTorch-fixture parity andbitwise resume;
make metal-test METAL=1covers the Metal kernels.the runbook under
docs/plans/and the brief inAGENTS.md(happy torelocate/trim both to your taste).
Port notes
The trainer
#includes the engine wholesale; the port fromglm.camountedto the include swap, a 54-line engine-side patch (adapter load + o_proj
residual + config fingerprint), re-merging the Metal training kernels around
your new
r_top8_parwork, and adapting the scripts to thecolibribinaryname. The original M7/M8 evidence was produced on the pre-v1.0 base; the
real-model probe above ties the two bases together (identical first-step
loss and I/O profile).
Questions for you
/ trainer / scripts+docs)?
c/train/layout and thecoli_trainbinary name?differ from the CPU idot path (both valid; documented in the commit log) —
happy to align on whichever default you prefer.
Validation
make -C c check— full pass on this branch (197 tests OK, 13 env-dependent skips), macOS/M4 Maxmake -C c cuda-test— n/a, no CUDA code touchedc/scripts/m7_smoke.shandc/scripts/m8_overfit.sh(invocations in the PR body); raw logs attached to the fork's releaseCompatibility
make portablepasses in check; Python/PyTorch are needed only to generate optional test fixtures, and the tests self-skip without themc/data/(persona set + eval prompts) so the milestone scripts run fully offline — happy to replace with aprepare_sft.pystep if you'd rather not carry them