Van neuron tot taalmodel

Hoofdstuk 12 · van speelgoednetwerk naar taalmodel

Wat staat er in een GGUF-bestand?

Je downloadt een bestand van 379 megabyte, iemand noemt het "een taalmodel", en als je het in een teksteditor opent zie je alleen brol. Dit hoofdstuk pelt zo'n bestand laag voor laag af — met de echte bytes uit een echt model.

In één alinea

Een GGUF-bestand is een enorme stapel getallen, met vooraan een etiket dat vertelt wat die getallen betekenen. Meer is het niet.

In Deel I bouwden we netwerken die we volledig konden overzien: 329 parameters voor Optelnet, ruim tweeduizend voor RekenNet. Het bestand dat we nu openmaken bevat er 494 miljoen — maar het is nog steeds precies hetzelfde soort ding: gewichten in roosters.

Het etiket is klein: 1,5 % van het bestand. De overige 98,5 % zijn 494 miljoen getallen, samengeperst tot gemiddeld 6,35 bit per stuk. Die getallen zijn de gewichten: de sterkte van elke verbinding in het netwerk. Wat er verder in dit hoofdstuk staat, is vooral uitleg bij die twee zinnen.

4
delen waaruit elk GGUF-bestand bestaat
494 mln
getallen in dit ene bestand
98,5 %
van het bestand zijn die getallen
6,35 bit
gemiddeld per getal, in plaats van 32

Deel 01Eerst: wat is een "gewicht"?

Deze vraag moet eerst beantwoord worden, want zonder dat blijft de rest van het bestand betekenisloos. En het antwoord is eenvoudiger dan de meeste uitleg doet vermoeden.

Een neuraal netwerk doet in wezen één ding: het neemt een rijtje getallen en maakt daar een ander rijtje getallen van. Dat herhaalt het een aantal keer. Alles wat "kunstmatige intelligentie" heet, is opgebouwd uit die ene bewerking.

Neem het allerkleinste voorbeeld: drie getallen in, twee getallen uit. Elk ingangsgetal heeft invloed op elk uitgangsgetal, dus er zijn zes verbindingen. En bij elke verbinding hoort één getal dat zegt hoe sterk die invloed is. Dat getal is een gewicht.

Zo teken je het drie ingangen, twee uitgangen, zes verbindingen +0,31 −0,74 −0,12 +0,08 +0,55 −0,29 2,0 0,5 1,0 1,11 −1,73 ingang uitgang de onderste uitgang: 2,0 × −0,74 = −1,48 0,5 × +0,08 = +0,04 1,0 × −0,29 = −0,29 samen −1,73 hetzelfde, anders opgeschreven Zo staat het in het bestand alleen de zes getallen, als rooster naar uitgang 1 naar uitgang 2 +0,31 −0,74 −0,12 +0,08 +0,55 −0,29 van ingang 1 van ingang 2 van ingang 3 Zo'n rooster heet een tensor. Dit is er één van 3 × 2. In Qwen2.5-0.5B is de kleinste 896 × 128, de grootste 896 × 151 936.
Links het schoolboekplaatje, rechts wat er werkelijk opgeslagen wordt. De twee zijn identiek: zes verbindingen, zes getallen.

Rechts staat de sleutel tot dit hele hoofdstuk. Zodra je de zes gewichten in een rooster zet, heb je de tekening niet meer nodig. De rijen zeggen waar een verbinding vandaan komt, de kolommen waar ze naartoe gaat, en het getal in het vakje is de sterkte. Zo'n rooster heet een tensor.

Het belangrijkste inzicht van dit hoofdstuk

De verbindingen staan niet in het bestand. Nergens staat een lijst van "ingang 1 is verbonden met uitgang 2". Dat hoeft ook niet, want alles is met alles verbonden. Als je weet dat er drie ingangen en twee uitgangen zijn, weet je dat er precies zes verbindingen zijn, in een vaste volgorde.

Daarom kan een modelbestand niets anders zijn dan een lange rij getallen. De structuur zit in de vorm van de roosters, niet in het bestand als aparte informatie.

En dan schaal je het op

Het voorbeeld hierboven heeft zes gewichten. Het model dat we in dit hoofdstuk opendoen heeft er 494 miljoen, verdeeld over 290 roosters. Eén enkel rooster daarin, dat de aandachtsberekening van laag 7 doet, is 896 bij 896 — dat zijn 802 816 getallen, voor dat ene rooster alleen.

Er verandert conceptueel niets. Het is exact dezelfde bewerking: neem elk ingangsgetal, vermenigvuldig het met het gewicht van zijn verbinding, tel alles op. Alleen 134 000 keer zo veel.

Waarom er nergens een "netwerktekening" in zit

Een GGUF-bestand vertelt niet in welke volgorde de roosters gebruikt moeten worden. Er staat alleen general.architecture = "qwen2", en het programma dat het bestand leest weet wat een qwen2 is: eerst normaliseren, dan de drie aandachtsroosters, dan roteren, enzovoort. Die volgorde zit in het programma, niet in het bestand.

Het bestand is dus geen bouwtekening. Het is een doos onderdelen met een etiket erop dat zegt welk model het is.


Deel 02Waarom een eigen bestandsformaat nodig is

Als een model alleen maar getallen is, waarom zet je die dan niet gewoon achter elkaar in een bestand?

Omdat je er dan niets mee kan. Wie de getallen wil gebruiken moet ook weten:

  • Hoe ze verdeeld zijn. 494 miljoen getallen op een rij is nutteloos. Je moet weten waar rooster 1 eindigt en rooster 2 begint, en welke vorm elk rooster heeft.
  • Welk rooster welk is. Het rooster voor de aandacht van laag 7 doet iets heel anders dan dat voor de feed-forward van laag 3.
  • De instellingen. Hoeveel lagen? Hoeveel aandachtskoppen? Hoe lang mag de invoer zijn?
  • De woordenschat. Een taalmodel rekent niet met letters maar met tokennummers. De vertaaltabel tussen tekst en nummers moet mee in het bestand, anders is het model onbruikbaar.

GGUF is het formaat dat llama.cpp daarvoor bedacht heeft, en het is inmiddels de standaard voor modellen die je lokaal draait. De naam staat voor "GGML Universal File" — GGML is de rekenbibliotheek eronder.

Eén ontwerpkeuze is de moeite van het vermelden waard: het formaat is zo gemaakt dat een programma het bestand niet hoeft in te lezen. Het kan het bestand rechtstreeks aan het geheugen koppelen en de getallen laten staan waar ze staan. Daarom staan de gewichten achteraan, in één aaneengesloten blok, netjes uitgelijnd.


Deel 03De vier delen van het bestand

Elk GGUF-bestand heeft dezelfde opbouw. Vier stukken, netjes na elkaar, geen inhoudsopgave nodig.

Het volledige bestand — 397 807 936 bytes de gewichten — 391 859 712 bytes, 98,50 % het etiket: 5 948 224 bytes, 1,50 % Het etiket uitvergroot — 5 948 224 bytes de metadata — 5 931 701 bytes, 99,72 % header, 24 bytes tensorregister, 16 499 bytes De metadata uitvergroot — 5 931 701 bytes de woordenschat — 5 927 570 bytes, 99,93 % 151 936 tokens, 151 387 samenvoegregels, 151 936 tokensoorten de echte instellingen: 4 131 bytes De dunne blokjes zijn op minimumbreedte getekend, anders zouden ze onzichtbaar zijn. Alle byteaantallen zijn exact.
Driemaal inzoomen, en telkens blijkt hetzelfde: bijna alles is iets anders dan je zou denken. Het bestand is haast volledig gewichten; het etiket is haast volledig woordenschat; en wat er dan overblijft aan echte instellingen — hoeveel lagen, hoeveel koppen, welke architectuur — past in 4 kilobyte.
De vier delen met hun werkelijke posities in Qwen2.5-0.5B. Er zit hier geen opvulling tussen het register en de data, omdat het register toevallig al op een grens van 32 bytes eindigt.
DeelBytesGrootteWat erin staat
1 Header0 – 2324Handtekening, versie, en de twee aantallen die de rest ontsluiten.
2 Metadata24 – 5.931.7245.931.70134 sleutel-waardeparen: de instellingen en de volledige tokenizer.
3 Register5.931.725 – 5.948.22316.499290 ingangen van gemiddeld 57 bytes: naam, vorm, formaat, positie.
4 Gewichten5.948.224 – einde391.859.712De 494 miljoen getallen zelf, samengeperst.

Er is geen inhoudsopgave nodig omdat elk deel precies zegt hoe groot het volgende is. De header geeft het aantal metadata-sleutels en het aantal tensoren; elke sleutel begint met zijn eigen lengte; elke registeringang met de lengte van zijn naam. Je kan het bestand dus van voor naar achter doorlopen zonder ooit te moeten raden of terug te springen.


Deel 04De header, byte voor byte

Hier is het begin van een echt bestand. Links de positie, in het midden de bytes in hexadecimale notatie, rechts diezelfde bytes als tekst gelezen — punten waar het geen leesbaar teken is.

00000000  47 47 55 46 03 00 00 00  22 01 00 00 00 00 00 00  |GGUF....".......|
00000010  22 00 00 00 00 00 00 00  14 00 00 00 00 00 00 00  |"...............|
00000020  67 65 6E 65 72 61 6C 2E  61 72 63 68 69 74 65 63  |general.architec|
00000030  74 75 72 65 08 00 00 00  05 00 00 00 00 00 00 00  |ture............|
00000040  71 77 65 6E 32 0C 00 00  00 00 00 00 00 67 65 6E  |qwen2........gen|

Daar zit het "aha" al in: als je zo'n bestand in een teksteditor opent zie je grotendeels brol, maar er staan wel degelijk leesbare stukjes tussen. Dat zijn de namen van de instellingen en de tokens van de woordenschat.

De eerste 24 bytes zijn de header. Meer dan vier gegevens staan er niet in:

De volledige header van Qwen2.5-0.5B. Merk op hoe de getallen achterstevoren lijken te staan: dat is klein-endische notatie, waarover hieronder meer.
BytesRuwBetekenis
0–347 47 55 46De letters GGUF. Een handtekening: leest een programma iets anders, dan stopt het meteen.
4–703 00 00 00Versie 3 van het formaat.
8–1522 01 00 00 00 00 00 00290 tensoren. (0x122 = 290)
16–2322 00 00 00 00 00 00 0034 metadata-sleutels. (0x22 = 34)

Waarom de getallen achterstevoren staan

Het getal 3 staat er als 03 00 00 00 en niet als 00 00 00 03. Dat is geen fout. Er zijn twee manieren om een getal van meerdere bytes op te schrijven, en GGUF kiest voor klein-endisch: het minst belangrijke stuk eerst, zoals wanneer je een datum als 14-08-2026 schrijft in plaats van 2026-08-14.

Zo lees je 290 dus als 22 01 00 00 00 00 00 00: de 22 vooraan is hexadecimaal voor 34, de 01 daarachter telt er 256 bij op, samen 290.


Deel 05De metadata: het etiket

Na de header volgen de sleutel-waardeparen. Elk paar is opgebouwd volgens hetzelfde recept: eerst de lengte van de naam, dan de naam, dan een typenummer, dan de waarde.

Hier is het allereerste paar van ons bestand, uit elkaar gehaald:

positie  ruwe bytes                              betekenis
-------  --------------------------------------  ---------------------------
24       14 00 00 00 00 00 00 00                 de naam is 20 tekens lang
32       67 65 6E 65 72 61 6C 2E 61 72 ...       "general.architecture"
52       08 00 00 00                             waardetype 8 = tekst
56       05 00 00 00 00 00 00 00                 de waarde is 5 tekens lang
64       71 77 65 6E 32                          "qwen2"
                                                 samen 45 bytes

Dat is alles. Vijfenveertig bytes voor de mededeling "dit is een qwen2". Zo gaat het 34 keer door, en pas daarna begint het tensorregister.

Er zijn dertien waardetypes. Naast tekst zijn dat gehele getallen in verschillende maten, kommagetallen, ja-nee-waarden, en het type lijst — dat laatste is hoe de hele woordenschat in één sleutel past.

Wat er zoal in staat

Een selectie uit de 34 sleutels van dit model. De namen met het voorvoegsel qwen2. zijn architectuurgebonden: bij een Llama-model heten ze llama. en zo verder.
SleutelWaardeWat het betekent
general.architectureqwen2Welk soort model. Bepaalt hoe het programma de roosters gebruikt.
general.nameQwen2.5 0.5B InstructAlleen ter informatie.
qwen2.block_count24Het model bestaat uit 24 identiek opgebouwde lagen.
qwen2.embedding_length896Elk woord wordt onderweg voorgesteld als 896 getallen.
qwen2.attention.head_count14De aandacht wordt in 14 parallelle stukken gesplitst.
qwen2.attention.head_count_kv2Maar er zijn er maar 2 die eigen geheugen bijhouden; dat bespaart veel.
qwen2.feed_forward_length4864Binnen elke laag wordt tijdelijk uitgezet naar 4864 getallen.
qwen2.context_length32768Zoveel tokens kan het model maximaal tegelijk overzien.
tokenizer.ggml.tokens151 936 stuksDe volledige woordenschat, als lijst van tekstjes.
tokenizer.ggml.merges151 387 stuksDe regels waarmee tekst in tokens geknipt wordt.
tokenizer.chat_templatetekstHoe een gesprek opgemaakt moet worden voor dit model.

Waarom de metadata toch 5,9 MB is

Alle instellingen uit de tabel hierboven samen — architectuur, lagen, koppen, contextlengte, zelfs het volledige gespreksjabloon — nemen 4 131 bytes in. Vier kilobyte.

De overige 5 927 570 bytes zijn de tokenizer: 151 936 tokentekstjes, 151 387 samenvoegregels, en een lijst van 151 936 tokensoorten. Dat is 99,93 % van de metadata. Wie een GGUF-bestand opendoet en zich afvraagt waarom het "etiket" zo zwaar weegt: het is geen etiket, het is een woordenboek.


Deel 06Het register: de inhoudsopgave van de roosters

Nu weten we hoe het model is ingesteld, maar nog niet waar de getallen staan. Daarvoor is het derde deel: 290 ingangen, één per rooster.

Elke ingang bevat vier dingen: een naam, de vorm, het opslagformaat en de positie. Meer niet.

De namen zijn geen willekeurige etiketten — ze zijn opgebouwd volgens een vast stramien, en als je dat kent kan je uit de naam alleen al afleiden wat het rooster doet.

Een tensornaam ontleed blk 7 attn q weight . . . . "block" een van de 24 lagen laag 7 geteld vanaf 0 "attention" het deel dat naar vorige woorden kijkt "query" de vraag: waar zoek ik naar? gewichten tegenover .bias, een vaste bijtelling Andere namen die je tegenkomt token_embd de woordentabel attn_k, attn_v sleutel en waarde attn_output terug naar 896 ffn_gate, ffn_up uitzetten naar 4864 ffn_down terug naar 896
Alle 290 namen volgen dit stramien. De 24 lagen hebben allemaal exact dezelfde twaalf roosters, alleen met een ander laagnummer.

Zo ziet laag 7 er in werkelijkheid uit — en laag 0 tot 23 zijn identiek, op het nummer na:

De twaalf roosters van één laag. Merk op dat de vorm altijd begint met 896: dat is de breedte waarmee het model werkt. En dat de drie feed-forward-roosters samen veruit het grootst zijn.
NaamVormGetallenFormaatRol
attn_norm.weight[896]896F32schaalt de invoer bij
attn_q.weight[896, 896]802.816Q5_0de vraag
attn_k.weight[896, 128]114.688Q5_0de sleutel
attn_v.weight[896, 128]114.688Q8_0de waarde
attn_output.weight[896, 896]802.816Q5_0mengt de koppen terug samen
attn_q/k/v.bias[896] [128] [128]1.152F32vaste bijtelling
ffn_norm.weight[896]896F32schaalt opnieuw bij
ffn_gate.weight[896, 4864]4.358.144Q5_0poort: wat mag door?
ffn_up.weight[896, 4864]4.358.144Q5_0zet uit naar 4864
ffn_down.weight[4864, 896]4.358.144Q6_Kperst terug naar 896

Nu kan je de vormen zelf natrekken. attn_k.weight is 896 bij 128, want er zijn 2 sleutelkoppen van elk 64 getallen breed: 2 × 64 = 128. En attn_q.weight is 896 bij 896, want daar zijn het 14 koppen van 64: 14 × 64 = 896. Precies de getallen die in de metadata stonden.


Deel 07De gewichten, en hoe ze samengeperst zijn

We komen bij het laatste en veruit grootste deel. En bij een raadsel.

Er zijn 494 032 768 getallen. Een gewoon kommagetal in een computer neemt 4 bytes in. Dat zou dus bijna 2 gigabyte moeten zijn. Het bestand is 379 megabyte. Waar zijn die getallen gebleven?

Ze zijn gekwantiseerd: met opzet minder nauwkeurig opgeslagen. En het mooie is dat dat nauwelijks uitmaakt, om een reden die je misschien niet verwacht.

Het idee: één schaalfactor voor een groepje

Neem 32 gewichten die bij elkaar horen. Ze liggen meestal dicht bij elkaar in grootte — allemaal kleine getallen rond nul. In plaats van elk getal volledig op te schrijven, doe je dit: zoek het grootste getal in het groepje, en druk alle andere uit als een breukdeel daarvan.

Stap 1 — dit zijn de echte gewichten (de eerste 8 van 32) −0,010277+0,040786+0,009634 0,000000 −0,026977−0,002890−0,001285−0,019590 Elk zou 4 bytes kosten: 32 × 4 = 128 bytes voor dit groepje. Stap 2 — zoek de grootste, en deel alles daardoor De grootste is +0,040786. Deel die door 127 en je hebt de schaalfactor: d = 0,040786 / 127 = 0,00032115 Elk gewicht wordt nu een geheel getal tussen −127 en +127: −32+127+300 −84−9−4−61 Elk past nu in 1 byte. Het gewicht terugkrijgen doe je met: getal × d. Stap 3 — zo staat het in het bestand 43 0D E0 7F 1E 00 AC F7 FC C3 nog 24 bytes de schaalfactor, 2 bytes de 32 gehele getallen, elk 1 byte 34 bytes in plaats van 128 bijna vier keer kleiner, en dit zijn echte bytes uit het bestand
De eerste 34 bytes van token_embd.weight: het begin van de inbedding van token 0, het teken "!". Deze getallen komen rechtstreeks uit het bestand op je schijf.

Dit formaat heet Q8_0: 8 bits per gewicht, variant 0. De 43 0D vooraan is de schaalfactor in halve precisie, de 32 bytes daarna zijn de gehele getallen. Wie de gewichten wil gebruiken vermenigvuldigt gewoon terug.

Waarom dit werkt en niet tot onzin leidt

Je verliest wel degelijk nauwkeurigheid. Een gewicht van −0,0102771 wordt −0,0102768. Maar een neuraal netwerk telt honderden van die vermenigvuldigingen bij elkaar op, en die kleine afrondingen heffen elkaar grotendeels op — de ene te hoog, de andere te laag. Wat overblijft is ruis die verdwijnt in het geheel.

Dat is de eigenlijke reden dat je een model van 2 GB tot 379 MB kan indikken zonder dat het merkbaar dommer wordt.

Nog verder: 4 en 5 bits

Hetzelfde trucje werkt met kleinere getallen. Bij Q4_0 krijgt elk gewicht maar 4 bits, dus 16 mogelijke waarden, en passen er twee in één byte: 32 gewichten in 18 bytes. Bij Q5_0 zijn het er 32, in 22 bytes.

De K-varianten (Q4_K, Q6_K) zijn slimmer: ze werken met groepen van 256 en gebruiken twee niveaus van schaalfactoren, een grove voor het hele blok en fijnere per stukje van 32. Dat levert bij dezelfde grootte een merkbaar betere benadering op.

De formaten die in dit ene bestand voorkomen. De laatste kolom is wat het werkelijk kost per gewicht, inclusief de schaalfactoren — vandaar dat Q8_0 op 8,5 uitkomt en niet op 8.
FormaatGroepBytesBit/gewichtWaar het hier voor gebruikt wordt
F321432,00de 121 kleine roosters: normalisaties en biassen
Q8_032348,50de woordentabel en enkele gevoelige roosters
Q6_K2562106,5612 van de 24 ffn_down-roosters
Q5_032225,50132 roosters — het merendeel
Q4_K2561444,50de andere 12 ffn_down-roosters

Een raadsel dat zichzelf verklaart

Dit model wordt aangeboden als "Q4_K_M". Toch bestaat het grotendeels uit Q5_0, en niet uit Q4_K. Dat is geen fout.

De K-varianten werken in groepen van 256, en dus moet de rijlengte van een rooster deelbaar zijn door 256. Qwen2.5-0.5B werkt met een breedte van 896, en 896 is niet deelbaar door 256. Elk rooster dat uit die breedte leest kán dus geen Q4_K zijn en valt terug op Q5_0. Alleen ffn_down leest uit de feed-forward-breedte 4864, en die is wél deelbaar door 256 — vandaar precies 12 + 12 K-roosters voor 24 lagen.

Zulke details merk je pas als je zelf in het bestand kijkt. Ze staan in geen enkele modelbeschrijving.


Deel 08Waar zitten die 494 miljoen getallen eigenlijk?

Als je alle roosters bij elkaar optelt en groepeert, komt er iets uit dat de meeste mensen verrast.

494 032 768 parameters, naar onderdeel 63,5 % feed-forward — 313,8 miljoen 27,6 % woordentabel 8,9 % aandacht Feed-forward — 313 786 368 Per laag drie grote roosters die de 896 getallen tijdelijk uitzetten naar 4864 en weer terugpersen. Dit is waar het model zijn "kennis" bewaart. Verreweg het grootste deel van elk taalmodel. Woordentabel — 136 134 656 151 936 tokens × 896 getallen. Bij een klein model weegt die tabel zwaar door; bij een model van 8 miljard is hetzelfde tabelletje verwaarloosbaar. Aandacht — 44 040 192 De vier roosters per laag die naar vorige woorden kijken. Berucht, maar getalsmatig het kleinste onderdeel. Normalisaties en biassen: 0,01 %.
Alle percentages zijn uit het bestand geteld, niet geschat.

De aandacht — het mechanisme waar de meeste uitleg over transformers over gaat, en waar de titel "Attention Is All You Need" naar verwijst — beslaat nog geen 9 % van de getallen. Bijna twee derde zit in de feed-forward-lagen, het onderdeel waar zelden over gesproken wordt.

En de woordentabel is bij dit kleine model bijna 28 %. Dat is ook waarom deze modellen vaak gebonden inbeddingen gebruiken: dezelfde tabel wordt hergebruikt aan het begin (woord naar getallen) en aan het eind (getallen naar woord). Scheelt in één klap 136 miljoen getallen. In dit bestand is dat te zien doordat er wel een token_embd.weight is maar geen output.weight.


Deel 09Zelf kijken

Alle cijfers in dit hoofdstuk komen uit één bestand dat waarschijnlijk al op je schijf staat.

De volledige broncode zit bij dit boek in de map code/ — hieronder ook als zip — en draait met één JDK (22 of nieuwer). Voer de opdrachten uit vanuit die map. De code is vrij te gebruiken onder de MIT-licentie.

Als je ooit een model met ollama hebt gedraaid, heb je GGUF-bestanden. Ollama bewaart ze zonder omhaal als gewone GGUF-blobs. Het pad vind je via het manifest:

# welke modellen staan er
ls ~/.ollama/models/manifests/registry.ollama.ai/library/

# het manifest wijst naar de blob; de laag met mediaType
# application/vnd.ollama.image.model is het GGUF-bestand
cat ~/.ollama/models/manifests/registry.ollama.ai/library/qwen2.5/0.5b

En dan kan je er het leesgereedschap uit dit project op loslaten:

java src/GgufDump.java ~/.ollama/models/blobs/sha256-c5396e06af... --tokens 10

Dat drukt precies af wat hierboven beschreven staat: de header, de 34 instellingen, alle 290 roosters met hun vorm en positie, de verdeling per formaat, en een reeks controles die nagaan of het bestand inwendig klopt.

Wil je de bytes zelf zien

Een hexdump van de eerste regels werkt op elk GGUF-bestand en is het snelste bewijs dat er niets magisch aan is:

xxd -l 96 model.gguf

Je zou nu elk teken van die uitvoer moeten kunnen verklaren.


SamenvattingHet hele bestand in zeven zinnen

  1. Een neuraal netwerk is een stapel getallenroosters; elk getal is de sterkte van één verbinding.
  2. De verbindingen zelf worden niet opgeslagen, want alles is met alles verbonden — de vorm van het rooster zegt genoeg.
  3. Een GGUF-bestand bestaat uit vier delen: een header van 24 bytes, de instellingen, een register van de roosters, en dan de getallen.
  4. De eerste drie delen samen zijn hier 1,5 % van het bestand, en daarvan is het grootste deel de woordenschat.
  5. De getallen zijn gekwantiseerd: per groepje van 32 of 256 wordt één schaalfactor bewaard plus een klein geheel getal per gewicht.
  6. Welk formaat een rooster krijgt, hangt af van de vorm: de betere K-formaten eisen een rijlengte die deelbaar is door 256.
  7. De volgorde waarin de roosters gebruikt moeten worden staat níét in het bestand — alleen de naam van de architectuur, waarna het programma de rest weet.

Alle bytes, byteposities, gewichtswaarden en percentages in dit hoofdstuk zijn afgelezen uit Qwen2.5-0.5B-Instruct (Q4_K_M, 397 807 936 bytes), zoals het door ollama op schijf bewaard wordt. Ze zijn niet nagemaakt of vereenvoudigd.

Lettertypen: IBM Plex Sans en IBM Plex Serif, SIL Open Font License 1.1, ingesloten in de gedeelde stylesheet van dit boek.