mlx-bun 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/docs/dag/training-inference-map.html +575 -0
- package/docs/design/memory-system.md +193 -295
- package/docs/design/orpo-dynamic-lambda.md +179 -0
- package/docs/design/orpo-future-enhancements.md +42 -0
- package/docs/design/orpo-training.md +13 -0
- package/docs/design/parity-tier-dag.md +143 -0
- package/docs/design/w4a16-compute-precision-spike.md +232 -0
- package/docs/investigations/orpo-base-uf-experiment-and-directions.md +120 -0
- package/docs/investigations/orpo-flash-cce-pin-leak.md +167 -0
- package/docs/investigations/orpo-uf-testing-handoff.md +126 -0
- package/docs/investigations/steel-flash-cce-handoff.md +58 -0
- package/docs/investigations/trainer-validation-experiment.md +173 -0
- package/docs/reference/memory.md +77 -0
- package/docs/reference/orpo-quickstart.md +44 -5
- package/docs/reference/training.md +9 -5
- package/package.json +1 -1
- package/src/cli.ts +651 -18
- package/src/eval/runner.ts +76 -6
- package/src/eval/tasks/bfcl.ts +49 -15
- package/src/eval/tasks/gsm8k.ts +21 -9
- package/src/eval/tasks/hashhop.ts +33 -4
- package/src/eval/tasks/humaneval.ts +30 -8
- package/src/eval/tasks/ifeval.ts +52 -18
- package/src/eval/tasks/mmlu.ts +85 -74
- package/src/jobs/runner.ts +6 -0
- package/src/memory/chunk.ts +25 -0
- package/src/memory/cluster.ts +24 -0
- package/src/memory/db.ts +114 -0
- package/src/memory/ingest.ts +22 -0
- package/src/memory/pipeline.ts +63 -0
- package/src/memory/prompts.ts +23 -0
- package/src/memory/schedule.ts +163 -0
- package/src/memory/synthesize.ts +29 -0
- package/src/memory/tools.ts +399 -0
- package/src/memory/vault.ts +694 -0
- package/src/memory/wikify.ts +23 -0
- package/src/mlx/metal-kernel.ts +13 -0
- package/src/model/flash-attention.ts +5 -3
- package/src/pi-provider.ts +29 -13
- package/src/pi-session.ts +81 -0
- package/src/pi-terminal.ts +13 -24
- package/src/pi-web.ts +246 -166
- package/src/quantize/job.ts +17 -2
- package/src/quantize/quantizer.ts +26 -2
- package/src/server.ts +213 -44
- package/src/tokenizer.ts +6 -2
- package/src/train/dataset.ts +11 -3
- package/src/train/flash-cce.ts +18 -5
- package/src/train/job.ts +41 -4
- package/src/train/loss.ts +10 -4
- package/src/train/trainer.ts +62 -3
- package/src/train/watch.ts +542 -0
- package/src/web/app.html +264 -14
- package/src/web/skills/memory/SKILL.md +97 -0
- package/src/web/skills/web-research/SKILL.md +3 -1
- package/src/web/skills.ts +46 -8
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Trainer validation experiment
|
|
2
|
+
|
|
3
|
+
**Status:** in progress (started 2026-06-20). **Goal:** prove the from-scratch
|
|
4
|
+
SFT/DPO/ORPO trainer actually works before trusting any downstream result — general
|
|
5
|
+
capability (Goal 1) or task-specific (Goal 2).
|
|
6
|
+
|
|
7
|
+
## Why this exists — the smoking gun
|
|
8
|
+
|
|
9
|
+
The margin/accuracy is recorded in `metrics.json` (`valTrajectory[].margin`,
|
|
10
|
+
`bestMargin`), not the stdout log. The prior `cpm5-uf` ORPO run (λ=0.1) shows:
|
|
11
|
+
|
|
12
|
+
| step | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 |
|
|
13
|
+
|---|---|---|---|---|---|---|---|---|
|
|
14
|
+
| accuracy | .507 | .514 | .534 | .541 | .534 | .520 | .547 | .547 |
|
|
15
|
+
| margin | .132 | .124 | .110 | .120 | .136 | .128 | .118 | .123 |
|
|
16
|
+
|
|
17
|
+
**Preference accuracy is ~0.52 (near the 0.50 coin-flip) and the margin is flat.**
|
|
18
|
+
ORPO's defining job is to push accuracy up and widen the margin. This is *not* a clean
|
|
19
|
+
"it works" signal. Caveats: 800 steps ≈ 0.05 epoch (undertrained), provenance
|
|
20
|
+
(from-base vs warm-start) unconfirmed. But it's enough to make validation mandatory:
|
|
21
|
+
**we cannot currently claim our ORPO learns the preference at all.**
|
|
22
|
+
|
|
23
|
+
## Method-decomposition (the validation key)
|
|
24
|
+
|
|
25
|
+
`ORPO_loss = NLL(chosen) + λ·L_OR`. NLL is exactly the SFT objective; L_OR is the
|
|
26
|
+
reference-free odds-ratio preference term. So we validate the building blocks, and ORPO
|
|
27
|
+
is covered by its parts plus a small ORPO-specific surface (the OR term + λ).
|
|
28
|
+
|
|
29
|
+
mlx-lm has **no DPO/ORPO** — so the oracle strategy is:
|
|
30
|
+
- **SFT/NLL** → numeric parity vs **mlx-lm tuner** (oracle env exists).
|
|
31
|
+
- **DPO + OR loss** → **numeric unit test vs the paper's equations** on a fixed tiny
|
|
32
|
+
input (hand-computed expected loss + grad). No external dep. Optional stronger
|
|
33
|
+
cross-check: **TRL** `DPOTrainer`/`ORPOTrainer` loss (PyTorch; multi-GB env →
|
|
34
|
+
approve-first, not default).
|
|
35
|
+
|
|
36
|
+
Every positive control is designed so "weak data / weak lever" is **not** an available
|
|
37
|
+
excuse — success must be unambiguous.
|
|
38
|
+
|
|
39
|
+
## PILLAR 1 — Prove the trainer (gate for everything)
|
|
40
|
+
|
|
41
|
+
- **1a · SFT positive control** *(infra GREEN, behavioral check WEAK)*. Train
|
|
42
|
+
`fixtures/adapters/data-upper` from scratch on MiniCPM5; assert generations are
|
|
43
|
+
actually uppercased (oracle: the repo's pre-trained `upper` adapter). Existing
|
|
44
|
+
`train-e2e.test.ts` only checks loss↓ — add the behavioral assert. *(seconds)*
|
|
45
|
+
- **1b · DPO positive control** *(MISSING)*. Trivial preference (chosen uppercase,
|
|
46
|
+
rejected lowercase). Assert **margin increases** and **preference accuracy → ~1.0**.
|
|
47
|
+
- **1c · ORPO positive control + the real question.** (i) Same trivial preference →
|
|
48
|
+
accuracy → ~1.0. (ii) **From-BASE ORPO on UF**, multiple epochs, watch margin widen
|
|
49
|
+
and accuracy climb well above chance — directly resolves the near-chance finding
|
|
50
|
+
(undertraining vs warm-start vs bug).
|
|
51
|
+
- **1d · Loss-formula parity.** SFT NLL vs mlx-lm; DPO/ORPO loss + grad vs hand-computed
|
|
52
|
+
paper equations (unit test). The training-side analogue of the inference logit-parity
|
|
53
|
+
oracle — currently MISSING.
|
|
54
|
+
- **1e · vs the paper.** Internal proxy first: preference accuracy robustly > chance on
|
|
55
|
+
UF. Then (approve-first, LLM-judge) AlpacaEval/MT-Bench **direction** vs base (not
|
|
56
|
+
absolute — paper used 7B). optiq suite (incl. IFEval) is an already-local downstream.
|
|
57
|
+
|
|
58
|
+
> **Gate:** until 1a–1c are green, Pillars 2 & 3 sit on an unverified trainer.
|
|
59
|
+
|
|
60
|
+
## PILLAR 2 — Measure general-capability impact (optiq suite)
|
|
61
|
+
|
|
62
|
+
All 6 datasets are present locally (`~/.cache/mlx-bun/eval-data/`: mmlu, gsm8k, ifeval,
|
|
63
|
+
bfcl, humaneval, hashhop); harness = `scripts/eval.ts capability`.
|
|
64
|
+
- **2a · Eval parity:** run on base CPM5, reproduce optiq's **30.28** within tolerance
|
|
65
|
+
(validates the *measurement* before trusting deltas; confirm the README's open BFCL
|
|
66
|
+
"no-calls" / MMLU "at-chance" flags aren't broken on our side).
|
|
67
|
+
- **2b · Delta:** trained checkpoint vs base, on the 6-task mean.
|
|
68
|
+
- **2c · Targeting:** mean is floored by **BFCL 0.0 / GSM8K 2.7 / HashHop 4.0**; UF hits
|
|
69
|
+
the already-strong IFEval, so expect small mean movement. Highest-leverage Goal-1 lever
|
|
70
|
+
is **BFCL** (0.0 is format-trainable; 0→30 = +5 on the mean).
|
|
71
|
+
|
|
72
|
+
## PILLAR 3 — Task-specific (separate; gated on Pillar 1)
|
|
73
|
+
|
|
74
|
+
Chunk segmenter via `scripts/chunk-eval.ts` (boundary/label accuracy), using the
|
|
75
|
+
hard-negative ORPO data (remove-a-cut / add-a-cut, balanced to kill the length
|
|
76
|
+
shortcut). Measured by chunk accuracy, not val loss / capability. Credibility depends on
|
|
77
|
+
Pillar 1.
|
|
78
|
+
|
|
79
|
+
## Sequencing & gates
|
|
80
|
+
|
|
81
|
+
1. Pillar 1 positive controls (1a→1b→1c-trivial) — fast, "does the machinery work."
|
|
82
|
+
2. Pillar 1 real replication (1c from-base UF) + 1d loss-formula parity.
|
|
83
|
+
3. Pillar 2a eval-parity (reproduce 30.28) → 2b deltas.
|
|
84
|
+
4. Pillar 3 — once the trainer is trusted.
|
|
85
|
+
|
|
86
|
+
## Execution log
|
|
87
|
+
|
|
88
|
+
### 2026-06-20 — Pillar 1 quick validations: PASS
|
|
89
|
+
|
|
90
|
+
**Machinery + gradient correctness — GREEN.** `MLX_BUN_TEST_TRAIN=1 bun test
|
|
91
|
+
train-autograd train-e2e train-orpo-e2e` → 8 pass / 0 fail, real training on
|
|
92
|
+
MiniCPM5 (11.3s). `train-autograd` confirms the backward matches autograd.
|
|
93
|
+
|
|
94
|
+
**ORPO positive control (prefer-uppercase, 12 train / 4 val, 120 iters, lr 1e-3) — PASS.**
|
|
95
|
+
Held-out preference accuracy **1.000 by step 10**, margin **5.5 → 4.0** (noisy at lr=1e-3
|
|
96
|
+
but accuracy solid). Logged: `runs/orpo-pc.{json,csv}`.
|
|
97
|
+
|
|
98
|
+
**DPO positive control (same data) — PASS, cleaner.** Accuracy **1.000 from step 10**,
|
|
99
|
+
margin **monotonic 6.46 → 9.67**. Logged: `runs/dpo-pc.{json,csv}`.
|
|
100
|
+
|
|
101
|
+
**Conclusion: the preference machinery is validated.** On a learnable preference both
|
|
102
|
+
DPO and ORPO drive accuracy to 1.0 and the margin wide open (vs UF's flat ~0.12). A
|
|
103
|
+
broken trainer could not. ⇒ **The UF near-chance result is the DATA, not a bug** — UF
|
|
104
|
+
pairs are genuinely subtle for a 1B (both responses plausible); "prefer uppercase" is
|
|
105
|
+
trivially separable. The smoking gun is explained: implementation sound, lever weak.
|
|
106
|
+
|
|
107
|
+
**Gotcha found (recorded so we don't repeat it):** `AdapterManager.mount` (the *serving*
|
|
108
|
+
hot-swap path) does **not** reflect into `branchLogpMean` (the *training* forward) — an
|
|
109
|
+
external base-vs-adapter `orpoMetrics` measurement via mount returns bit-identical
|
|
110
|
+
numbers (measured: 0.500/0.1463 both). Measure training-side metrics via the
|
|
111
|
+
`trainLora` emit / loraState path (`scripts/experiments/pref-control.ts`), not mount.
|
|
112
|
+
|
|
113
|
+
**Logging for plotting:** `runs/<stem>.{json,csv}` — per-step train `{loss, accuracy,
|
|
114
|
+
margin}` + per-eval val `{loss, accuracy, margin}`. CSV columns: `phase,step,loss,
|
|
115
|
+
accuracy,margin`. Tools: `scripts/experiments/pref-control.ts` (train+log),
|
|
116
|
+
`scripts/experiments/measure-pref.ts` (standalone adapter eval — superseded by the
|
|
117
|
+
emit path for training metrics; see gotcha).
|
|
118
|
+
|
|
119
|
+
### 2026-06-20 — Pillar 2a: capability-harness parity. Two bugs found + fixed.
|
|
120
|
+
|
|
121
|
+
**The first-measure step caught two harness bugs** (exactly its purpose — never trust a
|
|
122
|
+
delta until the harness reproduces the published baseline):
|
|
123
|
+
|
|
124
|
+
1. **Thinking mode.** The eval rendered the chat template's *default* (thinking-on for
|
|
125
|
+
MiniCPM5); optiq's numbers are **non-thinking** (HF card: IFEval 64.7 is non-thinking).
|
|
126
|
+
Symptom: IFEval **36.7%** vs 64.7. **Fix:** `src/eval/runner.ts` `generateText` now
|
|
127
|
+
renders `enableThinking: false` by default (env `MLX_BUN_EVAL_THINK=1` to re-enable).
|
|
128
|
+
→ IFEval **73.3%** (n=30), optiq ballpark.
|
|
129
|
+
2. **HumanEval oracle-python path.** `src/eval/tasks/humaneval.ts` hardcoded
|
|
130
|
+
`/Users/joshrossi/Code/mlx-lm/.venv/bin/python` — wrong on this machine (it's
|
|
131
|
+
`mlx-lm-example`), so every code-exec spawned a missing interpreter → **0%**. **Fix:**
|
|
132
|
+
env-overridable `MLX_BUN_ORACLE_PYTHON` (committed default = reference box; never
|
|
133
|
+
re-commit the path). → HumanEval **53.3%** (n=15), optiq 57.9.
|
|
134
|
+
|
|
135
|
+
**Post-fix parity (base CPM5, non-thinking, n=20 — all ballpark):**
|
|
136
|
+
|
|
137
|
+
| | MMLU | GSM8K | IFEval | BFCL | HumanEval | HashHop |
|
|
138
|
+
|---|---|---|---|---|---|---|
|
|
139
|
+
| ours | 55.0 | 10.0 | 75.0 | 0.0 | ~53 | 0.0 |
|
|
140
|
+
| optiq | 52.4 | 2.7 | 64.7 | 0.0 | 57.9 | 4.0 |
|
|
141
|
+
|
|
142
|
+
⇒ **harness reproduces optiq.** Small-n inflation on GSM8K/IFEval will settle toward
|
|
143
|
+
optiq on the full set. **Exp-1 baseline harness VALIDATED.**
|
|
144
|
+
|
|
145
|
+
### Ready-to-fire sequence (everything staged; only GPU time remains)
|
|
146
|
+
|
|
147
|
+
All measurement tools validated. `scripts/eval.ts` now takes `--adapter <dir>` (mounts the
|
|
148
|
+
trained LoRA → x2). `ORC=/Users/joshrossi/Code/mlx-lm-example/.venv/bin/python` below.
|
|
149
|
+
|
|
150
|
+
1. **x1** (running): `MLX_BUN_ORACLE_PYTHON=$ORC bun scripts/eval.ts capability --candidate MiniCPM5`
|
|
151
|
+
2. **Train** (user): from-base ORPO/UF → `./adapters/cpm5-uf-frombase` (command in chat).
|
|
152
|
+
3. **x2 (Exp 1 after):** `MLX_BUN_ORACLE_PYTHON=$ORC bun scripts/eval.ts capability --candidate MiniCPM5 --adapter ./adapters/cpm5-uf-frombase` → compare to x1.
|
|
153
|
+
4. **Exp 2 gen (GPU):** `bun scripts/experiments/prefbench.ts gen mtbench ./adapters/cpm5-uf-frombase mtbench-uf` (and `gen alpaca … alpaca-uf 200` for a subset).
|
|
154
|
+
5. **Exp 2 judge (codex, anytime):** `bun scripts/experiments/prefbench.ts judge mtbench-uf` → TRAINED win-rate vs BASE.
|
|
155
|
+
|
|
156
|
+
### Next
|
|
157
|
+
- **Full baseline:** see step 1 (running). **(long → user-launched)**
|
|
158
|
+
- **Train (the "do"):** from-base ORPO on UF (`pref-control.ts`, command in chat). **(long → user-launched)**
|
|
159
|
+
- **Re-measure → x2**, compare to x1. Exp 1 done.
|
|
160
|
+
- **Exp 2 judge — VALIDATED (keyless).** No OpenAI key needed: the judge shells out to
|
|
161
|
+
`codex exec -m gpt-5.4-mini` (codex's own auth), forcing structured `{"winner":...}` via
|
|
162
|
+
`--output-schema` and reading the clean verdict from `-o`. `scripts/experiments/llm-judge.ts`
|
|
163
|
+
— `judgeOnce` + `judgePair` (judges BOTH orders to cancel small-judge position bias).
|
|
164
|
+
Positive control: **3/3 obvious good-vs-bad pairs, good won both orders, zero flips.**
|
|
165
|
+
1bx vs 1bx1; consistency (same judge both sides), not absolute comparability to the paper's GPT-4.
|
|
166
|
+
- **Exp 2 harness — STAGED.** Prompts downloaded: `~/.cache/mlx-bun/eval-data/{mt_bench.jsonl
|
|
167
|
+
(80), alpaca_eval.json (805)}`. Two-phase runner `scripts/experiments/prefbench.ts`:
|
|
168
|
+
`gen <mtbench|alpaca> <adapterDir> <stem> [limit]` (GPU — base then trained, non-thinking)
|
|
169
|
+
→ `runs/<stem>.responses.jsonl`; `judge <stem> [limit]` (codex) → TRAINED win-rate vs BASE,
|
|
170
|
+
judging trained-vs-base **directly** (head-to-head, not vs a fixed reference). Only the `gen`
|
|
171
|
+
phase needs GPU; everything else is validated. Fire after the from-base training produces
|
|
172
|
+
`./adapters/cpm5-uf-frombase`.
|
|
173
|
+
- **1c real / 1d parity** — still open (lower priority now the positive controls + harness parity are green).
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Memory
|
|
2
|
+
|
|
3
|
+
mlx-bun memory is a local Markdown wiki that the built-in pi agents can read as durable user context.
|
|
4
|
+
|
|
5
|
+
Core rule:
|
|
6
|
+
|
|
7
|
+
> Chat agents read memory articles. `mlx-bun memory synthesize` is the only writer.
|
|
8
|
+
|
|
9
|
+
The current release includes the vault, read path, Obsidian open flow, and scheduler. Synthesis is scaffolded but still a no-op M1 stub. The planned synthesis engine uses the same local models you chat with, switched into an editorial pipeline with stage-specific LoRA adapters.
|
|
10
|
+
|
|
11
|
+
## Storage
|
|
12
|
+
|
|
13
|
+
- Vault: `~/.mlx-bun/wiki/`
|
|
14
|
+
- User articles: `~/.mlx-bun/wiki/articles/*.md`
|
|
15
|
+
- Read-only reference docs: `~/.mlx-bun/wiki/Reference/*.md`
|
|
16
|
+
- Meta pages: `~/.mlx-bun/wiki/Meta/*.md`
|
|
17
|
+
- Talk pages: `~/.mlx-bun/wiki/Talk/`
|
|
18
|
+
- Override: `MLX_BUN_WIKI=/path/to/wiki`
|
|
19
|
+
- Derived synthesis DB: `~/.cache/mlx-bun/memory.sqlite`
|
|
20
|
+
|
|
21
|
+
The vault is plain Markdown, git-tracked, and opens directly in Obsidian. `Reference/` contains read-only symlinks to mlx-bun's own docs so memory has useful built-in residents on day one; synthesis must never write them.
|
|
22
|
+
|
|
23
|
+
## CLI
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mlx-bun memory init # create the wiki; optional import + schedule
|
|
27
|
+
mlx-bun memory status # path, article count, git, schedule, recent articles
|
|
28
|
+
mlx-bun memory open # open wiki in Obsidian/Finder
|
|
29
|
+
mlx-bun memory open <article> # open a specific article
|
|
30
|
+
mlx-bun memory list # list article stems + Reference/* docs
|
|
31
|
+
mlx-bun memory search <query> # search articles
|
|
32
|
+
mlx-bun memory toc <article> # headings + anchors
|
|
33
|
+
mlx-bun memory section <article> <anchor>
|
|
34
|
+
mlx-bun memory links <article> # outbound/inbound wikilinks
|
|
35
|
+
mlx-bun memory read <article> # print article body
|
|
36
|
+
mlx-bun memory synthesize --dry-run
|
|
37
|
+
mlx-bun memory schedule --at 03:00
|
|
38
|
+
mlx-bun memory unschedule
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`memory synthesize` is currently safe and side-effect-free: it reports that M1 stage bodies are not implemented and writes nothing. In the full flow, `memory init` will be able to offer the HF-hosted memory adapter pack download so synthesis can run locally.
|
|
42
|
+
|
|
43
|
+
## Agent tools
|
|
44
|
+
|
|
45
|
+
Both `mlx-bun pi` and the web chat expose these read-only tools:
|
|
46
|
+
|
|
47
|
+
- `memory_search`
|
|
48
|
+
- `memory_read`
|
|
49
|
+
- `memory_toc`
|
|
50
|
+
- `memory_section`
|
|
51
|
+
- `memory_links`
|
|
52
|
+
- `memory_list` — user articles plus read-only `Reference/*` docs
|
|
53
|
+
- `memory_status` — vault path, setup state, article count, git state, last-synthesis placeholder, schedule state, recent changed articles
|
|
54
|
+
|
|
55
|
+
The tools are always registered. If no vault exists, they return a setup message instead of disappearing. Search/read results prefixed with `Reference/` are mlx-bun docs, not user memory articles.
|
|
56
|
+
|
|
57
|
+
## Obsidian
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
mlx-bun memory open
|
|
61
|
+
mlx-bun memory open Archie_Project
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The first command opens the vault. The second opens one article. Obsidian is preferred; Finder/default Markdown app is the fallback.
|
|
65
|
+
|
|
66
|
+
## Synthesis roadmap
|
|
67
|
+
|
|
68
|
+
M1/M2 will turn saved pi sessions into articles using the local model plus four specialized LoRA adapters trained by Josh from existing Lucien-scale pipeline data and distributed from Hugging Face:
|
|
69
|
+
|
|
70
|
+
1. deterministic ingest of saved web/terminal pi session JSONL;
|
|
71
|
+
2. **memory-chunk LoRA** — conversation → topic chunks (already trained as a WIP artifact);
|
|
72
|
+
3. **memory-bucket LoRA** — chunk → existing bucket or new article proposal;
|
|
73
|
+
4. **memory-synthesis LoRA** — chunks + existing article → conservative article update candidate;
|
|
74
|
+
5. **memory-editor LoRA** — candidate article → polished wiki Markdown;
|
|
75
|
+
6. deterministic footnote/wikilink normalization, quality gates, and git commit.
|
|
76
|
+
|
|
77
|
+
The adapters propose; the pipeline validates and writes. The pipeline writes only `articles/`; `Reference/` is immutable context. Until synthesis lands, memory works with imported/hand-written user articles plus the built-in read-only mlx-bun reference docs.
|
|
@@ -6,16 +6,55 @@ One launcher runs the complete new system — **flash-CCE Metal head** (steel fw
|
|
|
6
6
|
**falls back and logs** if a precondition isn't met (e.g. a row whose chosen/rejected
|
|
7
7
|
prompts aren't identical → two-forward for that row).
|
|
8
8
|
|
|
9
|
-
## Command
|
|
9
|
+
## Command — `mlx-bun train` (recommended)
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
|
|
12
|
+
mlx-bun train <model> --data <dir>
|
|
13
13
|
```
|
|
14
|
-
-
|
|
14
|
+
- `<model>` is a query or snapshot path (e.g. `e4b`, `MiniCPM5`); omit it to auto-pick.
|
|
15
|
+
- `--data <dir>` is a directory with `train.jsonl` (+ optional `valid.jsonl`); rows are
|
|
15
16
|
`{"prompt": "...", "chosen": "...", "rejected": "..."}`.
|
|
16
|
-
- The
|
|
17
|
+
- The command **auto-detects e4b/Gemma** and sets its required training env flags
|
|
17
18
|
(`MLX_BUN_PERF_KERNEL=0`, `MLX_BUN_FUSED_GELU=0`) before loading the model — you don't
|
|
18
|
-
set them yourself.
|
|
19
|
+
set them yourself. The full stack (flash head + prefix-share + segmented) is on by default.
|
|
20
|
+
- It prints a plan box (dataset counts, format, the resolved knobs), streams per-step loss
|
|
21
|
+
+ peak memory, and saves a **mountable** adapter (`mlx-bun serve <model> --adapter <dir>`).
|
|
22
|
+
- `--dry-run` inspects the dataset and prints the plan without training. `mlx-bun help train`
|
|
23
|
+
lists every flag.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# e4b ORPO on your preference data, checkpointing every 200 steps; run it detached
|
|
27
|
+
nohup mlx-bun train e4b --data ./prefs --iters 1000 --save-every 200 > train.log 2>&1 &
|
|
28
|
+
# resume from a checkpoint (warm-start the LoRA weights)
|
|
29
|
+
mlx-bun train e4b --data ./prefs --resume ~/.cache/mlx-bun/mlx-bun-finetunes/orpo-e4b/checkpoints/step-00200
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| flag | default | meaning |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `--method` | `orpo` | `sft` · `dpo` · `orpo` |
|
|
35
|
+
| `--iters` | 100 | iterations |
|
|
36
|
+
| `--lr` | orpo 1e-5 · dpo 5e-5 · sft 2e-4 | learning rate (cosine + short warmup) |
|
|
37
|
+
| `--rank` / `--scale` | orpo 16 / 2.0 | LoRA rank (by_bits scaled) / scale |
|
|
38
|
+
| `--seq` | e4b 8192 · cpm 4096 | max sequence length |
|
|
39
|
+
| `--lambda` | 0.1 | ORPO odds-ratio weight |
|
|
40
|
+
| `--seg` | 2 | layers/segment (segmented backward); `--no-segment` to disable |
|
|
41
|
+
| `--save-every` | off | mountable checkpoint every N steps |
|
|
42
|
+
| `--resume <dir>` | — | warm-start LoRA weights from a checkpoint/adapter |
|
|
43
|
+
| `--no-flash` / `--no-prefix` | (on) | drop the flash Metal head / prefix-sharing |
|
|
44
|
+
| `--adapter <dir>` | `~/.cache/mlx-bun/mlx-bun-finetunes/<method>-<model>` | output dir |
|
|
45
|
+
|
|
46
|
+
> **Long runs:** launch detached from your own shell (`nohup … &`). Foreground is fine for
|
|
47
|
+
> short runs; the command blocks and streams until done.
|
|
48
|
+
|
|
49
|
+
## The launcher (env-var alternative)
|
|
50
|
+
|
|
51
|
+
`scripts/train-orpo.ts` is the same stack driven by env vars — handy for scripting:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
MODEL=/path/to/snapshot DATA=/path/to/datadir bun scripts/train-orpo.ts
|
|
55
|
+
```
|
|
56
|
+
- `DATA` is a directory with `train.jsonl` (+ optional `valid.jsonl`).
|
|
57
|
+
- Auto-detects e4b/Gemma and sets the training env flags, same as the CLI.
|
|
19
58
|
|
|
20
59
|
### Preconfigured defaults (override via env)
|
|
21
60
|
| env | default | meaning |
|
|
@@ -17,18 +17,22 @@ for the long-context memory mechanism.
|
|
|
17
17
|
|
|
18
18
|
## Entry points
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
Training is reachable several ways — all of them drive the **same** runner
|
|
21
|
+
(`finetuneRunner` in `src/train/job.ts`):
|
|
22
22
|
|
|
23
23
|
| Path | How | Use when |
|
|
24
24
|
|---|---|---|
|
|
25
|
-
| **
|
|
25
|
+
| **CLI** | `mlx-bun train <model> --data <dir> [options]` — foreground, full ORPO stack on by default; auto-detects e4b/Gemma + sets its env; `--save-every`, `--resume`, `--dry-run`; streams loss to the terminal. `mlx-bun help train` for all flags. Run long jobs detached: `nohup mlx-bun train … &` | **The default for CLI users** |
|
|
26
|
+
| **Web UI** | `mlx-bun serve`, open `/finetune` — pick model → dataset → hyperparameters → train; watch live train/val loss; merge/export the adapter | Interactive, in the browser |
|
|
26
27
|
| **HTTP API** | `POST /api/finetune/submit` (job id + SSE events); `POST /api/finetune/inspect-dataset` to probe a file; `POST /api/finetune/merge` to fold an adapter into base weights | Scripted / remote |
|
|
27
|
-
| **
|
|
28
|
+
| **ORPO launcher** | [`scripts/train-orpo.ts`](../../scripts/train-orpo.ts) — the same stack driven by env vars (`MODEL=`, `DATA=`, `RANK=`, `RESUME=`, …). See [orpo-quickstart](./orpo-quickstart.md) | Env-var scripting |
|
|
28
29
|
| **Shell recipe** | [`scripts/ft-e4b-v2.sh`](../../scripts/ft-e4b-v2.sh) `probe`\|`train` — the actual e4b run we use; sets the required env (see [What we actually run](#what-we-actually-run-the-e4b-recipe)) | Reproducing our e4b fine-tune |
|
|
29
|
-
| **ORPO launcher** | [`scripts/train-orpo.ts`](../../scripts/train-orpo.ts) — preconfigured ORPO with the **full stack on by default** (flash-CCE head + segmented backward + prefix-sharing); auto-detects e4b and sets its env; `RESUME=<dir>` warm-starts. See [orpo-quickstart](./orpo-quickstart.md) | Running an ORPO fine-tune |
|
|
30
30
|
| **Library** | `import { finetuneRunner } from "./src/train/job"` and call it with a config + emitter | Embedding training in your own TS |
|
|
31
31
|
|
|
32
|
+
The CLI and the launcher run the runner **in-process / foreground** (stream to the
|
|
33
|
+
terminal, `nohup`-able); the Web UI and HTTP API run it as a **subprocess job** (crash
|
|
34
|
+
isolation + a single GPU lease across concurrent server requests).
|
|
35
|
+
|
|
32
36
|
### Quick start (HTTP API)
|
|
33
37
|
|
|
34
38
|
```bash
|
package/package.json
CHANGED