inductive-mlxrl 0.2.4__tar.gz → 0.3.0__tar.gz
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.
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/CHANGELOG.md +19 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/DESIGN.md +18 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/PKG-INFO +50 -1
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/README.md +49 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/THIRD_PARTY_LICENSES.md +9 -0
- inductive_mlxrl-0.3.0/benchmarks/results/score_centering_4bit_2026-10-30.jsonl +505 -0
- inductive_mlxrl-0.3.0/benchmarks/results/score_centering_4bit_2026-10-30.md +102 -0
- inductive_mlxrl-0.3.0/benchmarks/results/score_centering_4bit_2026-10-30.summary.json +549 -0
- inductive_mlxrl-0.3.0/benchmarks/results/score_centering_capture_2026-10-30.jsonl +26 -0
- inductive_mlxrl-0.3.0/benchmarks/results/score_centering_existing_baselines_2026-10-30.jsonl +505 -0
- inductive_mlxrl-0.3.0/benchmarks/run_score_centering.py +463 -0
- inductive_mlxrl-0.3.0/examples/qwen3_0_6b_score_centering.toml +25 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/__init__.py +1 -1
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/algo/__init__.py +3 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/algo/grpo.py +4 -0
- inductive_mlxrl-0.3.0/mlxrl/algo/score_centering.py +97 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/algorithm.py +20 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/cli.py +20 -2
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/config.py +12 -2
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/policy/logprobs.py +32 -7
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/rollout/naive.py +26 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/rollout/optimized.py +50 -9
- inductive_mlxrl-0.3.0/mlxrl/sampling.py +148 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/train/grpo.py +59 -16
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/pyproject.toml +1 -1
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_cli.py +16 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_config.py +15 -0
- inductive_mlxrl-0.3.0/tests/test_score_centering.py +328 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/uv.lock +25 -27
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/.github/scripts/audit_dependencies.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/.github/scripts/verify_release.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/.gitignore +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/CONTRIBUTING.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/LICENSE +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/SECURITY.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/audit_phase4_config.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/external_baseline_worker.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after_final.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after_final.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after_noprefixsync.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_after_noprefixsync.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_before.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate1_mlxrl_before.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate2_unified_smoke.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate2_unified_smoke.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_mlx_tune_sync_256.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_mlx_tune_sync_256.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_sync_blocked.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_sync_blocked.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_sync_smoke.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate3_sync_smoke.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate4_config_audit.json +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate4_config_audit.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate4_sampler_smoke.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate4_sampler_smoke.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate5_full_reconciled.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/gate5_full_reconciled.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_external_smoke.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_external_smoke.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_full_all_targets.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_full_all_targets.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_local_mlxrl_vs_mlx_lm.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_local_mlxrl_vs_mlx_lm.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_mlx_lm_g4_smoke.jsonl +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/results/phase4_mlx_lm_g4_smoke.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/benchmarks/run_phase4.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/README.md +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/assets/terminal-results.jpg +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/assets/terminal-training.jpg +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/assets/ui-ranking.jpg +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/assets/ui-scorer-eval.jpg +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/compare.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/config.toml +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/data/briefs.json +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/data/scorer.json +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/taste_reward.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/test_reward.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/personal_taste_grpo/train.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/qwen35_9b_g2_checkpoint.toml +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/qwen3_0_6b_gigpo_recurring.toml +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/examples/qwen3_0_6b_grpo.toml +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/algo/gigpo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/data/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/data/gsm8k.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/data/rewards.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/echo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/env/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/env/base.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/env/reference.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/policy/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/policy/model.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/policy/trajectory_logprobs.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/py.typed +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/rollout/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/rollout/agentic.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/train/__init__.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/train/reference.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/train/trajectory.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/mlxrl/trajectory.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_agentic_env.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_agentic_rollout.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_benchmark_metrics.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_cache_compatibility.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_echo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_gigpo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_grpo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_import_direction.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_optimized_rollout.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_policy_logprobs.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_policy_model.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_release_policy.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_rewards.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_security.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_train_grpo.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_train_trajectory.py +0 -0
- {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.3.0}/tests/test_trajectory_logprobs.py +0 -0
|
@@ -5,6 +5,25 @@ All notable changes to this project are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project uses semantic versioning once releases are cut.
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
Score centering is experimental and disabled by default. Existing configurations
|
|
11
|
+
continue to use GRPO. Enable it explicitly with `[algorithm] name = "score-centering"`.
|
|
12
|
+
The local benchmarks do not establish a reliable quality or long-run stability gain.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Experimental, opt-in single-turn score centering with actual post-filter,
|
|
17
|
+
post-temperature sampler probabilities, detached top-k/tail correction,
|
|
18
|
+
fp32 trainer probabilities, and micro-batch support.
|
|
19
|
+
- Correctness gates for exact gradients, drift cancellation, sampling laws,
|
|
20
|
+
seeded capture parity across rollout engines, and padded optimizer updates.
|
|
21
|
+
- A paired multi-seed 4-bit QLoRA benchmark with raw training diagnostics.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Update locked filelock to 4.2.1 and Ruff to 0.17.1, and pin setup-uv 10.3.2.
|
|
26
|
+
|
|
8
27
|
## [0.2.4] - 2026-10-22
|
|
9
28
|
|
|
10
29
|
### Security
|
|
@@ -37,6 +37,13 @@ than widening the single-turn `Algorithm` contract. GiGPO is the first
|
|
|
37
37
|
implementation: the rollout engine records env-provided state ids, while the
|
|
38
38
|
algorithm owns anchor-state grouping and credit assignment.
|
|
39
39
|
|
|
40
|
+
The experimental single-turn `SamplerAwareAlgorithm` sibling extends the loss
|
|
41
|
+
input with a recorded sampler head and corresponding full-forward trainer
|
|
42
|
+
logprobs. The engine dispatches through that protocol when a prepared batch has
|
|
43
|
+
sampler records; it still imports no algorithm implementations. The first user
|
|
44
|
+
is score centering, a token-mean REINFORCE objective with centered rewards.
|
|
45
|
+
Existing algorithms continue through the original protocol and probability path.
|
|
46
|
+
|
|
40
47
|
## Agentic Environment Seam
|
|
41
48
|
|
|
42
49
|
Agentic rollout is built around `EnvFactory(task, seed, group_index) ->
|
|
@@ -86,6 +93,17 @@ diagnostic data, not training data.
|
|
|
86
93
|
|
|
87
94
|
## Memory As A First-Class Constraint
|
|
88
95
|
|
|
96
|
+
Experimental score centering captures the actual categorical sampling law after
|
|
97
|
+
filtering and temperature, rather than treating raw model logprobs as sampler
|
|
98
|
+
probabilities. Records contain the selected-token probability and a top-k head;
|
|
99
|
+
the head is not renormalized. Persistent capture storage scales with completion
|
|
100
|
+
tokens times k, rather than vocabulary size. All gradient-bearing candidate
|
|
101
|
+
logprobs come from the trainer's full-forward path with fp32 normalization.
|
|
102
|
+
The detached tail correction follows equations 8--10 of arXiv:2609.20807, using
|
|
103
|
+
an exact zero sampler tail when its head exhausts all mass and a 1e-6 floor on
|
|
104
|
+
the trainer tail for numerical stability. This path is opt-in, single-turn only,
|
|
105
|
+
and requires separate measured memory and stability gates.
|
|
106
|
+
|
|
89
107
|
Apple Silicon's unified memory is the deployment target, not an afterthought.
|
|
90
108
|
The code supports per-layer MLX-LM gradient checkpointing because DeltaNet and
|
|
91
109
|
linear-attention models otherwise keep O(sequence length) recurrent state live
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: inductive-mlxrl
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Small single-process RL post-training for LLMs on Apple Silicon with MLX.
|
|
5
5
|
Project-URL: Homepage, https://github.com/inductiveML/mlxrl
|
|
6
6
|
Project-URL: Repository, https://github.com/inductiveML/mlxrl
|
|
@@ -355,6 +355,55 @@ import concrete algorithm implementations.
|
|
|
355
355
|
| GSPO | sequence-level, length-normalized importance ratio and clipping |
|
|
356
356
|
| RLOO | leave-one-out group baseline, no critic, no std-normalized advantage |
|
|
357
357
|
| GiGPO | multi-turn episode advantage plus anchor-state step advantage |
|
|
358
|
+
| Score centering (experimental) | centered rewards, token-mean REINFORCE with sampler-score correction |
|
|
359
|
+
|
|
360
|
+
### Experimental score centering
|
|
361
|
+
|
|
362
|
+
Score centering is an explicit single-turn opt-in based on
|
|
363
|
+
[Marek and Ryabinin, 2026](https://arxiv.org/abs/2609.20807). Existing algorithm
|
|
364
|
+
defaults and old-policy recompute semantics stay unchanged.
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
UV_CACHE_DIR=.uv-cache uv run mlxrl train \
|
|
368
|
+
--config examples/qwen3_0_6b_score_centering.toml \
|
|
369
|
+
--available-memory-gb 48
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The config selects the objective and the number of sampler candidates to retain:
|
|
373
|
+
|
|
374
|
+
```toml
|
|
375
|
+
[algorithm]
|
|
376
|
+
name = "score-centering"
|
|
377
|
+
score_centering_top_k = 128
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
This objective uses group-centered rewards without standard-deviation scaling.
|
|
381
|
+
The sampler records probabilities **after top-p, min-p, top-k, and temperature**,
|
|
382
|
+
including the selected token's probability even when it falls outside the
|
|
383
|
+
recorded head. Greedy sampling is recorded as a point mass. Candidate
|
|
384
|
+
probabilities retain their original mass; the trainer approximates the remaining
|
|
385
|
+
tail as described in the paper. The correction coefficients are detached, and
|
|
386
|
+
trainer logprobs are normalized in fp32. `kl_beta` remains the independent
|
|
387
|
+
adapter-disabled reference-model penalty.
|
|
388
|
+
|
|
389
|
+
For the import API, construct `ScoreCenteringAlgorithm(sampler_top_k=128)` from
|
|
390
|
+
`mlxrl.algo` and use `SamplingConfig(record_sampler_top_k=128)` for generation.
|
|
391
|
+
Pass the resulting completions and that algorithm to `batch_from_rollouts` and
|
|
392
|
+
`optimizer_step`. Missing sampler records are rejected. `Completion.sampler_trace`
|
|
393
|
+
contains the selected-token and candidate probabilities; config-driven runs also
|
|
394
|
+
save `sampler_logprobs`, `sampler_topk_ids`, and `sampler_topk_logprobs` in the
|
|
395
|
+
output NPZ. Ordinary `old_policy_logprobs` remain separate.
|
|
396
|
+
|
|
397
|
+
Capture works with naive, prefix-cached, batched, and compiled single-turn
|
|
398
|
+
rollouts and preserves seeded token draws. The objective supports token-weighted
|
|
399
|
+
micro-batching. Multi-turn/ECHO training is outside this initial experiment.
|
|
400
|
+
The memory preflight estimates have not been calibrated for this objective.
|
|
401
|
+
Its benefit and overhead on a particular model must be measured; the paper's
|
|
402
|
+
large-model results do not establish a benefit for MLX QLoRA.
|
|
403
|
+
|
|
404
|
+
The reproducible paired benchmark is `benchmarks/run_score_centering.py`; its
|
|
405
|
+
protocol and measured results are in
|
|
406
|
+
[the 4-bit benchmark report](benchmarks/results/score_centering_4bit_2026-10-30.md).
|
|
358
407
|
|
|
359
408
|
## Memory Preflight
|
|
360
409
|
|
|
@@ -330,6 +330,55 @@ import concrete algorithm implementations.
|
|
|
330
330
|
| GSPO | sequence-level, length-normalized importance ratio and clipping |
|
|
331
331
|
| RLOO | leave-one-out group baseline, no critic, no std-normalized advantage |
|
|
332
332
|
| GiGPO | multi-turn episode advantage plus anchor-state step advantage |
|
|
333
|
+
| Score centering (experimental) | centered rewards, token-mean REINFORCE with sampler-score correction |
|
|
334
|
+
|
|
335
|
+
### Experimental score centering
|
|
336
|
+
|
|
337
|
+
Score centering is an explicit single-turn opt-in based on
|
|
338
|
+
[Marek and Ryabinin, 2026](https://arxiv.org/abs/2609.20807). Existing algorithm
|
|
339
|
+
defaults and old-policy recompute semantics stay unchanged.
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
UV_CACHE_DIR=.uv-cache uv run mlxrl train \
|
|
343
|
+
--config examples/qwen3_0_6b_score_centering.toml \
|
|
344
|
+
--available-memory-gb 48
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The config selects the objective and the number of sampler candidates to retain:
|
|
348
|
+
|
|
349
|
+
```toml
|
|
350
|
+
[algorithm]
|
|
351
|
+
name = "score-centering"
|
|
352
|
+
score_centering_top_k = 128
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
This objective uses group-centered rewards without standard-deviation scaling.
|
|
356
|
+
The sampler records probabilities **after top-p, min-p, top-k, and temperature**,
|
|
357
|
+
including the selected token's probability even when it falls outside the
|
|
358
|
+
recorded head. Greedy sampling is recorded as a point mass. Candidate
|
|
359
|
+
probabilities retain their original mass; the trainer approximates the remaining
|
|
360
|
+
tail as described in the paper. The correction coefficients are detached, and
|
|
361
|
+
trainer logprobs are normalized in fp32. `kl_beta` remains the independent
|
|
362
|
+
adapter-disabled reference-model penalty.
|
|
363
|
+
|
|
364
|
+
For the import API, construct `ScoreCenteringAlgorithm(sampler_top_k=128)` from
|
|
365
|
+
`mlxrl.algo` and use `SamplingConfig(record_sampler_top_k=128)` for generation.
|
|
366
|
+
Pass the resulting completions and that algorithm to `batch_from_rollouts` and
|
|
367
|
+
`optimizer_step`. Missing sampler records are rejected. `Completion.sampler_trace`
|
|
368
|
+
contains the selected-token and candidate probabilities; config-driven runs also
|
|
369
|
+
save `sampler_logprobs`, `sampler_topk_ids`, and `sampler_topk_logprobs` in the
|
|
370
|
+
output NPZ. Ordinary `old_policy_logprobs` remain separate.
|
|
371
|
+
|
|
372
|
+
Capture works with naive, prefix-cached, batched, and compiled single-turn
|
|
373
|
+
rollouts and preserves seeded token draws. The objective supports token-weighted
|
|
374
|
+
micro-batching. Multi-turn/ECHO training is outside this initial experiment.
|
|
375
|
+
The memory preflight estimates have not been calibrated for this objective.
|
|
376
|
+
Its benefit and overhead on a particular model must be measured; the paper's
|
|
377
|
+
large-model results do not establish a benefit for MLX QLoRA.
|
|
378
|
+
|
|
379
|
+
The reproducible paired benchmark is `benchmarks/run_score_centering.py`; its
|
|
380
|
+
protocol and measured results are in
|
|
381
|
+
[the 4-bit benchmark report](benchmarks/results/score_centering_4bit_2026-10-30.md).
|
|
333
382
|
|
|
334
383
|
## Memory Preflight
|
|
335
384
|
|
|
@@ -44,3 +44,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
44
44
|
- License: MIT per installed package metadata.
|
|
45
45
|
- Used for: benchmark configuration inspection and external baseline adapter
|
|
46
46
|
patterns only. No `mlx-lm-lora` implementation code is vendored in `mlxrl`.
|
|
47
|
+
|
|
48
|
+
## Score centering research
|
|
49
|
+
|
|
50
|
+
- Martin Marek and Max Ryabinin, *Score Centering Stabilizes Off-policy
|
|
51
|
+
Reinforcement Learning* (2026): https://arxiv.org/abs/2609.20807.
|
|
52
|
+
- The experimental MLX objective implements the mathematics in equations 8--10
|
|
53
|
+
with independently written MLX code and explicit gradient-equivalence tests.
|
|
54
|
+
The paper is a research reference, not an installed dependency. This objective
|
|
55
|
+
is distinct from Tinker's StableReinforce service implementation.
|