inductive-mlxrl 0.2.4__tar.gz → 0.4.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.
Files changed (133) hide show
  1. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/CHANGELOG.md +41 -0
  2. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/DESIGN.md +18 -0
  3. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/PKG-INFO +97 -2
  4. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/README.md +96 -1
  5. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/THIRD_PARTY_LICENSES.md +15 -0
  6. inductive_mlxrl-0.4.0/benchmarks/results/confidence_4bit_2026-11-01.interrupted.jsonl +14 -0
  7. inductive_mlxrl-0.4.0/benchmarks/results/confidence_4bit_2026-11-01.jsonl +241 -0
  8. inductive_mlxrl-0.4.0/benchmarks/results/confidence_4bit_2026-11-01.md +151 -0
  9. inductive_mlxrl-0.4.0/benchmarks/results/confidence_4bit_2026-11-01.prevalidation.jsonl +147 -0
  10. inductive_mlxrl-0.4.0/benchmarks/results/confidence_4bit_2026-11-01.summary.json +419 -0
  11. inductive_mlxrl-0.4.0/benchmarks/results/confidence_cli_2026-11-01.json +77 -0
  12. inductive_mlxrl-0.4.0/benchmarks/results/score_centering_4bit_2026-10-30.jsonl +505 -0
  13. inductive_mlxrl-0.4.0/benchmarks/results/score_centering_4bit_2026-10-30.md +102 -0
  14. inductive_mlxrl-0.4.0/benchmarks/results/score_centering_4bit_2026-10-30.summary.json +549 -0
  15. inductive_mlxrl-0.4.0/benchmarks/results/score_centering_capture_2026-10-30.jsonl +26 -0
  16. inductive_mlxrl-0.4.0/benchmarks/results/score_centering_existing_baselines_2026-10-30.jsonl +505 -0
  17. inductive_mlxrl-0.4.0/benchmarks/run_confidence.py +306 -0
  18. inductive_mlxrl-0.4.0/benchmarks/run_score_centering.py +463 -0
  19. inductive_mlxrl-0.4.0/docs/confidence.md +138 -0
  20. inductive_mlxrl-0.4.0/examples/qwen3_0_6b_confidence.toml +33 -0
  21. inductive_mlxrl-0.4.0/examples/qwen3_0_6b_score_centering.toml +25 -0
  22. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/__init__.py +1 -1
  23. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/algo/__init__.py +3 -0
  24. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/algo/grpo.py +42 -7
  25. inductive_mlxrl-0.4.0/mlxrl/algo/score_centering.py +120 -0
  26. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/algorithm.py +20 -0
  27. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/cli.py +71 -6
  28. inductive_mlxrl-0.4.0/mlxrl/confidence.py +237 -0
  29. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/config.py +69 -4
  30. inductive_mlxrl-0.4.0/mlxrl/data/confidence.py +108 -0
  31. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/policy/logprobs.py +51 -21
  32. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/rollout/naive.py +26 -0
  33. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/rollout/optimized.py +50 -9
  34. inductive_mlxrl-0.4.0/mlxrl/sampling.py +148 -0
  35. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/train/grpo.py +85 -21
  36. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/pyproject.toml +2 -1
  37. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_cli.py +16 -0
  38. inductive_mlxrl-0.4.0/tests/test_confidence.py +311 -0
  39. inductive_mlxrl-0.4.0/tests/test_confidence_cli.py +105 -0
  40. inductive_mlxrl-0.4.0/tests/test_confidence_spans.py +85 -0
  41. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_config.py +49 -0
  42. inductive_mlxrl-0.4.0/tests/test_score_centering.py +328 -0
  43. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_security.py +12 -0
  44. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_train_grpo.py +1 -1
  45. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/uv.lock +25 -27
  46. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/.github/scripts/audit_dependencies.py +0 -0
  47. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/.github/scripts/verify_release.py +0 -0
  48. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/.gitignore +0 -0
  49. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/CONTRIBUTING.md +0 -0
  50. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/LICENSE +0 -0
  51. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/SECURITY.md +0 -0
  52. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/__init__.py +0 -0
  53. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/audit_phase4_config.py +0 -0
  54. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/external_baseline_worker.py +0 -0
  55. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after.jsonl +0 -0
  56. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after.md +0 -0
  57. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after_final.jsonl +0 -0
  58. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after_final.md +0 -0
  59. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after_noprefixsync.jsonl +0 -0
  60. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_after_noprefixsync.md +0 -0
  61. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_before.jsonl +0 -0
  62. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate1_mlxrl_before.md +0 -0
  63. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate2_unified_smoke.jsonl +0 -0
  64. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate2_unified_smoke.md +0 -0
  65. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_mlx_tune_sync_256.jsonl +0 -0
  66. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_mlx_tune_sync_256.md +0 -0
  67. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_sync_blocked.jsonl +0 -0
  68. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_sync_blocked.md +0 -0
  69. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_sync_smoke.jsonl +0 -0
  70. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate3_sync_smoke.md +0 -0
  71. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate4_config_audit.json +0 -0
  72. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate4_config_audit.md +0 -0
  73. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate4_sampler_smoke.jsonl +0 -0
  74. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate4_sampler_smoke.md +0 -0
  75. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate5_full_reconciled.jsonl +0 -0
  76. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/gate5_full_reconciled.md +0 -0
  77. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_external_smoke.jsonl +0 -0
  78. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_external_smoke.md +0 -0
  79. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_full_all_targets.jsonl +0 -0
  80. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_full_all_targets.md +0 -0
  81. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_local_mlxrl_vs_mlx_lm.jsonl +0 -0
  82. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_local_mlxrl_vs_mlx_lm.md +0 -0
  83. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_mlx_lm_g4_smoke.jsonl +0 -0
  84. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/results/phase4_mlx_lm_g4_smoke.md +0 -0
  85. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/benchmarks/run_phase4.py +0 -0
  86. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/README.md +0 -0
  87. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/assets/terminal-results.jpg +0 -0
  88. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/assets/terminal-training.jpg +0 -0
  89. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/assets/ui-ranking.jpg +0 -0
  90. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/assets/ui-scorer-eval.jpg +0 -0
  91. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/compare.py +0 -0
  92. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/config.toml +0 -0
  93. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/data/briefs.json +0 -0
  94. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/data/scorer.json +0 -0
  95. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/taste_reward.py +0 -0
  96. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/test_reward.py +0 -0
  97. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/personal_taste_grpo/train.py +0 -0
  98. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/qwen35_9b_g2_checkpoint.toml +0 -0
  99. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/qwen3_0_6b_gigpo_recurring.toml +0 -0
  100. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/examples/qwen3_0_6b_grpo.toml +0 -0
  101. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/algo/gigpo.py +0 -0
  102. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/data/__init__.py +0 -0
  103. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/data/gsm8k.py +0 -0
  104. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/data/rewards.py +0 -0
  105. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/echo.py +0 -0
  106. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/env/__init__.py +0 -0
  107. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/env/base.py +0 -0
  108. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/env/reference.py +0 -0
  109. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/policy/__init__.py +0 -0
  110. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/policy/model.py +0 -0
  111. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/policy/trajectory_logprobs.py +0 -0
  112. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/py.typed +0 -0
  113. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/rollout/__init__.py +0 -0
  114. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/rollout/agentic.py +0 -0
  115. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/train/__init__.py +0 -0
  116. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/train/reference.py +0 -0
  117. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/train/trajectory.py +0 -0
  118. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/mlxrl/trajectory.py +0 -0
  119. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_agentic_env.py +0 -0
  120. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_agentic_rollout.py +0 -0
  121. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_benchmark_metrics.py +0 -0
  122. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_cache_compatibility.py +0 -0
  123. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_echo.py +0 -0
  124. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_gigpo.py +0 -0
  125. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_grpo.py +0 -0
  126. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_import_direction.py +0 -0
  127. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_optimized_rollout.py +0 -0
  128. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_policy_logprobs.py +0 -0
  129. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_policy_model.py +0 -0
  130. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_release_policy.py +0 -0
  131. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_rewards.py +0 -0
  132. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_train_trajectory.py +0 -0
  133. {inductive_mlxrl-0.2.4 → inductive_mlxrl-0.4.0}/tests/test_trajectory_logprobs.py +0 -0
@@ -5,6 +5,47 @@ 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
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] - 2026-11-01
11
+
12
+ CONF is experimental and disabled by default. Enable it explicitly with
13
+ `[confidence] enabled = true`. GRPO remains the default algorithm, and score
14
+ centering remains a separate opt-in. The 12 local 4-bit runs have mixed results
15
+ and do not establish a reliable quality gain or a 2× time-to-target speedup;
16
+ the score-centering combination regresses in this small screening fixture.
17
+ See the [complete benchmark report](benchmarks/results/confidence_4bit_2026-11-01.md).
18
+
19
+ ### Added
20
+
21
+ - Experimental, opt-in CONF warm-start credit for single-turn GRPO and score
22
+ centering, with raw fp32 confidence, explicit step/result spans, excluded value
23
+ and answer tokens, bounded calibration and an eight-update handoff.
24
+ - Arithmetic step extraction, a runnable config, controller state serialization,
25
+ credit diagnostics and a matched multi-seed 4-bit screening harness.
26
+ - Multiple prompt groups per CLI update and exact sequence-mean micro-batching
27
+ for GRPO and score centering. Score centering can explicitly normalize rewards
28
+ for comparisons with GRPO. Existing algorithm and reduction defaults are preserved.
29
+
30
+ ## [0.3.0] - 2026-10-30
31
+
32
+ Score centering is experimental and disabled by default. Existing configurations
33
+ continue to use GRPO. Enable it explicitly with `[algorithm] name = "score-centering"`.
34
+ The local benchmarks do not establish a reliable quality or long-run stability gain.
35
+
36
+ ### Added
37
+
38
+ - Experimental, opt-in single-turn score centering with actual post-filter,
39
+ post-temperature sampler probabilities, detached top-k/tail correction,
40
+ fp32 trainer probabilities, and micro-batch support.
41
+ - Correctness gates for exact gradients, drift cancellation, sampling laws,
42
+ seeded capture parity across rollout engines, and padded optimizer updates.
43
+ - A paired multi-seed 4-bit QLoRA benchmark with raw training diagnostics.
44
+
45
+ ### Changed
46
+
47
+ - Update locked filelock to 4.2.1 and Ruff to 0.17.1, and pin setup-uv 10.3.2.
48
+
8
49
  ## [0.2.4] - 2026-10-22
9
50
 
10
51
  ### 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.2.4
3
+ Version: 0.4.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
@@ -301,7 +301,9 @@ metrics = optimizer_step_trajectory(
301
301
  `micro_batch_size=0` keeps the original whole-batch path. Micro-batching is
302
302
  currently exact for token-mean policy losses: base GRPO, DAPO, GSPO token mode,
303
303
  RLOO, and Dr. GRPO with `loss_reduction="token_mean"`. Sequence-reduced losses
304
- should keep `micro_batch_size=0`.
304
+ should keep `micro_batch_size=0`, except GRPO and score centering's explicit
305
+ `sequence_mean` mode, which accumulates by completion count. CONF token credits
306
+ support both of these reductions.
305
307
 
306
308
  ## ECHO World Modeling
307
309
 
@@ -355,6 +357,99 @@ import concrete algorithm implementations.
355
357
  | GSPO | sequence-level, length-normalized importance ratio and clipping |
356
358
  | RLOO | leave-one-out group baseline, no critic, no std-normalized advantage |
357
359
  | GiGPO | multi-turn episode advantage plus anchor-state step advantage |
360
+ | Score centering (experimental) | centered rewards, token-mean REINFORCE with sampler-score correction |
361
+
362
+ ### Experimental score centering
363
+
364
+ Score centering is an explicit single-turn opt-in based on
365
+ [Marek and Ryabinin, 2026](https://arxiv.org/abs/2609.20807). Existing algorithm
366
+ defaults and old-policy recompute semantics stay unchanged.
367
+
368
+ ```bash
369
+ UV_CACHE_DIR=.uv-cache uv run mlxrl train \
370
+ --config examples/qwen3_0_6b_score_centering.toml \
371
+ --available-memory-gb 48
372
+ ```
373
+
374
+ The config selects the objective and the number of sampler candidates to retain:
375
+
376
+ ```toml
377
+ [algorithm]
378
+ name = "score-centering"
379
+ score_centering_top_k = 128
380
+ ```
381
+
382
+ This objective uses group-centered rewards without standard-deviation scaling.
383
+ The sampler records probabilities **after top-p, min-p, top-k, and temperature**,
384
+ including the selected token's probability even when it falls outside the
385
+ recorded head. Greedy sampling is recorded as a point mass. Candidate
386
+ probabilities retain their original mass; the trainer approximates the remaining
387
+ tail as described in the paper. The correction coefficients are detached, and
388
+ trainer logprobs are normalized in fp32. `kl_beta` remains the independent
389
+ adapter-disabled reference-model penalty.
390
+
391
+ For the import API, construct `ScoreCenteringAlgorithm(sampler_top_k=128)` from
392
+ `mlxrl.algo` and use `SamplingConfig(record_sampler_top_k=128)` for generation.
393
+ Pass the resulting completions and that algorithm to `batch_from_rollouts` and
394
+ `optimizer_step`. Missing sampler records are rejected. `Completion.sampler_trace`
395
+ contains the selected-token and candidate probabilities; config-driven runs also
396
+ save `sampler_logprobs`, `sampler_topk_ids`, and `sampler_topk_logprobs` in the
397
+ output NPZ. Ordinary `old_policy_logprobs` remain separate.
398
+
399
+ Capture works with naive, prefix-cached, batched, and compiled single-turn
400
+ rollouts and preserves seeded token draws. The objective supports token-weighted
401
+ micro-batching. Multi-turn/ECHO training is outside this initial experiment.
402
+ The memory preflight estimates have not been calibrated for this objective.
403
+ Its benefit and overhead on a particular model must be measured; the paper's
404
+ large-model results do not establish a benefit for MLX QLoRA.
405
+
406
+ The reproducible paired benchmark is `benchmarks/run_score_centering.py`; its
407
+ protocol and measured results are in
408
+ [the 4-bit benchmark report](benchmarks/results/score_centering_4bit_2026-10-30.md).
409
+
410
+ ### Experimental confidence warm start (CONF)
411
+
412
+ CONF adds detached, centered step-confidence credit during the first eight
413
+ optimizer updates, then continues with the selected base loss and optimizer.
414
+ It is **experimental and disabled by default**. GRPO stays the default algorithm;
415
+ score centering remains a separate opt-in.
416
+
417
+ ```bash
418
+ UV_CACHE_DIR=.uv-cache uv run mlxrl train \
419
+ --config examples/qwen3_0_6b_confidence.toml
420
+ ```
421
+
422
+ The example uses explicit `step: expression = numeric_result` reasoning lines,
423
+ four prompt groups per update, and per-completion loss averaging. To compare
424
+ against its matched baseline, change only `[confidence] enabled = false`.
425
+ The prompt format and loss reduction are independent settings and stay fixed.
426
+
427
+ Confidence is the model's temperature-1 probability of the written result,
428
+ computed with fp32 normalization before optimization. Each step receives
429
+ `lambda * (confidence - token_weighted_mean_confidence)` on eligible reasoning
430
+ tokens. Result tokens, final answers and padding receive no extra credit.
431
+ Unscored or unalignable text contributes zero. This arithmetic reader does not
432
+ validate correctness or infer steps from arbitrary prose.
433
+
434
+ The default controller targets a credit share of `0.875`, caps its measured
435
+ relative p99 at `10`, and holds the previous scale when fewer than four mixed
436
+ groups carry credit. The cap is reapplied on held scales whenever measurable.
437
+ With no initial calibration signal, credit stays zero. Logs expose the scale,
438
+ actual share, scored steps, credited tokens and handoff; the output NPZ retains
439
+ the credit history. Held scales can produce shares above or below the target.
440
+
441
+ For custom tasks, supply completion-token spans through `ConfidenceStep` and
442
+ `TokenSpan`; see [the Python integration and validation notes](docs/confidence.md).
443
+ The initial integration supports single-turn GRPO and score centering. It does
444
+ not support DAPO filtering, GiGPO, ECHO or multi-turn credit.
445
+
446
+ RAMP's reported speedup is specific to its research setup. The MLX arithmetic
447
+ screening harness is `benchmarks/run_confidence.py`; it compares all four
448
+ combinations across seeds, including sampling and confidence overhead.
449
+ The [12-run 4-bit screening](benchmarks/results/confidence_4bit_2026-11-01.md)
450
+ found mixed outcomes: CONF raised final GRPO accuracy on two seeds and lowered it
451
+ on one; with score centering it lowered accuracy on two seeds and tied on one.
452
+ These short runs do not establish a reliable gain or a 2× speedup.
358
453
 
359
454
  ## Memory Preflight
360
455
 
@@ -276,7 +276,9 @@ metrics = optimizer_step_trajectory(
276
276
  `micro_batch_size=0` keeps the original whole-batch path. Micro-batching is
277
277
  currently exact for token-mean policy losses: base GRPO, DAPO, GSPO token mode,
278
278
  RLOO, and Dr. GRPO with `loss_reduction="token_mean"`. Sequence-reduced losses
279
- should keep `micro_batch_size=0`.
279
+ should keep `micro_batch_size=0`, except GRPO and score centering's explicit
280
+ `sequence_mean` mode, which accumulates by completion count. CONF token credits
281
+ support both of these reductions.
280
282
 
281
283
  ## ECHO World Modeling
282
284
 
@@ -330,6 +332,99 @@ import concrete algorithm implementations.
330
332
  | GSPO | sequence-level, length-normalized importance ratio and clipping |
331
333
  | RLOO | leave-one-out group baseline, no critic, no std-normalized advantage |
332
334
  | GiGPO | multi-turn episode advantage plus anchor-state step advantage |
335
+ | Score centering (experimental) | centered rewards, token-mean REINFORCE with sampler-score correction |
336
+
337
+ ### Experimental score centering
338
+
339
+ Score centering is an explicit single-turn opt-in based on
340
+ [Marek and Ryabinin, 2026](https://arxiv.org/abs/2609.20807). Existing algorithm
341
+ defaults and old-policy recompute semantics stay unchanged.
342
+
343
+ ```bash
344
+ UV_CACHE_DIR=.uv-cache uv run mlxrl train \
345
+ --config examples/qwen3_0_6b_score_centering.toml \
346
+ --available-memory-gb 48
347
+ ```
348
+
349
+ The config selects the objective and the number of sampler candidates to retain:
350
+
351
+ ```toml
352
+ [algorithm]
353
+ name = "score-centering"
354
+ score_centering_top_k = 128
355
+ ```
356
+
357
+ This objective uses group-centered rewards without standard-deviation scaling.
358
+ The sampler records probabilities **after top-p, min-p, top-k, and temperature**,
359
+ including the selected token's probability even when it falls outside the
360
+ recorded head. Greedy sampling is recorded as a point mass. Candidate
361
+ probabilities retain their original mass; the trainer approximates the remaining
362
+ tail as described in the paper. The correction coefficients are detached, and
363
+ trainer logprobs are normalized in fp32. `kl_beta` remains the independent
364
+ adapter-disabled reference-model penalty.
365
+
366
+ For the import API, construct `ScoreCenteringAlgorithm(sampler_top_k=128)` from
367
+ `mlxrl.algo` and use `SamplingConfig(record_sampler_top_k=128)` for generation.
368
+ Pass the resulting completions and that algorithm to `batch_from_rollouts` and
369
+ `optimizer_step`. Missing sampler records are rejected. `Completion.sampler_trace`
370
+ contains the selected-token and candidate probabilities; config-driven runs also
371
+ save `sampler_logprobs`, `sampler_topk_ids`, and `sampler_topk_logprobs` in the
372
+ output NPZ. Ordinary `old_policy_logprobs` remain separate.
373
+
374
+ Capture works with naive, prefix-cached, batched, and compiled single-turn
375
+ rollouts and preserves seeded token draws. The objective supports token-weighted
376
+ micro-batching. Multi-turn/ECHO training is outside this initial experiment.
377
+ The memory preflight estimates have not been calibrated for this objective.
378
+ Its benefit and overhead on a particular model must be measured; the paper's
379
+ large-model results do not establish a benefit for MLX QLoRA.
380
+
381
+ The reproducible paired benchmark is `benchmarks/run_score_centering.py`; its
382
+ protocol and measured results are in
383
+ [the 4-bit benchmark report](benchmarks/results/score_centering_4bit_2026-10-30.md).
384
+
385
+ ### Experimental confidence warm start (CONF)
386
+
387
+ CONF adds detached, centered step-confidence credit during the first eight
388
+ optimizer updates, then continues with the selected base loss and optimizer.
389
+ It is **experimental and disabled by default**. GRPO stays the default algorithm;
390
+ score centering remains a separate opt-in.
391
+
392
+ ```bash
393
+ UV_CACHE_DIR=.uv-cache uv run mlxrl train \
394
+ --config examples/qwen3_0_6b_confidence.toml
395
+ ```
396
+
397
+ The example uses explicit `step: expression = numeric_result` reasoning lines,
398
+ four prompt groups per update, and per-completion loss averaging. To compare
399
+ against its matched baseline, change only `[confidence] enabled = false`.
400
+ The prompt format and loss reduction are independent settings and stay fixed.
401
+
402
+ Confidence is the model's temperature-1 probability of the written result,
403
+ computed with fp32 normalization before optimization. Each step receives
404
+ `lambda * (confidence - token_weighted_mean_confidence)` on eligible reasoning
405
+ tokens. Result tokens, final answers and padding receive no extra credit.
406
+ Unscored or unalignable text contributes zero. This arithmetic reader does not
407
+ validate correctness or infer steps from arbitrary prose.
408
+
409
+ The default controller targets a credit share of `0.875`, caps its measured
410
+ relative p99 at `10`, and holds the previous scale when fewer than four mixed
411
+ groups carry credit. The cap is reapplied on held scales whenever measurable.
412
+ With no initial calibration signal, credit stays zero. Logs expose the scale,
413
+ actual share, scored steps, credited tokens and handoff; the output NPZ retains
414
+ the credit history. Held scales can produce shares above or below the target.
415
+
416
+ For custom tasks, supply completion-token spans through `ConfidenceStep` and
417
+ `TokenSpan`; see [the Python integration and validation notes](docs/confidence.md).
418
+ The initial integration supports single-turn GRPO and score centering. It does
419
+ not support DAPO filtering, GiGPO, ECHO or multi-turn credit.
420
+
421
+ RAMP's reported speedup is specific to its research setup. The MLX arithmetic
422
+ screening harness is `benchmarks/run_confidence.py`; it compares all four
423
+ combinations across seeds, including sampling and confidence overhead.
424
+ The [12-run 4-bit screening](benchmarks/results/confidence_4bit_2026-11-01.md)
425
+ found mixed outcomes: CONF raised final GRPO accuracy on two seeds and lowered it
426
+ on one; with score centering it lowered accuracy on two seeds and tied on one.
427
+ These short runs do not establish a reliable gain or a 2× speedup.
333
428
 
334
429
  ## Memory Preflight
335
430
 
@@ -44,3 +44,18 @@ 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.
56
+
57
+ ## CONF research
58
+
59
+ - CONF warm-start credit was developed in the RAMP research project (CONF-5/6).
60
+ - MLXRL independently implements its confidence-weighted token-credit mathematics.
61
+ No RAMP, PyTorch or vLLM implementation is vendored or required at runtime.
@@ -0,0 +1,14 @@
1
+ {"kind": "manifest", "source_sha256": "1ef24cd51e9abffcad8c9254b516040bfb1d84a2166288a31e5b1c57c3eb9bfe", "model_revision": "73e3e38d981303bc594367cd910ea6eb48349da8", "model_files_sha256": {"model.safetensors": "392e8d466d56100ada00eb82031fb854297fc9e389b7d303eba3af114e87bce2"}, "harness_sha256": "acabda1b3da14362bc08c59f4b27daa2951037f06b44981c293aa121dff6fb67", "runtime": {"mlx": "0.32.3", "mlx-lm": "0.32.0"}, "platform": "macOS-26.6.2-arm64-arm-64bit", "order": [[17, "grpo"], [7, "sc-grpo"], [7, "sc-grpo-conf"], [27, "grpo-conf"], [27, "grpo"], [17, "sc-grpo-conf"], [17, "grpo-conf"], [7, "grpo"], [17, "sc-grpo"], [27, "sc-grpo"], [7, "grpo-conf"], [27, "sc-grpo-conf"]], "steps": 16, "max_tokens": 64, "learning_rate": 1e-05, "train_cases": [[19, 7, 4], [23, 3, 8], [17, 4, 6], [12, 6, -5], [28, 2, 9], [31, 3, -8], [26, 4, 7], [14, 9, -3]], "eval_cases": [[16, 7, 2], [22, 3, 5], [18, 4, -7], [13, 6, -4], [27, 2, 8], [32, 3, -9], [25, 4, 6], [15, 9, -2]], "scope": "Short arithmetic screening; does not establish a 2x speedup."}
2
+ {"kind": "interruption", "cause": "user_requested_pause", "paused_utc": "2026-10-31T21:11:20Z", "method": "grpo-conf", "seed": 7, "restart_from_update": 1}
3
+ {"kind": "eval", "method": "grpo-conf", "seed": 7, "update": 0, "accuracy": 0.5, "training_seconds": 0.0}
4
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.02334711141884327, "policy_gradient_loss": -0.02334711141884327, "kl": 0.0, "mean_ratio": 1.0060958862304688, "clip_fraction": 0.0, "mean_reward": 0.33390313386917114, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 189.49169921875, "update": 1, "active": true, "scale": 273.43975160100183, "share": 0.8750000000000001, "p99": 1.786863397050134, "mixed_groups": 1, "scored_steps": 16, "credited_tokens": 200, "held": false, "capped": false, "seconds": 5.233225541887805, "credit_preparation_seconds": 0.0040896248538047075, "tokens": 512, "training_accuracy": 0.25, "first_completion": "step: x = 19 + 7 = 26\nstep: y = 26 - 4 = 22\n<answer>22</answer>\n\nQuestion: a=3, b=-1, c=5\n\nstep: x = a + b = 3 + (-"}
5
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": 0.002402217360213399, "policy_gradient_loss": 0.002402217360213399, "kl": 0.0, "mean_ratio": 1.0018539428710938, "clip_fraction": 0.0, "mean_reward": 0.5168052911758423, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 21.20281410217285, "update": 2, "active": true, "scale": 273.43975160100183, "share": 3.416387992064073, "p99": 8.142404721836918, "mixed_groups": 2, "scored_steps": 17, "credited_tokens": 175, "held": true, "capped": false, "seconds": 5.626388540957123, "credit_preparation_seconds": 0.003768333001062274, "tokens": 512, "training_accuracy": 0.5, "first_completion": null}
6
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.001930969301611185, "policy_gradient_loss": -0.001930969301611185, "kl": 0.0, "mean_ratio": 1.0067520141601562, "clip_fraction": 0.0, "mean_reward": 0.21659544110298157, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 10.466052055358887, "update": 3, "active": true, "scale": 273.43975160100183, "share": 0.3948457839433382, "p99": 1.4129734064278312, "mixed_groups": 3, "scored_steps": 14, "credited_tokens": 125, "held": true, "capped": false, "seconds": 5.4301337080542, "credit_preparation_seconds": 0.003907875157892704, "tokens": 512, "training_accuracy": 0.375, "first_completion": null}
7
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": 0.009084708988666534, "policy_gradient_loss": 0.009084708988666534, "kl": 0.0, "mean_ratio": 0.9991836547851562, "clip_fraction": 0.0, "mean_reward": 0.4801282286643982, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 26.136913299560547, "update": 4, "active": true, "scale": 82.95137240121521, "share": 3.6471356947030436, "p99": 10.0, "mixed_groups": 3, "scored_steps": 14, "credited_tokens": 140, "held": true, "capped": true, "seconds": 5.514076165854931, "credit_preparation_seconds": 0.0032486668787896633, "tokens": 512, "training_accuracy": 0.375, "first_completion": null}
8
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.0017613917589187622, "policy_gradient_loss": -0.0017613917589187622, "kl": 0.0, "mean_ratio": 1.0053329467773438, "clip_fraction": 0.0, "mean_reward": 0.2582579255104065, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 10.299310684204102, "update": 5, "active": true, "scale": 77.89243246778562, "share": 0.8750000000000001, "p99": 2.6011275337767557, "mixed_groups": 4, "scored_steps": 14, "credited_tokens": 99, "held": false, "capped": false, "seconds": 6.338310166960582, "credit_preparation_seconds": 0.0044094170443713665, "tokens": 512, "training_accuracy": 0.375, "first_completion": null}
9
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.0012859031558036804, "policy_gradient_loss": -0.0012859031558036804, "kl": 0.0, "mean_ratio": 0.9999313354492188, "clip_fraction": 0.0, "mean_reward": -0.42107588052749634, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 10.86573600769043, "update": 6, "active": true, "scale": 9.341925147310798, "share": 0.875, "p99": 5.7912476128239545, "mixed_groups": 4, "scored_steps": 14, "credited_tokens": 138, "held": false, "capped": false, "seconds": 6.161447166930884, "credit_preparation_seconds": 0.005332167027518153, "tokens": 512, "training_accuracy": 0.125, "first_completion": null}
10
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.003259904682636261, "policy_gradient_loss": -0.003259904682636261, "kl": 0.0, "mean_ratio": 0.99853515625, "clip_fraction": 0.0, "mean_reward": 0.0703703761100769, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 10.55459976196289, "update": 7, "active": true, "scale": 9.341925147310798, "share": 0.33623985466741596, "p99": 1.5916481428289313, "mixed_groups": 3, "scored_steps": 10, "credited_tokens": 69, "held": true, "capped": false, "seconds": 7.07123037497513, "credit_preparation_seconds": 0.0036426668521016836, "tokens": 512, "training_accuracy": 0.375, "first_completion": null}
11
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.00380079448223114, "policy_gradient_loss": -0.00380079448223114, "kl": 0.0, "mean_ratio": 1.002838134765625, "clip_fraction": 0.0, "mean_reward": -0.06327097117900848, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": 12.207550048828125, "update": 8, "active": true, "scale": 9.113473694214646, "share": 0.875, "p99": 3.8359333401651647, "mixed_groups": 4, "scored_steps": 11, "credited_tokens": 71, "held": false, "capped": false, "seconds": 8.226299624890089, "credit_preparation_seconds": 0.004143709084019065, "tokens": 512, "training_accuracy": 0.125, "first_completion": "step: 28 +2=29\nstep :29 -2 =27\n27\n<answer>27</answer>\n\nQuestion: a=4, b=5, c=7.\nstep:4 +5 =9\n9 7 (=)=9 or 7?"}
12
+ {"kind": "eval", "method": "grpo-conf", "seed": 7, "update": 8, "accuracy": 0.625, "training_seconds": 49.601111290510744}
13
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.0011902004480361938, "policy_gradient_loss": -0.0011902004480361938, "kl": 0.0, "mean_ratio": 0.99822998046875, "clip_fraction": 0.0, "mean_reward": -0.14005783200263977, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": null, "update": 9, "active": false, "scale": 0.0, "share": null, "p99": null, "mixed_groups": 0, "scored_steps": 0, "credited_tokens": 0, "held": false, "capped": false, "seconds": 6.718482790980488, "credit_preparation_seconds": 0.00010529183782637119, "tokens": 512, "training_accuracy": 0.125, "first_completion": "step:19 +7= 26\nstep:26-2= 24\n<answer>24</answer>\n\nCheck if answer is correct: \n19+7=26=24\nSo, 19 +7+4=26 +4=3"}
14
+ {"kind": "step", "method": "grpo-conf", "seed": 7, "loss": -0.003959663212299347, "policy_gradient_loss": -0.003959663212299347, "kl": 0.0, "mean_ratio": 1.0046157836914062, "clip_fraction": 0.0, "mean_reward": 0.39585065841674805, "loss_action": 0.0, "loss_echo": 0.0, "echo_accuracy": 0.0, "echo_alpha": 0.0, "gradient_norm": null, "update": 10, "active": false, "scale": 0.0, "share": null, "p99": null, "mixed_groups": 0, "scored_steps": 0, "credited_tokens": 0, "held": false, "capped": false, "seconds": 7.333210832905024, "credit_preparation_seconds": 1.9124941900372505e-05, "tokens": 512, "training_accuracy": 0.375, "first_completion": null}