End-to-end verification of the examples/mlx_tictactoe.py GRPO example: what broke against
the current mlx-lm API, the fixes applied, and live training logs proving real gradient
updates on an Apple M1 GPU.
A fresh 90-game training run (15 steps × 6 games) with initial and final evaluation over 30 games each.
The example was written against an older mlx-lm release. On current versions it failed at three
points — the first two are API drift, the third is a fundamental limitation of training a quantized model.
| Symptom | Root cause | Type |
|---|---|---|
generate_step() got an unexpected keyword argument 'temp' |
The temp= argument was removed from generate(); sampling params now come from a sampler object. |
API |
module 'mlx.core' has no attribute 'log_softmax' |
log_softmax moved to mlx.nn. |
API |
[QuantizedMatmul::vjp] no gradient wrt the quantized weights |
The example loads a 4-bit quantized model. MLX cannot backprop through quantized weights, so full-model gradient updates are impossible. | Core |
Three targeted changes in examples/mlx_tictactoe.py — the key one converts training to a
QLoRA scheme so gradients flow through small trainable adapters instead of the frozen quantized base.
| Change | Detail | |
|---|---|---|
| Sampler API | temp=0.8 → sampler=make_sampler(temp=0.8) |
fixed |
| log-softmax | mx.log_softmax → nn.log_softmax |
fixed |
| QLoRA training | model.freeze() + linear_to_lora_layers(...), and
mx.value_and_grad(fn)(model) → nn.value_and_grad(model, fn)()
so only the ~1.47M LoRA params receive gradients. |
fixed |
mlx_quicktest.pyConfirms MLX, the Metal GPU, model loading, and generation before any training.
================================================== ART-MLX Environment Check ================================================== ✓ MLX available (version: 0.32.0) ✓ Device: Device(gpu, 0) ✓ MLX-LM available Loading mlx-community/Qwen2.5-0.5B-Instruct-4bit... ✓ Model loaded: mlx-community/Qwen2.5-0.5B-Instruct-4bit Testing generation with prompt: 'What is 2 + 2?' ✓ Generation working Response: The answer is 4. ✓ ART MLX backend importable ================================================== ✓ Ready for GRPO training! ==================================================
mlx_tictactoe.pyFull GRPO loop: load → evaluate → train (15 steps × 6 games) → re-evaluate. Loss changes and win rate fluctuates between steps, confirming real weight updates.
============================================================ ART-MLX Tic Tac Toe — GRPO Training ============================================================ Loading: mlx-community/Qwen2.5-0.5B-Instruct-4bit ✓ Loaded in 0.5s ✓ Device: Device(gpu, 0) ✓ LoRA adapters applied (1,466,368 trainable params) GRPOTrainer initialized with lr=1e-05 Initial evaluation (30 games)... Win rate: 53.3% W/L/D: 16/9/5 Training: 15 steps × 6 games = 90 total games ------------------------------------------------------------ Step 1/15: win_rate=83% loss= 0.3247 updates=26 (5.9s) Step 2/15: win_rate=33% loss=-3.1994 updates=28 (6.6s) Step 3/15: win_rate=83% loss= 3.1662 updates=27 (6.4s) Step 4/15: win_rate=50% loss= 0.0065 updates=24 (5.8s) Step 5/15: win_rate=50% loss= 0.6881 updates=28 (6.7s) Step 6/15: win_rate=67% loss= 2.8066 updates=24 (5.8s) Step 7/15: win_rate=50% loss= 0.6254 updates=28 (6.7s) Step 8/15: win_rate=33% loss= 1.4201 updates=26 (6.4s) Step 9/15: win_rate=33% loss= 1.0067 updates=22 (5.3s) Step 10/15: win_rate=50% loss= 0.9868 updates=27 (6.5s) Step 11/15: win_rate=83% loss=-0.8903 updates=26 (6.3s) Step 12/15: win_rate=50% loss= 1.2905 updates=25 (6.0s) Step 13/15: win_rate=67% loss= 2.2286 updates=23 (5.5s) Step 14/15: win_rate=83% loss= 0.0288 updates=24 (5.8s) Step 15/15: win_rate=50% loss= 0.4528 updates=26 (6.2s) ------------------------------------------------------------ Final evaluation (30 games)... Win rate: 56.7% W/L/D: 17/10/3 ============================================================ RESULTS ============================================================ Initial win rate: 53.3% Final win rate: 56.7% Change: +3.3%
pip install -e ".[mlx]"python examples/mlx_quicktest.pypython examples/mlx_tictactoe.py