Hoofdstuk 18 · mijlpaal M9
De tweede motor
Hoofdstuk 16 eindigde met een weigering: de hybride graf van Qwen3.8 was "een andere motor, geen grotere", en die motor hadden we niet. Dit hoofdstuk bouwt hem. Eén dag na de vraag draait de Gated DeltaNet-architectuur in pure Java — en is ze op de vertrouwde manier bewezen: acht van acht greedy reeksen exact identiek aan llama.cpp.
Deel 01 De sleutel: een klein zustermodel
Wat hoofdstuk 16 onmogelijk maakte was niet alleen de maat van de 27B, maar het ontbreken van een oefenmodel: een nieuwe motor bouw je niet blind tegen een bestand dat niet eens in het geheugen past. De sleutel bleek qwen3.5:0.8b — één gigabyte, en in het GGUF-bestand staat exact dezelfde architectuurstring als bij Qwen3.8-27B: qwen35. Daarmee gold weer het recept van hoofdstuk 14: bouw lokaal tegen het kleine model, bewijs elke stap greedy woord-voor-woord tegen llama.cpp (via ollama), en het grote model volgt vanzelf — dezelfde graf, andere getallen.
Het bestand verklapt zijn opbouw zelf. De kv-koppen staan er niet als getal maar als rij per laag: [0, 0, 0, 2, 0, 0, 0, 2, …] — achttien nullen en zes tweeën. Een nul betekent: geen aandacht, geen KV-cache; die laag is een Gated DeltaNet-laag.
Deel 02 Gated DeltaNet: een geheugen van vaste grootte
De aandacht van hoofdstuk 14 kijkt bij elke token terug naar álle vorige — vandaar de KV-cache die meegroeit met het gesprek. Een DeltaNet-laag doet iets fundamenteel anders: ze onderhoudt per kop een geheugenmatrix S van vaste grootte (128×128 getallen), en werkt die per token bij in vier stappen:
- Vervalen. S ← exp(g)·S — oud geheugen vervaagt, met een vervalsnelheid g die het model per token en per kop zélf kiest (uit de invoer berekend, via een softplus en de geleerde parameters ssm_a en ssm_dt).
- Uitlezen. Met de sleutel k wordt gelezen wat het geheugen nú over deze invoer denkt: Sᵀk.
- De delta. Het verschil tussen wat er bínnenkomt (v) en wat het geheugen verwachtte, geschaald met een poort β: δ = β·(v − Sᵀk).
- Bijschrijven en antwoorden. S ← S + k·δᵀ, en de uitvoer is Sᵀq (geschaald met 1/√128).
Wie Deel I van dit boek heeft gelezen, herkent stap drie: dit is een leerregel — corrigeer het geheugen met precies de fout die het maakte, zoals de netjes van hoofdstuk 3 leerden van hun fout. Een DeltaNet-laag leert dus een beetje tíjdens het spreken, binnen één gesprek, in een matrix die nooit groeit. Dáárom kan Qwen3.8 een context van 262.144 tokens aan: driekwart van zijn lagen heeft geen cache die meegroeit.
Om de invoer heen zit nog het nodige randwerk, en elk stukje bleek te tellen: een korte causale convolutie (kernel 4) met SiLU die elke q/k/v-projectie mengt met de drie vorige tokens, L2-normalisatie van q en k per kop, en aan de uitgang een RMSNorm die met silu(z) wordt ge-gate vóór de uitprojectie. De toestand per laag is bescheiden: de S-matrices plus drie kolommen conv-geheugen — ongeveer één megabyte, tegen een KV-cache die bij hoofdstuk 14 met elke token groeide.
Deel 03 De aandacht nieuwe stijl
De zes overgebleven aandachtslagen zijn familie van hoofdstuk 14, met drie afwijkingen die stuk voor stuk stille valkuilen waren geweest:
- QK-norm. Elke q- en k-kop krijgt een eigen RMSNorm vóórdat RoPE draait — met gewichten die in het GGUF-bestand al "+1 ingebakken" hebben (het HF-origineel telt vanaf nul; de conversie doet de optelling).
- Partiële RoPE. De koppen zijn hier 256 breed, maar RoPE draait alleen de eerste 64 dimensies; de overige 192 gaan ongedraaid door. (Het bestand vermeldt multimodale M-RoPE-secties, maar bij alleen-tekst degenereert die wiskundig exact tot de gewone NEOX-draai van hoofdstuk 14 — dat scheelde een halve motor.)
- Een poort in de q-projectie. De q-matrix levert per kop niet 256 maar 512 getallen: eerst de query, dan een poort. Na de aandacht wordt de uitvoer elementgewijs met sigmoid(poort) vermenigvuldigd, vóór de uitprojectie. Eén projectie, twee banen.
Deel 04 De methode, en drie valkuilen
Net als bij hoofdstuk 14 is de graf niet uit een artikel overgeschreven maar uit de referentie-implementatie: vier onderzoeksagenten hebben de llama.cpp-bron (de qwen35-grafbouwer, de delta-regel-operatie, het conversiescript) en de transformers-referentie regel voor regel ontleed, mét regelnummers. Qwen35.java volgt dat spoor operatie voor operatie; de lader, tokenizer, kernen, sampler en het sjabloon van de eerdere mijlpalen doen gewoon mee, ongewijzigd. Drie dingen hadden stil kunnen misgaan:
- Het gewicht van de ge-gate uitgangsnorm (ssm_norm) is de enige norm waar de conversie géén +1 bij optelt. Eén blik in het echte bestand besliste het: de waarden liggen rond de 1,0 — gewoon vermenigvuldigen dus, overal dezelfde RMSNorm.
- <think> is een speciaal token, en qwen3.5 opent er elk chatantwoord mee. De toetsen en de chat stopten op élk speciaal token — waardoor onze kant leek te zwijgen waar llama.cpp dacht. De stopregel is nu tweeledig: de externe poort stopt alleen nog op het eos-token, en de chat laat précies de twee denk-tokens door — andere vreemde stop-tokens blijven daar geweerd, zoals sinds hoofdstuk 15.
- De kv-koppen-rij per laag liet zelfs GgufDump struikelen — die verwachtte sinds hoofdstuk 12 een enkel getal. Ook het gereedschap groeit mee.
Deel 05 Het bewijs
De externe poort van hoofdstuk 14, nu met de tweede motor eronder:
$ java --add-modules jdk.incubator.vector src/ForwardVsOllama.java $MODEL35 qwen3.5:0.8b
[==] hoofdstad identiek: " Paris.\nThe capital of France is Paris.\nThe"
[==] nederlands identiek: " Amsterdam.\nDeze 100000"
[==] gesprek identiek: "<think>\n\n</think>\n\n2 + 2 = **4**"
…
== RESULTAAT ==
exact gelijk 8 van 8 prompts
GESLAAGD - elke reeks identiek aan llama.cpp
Acht prompts, twaalf greedy tokens per stuk, woord voor woord gelijk — inclusief het denkblok waarmee dit model elk chatantwoord opent. De zelftest (Qwen35Test) bewaakt wat daarvoor nodig is en stil kan breken: determinisme, een reset() die de recurrente toestand écht wist (een half gewiste S-matrix geeft geen foutmelding, alleen andere woorden), en de kernenweg tegen de fp32-referentie: grootste afwijking 1,1·10⁻⁵. En praten gaat ook, met dezelfde Chat.java als in hoofdstuk 15:
jij> What is 2 + 2?
model> <think>
</think>
2 + 2 = 4
(11 tokens, 22,8 per seconde; gesprek: 40/2048)
Nieuw is het loket: Motor.java kiest per bestand de juiste motor op grond van de architectuurstring, en Forward, Chat en de ollama-poorten praten alleen nog tegen dat loket. De bewuste weigering van hoofdstuk 16 is daarmee geen eindpunt meer maar een wegwijzer — en de qwen2-poorten bleven bij dit alles onveranderd groen (8/8, en alle zelftests).
Slotwoord De grens is verlegd, niet verdwenen
Wat betekent dit voor het echte Qwen3.8-27B? De architectuurgrens is gevallen: dezelfde motor die het 0,8B-zustermodel exact naspeelt, aanvaardt ook het 27B-bestand — inclusief zijn losse lm-head, die de review nog uit de kop van het echte bestand viste — want de graf is identiek, alleen de getallen verschillen. Het bewijs op de 27B zelf is aan de DGX. De geheugengrens staat nog fier overeind: zeventien gigabyte gewichten passen niet in de acht van deze MacBook Air. Maar dáárvoor staat sinds kort een DGX Spark met 128 GB klaar, en de meegeleverde DGX-testronde (draai-dgx-test.sh) draait het volledige programma nu ook op het echte bestand: motor-zelftest, greedy rooktest en een chatbeurt op de 27B, door onze eigen Java-code.
Eén ding spreekt nog het oude dialect: de agent van hoofdstuk 17 biedt zijn gereedschap aan in het JSON-formaat van qwen2, terwijl de qwen3.5-familie een XML-variant verwacht. Dat is bewust blijven liggen — een mijlpaal hoort één ding goed te doen. De cirkel van dit boek begon bij één neuron dat leerde van zijn fout; hij eindigt, voorlopig, bij een geheugenmatrix die datzelfde doet — midden in een gesprek, 128 bij 128 getallen groot.
# de tweede motor, vanuit de map code/ ($MODEL35 = een qwen3.5/qwen3.8-bestand)
java --add-modules jdk.incubator.vector src/Qwen35Test.java $MODEL35
java --add-modules jdk.incubator.vector src/Forward.java $MODEL35 --n 24 "The capital of France is"
java --add-modules jdk.incubator.vector src/Chat.java $MODEL35 --systeem "You are a helpful assistant."