Apple Silicon MLX Backend
Soup v0.25.0 adds a native MLX training backend for M1 to M4 Macs, running on unified memory without CUDA, Rosetta, or x86 emulation. Supervised fine-tuning is the only task it supports: every other task, DPO and GRPO included, is refused at config load. See the table below.
Install
pip install "soup-cli[mlx]"This pulls mlx>=0.20 and mlx-lm>=0.31.3 as optional dependencies. The mlx-lm floor was raised in v0.73.1, to the version the MLX supervised path is actually built against.
Enable the backend
Set backend: mlx in soup.yaml, or pass --backend mlx to soup train:
base: mlx-community/Llama-3.1-8B-Instruct-4bit
backend: mlx
task: sft
data:
train: data.jsonl
format: chatml
training:
epochs: 3
lr: 1e-4
batch_size: 2
lora:
r: 16
alpha: 32Supported tasks
| Task | MLX | Notes |
|---|---|---|
| sft | ✓ | LoRA and QLoRA (mlx-community 4bit models) |
| dpo | – | Refused at config load: upstream mlx-lm exposes no training helper. Use backend: transformers |
| grpo | – | Refused at config load, same reason |
| ppo | – | Refused — use the transformers backend |
| reward_model | – | Refused at config load, same reason |
| embedding | – | Refused at config load, same reason |
| pretrain | – | Refused at config load, same reason |
The MLX backend supports sft and nothing else: every other task is rejected by the same cross-validator, with the same message, before anything loads. The rows above differ in what you would use them for, not in how they fail.
Diagnostics
soup doctorOn Apple Silicon this reports MLX version, chip name, unified memory, and a recommended batch size.
Since v0.73.3, device detection knows this backend exists. Before that, detect_device() only probed PyTorch MPS and fell back to cpu, so an MLX run announced "CPU (no GPU detected)" — and the label was never the harm. That fallback also fired a "4bit quantization is not supported on CPU" warning and silently rewrote quantization: 4bit to none, on every MLX run that asked for 4-bit. With backend: mlx set, detection now resolves to MLX with the chip name, reports Apple unified memory in telemetry, and preserves 4bit for pre-quantized mlx-community checkpoints, which is a different thing from bitsandbytes NF4 and was never what that warning was written about. The CUDA-shaped analytical VRAM pre-flight is also skipped on this path, because Apple unified memory is managed by Metal rather than by a fixed VRAM pool.
Also since v0.73.3: [mlx] is a standalone install for supervised fine-tuning on local JSONL, JSON or CSV data. Do not add [train], which is the PyTorch and TRL stack the Transformers backend uses. A Hugging Face Hub or streaming dataset still needs datasets, because that data source owns the dependency; a local file path does not.
MLX-native recipes
llama3.1-8b-sft-mlx— M2+ 16GBqwen3-8b-sft-mlx— M2+ 16GBgemma3-9b-sft-mlx— M2+ 16GB
Use any of them with soup recipes use <name>.
Limitations
- Single-device only, no distributed MLX training.
- Base models must be MLX-format (typically from the
mlx-communityHF org). bitsandbytesis unused on this backend: quantization comes from the MLX model itself.- Unsloth backend is not MLX-compatible (they're separate execution paths).
- It reads neither
training.seednortraining.data_seed. Setting either is a warning, not a rejection, because a config valid on transformers should not become unloadable by switching backend. Seeding MLX for real needs a separate generator and is separate work. See seeds and reproducibility.
If you trained on this backend before v0.73.1, re-run it
Two defects meant an MLX run could complete successfully and be worthless, and both are repaired in v0.73.1.
backend: mlxnever dispatched to the MLX trainer at all. It fell through silently to the transformers path. And without a freeze first, what it saved was not an adapter but a full fine-tune: 172 tensors where 24 were expected on a 1.2B model.- The MLX
adapter_config.jsonsavedtarget_modulesunresolved, as{"keys": ["auto"]}. Loading that file dropped every LoRA tensor without a word, so generation with the adapter was bit-identical to the base model. Sinceautois the schema default, this was every MLX run that did not name its modules by hand. The file cannot be repaired after the fact; the run has to be redone.
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.