Summary
I have a working MLX port of the training-path forward pass plus LoRA finetuning (needle finetune --backend mlx), and would like to know if there is interest in upstreaming it before I invest in rebasing it onto the current architecture.
Motivation
The current macOS GPU path depends on jax-metal, which pins jax==0.4.38, needs ENABLE_PJRT_COMPATIBILITY, and forces flash/remat/scan off. A native MLX backend removes the plugin and the pin, and runs on the current jax for everything else. During my usage of ee221ce, which my fork was based on, the metal extra also produced NaN losses in the first steps but trained cleanly after changing to MLX.
What was built
563 lines, 6 files, fork branch feat/mlx-finetune-backend.
needle/model/architecture_mlx.py:
- MLX forward of exactly what finetuning exercises (SimpleAttentionNetwork, quant=False). Flat param dict mirroring the flax tree
mx.checkpoint per MHC layer, mirroring nn.remat. dtype=bfloat16 follows the flax semantics (dense/attention in bf16
- norms, MHC mixing, sinkhorn, logits in f32). Confidence/contrastive heads, MTP and KV-window decode are out of scope.
needle/model/finetune_mlx.py:
- same data format, LoRA placement (five stacked attention kernels), AdamW + warmup/cosine, global-norm clip 1.0, masked CE, val holdout. Step compiled with
mx.compile. Writes the same adapter .pkl, so needle build --lora works unchanged.
- CLI:
--backend {jax,mlx} and --dtype {float32,bfloat16} on finetune (jax remains the default).
pyproject.toml:
train-mlx extra (mlx>=0.29, numpy, sentencepiece).
tests/test_mlx_parity.py:
- f32 forward parity vs the JAX model on the published checkpoint, argmax-identical, max-abs logit diff ~3e-3 (skips without jax+mlx+checkpoint).
Known gap
The port targets the needle2 architecture at ee221ce, and since that main has added sliding_window, global_layers, ladder_*, qkv_conv_taps, qk_head_dim, safetensors and depth rungs, none of which the MLX forward implements yet. Rebasing is a rewrite of the forward, hence this RFC first.
Questions
- Is a second forward implementation acceptable maintenance-wise, given the parity test pins it to the JAX reference?
- Would you prefer this as a replacement for the
metal extra or alongside it?
- Is the architecture on
main stable enough to port against now?
Happy to open a PR against main if there is interest.
Summary
I have a working MLX port of the training-path forward pass plus LoRA finetuning (
needle finetune --backend mlx), and would like to know if there is interest in upstreaming it before I invest in rebasing it onto the current architecture.Motivation
The current macOS GPU path depends on
jax-metal, which pinsjax==0.4.38, needsENABLE_PJRT_COMPATIBILITY, and forces flash/remat/scan off. A native MLX backend removes the plugin and the pin, and runs on the currentjaxfor everything else. During my usage of ee221ce, which my fork was based on, themetalextra also produced NaN losses in the first steps but trained cleanly after changing to MLX.What was built
563 lines, 6 files, fork branch
feat/mlx-finetune-backend.needle/model/architecture_mlx.py:mx.checkpointper MHC layer, mirroringnn.remat.dtype=bfloat16follows the flax semantics (dense/attention in bf16needle/model/finetune_mlx.py:mx.compile. Writes the same adapter.pkl, soneedle build --loraworks unchanged.--backend {jax,mlx}and--dtype {float32,bfloat16}onfinetune(jaxremains the default).pyproject.toml:train-mlxextra (mlx>=0.29, numpy, sentencepiece).tests/test_mlx_parity.py:Known gap
The port targets the needle2 architecture at ee221ce, and since that
mainhas addedsliding_window,global_layers,ladder_*,qkv_conv_taps,qk_head_dim, safetensors and depth rungs, none of which the MLX forward implements yet. Rebasing is a rewrite of the forward, hence this RFC first.Questions
metalextra or alongside it?mainstable enough to port against now?Happy to open a PR against main if there is interest.