mlx-bun 0.2.0 → 0.4.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 +71 -588
- package/docs/README.md +94 -0
- package/docs/{design → archive/investigations}/audio-input-plan.md +4 -4
- package/docs/{design → archive/investigations}/diffusion-gemma-port.md +3 -3
- package/docs/{design → archive/investigations}/faithful-l1-consolidation.md +4 -4
- package/docs/{design → archive/investigations}/minicpm5-decode-megakernel.md +11 -11
- package/docs/archive/investigations/openjarvis-fit.md +198 -0
- package/docs/{design → archive/investigations}/optimization_plan.md +1 -1
- package/docs/{design → archive/investigations}/pre-colibri-stabilization.md +5 -5
- package/docs/archive/investigations/qwen38-27b-campaign-log.md +1092 -0
- package/docs/{design → archive/investigations}/the-dreaming-handoff.md +2 -2
- package/docs/archive/planning/release-notes-v0.3.0.md +35 -0
- package/docs/design/batching.md +1841 -0
- package/docs/design/decode-speed-program.md +2346 -169
- package/docs/design/dreaming-nightly-pipeline.md +675 -282
- package/docs/design/generic-model-support.md +655 -334
- package/docs/design/kv-cache.md +824 -0
- package/docs/design/orpo-training.md +673 -1239
- package/docs/design/speculative-decoding.md +1990 -0
- package/docs/design/turboquant.md +2212 -0
- package/docs/design/unified-engine-frontier-plan.md +1833 -727
- package/docs/design/web-chat-redesign.md +666 -1083
- package/docs/planning/PRODUCT_ROADMAP.md +50 -1
- package/docs/planning/release-notes-v0.4.0.md +45 -0
- package/docs/planning/video-series.md +2 -2
- package/docs/reference/benchmarks.md +3613 -0
- package/docs/reference/cli.md +434 -160
- package/docs/reference/distribution.md +328 -182
- package/docs/reference/environment.md +398 -0
- package/docs/reference/glossary.md +39 -0
- package/docs/reference/library-api.md +566 -168
- package/docs/reference/memory.md +227 -64
- package/docs/reference/models.md +113 -6
- package/docs/reference/server-api.md +944 -429
- package/docs/reference/server-config.md +740 -338
- package/docs/reference/training.md +393 -254
- package/docs/reference/troubleshooting.md +158 -0
- package/package.json +9 -4
- package/src/audio/decode.ts +2 -2
- package/src/audio/features.ts +2 -2
- package/src/backends/mlx/assistant-target.ts +46 -0
- package/src/backends/mlx/autoregressive-draft-rows.ts +120 -0
- package/src/backends/mlx/autoregressive.ts +108 -0
- package/src/backends/mlx/batch-group.ts +1790 -0
- package/src/backends/mlx/cache-attachment.ts +67 -0
- package/src/backends/mlx/cache-layout.ts +31 -0
- package/src/backends/mlx/checkpoint-state.ts +53 -0
- package/src/backends/mlx/continuation-persistence.ts +106 -0
- package/src/backends/mlx/continuation-request.ts +43 -0
- package/src/backends/mlx/continuation.ts +77 -0
- package/src/backends/mlx/diffusion.ts +39 -0
- package/src/backends/mlx/draft-target.ts +32 -0
- package/src/backends/mlx/evaluation.ts +105 -0
- package/src/backends/mlx/fill-append.ts +45 -0
- package/src/backends/mlx/full-kv-row-append.ts +32 -0
- package/src/backends/mlx/full-kv-row-donor.ts +54 -0
- package/src/backends/mlx/full-transitioning-kv-rows.ts +65 -0
- package/src/backends/mlx/gateway-binding.ts +130 -0
- package/src/backends/mlx/graph.ts +38 -0
- package/src/backends/mlx/kv-codec.ts +9 -0
- package/src/backends/mlx/kv-maintenance.ts +133 -0
- package/src/backends/mlx/kv-tensor-rows.ts +212 -0
- package/src/backends/mlx/memory-client.ts +189 -0
- package/src/backends/mlx/methods.ts +90 -0
- package/src/backends/mlx/model-prompt.ts +164 -0
- package/src/backends/mlx/model-serving.ts +79 -0
- package/src/backends/mlx/prefill-cohort.ts +89 -0
- package/src/backends/mlx/prefill-rows.ts +200 -0
- package/src/backends/mlx/prefill.ts +33 -0
- package/src/backends/mlx/projected-context-rows.ts +72 -0
- package/src/backends/mlx/request-policy.ts +20 -0
- package/src/backends/mlx/request-state-policy.ts +41 -0
- package/src/backends/mlx/rollback.ts +89 -0
- package/src/backends/mlx/row-sampling.ts +49 -0
- package/src/backends/mlx/serial-executor.ts +242 -0
- package/src/backends/mlx/speculative-group.ts +359 -0
- package/src/backends/mlx/speculative-round.ts +161 -0
- package/src/backends/mlx/speculative.ts +114 -0
- package/src/backends/mlx/state-rows.ts +104 -0
- package/src/backends/mlx/state-views.ts +46 -0
- package/src/backends/mlx/target-layout-capability.ts +35 -0
- package/src/backends/mlx/text-engine.ts +73 -0
- package/src/backends/mlx/transitioning-kv-rows.ts +165 -0
- package/src/cli.ts +66 -58
- package/src/client.ts +66 -0
- package/src/config.ts +21 -4
- package/src/contracts/completion.ts +16 -0
- package/src/contracts/execution.ts +60 -0
- package/src/contracts/generation.ts +103 -0
- package/src/contracts/host.ts +7 -0
- package/src/contracts/jobs.ts +66 -0
- package/src/contracts/pi-provider.ts +30 -0
- package/src/contracts/pi.ts +259 -0
- package/src/contracts/prefix-cache.ts +23 -0
- package/src/contracts/resources.ts +25 -0
- package/src/contracts/sampling.ts +23 -0
- package/src/contracts/scheduling.ts +45 -0
- package/src/contracts/tsconfig.json +14 -0
- package/src/diffusion/diffusion-generate.ts +218 -153
- package/src/embed.ts +1 -1
- package/src/engine/admission.ts +59 -0
- package/src/engine/cancellation.ts +38 -0
- package/src/engine/engine.ts +56 -0
- package/src/engine/execution-coordinator.ts +70 -0
- package/src/engine/execution-plan.ts +53 -0
- package/src/engine/resources.ts +53 -0
- package/src/engine/scheduler.ts +72 -0
- package/src/engine/session.ts +247 -0
- package/src/eval/README.md +44 -1
- package/src/eval/kl.ts +236 -3
- package/src/eval/reference-logits.ts +207 -0
- package/src/eval/runner.ts +8 -96
- package/src/fill/echo-index.ts +188 -0
- package/src/fill/fill-session.ts +452 -0
- package/src/fill/proposal.ts +68 -0
- package/src/fill/schema-rows.ts +506 -0
- package/src/fill/tool-boundary.ts +103 -0
- package/src/fit.ts +30 -16
- package/src/generate.ts +709 -417
- package/src/grammar.ts +12 -7
- package/src/harness-pi.ts +14 -33
- package/src/hub-rest.ts +4 -4
- package/src/index.ts +7 -0
- package/src/inference/denoising.ts +17 -0
- package/src/inference/draft-acceptance.ts +41 -0
- package/src/inference/draft-output.ts +49 -0
- package/src/inference/graph.ts +33 -0
- package/src/inference/prefill.ts +39 -0
- package/src/inference/rollback.ts +7 -0
- package/src/jobs/db.ts +4 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-entry.ts +12 -12
- package/src/jobs/runner.ts +98 -13
- package/src/jobs/task-client.ts +19 -0
- package/src/jobs/types.ts +2 -58
- package/src/kv-scheme.ts +70 -12
- package/src/kv-store.ts +177 -63
- package/src/lab/README.md +18 -0
- package/src/{assets → lab/curve}/curve-designer.html +2 -2
- package/src/{curve-sampler.ts → lab/curve/curve-sampler.ts} +2 -2
- package/src/{expert-trace.ts → lab/expert-trace/expert-trace.ts} +5 -5
- package/src/lab/paged-kv/paged-kv-rows.ts +83 -0
- package/src/{model → lab/paged-kv}/paged-kv.ts +32 -15
- package/src/library.ts +40 -0
- package/src/lora.ts +18 -0
- package/src/media-fetch.ts +1 -1
- package/src/memory/article.ts +1 -1
- package/src/memory/chunk.ts +1 -1
- package/src/memory/model.ts +30 -169
- package/src/memory/prompts.ts +1 -1
- package/src/memory/rest.ts +1 -1
- package/src/memory/schedule.ts +1 -1
- package/src/memory/stages.ts +4 -1
- package/src/memory/vault.ts +1 -3
- package/src/mlx/array.ts +11 -11
- package/src/mlx/compile.ts +12 -2
- package/src/mlx/ffi.ts +58 -11
- package/src/mlx/materialize.ts +23 -0
- package/src/mlx/metal-kernel.ts +10 -11
- package/src/mlx/ops.ts +33 -13
- package/src/mlx/quantized-rows.ts +36 -0
- package/src/mlx/token-bitmask.ts +52 -0
- package/src/mlx/turboquant-kv-decode.ts +82 -0
- package/src/mlx/turboquant-kv-inverse.ts +122 -0
- package/src/mlx/turboquant-ops.ts +33 -3
- package/src/mlx/turboquant-tables.ts +2 -2
- package/src/model/artifact-identity.ts +73 -0
- package/src/model/batched-kv.ts +235 -0
- package/src/model/batched-mask.ts +20 -6
- package/src/model/batched-quant.ts +9 -3
- package/src/model/batched-quantized-kv.ts +144 -0
- package/src/model/batched-rotating-quant.ts +78 -17
- package/src/model/batched-rotating-state.ts +76 -0
- package/src/model/batched-rotating.ts +65 -23
- package/src/model/batched-row-storage.ts +52 -0
- package/src/model/batched-ssm.ts +52 -0
- package/src/model/batched-turboquant-kv.ts +93 -0
- package/src/model/compiled-decode.ts +8 -2
- package/src/model/decoded-kv-donor.ts +12 -0
- package/src/model/delayed-quantized-kv.ts +72 -0
- package/src/model/delayed-rotating-quantized-kv.ts +93 -0
- package/src/model/delayed-turboquant-kv.ts +55 -0
- package/src/model/diffusion-gemma.ts +8 -8
- package/src/model/factory.ts +76 -33
- package/src/model/fingerprint.ts +1 -1
- package/src/model/flash-attention.ts +3 -3
- package/src/model/full-prefill-padding.ts +56 -0
- package/src/model/full-prefill-row.ts +105 -0
- package/src/model/gemma4-base.ts +179 -80
- package/src/model/gemma4.ts +40 -32
- package/src/model/generated/gemma4-12b.ts +6 -3
- package/src/model/generated/gemma4-26b.ts +6 -3
- package/src/model/generated/gemma4-e4b.ts +6 -3
- package/src/model/glm52-cache.ts +80 -4
- package/src/model/glm52-dsa.ts +13 -9
- package/src/model/glm52-memory.ts +1 -1
- package/src/model/glm52-mla.ts +12 -6
- package/src/model/glm52.ts +19 -40
- package/src/model/implementation.ts +69 -0
- package/src/model/kv-attention-view.ts +67 -0
- package/src/model/minicpm5.ts +17 -15
- package/src/model/profile.ts +12 -2
- package/src/model/qwen-append.ts +12 -0
- package/src/model/qwen-conv.ts +52 -0
- package/src/model/qwen3-delta-state.ts +74 -0
- package/src/model/qwen3-delta.ts +59 -36
- package/src/model/qwen3_5-checkpoint.ts +164 -0
- package/src/model/qwen3_5.ts +202 -78
- package/src/model/rotating-kv-donor.ts +35 -0
- package/src/model/rotating-kv-layout.ts +262 -0
- package/src/model/rotating-row-transaction.ts +35 -0
- package/src/model/speculative-rotating-kv.ts +101 -0
- package/src/model/ssm-prefill-padding.ts +39 -0
- package/src/model/support.ts +1 -1
- package/src/model/trellis-balanced-scatter.ts +51 -0
- package/src/model/trellis-linear.ts +672 -0
- package/src/model/trellis-shared-m.ts +151 -0
- package/src/model/trellis-shared-scatter.ts +144 -0
- package/src/model/trellis-splitk-prefill.ts +158 -0
- package/src/model/trellis-tiled-prefill.ts +136 -0
- package/src/model/trellis-vector-expand.ts +62 -0
- package/src/model/trellis-wide-prefill.ts +94 -0
- package/src/model/turboquant-codec.ts +83 -0
- package/src/model/universal/dense.ts +15 -14
- package/src/model/universal/modules.ts +8 -1
- package/src/native-pack.ts +6 -5
- package/src/pi-provider.ts +6 -64
- package/src/pi-web.ts +8 -315
- package/src/preflight.ts +31 -15
- package/src/prompt-cache.ts +110 -69
- package/src/quantize/allocator.ts +1 -1
- package/src/quantize/config-writer.ts +10 -3
- package/src/quantize/rotate.ts +4 -4
- package/src/quantize/trellis.ts +586 -0
- package/src/responses.ts +28 -1
- package/src/runtime-config.ts +18 -4
- package/src/sampler.ts +40 -29
- package/src/serve/batch-scheduler.ts +8 -1671
- package/src/serve/chat-request.ts +391 -0
- package/src/serve/chat-stage.ts +195 -0
- package/src/serve/checkpoint-identity.ts +64 -0
- package/src/serve/completion-executor.ts +15 -7
- package/src/serve/discovery-routes.ts +12 -13
- package/src/serve/generation-gateway.ts +183 -213
- package/src/serve/http.ts +159 -0
- package/src/serve/inference-request.ts +79 -0
- package/src/serve/isolate.ts +297 -105
- package/src/serve/lab-routes.ts +3 -0
- package/src/serve/lane-registry.ts +2 -1
- package/src/serve/model-admin-routes.ts +7 -5
- package/src/serve/model-binding.ts +43 -0
- package/src/serve/model-host.ts +511 -0
- package/src/serve/openai-wire.ts +103 -0
- package/src/serve/parent-application.ts +56 -0
- package/src/serve/pipeline.ts +33 -0
- package/src/serve/preparation.ts +33 -0
- package/src/serve/request-plan.ts +23 -18
- package/src/serve/request-prep.ts +344 -0
- package/src/serve/response-lease.ts +24 -0
- package/src/serve/responses-client.ts +69 -0
- package/src/serve/session-completion-engine.ts +110 -0
- package/src/serve/session-search.ts +1 -1
- package/src/serve/static-routes.ts +5 -5
- package/src/serve/text-completion-stage.ts +104 -0
- package/src/serve/token-streams.ts +519 -0
- package/src/server.ts +335 -2711
- package/src/spec/assistant-rows.ts +92 -0
- package/src/spec/assistant-source.ts +31 -55
- package/src/spec/cache-identity.ts +22 -0
- package/src/spec/deepspec-rows.ts +29 -0
- package/src/spec/deepspec-source.ts +16 -14
- package/src/spec/dflash-source.ts +36 -12
- package/src/spec/drafter.ts +153 -142
- package/src/spec/dspark/ab-stats.ts +3 -3
- package/src/spec/dspark/calibration.ts +2 -2
- package/src/spec/dspark/deepspec-module.ts +97 -122
- package/src/spec/dspark/generate-dflash.ts +2 -2
- package/src/spec/dspark/generate.ts +2 -2
- package/src/spec/dspark/loader.ts +1 -1
- package/src/spec/dspark/module-dflash.ts +129 -172
- package/src/spec/dspark/module.ts +3 -3
- package/src/spec/dspark/quantize-drafter.ts +1 -1
- package/src/spec/generate.ts +1 -1
- package/src/spec/glm52-mtp-graph.ts +29 -0
- package/src/spec/glm52-mtp-rows.ts +132 -0
- package/src/spec/glm52-mtp-source.ts +54 -137
- package/src/spec/glm52-mtp-state.ts +33 -0
- package/src/spec/ngram-source.ts +93 -21
- package/src/spec/projected-draft-rows.ts +73 -0
- package/src/spec/qwen-mtp-module.ts +171 -0
- package/src/spec/qwen-mtp-rows.ts +219 -0
- package/src/spec/qwen-mtp-source.ts +147 -195
- package/src/spec/qwen-mtp-state.ts +30 -0
- package/src/spec/serve-loop.ts +201 -201
- package/src/spec/source.ts +110 -13
- package/src/spec/two-model-rows.ts +61 -0
- package/src/spec/two-model.ts +44 -102
- package/src/ssd-cache.ts +99 -19
- package/src/ssd-durability.ts +36 -20
- package/src/tool-call.ts +51 -14
- package/src/train/flash-cce.ts +2 -2
- package/src/train/forward.ts +6 -0
- package/src/train/loss.ts +1 -1
- package/src/train/prefix-shared.ts +5 -1
- package/src/train/segmented.ts +5 -3
- package/src/train/trainer.ts +6 -6
- package/src/vision/prompt.ts +1 -1
- package/src/vision/qwen3vl-tower.ts +52 -10
- package/src/web/app.html +1 -1
- package/src/web/app.js +62 -60
- package/src/web/src/adapters-panel.ts +3 -3
- package/src/web/src/assistant.ts +2 -36
- package/src/web/src/chat.ts +4 -4
- package/src/web/src/composer.ts +4 -4
- package/src/web/src/hub.ts +1 -1
- package/src/web/src/markdown.ts +0 -0
- package/src/web/src/memory-panel.ts +1 -1
- package/src/web/src/model-picker.ts +1 -1
- package/src/web/src/palette.ts +1 -1
- package/src/web/src/protocol.ts +3 -39
- package/src/web/src/sessions.ts +1 -1
- package/src/web/src/ui-catalog.ts +4 -18
- package/src/web-assets.ts +47 -0
- package/src/weights.ts +55 -9
- package/docs/archive/investigations/curve-runs/bisector-route.json +0 -98
- package/docs/archive/investigations/curve-runs/bisector-trace.json +0 -1393
- package/docs/archive/investigations/curve-runs/curve-terrain.json +0 -1179
- package/docs/archive/investigations/curve-runs/curve-terrain.svg +0 -42
- package/docs/archive/investigations/hlg-ab.html +0 -34
- package/docs/archive/investigations/hlg-explorer.html +0 -238
- package/docs/archive/investigations/hlg-figs/cliff.svg +0 -1
- package/docs/archive/investigations/hlg-figs/cross.svg +0 -1
- package/docs/archive/investigations/hlg-figs/edges.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-grids.svg +0 -1
- package/docs/archive/investigations/hlg-figs/k20-peaks.svg +0 -1
- package/docs/archive/investigations/hlg-figs/lw-curve.svg +0 -1
- package/docs/archive/investigations/hlg-figs/terrain.svg +0 -1
- package/docs/archive/investigations/hlg-report.html +0 -262
- package/docs/archive/investigations/hlg-runs/hlg-ab.json +0 -149
- package/docs/archive/investigations/hlg-runs/hlg-blind-prompts.md +0 -87
- package/docs/archive/investigations/hlg-runs/hlg-blind.json +0 -107
- package/docs/archive/investigations/hlg-runs/hlg-cliff.json +0 -197
- package/docs/archive/investigations/hlg-runs/hlg-cross.json +0 -505
- package/docs/archive/investigations/hlg-runs/hlg-edge.json +0 -388
- package/docs/archive/investigations/hlg-runs/hlg-eval1.json +0 -25
- package/docs/archive/investigations/hlg-runs/hlg-frontier.json +0 -86
- package/docs/archive/investigations/hlg-runs/hlg-grid.json +0 -242
- package/docs/archive/investigations/hlg-runs/hlg-grid2.json +0 -343
- package/docs/archive/investigations/hlg-runs/hlg-lw-coarse.json +0 -110
- package/docs/archive/investigations/hlg-runs/hlg-map.json +0 -383
- package/docs/archive/investigations/hlg-runs/run0-original.log +0 -159
- package/docs/archive/investigations/hlg-runs/runA-recommended.log +0 -131
- package/docs/archive/investigations/hlg-runs/runAA-cliff-cpm5.log +0 -26
- package/docs/archive/investigations/hlg-runs/runAB-grid2-e4b.log +0 -18
- package/docs/archive/investigations/hlg-runs/runAC-peak-e4b-partial.log +0 -25
- package/docs/archive/investigations/hlg-runs/runAD-ab-samples.log +0 -8
- package/docs/archive/investigations/hlg-runs/runAE-edge-e4b.log +0 -51
- package/docs/archive/investigations/hlg-runs/runAF-logit-calibration.log +0 -10
- package/docs/archive/investigations/hlg-runs/runAG-eval-config2.log +0 -16
- package/docs/archive/investigations/hlg-runs/runB-gain-variants.log +0 -192
- package/docs/archive/investigations/hlg-runs/runC-pivots.log +0 -162
- package/docs/archive/investigations/hlg-runs/runD-oetf.log +0 -132
- package/docs/archive/investigations/hlg-runs/runE-eotf.log +0 -162
- package/docs/archive/investigations/hlg-runs/runF-shaper-working.log +0 -86
- package/docs/archive/investigations/hlg-runs/runG-shaper-sweep.log +0 -101
- package/docs/archive/investigations/hlg-runs/runH-wsweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runI-asweep.log +0 -176
- package/docs/archive/investigations/hlg-runs/runJ-knobmap.log +0 -49
- package/docs/archive/investigations/hlg-runs/runK-frontier.log +0 -19
- package/docs/archive/investigations/hlg-runs/runL-widemap.log +0 -65
- package/docs/archive/investigations/hlg-runs/runM-frontier-emb.log +0 -15
- package/docs/archive/investigations/hlg-runs/runN-verify.log +0 -116
- package/docs/archive/investigations/hlg-runs/runO-frontier-seed5000.log +0 -15
- package/docs/archive/investigations/hlg-runs/runP-grid-AxSm.log +0 -15
- package/docs/archive/investigations/hlg-runs/runQ-grid-tgW.log +0 -15
- package/docs/archive/investigations/hlg-runs/runR-blind-transcript.log +0 -79
- package/docs/archive/investigations/hlg-runs/runS-cross-coherence.log +0 -33
- package/docs/archive/investigations/hlg-runs/runT-cross-cpm5.log +0 -33
- package/docs/archive/investigations/hlg-runs/runU-cross-12b.log +0 -33
- package/docs/archive/investigations/hlg-runs/runV-lw-e4b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runW-lw-12b.log +0 -22
- package/docs/archive/investigations/hlg-runs/runX-lw-cpm5.log +0 -22
- package/docs/archive/investigations/hlg-runs/runY-cliff-e4b.log +0 -26
- package/docs/archive/investigations/hlg-runs/runZ-cliff-12b.log +0 -26
- package/docs/archive/mlx-bun-lab-report.html +0 -275
- package/docs/archive/wwdc-mlx-bun.html +0 -848
- package/docs/design/adapters-end-to-end.md +0 -205
- package/docs/design/batching-perf-path.md +0 -255
- package/docs/design/batching-v2-plan.md +0 -529
- package/docs/design/bucketing-stage.md +0 -197
- package/docs/design/compat-cli-surface-design.md +0 -230
- package/docs/design/docs-reorg-plan.md +0 -165
- package/docs/design/dspark-serving-program.md +0 -371
- package/docs/design/dspark-speculative-decoding.md +0 -318
- package/docs/design/grammar-spec-batching-integration.md +0 -301
- package/docs/design/memory-inference-path.md +0 -422
- package/docs/design/memory-synthesis.md +0 -287
- package/docs/design/memory-system.md +0 -237
- package/docs/design/mlx-lm-serving-execution-seam.md +0 -81
- package/docs/design/mlx-lm-tool-parity-plan.md +0 -545
- package/docs/design/omlx-adoption-map.md +0 -98
- package/docs/design/orpo-dynamic-lambda.md +0 -220
- package/docs/design/orpo-future-enhancements.md +0 -340
- package/docs/design/paged-kv-cache.md +0 -214
- package/docs/design/parallel-slots.md +0 -567
- package/docs/design/parity-tier-dag.md +0 -161
- package/docs/design/repo-cleanup-plan.md +0 -231
- package/docs/design/runtime-isolation.md +0 -263
- package/docs/design/segmented-backward-training.md +0 -543
- package/docs/design/spec-decode-larger-targets.md +0 -229
- package/docs/design/ssd-kv-cold-tier.md +0 -415
- package/docs/design/structured-output.md +0 -484
- package/docs/design/tauri-desktop-app.md +0 -127
- package/docs/design/the-dreaming-master-plan.md +0 -445
- package/docs/design/turboquant-kv.md +0 -235
- package/docs/design/turboquant-weights.md +0 -467
- package/docs/design/w4a16-compute-precision-spike.md +0 -232
- package/docs/design/web-chat-beat-matrix.md +0 -315
- package/docs/design/write-pipeline-entity.md +0 -468
- package/docs/planning/IDEAS.md +0 -69
- package/docs/reference/embedding.md +0 -197
- package/docs/reference/features-matrix.md +0 -101
- package/docs/reference/orpo-quickstart.md +0 -135
- package/src/flags.ts +0 -11
- /package/docs/{design → archive/investigations}/colibri-glm52-port.md +0 -0
- /package/docs/{planning → archive/planning}/chunk-task-roadmap.md +0 -0
- /package/docs/{planning → archive/planning}/curve-sampler-research-plan.md +0 -0
- /package/docs/{planning → archive/planning}/memory-docs-and-dag-plan.md +0 -0
- /package/docs/{planning → archive/planning}/pi-integration-review.md +0 -0
- /package/docs/{planning → archive/planning}/release-notes-v0.2.0.md +0 -0
- /package/src/{memory-plan.ts → admission-plan.ts} +0 -0
- /package/{docs/archive/investigations → src/serve/assets}/curve-terrain.html +0 -0
- /package/{docs/dag → src/serve/assets}/training-inference-map.html +0 -0
|
@@ -1,543 +0,0 @@
|
|
|
1
|
-
# Segmented backward: long-context LoRA training that beats the reference
|
|
2
|
-
|
|
3
|
-
**Status: Phase A (MiniCPM5) + Phase B (e4b) BOTH WORK — 2026-06-16.** MiniCPM5:
|
|
4
|
-
bit-exact, no leak, chunk-eval 95.10 (beats the 91.70 baseline). e4b: forward
|
|
5
|
-
bit-exact, grads bf16-class, trains **all 42 layers at 8K (17.5 GB)** where
|
|
6
|
-
`mlx_lm.lora --grad-checkpoint` OOMs trying the same (verified — mlx-lm must drop
|
|
7
|
-
to its default 16 trainable layers, 25.7 GB, to fit 8K). §1–8 are the original
|
|
8
|
-
design dossier; **§9 = Phase A results, §10 = Phase B (e4b) results — read those
|
|
9
|
-
first for current state.**
|
|
10
|
-
|
|
11
|
-
## 0. The one-paragraph version
|
|
12
|
-
|
|
13
|
-
mlx-bun (and Python mlx) gradient checkpointing does NOT stream the backward —
|
|
14
|
-
it holds all 42 layers' recompute activations at once, so e4b's training backward
|
|
15
|
-
spikes to **23 GB live @2048** (resting is only 6.95 GB) and **crashes at 4096+**.
|
|
16
|
-
We proved a fix — **segmented backward** — that forces per-segment streaming:
|
|
17
|
-
forward saves detached boundary activations at segment edges, then backprop runs
|
|
18
|
-
segment-by-segment via `mlx_vjp` (the shipped production implementation — see §9.4;
|
|
19
|
-
an earlier surrogate-loss `value_and_grad` approach was tried but leaked ~32 MB/seg/step
|
|
20
|
-
and was replaced). Only one segment's activations are ever live.
|
|
21
|
-
On an activation-dominated toy it beat per-layer
|
|
22
|
-
checkpointing (4.26 vs 5.97 GB) with **bit-exact gradients**. Extrapolated to e4b
|
|
23
|
-
this lands at **~10 GB @8K** — fitting on the 32 GB M1 Max, where the optiq/mlx-lm
|
|
24
|
-
reference cannot (it crashes on e4b @2048+). Next: wire it into the trainer,
|
|
25
|
-
MiniCPM5 first (clean), then e4b (handle KV-sharing + per-layer-input + the
|
|
26
|
-
O(L²) full-attention layers + mixed-precision quant in the segmentation plan).
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 1. What is already done and CORRECT (do not redo)
|
|
31
|
-
|
|
32
|
-
All committed-worthy, validated this session:
|
|
33
|
-
|
|
34
|
-
- **Chunk fine-tune experiment (the original goal) — DONE on MiniCPM5-1B.**
|
|
35
|
-
Baseline 11.89/100 → fine-tuned **91.70/100** on lucien's 25-case chunk holdout
|
|
36
|
-
(valid-JSON rate 12% → 100%). Full mlx-bun loop: train → save adapter (hot-swap
|
|
37
|
-
format) → `AdapterManager.mount` → generate. Writeup:
|
|
38
|
-
`docs/archive/investigations/chunk-finetune-experiment.md`. Scripts: `scripts/chunk-eval.ts`,
|
|
39
|
-
`scripts/chunk-finetune.ts`, `scripts/chunk-filter.ts`. Adapter:
|
|
40
|
-
`~/.cache/mlx-bun-finetunes/minicpm5-chunk-final`.
|
|
41
|
-
|
|
42
|
-
- **`ops.sdpa` is CORRECT** (NOT the inverse of an earlier wrong claim). Validated
|
|
43
|
-
vs autograd ground truth (`scripts/sdpa-vs-manual.ts`): dQ/dK/dV all 0.00%. Use
|
|
44
|
-
it for training. It's O(L²) memory in the backward (mlx autographs the fused op).
|
|
45
|
-
|
|
46
|
-
- **Flash dK kernel FIXED.** The dKV Metal kernel emitted dK with its (Tkv,D) axes
|
|
47
|
-
transposed in the buffer; fix in `flashBackward` (`src/model/flash-attention.ts`):
|
|
48
|
-
`reshape(ops.contiguous(transposeAxes(dK,[0,1,3,2])), [B,Hkv,Tkv,D])`. Validated
|
|
49
|
-
vs autograd: dK 0.06% non-GQA, 0.12% GQA D=256 (e4b geom). flash is now correct +
|
|
50
|
-
O(L) memory. (But flash is ~30× slower per iter and crashes on e4b at 42-layer
|
|
51
|
-
scale — see §6. Default training attn is ops.sdpa; flash is opt-in via
|
|
52
|
-
`MLX_BUN_TRAIN_ATTN=flash`.)
|
|
53
|
-
|
|
54
|
-
- **Combined-eval fix in the trainer.** `trainer.ts` used to eval `value` (forward)
|
|
55
|
-
separately via `value.toFloat32()` before `globalNorm(grads)` — that frees the
|
|
56
|
-
forward and forces the backward to recompute+hold everything, crippling
|
|
57
|
-
checkpointing. Now one `evalAll([value, ...grads])` (Python's `mx.eval(v,g)`
|
|
58
|
-
pattern). Verified no MiniCPM5 regression. Toy: separate-eval 0.3% reduction →
|
|
59
|
-
combined-eval 16%.
|
|
60
|
-
|
|
61
|
-
- **Per-step memory instrumentation.** `MLX_BUN_MEM_LOG=1` logs
|
|
62
|
-
`PEAK(live) / active / cache` per step in `trainer.ts`.
|
|
63
|
-
|
|
64
|
-
## 2. The diagnosis (why naive checkpointing fails) — measured, not guessed
|
|
65
|
-
|
|
66
|
-
Measurement methodology (the key unlock, courtesy of the active-vs-cache insight):
|
|
67
|
-
`mlx_get_peak_memory` = **peak LIVE (active)**, NOT active+cache (proved: toy
|
|
68
|
-
active-after 8.66 + cache-after 6.61 = 15.27 > peak 14.90, impossible if peak were
|
|
69
|
-
total). MLX holds freed buffers in a **cache** (not returned to OS) so RSS overstates
|
|
70
|
-
live memory; use `activeMemory()` / `cacheMemory()` / `clearCache()` to separate
|
|
71
|
-
them (`src/mlx/ffi.ts`). Caveat from the MoE expert-offload work
|
|
72
|
-
(`docs/archive/investigations/expert-offload-single-user-moe.md`): `clearCache()` does NOT
|
|
73
|
-
reliably return device-buffer RAM to the OS — only the mmap clean-page path does.
|
|
74
|
-
|
|
75
|
-
Facts established:
|
|
76
|
-
- **Forward memory is FLAT** (~8 GB at any seq) — the forward streams fine. The
|
|
77
|
-
whole per-token cost is in the BACKWARD.
|
|
78
|
-
- **e4b @2048 (ops.sdpa + grad-ckpt): peak-live 23.2 GB, resting 6.95 GB.** The
|
|
79
|
-
16 GB spike is per-layer MLP/hidden activations held across ALL 42 layers — the
|
|
80
|
-
checkpointed backward does NOT stream.
|
|
81
|
-
- **Checkpointing works and matches Python** (toy: both remove exactly 2.28 GB).
|
|
82
|
-
The earlier "checkpoint is 0%/broken" claim was a measurement artifact
|
|
83
|
-
(separate-eval + peak-vs-live confusion). It's NOT broken — it just doesn't
|
|
84
|
-
stream, and neither does Python's (optiq crashes on e4b @2048+ too).
|
|
85
|
-
- **e4b @4096 crashes immediately at ~7 GB RSS** (not gradual OOM). A 28 GB
|
|
86
|
-
`set_memory_limit` forcing cache eviction/reuse did NOT help → it's a genuine
|
|
87
|
-
backward-eval allocation failure, not cache non-reuse.
|
|
88
|
-
- Memory scales LINEARLY in seq: `peak(L) ≈ 7.7 GB + 7.6 MB/token`. @4096 ≈ 38 GB
|
|
89
|
-
(> 32 → crash). At the IDEAL streamed rate (~0.2–0.5 MB/tok) e4b@8K ≈ 10 GB.
|
|
90
|
-
|
|
91
|
-
## 3. The fix: segmented backward (PROVEN)
|
|
92
|
-
|
|
93
|
-
Reference implementation + proof: **`scripts/ckpt-mem-test.ts`** (`SEG=n` mode).
|
|
94
|
-
|
|
95
|
-
Algorithm:
|
|
96
|
-
1. **Forward, saving boundaries.** Run the layer stack; at each segment edge (group
|
|
97
|
-
of `n` layers) materialize (`eval`) and **detach** (`stopGradient`) the hidden
|
|
98
|
-
state into a boundary leaf. Discard intra-segment interiors. Boundaries are
|
|
99
|
-
small (one [B,L,hidden] per edge) and cheap.
|
|
100
|
-
2. **Compute the loss** from the final hidden (LM head + response-only CE) and its
|
|
101
|
-
gradient w.r.t. the final hidden — this `dh_out` seeds the last layer segment.
|
|
102
|
-
3. **Backward, reverse over segments.** For each segment k (last→first), call
|
|
103
|
-
**`mlx_vjp`** (bound as `Vjp` in `src/mlx/autograd.ts`) over
|
|
104
|
-
`[boundary_leaf_k, ...segment_LoRA_params]` with cotangent `dh_out`. It returns
|
|
105
|
-
`[dh_in, ...dLoRA_segment]`. `dh_in` becomes `dh_out` for segment k−1; accumulate
|
|
106
|
-
`dLoRA`. Only segment k's activations are live during its vjp.
|
|
107
|
-
**NOTE:** an earlier design used a surrogate-loss `value_and_grad` here
|
|
108
|
-
(`sum(stop_grad(dh)⊙segment_forward(...))`). That proved to leak ~32 MB per
|
|
109
|
-
segment per step (linear, no plateau) and was replaced by `mlx_vjp` in §9.4.
|
|
110
|
-
The production code uses `Vjp`/`mlx_vjp` throughout — not surrogate-loss
|
|
111
|
-
`value_and_grad`.
|
|
112
|
-
|
|
113
|
-
Why it's exact: `d/dx sum(stop_grad(dh)⊙y) = (dy/dx)ᵀ dh = dLoss/dx`, and likewise
|
|
114
|
-
for params — this is reverse-mode AD done in segments. Verified **0.000%** vs the
|
|
115
|
-
full `value_and_grad`.
|
|
116
|
-
|
|
117
|
-
Proof numbers (activation-dominated toy, `L=8192 D=1024 LAYERS=24`, the e4b regime):
|
|
118
|
-
|
|
119
|
-
| method | peak | grads |
|
|
120
|
-
|---|---|---|
|
|
121
|
-
| full, no checkpoint | 6.81 GB | — |
|
|
122
|
-
| per-layer `Checkpoint` | 5.97 GB | — |
|
|
123
|
-
| **segmented (SEG=4)** | **4.26 GB** (active-live 2.52 ≈ resting 1.68 + 1 segment) | **0.000%** |
|
|
124
|
-
| segmented (SEG=2) | 4.66 GB | 0.000% |
|
|
125
|
-
|
|
126
|
-
Segmented beats per-layer checkpointing and approaches the ideal floor. **Optimal
|
|
127
|
-
granularity exists** (SEG=4 < SEG=2): a few layers/segment keeps kernel fusion while
|
|
128
|
-
still streaming. NOT all-at-once (spikes), NOT each-tiny-op (loses fusion).
|
|
129
|
-
|
|
130
|
-
Recompute cost: each segment's backward recomputes its own layers' forward once →
|
|
131
|
-
total recompute overhead ≈ **1× forward, independent of segment count**. So segment
|
|
132
|
-
count is a knob for PEAK, with ~fixed time cost (plus minor per-`value_and_grad`
|
|
133
|
-
and boundary-materialization overhead). The wall-clock target (finish the chunk
|
|
134
|
-
task in budget) is mostly set by this fixed 1× recompute, not the segment count.
|
|
135
|
-
|
|
136
|
-
## 4. Integration plan
|
|
137
|
-
|
|
138
|
-
### Phase A — MiniCPM5-1B first (clean real-model proof)
|
|
139
|
-
Plain 24-layer Llama: no KV-sharing, no per-layer-input, no sliding window, uniform-ish
|
|
140
|
-
quant. The trivial case to validate the machinery on a real model.
|
|
141
|
-
|
|
142
|
-
1. **Model:** add `Gemma4Model`/`MiniCPM5Model` method `runLayerRange(h, aIdx, bIdx,
|
|
143
|
-
cache, ...) -> h'` that runs layers `[aIdx, bIdx)` on hidden `h` (the existing
|
|
144
|
-
`forwardLayers` loop body, parameterized by range). Keep `forwardLayers` as the
|
|
145
|
-
non-segmented path.
|
|
146
|
-
2. **Trainer:** add a `segmented` training path (flag `TrainConfig.segments` or auto;
|
|
147
|
-
gate alongside the existing `gradCheckpoint`). It:
|
|
148
|
-
- runs the segmented forward (save detached boundaries),
|
|
149
|
-
- computes response-only CE loss and `dFinalHidden` via a `Vjp` of the loss head
|
|
150
|
-
with scalar cotangent 1.0 (already memory-bounded),
|
|
151
|
-
- runs the backward per-segment using **`mlx_vjp`** (`Vjp` in
|
|
152
|
-
`src/mlx/autograd.ts`): for each segment k (last→first), calls
|
|
153
|
-
`vjp.apply([boundary_k, ...LoRA_k], [dh_out])` to get `[dh_in, ...dLoRA_k]`,
|
|
154
|
-
threads `dh_in` as `dh_out` for segment k−1, and accumulates `dLoRA` — no
|
|
155
|
-
surrogate scalar, no `value_and_grad` (see §9.4 for why that approach leaked
|
|
156
|
-
~32 MB/seg/step and was replaced),
|
|
157
|
-
- also handles the embedding + (e4b) per-layer-input grads from the first
|
|
158
|
-
segment's `dh_in`.
|
|
159
|
-
3. **Validate:** grads bit-match the current trainer (response-only CE, same seed);
|
|
160
|
-
measure peak drop with `MLX_BUN_MEM_LOG=1`. Expect a clear reduction even though
|
|
161
|
-
MiniCPM5 already fits — it's the template.
|
|
162
|
-
|
|
163
|
-
### Phase B — e4b (the goal). Extra boundary state to thread across segments:
|
|
164
|
-
- **KV-shared donor cache.** Layers [0,24) are donors (own caches); [24,42) are
|
|
165
|
-
sharers reusing the last same-type donor's K/V. Concretely (verified in
|
|
166
|
-
`gemma4.ts`: `numDonors = numHiddenLayers − numKvSharedLayers = 24`,
|
|
167
|
-
`previousKvs`/`cacheIndex`): all sliding sharers reuse **donor 22**'s K/V, all
|
|
168
|
-
full sharers reuse **donor 23**'s K/V. So when a sharer falls in a later segment
|
|
169
|
-
than its donor, **save that donor's K/V (rope-applied) as a detached boundary**
|
|
170
|
-
and thread it into the sharer's segment forward AND its recompute. K/V are tiny
|
|
171
|
-
([B,2,L,256] ≈ 1 KB/tok per donor), so this is cheap. (Note: `runCheckpointedLayer`
|
|
172
|
-
already threads shared K/V as checkpoint outputs — reuse that plumbing.)
|
|
173
|
-
- **Per-layer-input** (`computePerLayerInputs`): [B,L,42,256] computed once from the
|
|
174
|
-
embedding, sliced per layer. Compute it in the forward, **detach + save as a
|
|
175
|
-
boundary**, slice per layer inside each segment. Its gradient (back to the embed /
|
|
176
|
-
projection) accumulates from the per-segment grads.
|
|
177
|
-
- **Sliding vs full attention.** 7 full-attention layers (5,11,17,23,29,35,41); the
|
|
178
|
-
rest sliding (window 512). With ops.sdpa the **full layers are O(L²)** in the
|
|
179
|
-
backward (≈1 GB bf16 @8K for the [1,8,L,L] scores); sliding are O(L·512). The
|
|
180
|
-
segmentation must treat a full layer as a heavy unit (see §5).
|
|
181
|
-
- **Masks:** one per attention type (sliding/full), built at offset 0; pass into each
|
|
182
|
-
segment (cheap; the sliding [L,L] mask is the only quadratic term besides the full
|
|
183
|
-
attention — already handled by the current trainer).
|
|
184
|
-
|
|
185
|
-
## 5. The segmentation strategy (the part to get right)
|
|
186
|
-
|
|
187
|
-
> **2026-07-02 CORRECTION — this section's cost model is WRONG, measured.**
|
|
188
|
-
> The full-attention isolation planner was built and A/B'd @8K on real chunk
|
|
189
|
-
> rows (scripts/experiments/seg-isolation-smoke.ts, MLX_BUN_SEG_MEM_LOG=1
|
|
190
|
-
> phase probes): ZERO peak win (18.09 vs 18.02 GB seg2). The premise "sliding
|
|
191
|
-
> layers are O(L·window) in the backward" is false — mlx's sdpa BACKWARD
|
|
192
|
-
> materializes the O(L²) scores for EVERY layer (~3.5 GB/layer @8K e4b; the
|
|
193
|
-
> sliding window is only an additive mask; a sliding PAIR costs +7.1 GB ≈
|
|
194
|
-
> sliding+full's +7.15). The worst segment is therefore set by LAYER COUNT
|
|
195
|
-
> alone and `segment_size` is the whole knob: **seg1 = 14.59 GB @8K, +3% step
|
|
196
|
-
> time, loss identical — fits the 24 GB M4 Pro today.** The head vjp adds
|
|
197
|
-
> ~3 GB (full-[M,V] responseOnlyCe — kernel-review backlog #8). Going below
|
|
198
|
-
> ~14 GB needs that head fix plus an O(L)-memory attention backward (flash
|
|
199
|
-
> track), not smarter segmentation. Section kept for the (still-correct)
|
|
200
|
-
> donor/boundary constraints; ignore its sliding-vs-full peak arithmetic.
|
|
201
|
-
|
|
202
|
-
NOT a naive "divide by N". Compute the split points from the model:
|
|
203
|
-
|
|
204
|
-
**Constraints & inputs (all readable from the model/config):**
|
|
205
|
-
- Layer attention type per index (`layer_types`): sliding vs full. Full = O(L²) heavy.
|
|
206
|
-
- KV donor/sharer boundary at layer 24 (donors below, sharers above); donor→sharer
|
|
207
|
-
edges that must become saved boundaries (donors 22, 23).
|
|
208
|
-
- Per-layer mixed-precision quant bits (e.g. MiniCPM5's OptiQ config has layer-0 at
|
|
209
|
-
8-bit, others 4/8 mixed; e4b's HF config is default 4-bit but the strategy must
|
|
210
|
-
read per-layer bits generically). Bits affect **compute time** per layer (8-bit
|
|
211
|
-
matmul slower than 4-bit), slightly the weight residency — NOT the activation size.
|
|
212
|
-
- Uniform per-layer activation size (hidden 2560 + MLP gate/up 2×10240, bf16).
|
|
213
|
-
|
|
214
|
-
**Memory cost model** (per candidate segmentation):
|
|
215
|
-
```
|
|
216
|
-
peak ≈ resting(model + grads + optimizer + saved boundaries)
|
|
217
|
-
+ max_over_segments( Σ_layers_in_seg uniform_activation(L)
|
|
218
|
-
+ Σ_full_layers_in_seg attn_OL2(L) ) [+ backward transient]
|
|
219
|
-
```
|
|
220
|
-
At 8K: uniform_activation ≈ 0.38 MB/tok·layer; attn_OL2(full, ops.sdpa) ≈ ~1 GB.
|
|
221
|
-
So a 6-layer segment with one full layer @8K ≈ 6·0.38·8192 ≈ 2.3 GB + ~1–2 GB full
|
|
222
|
-
≈ 3.3–4.3 GB; with resting ~6.95 GB that's ~10–11 GB. **To stay ≤10 GB, isolate
|
|
223
|
-
full-attention layers** (put each in a short segment) and group sliding layers.
|
|
224
|
-
|
|
225
|
-
**Time cost model:** total ≈ base(fwd+bwd) + 1×fwd recompute (≈fixed) +
|
|
226
|
-
per-segment `value_and_grad`/boundary overhead (∝ segment count, small). Weight
|
|
227
|
-
per-layer forward time by quant bits + attention type. Target: ≤ a chosen
|
|
228
|
-
wall-clock for the chunk task (e.g. N iters × per-iter ≤ budget).
|
|
229
|
-
|
|
230
|
-
**Natural alignment:** the 6-layer period (`sssssF`) makes 7 segments of 6 (one full
|
|
231
|
-
layer each) the obvious first cut, with the donor/sharer boundary at 24 respected
|
|
232
|
-
(segments 0–3 = donors, 4–6 = sharers). If peak still > target at 8K, split each
|
|
233
|
-
6-group around its full layer (e.g. [0–4 sliding][5 full] …) so no segment carries a
|
|
234
|
-
full layer plus many sliding layers. The optimizer picks split points to **minimize
|
|
235
|
-
peak subject to the time budget**, respecting: (a) KV donor→sharer boundaries become
|
|
236
|
-
saved K/V, (b) don't split mid-attention-pair unnecessarily, (c) keep ≥~3 layers per
|
|
237
|
-
segment for fusion.
|
|
238
|
-
|
|
239
|
-
**Deliverable:** a `planSegments(model, {L, peakTargetGB, timeBudget})` function that
|
|
240
|
-
returns the split indices + which donor K/V to save, computed from the above.
|
|
241
|
-
|
|
242
|
-
## 6. Notes, risks, alternatives
|
|
243
|
-
- **Flash vs ops.sdpa for the full layers.** Flash (now dK-correct) is O(L) and would
|
|
244
|
-
remove the full-attention O(L²) term entirely — but flash is ~30× slower per iter
|
|
245
|
-
and currently crashes on e4b at 42-layer scale (immediate C++ exception; separate
|
|
246
|
-
bug). With segmented backward, the full-attention O(L²) is only transient within
|
|
247
|
-
one segment, so ops.sdpa may suffice; revisit flash only if the full term blows the
|
|
248
|
-
budget at 8K AND its e4b crash is fixed.
|
|
249
|
-
- The chunk data needs ≥4096 (median 4092; 0% ≤2048; 98% ≤8192). Success = e4b
|
|
250
|
-
trains at 8192 ≤ ~10 GB AND the chunk eval (`scripts/chunk-eval.ts`) shows a
|
|
251
|
-
quality delta on e4b like MiniCPM5's.
|
|
252
|
-
- Training needs no env-flag sanitization (the no-vjp fused CustomKernels that
|
|
253
|
-
once required `MLX_BUN_PERF_KERNEL=0 MLX_BUN_FUSED_GELU=0` were deleted
|
|
254
|
-
2026-07-05; see docs/design/unified-engine-frontier-plan.md §6).
|
|
255
|
-
- MLX `Date.now()`/random caveats don't apply here; standard determinism via seed.
|
|
256
|
-
|
|
257
|
-
## 7. Key files
|
|
258
|
-
- `scripts/ckpt-mem-test.ts` — segmented-backward proof + memory harness (SEG/CKPT/REUSE, active/cache/peak).
|
|
259
|
-
- `scripts/ft-chunk-smoke.ts` — e4b train/fwd/infer smoke (GRAD_CKPT, MEM_LIMIT_GB, MLX_BUN_MEM_LOG).
|
|
260
|
-
- `scripts/sdpa-vs-manual.ts`, `scripts/flash-dk-debug.ts`, `scripts/flash-grad-test.ts` — gradient correctness.
|
|
261
|
-
- `scripts/chunk-{eval,finetune,filter}.ts` — the chunk experiment pipeline.
|
|
262
|
-
- `src/train/trainer.ts` — training loop (combined-eval, mem-log, gradCkpt ctx); contains the segmented path (`TrainConfig.segmentSize`).
|
|
263
|
-
- `src/train/forward.ts`, `src/train/loss.ts` — training forward + response-only CE.
|
|
264
|
-
- `src/model/gemma4.ts` — e4b model: `forwardLayers`, `runCheckpointedLayer`, `computePerLayerInputs`, KV-sharing (`numDonors`, `previousKvs`, `cacheIndex`), `layer_types`, `runLayerRange` (added for segmented backward).
|
|
265
|
-
- `src/model/minicpm5.ts` — MiniCPM5 (Phase A).
|
|
266
|
-
- `src/mlx/autograd.ts` (`ValueAndGrad`, `Vjp`), `src/mlx/checkpoint.ts` (`Checkpoint`), `src/mlx/ffi.ts` (memory fns, `stopGradient`, `evalAll`, `mlx_vjp`).
|
|
267
|
-
- Memory: `[[e4b-lora-training-seqlen-ceiling]]` (full findings), `[[opssdpa-dk-vjp-bug]]`, `[[three-level-fidelity-tree-model]]`.
|
|
268
|
-
|
|
269
|
-
## 8. Original first action (COMPLETED)
|
|
270
|
-
~~Phase A step 1: add `runLayerRange` to `MiniCPM5Model`, then a segmented training
|
|
271
|
-
path in `trainer.ts`, validate bit-exact grads + measure peak on
|
|
272
|
-
`scripts/ft-chunk-smoke.ts`-style MiniCPM5 run. Then Phase B (e4b) with the §5 plan.~~
|
|
273
|
-
|
|
274
|
-
**Both Phase A and Phase B are done — see §9 and §10.** The genuine remaining work
|
|
275
|
-
is `planSegments` full-attention isolation for the ~10 GB @8K e4b target: put each
|
|
276
|
-
O(L²) full-attn layer (5,11,17,23,29,35,41) in its own short segment so it isn't
|
|
277
|
-
materialized alongside sliding-layer activations. Currently 17.5 GB @8K (seg2);
|
|
278
|
-
resting is ~6.6 GB, so ~3.4 GB of transient budget remains. The mechanism and tunable
|
|
279
|
-
segmentation knob are proven; this is a peak-optimization step. See §10 close.
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
## 9. Phase A IMPLEMENTED + validated (2026-06-16, session 2)
|
|
284
|
-
|
|
285
|
-
Phase A is built and the mechanism is **proven on the real MiniCPM5 model**.
|
|
286
|
-
Two findings landed (one a win, one a blocker). Code:
|
|
287
|
-
- `src/model/minicpm5.ts` — `runLayerRange(h, aIdx, bIdx, cache)` (added;
|
|
288
|
-
`forwardLayers` now delegates to it). NOTE it does **not** dispose its input
|
|
289
|
-
`h` (the caller / autograd owns the boundary leaf).
|
|
290
|
-
- `src/train/segmented.ts` — `SegmentedBackward` class (build once, `.step(batch)`
|
|
291
|
-
per iter) + `planSegmentsBySize`. Reusable per-segment value_and_grads.
|
|
292
|
-
- `src/train/trainer.ts` — `TrainConfig.segmentSize` (0 = off; >0 = layers per
|
|
293
|
-
segment). Gated in `sftLoop`, mutually exclusive with `gradCheckpoint`,
|
|
294
|
-
MiniCPM5-only for now. `src/train/job.ts` threads `segment_size`.
|
|
295
|
-
- `src/mlx/array.ts` — `MlxArray.fromBytesCopy(bytes, shape, dtype)` (a COPYING
|
|
296
|
-
leaf constructor via `mlx_array_new_data`, page-aligned, unlike `fromView`).
|
|
297
|
-
- `src/train/loss.ts` — `responseOnlyCe` is now exported.
|
|
298
|
-
- Harness: `scripts/segmented-grad-test.ts` (correctness + peak + `LEAK_LOOP`),
|
|
299
|
-
`scripts/seg-debug.ts` (forward-fidelity bisect), `scripts/vag-leak-test.ts`
|
|
300
|
-
(minimal value_and_grad leak repro). `scripts/chunk-finetune.ts` takes `SEG=n`.
|
|
301
|
-
|
|
302
|
-
### 9.1 Correctness — the mechanism is exact
|
|
303
|
-
`scripts/segmented-grad-test.ts` (MiniCPM5, synthetic B=1 batch):
|
|
304
|
-
- **Under flash attention: grads bit-match the full value_and_grad — relNorm
|
|
305
|
-
0.0000%, maxAbs 0.0, loss identical.** The segmentation algorithm (forward
|
|
306
|
-
boundaries → loss-head dh → reverse per-segment surrogate) is reverse-mode AD
|
|
307
|
-
done in segments, exactly equal to one big backward. PROVEN.
|
|
308
|
-
- Run training with `MLX_BUN_TRAIN_ATTN=flash` for bit-exact segmented grads.
|
|
309
|
-
|
|
310
|
-
### 9.2 Forward-fidelity finding: `ops.sdpa` fused-eager ≠ autograd forward
|
|
311
|
-
Under the default `ops.sdpa`, the segmented grads diverge ~6% (relNorm) from the
|
|
312
|
-
full value_and_grad — **not** a segmentation bug. Root cause (bisected in
|
|
313
|
-
`scripts/seg-debug.ts`): mlx's fused `ops.sdpa` computes a **different bf16 result
|
|
314
|
-
in its eager forward (12.0712) than in its autograd-decomposed forward (12.0568)**
|
|
315
|
-
— a 0.12% gap, independent of LoRA (holds at lora scale 0). Flash uses one forward
|
|
316
|
-
for both → no gap. Consequences:
|
|
317
|
-
- The **existing non-segmented trainer already trains against the autograd forward
|
|
318
|
-
(12.0568) while inference uses the eager forward (12.0712)** — pre-existing, not
|
|
319
|
-
introduced here.
|
|
320
|
-
- Segmented saves boundaries with the eager forward but recomputes/backprops with
|
|
321
|
-
the autograd forward, so the dh seed is inconsistent under `ops.sdpa` → ~6% grad
|
|
322
|
-
perturbation (bf16-class; segmented's *loss* still matches the true eager forward
|
|
323
|
-
exactly). Fine for LoRA fine-tuning; use flash if you want exactness.
|
|
324
|
-
|
|
325
|
-
### 9.3 Memory — the win is real and large
|
|
326
|
-
`MLX_BUN_MEM_LOG=1`, MiniCPM5 (24 layers, hidden 1536), peak LIVE:
|
|
327
|
-
|
|
328
|
-
| seq | full backward | segmented (SEG=4) | saved |
|
|
329
|
-
|---|---|---|---|
|
|
330
|
-
| 512 | 3.15 GB | 1.87 GB | 1.3 GB |
|
|
331
|
-
| 2048 | 10.91 GB | 3.44 GB | **7.5 GB (3.2×)** |
|
|
332
|
-
| 4096 | **21–26 GB** (spikes) | 6–8 GB | ~3× |
|
|
333
|
-
|
|
334
|
-
This is the thesis confirmed: at 4096 the full backward spikes to 21–26 GB (would
|
|
335
|
-
crash e4b); segmented stays at 6–8 GB. Segmented makes long-context training FIT.
|
|
336
|
-
|
|
337
|
-
### 9.4 A per-segment memory leak — FOUND and FIXED (use mlx_vjp, not value_and_grad)
|
|
338
|
-
The first segmented implementation used the optiq surrogate-loss trick — a
|
|
339
|
-
`value_and_grad` of `sum(stop_grad(dh) (.) segment_forward(...))` per segment. It
|
|
340
|
-
**leaked ~32 MB of genuinely-live memory per segment per step** (linear, no
|
|
341
|
-
plateau: `LEAK_LOOP` 1.12 → 6.61 GB over 30 iters at SEG=4/2048; ~0.19 GB/step;
|
|
342
|
-
would OOM well before a 300-iter run). The leak survived `clearCache`,
|
|
343
|
-
`Bun.gc(true)`, `synchronize`, disposing every returned array, and reusing the
|
|
344
|
-
value_and_grads across steps. It was segmented-specific (the non-segmented trainer
|
|
345
|
-
is flat). Minimal repro that pins it to `value_and_grad`: `vag-leak-test`
|
|
346
|
-
**MODE=layers** (runLayerRange under value_and_grad) leaks ~6 MB/iter; **MODE=vjp**
|
|
347
|
-
(the same forward via mlx_vjp) is **flat**.
|
|
348
|
-
|
|
349
|
-
**Fix:** drive each segment's backward with **`mlx_vjp`** (the natural primitive:
|
|
350
|
-
forward + an explicit cotangent `dh`) instead of a surrogate-loss `value_and_grad`.
|
|
351
|
-
New binding `Vjp` in `src/mlx/autograd.ts` (+ `mlx_vjp` in `ffi.ts`). Per segment:
|
|
352
|
-
`vjp.apply([boundary, ...LoRA], [dh])` returns outputs (the segment output, unused)
|
|
353
|
-
and vjps `[dh_in, ...dLoRA]` — exactly what the chain needs, no surrogate scalar.
|
|
354
|
-
The loss head is likewise a vjp of `responseOnlyCe(finalNorm(boundary))` with a
|
|
355
|
-
scalar cotangent 1.0, returning the loss and `dh`. Result: **active is flat**
|
|
356
|
-
(0.93 GB × 15 iters in `LEAK_LOOP`; 1.04 GB × 12 iters end-to-end through the
|
|
357
|
-
trainer at SEQ=4096), with the memory win intact (peak 10.91 → 3.29 GB @2048).
|
|
358
|
-
|
|
359
|
-
NOTE on the OTHER memory fix that is also in place: mlx `eval` does **not** detach —
|
|
360
|
-
an eval'd array keeps its upstream graph. Each forward boundary is therefore copied
|
|
361
|
-
into a fresh leaf via `detachLeaf` (`MlxArray.fromBytesCopy`), otherwise each
|
|
362
|
-
boundary drags a whole layer-stack's activations (~0.1 GB per 4-layer segment of
|
|
363
|
-
*within-step* retention before this fix). This is independent of the vjp fix.
|
|
364
|
-
|
|
365
|
-
### 9.5 STATUS: Phase A complete. Next actions
|
|
366
|
-
Phase A (MiniCPM5) is done and production-ready: mechanism bit-exact (flash),
|
|
367
|
-
memory win confirmed, no leak, trains end-to-end through `scripts/chunk-finetune.ts`
|
|
368
|
-
(`SEG=n`). Remaining:
|
|
369
|
-
1. ✅ **DONE** — real 300-iter segmented fine-tune (SEG=4, SEQ=4096, ops.sdpa):
|
|
370
|
-
**peak 6.51 GB** (non-seg baseline was 25.47 GB), 35.6 min, val loss 0.231→0.189,
|
|
371
|
-
no leak (active flat at 1.04 GB the whole run). `chunk-eval` on the holdout:
|
|
372
|
-
**95.10/100** — EXCEEDS the non-segmented baseline (91.70) and the pre-tune
|
|
373
|
-
baseline (11.89). json/schema/labels/nonempty 100%, anchors 86%. Segmented
|
|
374
|
-
training produces a quality adapter. Adapter:
|
|
375
|
-
`~/.cache/mlx-bun-finetunes/minicpm5-chunk-segmented`.
|
|
376
|
-
2. ✅ **DONE — see §10.** **Phase B (e4b)** with the §5 plan (KV-sharing,
|
|
377
|
-
per-layer-input, full-attn O(L²) isolation). e4b `runLayerRange` added to
|
|
378
|
-
`Gemma4Model` with donor-KV / per-layer-input boundary threading;
|
|
379
|
-
`SegmentedBackwardGemma4` + vjp machinery wired into trainer, validated on real
|
|
380
|
-
e4b. Full details in §10.
|
|
381
|
-
|
|
382
|
-
---
|
|
383
|
-
|
|
384
|
-
## 10. Phase B (e4b) — BUILT + VALIDATED (2026-06-16)
|
|
385
|
-
|
|
386
|
-
**Status: e4b segmented backward works.** `SegmentedBackwardGemma4`
|
|
387
|
-
(`src/train/segmented.ts`) + the model-side `runLayerRange` are done, wired into
|
|
388
|
-
the trainer (Gemma4 path), and validated on the real e4b
|
|
389
|
-
(`scripts/segmented-grad-test-e4b.ts`):
|
|
390
|
-
- **Forward bit-exact** vs the full value_and_grad (loss identical at every L).
|
|
391
|
-
- **Grads: single-consumer donor reuse is BIT-EXACT** (2-segment split at 24:
|
|
392
|
-
0/258 targets off). With the natural 7-segment cut the donor K/V grad is ~0.97%
|
|
393
|
-
(relNorm) off the full backward — **bf16 non-associativity, established by
|
|
394
|
-
discriminating tests (NOT a logic bug, and NOT "irreducible because I must match
|
|
395
|
-
bf16" — that earlier inference was wrong):**
|
|
396
|
-
- single-consumer reuse is bit-exact (no sum to reorder);
|
|
397
|
-
- the error is FLAT in consumer-segment count (1→0%, 2→0.98%, 3→0.97%, 6→0.96%,
|
|
398
|
-
9→1.16% — jumps once, doesn't compound);
|
|
399
|
-
- it tracks SUMMAND count, not segment count: donor 22 (sliding, 15 sharers)
|
|
400
|
-
≈0.46%, donor 23 (full, 3 sharers) ≈0.000%;
|
|
401
|
-
- the donor-22 grad is grouping-DEPENDENT (~0.5% moving from a 2- vs 9-consumer
|
|
402
|
-
split) for BOTH bf16 AND fp32 accumulation — neither is grouping-invariant.
|
|
403
|
-
fp32 cross-segment accumulation does NOT help (identical at the natural cut,
|
|
404
|
-
marginally worse at fine cuts): the dominant non-associativity is mlx's
|
|
405
|
-
WITHIN-vjp bf16 cotangent sum per consumer, which regrouping the sharers
|
|
406
|
-
changes; only an fp32 forward could remove it. So bf16 is the right (simplest)
|
|
407
|
-
choice; ~1% is bf16-class and fine for LoRA. (The producer's `dh_in` depends on
|
|
408
|
-
`dKV`, so the donor-22 ~0.46% smears thinly across earlier layers — broad but
|
|
409
|
-
small, ~1.4% worst-target.)
|
|
410
|
-
- **The error is CONTROLLABLE BY GROUPING, not fixed by nature.** The lever is
|
|
411
|
-
the SEGMENT CUT — how a donor's sharers distribute across consumer segments —
|
|
412
|
-
NOT the accumulation dtype. The floor is bit-exact: with all of a donor's
|
|
413
|
-
sharers in ONE consumer segment (the 1-consumer cut) there is no sum to
|
|
414
|
-
reorder, 0/258 off. Fewer/larger consumer segments → tighter grad (toward
|
|
415
|
-
bit-exact) but higher peak; more/smaller → ~1% but lower peak. So if you ever
|
|
416
|
-
needed it tighter, keep a donor's sharers together (coarser sharer-side cut),
|
|
417
|
-
trading some peak for fidelity. You almost certainly won't — ~0.5–1% bf16-class
|
|
418
|
-
is fine for LoRA (CPM5 converged healthily to 95.10) — but the lever exists and
|
|
419
|
-
it is the cut, not the dtype.
|
|
420
|
-
- **Memory — VERIFIED vs the real reference (`mlx_lm.lora --train --grad-checkpoint`,
|
|
421
|
-
the way a user trains, oracle venv, freshly measured).** An earlier draft of
|
|
422
|
-
this doc compared against *mlx-bun's own* checkpointed full backward (22.96 GB
|
|
423
|
-
@2048, crash @4096) and claimed "the reference crashes at 4K" — that was WRONG:
|
|
424
|
-
mlx-bun's gradient checkpointing is itself ineffective
|
|
425
|
-
(`[[e4b-lora-training-seqlen-ceiling]]` pt 9), so it is NOT the honest baseline.
|
|
426
|
-
**GROUPING — they differ, and the first table I drew did not match them.**
|
|
427
|
-
mlx-lm checkpoints PER-LAYER: `grad_checkpoint(model.layers[0])` patches
|
|
428
|
-
`type(layer).__call__` to wrap every layer in `mx.checkpoint`, i.e. 42 groups of
|
|
429
|
-
1, fixed (no coarser/finer knob). Ours groups `segmentSize` layers per segment
|
|
430
|
-
(tunable). The honest, MATCHED-grouping comparison (both per-layer = group of 1,
|
|
431
|
-
all 42 layers, batch 1):
|
|
432
|
-
|
|
433
|
-
| L | mlx-lm (per-layer) | ours segSize=1 (per-layer) | ours segSize=6 |
|
|
434
|
-
|---|---|---|---|
|
|
435
|
-
| 2048 | 12.84 GB | **8.76 GB** | 11.05 |
|
|
436
|
-
| 4096 | 20.87 GB | **10.93 GB** | 16.06 |
|
|
437
|
-
| 8192 | **OOM** (25.72 @16 layers) | **15.29 GB** | 17.5 (seg2) |
|
|
438
|
-
|
|
439
|
-
So at matched grouping ours is lower at every length, AND at 8K mlx-lm OOMs
|
|
440
|
-
training all 42 layers (must drop to 16, 25.7 GB) while ours trains all 42.
|
|
441
|
-
**Two confounds make this not a pure segmentation-vs-checkpoint number, and I
|
|
442
|
-
have not isolated them:** (1) mlx-lm's `default_loss` materializes the full
|
|
443
|
-
`[1,L,262144]` logits + their grad (~4 GB @4K), while ours uses response-only CE;
|
|
444
|
-
(2) ours fully streams the backward (detached per-segment value_and_grads) vs
|
|
445
|
-
mlx-lm's per-layer checkpoint inside one backward. Both are legitimate mlx-bun
|
|
446
|
-
choices, but the headline gap is part CE-optimization, part mechanism — not all
|
|
447
|
-
"segmentation beats checkpointing." (segSize is the peak↔grad-fidelity knob:
|
|
448
|
-
finer = lower peak but more donor-KV bf16 deviation; §10 grad note.) Separately,
|
|
449
|
-
mlx-bun's OWN checkpoint is the broken one (23 GB @2048 vs mlx-lm's 12.8) — that's
|
|
450
|
-
why segmented matters *within* mlx-bun, and fixing it is orthogonal.
|
|
451
|
-
- **Trainer end-to-end** (e4b, SEQ=2048, SEG=4, real chunk data): peak 10 GB,
|
|
452
|
-
active flat (no leak), adapter saved.
|
|
453
|
-
- TWO contiguity fixes were essential: `detachLeaf` must force a row-major copy
|
|
454
|
-
(`ops.contiguous`) — the donor K/V are TRANSPOSED views and `rawBytes` reads
|
|
455
|
-
the buffer linearly (without this the forward was 4% wrong); and the producer
|
|
456
|
-
segment outputs contiguous donor K/V to match the row-major cotangent.
|
|
457
|
-
|
|
458
|
-
**To reach the ~10 GB @8K target** (currently 17.5 GB @8K, seg2): the resting is
|
|
459
|
-
~6.6 GB (e4b weights), so ~3.4 GB transient is the budget. Next is the §5
|
|
460
|
-
full-attention ISOLATION `planSegments` — put each O(L²) full-attn layer
|
|
461
|
-
(5,11,17,23,29,35,41) in its own short segment so it isn't materialized alongside
|
|
462
|
-
sliding-layer activations — plus possibly flash for the full layers (O(L); flash
|
|
463
|
-
crashes on e4b at multi-K today — a separate bug). The mechanism + tunable knob
|
|
464
|
-
are proven; this is a peak-optimization, not a blocker.
|
|
465
|
-
|
|
466
|
-
**Model side** (`src/model/gemma4.ts`, additive — `forwardLayers` untouched):
|
|
467
|
-
- `runLayerRange(h, aIdx, bIdx, cache, masks, perLayer, donorKvIn) -> {h, donorKvOut}`
|
|
468
|
-
— runs layers `[aIdx,bIdx)`, slices `perLayer` per layer, threads KV-shared
|
|
469
|
-
donors: a sharer reads its donor's K/V from `donorKvIn` (earlier segment) or
|
|
470
|
-
`donorKvOut` (same range); reused donors' K/V (e4b: {22,23}) are returned for
|
|
471
|
-
later segments, non-reused donors' K/V disposed. Does NOT dispose `h`.
|
|
472
|
-
- `makeTrainingMasks(cache, L)` — sliding + full masks, built once.
|
|
473
|
-
- `embedForSegmented(ids) -> {hScaled, perLayer}` — scaled input embedding (first
|
|
474
|
-
boundary) + the `[B,L,nLayers,width]` per-layer-input tensor.
|
|
475
|
-
- `reusedDonors` getter ({22,23} for e4b, ∅ for dense).
|
|
476
|
-
- **Validate first** (when GPU free): running the full range through `runLayerRange`
|
|
477
|
-
must bit-match `forwardLayers` (a forward-equivalence check on the 12B/e4b parity
|
|
478
|
-
harness) before trusting the backward.
|
|
479
|
-
|
|
480
|
-
- **PARITY vs mlx-lm — VERIFIED bit-exact (the correctness oracle).** Ran
|
|
481
|
-
`mlx_lm.lora`'s actual `default_loss` and `nn.value_and_grad` against ours on the
|
|
482
|
-
SAME fixed 2048-token input (`scripts/parity-vs-mlxlm.ts`,
|
|
483
|
-
`scripts/grad-parity-vs-mlxlm.ts`; mlx-lm side in `/tmp/mlxlm-*-parity.py`):
|
|
484
|
-
- **forward + loss:** verified across 6 mask spans (varying promptLen incl. the
|
|
485
|
-
edges ntoks=2047 and ntoks=1), checking the SUMMATION not just one number:
|
|
486
|
-
the **denominator** (our `M = (len-1)-max(0,promptLen-1)`) == mlx-lm's
|
|
487
|
-
`ntoks = mask.sum()` BIT-EXACT on every span; **response-only CE == full-masked
|
|
488
|
-
CE** on every span (so response-only is a pure memory optimization, not a
|
|
489
|
-
parity risk); the loss is BIT-EXACT vs mlx-lm on 4/6 spans and **0.5 float32-ulp**
|
|
490
|
-
(~1e-6, ~5e-5%) on 2 — a logsumexp/reduction f32 non-associativity (full-masked
|
|
491
|
-
shows the same residual, so it's NOT the response-only summation), far below
|
|
492
|
-
bf16 and harmless;
|
|
493
|
-
- **gradients (FULL value_and_grad = our non-segmented trainer): BIT-EXACT** — dB
|
|
494
|
-
over all 42 q_proj layers (identical injected A, B=0, rank 8, scale 20) matches
|
|
495
|
-
mlx-lm 0.000e+0. So mlx-bun's non-segmented training equals mlx-lm's bit-for-bit
|
|
496
|
-
(loss AND grad), extending the inference logit-parity oracle to training;
|
|
497
|
-
- **segmented gradients: ~2.3% (bf16-class)** off mlx-lm (q_proj dB) — the
|
|
498
|
-
donor-KV bf16 non-associativity (above), not a logic bug. So the SEGMENTED path
|
|
499
|
-
is NOT bit-exact with mlx-lm; it's bf16-class. Both `ops.sdpa` value_and_grads
|
|
500
|
-
(ours and mlx-lm's) use the same autograd forward, which is why FULL is exact.
|
|
501
|
-
(Config note: mlx-lm's default LoRA scale is 20.0; mlx-bun's `DEFAULT_TRAIN_CONFIG`
|
|
502
|
-
is 1.0 — a config difference, not correctness. The chunk runs used 1.0.)
|
|
503
|
-
|
|
504
|
-
**Trainer side — `SegmentedBackwardGemma4` (BUILT + VALIDATED, `src/train/segmented.ts`):**
|
|
505
|
-
same skeleton as the MiniCPM5 `SegmentedBackward`, with TWO additions — both are
|
|
506
|
-
implemented and verified in the Phase B results above.
|
|
507
|
-
|
|
508
|
-
(a) **per-layer-input is a pure constant boundary — NO gradient threading.** The
|
|
509
|
-
`per_layer_input_gate` / `per_layer_projection` LoRA live INSIDE the layer (applied
|
|
510
|
-
to the sliced `pls`), so they're differentiated within each segment's vjp normally.
|
|
511
|
-
The `perLayer` tensor itself feeds in as a constant (its grad would flow to the
|
|
512
|
-
embed / `per_layer_model_projection`, neither a LoRA target) — detached once and
|
|
513
|
-
sliced per segment; its cotangent is discarded.
|
|
514
|
-
|
|
515
|
-
(b) **the KV-shared donor K/V is a SECOND boundary stream WITH its own cotangent.**
|
|
516
|
-
The donor's K/V depend on the donor layer's q/k/v LoRA and are attended by every
|
|
517
|
-
sharer, so `dLoss/d(donor LoRA)` gets contributions from the donor's own attention
|
|
518
|
-
AND all sharers. Threading:
|
|
519
|
-
- **Forward (boundary-saving):** when a segment produces a reused donor's K/V
|
|
520
|
-
(`donorKvOut`), it is detached (rope-applied K + V, tiny — ~1 KB/tok) and saved as
|
|
521
|
-
a boundary; later sharer segments pass it in via `donorKvIn`.
|
|
522
|
-
- **Backward (reverse), two extra pieces:**
|
|
523
|
-
- a cotangent accumulator `dKV[d]` per reused donor d (init zero).
|
|
524
|
-
- a SHARER segment's vjp differentiates `[boundary_h, donorK, donorV, ...LoRA]`
|
|
525
|
-
with cotangent `[dh]`; its vjps for `donorK/donorV` are ACCUMULATED into `dKV[d]`.
|
|
526
|
-
- a DONOR segment (produces donor d's K/V) has a MULTI-OUTPUT forward
|
|
527
|
-
`[h_out, donorK, donorV]`; its vjp takes cotangents `[dh, dKV[d].k, dKV[d].v]`
|
|
528
|
-
(the `Vjp` class supports nOut>1 + multiple cotangents) and returns
|
|
529
|
-
`[dh_in, ...donor LoRA]`. This folds the sharers' gradient back into the donor.
|
|
530
|
-
- donors 22 (sliding) and 23 (full) are each reused by all sharers of their type;
|
|
531
|
-
if both live in one segment, that segment's forward outputs 5 arrays
|
|
532
|
-
`[h, k22, v22, k23, v23]` with 5 cotangents.
|
|
533
|
-
- **Segmentation (the §5 plan):** the donor/sharer split at layer 24 is respected and
|
|
534
|
-
the 7 full-attention layers (5,11,17,23,29,35,41) — each O(L²) in the backward
|
|
535
|
-
under ops.sdpa (~1 GB bf16 @8K) — are candidates for isolation into short segments;
|
|
536
|
-
the natural first cut is the 6-layer `sssssF` period → 7 segments of 6 (one full
|
|
537
|
-
layer each). `planSegments` from §5 returns the split indices + which donor K/V to
|
|
538
|
-
save. Full-attention ISOLATION for the ~10 GB @8K target remains the next
|
|
539
|
-
optimization step (currently 17.5 GB @8K, seg2 — see §10 close above).
|
|
540
|
-
|
|
541
|
-
Everything else (forward boundaries via `detachLeaf`, head vjp with cotangent 1.0,
|
|
542
|
-
reverse `dh` chain, `mlx_vjp` not surrogate) is identical to the MiniCPM5 path and
|
|
543
|
-
is verified end-to-end: peak 10 GB @2048, active flat (no leak), adapter saved.
|