mlx-bun 0.4.0 → 1.0.0
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 +1453 -88
- package/THIRD_PARTY_NOTICES.md +882 -0
- package/bin/mlx-bun.convert.mjs +4 -0
- package/bin/mlx-bun.fuse.mjs +4 -0
- package/bin/mlx-bun.generate.mjs +4 -0
- package/bin/mlx-bun.lora.mjs +4 -0
- package/bin/mlx-bun.mjs +13 -21
- package/bin/mlx-bun.server.mjs +4 -0
- package/bin/mlx-bun.upload.mjs +4 -0
- package/dist/web/app.js +9584 -0
- package/package.json +50 -53
- package/src/cli/ane-install.ts +76 -0
- package/src/cli/args.ts +179 -0
- package/src/cli/engine-entry.ts +68 -0
- package/src/cli/inference.ts +214 -0
- package/src/cli/job-entry.ts +97 -0
- package/src/cli/library-host.ts +117 -0
- package/src/cli/main.ts +57 -0
- package/src/cli/memory-engine.ts +163 -0
- package/src/cli/memory.ts +36 -0
- package/src/cli/mlx-lm-aliases.ts +309 -0
- package/src/cli/model-choice.ts +33 -0
- package/src/cli/model-selection.ts +84 -0
- package/src/cli/module-host.ts +11 -0
- package/src/cli/module-verbs.ts +50 -0
- package/src/cli/numerical-policy.ts +12 -0
- package/src/cli/perplexity.ts +89 -0
- package/src/cli/serve-host.ts +423 -0
- package/src/cli/serve-isolated.ts +217 -0
- package/src/cli/serve-options.ts +106 -0
- package/src/cli/serve-state.ts +205 -0
- package/src/cli/serve.ts +500 -0
- package/src/cli/served-model-host.ts +102 -0
- package/src/cli/server-entry.ts +148 -0
- package/src/cli/serving-unit.ts +177 -0
- package/src/{tui.ts → cli/terminal.ts} +13 -8
- package/src/cli/worker-adapters.ts +34 -0
- package/src/cli/worker-entry.ts +272 -0
- package/src/cli/worker-unit.ts +179 -0
- package/src/engine/adapter-operation.ts +26 -0
- package/src/engine/cache-services.ts +114 -0
- package/src/engine/completion.ts +39 -0
- package/src/engine/generation-gateway.ts +358 -0
- package/src/engine/index.ts +59 -0
- package/src/engine/kv-budget.ts +120 -0
- package/src/engine/media-preparation.ts +232 -0
- package/src/engine/model-binding.ts +24 -0
- package/src/engine/model-host.ts +513 -0
- package/src/engine/model-serving.ts +89 -0
- package/src/{serve → engine}/preparation.ts +10 -8
- package/src/{serve → engine}/session-completion-engine.ts +6 -6
- package/src/engine/telemetry.ts +109 -0
- package/src/hub/downloads.ts +116 -0
- package/src/jobs/db.ts +16 -7
- package/src/jobs/events.ts +29 -0
- package/src/jobs/executable.ts +5 -0
- package/src/jobs/host.ts +51 -0
- package/src/jobs/in-process.ts +55 -0
- package/src/{contracts/jobs.ts → jobs/protocol.ts} +7 -9
- package/src/jobs/runner.ts +150 -231
- package/src/jobs/service.ts +75 -0
- package/src/jobs/sse.ts +59 -23
- package/src/jobs/worker-memory.ts +46 -0
- package/src/jobs/worker-process.ts +163 -0
- package/src/jobs/worker-supervisor.ts +258 -0
- package/src/modules.ts +58 -0
- package/src/publishing/catalog-hub.ts +18 -0
- package/src/publishing/credentials.ts +31 -0
- package/src/publishing/upload.ts +37 -0
- package/src/residency/leased-adapters.ts +21 -0
- package/src/residency/local-records.ts +16 -0
- package/src/residency/model-residency.ts +405 -0
- package/src/residency/record-index.ts +28 -0
- package/src/residency/resident-estimate.ts +50 -0
- package/src/{anthropic.ts → server/anthropic.ts} +3 -81
- package/src/server/cache-routes.ts +36 -0
- package/src/{serve → server}/chat-request.ts +12 -10
- package/src/{serve → server}/chat-stage.ts +27 -22
- package/src/{client.ts → server/client.ts} +28 -2
- package/src/{serve → server}/completion-executor.ts +42 -42
- package/src/server/decision-body.ts +55 -0
- package/src/server/decision-routes.ts +134 -0
- package/src/server/decision-wire.ts +228 -0
- package/src/{serve → server}/discovery-routes.ts +42 -83
- package/src/server/generated-token-history.ts +57 -0
- package/src/{serve → server}/http.ts +25 -13
- package/src/{serve → server}/inference-request.ts +5 -5
- package/src/server/job-routes.ts +20 -0
- package/src/server/media-prompt.ts +63 -0
- package/src/server/memory-completion-client.ts +46 -0
- package/src/server/model-routes.ts +192 -0
- package/src/{serve → server}/openai-wire.ts +3 -2
- package/src/{serve → server}/pipeline.ts +1 -1
- package/src/server/prompt-builder.ts +13 -0
- package/src/server/prompt-contracts.ts +17 -0
- package/src/server/proxy-routes.ts +187 -0
- package/src/server/publishing-routes.ts +41 -0
- package/src/{serve → server}/request-plan.ts +8 -8
- package/src/{serve → server}/request-prep.ts +82 -68
- package/src/{serve → server}/responses-client.ts +30 -13
- package/src/{responses.ts → server/responses.ts} +12 -93
- package/src/server/routes.ts +205 -0
- package/src/server/start.ts +72 -0
- package/src/server/status-routes.ts +108 -0
- package/src/{serve → server}/text-completion-stage.ts +25 -4
- package/src/server/text-prompt.ts +12 -0
- package/src/{serve → server}/token-streams.ts +36 -38
- package/src/server/worker-routes.ts +311 -0
- package/src/storage/paths.ts +61 -0
- package/src/web/assets.ts +37 -0
- package/src/web/{src → browser}/api.ts +5 -23
- package/src/web/browser/chat-host.ts +17 -0
- package/src/web/browser/installed-panels.ts +6 -0
- package/src/web/browser/main.ts +55 -0
- package/src/web/browser/markdown.ts +279 -0
- package/src/web/browser/memory-panel.ts +9 -0
- package/src/web/browser/palette.ts +64 -0
- package/src/web/browser/protocol.ts +13 -0
- package/src/web/browser/shell.ts +467 -0
- package/src/web/{src → browser}/status.ts +32 -36
- package/src/web/build.ts +51 -0
- package/src/web/public/app.html +1054 -0
- package/src/web/{sw.js → public/sw.js} +4 -3
- package/src/web/{vendor → public/vendor}/hljs-theme.css +2 -2
- package/src/web/{vendor → public/vendor}/hljs.js +2 -2
- package/THIRD_PARTY_LICENSES.md +0 -51
- package/docs/README.md +0 -94
- package/docs/archive/hlg-sampling.md +0 -350
- package/docs/archive/investigations/audio-input-plan.md +0 -356
- package/docs/archive/investigations/chunk-finetune-experiment.md +0 -84
- package/docs/archive/investigations/colibri-glm52-port.md +0 -1486
- package/docs/archive/investigations/colibri-oracle-pin.md +0 -1284
- package/docs/archive/investigations/curve-bisector-routing.md +0 -123
- package/docs/archive/investigations/decode-roofline-lookagain.md +0 -348
- package/docs/archive/investigations/diffusion-gemma-port.md +0 -602
- package/docs/archive/investigations/dspark-handoff.md +0 -224
- package/docs/archive/investigations/expert-offload-single-user-moe.md +0 -292
- package/docs/archive/investigations/faithful-l1-consolidation.md +0 -310
- package/docs/archive/investigations/hlg-sampling-investigation.md +0 -970
- package/docs/archive/investigations/indexshare-performance-spike.md +0 -357
- package/docs/archive/investigations/kernel-perf-review-2026-07.md +0 -171
- package/docs/archive/investigations/lab-build-journal.md +0 -157
- package/docs/archive/investigations/minicpm5-decode-megakernel.md +0 -489
- package/docs/archive/investigations/openjarvis-fit.md +0 -198
- package/docs/archive/investigations/openwiki-evaluation.md +0 -249
- package/docs/archive/investigations/optimization_plan.md +0 -346
- package/docs/archive/investigations/orpo-base-uf-experiment-and-directions.md +0 -120
- package/docs/archive/investigations/orpo-flash-cce-pin-leak.md +0 -167
- package/docs/archive/investigations/orpo-uf-testing-handoff.md +0 -126
- package/docs/archive/investigations/pareto-specialized-runtime-findings.md +0 -92
- package/docs/archive/investigations/pi-builtin-investigation.md +0 -308
- package/docs/archive/investigations/pre-colibri-stabilization.md +0 -329
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +0 -1092
- package/docs/archive/investigations/segmented-backward-handoff.md +0 -148
- package/docs/archive/investigations/starter-model-port-handoff.md +0 -342
- package/docs/archive/investigations/steel-flash-cce-handoff.md +0 -489
- package/docs/archive/investigations/the-dreaming-handoff.md +0 -141
- package/docs/archive/investigations/trainer-validation-experiment.md +0 -173
- package/docs/archive/planning/chat-ui-vision-memory-first.md +0 -199
- package/docs/archive/planning/chat-ui-vision.md +0 -50
- package/docs/archive/planning/chunk-task-roadmap.md +0 -77
- package/docs/archive/planning/curve-sampler-research-plan.md +0 -331
- package/docs/archive/planning/journal.md +0 -316
- package/docs/archive/planning/memory-docs-and-dag-plan.md +0 -194
- package/docs/archive/planning/pi-integration-review.md +0 -178
- package/docs/archive/planning/release-notes-v0.0.10.md +0 -57
- package/docs/archive/planning/release-notes-v0.0.11.md +0 -239
- package/docs/archive/planning/release-notes-v0.0.12.md +0 -80
- package/docs/archive/planning/release-notes-v0.0.13.md +0 -77
- package/docs/archive/planning/release-notes-v0.0.9.md +0 -122
- package/docs/archive/planning/release-notes-v0.2.0.md +0 -49
- package/docs/archive/planning/release-notes-v0.3.0.md +0 -35
- package/docs/archive/planning/web-ui-pass-plan.md +0 -213
- package/docs/archive/planning/website-readme-pass-plan.md +0 -224
- package/docs/design/batching.md +0 -1841
- package/docs/design/decode-speed-program.md +0 -2349
- package/docs/design/dreaming-nightly-pipeline.md +0 -684
- package/docs/design/generic-model-support.md +0 -705
- package/docs/design/kv-cache.md +0 -824
- package/docs/design/orpo-training.md +0 -680
- package/docs/design/speculative-decoding.md +0 -1990
- package/docs/design/turboquant.md +0 -2212
- package/docs/design/unified-engine-frontier-plan.md +0 -1880
- package/docs/design/web-chat-redesign.md +0 -695
- package/docs/planning/PRODUCT_ROADMAP.md +0 -594
- package/docs/planning/ResearchTopics.md +0 -119
- package/docs/planning/release-notes-v0.4.0.md +0 -45
- package/docs/planning/video-series.md +0 -421
- package/docs/reference/benchmarks.md +0 -3613
- package/docs/reference/cli.md +0 -585
- package/docs/reference/distribution.md +0 -355
- package/docs/reference/environment.md +0 -398
- package/docs/reference/glossary.md +0 -39
- package/docs/reference/library-api.md +0 -674
- package/docs/reference/memory.md +0 -260
- package/docs/reference/models.md +0 -334
- package/docs/reference/server-api.md +0 -1775
- package/docs/reference/server-config.md +0 -846
- package/docs/reference/training.md +0 -462
- package/docs/reference/troubleshooting.md +0 -158
- package/src/admission-plan.ts +0 -18
- package/src/audio/conformer.ts +0 -647
- package/src/audio/decode.ts +0 -133
- package/src/audio/features.ts +0 -202
- package/src/audio/transcode.ts +0 -67
- package/src/backends/mlx/assistant-target.ts +0 -46
- package/src/backends/mlx/autoregressive-draft-rows.ts +0 -120
- package/src/backends/mlx/autoregressive.ts +0 -108
- package/src/backends/mlx/batch-group.ts +0 -1790
- package/src/backends/mlx/cache-attachment.ts +0 -67
- package/src/backends/mlx/cache-layout.ts +0 -31
- package/src/backends/mlx/checkpoint-state.ts +0 -53
- package/src/backends/mlx/continuation-persistence.ts +0 -106
- package/src/backends/mlx/continuation-request.ts +0 -43
- package/src/backends/mlx/continuation.ts +0 -77
- package/src/backends/mlx/diffusion.ts +0 -39
- package/src/backends/mlx/draft-target.ts +0 -32
- package/src/backends/mlx/evaluation.ts +0 -105
- package/src/backends/mlx/fill-append.ts +0 -45
- package/src/backends/mlx/full-kv-row-append.ts +0 -32
- package/src/backends/mlx/full-kv-row-donor.ts +0 -54
- package/src/backends/mlx/full-transitioning-kv-rows.ts +0 -65
- package/src/backends/mlx/gateway-binding.ts +0 -130
- package/src/backends/mlx/graph.ts +0 -38
- package/src/backends/mlx/kv-codec.ts +0 -9
- package/src/backends/mlx/kv-maintenance.ts +0 -133
- package/src/backends/mlx/kv-tensor-rows.ts +0 -212
- package/src/backends/mlx/memory-client.ts +0 -189
- package/src/backends/mlx/methods.ts +0 -90
- package/src/backends/mlx/model-prompt.ts +0 -164
- package/src/backends/mlx/model-serving.ts +0 -79
- package/src/backends/mlx/prefill-cohort.ts +0 -89
- package/src/backends/mlx/prefill-rows.ts +0 -200
- package/src/backends/mlx/prefill.ts +0 -33
- package/src/backends/mlx/projected-context-rows.ts +0 -72
- package/src/backends/mlx/request-policy.ts +0 -20
- package/src/backends/mlx/request-state-policy.ts +0 -41
- package/src/backends/mlx/rollback.ts +0 -89
- package/src/backends/mlx/row-sampling.ts +0 -49
- package/src/backends/mlx/serial-executor.ts +0 -242
- package/src/backends/mlx/speculative-group.ts +0 -359
- package/src/backends/mlx/speculative-round.ts +0 -161
- package/src/backends/mlx/speculative.ts +0 -114
- package/src/backends/mlx/state-rows.ts +0 -104
- package/src/backends/mlx/state-views.ts +0 -46
- package/src/backends/mlx/target-layout-capability.ts +0 -35
- package/src/backends/mlx/text-engine.ts +0 -73
- package/src/backends/mlx/transitioning-kv-rows.ts +0 -165
- package/src/chat-template.ts +0 -382
- package/src/cli.ts +0 -3168
- package/src/config.ts +0 -399
- package/src/contracts/completion.ts +0 -16
- package/src/contracts/execution.ts +0 -60
- package/src/contracts/generation.ts +0 -103
- package/src/contracts/host.ts +0 -7
- package/src/contracts/pi-provider.ts +0 -30
- package/src/contracts/pi.ts +0 -259
- package/src/contracts/prefix-cache.ts +0 -23
- package/src/contracts/resources.ts +0 -25
- package/src/contracts/sampling.ts +0 -23
- package/src/contracts/scheduling.ts +0 -45
- package/src/contracts/tsconfig.json +0 -14
- package/src/dataset/generators.ts +0 -1087
- package/src/dataset/index.ts +0 -12
- package/src/dataset/job.ts +0 -81
- package/src/dataset/llm.ts +0 -71
- package/src/dataset/registry.ts +0 -528
- package/src/diffusion/diffusion-generate.ts +0 -507
- package/src/download.ts +0 -505
- package/src/embed.ts +0 -77
- package/src/engine/admission.ts +0 -59
- package/src/engine/cancellation.ts +0 -38
- package/src/engine/engine.ts +0 -56
- package/src/engine/execution-coordinator.ts +0 -70
- package/src/engine/execution-plan.ts +0 -53
- package/src/engine/resources.ts +0 -53
- package/src/engine/scheduler.ts +0 -72
- package/src/engine/session.ts +0 -247
- package/src/eval/README.md +0 -130
- package/src/eval/ifeval.ts +0 -81
- package/src/eval/kl-prompts.ts +0 -29
- package/src/eval/kl.ts +0 -590
- package/src/eval/perplexity.ts +0 -164
- package/src/eval/quality-db.ts +0 -92
- package/src/eval/reference-logits.ts +0 -207
- package/src/eval/runner.ts +0 -124
- package/src/eval/score.ts +0 -49
- package/src/eval/tasks/bfcl.ts +0 -535
- package/src/eval/tasks/gsm8k.ts +0 -98
- package/src/eval/tasks/hashhop.ts +0 -139
- package/src/eval/tasks/humaneval.ts +0 -275
- package/src/eval/tasks/ifeval.ts +0 -525
- package/src/eval/tasks/mmlu.ts +0 -157
- package/src/eval/ultrafeedback.ts +0 -60
- package/src/evaldb.ts +0 -199
- package/src/expert-io.ts +0 -393
- package/src/expert-offload-build.ts +0 -77
- package/src/expert-offload.ts +0 -76
- package/src/expert-residency.ts +0 -1134
- package/src/expert-usage.ts +0 -415
- package/src/fill/echo-index.ts +0 -188
- package/src/fill/fill-session.ts +0 -452
- package/src/fill/proposal.ts +0 -68
- package/src/fill/schema-rows.ts +0 -506
- package/src/fill/tool-boundary.ts +0 -103
- package/src/fit.ts +0 -249
- package/src/generate.ts +0 -1346
- package/src/grammar.ts +0 -591
- package/src/harness-pi.ts +0 -184
- package/src/hf-push.ts +0 -628
- package/src/hub-rest.ts +0 -242
- package/src/index.ts +0 -63
- package/src/inference/denoising.ts +0 -17
- package/src/inference/draft-acceptance.ts +0 -41
- package/src/inference/draft-output.ts +0 -49
- package/src/inference/graph.ts +0 -33
- package/src/inference/prefill.ts +0 -39
- package/src/inference/rollback.ts +0 -7
- package/src/jobs/index.ts +0 -31
- package/src/jobs/job-entry.ts +0 -107
- package/src/jobs/task-client.ts +0 -19
- package/src/jobs/types.ts +0 -2
- package/src/kv-scheme.ts +0 -257
- package/src/kv-store.ts +0 -1099
- package/src/lab/README.md +0 -18
- package/src/lab/curve/curve-designer.html +0 -303
- package/src/lab/curve/curve-sampler.ts +0 -122
- package/src/lab/expert-trace/expert-trace.ts +0 -69
- package/src/lab/paged-kv/paged-kv-rows.ts +0 -83
- package/src/lab/paged-kv/paged-kv.ts +0 -270
- package/src/library.ts +0 -40
- package/src/lora.ts +0 -408
- package/src/md-modules.d.ts +0 -49
- package/src/media-fetch.ts +0 -442
- package/src/memory/article.ts +0 -558
- package/src/memory/chunk-validation.ts +0 -150
- package/src/memory/chunk.ts +0 -318
- package/src/memory/cluster.ts +0 -351
- package/src/memory/coverage.ts +0 -134
- package/src/memory/crosslink.ts +0 -0
- package/src/memory/db.ts +0 -253
- package/src/memory/entity.ts +0 -203
- package/src/memory/gate.ts +0 -187
- package/src/memory/history.ts +0 -152
- package/src/memory/ingest.ts +0 -335
- package/src/memory/model.ts +0 -183
- package/src/memory/normalize.ts +0 -671
- package/src/memory/parse.ts +0 -22
- package/src/memory/pipeline.ts +0 -275
- package/src/memory/prompts.ts +0 -65
- package/src/memory/query.ts +0 -334
- package/src/memory/reconcile.ts +0 -734
- package/src/memory/redirect.ts +0 -108
- package/src/memory/reindex.ts +0 -325
- package/src/memory/resolve.ts +0 -307
- package/src/memory/rest.ts +0 -245
- package/src/memory/route.ts +0 -272
- package/src/memory/schedule.ts +0 -165
- package/src/memory/stages.ts +0 -777
- package/src/memory/synthesize.ts +0 -1638
- package/src/memory/tools.ts +0 -749
- package/src/memory/vault.ts +0 -1194
- package/src/memory/wikify.ts +0 -1064
- package/src/mlx/array.ts +0 -388
- package/src/mlx/autograd.ts +0 -304
- package/src/mlx/checkpoint.ts +0 -149
- package/src/mlx/compile.ts +0 -168
- package/src/mlx/custom-vjp.ts +0 -188
- package/src/mlx/ffi.ts +0 -512
- package/src/mlx/materialize.ts +0 -23
- package/src/mlx/metal-kernel.ts +0 -159
- package/src/mlx/ops.ts +0 -1021
- package/src/mlx/quantized-rows.ts +0 -36
- package/src/mlx/token-bitmask.ts +0 -52
- package/src/mlx/turboquant-kv-decode.ts +0 -82
- package/src/mlx/turboquant-kv-inverse.ts +0 -122
- package/src/mlx/turboquant-ops.ts +0 -861
- package/src/mlx/turboquant-tables.ts +0 -237
- package/src/mmap.ts +0 -90
- package/src/model/artifact-identity.ts +0 -73
- package/src/model/batched-kv.ts +0 -235
- package/src/model/batched-mask.ts +0 -279
- package/src/model/batched-quant.ts +0 -212
- package/src/model/batched-quantized-kv.ts +0 -144
- package/src/model/batched-rotating-quant.ts +0 -353
- package/src/model/batched-rotating-state.ts +0 -149
- package/src/model/batched-rotating.ts +0 -338
- package/src/model/batched-row-storage.ts +0 -179
- package/src/model/batched-ssm.ts +0 -52
- package/src/model/batched-turboquant-kv.ts +0 -93
- package/src/model/compiled-decode.ts +0 -712
- package/src/model/decoded-kv-donor.ts +0 -12
- package/src/model/delayed-quantized-kv.ts +0 -72
- package/src/model/delayed-rotating-quantized-kv.ts +0 -93
- package/src/model/delayed-turboquant-kv.ts +0 -55
- package/src/model/diffusion-gemma.ts +0 -1026
- package/src/model/factory.ts +0 -189
- package/src/model/fingerprint.ts +0 -33
- package/src/model/flash-attention.ts +0 -694
- package/src/model/full-prefill-padding.ts +0 -56
- package/src/model/full-prefill-row.ts +0 -105
- package/src/model/gemma4-base.ts +0 -2351
- package/src/model/gemma4.ts +0 -1315
- package/src/model/generated/gemma4-12b.ts +0 -429
- package/src/model/generated/gemma4-26b.ts +0 -448
- package/src/model/generated/gemma4-e4b.ts +0 -717
- package/src/model/generated/index.ts +0 -18
- package/src/model/glm52-atlas.ts +0 -511
- package/src/model/glm52-cache.ts +0 -792
- package/src/model/glm52-config.ts +0 -262
- package/src/model/glm52-container.ts +0 -305
- package/src/model/glm52-coupling.ts +0 -388
- package/src/model/glm52-dsa.ts +0 -606
- package/src/model/glm52-expert-layout.ts +0 -253
- package/src/model/glm52-layout.ts +0 -152
- package/src/model/glm52-memory.ts +0 -500
- package/src/model/glm52-mla.ts +0 -792
- package/src/model/glm52-moe.ts +0 -223
- package/src/model/glm52-pilot.ts +0 -537
- package/src/model/glm52-reference.ts +0 -445
- package/src/model/glm52-residency.ts +0 -557
- package/src/model/glm52-resident-weights.ts +0 -135
- package/src/model/glm52-streamed-experts.ts +0 -453
- package/src/model/glm52-streamed-metal.ts +0 -610
- package/src/model/glm52-weights.ts +0 -503
- package/src/model/glm52.ts +0 -1132
- package/src/model/implementation.ts +0 -69
- package/src/model/kv-attention-view.ts +0 -67
- package/src/model/minicpm5.ts +0 -376
- package/src/model/profile.ts +0 -443
- package/src/model/qwen-append.ts +0 -12
- package/src/model/qwen-conv.ts +0 -52
- package/src/model/qwen3-delta-state.ts +0 -74
- package/src/model/qwen3-delta.ts +0 -451
- package/src/model/qwen3-moe.ts +0 -404
- package/src/model/qwen3-mrope.ts +0 -252
- package/src/model/qwen3.ts +0 -277
- package/src/model/qwen3_5-checkpoint.ts +0 -164
- package/src/model/qwen3_5.ts +0 -819
- package/src/model/rotating-kv-donor.ts +0 -35
- package/src/model/rotating-kv-layout.ts +0 -262
- package/src/model/rotating-row-transaction.ts +0 -35
- package/src/model/speculative-rotating-kv.ts +0 -101
- package/src/model/ssm-prefill-padding.ts +0 -39
- package/src/model/support.ts +0 -102
- package/src/model/trellis-balanced-scatter.ts +0 -51
- package/src/model/trellis-linear.ts +0 -672
- package/src/model/trellis-shared-m.ts +0 -151
- package/src/model/trellis-shared-scatter.ts +0 -144
- package/src/model/trellis-splitk-prefill.ts +0 -158
- package/src/model/trellis-tiled-prefill.ts +0 -136
- package/src/model/trellis-vector-expand.ts +0 -62
- package/src/model/trellis-wide-prefill.ts +0 -94
- package/src/model/turboquant-codec.ts +0 -83
- package/src/model/universal/archs.ts +0 -332
- package/src/model/universal/dense.ts +0 -612
- package/src/model/universal/modules.ts +0 -170
- package/src/model/universal/rope.ts +0 -381
- package/src/native/expert_io.c +0 -523
- package/src/native/frame_extract.swift +0 -140
- package/src/native-pack.ts +0 -162
- package/src/pi-provider.ts +0 -53
- package/src/pi-session.ts +0 -81
- package/src/pi-terminal.ts +0 -274
- package/src/pi-web.ts +0 -1932
- package/src/preflight.ts +0 -158
- package/src/prompt-cache.ts +0 -460
- package/src/quantize/allocator.ts +0 -627
- package/src/quantize/atomic-output.ts +0 -38
- package/src/quantize/calibration.ts +0 -181
- package/src/quantize/config-writer.ts +0 -145
- package/src/quantize/index.ts +0 -84
- package/src/quantize/job.ts +0 -162
- package/src/quantize/quantizer.ts +0 -666
- package/src/quantize/rotate.ts +0 -603
- package/src/quantize/safetensors-writer.ts +0 -249
- package/src/quantize/sensitivity.ts +0 -216
- package/src/quantize/trellis.ts +0 -586
- package/src/quantize/weight-transform.ts +0 -292
- package/src/registry.ts +0 -602
- package/src/runtime-config.ts +0 -81
- package/src/safetensors.ts +0 -158
- package/src/sampler.ts +0 -1011
- package/src/serve/admin-routes.ts +0 -184
- package/src/serve/assets/curve-terrain.html +0 -105
- package/src/serve/assets/training-inference-map.html +0 -575
- package/src/serve/aux-routes.ts +0 -132
- package/src/serve/batch-scheduler.ts +0 -8
- package/src/serve/checkpoint-identity.ts +0 -64
- package/src/serve/generation-gateway.ts +0 -496
- package/src/serve/isolate.ts +0 -609
- package/src/serve/kv-budget.ts +0 -14
- package/src/serve/lab-routes.ts +0 -341
- package/src/serve/lane-registry.ts +0 -45
- package/src/serve/model-admin-routes.ts +0 -155
- package/src/serve/model-binding.ts +0 -43
- package/src/serve/model-host.ts +0 -511
- package/src/serve/parent-application.ts +0 -56
- package/src/serve/prompt-response-trace.ts +0 -147
- package/src/serve/response-lease.ts +0 -24
- package/src/serve/session-search.ts +0 -326
- package/src/serve/static-routes.ts +0 -101
- package/src/server.ts +0 -1314
- package/src/spec/assistant-rows.ts +0 -92
- package/src/spec/assistant-source.ts +0 -126
- package/src/spec/cache-identity.ts +0 -22
- package/src/spec/deepspec-rows.ts +0 -29
- package/src/spec/deepspec-source.ts +0 -145
- package/src/spec/dflash-source.ts +0 -149
- package/src/spec/drafter.ts +0 -304
- package/src/spec/dspark/ab-stats.ts +0 -136
- package/src/spec/dspark/calibration.ts +0 -112
- package/src/spec/dspark/data-dflash.ts +0 -156
- package/src/spec/dspark/data.ts +0 -201
- package/src/spec/dspark/deepspec-module.ts +0 -776
- package/src/spec/dspark/generate-dflash.ts +0 -245
- package/src/spec/dspark/generate.ts +0 -290
- package/src/spec/dspark/loader.ts +0 -23
- package/src/spec/dspark/loss.ts +0 -213
- package/src/spec/dspark/module-dflash.ts +0 -560
- package/src/spec/dspark/module.ts +0 -540
- package/src/spec/dspark/quantize-drafter.ts +0 -91
- package/src/spec/dspark/sample.ts +0 -112
- package/src/spec/generate.ts +0 -276
- package/src/spec/glm52-mtp-graph.ts +0 -29
- package/src/spec/glm52-mtp-rows.ts +0 -132
- package/src/spec/glm52-mtp-source.ts +0 -317
- package/src/spec/glm52-mtp-state.ts +0 -33
- package/src/spec/ngram-source.ts +0 -204
- package/src/spec/projected-draft-rows.ts +0 -73
- package/src/spec/qwen-mtp-module.ts +0 -171
- package/src/spec/qwen-mtp-rows.ts +0 -219
- package/src/spec/qwen-mtp-source.ts +0 -383
- package/src/spec/qwen-mtp-state.ts +0 -30
- package/src/spec/serve-loop.ts +0 -585
- package/src/spec/source.ts +0 -215
- package/src/spec/two-model-rows.ts +0 -61
- package/src/spec/two-model.ts +0 -113
- package/src/ssd-cache.ts +0 -372
- package/src/ssd-durability.ts +0 -211
- package/src/tokenizer.ts +0 -335
- package/src/tool-approvals.ts +0 -131
- package/src/tool-call.ts +0 -695
- package/src/train/dataset.ts +0 -484
- package/src/train/diffusion-lora.ts +0 -216
- package/src/train/export.ts +0 -32
- package/src/train/flash-cce.ts +0 -1234
- package/src/train/forward.ts +0 -266
- package/src/train/fuse.ts +0 -243
- package/src/train/index.ts +0 -46
- package/src/train/job.ts +0 -205
- package/src/train/lora-params.ts +0 -254
- package/src/train/loss.ts +0 -1591
- package/src/train/merge.ts +0 -195
- package/src/train/optimizer.ts +0 -183
- package/src/train/prefix-shared.ts +0 -490
- package/src/train/rank.ts +0 -137
- package/src/train/segmented.ts +0 -1828
- package/src/train/steel-qmm-header.ts +0 -1461
- package/src/train/trainer.ts +0 -1221
- package/src/train/watch.ts +0 -542
- package/src/vision/diffusion-vision.ts +0 -620
- package/src/vision/embedder.ts +0 -168
- package/src/vision/preprocess.ts +0 -247
- package/src/vision/prompt.ts +0 -373
- package/src/vision/qwen3vl-preprocess.ts +0 -409
- package/src/vision/qwen3vl-prompt.ts +0 -196
- package/src/vision/qwen3vl-tower.ts +0 -555
- package/src/vision/siglip.ts +0 -599
- package/src/vision/video-frames.ts +0 -148
- package/src/web/app.html +0 -2143
- package/src/web/app.js +0 -5703
- package/src/web/skills/memory/SKILL.md +0 -120
- package/src/web/skills/web-research/SKILL.md +0 -46
- package/src/web/skills.ts +0 -97
- package/src/web/src/adapters-panel.ts +0 -288
- package/src/web/src/assistant.ts +0 -451
- package/src/web/src/chat.ts +0 -1057
- package/src/web/src/composer.ts +0 -959
- package/src/web/src/dataset.ts +0 -132
- package/src/web/src/finetune.ts +0 -267
- package/src/web/src/hub.ts +0 -428
- package/src/web/src/main.ts +0 -98
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +0 -572
- package/src/web/src/model-picker.ts +0 -159
- package/src/web/src/palette.ts +0 -346
- package/src/web/src/protocol.ts +0 -26
- package/src/web/src/quantize.ts +0 -167
- package/src/web/src/rag.ts +0 -274
- package/src/web/src/sessions.ts +0 -469
- package/src/web/src/shell.ts +0 -804
- package/src/web/src/ui-catalog.ts +0 -78
- package/src/web/vendor/README.md +0 -61
- package/src/web-assets.ts +0 -47
- package/src/web-tools.ts +0 -478
- package/src/weights.ts +0 -255
- /package/src/{serve → server}/completion-sink.ts +0 -0
- /package/src/web/{icon.svg → public/icon.svg} +0 -0
- /package/src/web/{manifest.webmanifest → public/manifest.webmanifest} +0 -0
- /package/src/web/{vendor → public/vendor}/hljs-LICENSE +0 -0
|
@@ -1,680 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
status: landed
|
|
3
|
-
axis: USING
|
|
4
|
-
canonical-for: training
|
|
5
|
-
plan-anchor: "Phase: Steel flash-CCE ORPO head + full ORPO training stack"
|
|
6
|
-
last-verified: 2026-08-23
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Training — design (ORPO, flash-CCE head, prefix sharing, segmented backward)
|
|
10
|
-
|
|
11
|
-
The canonical design record for mlx-bun's LoRA trainer: the **ORPO** objective
|
|
12
|
-
(Hong et al. 2024) as a third `method` beside `sft`/`dpo`, and the long-context
|
|
13
|
-
machinery it runs on — the `[M,V]`-free **flash-CCE** head, the **prefix-shared**
|
|
14
|
-
two-branch forward, and the **segmented backward**. All of it is landed and
|
|
15
|
-
default-on in `scripts/train-orpo.ts` and `mlx-bun train`.
|
|
16
|
-
|
|
17
|
-
- Usage, flags, measured run tables: [training.md](../reference/training.md),
|
|
18
|
-
[docs/reference/training.md](../reference/training.md).
|
|
19
|
-
- Build narratives (frozen): [steel-flash-cce-handoff.md](../archive/investigations/steel-flash-cce-handoff.md),
|
|
20
|
-
[segmented-backward-handoff.md](../archive/investigations/segmented-backward-handoff.md),
|
|
21
|
-
[orpo-flash-cce-pin-leak.md](../archive/investigations/orpo-flash-cce-pin-leak.md),
|
|
22
|
-
[orpo-base-uf-experiment-and-directions.md](../archive/investigations/orpo-base-uf-experiment-and-directions.md),
|
|
23
|
-
[orpo-uf-testing-handoff.md](../archive/investigations/orpo-uf-testing-handoff.md).
|
|
24
|
-
- Open training work is tracked in PLAN.md under the plan-anchor above; status
|
|
25
|
-
prose lives there, not here.
|
|
26
|
-
|
|
27
|
-
Code: [`src/train/loss.ts`](../../src/train/loss.ts) (objective + heads),
|
|
28
|
-
[`trainer.ts`](../../src/train/trainer.ts) (loops, config), [`segmented.ts`](../../src/train/segmented.ts),
|
|
29
|
-
[`prefix-shared.ts`](../../src/train/prefix-shared.ts), [`flash-cce.ts`](../../src/train/flash-cce.ts)
|
|
30
|
-
(+ `steel-qmm-header.ts`), [`job.ts`](../../src/train/job.ts) (submit surface),
|
|
31
|
-
[`lora-params.ts`](../../src/train/lora-params.ts), [`rank.ts`](../../src/train/rank.ts).
|
|
32
|
-
Entry points: [`scripts/train-orpo.ts`](../../scripts/train-orpo.ts) (env launcher),
|
|
33
|
-
`mlx-bun train` (`src/cli.ts`), the `/finetune` job runner. SFT examples:
|
|
34
|
-
`scripts/examples/chunk-finetune.ts`, `scripts/examples/ft-e4b-v2.sh`,
|
|
35
|
-
`scripts/examples/chunk-eval.ts`.
|
|
36
|
-
|
|
37
|
-
## 1. The objective
|
|
38
|
-
|
|
39
|
-
$$\mathcal{L}_{ORPO} = \mathcal{L}_{NLL}(y_w) + \lambda \cdot \mathcal{L}_{OR}$$
|
|
40
|
-
|
|
41
|
-
- $\mathcal{L}_{NLL}(y_w)$ — cross-entropy on the **chosen** sequence. Scope is
|
|
42
|
-
`sft_scope` (`TrainConfig.sftScope`, default **`full`**):
|
|
43
|
-
- `full` (paper/TRL-faithful): token-mean CE over all non-pad positions,
|
|
44
|
-
prompt AND response, from the **same chosen forward** — the head runs once
|
|
45
|
-
over the prompt span and once over the response span (disjoint), combined
|
|
46
|
-
as $\text{NLL}_{full} = -\frac{(P{-}1)\,\text{pm} + M\,\ell_w}{(P{-}1)+M}$
|
|
47
|
-
(`combineFullNll`). Matches TRL `ORPOTrainer.chosen_nll_loss` and
|
|
48
|
-
xfactlab/orpo `outputs_pos.loss`.
|
|
49
|
-
- `response`: $-\ell_w$, the pre-2026-07 house convention (prompt masked),
|
|
50
|
-
kept bit-exact for reproducing older runs.
|
|
51
|
-
- $\mathcal{L}_{OR} = -\log\sigma(\text{log\_odds})$ with
|
|
52
|
-
$\text{log\_odds} = (\ell_w - \ell_r) - (\text{log1mexp}(\ell_w) - \text{log1mexp}(\ell_r))$,
|
|
53
|
-
$\text{log1mexp}(x) = \log(1-e^x)$. $\ell_w,\ell_r$ are **length-normalized
|
|
54
|
-
means** over response tokens in BOTH scope modes (also TRL's convention —
|
|
55
|
-
its `get_batch_logps` masks the prompt for the odds ratio; only the NLL term
|
|
56
|
-
is full-sequence). $\lambda$ weights only $\mathcal{L}_{OR}$; the NLL term is
|
|
57
|
-
unweighted.
|
|
58
|
-
|
|
59
|
-
Implementation (`loss.ts`): `log1mexp` (stable `log(clip(1 − exp(x), eps, 1))`
|
|
60
|
-
— mlx-bun has no `expm1`/`log1p`; per-token mean logp is comfortably negative
|
|
61
|
-
so the clip is safe), `orpoLogOdds`, `orpoLossFromLogps(lw, lr, λ, nllFull?)`
|
|
62
|
-
(model-free, unit-testable against a hand reference), `orpoLoss(model, batch,
|
|
63
|
-
λ, chunk?, sftScope)` (runs the two forwards), `orpoMetrics` (no-grad
|
|
64
|
-
`loss`/`nll`/`or`/`accuracy`/`margin`).
|
|
65
|
-
|
|
66
|
-
Two facts that shape everything downstream:
|
|
67
|
-
|
|
68
|
-
1. **Mean, not sum.** The one substantive difference from DPO's `seqLogp`
|
|
69
|
-
(per-row sum). Summing biases toward short responses and makes the OR
|
|
70
|
-
gradient length-dependent.
|
|
71
|
-
2. **The chosen forward does double duty.** ORPO is **2 forwards/step**
|
|
72
|
-
(chosen + rejected, both with grad) vs DPO's 4 (2 reference + 2 policy). No
|
|
73
|
-
reference model, no `setLoraScale(0)` dance, no detached reference tensors.
|
|
74
|
-
Never forward chosen twice; never compute a rejected NLL.
|
|
75
|
-
|
|
76
|
-
Two unpadded sequential B=1 forwards are the *right* shape, not a limitation:
|
|
77
|
-
TRL's padded `concatenated_forward` exists to amortize FSDP all-gathers we
|
|
78
|
-
don't have; on unified memory the padding is pure waste.
|
|
79
|
-
|
|
80
|
-
## 2. Oracles and the parity ladder
|
|
81
|
-
|
|
82
|
-
Neither `mlx_lm` nor `optiq` ships ORPO, so — unlike DPO (`loss.ts` ported
|
|
83
|
-
`optiq/lora/dpo.py`) — ORPO is a **no-oracle mlx-bun original (Lab-class)**.
|
|
84
|
-
The current MLX SOTA (`mlx-lm-lora`) ships a non-canonical ORPO
|
|
85
|
-
(`log_sigmoid(chosen − rejected)`, no SFT-NLL term); do not copy it.
|
|
86
|
-
|
|
87
|
-
What IS oracle-backed, and where each tier bottoms out:
|
|
88
|
-
|
|
89
|
-
| Tier | Path | Oracle / gate |
|
|
90
|
-
|---|---|---|
|
|
91
|
-
| L1 | Non-segmented SFT `value_and_grad` through `ops.sdpa` | **Bit-exact vs `mlx_lm.lora`** — loss on 4/6 mask spans, 0.5 f32-ulp on 2; `dB` over all 42 e4b q_proj layers `0.000e+0` (2026-06-16). Response-only CE == full-masked CE on every span. |
|
|
92
|
-
| L2 | `MLX_BUN_TRAIN_ATTN=flash` (port of optiq's `flash_attention_metal`) | optiq flash dK == `ops.sdpa` to f16 (`scripts/oracle/flash-optiq-check.py`); ours == `ops.sdpa` (`tests/flash-attention.test.ts`). |
|
|
93
|
-
| Lab | ORPO loss math | Standalone JS/numpy reference + finite differences (`tests/train-orpo.test.ts`; the deleted `parity-orpo.ts` passed 10/10 incl. FD grads at B=1/B=3). |
|
|
94
|
-
| Lab | Each memory tier (chunked → fused → flash head; segmented; prefix) | Validated against the tier below: value bit-exact or bf16-class, grads bf16-class, bottoming out at the naive full-logits two-forward path. |
|
|
95
|
-
|
|
96
|
-
Rules learned the hard way:
|
|
97
|
-
|
|
98
|
-
- A parity reference is trustworthy only if read **contiguously**
|
|
99
|
-
(`ops.contiguous` before `toFloat32`) with a **non-uniform cotangent** — both
|
|
100
|
-
pitfalls produced false "kernel is wrong" signals; finite differences settled it.
|
|
101
|
-
- "0.28% dh" for the flash-CCE head is fp-reassociation vs a full-logits
|
|
102
|
-
*proxy*, not error (two correct reductions in different orders aren't
|
|
103
|
-
bit-identical). Correctness is FD + the math audit; fidelity is
|
|
104
|
-
**teacher-forced on real hiddens**, not synthetic random logits (whose flat
|
|
105
|
-
softmax is the coeff filter's worst case). Standing regression:
|
|
106
|
-
`tests/train-orpo-fused-ce.test.ts` "teacher-forced fidelity" (cos > 0.999,
|
|
107
|
-
relnorm < 5% vs a `Vjp` full-logits reference).
|
|
108
|
-
- Validate every custom `.vjp` against `mx.vjp`/autograd, never a hand formula.
|
|
109
|
-
|
|
110
|
-
## 3. Trainer architecture
|
|
111
|
-
|
|
112
|
-
`trainLora` dispatches on `cfg.method`: `sftLoop` / `dpoLoop` / `orpoLoop`. All
|
|
113
|
-
share `resolveRanks` → `buildTrainableLora` → `AdamW` → a closure that swaps the
|
|
114
|
-
LoRA A/B primals so only those leaves differentiate. ORPO consumes the
|
|
115
|
-
**preference** dataset format `{prompt, chosen, rejected}` (`"dpo"` kept as an
|
|
116
|
-
alias) via `loadDpoDataset` / `iterateDpoBatches`.
|
|
117
|
-
|
|
118
|
-
`orpoLoop` path selection (verified in `trainer.ts`):
|
|
119
|
-
|
|
120
|
-
- `segmentSize > 0` → segmented classes (MiniCPM5 or Gemma4 only; else throws).
|
|
121
|
-
With `orpoPrefixShared` the composed `SegmentedBackwardOrpoPrefix[Gemma4]` is
|
|
122
|
-
built alongside a plain `SegmentedBackwardOrpo[Gemma4]` for per-row fallback.
|
|
123
|
-
The segmented head is `boundedHeadFromHidden` (Checkpoint-chunked or fused)
|
|
124
|
-
when `orpoFusedCe || orpoFlashCe`, else full `responseOnlyLogpMean`.
|
|
125
|
-
- Otherwise a `ValueAndGrad` over `orpoLossPrefixShared[Gemma]` (when the row's
|
|
126
|
-
prompts are byte-identical; `splitPrefixBatch`) or `orpoLoss`, with a
|
|
127
|
-
`ChunkCtx` selecting the head: `{fused, flash}` (chunk = `orpoChunkSize` or
|
|
128
|
-
512) / Checkpoint token-chunked (`orpoChunkSize > 0`) / full logits.
|
|
129
|
-
- Every micro-step goes through `accumulateStep(gradAccumSteps, …)`; the
|
|
130
|
-
per-chunk Checkpoint sink is disposed in its `afterMicroEval` hook.
|
|
131
|
-
- Dropout: `model.loraState.dropoutSeed` is set once per micro-step so the
|
|
132
|
-
segmented/checkpoint recompute replays the same mask; cleared before
|
|
133
|
-
metrics/val.
|
|
134
|
-
- `MLX_BUN_TRAIN_ATTN=flash` is **refused for Gemma models** (e4b SIGTRAPed at
|
|
135
|
-
≥2K and has not been re-validated since the kernel fixes); MiniCPM5 allowed.
|
|
136
|
-
|
|
137
|
-
### Config surface (TrainConfig ↔ job submit ↔ CLI/launcher)
|
|
138
|
-
|
|
139
|
-
| TrainConfig | `FinetuneSubmit` | `mlx-bun train` / `train-orpo.ts` env | Default | Notes |
|
|
140
|
-
|---|---|---|---|---|
|
|
141
|
-
| `orpoLambda` | `orpo_lambda` | `LAMBDA` | 0.1 | weights only L_OR |
|
|
142
|
-
| `orpoWarmupIters` / `orpoLrSchedule` | `orpo_warmup_iters` / `orpo_lr_schedule` | — | 0 / `cosine` | launcher sets warmup = min(10, iters/10) |
|
|
143
|
-
| `orpoChunkSize` | `orpo_chunk_size` | — | 0 (job: 512 for orpo) | token-chunk for all head variants |
|
|
144
|
-
| `orpoFusedCe` | `orpo_fused_ce` | `FLASH=0` selects it | false | analytic MLX fused head |
|
|
145
|
-
| `orpoFlashCe` | `orpo_flash_ce` | `FLASH` (default on) | false (job: true for orpo) | implies fused |
|
|
146
|
-
| `orpoPrefixShared` | `orpo_prefix_shared` | `PREFIX` (default on) | false (job: true for orpo) | MiniCPM5 + Gemma4, B=1 |
|
|
147
|
-
| `sftScope` | `sft_scope` | `SFT_SCOPE` | `full` | |
|
|
148
|
-
| `segmentSize` | `segment_size` | `SEG` / `SEGOFF=1`; `--seg` / `--no-segment` | 0 (job/CLI: 2 for orpo) | |
|
|
149
|
-
| `gradAccumSteps` | `grad_accumulation_steps` | `--grad-accum` (CLI only; no env knob in `train-orpo.ts`) | 1 | |
|
|
150
|
-
| `loraDropout` / `rsLora` / `loraPlusRatio` | `lora_dropout` / `rs_lora` / `lora_plus_ratio` | — | 0 / false / 1 | |
|
|
151
|
-
| `warmStartAdapter` | `warm_start_adapter` | `RESUME` / `--resume` | "" | weights only; optimizer + schedule restart |
|
|
152
|
-
| `gradCheckpoint` + `mlpSplit` | `grad_checkpoint` / `mlp_split` | — | false | mutually exclusive with `segmentSize`; `mlpSplit` Gemma4-only |
|
|
153
|
-
| `learningRate` | `learning_rate` | `LR` | 1e-5 for orpo (job/CLI/launcher) | `DEFAULT_TRAIN_CONFIG` is 2e-4 (SFT) |
|
|
154
|
-
| `rank` / `scale` | `rank` / `scale` | `RANK` / `SCALE` | orpo: 16 / **2.0** (CLI + launcher) | `DEFAULT_TRAIN_CONFIG`: 8 / 1.0; SFT example scripts use 20 |
|
|
155
|
-
|
|
156
|
-
Method-prefixed knobs (`orpo_*` / `dpo_*`) are deliberate: no shared or renamed
|
|
157
|
-
fields.
|
|
158
|
-
|
|
159
|
-
## 4. Recommended defaults and regularization geometry
|
|
160
|
-
|
|
161
|
-
From the hyperparameter spike (paper, TRL `ORPOConfig`, Labonne's Llama-3
|
|
162
|
-
ORPO-LoRA). The trap: the paper's `8e-6` is for *full* fine-tuning and
|
|
163
|
-
DPO-LoRA's `5e-5` is too hot because ORPO carries a full SFT-NLL term.
|
|
164
|
-
|
|
165
|
-
| Knob | Default | Why |
|
|
166
|
-
|---|---|---|
|
|
167
|
-
| `learning_rate` | 1e-5 | matches our SFT recipe; sweep 5e-6–2e-5 |
|
|
168
|
-
| `orpo_lambda` | 0.1 | paper's primary value + TRL default; stable 0.1–0.5 (λ=1.0 depresses chosen logp) |
|
|
169
|
-
| schedule / warmup | cosine, ≈3% of iters | the OR gradient's `1/(1−P)` factor amplifies low-likelihood updates, so cold high LR hurts more than in SFT |
|
|
170
|
-
| `rank` | 16 | |
|
|
171
|
-
| epochs | 1 to start | eval loss rises fast past epoch 1–2; early-stop on val margin |
|
|
172
|
-
|
|
173
|
-
Sources: [ORPO paper](https://aclanthology.org/2024.emnlp-main.626/),
|
|
174
|
-
[TRL `orpo_config.py`](https://github.com/huggingface/trl/blob/main/trl/trainer/orpo_config.py),
|
|
175
|
-
[Labonne](https://huggingface.co/blog/mlabonne/orpo-llama-3).
|
|
176
|
-
|
|
177
|
-
**Regularization, ordered by leverage** (ORPO is ~1 epoch on small sets, so
|
|
178
|
-
overfitting and step geometry matter more than capacity):
|
|
179
|
-
|
|
180
|
-
- **Rank is the primary regularizer**; `rank_scaling` (`by_bits`/`by_kl`) is how
|
|
181
|
-
capacity is distributed. `bitsMapFromModel` (reads `linear.spec.bits`) and
|
|
182
|
-
`readPerLayerKl` (`optiq_metadata.json`) feed `resolveRanks` — before this was
|
|
183
|
-
wired the policy silently went uniform. `by_bits`: `rank·(bits/4)`; `by_kl`:
|
|
184
|
-
`rank·clip(kl/median, 0.5, 2)` — optiq's "one signal, two optimizations"
|
|
185
|
-
(`tests/train-rank.test.ts`).
|
|
186
|
-
- **Rank ↔ scale ↔ init.** Update is `α·BA` with B zero-init, A uniform(±1/√in).
|
|
187
|
-
At step 0 `dA ∝ dy·Bᵀ = 0`: B does all the early learning → **LoRA+**
|
|
188
|
-
(`lora_plus_ratio`, higher LR on B leaves) helps. A fixed α makes the step
|
|
189
|
-
size scale with per-layer rank, defeating rank scaling → **rsLoRA**
|
|
190
|
-
(`rs_lora`, α/√r; saved into the adapter config so inference applies the same
|
|
191
|
-
per-layer scale).
|
|
192
|
-
- **Weight decay** on A and B independently is a nuclear-norm-style penalty on
|
|
193
|
-
`ΔW`, not Frobenius; the SFT-NLL term already anchors, so default lower than
|
|
194
|
-
SFT's 0.01 and tune.
|
|
195
|
-
- **LoRA-input dropout** (`lora_dropout`, PEFT-style, default 0, ~0.05 for small
|
|
196
|
-
sets). The base has no dropout modules (TRL's `disable_dropout` is moot).
|
|
197
|
-
Recompute determinism is mandatory: mask keyed by (step, layer) so segmented
|
|
198
|
-
/ checkpoint / chunked recompute reproduces it (`tests/train-regularization*.test.ts`).
|
|
199
|
-
|
|
200
|
-
The remaining work here is empirical: sweep `rs_lora` / `lora_plus_ratio` /
|
|
201
|
-
`lora_dropout` / `weight_decay` on a real run and judge by held-out val margin
|
|
202
|
-
(`bestMargin` in `metrics.json`).
|
|
203
|
-
|
|
204
|
-
## 5. Memory and speed
|
|
205
|
-
|
|
206
|
-
Two terms dominate at long context, and neither is the LoRA grads: (1) the LM
|
|
207
|
-
head logits `[1,T,V]` + gradient (~4.3/8.6 GB bf16 at T=8192, V=262k), doubled
|
|
208
|
-
by ORPO's two branches; (2) per-layer activations held for the backward. The
|
|
209
|
-
ordering principle for Apple Silicon: **only reducing total live footprint
|
|
210
|
-
moves the constraint — relocation does not.** CPU and GPU share one pool, so
|
|
211
|
-
activation offloading frees zero bytes, risks double residency, and burns the
|
|
212
|
-
scarce bandwidth. We do not implement it.
|
|
213
|
-
|
|
214
|
-
### 5.1 The head: from token-chunking to the flash-CCE kernel
|
|
215
|
-
|
|
216
|
-
CE and the per-token target logp ORPO needs are the same quantity
|
|
217
|
-
(`logp_t = h_t·W_{y_t} − logsumexp_v(h_t·W_v)`), so one memory-efficient engine
|
|
218
|
-
serves the NLL and the odds-ratio terms. Four tiers were built; each is
|
|
219
|
-
validated against the one below.
|
|
220
|
-
|
|
221
|
-
1. **Token-chunked Checkpoint head** (`chunkedLogpMeanB1`, `orpoChunkSize`).
|
|
222
|
-
Each `[chunk,V]` head is wrapped in a `Checkpoint` and recomputed in the
|
|
223
|
-
backward. A Checkpoint *drops* its input activations, so the hidden slices
|
|
224
|
-
it reads must live in the `sink` until grads are eval'd (found during
|
|
225
|
-
bring-up). Forward parity within bf16 (single-ULP head-matmul tiling
|
|
226
|
-
differences), exact for one chunk (`tests/train-orpo-chunked.test.ts`).
|
|
227
|
-
2. **Fused linear-CE head** (`fusedLogpMeanB1`, `orpo_fused_ce`; Liger
|
|
228
|
-
`FusedLinearCrossEntropy` on MLX). One `CustomVjp`: forward materializes
|
|
229
|
-
`[chunk,V]` per chunk (`quantizedMatmul(h, W, transpose=true)` + Gemma
|
|
230
|
-
`logitSoftcap`), reduces to `target − lse`, drops the logits; the analytic
|
|
231
|
-
backward recomputes and forms `g·(onehot − softmax)·sech²` directly, then
|
|
232
|
-
`dh = quantizedMatmul(grad, W, transpose=false)` — mlx's own x-vjp,
|
|
233
|
-
contracting vocab without dequantizing the head. Value **bit-exact** vs full
|
|
234
|
-
logits on MiniCPM5 and e4b (softcap=30 matched by reusing `logitSoftcap`'s
|
|
235
|
-
div-by-bf16-cap formula); grads 0.51% / 1.97% bf16-class
|
|
236
|
-
(`tests/train-orpo-fused-ce.test.ts`). The head is frozen (not a LoRA
|
|
237
|
-
target), so only `dh` flows back.
|
|
238
|
-
3. **Vocab-blocked online-softmax in pure MLX** — implemented, numerically
|
|
239
|
-
correct, **does not bound memory**, gated off. The `(m,d)` recurrence and
|
|
240
|
-
the cross-block `dh` accumulation must be f32 (bf16 gave 17→45% grad error
|
|
241
|
-
scaling with block count). But the online softmax is a sequentially
|
|
242
|
-
dependent chain and MLX's lazy eval retains the whole chain: head-isolated
|
|
243
|
-
peak went 3.4 GB → 14.1 GB at 512-wide blocks (MiniCPM5 M=2048, 2026-06-18).
|
|
244
|
-
Forcing an eval mid-chain breaks autodiff. Conclusion: the CCE residency
|
|
245
|
-
bound needs the vocab loop INSIDE a kernel.
|
|
246
|
-
4. **flash-CCE Metal kernel** (`flash-cce.ts`, `orpo_flash_ce`) — Apple
|
|
247
|
-
Cut-Cross-Entropy + Liger transpiled Triton → Metal with three divergences:
|
|
248
|
-
the classifier is the **quantized** head (in-Metal 4/8-bit affine dequant,
|
|
249
|
-
mlx `quantized.h` qdot pattern); only `dE`/`dh` is needed (frozen head, no
|
|
250
|
-
`dC`); the cross-block LSE merge is cheap MLX ops on per-block partials, not
|
|
251
|
-
atomic logaddexp. Evolution, all 2026-06-18/19 on the M1 Max 32 GB dev box:
|
|
252
|
-
- v1 (one threadgroup per token, whole vocab serially): memory bound
|
|
253
|
-
achieved (MiniCPM5 M=8192 fwd 2.45 → 0.34 GB; e4b M=512 bwd 2.06 → 1.17
|
|
254
|
-
GB) but e4b ≥1024 tripped the GPU watchdog.
|
|
255
|
-
- Vocab-parallel grid `[TG, nVocabBlocks, M]`, `VOCAB_BLOCK=8192`; backward
|
|
256
|
-
atomic-adds partial `dh` into `[M,H]`. e4b M=1024 deterministic, logp
|
|
257
|
-
0.20% / dh 0.40%, bwd peak 3.41 → 1.46 GB.
|
|
258
|
-
- `BLOCK_B` token tiling (dequant a vocab row once, reuse across 8 tokens
|
|
259
|
-
fwd / 4 bwd — the backward is register-bound by `dacc`): 2.5× total.
|
|
260
|
-
- **steel GEMM** (`steel-qmm-header.ts`, MLX's verbatim `BlockMMA`) for
|
|
261
|
-
forward and backward, with a temp-BlockMMA-per-H-tile + lane-local
|
|
262
|
-
fragment accumulation: e4b bwd **3687 → 754 ms**, fwd 180 ms, peak
|
|
263
|
-
**0.93 GB flat @ M=8192**; dh 0.40% e4b / 0.28% cpm.
|
|
264
|
-
`MLX_BUN_CCE_NOSTEEL` / `MLX_BUN_CCE_BWD_NOSTEEL` fall back to the
|
|
265
|
-
simdgroup / scalar kernels (also the non-32-divisible-shape fallback).
|
|
266
|
-
- Apple-CCE backward skips: coeff filter (`MLX_BUN_CCE_BWD_FILTER_EPS`) and
|
|
267
|
-
blockMax skip (`MLX_BUN_CCE_BWD_BLOCK_EPS`), **both default `1e-5` since
|
|
268
|
-
2026-07-02** (`=0` compiles them out). Real-data teacher-forced
|
|
269
|
-
measurement (M1 Max, chunk-ORPO data): filter 0.343%/1.41× (CPM5),
|
|
270
|
-
0.158%/1.70× (e4b); block skip ≤0.004% dh; combined **1.71× / 3.16×**
|
|
271
|
-
backward vs exact. Run-to-run dh is not byte-stable at any eps (atomic-add
|
|
272
|
-
reassociation). Note: docs/reference/training.md still describes the skips as
|
|
273
|
-
off by default; `flash-cce.ts` is the truth.
|
|
274
|
-
- Operand precision today is **W4A32**: 4-bit weights dequantized into fp32
|
|
275
|
-
tiles and multiplied with fp32 simdgroup matrices; the `dh` accumulator is
|
|
276
|
-
already fp32. See §8.1 (W4A16 spike).
|
|
277
|
-
|
|
278
|
-
Composition finding: the fused `CustomVjp` **does not bound memory when nested
|
|
279
|
-
inside the segmented `mlx_vjp` head** (its per-chunk graph is not freed
|
|
280
|
-
incrementally there; +0.8–1.1 GB at L=3072–4096 on MiniCPM5), so the segmented
|
|
281
|
-
bounded head uses the Checkpoint variant `chunkedLogpMeanFromHidden` (bit-exact
|
|
282
|
-
recompute). And bounding the head did not reduce the segmented peak at any
|
|
283
|
-
reachable scale (MiniCPM5 L=2048–4096 × SEG 2/6; e4b L=2048: 11.95 GB both) —
|
|
284
|
-
the head `[M,V]` is transient and never stacks with the per-segment backward
|
|
285
|
-
peak. The trainer still routes flash/fused into the segmented head so e4b never
|
|
286
|
-
reintroduces `[M,V]`.
|
|
287
|
-
|
|
288
|
-
Host-buffer lesson (2026-06-21): `u32()` kernel-arg arrays built with the
|
|
289
|
-
zero-copy `fromView` and disposed before the lazy kernel evaluated leaked pins
|
|
290
|
-
(+32/step) → use-after-free that ran clean on the 32 GB M1 Max and crashed on
|
|
291
|
-
the 24 GB M4 Pro. Fix: `fromBytesCopy`; `MetalKernel.apply` `ptr()` lifetimes
|
|
292
|
-
hardened; a `pinned` canary on the train metric. `fromView` is only for
|
|
293
|
-
process-lifetime memory. A related UAF in the segmented ORPO classes (the flash
|
|
294
|
-
head's `headSink` freed before the lazy backward read `lse`/`blockMax`) is fixed
|
|
295
|
-
by `ops.evalAll` on the head-VJP roots before dispose.
|
|
296
|
-
|
|
297
|
-
### 5.2 Attention backward: `ops.sdpa` vs the flash kernel
|
|
298
|
-
|
|
299
|
-
Default training attention is `ops.sdpa` (`mx.fast.scaled_dot_product_attention`)
|
|
300
|
-
— exact (dQ/dK/dV FD-verified 0.00%) and fast, but its Metal backward is NYI
|
|
301
|
-
(`ScaledDotProductAttentionVJP::use_fallback()` returns true), so MLX
|
|
302
|
-
decomposes it and **materializes the L×L scores for every layer**: O(L²)
|
|
303
|
-
backward memory (0.02 → 0.33 → 1.40 GB at T=512/2048/4096 in the isolated
|
|
304
|
-
attention-backward bench; ~3.5 GB/layer @8K on e4b). The hand-rolled flash
|
|
305
|
-
kernel (`src/model/flash-attention.ts`, `MLX_BUN_TRAIN_ATTN=flash`) is O(L)
|
|
306
|
-
(0.03 → 0.23 → 0.90 GB) but ~30× slower — a naive scalar kernel at ~0.5% of GPU
|
|
307
|
-
peak. Two genuine port bugs were fixed rather than deleting it (a spurious dK
|
|
308
|
-
transpose for Tkv≠D; a divergent `threadgroup_barrier` in the dQ causal
|
|
309
|
-
tile-skip), restoring L2 parity with optiq. It remains the dormant long-context
|
|
310
|
-
fallback for MiniCPM5; the trainer refuses it for Gemma.
|
|
311
|
-
|
|
312
|
-
The sliding-window layers are NOT cheaper in the backward — the window is only
|
|
313
|
-
an additive mask on the materialized scores (measured 2026-07-02: a sliding
|
|
314
|
-
pair +7.1 GB ≈ sliding+full +7.15 GB @8K). This falsified the earlier
|
|
315
|
-
full-attention-isolation planner premise (§5.3).
|
|
316
|
-
|
|
317
|
-
### 5.3 Segmented backward
|
|
318
|
-
|
|
319
|
-
Naive gradient checkpointing (ours and Python mlx's) does not stream: the
|
|
320
|
-
checkpointed backward holds every layer's recompute activations at once, so
|
|
321
|
-
e4b spiked to 23 GB live @2048 (resting 6.95 GB) and crashed at 4096
|
|
322
|
-
(`peak(L) ≈ 7.7 GB + 7.6 MB/token`). Measurement note that unlocked this:
|
|
323
|
-
`mlx_get_peak_memory` is peak **live**, not live+cache; use
|
|
324
|
-
`activeMemory()`/`cacheMemory()`/`clearCache()` to separate them, and eval the
|
|
325
|
-
loss and grads together (`evalAll([value, ...grads])`) — evaluating `value`
|
|
326
|
-
separately frees the forward and forces the backward to recompute-and-hold
|
|
327
|
-
everything.
|
|
328
|
-
|
|
329
|
-
**Mechanism** (`SegmentedBackward*`, `planSegmentsBySize`):
|
|
330
|
-
|
|
331
|
-
1. Forward, saving boundaries: run the layer stack; at each segment edge
|
|
332
|
-
(`segmentSize` layers) eval and **detach** the residual hidden into a leaf
|
|
333
|
-
(`detachLeaf` = `ops.contiguous` + `detachCopy`; `detachLeaves` batches the
|
|
334
|
-
eval barrier). mlx `eval` does not detach — without the copy each boundary
|
|
335
|
-
drags a layer-stack of activations.
|
|
336
|
-
2. Head: `finalNorm` + LM head + loss over the last boundary as a `Vjp` with
|
|
337
|
-
scalar cotangent 1.0 → loss value and `dh_out`.
|
|
338
|
-
3. Backward, reverse over segments: `vjp.apply([boundary_k, ...LoRA_k], [dh_out])`
|
|
339
|
-
→ `[dh_in, ...dLoRA_k]`; `dh_in` seeds segment k−1; LoRA grads scatter into
|
|
340
|
-
the flat grad vector. Only one segment's activations are live. Recompute
|
|
341
|
-
overhead ≈ 1× forward regardless of segment count, so `segmentSize` is a
|
|
342
|
-
peak knob at ~fixed time.
|
|
343
|
-
|
|
344
|
-
**The `mlx_vjp` finding.** The first implementation used optiq's
|
|
345
|
-
surrogate-loss trick — `value_and_grad` of
|
|
346
|
-
`sum(stop_grad(dh) ⊙ segment_forward(...))` per segment. Numerically identical,
|
|
347
|
-
but it **leaked ~32 MB of live memory per segment per step** (linear, no
|
|
348
|
-
plateau; 1.12 → 6.61 GB over 30 iters at SEG=4/2048), unreclaimable by
|
|
349
|
-
`clearCache`/GC/`synchronize`/disposal. A minimal repro pinned it to
|
|
350
|
-
`value_and_grad` (`MODE=layers` leaks ~6 MB/iter; `MODE=vjp` flat). Driving
|
|
351
|
-
each segment with **`mlx_vjp`** (`Vjp` in `src/mlx/autograd.ts`) is the
|
|
352
|
-
production path: active flat (1.04 GB × 12 iters end-to-end at SEQ=4096). Vjp
|
|
353
|
-
objects are built once per run and reused; rebuilding per step leaks.
|
|
354
|
-
|
|
355
|
-
**Numbers.** MiniCPM5-1B, SEG=4, M1 Max 32 GB, 2026-06-16, peak live:
|
|
356
|
-
|
|
357
|
-
| seq | full backward | segmented | note |
|
|
358
|
-
|---|---|---|---|
|
|
359
|
-
| 512 | 3.15 GB | 1.87 GB | |
|
|
360
|
-
| 2048 | 10.91 GB | **3.29 GB** | post-vjp fix (3.44 pre) |
|
|
361
|
-
| 4096 | 21–26 GB (spikes) | 6–8 GB | |
|
|
362
|
-
|
|
363
|
-
Under flash attention the segmented grads bit-match the full `value_and_grad`
|
|
364
|
-
(relNorm 0.0000%, maxAbs 0.0). Under `ops.sdpa` they differ ~6% relNorm — not a
|
|
365
|
-
segmentation bug: mlx's fused sdpa gives a different bf16 result in its eager
|
|
366
|
-
forward (12.0712) than its autograd-decomposed forward (12.0568), so boundaries
|
|
367
|
-
saved eagerly and recomputed under autograd disagree. The non-segmented trainer
|
|
368
|
-
already trained against the autograd forward while inference used the eager
|
|
369
|
-
one. A real 300-iter run (SEG=4, SEQ=4096) peaked 6.51 GB (non-seg 25.47 GB),
|
|
370
|
-
no leak, and its adapter scored 95.10 on the chunk holdout vs 91.70
|
|
371
|
-
non-segmented.
|
|
372
|
-
|
|
373
|
-
**e4b (Gemma4) additions** (`SegmentedBackwardGemma4`, `Gemma4Model.runLayerRange`
|
|
374
|
-
/ `makeTrainingMasks` / `embedForSegmented` / `reusedDonors`):
|
|
375
|
-
|
|
376
|
-
- Per-layer-input is a pure constant boundary (its grad would flow to the
|
|
377
|
-
embed / `per_layer_model_projection`, neither a LoRA target): detached once,
|
|
378
|
-
sliced per segment, cotangent discarded.
|
|
379
|
-
- KV-shared donor K/V (donors 22 sliding / 23 full feed all sharers in
|
|
380
|
-
[24,42)) is a **second boundary stream with its own cotangent**: sharer
|
|
381
|
-
segments differentiate `[h, donorK, donorV, ...LoRA]` and accumulate the
|
|
382
|
-
donor vjps into `dKV[d]`; the donor segment's multi-output forward
|
|
383
|
-
`[h_out, k, v]` takes `[dh, dKV.k, dKV.v]`. Two contiguity fixes were
|
|
384
|
-
essential: `detachLeaf` must force row-major (donor K/V are transposed
|
|
385
|
-
views) and the producer must output contiguous donor K/V.
|
|
386
|
-
- Grads: single-consumer donor reuse is bit-exact (2-segment split at 24: 0/258
|
|
387
|
-
off); the natural 7-segment cut is ~0.97% relNorm from bf16
|
|
388
|
-
non-associativity of the within-vjp cotangent sums — flat in consumer count,
|
|
389
|
-
tracks summand count (donor 22 ≈0.46%, donor 23 ≈0.000%), not helped by fp32
|
|
390
|
-
cross-segment accumulation. **The lever is the segment cut, not the dtype**:
|
|
391
|
-
keep a donor's sharers in one consumer segment for tighter grads at higher
|
|
392
|
-
peak. Vs the `mlx_lm.lora` bit-exact reference: segmented q_proj dB ~2.3%
|
|
393
|
-
(bf16-class).
|
|
394
|
-
- Memory vs the honest reference (`mlx_lm.lora --train --grad-checkpoint`,
|
|
395
|
-
which checkpoints per layer, 42 groups of 1; oracle venv, M1 Max 32 GB,
|
|
396
|
-
2026-06-16), matched grouping:
|
|
397
|
-
|
|
398
|
-
| L | mlx-lm (per-layer) | ours segSize=1 | ours segSize=6 |
|
|
399
|
-
|---|---|---|---|
|
|
400
|
-
| 2048 | 12.84 GB | **8.76 GB** | 11.05 |
|
|
401
|
-
| 4096 | 20.87 GB | **10.93 GB** | 16.06 |
|
|
402
|
-
| 8192 | OOM (25.72 @16 layers) | **15.29 GB** | 17.5 (seg2) |
|
|
403
|
-
|
|
404
|
-
Two unisolated confounds: mlx-lm's `default_loss` materializes full
|
|
405
|
-
`[1,L,262144]` logits (~4 GB @4K) vs our response-only CE; and full streaming
|
|
406
|
-
vs per-layer checkpoint inside one backward. The headline is part
|
|
407
|
-
CE-optimization, part mechanism.
|
|
408
|
-
|
|
409
|
-
**Segmentation strategy — corrected 2026-07-02.** The original plan (isolate the
|
|
410
|
-
7 O(L²) full-attention layers in singleton segments, `planSegments` with a
|
|
411
|
-
memory cost model) was built and A/B'd @8K: **zero peak win** (18.09 vs 18.02
|
|
412
|
-
GB seg2), because every layer's sdpa backward is O(L²) (§5.2). The worst
|
|
413
|
-
segment is set by **layer count alone** — `segment_size` is the whole knob:
|
|
414
|
-
seg1 = 14.59 GB @8K on e4b, +3% step time, loss identical. Going lower needs an
|
|
415
|
-
O(L)-memory attention backward, not smarter segmentation. `planSegmentsBySize`
|
|
416
|
-
stays; the donor/boundary constraints from the plan still hold. Rotor/min-cut
|
|
417
|
-
planners (§8.1) would need a corrected cost model.
|
|
418
|
-
|
|
419
|
-
**ORPO variants.** `SegmentedBackwardOrpo[Gemma4]` run chosen and rejected
|
|
420
|
-
forwards segment-by-segment, a two-input head `Vjp` over `[hC, hR]` yields
|
|
421
|
-
`[dh_c, dh_r]`, each branch walks backward over its segments (donor cotangents
|
|
422
|
-
in a per-branch `dKV`), LoRA grads summed. e4b at L=256: full two-branch 12.67
|
|
423
|
-
GB → segmented 7.66 GB; 8.71 GB at L=1024 where the full backward doesn't fit
|
|
424
|
-
(2026-06-18, M1 Max 32 GB). At λ=0 the e4b path is byte-identical to SFT
|
|
425
|
-
`SegmentedBackwardGemma4` (relNorm 1.1341%, maxAbs 3.79e-3 — the same numbers),
|
|
426
|
-
proving zero added error; donor recipients (layers 22/23 k/v_proj) stay <0.5%.
|
|
427
|
-
|
|
428
|
-
**Intra-layer MLP split** (`mlpSplit`, gradCheckpoint path, Gemma4 only):
|
|
429
|
-
`forwardAttn` / `forwardMlp` checkpointed separately with the post-attn
|
|
430
|
-
residual `hMid` as the boundary. Bit-exact (0.000000%). Honest peak: L=2048
|
|
431
|
-
no-ckpt 28.60 → single 23.40 → split 23.35 GB — the per-layer checkpoint
|
|
432
|
-
already serializes layers, so the split saves one layer's attn-sized window;
|
|
433
|
-
under `ops.sdpa` the MLP intermediate dominates. Off by default.
|
|
434
|
-
|
|
435
|
-
### 5.4 Shared prompt-prefix forward
|
|
436
|
-
|
|
437
|
-
Chosen and rejected share the prompt; the causal mask makes the prefix's
|
|
438
|
-
hidden states independent of the continuation. One forward over
|
|
439
|
-
`[prompt(P); chosen(Rc); rejected(Rr)]` with (a) a **block-sparse mask**
|
|
440
|
-
(causal AND NOT rejected→chosen, `blockSparseMask`, carried by
|
|
441
|
-
`PrefixSharedCache`), (b) **block-wise RoPE** resetting each response to offset
|
|
442
|
-
P (`ropeBlocks`, `setMiniCpmPrefixPlan` / `setGemmaPrefixPlan`), and the head
|
|
443
|
-
gathering `chosen[k] ← H[P−1+k]`, `rejected[0] ← H[P−1]`, `rejected[k≥1] ←
|
|
444
|
-
H[P+Rc+k−1]` (`prefixGatherIdx`). Token cost `2(P+R) → P+2R`; reverse-mode AD
|
|
445
|
-
sums the two branches' cotangents into the prefix for free. Published for DPO
|
|
446
|
-
(Wang & Hegde, arXiv 2410.20305); no mainstream trainer does it at training
|
|
447
|
-
time. `splitPrefixBatch` falls back to two-forward for rows whose prompts
|
|
448
|
-
differ (logged once, counted).
|
|
449
|
-
|
|
450
|
-
- **MiniCPM5**: forward **bit-exact** vs two-forward (P=512/Rc=64/Rr=80), grads
|
|
451
|
-
~1.05% (f32-over-bf16 reassociation in LoRA grad matmuls over the merged
|
|
452
|
-
length; holds at λ=0), 1.78× fewer token-passes, peak 5.40 → 3.80 GB
|
|
453
|
-
(2026-06-18).
|
|
454
|
-
- **e4b** (`blockSparsePrefixMaskGemma` with a LOGICAL-position sliding-window
|
|
455
|
-
cut, `Gemma4PrefixSharedCache`, `orpoLossPrefixSharedGemma`): the
|
|
456
|
-
construction is proven — block-RoPE+mask == a plain forward over the same
|
|
457
|
-
concat to 0.00% at every chosen position — but it does NOT reproduce the
|
|
458
|
-
two-forward numbers (loss ~0.08%, hiddens 3–14%) because **the e4b forward is
|
|
459
|
-
itself length-sensitive**: `forward([prompt])` vs `forward([prompt; extra])`
|
|
460
|
-
differ 1.7% at position 0 up to ~14% later, through the production
|
|
461
|
-
`TrainingCache`. Root cause is e4b's scale=1.0 attention (headDim 256, normed
|
|
462
|
-
q/k → sharply peaked softmax) amplifying sub-bf16 matmul-tiling roundings
|
|
463
|
-
that vary with row count — the same class as the SigLIP tower. MiniCPM5
|
|
464
|
-
(1/√d) shows 0.00%. Accepted as a numerical caveat for a fine-tune; the
|
|
465
|
-
single-concat forward is its oracle. Composed with the segmented backward
|
|
466
|
-
(`SegmentedBackwardOrpoPrefixGemma4`, donor-KV + logical-window mask threaded
|
|
467
|
-
through segments): seg-vs-non-seg grads 1.7–2.3%, peak 30–39% lower
|
|
468
|
-
(2026-06-19).
|
|
469
|
-
- Measured effect on e4b @8192 (M1 Max 32 GB, prompt-dominant data, short
|
|
470
|
-
probe): 16.1 GB / ~175 s/step → **13.3 GB / ~70 s/step** with prefix-share.
|
|
471
|
-
|
|
472
|
-
### 5.5 What composes with what
|
|
473
|
-
|
|
474
|
-
| | flash/fused head | segmented | prefix-shared | grad-accum | B>1 |
|
|
475
|
-
|---|---|---|---|---|---|
|
|
476
|
-
| flash/fused head | — | yes (Checkpoint-bounded head inside segments) | yes (per branch) | yes | no (B=1) |
|
|
477
|
-
| segmented | | — | yes (MiniCPM5 + Gemma4) | yes | no |
|
|
478
|
-
| prefix-shared | | | — | yes | no |
|
|
479
|
-
| grad-accum | | | | — | n/a |
|
|
480
|
-
|
|
481
|
-
The memory-optimized paths are **B=1 only**; `grad_accumulation_steps` is the
|
|
482
|
-
effective-batch lever at B=1 memory (`accumulateStep` evals and frees each
|
|
483
|
-
micro-graph; measured ~1.02× peak at N=3 on MiniCPM5, both SFT and ORPO;
|
|
484
|
-
`tests/train-gradaccum.test.ts`). B>1 falls out on the monolithic path via
|
|
485
|
-
`iterateDpoBatches` padding + the batched pad mask (`forward.ts`). True batched
|
|
486
|
-
segmented backward is not built. Qwen has no segmented backward (needs
|
|
487
|
-
`SSMCache` + a differentiable gated-delta kernel) and would land on the full
|
|
488
|
-
backward first.
|
|
489
|
-
|
|
490
|
-
Explicitly dropped: activation offloading (no-op on unified memory), TRL's
|
|
491
|
-
padded concatenated forward, sequence packing / length-grouped batching
|
|
492
|
-
(SFT-only wins, moot at B=1).
|
|
493
|
-
|
|
494
|
-
### 5.6 Quantized base
|
|
495
|
-
|
|
496
|
-
Each `QuantizedLinear` carries its own `QuantSpec`; `quantized_matmul` has a
|
|
497
|
-
working x-vjp and the base is frozen, so mixed precision costs nothing in the
|
|
498
|
-
layer stack. The only quant-aware training code is the head: a vocab chunk is
|
|
499
|
-
an axis-0 row slice of the quantized `w`/`scales`/`biases` (one spec per head),
|
|
500
|
-
and the flash-CCE kernel dequantizes in-kernel.
|
|
501
|
-
|
|
502
|
-
## 6. Kernel-fusion notes (the flat-DAG payoff)
|
|
503
|
-
|
|
504
|
-
Verified MLX facts (re-grep against the pinned 0.31.2 before relying):
|
|
505
|
-
`mx.compile` fuses contiguous elementwise chains in fwd AND bwd, bounded by
|
|
506
|
-
matmul/SDPA/reductions/reshape — compile the *outermost* `value_and_grad` or
|
|
507
|
-
the backward lands outside the region; `mx.fast` ships fused fwd+bwd for
|
|
508
|
-
`rms_norm`/`layer_norm`/`rope` (use them); SDPA backward is the Metal
|
|
509
|
-
exception (§5.2); `mx.fast.metal_kernel` inside `mx.custom_function` with a
|
|
510
|
-
`.vjp` is fully differentiable (the mechanism every custom head uses).
|
|
511
|
-
|
|
512
|
-
Remaining custom-kernel targets, ranked: fused SDPA backward (FA2-style,
|
|
513
|
-
store per-row LSE, recompute `P` — removes the O(L²) term segmentation
|
|
514
|
-
compensates for); fused dequant-GEMM + LoRA epilogue with the Unsloth-style
|
|
515
|
-
manual backward (`h=x@A; dB=s·hᵀ@dy; dA=s·xᵀ@(dy@Bᵀ); dx=dy@Wᵀ + s·(dy@Bᵀ)@Aᵀ`);
|
|
516
|
-
recompute↔backward fusion in the segmented path; min-cut rematerialization
|
|
517
|
-
(AOTAutograd's partitioner) as a fusion-aware replacement for hand-chosen cuts.
|
|
518
|
-
The GeGLU/SwiGLU `CustomVjp` (grad_b = dc·gelu(a), grad_a = dc·b·gelu'(a)) was
|
|
519
|
-
written and validated, then deleted 2026-07-05 with the fused kernels —
|
|
520
|
-
compiled activations own that fusion bit-exactly
|
|
521
|
-
([unified-engine-frontier-plan.md §6](unified-engine-frontier-plan.md)), and
|
|
522
|
-
training needs no env-flag sanitization.
|
|
523
|
-
|
|
524
|
-
Per-model specialized training paths would mirror `scripts/gen-model.ts`'s
|
|
525
|
-
fingerprint-dispatched generated forwards with the monolith as fallback; not
|
|
526
|
-
built.
|
|
527
|
-
|
|
528
|
-
## 7. Quality experiment pipeline
|
|
529
|
-
|
|
530
|
-
Pieces (judge-free, unit-tested): `src/eval/ifeval.ts` (verifiable-instruction
|
|
531
|
-
scorer), `src/eval/ultrafeedback.ts` + `scripts/curate-ultrafeedback.ts`
|
|
532
|
-
(binarized UF → preference JSONL with length filtering), `scripts/run-ifeval.ts`
|
|
533
|
-
(before/after runner), `scripts/bench-orpo.ts` (ms/step + peak across head /
|
|
534
|
-
segment configs). Prompt masking is verified (`tests/dpo-masking.test.ts`):
|
|
535
|
-
left-truncation preserves the prompt/response boundary; the only leak is a
|
|
536
|
-
completion ≥ `max_seq_length`, which `loadDpoDataset` warns on.
|
|
537
|
-
|
|
538
|
-
Result so far: an 800-step MiniCPM5 UltraFeedback run left IFEval flat
|
|
539
|
-
(22.5%) — general data + a tiny run on a 1B is the wrong lever. The
|
|
540
|
-
load-bearing target is the **chunk segmenter** (distilled segmentation with
|
|
541
|
-
boundary/label accuracy vs gold). Small sets (~450 pairs) overfit fast: keep λ
|
|
542
|
-
modest, carve a ~50-pair `valid.jsonl`, watch val margin, `save_checkpoints`
|
|
543
|
-
and mount the best-margin checkpoint.
|
|
544
|
-
|
|
545
|
-
## 8. Future
|
|
546
|
-
|
|
547
|
-
### 8.1 Deferred performance levers
|
|
548
|
-
|
|
549
|
-
- **Grad-accumulation A/B.** `--grad-accum` is exposed in `mlx-bun train`
|
|
550
|
-
(`scripts/train-orpo.ts` has no env knob for it). What's open is the
|
|
551
|
-
measurement: at hard B=1 every update is one pair (the 0.4↔3.3 per-step loss
|
|
552
|
-
swing is that noise); A/B G=4/8 on val margin. It is not a speedup and not a
|
|
553
|
-
guaranteed win (the prior run converged at B=1, val 1.66→1.50). Generic
|
|
554
|
-
N-way batching is parked — prefix-share is already a hand-built
|
|
555
|
-
batch-of-2 (1F+1B over `[P; Rc; Rr]`), strictly cheaper than a generic
|
|
556
|
-
2-batch's 2F+2B.
|
|
557
|
-
- **W4A16 head GEMM (compute precision) — proposed 2026-06-19, not started.**
|
|
558
|
-
The steel head runs W4A32 (`BlockMMA<float,float>` fwd and both bwd phases;
|
|
559
|
-
fp32 staging tiles; dequant into fp32). Apple GPUs run fp16 FMA at ~2× fp32,
|
|
560
|
-
and the kernel is **ALU-bound, measured live** (M1 Max, MiniCPM5 ORPO
|
|
561
|
-
`--no-segment` seq 4096: GPU 100% @1296 MHz, memory BW 129 GB/s ≈ ⅓ of ~400,
|
|
562
|
-
17.4/32 GB) — the 4-bit storage already did its job and the GPU spends
|
|
563
|
-
cycles on fp32 FMAs. Plan: (0) confirm which `BlockMMA`/`BlockLoader`
|
|
564
|
-
template params are operand vs accumulator; (1b) profile the dequant-ALU vs
|
|
565
|
-
FMA split — if FMA < ~40% of the kernel, reset expectations; (2) add a gate
|
|
566
|
-
(`CCE_A_HALF` define + env flag, fp32 stays the exact parity mode for the
|
|
567
|
-
1e-5 gate, half graded by greedy-prefix logit parity); (3) forward to half
|
|
568
|
-
operands first, land alone; (4) backward range probe — the survivors after
|
|
569
|
-
the skips are near-zero coeff tails where fp16 underflows, and `h` can carry
|
|
570
|
-
residual outliers (M1 Max has no bf16 simdgroup_matrix, so fp16 is the only
|
|
571
|
-
half); (5) backward to half only on a go; (6) re-sweep `BLOCK_B`/tiles —
|
|
572
|
-
half staging halves threadgroup memory, the constraint that capped the
|
|
573
|
-
current tiling. fp32 accumulate is non-negotiable; validate grads vs
|
|
574
|
-
autograd. Context measurement (M1 Max, MiniCPM5 seq 4096): `--no-segment`
|
|
575
|
-
~1.25 s/step @9.26 GB vs `--seg 2` ~1.58 s/step @2.62 GB — bit-identical
|
|
576
|
-
output, ~20% faster for 3.5× peak.
|
|
577
|
-
- **Sequence-chunk the MLP sub-block** inside segments: MLP is per-token
|
|
578
|
-
independent, so its `d_ff` intermediate can drop from `O(T·d_ff)` to
|
|
579
|
-
`O(chunk·d_ff)` at ~zero recompute — the bigger MLP lever than `mlpSplit`.
|
|
580
|
-
- **Budget-aware segment planner** (rotor DP / min-cut): only worth building
|
|
581
|
-
with the corrected cost model (every layer O(L²) under `ops.sdpa`); today
|
|
582
|
-
`segment_size` is the whole knob.
|
|
583
|
-
- **Faster flash-attention backward**: simdgroup tiling, larger threadgroups,
|
|
584
|
-
FA2-style LSE-only storage — matters only where `ops.sdpa`'s O(L²) is the
|
|
585
|
-
blocker (≳16K). MLX's own `mx.fast.sdpa` source is the blueprint.
|
|
586
|
-
- **Fused LoRA epilogue** (§6) — benefits SFT/DPO/ORPO and adapter serving.
|
|
587
|
-
- **Shape-specialized compiled training graphs**: each row has concrete
|
|
588
|
-
`(P, Rc, Rr)`, so ORPO can compile exact or bucketed shapes keyed on model
|
|
589
|
-
checksum + flags + segment plan + chunk sizes, eager fallback for misses.
|
|
590
|
-
Custom Metal kernels stay outside compiled regions.
|
|
591
|
-
- **ORPO-grad folding inside the head kernel** (speed-only; autograd composes
|
|
592
|
-
the odds-ratio grad outside it today) and a head-LoRA `∇A/∇B` path if the
|
|
593
|
-
head ever becomes a target.
|
|
594
|
-
- Optimize the flash SDPA kernel with simdgroup_matrix, or beat `mx.fast.sdpa`
|
|
595
|
-
for our fixed head-dims / training-only backward / O(L) constraint —
|
|
596
|
-
research spike, not scheduled.
|
|
597
|
-
|
|
598
|
-
### 8.2 Dynamic λ controller (proposed 2026-06-20, reframed 2026-07-01; not built)
|
|
599
|
-
|
|
600
|
-
Josh's framing: *λ is not a hyperparameter to tune but a process variable to
|
|
601
|
-
control* — push preference pressure to the maximum the model can bear
|
|
602
|
-
(self-discovering), retreat on confirmed coherence damage (self-stabilizing),
|
|
603
|
-
hold inside the healthy band. Motivation: the SFT/NLL half learns easily (val
|
|
604
|
-
loss falls while accuracy/margin barely move); over-weighting OR triggers
|
|
605
|
-
**likelihood displacement** (margin widened by crushing `logp(rejected)` and
|
|
606
|
-
sometimes `logp(chosen)`, cf. arXiv 2410.08847). Closest prior is β-DPO
|
|
607
|
-
(arXiv 2407.08639, per-batch calibration); the claim here would be closed-loop
|
|
608
|
-
trajectory control with a displacement-aware signal — the controller must beat
|
|
609
|
-
the *best constant* λ from a sweep, not λ=0.1.
|
|
610
|
-
|
|
611
|
-
- **Signals**: `lw` and `lr` separately (today `orpoMetrics` returns only
|
|
612
|
-
`loss/nll/or/accuracy/margin` — surfacing `lw`/`lr` is the load-bearing
|
|
613
|
-
instrumentation), on **val** each `stepsPerEval` (per-step B=1 train metrics
|
|
614
|
-
are too noisy). Control on the **response-only** `lw` — full-scope NLL mixes
|
|
615
|
-
prompt modeling into the level. Margin↑ via `lw`↑ is healthy; margin↑ via
|
|
616
|
-
`lr`↓ with `lw` flat/falling is displacement.
|
|
617
|
-
- **Law**: phase machine (SFT warmup at `LAMBDA_MIN` until `dLw` plateaus →
|
|
618
|
-
ramp `+RAMP` per eval while `dLw ≥ −EPS_DISP` → back off on displacement,
|
|
619
|
-
hysteresis over K evals; hard stop if `lw` falls below its start by a
|
|
620
|
-
threshold) — i.e. AIMD; generalize to P → PD → PID incrementally, each term
|
|
621
|
-
earning its place (D on a robust EMA slope for the leading indicator; I with
|
|
622
|
-
anti-windup for chronic offset). Bias toward pushing with a safety margin
|
|
623
|
-
below the cliff; the degenerate failure is collapsing to pure SFT.
|
|
624
|
-
- **Wiring**: λ becomes a mutable ref read per step (today it is a constant
|
|
625
|
-
threaded into `orpoLoss` and baked into the segmented classes' constructors
|
|
626
|
-
→ add `setLambda`); controller lives in `orpoLoop` after each val eval;
|
|
627
|
-
gated `orpoLambdaSchedule: "static" | "adaptive"`, static = today exactly.
|
|
628
|
-
- **Validation**: same data/seed, Arm A static 0.1 vs Arm B adaptive; compare
|
|
629
|
-
val accuracy/margin and `lw` trajectories plus downstream win-rate; validate
|
|
630
|
-
the controller on the prefer-uppercase positive control first; set EPS from
|
|
631
|
-
observed val-`lw` noise, not hand-fit. Report both arms (adaptive is our
|
|
632
|
-
method, not "standard ORPO").
|
|
633
|
-
|
|
634
|
-
### 8.3 Objective variants and data
|
|
635
|
-
|
|
636
|
-
Once the shared logp engine exists these are cheap siblings: **SimPO**
|
|
637
|
-
(`softplus(−β((lw − lr) − γ))`), **IPO**, **hinge/SLiC** margin loss, label
|
|
638
|
-
smoothing / per-example weights / loss clipping / ambiguity filtering. Data
|
|
639
|
-
quality likely beats objective cleverness on small runs: filter UltraFeedback
|
|
640
|
-
by score gap, bounded length ratio, semantic contrast, and instruction-following
|
|
641
|
-
categories if IFEval is the metric.
|
|
642
|
-
|
|
643
|
-
## History
|
|
644
|
-
|
|
645
|
-
- 2026-06-15 — e4b LoRA enablement diagnosis: the wall is backward memory
|
|
646
|
-
(full-vocab logits + un-streamed activations), not attention; response-only
|
|
647
|
-
CE landed.
|
|
648
|
-
- 2026-06-16 — Segmented backward Phase A (MiniCPM5, bit-exact under flash,
|
|
649
|
-
10.91 → 3.29 GB @2048) and Phase B (e4b, donor-KV + per-layer-input
|
|
650
|
-
threading, all 42 layers @8K where mlx-lm OOMs); surrogate `value_and_grad`
|
|
651
|
-
leak found, replaced by `mlx_vjp`; non-segmented SFT proven bit-exact vs
|
|
652
|
-
`mlx_lm.lora`.
|
|
653
|
-
- 2026-06-17 — ORPO landed as a method: loss math + tests, `orpoLoop`, job
|
|
654
|
-
submit, `/finetune` picker, e2e on MiniCPM5; `bitsMap` rank scaling wired;
|
|
655
|
-
dataset format renamed `preference`; flash SDPA kernel fixed to L2 parity.
|
|
656
|
-
- 2026-06-18 — Token-chunked head; fused linear-CE head; vocab-blocked pure-MLX
|
|
657
|
-
CCE (correct, unbounded, gated off); flash-CCE kernel v1 → vocab-parallel →
|
|
658
|
-
`BLOCK_B`; `SegmentedBackwardOrpo` + `SegmentedBackwardOrpoGemma4`; MiniCPM5
|
|
659
|
-
prefix-shared reference (forward bit-exact); LoRA dropout / rsLoRA / LoRA+;
|
|
660
|
-
`mlpSplit`.
|
|
661
|
-
- 2026-06-19 — Steel GEMM flash head fwd+bwd in production (e4b bwd 3687 → 754
|
|
662
|
-
ms, 0.93 GB flat); prefix-sharing wired and composed with the flash head and
|
|
663
|
-
segmented backward for MiniCPM5 and e4b; warm-start (`RESUME`);
|
|
664
|
-
`scripts/train-orpo.ts` launcher; W4A16 spike proposed; grad-accum A/B
|
|
665
|
-
deferred.
|
|
666
|
-
- 2026-06-20/23 — Dynamic λ controller proposed, PID/AIMD framing added.
|
|
667
|
-
- 2026-06-21/22 — Host-buffer pin-leak post-mortem (`fromView` → `fromBytesCopy`);
|
|
668
|
-
Lab gates: FD + teacher-forced fidelity replace "parity" for the no-oracle
|
|
669
|
-
head; coeff filter measured near-free on real data.
|
|
670
|
-
- 2026-07-01 — `sft_scope: full | response` (default `full`), implemented on
|
|
671
|
-
every ORPO path; dynamic-λ reframed as band control.
|
|
672
|
-
- 2026-07-02 — Apple-CCE skips default 1e-5 (1.71× / 3.16× backward);
|
|
673
|
-
teacher-forced fidelity regression test; full-attention isolation planner
|
|
674
|
-
refuted (every layer O(L²)); `segment_size` 1 = 14.59 GB @8K on e4b.
|
|
675
|
-
- 2026-07-05 — Fused GeGLU/SwiGLU and perf kernels deleted; no training flag
|
|
676
|
-
sanitization remains.
|
|
677
|
-
- 2026-08-23 — This doc consolidated `docs/design/orpo-training.md`,
|
|
678
|
-
`docs/design/orpo-training.md`, `docs/design/orpo-training.md`, and
|
|
679
|
-
`docs/design/orpo-training.md`; examples scripts moved to
|
|
680
|
-
`scripts/examples/`.
|