Kísérleti napló · 2026-05-28
Kidobjuk a soha-nem-
használt szakértőket?
Egy 35 milliárd paraméteres MoE-modellt szabunk a saját otthoni munkámra. Egy éjszaka. Két reboot. Egy bug, amit ha nem találunk meg, az egész kísérlet hamis konklúzióval végződött volna.
Két egyszerű kérdés egy bonyolult modellről
Beégethetjük a system promptot?
Minden hívásnál ugyanazt a hosszú utasítást küldjük. Be lehet ezt sütni a modell súlyaiba, hogy ne kelljen mindig elküldenünk?
Kidobhatjuk amit nem használunk?
Ha mindig hasonló dolgokat csinálok vele (kódolás, projektmenedzsment, családi nyaralás), van-e olyan rész a modellben, amit szinte sosem ébresztek fel?
A poszt mindkettőre választ ad — de a B kérdést jártuk körül komolyabban.
Mi az a 'Mixture of Experts'?
Képzelj el egy konferenciatermet 256 specialistával. Mindegyikük valamire jó: egy a hibakeresésre, másik a magyar nyelvre, harmadik a Python-syntaxra, és így tovább.
Amikor a modellnek el kell döntenie a következő szót, egy 'router' kiválasztja közülük a 8 legrelevánsabbat, és csak ők szólalnak meg. A többi 248 specialista pihen azon a szón.
Ez a trükk teszi lehetővé, hogy egy 35 milliárd paraméteres modell olyan gyorsan fusson, mintha 3 milliárd lenne — mert minden szóra effektíve csak 3 milliárd 'aktív' paraméter számol.
A kérdés tehát: van-e 256 specialista közül olyan, akit én — az én jellegzetes munkámban — szinte sosem hívok meg?
Az éjszakai program
Bake — a system prompt beégetése
Egy rövid finetuning-menet (úgynevezett LoRA): a modellnek megmutatjuk hogyan reagáljon mint Aemie, miközben elhagyjuk a system promptot az inputból. Idővel a stílus a súlyokba költözik.
Prune — kivágni a hideg experteket
Megmérjük melyik specialistát mennyit hívom, majd a legritkábban használtakat egyszerűen kihasítjuk a modellfájlból. Kisebb fájl, gyorsabb betöltés, ugyanolyan minőség (remélhetőleg).
Spoiler: A bake-hez NVIDIA GPU kell — itthon csak inferenciára való iGPU-m van —, úgyhogy azt csak előkészítettük. A prune lefutott. Erről a maradék slide-ok.
A főeredmény
Negyedével kisebb modell, alig érzékelhető minőségvesztés
| Variáns | Méret | Minőség (PPL) | Prefill |
|---|---|---|---|
| Eredeti (256 expert) | 22 GB | 8.82 | 353 t/s |
| K=224 (12.5% kivágva) | 19 GB (−14%) | 8.83 (+0.12%) | 371 (+5%) |
| K=192 ★ sweet spot | 17 GB (−23%) | 8.92 (+1.13%) | 387 (+10%) |
| K=128 (fele kivágva) | 12 GB (−45%) | 9.33 (+5.83%) | 464 (+32%) |
A K=192 a sweet spot: a modell 23%-kal kisebb, miközben a minősége gyakorlatilag változatlan (1.13% növekedés a perplexitásban — az 'mennyire bizonytalan a következő szóban' mérőszámban). Ez ingyen-pénz.
Hogy néz ki a 'használat' egy rétegen?
Egy reprezentatív réteg 256 expertje, használatra rendezve. A bal oldali kevés sztár csinálja a munka nagy részét, a jobb oldal felé a hidegebb tail.
Az enyhén meglepő: nincs olyan expert, ami SOHA nem szólal meg. A modell-tréning szándékosan szétkeni a munkát. Viszont a farok éppen elég vékony, hogy levághassuk.
A profilozó: egy llama.cpp eval-callback
Új példa az llama.cpp tree-ben, ami minden forward pass alatt elcsípi a router döntéseit.
// build_moe_ffn() in llama-graph.cpp:
// cb(selected_experts->src[0], "ffn_moe_argsort", il); // [256, n_tokens] I32, contiguous
// cb(selected_experts, "ffn_moe_topk", il); // [8, n_tokens] — VIEW (gotcha!)
static bool moe_cb_eval(ggml_tensor *t, bool ask, void *ud) {
if (ask) return strncmp(t->name, "ffn_moe_argsort", 15) == 0;
if (!is_argsort) return true;
int il = atoi(strrchr(t->name, '-') + 1);
int32_t *ids = (int32_t*) backend_get(t); // contiguous parent
for (int tok = 0; tok < n_tokens; ++tok)
for (int rank = 0; rank < n_used; ++rank) // top-8 per token
counts[il][ ids[tok * n_expert + rank] ]++;
return true;
}Inputja a meglévő GGUF model + egy korpusz; outputja egy JSON: rétegenként × expertenként hány selection.
Buktató — kis híján kísérlet-gyilkos
Amikor a tökéletes egyenlőség a hiba
ELSŐ FUTÁS · ELRONTOTT
Minden expert minden rétegen pontosan 1065–1068 selection-t kapott. std/mean = 0.001.
34 ezer tokenen statisztikailag képtelenség → biztos hogy bug.
JAVÍTOTT FUTÁS · VALÓDI
min=0, max=92, std/mean=2.66 a mélyebb rétegekben. Erősen ferde, valódi routing.
Ez a kép tette lehetővé az egész további pruningot.
Diagnózis és fix
A ffn_moe_topk egy nem-folytonos view a teljes argsort fölött. A ggml_backend_tensor_get folytonos bájtokat másol — így a teljes 0..255 permutációkat olvastam minden tokenre, ezért minden expert egyenletesen pontosan egyszer szerepelt. Fix: a folytonos parent ffn_moe_argsort ([256, n_tokens]) tensort olvasni, per-token az első 8 sort venni — ez a valódi top-8.
A valódi routing-tömeg
Mennyit fed le a TOP-K expert a routing tömegnek per layer, összevont dev+pm+family korpuszra. A worst layer oszlop a kritikus: az limitálja az uniform GGUF pruningot.
| K | tömeg(átlag) | tömeg(worst layer) | % expert |
|---|---|---|---|
| 256 | 100.00% | 100.00% | 100.0% |
| 224 | 99.31% | 95.13% | 87.5% |
| 192 ★ | 97.65% | 88.65% | 75.0% |
| 160 | 94.95% | 81.04% | 62.5% |
| 128 | 90.80% | 72.43% | 50.0% |
| 96 | 84.02% | 62.38% | 37.5% |
- · per-layer max/min expert-usage ratio: median 2810 (uniform = 1.0)
- · deeper layers ferdébbek: layer 0 std/mean=1.75, layer 39 = 3.19
- · 0 teljesen halott expert (load-balancing loss tervezett mellékhatása)
Sebészet: raw-slab gather, nincs requant
A trükk ami miatt nem kell dequantálni: a 4 expert-hordozó tensorban az expert-index a legkülső ggml-tengely. A gguf-py numpy byte-shape-ben az axis 0 = expert → data[keep_indices] egy fancy-index és KÉSZ. A Q4_K/Q5_K/Q6_K blokk-kvantálás betűre megőrződik.
# prune_gguf.py — the core
for t in reader.tensors:
data = t.data
if expert_tensor_re.match(t.name): # ffn_{gate,up,down}_exps
il = layer_from_name(t.name)
data = np.ascontiguousarray(data[keepset(il)]) # axis 0 = expert
elif router_re.match(t.name): # ffn_gate_inp
data = np.ascontiguousarray(data[keepset(il)])
writer.add_tensor_info(t.name, data.shape, data.dtype, data.nbytes, t.tensor_type)
# *_shexp (shared expert) -> copied verbatim, never pruned- · 4 érintett tensor / MoE layer: gate_exps + up_exps + down_exps + gate_inp (router)
- ·
*_shexp(shared expert, mindig aktív) érintetlenül átmásolva - · globális metadata: expert_count = K, expert_used_count marad 8
- · 23 GB → pruneolt GGUF: ~12 másodperc lemezírással együtt
Sebesség: prefill skálázódik, generation nem
llama-bench gfx1150 Vulkan-on, ngl=99. Érdekes minta: a prefill (prompt processing) szépen skálázódik K-val — de a generation lényegében változatlan, mert per-token mindenképpen csak 8 expert aktív.
| modell | méret | params | pp256 t/s | tg64 t/s |
|---|---|---|---|---|
| baseline (256) | 21.3 GiB | 35.5 B | 352.8 ± 7.6 | 22.85 |
| 224 | 18.9 GiB | 31.4 B | 371.0 +5% | 22.80 |
| 192 ★ | 16.6 GiB | 27.2 B | 386.8 +10% | 21.86 |
| 128 | 11.9 GiB | 19.0 B | 464.3 +32% | 23.43 |
Mellesleg: K=128 modell ugyanolyan sebességgel generál (23 t/s), mint a baseline 35 B — viszont a fele méreten. Bizonyos use-case-ekre (gyors draft MTP-hez, telefonra) ez érdekes.
Egy darab nyitva maradt rejtély
K=128 + llama-perplexity + Vulkan = GPU-fagyás
A K=128 fájl betöltése a perplexity-mérőben uninterruptible kernel-állapotba ragadt amdgpu-buffer-allokáció közben. kill -9 nem fogta. Két amdgpu_gpu_recover sem oldotta fel. Reboot kellett.
Name: llama-perplexit
State: D (disk sleep)
VmRSS: 12057500 kB
wchan: drm_suballoc_new
De érdekes:
A llama-bench ugyanazt a K=128 fájlt Vulkan-on PROBLÉMA NÉLKÜL betölti és futtatja (32% gyorsabb prefill, 23 t/s gen). Vagyis nem K=128 fájl-corrupt; egy specifikus tool×Vulkan×ez-a-shape interakció triggerel valami amdgpu-buffer-deadlock-ot. PPL méréshez K=128-on a CPU út (-ngl 0) biztonságos — onnan jött a 9.33 érték.
Két reboot egy éjszaka — és a stack mindkettő után magától visszaállt
Az első reboot az volt, amit én indítottam el a K=128 GPU-fagyás miatt. A második — egy órával később — egy klasszikus áramszünet. (Tényleg.)
Mindkét boot után a teljes stack magától felállt: az LLM-szerver (lemond), 7 user service, 13 cron-timer, 11 docker konténer. Manuális restore-script nem kellett.
A systemd enabled + linger + docker restart-unless-stopped trifecta a defense-in-depth megéri-érzet. (És most már UPS-t is veszek.)
Tanulság
A rossz kérdés és a jó kérdés
Az eredeti kérdésem így szólt: "Van-e olyan szakértő ebben a modellben, akit én SOHA nem használok?" Erre a válasz, kiderült, nincs — a load-balancing-loss-szal tanított modellben minden szakértő tüzel valahol.
A jó kérdés: "Mennyi minőségvesztést bírunk vesztni cserébe X% méretért?" Erre van pontos válasz: a K=192-vel 23% méretmegtakarítás, ~1% PPL ár, és ~ugyanolyan generation-sebesség. K=128-cal a fele méret, ~6% PPL áron — egy más felhasználási kategória (fast draft, mobil).
A bake (Track A) még előttünk áll — de már most látszik, hogy a bake és a prune együtt szinergikus: egy stílusra-betanított modell routingja koncentráltabb lesz → több kidobható expert.