mlx-bun 0.0.4 → 0.0.5
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 +166 -93
- package/docs/design/adapters-end-to-end.md +50 -46
- package/docs/design/compat-cli-surface-design.md +10 -8
- package/docs/design/docs-reorg-plan.md +14 -1
- package/docs/design/hlg-sampling.md +12 -2
- package/docs/design/memory-system.md +1 -1
- package/docs/design/optimization_plan.md +4 -2
- package/docs/design/orpo-future-enhancements.md +298 -0
- package/docs/design/orpo-training.md +1199 -0
- package/docs/design/parallel-slots.md +34 -26
- package/docs/design/runtime-isolation.md +1 -1
- package/docs/design/segmented-backward-training.md +58 -39
- package/docs/design/tauri-desktop-app.md +127 -0
- package/docs/{segmented-backward-handoff.md → investigations/segmented-backward-handoff.md} +1 -1
- package/docs/investigations/starter-model-port-handoff.md +32 -13
- package/docs/investigations/steel-flash-cce-handoff.md +431 -0
- package/docs/planning/IDEAS.md +29 -0
- package/docs/planning/PRODUCT_ROADMAP.md +147 -27
- package/docs/planning/chunk-task-roadmap.md +1 -1
- package/docs/planning/journal.md +161 -0
- package/docs/reference/distribution.md +61 -5
- package/docs/reference/library-api.md +29 -14
- package/docs/reference/orpo-quickstart.md +91 -0
- package/docs/reference/server-api.md +105 -7
- package/docs/reference/server-config.md +12 -5
- package/docs/reference/training.md +292 -0
- package/package.json +1 -1
- package/src/eval/ifeval.ts +138 -0
- package/src/eval/ultrafeedback.ts +60 -0
- package/src/lora.ts +12 -7
- package/src/mlx/metal-kernel.ts +18 -4
- package/src/model/flash-attention.ts +32 -16
- package/src/model/fused-geglu-kernel.ts +111 -0
- package/src/model/gemma4-base.ts +39 -1
- package/src/model/gemma4.ts +196 -21
- package/src/model/minicpm5.ts +44 -3
- package/src/server.ts +3 -3
- package/src/train/dataset.ts +18 -3
- package/src/train/flash-cce.ts +1162 -0
- package/src/train/job.ts +26 -2
- package/src/train/lora-params.ts +53 -4
- package/src/train/loss.ts +941 -0
- package/src/train/optimizer.ts +10 -3
- package/src/train/prefix-shared.ts +435 -0
- package/src/train/rank.ts +36 -0
- package/src/train/segmented.ts +1148 -3
- package/src/train/steel-qmm-header.ts +1461 -0
- package/src/train/trainer.ts +509 -52
- package/src/web/app.html +21 -5
- /package/{docs → src/assets}/curve-designer.html +0 -0
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
(src/ and scripts/ moves — they change import paths / script references
|
|
5
5
|
and need a test run, so deferred to their own pass). Authored 2026-06-14.
|
|
6
6
|
|
|
7
|
+
**Execution state (verified 2026-06-17):**
|
|
8
|
+
- P0 #1 — **DONE**: `AGENTS.md` is a symlink → `CLAUDE.md`; drift risk resolved.
|
|
9
|
+
- P0 #2 — **DONE**: `benchmarks/RESULTS.md` created; old committed benchmark artifacts `git rm`'d. Root untracked spew (`benchmarks-h2h-2026-06-13-*` etc.) is now cleaned — no untracked report files found at root.
|
|
10
|
+
- P0 #3 — **DONE**: root HTML/md spew cleared (no `benchmarks-h2h-*.md`, `benchmark-report-*.html`, `kernel-fusion-report-*.html`, or `project-review.html` at root).
|
|
11
|
+
- P0 #4 — **DONE**: hardcoded test count removed from CLAUDE.md (no "All 72 tests" present).
|
|
12
|
+
- P1 #5 — **DONE**: `STATUS.md` exists at root.
|
|
13
|
+
- P1 #6 — **DONE**: `docs/planning/` exists with `IDEAS.md`, `PRODUCT_ROADMAP.md`, `ResearchTopics.md`, `journal.md`.
|
|
14
|
+
- P1 #7 — open (optimization_plan.md still lives in docs/design/ as a standalone).
|
|
15
|
+
- P1 #8 — status unknown (archive/ not verified in this pass).
|
|
16
|
+
- P2 #9 — **NOT DONE**: `harness-pi.ts`, `pi-provider.ts`, `pi-terminal.ts`, `pi-web.ts` still live flat in `src/` root (no `src/pi/` subdirectory).
|
|
17
|
+
- P2 #10 — **PARTIALLY DONE**: ~57 one-off research/debug scripts moved to `scripts/experiments/`; production + tooling + bench-* + regen-* + parity-* + gen-model + eval/serve remain at `scripts/` root (the full bench/regen/build split was not applied).
|
|
18
|
+
- P2 #11 — **DONE**: CLI vocabulary reconciled.
|
|
19
|
+
|
|
7
20
|
**Scope & ethos.** Private repo, single author + agents, MLX-by-identity.
|
|
8
21
|
No OSS/community scaffolding (CONTRIBUTING, issue templates, badges) —
|
|
9
22
|
out of scope by design. **Prefer move / consolidate over delete.** The
|
|
@@ -127,7 +140,7 @@ files, not benchmark output. Leave entirely alone.
|
|
|
127
140
|
| 5 | Add `STATUS.md` (or a pinned top section in PLAN.md) holding only live handoff: current state + next action + blockers. Move PLAN.md's `NEXT UP` / `NEXT SESSION PICKUP` / `SESSION SWEEP` blocks into it. | Separates transient "do this next" from 2,475 lines of durable phase history; gives agents one entry point. |
|
|
128
141
|
| 6 | Create `docs/planning/` and move `PRODUCT_ROADMAP.md`, `IDEAS.md`, `ResearchTopics.md`, `journal.md` under it. Leave only README + PLAN.md (+ STATUS.md) at root. Add a ~6-line "doc map" to README. | Root is currently a 6-file planning thicket; extends the taxonomy that already works in `docs/`. |
|
|
129
142
|
| 7 | Pick one home for the optimization plan: fold `docs/design/optimization_plan.md` into PLAN.md Phase A–E, or have PLAN.md link out to it as canonical. | Two homes for the same plan = drift. |
|
|
130
|
-
| 8 | Resolve the archive overlap: `archive/pi-builtin-investigation.html` duplicates [docs/investigations/pi-builtin-investigation.md](../investigations/pi-builtin-investigation.md). Decide whether `archive/` (tracked;
|
|
143
|
+
| 8 | Resolve the archive overlap: `archive/pi-builtin-investigation.html` duplicates [docs/investigations/pi-builtin-investigation.md](../investigations/pi-builtin-investigation.md). Decide whether `archive/` (tracked; 2 HTML files: `mlx-bun-lab-report.html` + `wwdc-mlx-bun.html`) should be tracked at all, or moved to an ignored location. Keep the `.md`, drop the duplicate. | Stops committing heavyweight generated HTML; one source per investigation. |
|
|
131
144
|
|
|
132
145
|
## P2 — Polish (optional)
|
|
133
146
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# HLG sampling — a piecewise tone curve on the logits
|
|
2
2
|
|
|
3
|
-
Status: **
|
|
3
|
+
Status: **implemented, found neutral, superseded by Curve Designer** — `applyHlg` shipped in `src/sampler.ts`; investigation found HLG a wash vs plain temperature; the Curve Designer (`src/curve-sampler.ts`) is its v2 replacement. See [docs/investigations/hlg-sampling-investigation.md](../investigations/hlg-sampling-investigation.md) for findings. This doc remains as the design rationale.
|
|
4
4
|
Owner: sampling layer (`src/sampler.ts`)
|
|
5
5
|
Default: **off** (`--hlg-sampling off` = today's temperature sampler, untouched)
|
|
6
6
|
|
|
@@ -10,6 +10,12 @@ Default: **off** (`--hlg-sampling off` = today's temperature sampler, untouched)
|
|
|
10
10
|
> [docs/reference/server-config.md](../reference/server-config.md) and
|
|
11
11
|
> [docs/reference/server-api.md](../reference/server-api.md) once shipped.
|
|
12
12
|
|
|
13
|
+
## Outcome / Retrospective
|
|
14
|
+
|
|
15
|
+
HLG was implemented through **Piece 2** (pure curve + neutrality tests + serial wiring, `applyHlg` live in `src/sampler.ts`). Evaluation found it **neutral vs plain temperature** — no measurable gain in diversity or quality at matched output entropy. As a result, HLG is **superseded by the Curve Designer** (`src/curve-sampler.ts`), which generalizes the same region-shaping idea with an editable control-point interface and cleaner UX. Full empirical findings are in [docs/investigations/hlg-sampling-investigation.md](../investigations/hlg-sampling-investigation.md).
|
|
16
|
+
|
|
17
|
+
The sections below (Goal through Correctness) remain as design rationale. Pieces 3–6 of Phasing and the Open Questions were never executed; see the [Phasing](#phasing-the-piece-n-increments) and [Open Questions](#open-questions--risks) sections for historical context.
|
|
18
|
+
|
|
13
19
|
## Goal
|
|
14
20
|
|
|
15
21
|
Add a sampling transform — gated entirely behind `--hlg-sampling on` and a
|
|
@@ -290,6 +296,8 @@ quality + diversity rather than parity — except for the degeneracy gates, whic
|
|
|
290
296
|
|
|
291
297
|
## Phasing (the "piece N" increments)
|
|
292
298
|
|
|
299
|
+
> **Historical / superseded.** Pieces 1–2 shipped. Pieces 3–6 were never executed — HLG was evaluated after Piece 2 and found neutral; the feature was superseded by the Curve Designer before Piece 3 began.
|
|
300
|
+
|
|
293
301
|
1. **Pure curve + neutrality tests** — `applyHlg(lp, params)` in `sampler.ts`,
|
|
294
302
|
top-anchored pivot only; degeneracy + monotonicity tests green across all
|
|
295
303
|
four models. (No flag wired yet — pure, fully-tested function.)
|
|
@@ -305,6 +313,8 @@ quality + diversity rather than parity — except for the degeneracy gates, whic
|
|
|
305
313
|
|
|
306
314
|
## Open questions / risks
|
|
307
315
|
|
|
316
|
+
> **Historical / superseded.** These questions were open at the time of design. HLG evaluation concluded with a neutral verdict before they were answered; the Curve Designer supersedes this path. Questions are preserved as design context.
|
|
317
|
+
|
|
308
318
|
- **Default knob values** are calibration-pending — set empirically by the
|
|
309
319
|
Piece 5 sweep, not guessed. Initial exploration points: `c ≈ 6`, `w ≈ 4`,
|
|
310
320
|
`β_h ≈ 4`, `β_t ≈ 6` nats; confirm or replace with measured values.
|
|
@@ -337,4 +347,4 @@ for it. That a transfer function is standard practice in another domain says
|
|
|
337
347
|
nothing about whether it helps token sampling — that is decided only by
|
|
338
348
|
measurement (the KL characterization, the capability guardrail, and the
|
|
339
349
|
diversity lens in [Correctness](#correctness--evaluation)). The full empirical
|
|
340
|
-
write-up
|
|
350
|
+
write-up is in [docs/investigations/hlg-sampling-investigation.md](../investigations/hlg-sampling-investigation.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Memory system — design sketch
|
|
2
2
|
|
|
3
|
-
Status: **
|
|
3
|
+
Status: **aspirational — not scheduled; no PLAN.md phase** (nothing built yet; capture prerequisite already landed). Note: `src/jobs/` is the generic background-job runner for quantize/finetune/fit — it is NOT this memory system.
|
|
4
4
|
Owner: assistant layer (pi-web / pi-terminal / web-tools / jobs)
|
|
5
5
|
Default: **off** until the user opts in (vault + schedule are explicit, consented actions)
|
|
6
6
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# mlx-bun: gemma4.ts decompose & optimize — action plan
|
|
2
2
|
|
|
3
3
|
> This is the canonical **design/rationale** for the optimization work.
|
|
4
|
-
> Live **status/tracking** (checkboxes, findings) lives in
|
|
5
|
-
>
|
|
4
|
+
> Live **status/tracking** (checkboxes, findings) for Phases A–D lives in
|
|
5
|
+
> PLAN-archive.md; Phase E remains in PLAN.md.
|
|
6
|
+
|
|
7
|
+
**Status (verified 2026-06-17):** Phases A–D are **DONE** (`[x]` in PLAN-archive.md, all landed 2026-06-10/11). Phase E — per-config fused decode-SDPA kernel — is **in progress** (`[~]` in PLAN.md, started 2026-06-11; fused kernel exists and is quality-gated per PLAN.md Step 4). The body of this doc remains the durable design rationale; completed phase checkboxes and findings live in PLAN-archive.md; Phase E tracking lives in PLAN.md.
|
|
6
8
|
|
|
7
9
|
**Premise (confirmed from the source read):** the current engine sits at
|
|
8
10
|
parity-to-ahead vs mlx-lm on the 12B *while still interpreting the decode
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# ORPO future enhancements
|
|
2
|
+
|
|
3
|
+
Performance-first follow-on ideas for mlx-bun's ORPO trainer, plus a short
|
|
4
|
+
appendix of objective variants that may be useful once the runtime path is
|
|
5
|
+
comfortable.
|
|
6
|
+
|
|
7
|
+
> **LANDED since this was written (2026-06-19):** the top items here are now in
|
|
8
|
+
> production — **Priority 1 (Cut Cross Entropy / fused linear CE)** and **§1
|
|
9
|
+
> (vocab-axis chunked CE)** shipped as the `[M,vocab]`-free **flash-CCE Metal head**
|
|
10
|
+
> (verbatim MLX steel GEMM, fwd+bwd; `orpo_flash_ce`), and **§3 (prefix sharing in the
|
|
11
|
+
> trainer)** shipped as `orpo_prefix_shared`, composed with the flash head AND the
|
|
12
|
+
> segmented backward for MiniCPM5 **and** e4b. See [training.md](../reference/training.md),
|
|
13
|
+
> [orpo-quickstart.md](../reference/orpo-quickstart.md), and the live record
|
|
14
|
+
> [steel-flash-cce-handoff.md](../investigations/steel-flash-cce-handoff.md). The
|
|
15
|
+
> remaining sections (§2, §4–§9, the objective variants) are still genuinely future.
|
|
16
|
+
|
|
17
|
+
Product relevance: ORPO is not only a researcher feature. It is the path to
|
|
18
|
+
small, downloadable task adapters that make the default local assistant better
|
|
19
|
+
without forcing every user onto a larger base model. The performance work below
|
|
20
|
+
matters when it turns adapter training from a fragile lab run into a repeatable
|
|
21
|
+
part of the mlx-bun product loop.
|
|
22
|
+
|
|
23
|
+
## Priority 1: Cut Cross Entropy / Liger Fused Linear CE
|
|
24
|
+
|
|
25
|
+
The top ORPO performance priority is a Cut Cross Entropy / Liger-style Fused
|
|
26
|
+
Linear Cross Entropy path for the LM head. This should be treated as the next
|
|
27
|
+
major kernel project before lower-impact objective variants.
|
|
28
|
+
|
|
29
|
+
Why it is first:
|
|
30
|
+
|
|
31
|
+
- Gemma's vocab is huge, so materializing `[tokens, vocab]` logits dominates
|
|
32
|
+
peak memory in ORPO.
|
|
33
|
+
- ORPO needs chosen and rejected response log-probs, so the head cost is paid
|
|
34
|
+
twice unless the trainer can share or fuse more work.
|
|
35
|
+
- Token-axis chunking already helps fit the run, but vocab-axis chunking cuts
|
|
36
|
+
the largest remaining logits allocation at the source.
|
|
37
|
+
- The same fused engine can support SFT, DPO, ORPO, SimPO, IPO, and hinge-style
|
|
38
|
+
preference losses once it exposes mean log-prob with a custom upstream
|
|
39
|
+
cotangent.
|
|
40
|
+
- A Metal fused version can avoid extra MLX dispatches and intermediate memory
|
|
41
|
+
traffic in the hot training path.
|
|
42
|
+
|
|
43
|
+
The implementation target is not just "chunk logits"; it is a fused log-prob
|
|
44
|
+
head that computes the linear projection, online vocab logsumexp, target-logit
|
|
45
|
+
gather, loss reduction, and backward replay without ever storing full logits.
|
|
46
|
+
|
|
47
|
+
## Performance focus
|
|
48
|
+
|
|
49
|
+
### 1. Vocab-axis chunked cross entropy
|
|
50
|
+
|
|
51
|
+
The current ORPO head optimization is **token-axis chunking**: split the response
|
|
52
|
+
positions into chunks, run the LM head on `[chunk, vocab]`, and rematerialize
|
|
53
|
+
those chunks in the backward. That bounds peak logits by `chunk_tokens * vocab`.
|
|
54
|
+
|
|
55
|
+
The complementary trick is **vocab-axis chunking**: split the vocabulary instead,
|
|
56
|
+
run the head on `[tokens, vocab_chunk]`, and combine chunks with an
|
|
57
|
+
online-logsumexp recurrence. This is the idea behind **Cut Cross Entropy** and
|
|
58
|
+
Liger-style **Fused Linear Cross Entropy**. This is priority 1 for the ORPO
|
|
59
|
+
performance roadmap.
|
|
60
|
+
|
|
61
|
+
Exact forward sketch:
|
|
62
|
+
|
|
63
|
+
1. For each vocab chunk, compute partial logits `h @ W_chunk`.
|
|
64
|
+
2. Gather the target logit only if the target id falls inside this chunk.
|
|
65
|
+
3. Maintain per-token online `logsumexp` across vocab chunks.
|
|
66
|
+
4. At the end, `logp = target_logit - lse`.
|
|
67
|
+
|
|
68
|
+
Backward reconstructs the softmax chunk-by-chunk from the saved per-token LSE
|
|
69
|
+
and never materializes full `[tokens, vocab]` logits. For Gemma's 262k vocab,
|
|
70
|
+
this is likely the next real moat after segmented backward.
|
|
71
|
+
|
|
72
|
+
Two implementation levels:
|
|
73
|
+
|
|
74
|
+
- Pure MLX chunked VJP first: exact, easier to validate, probably enough to fit
|
|
75
|
+
longer contexts.
|
|
76
|
+
- Metal fused kernel later: fuse quantized head matmul + online LSE + target
|
|
77
|
+
gather + backward. This is the high-performance version.
|
|
78
|
+
|
|
79
|
+
### 2. Fold ORPO gradients into the fused CE path
|
|
80
|
+
|
|
81
|
+
ORPO needs two mean log-probs, `lw` and `lr`, then applies the odds-ratio loss.
|
|
82
|
+
For a fused CE/log-prob kernel, the clean design is to feed the upstream scalar
|
|
83
|
+
gradient for each branch back into the chunked head backward. In other words,
|
|
84
|
+
the fused head should expose "mean logp with custom upstream cotangent", not
|
|
85
|
+
only cross-entropy.
|
|
86
|
+
|
|
87
|
+
This avoids materializing logits while still letting ORPO, SimPO, and future
|
|
88
|
+
preference losses share the same head engine.
|
|
89
|
+
|
|
90
|
+
### 3. Prefix sharing in the trainer
|
|
91
|
+
|
|
92
|
+
`src/train/prefix-shared.ts` already proves the MiniCPM5 shared-prefix idea:
|
|
93
|
+
one forward over `[prompt; chosen; rejected]` with block-sparse masking and
|
|
94
|
+
block-wise RoPE. It is not yet wired into `orpoLoop`.
|
|
95
|
+
|
|
96
|
+
Next useful path:
|
|
97
|
+
|
|
98
|
+
- Add an opt-in flag, for example `orpo_prefix_sharing`.
|
|
99
|
+
- Gate it by prompt/response ratio so short-prompt data stays on the simple
|
|
100
|
+
two-forward path.
|
|
101
|
+
- Wire MiniCPM5 first.
|
|
102
|
+
- Port e4b later, where sliding-window masks, donor-KV reuse, and per-layer
|
|
103
|
+
input make it trickier.
|
|
104
|
+
|
|
105
|
+
### 4. Sequence-chunk the MLP sub-block
|
|
106
|
+
|
|
107
|
+
The MLP is per-token independent. Inside segmented backward, run MLP blocks over
|
|
108
|
+
sequence chunks so the GeGLU/SwiGLU intermediate is `chunk_tokens * d_ff` instead
|
|
109
|
+
of `seq_len * d_ff`.
|
|
110
|
+
|
|
111
|
+
This stacks with depth segmentation and vocab-axis CE:
|
|
112
|
+
|
|
113
|
+
`layer segment -> layer -> MLP seq chunk -> vocab chunk`
|
|
114
|
+
|
|
115
|
+
Attention cannot be chunked this naively, but the MLP can.
|
|
116
|
+
|
|
117
|
+
### 5. Rotor / budget-aware segment planner
|
|
118
|
+
|
|
119
|
+
Replace fixed `segment_size` with a planner that chooses segment boundaries from
|
|
120
|
+
estimated activation bytes and a target memory budget. The planner should isolate
|
|
121
|
+
expensive full-attention layers and larger MLP regions instead of treating every
|
|
122
|
+
layer equally.
|
|
123
|
+
|
|
124
|
+
This is the practical version of rotor/checkpoint scheduling for this codebase:
|
|
125
|
+
less hand tuning, better fit at 8K+.
|
|
126
|
+
|
|
127
|
+
### 6. Faster flash-attention backward
|
|
128
|
+
|
|
129
|
+
The custom flash backward is now correctness-useful but too slow. A serious
|
|
130
|
+
performance version should mimic MLX's tuned SDPA style: simdgroup tiling,
|
|
131
|
+
larger threadgroups, and FA2-style backward that stores only per-row LSE and
|
|
132
|
+
recomputes attention probabilities.
|
|
133
|
+
|
|
134
|
+
This matters most when `ops.sdpa` backward's O(L^2) memory is the blocker.
|
|
135
|
+
|
|
136
|
+
### 7. Fused LoRA epilogue
|
|
137
|
+
|
|
138
|
+
Today LoRA adds multiple ops after the quantized base matmul:
|
|
139
|
+
|
|
140
|
+
`base(x) + scale * ((x @ A) @ B)`
|
|
141
|
+
|
|
142
|
+
A fused dequant-GEMM + LoRA epilogue would reduce dispatches and intermediate
|
|
143
|
+
traffic. Backward can recompute the cheap rank projection:
|
|
144
|
+
|
|
145
|
+
- `h = x @ A`
|
|
146
|
+
- `dB = scale * h.T @ dy`
|
|
147
|
+
- `dA = scale * x.T @ (dy @ B.T)`
|
|
148
|
+
- `dx = dy @ W.T + scale * (dy @ B.T) @ A.T`
|
|
149
|
+
|
|
150
|
+
This is more kernel work, but it benefits SFT, DPO, ORPO, and serving with
|
|
151
|
+
adapters.
|
|
152
|
+
|
|
153
|
+
### 8. Shape-specialized compile for training graphs
|
|
154
|
+
|
|
155
|
+
Shapeless compile is mainly required when a dimension changes every replay, such
|
|
156
|
+
as autoregressive decode with a growing KV cache:
|
|
157
|
+
|
|
158
|
+
`active_kv_len = prompt_len + generated_tokens`
|
|
159
|
+
|
|
160
|
+
ORPO training is different. Each dataset row already has concrete lengths:
|
|
161
|
+
|
|
162
|
+
- `P`: prompt length
|
|
163
|
+
- `Rc`: chosen response length
|
|
164
|
+
- `Rr`: rejected response length
|
|
165
|
+
- `T`: prefix-shared length, `P + Rc + Rr`
|
|
166
|
+
|
|
167
|
+
That means ORPO does not need shapeless compile in the same way decode does. A
|
|
168
|
+
training graph can be compiled for an exact `(P, Rc, Rr)` shape, or for a padded
|
|
169
|
+
length bucket, and cached under a key that includes the model, flags, segment
|
|
170
|
+
plan, token/vocab chunk sizes, and objective mode.
|
|
171
|
+
|
|
172
|
+
This is especially relevant for e4b prefix-sharing: the proof path should stay
|
|
173
|
+
eager first, but a later L3 speed tier can use shape-specialized compiled graphs
|
|
174
|
+
for known training shapes instead of requiring every primitive to support
|
|
175
|
+
shapeless output-shape inference.
|
|
176
|
+
|
|
177
|
+
The deeper L3 framing is **model-checksum-specific specialization**. For a fixed
|
|
178
|
+
model snapshot, we know the architecture, layer count, hidden sizes, quantization
|
|
179
|
+
layout, RoPE geometry, vocab, layer types, KV-sharing pattern, and supported
|
|
180
|
+
context budget in advance. It is acceptable to spend much more work up front if
|
|
181
|
+
that buys a faster hot path later.
|
|
182
|
+
|
|
183
|
+
Practical version:
|
|
184
|
+
|
|
185
|
+
- Treat the model snapshot checksum as part of the compilation key.
|
|
186
|
+
- Generate/compile/cache a table of exact graph variants for the shapes and
|
|
187
|
+
modes we care about.
|
|
188
|
+
- Bucket or pad examples into those graph variants when helpful.
|
|
189
|
+
- Fall back to eager only for shapes/modes not in the table.
|
|
190
|
+
|
|
191
|
+
This is effectively a lookup table of specialized execution plans: more compile
|
|
192
|
+
and analysis cost up front, less branching, shape handling, and dispatch overhead
|
|
193
|
+
at runtime.
|
|
194
|
+
|
|
195
|
+
### 9. Fixed-shape decode cache as a compile route
|
|
196
|
+
|
|
197
|
+
Decode is harder because the active KV length grows by one token per step. The
|
|
198
|
+
current shapeless-compile path exists to avoid recompiling for:
|
|
199
|
+
|
|
200
|
+
`L = 17, 18, 19, 20, ...`
|
|
201
|
+
|
|
202
|
+
A separate route is to make decode shapes fixed:
|
|
203
|
+
|
|
204
|
+
1. Preallocate K/V buffers to a known maximum context length.
|
|
205
|
+
2. Write the new token into a dynamic position.
|
|
206
|
+
3. Carry `active_length` / `write_position` as scalar array inputs.
|
|
207
|
+
4. Mask out inactive cache positions, or preferably use an
|
|
208
|
+
active-length-aware attention kernel that simply does not scan them.
|
|
209
|
+
|
|
210
|
+
The correctness version is "fixed cache + mask". The performance version is
|
|
211
|
+
"fixed cache + active-length-aware attention"; otherwise early decode steps may
|
|
212
|
+
pay for the full max context even when only a short prefix is active.
|
|
213
|
+
|
|
214
|
+
There is also a more aggressive specialization route: prebuild a graph table for
|
|
215
|
+
specific active lengths or length buckets. Even if that means many compiled graph
|
|
216
|
+
variants, the trade can be valid for a fixed model/checksum if compile cost is
|
|
217
|
+
paid once and reused. In that regime, the important design question is not "can
|
|
218
|
+
one shapeless graph infer every shape?" but "which finite set of exact shapes and
|
|
219
|
+
runtime states should we specialize for?"
|
|
220
|
+
|
|
221
|
+
If every op in a chosen route is compile-safe, the whole decode graph can become
|
|
222
|
+
a stable compiled graph. Custom Metal kernels still need to stay outside compiled
|
|
223
|
+
regions unless the route is intentionally eager/custom-kernel-only or MLX compile
|
|
224
|
+
can accept the primitive's output contract.
|
|
225
|
+
|
|
226
|
+
## Quality and robustness ideas
|
|
227
|
+
|
|
228
|
+
These are lower priority if the current goal is performance, but they are useful
|
|
229
|
+
once ORPO can run comfortably.
|
|
230
|
+
|
|
231
|
+
### SimPO
|
|
232
|
+
|
|
233
|
+
SimPO is a reference-free preference loss that uses the **average log-probability
|
|
234
|
+
of each response** as the reward and adds a target reward margin. It reuses the
|
|
235
|
+
same `lw` / `lr` values ORPO already computes.
|
|
236
|
+
|
|
237
|
+
Rough shape:
|
|
238
|
+
|
|
239
|
+
`loss = softplus(-beta * ((lw - lr) - gamma))`
|
|
240
|
+
|
|
241
|
+
where `gamma` is the desired chosen-vs-rejected margin. It should be cheap to add
|
|
242
|
+
as a sibling objective because the expensive part, response mean log-probs, is
|
|
243
|
+
already implemented.
|
|
244
|
+
|
|
245
|
+
### IPO
|
|
246
|
+
|
|
247
|
+
IPO means **Identity Preference Optimization**. It was introduced as a more
|
|
248
|
+
overfit-resistant alternative to the original DPO logistic objective. Instead of
|
|
249
|
+
driving preference probabilities toward hard 0/1 labels indefinitely, IPO uses a
|
|
250
|
+
smoother identity-style objective over preference gaps. The practical point:
|
|
251
|
+
when preference labels are noisy, IPO is often less eager to push margins to
|
|
252
|
+
extremes.
|
|
253
|
+
|
|
254
|
+
For mlx-bun, IPO is interesting mainly as a loss-family option after the shared
|
|
255
|
+
logp engine exists.
|
|
256
|
+
|
|
257
|
+
### Hinge / SLiC-style loss
|
|
258
|
+
|
|
259
|
+
The hinge loss is a margin-ranking objective:
|
|
260
|
+
|
|
261
|
+
`loss = max(0, margin - reward_gap)`
|
|
262
|
+
|
|
263
|
+
For preference training, `reward_gap` is usually the chosen score minus rejected
|
|
264
|
+
score. Once the gap exceeds the margin, that pair contributes no gradient. This
|
|
265
|
+
can be useful when you want to stop over-optimizing already-solved pairs and
|
|
266
|
+
focus compute on pairs whose preference margin is still too small.
|
|
267
|
+
|
|
268
|
+
### Label smoothing and robust weighting
|
|
269
|
+
|
|
270
|
+
Preference pairs are noisy. Cheap robustness knobs:
|
|
271
|
+
|
|
272
|
+
- `label_smoothing`: treat a pair as, for example, 95% chosen / 5% rejected
|
|
273
|
+
rather than a hard label.
|
|
274
|
+
- Per-example `weight`: let curated data carry confidence or score gap.
|
|
275
|
+
- Loss clipping / winsorization: cap extreme high-loss pairs so noisy examples
|
|
276
|
+
do not dominate gradients.
|
|
277
|
+
- Ambiguity filtering: skip pairs with tiny initial margin or near-identical
|
|
278
|
+
chosen/rejected responses.
|
|
279
|
+
|
|
280
|
+
### Better curation
|
|
281
|
+
|
|
282
|
+
The current UltraFeedback curation mostly extracts rows and filters by length.
|
|
283
|
+
Quality can likely improve by filtering for:
|
|
284
|
+
|
|
285
|
+
- Large enough chosen-vs-rejected score gap when available.
|
|
286
|
+
- Similar response length, or at least bounded length ratio.
|
|
287
|
+
- High semantic contrast but not unrelated answers.
|
|
288
|
+
- Instruction-following categories if IFEval is the target metric.
|
|
289
|
+
|
|
290
|
+
Data quality may beat objective cleverness on small ORPO runs.
|
|
291
|
+
|
|
292
|
+
## Short answer to "token vs vocab"
|
|
293
|
+
|
|
294
|
+
The thing to look for is **vocab-axis chunked / fused cross entropy**, especially
|
|
295
|
+
under the names **Cut Cross Entropy** or **Liger Fused Linear Cross Entropy**.
|
|
296
|
+
Token chunking slices `[tokens, vocab]` by rows. Vocab chunking slices it by
|
|
297
|
+
columns and combines chunks with online logsumexp so the full logits matrix is
|
|
298
|
+
never live.
|