⚠️ THIS IS A TEST MODEL — A RESEARCH ARTIFACT, NOT AN ASSISTANT
UPDATE 30 Aug — a better variant now ships alongside the reference carve.
whittle-next-moe-kd-k104-Q4_K_M.ggufruns the same weights at k=104 with a short top-64 KD pass, and cuts the loop-test failure rate from 92.7% to 83.7% (246 paired generations, exact McNemar p = 0.0032). It is still a failing conversational model — just measurably less so. See Follow-up (30 Aug) at the bottom for the full A/B. It is published as the record of an experiment: turning a dense model into a Mixture-of-Experts without retraining the weights, then learning the neuron→expert assignment. It has had no instruction tuning, no chat healing, and no safety work as a MoE. Expect rough edges. Do not use it in production. Use it to reproduce or extend the method.
Whittle-Next MoE (test) — 14.7B dense → 240 experts, ~33% active
A training-free MoE carve of Qwen3.8-Whittle-tri-14.7B, followed by a short learned-assignment alignment pass. No expert weight was ever retrained — only which neurons sit in which expert, and the router.
Ships as Qwen3_5MoeForCausalLM, the stock architecture, so it loads in plain
transformers with no custom code and no patches.
What was done
- Activation calibration. Token-averaged post-SiLU gate activations were recorded for all 17,408 FFN neurons per layer over a 240-sample, 10-cluster, 40-task calibration set (task diversity, following ExpertWeaver's finding that it beats generic corpus text).
- Carve. Per layer, the most universal neurons (low coefficient of variation across
tasks, high mean activation) became a 2048-wide always-on shared expert; the
remaining 15,360 were grouped by balanced k-means on their activation profiles into
240 routed experts of 64 neurons. The router was built without training, as the
mean of each cluster's gate vectors. A neuron's
gate/up/downslices always move together, so the carve is a pure permutation — exact at full k. - Alignment. The neuron→expert assignment was then learned as a balanced optimal transport problem (log-domain Sinkhorn + straight-through estimator), trained against the original dense model as its own teacher. Dense weights stayed frozen throughout.
Geometry
| Layers | 32 (GDN : full-attention, 3:1) |
| Hidden | 5120 |
| FFN width | 17,408 (unchanged from the dense parent) |
| Shared expert | 2048, always on |
| Routed experts | 240 × 64 |
| Active per token | 58 experts + shared = 5,760 / 17,408 ≈ 33% |
Measured (held-out CE, 5-probe factual battery)
Same evaluation throughout; "baseline" is the identical carve before alignment.
| active experts (k) | baseline | this model (aligned) |
|---|---|---|
| 26 | CE 5.31 | CE 4.17 |
| 52 | CE 4.76, 2/5 | CE 3.94, 5/5 |
| 58 (shipped default) | CE 4.69, 4/5 | CE 3.91, 5/5 |
| 104 | CE 4.16 | CE 3.77 |
| 240 (all) | CE 3.486 | CE 3.485 |
Alignment is worth roughly 0.8 CE at every sparsity, and the aligned model at k=58 beats the un-aligned one at k=104 — better quality from half the active experts. At full k both reproduce the dense model exactly, which is the carve's correctness check.
Loop test (measured, not estimated)
Run with the standard Whittle loop_test.py harness against this repo's Q4_K_M on an A100,
~90 generations. These are FAILURE rates — lower is better.
| section | failed | loopy | short |
|---|---|---|---|
| single-turn 12x3 | 31/36 (86%) | 24 | 10 |
| structured 6x3 | 18/18 (100%) | 11 | 9 |
| multi-turn | 26/28 (93%) | 2 | 24 |
| late turns (>=5th) | 12/12 (100%) | 2 | 10 |
For context, the instruction-tuned Whittle v2.1 release passed at 8 / 22 / 7 / 8%.
Read this plainly: as a conversational model, this checkpoint fails. It loops and it answers too briefly. That is the expected consequence of what it is — a training-free carve plus a short alignment pass, with no instruction tuning and no chat healing as an MoE. Its strengths are narrow and real (held-out CE 3.91 at 33% active experts, 5/5 on the factual smoke battery); its conversational behaviour is not one of them. Use it to reproduce or extend the carve method, not as an assistant.
Honest limitations
- Test model. No instruction tuning as a MoE. Chat quality is not characterised.
- Held-out CE at the shipped k is still ~0.43 above the dense parent (3.91 vs 3.485). The sparsity is not free.
- The 5-probe battery is a smoke test, not a benchmark. No MMLU/GSM8K numbers are claimed.
- Alignment ran for a few thousand steps at batch size 1 — far below the reference recipe. More alignment is the obvious next lever.
- The serving configuration is baked into the weights (uniform top-k weighting via a
scaled router, with
down_projcompensating). Changenum_experts_per_tokand you must rescaledown_projbyk_new / 58, or the output amplitude will be wrong.
Use
from transformers import AutoModelForCausalLM, AutoTokenizer
# the reference carve, k=58 (what the method section describes)
m = AutoModelForCausalLM.from_pretrained("logic65/whittle-next-moe-test",
dtype="bfloat16", device_map="auto")
# the better-measured variant, k=104 + top-64 KD (92.7% -> 83.7% loop-test failures)
m = AutoModelForCausalLM.from_pretrained("logic65/whittle-next-moe-test",
subfolder="kd-k104", dtype="bfloat16", device_map="auto")
GGUF: whittle-next-moe-test-Q4_K_M.gguf (k=58 reference) and
whittle-next-moe-kd-k104-Q4_K_M.gguf (k=104, recommended). Both run on stock llama.cpp.
To try another sparsity yourself, use rebake_k.py from the kit: it rescales down_proj by
k_new/k_old and rewrites num_experts_per_tok. Nothing else needs to change — the router is
already flat, and top-k selection is scale-invariant.
A CUDA GPU is required — the gated-delta-net layers use Triton kernels.
Method credits
Carve follows ExpertWeaver (arXiv 2602.15521); learned assignment follows DOT-MoE (arXiv 2606.01666). Parent lineage: Qwen3.8-27B (Apache-2.0) → Whittle-tri-14.7B → this.
Built by David Aylward with Claude (Anthropic) as co-author.
If this is useful to you: ko-fi.com/davida81328
Follow-up (30 Aug): what actually fixes the loop test — capacity, not weights
The loop-test numbers above are bad, so we tried to fix them. Five arms were measured on the same harness, same host, same Q4_K_M quantisation, in one session. The baseline was re-measured rather than quoted, and it reproduced the published table exactly (31/36, 18/18, 26/28, 12/12), so these comparisons are like-for-like.
Failures out of 94 generations — lower is better.
| arm | total | single 12×3 | struct 6×3 | multi | late ≥5th |
|---|---|---|---|---|---|
| baseline, k=58 (published) | 87 | 31/36 | 18/18 | 26/28 | 12/12 |
| + chat SFT on 90 teacher answers | 87 | 31/36 | 17/18 | 27/28 | 12/12 |
| + top-64 KD from the 27B teacher | 86 | 29/36 | 18/18 | 27/28 | 12/12 |
| k=104, no training at all | 80 | 30/36 | 16/18 | 24/28 | 10/12 |
| KD weights served at k=104 | 74 | 27/36 | 18/18 | 22/28 | 7/12 |
The result, with a significance test
Raising the number of active experts — a pure config change, no training — beat every training run we did. Going from k=58 to k=104 removed 7 failures; the KD pass on top removed 6 more. Because 94 generations is not much evidence for a ~10% shift, the best arm was re-run against the baseline over three independent seed blocks (246 paired generations) and tested with an exact paired McNemar:
| failure rate | ||
|---|---|---|
| baseline, k=58 | 228/246 = 92.7% | |
| KD weights @ k=104 | 206/246 = 83.7% | discordant 37 vs 15, p = 0.0032 |
So the effect is real. On the single 94-generation run it was not significant (p = 0.12) — worth knowing if you use this harness to gate anything. Late-turn failures fell from 12/12 to 7/12. On an 8-prompt greedy probe the same effect is visible directly: 4-gram repetition halves, 0.227 at k=58 → 0.106 at k=104 → 0.058 at full k. For this carve, conversational degeneration is a capacity problem: at 33% active FFN the model does not have enough width to stay coherent, and it loops.
The cost is compute, not parameters: k=104 activates 2048 + 104×64 = 8,704 / 17,408 ≈ 50%
of the FFN instead of 33%. The weights are identical; only down_proj is rescaled by
104/58 to match the runtime's 1/k renormalisation.
Two traps worth knowing if you fine-tune this
Never train mlp.gate.weight. The serving semantics are baked in by scaling every router
by 1e-4 (that is what makes the top-k softmax uniform). Those weights are ~1e-4 of normal
magnitude, so any normal optimizer step re-peaks the router and down_proj × k/16 then
amplifies the damage. Same data, only the trainable set differing: routers trained → CE 4.41,
battery 1/5, rep4 0.843; routers frozen → CE 3.946, battery 4/5. Both Muon and AdamW failed
identically, so the optimizer was a red herring — the trainable set was the bug.
A short greedy probe is not a chat metric. During chat SFT, held-out CE fell monotonically (3.9522 → 3.9321) while 4-gram repetition on a 3-prompt probe swung 0.446 → 0.014 → 0.831. The checkpoint that looked repaired on the probe scored exactly the same 87/94 as the untrained baseline on the full harness. Gate on the harness, not the probe.
Where the remaining ceiling is
Because the carve is exact at full k, the checkpoint contains its own dense teacher: set
top_k = 240 and scale the routed output by 240/58 (never the shared expert). Doing that
recovers the dense parent — and the dense parent also fails to stop on 3 of 8 prompts, answers
"write a short paragraph about why the sky appears blue" with a JSON write_file tool call, and
reads "three tips for writing clearer code" as a three.js request. The carve is faithful to a
parent that was never instruction-tuned. The dominant surviving failure is short on
multi-turn, and those answers are not truncated — they are confidently wrong and tiny
("In macOS macOS."). That is inherited, and no amount of routing or self-distillation removes
it; it needs a better parent or real instruction tuning.
Scripts for every arm (rebake_k.py, teacher_gate.py, train_kd_sharpen.py, train_chat.py,
publish_prep.py, loop_test.py) and all raw logs are in
logic65/mini-next-a100-kit.