Generatieve AI voorspelt het volgende woord op basis van het laatst gegeven woord. Dit is in een notendop wat generatieve AI doet. Maar het hele proces is natuurlijk veel complexer. Het proces van het genereren van, bijvoorbeeld, één woord, noemen we de generatieve lus (generative loop). Naast woorden kan een generatieve AI-model ook audio en afbeeldingen voorspellen. Ook die worden gemaakt middels een lus, al zijn er verschillen. Hier gaan we het vooral hebben over taalmodellen: het genereren van teksten door AI.
Met deze uitleg wil ik onder de motorkap kijken van een AI-model en de verschillende elementen aanwijzen, niet om zelf de auto te kunnen repareren maar wel de werking van de auto beter te begrijpen. In eerste plaats helpt het om de mysterieuze mist rondom AI-modellen voor een deel weg te nemen. AI-modellen doen niets anders dan voorspellen en het optimaliseren van dat voorspellen. Ten tweede helpt het de risico’s van generatieve AI te plaatsen. Een aantal van de risico’s van AI zijn namelijk verbonden aan de architectuur van AI-modellen.
De uitleg kan op sommige punten wat technisch worden. Toch blijft het een versimpeling van wat er daadwerkelijk gebeurt. Niet alles wordt uitgeplozen en ik blijf weg van de daadwerkelijke wiskundige formules. Het doel is het proces te begrijpen, niet om een AI-model volledig te ontleden.
Wanneer we spreken over generatieve AI hebben we het over het gehele proces van het genereren van tekst. Onderdeel van generatieve AI is het Large Language Model (LLM), de motor van het generatieve AI-model. Het LLM is ontstaan via een training op basis van een dataset. Deze dataset is een gigantische verzameling van bronnen, veelal van het internet, en bepaalt de relaties tussen woorden die nodig zijn voor de verwerking in de generatieve lus. Een LLM is na training altijd af. Sterker nog, als je een nieuw model zou willen ontwikkelen, moet je dat weer helemaal van de grond af aan opbouwen. Jouw invoer in een LLM model verandert dus niet het huidige model, maar de data kan wel gebruikt worden om een nieuw model te ontwikkelen.

Eerst volgt hier een korte samenvatting van de generatieve lus, daarna gaan we meer in detail kijken naar wat er gebeurt in een AI-model. De generatieve lus begint bij een tekstinvoer. Omdat een LLM alleen met getallen kan rekenen, wordt de tekst eerst omgezet naar nummers (token-ID’s ), die dan weer worden omgezet naar vectoren. Een vector is een lange reeks van getallen, bij een LLM is dat vaak vaak rond de 12.000 per vector. De getallen in deze vectoren worden in het LLM continu aangepast zodat ze beter bij elkaar passen (een geheel vormen). Er wordt voor de voorspelling van een nieuw woord alleen naar het laatste woord van de invoer gekeken. Omdat de vector van dat laatste woord zoveel is aangepast en in nu lijn ligt met de rest van de invoer, kan een LLM het volgende woord raden. Via wiskundige berekeningen komen er kandidaten uit met een nummer, de logits. Deze logits worden nog wat aangepast naar de wens van de ontwikkelaar en dan komen hier percentages uit: hoeveel kans heeft een woord om een logisch gevolg te zijn om het laatst gegeven woord? Dan wordt er een keuze gemaakt. Het woord wordt geplaatst en hierna begint de lus opnieuw totdat de reactie ‘af’ is en het model wacht op nieuwe invoer.
1. Invoer: wat er daadwerkelijk wordt ingevoerd
De tekst die je invoert om aan generatieve AI een vraag te stellen of instructie te geven noem je een prompt. Echter, bij de invoer gebeurt meer dan alleen het geven van een prompt. Wanneer je een online model zoals ChatGPT of Gemini prompt, wordt er een invoerfilter geactiveerd. Deze controleert het prompt op elementen die mogelijk illegaal of schadelijk zijn. Wanneer dit filter iets schadelijks detecteert, stopt de verwerking van de prompt direct en krijg je hier een melding van.

Dan worden de systeeminstructies toegevoegd. De systeeminstructies zijn het meest directe middel voor een modelaanbieder om invloed uit te oefenen op de uitvoer van het model. Ze zijn verborgen voor de gebruiker maar omschrijven de gedragsregels van het model: de hulpmiddelen die het model mag gebruiken, de rol en toon van het model, en of er bronnen gebruikt mogen of moeten worden. Maar de systeeminstructies bevatten ook de zogenaamde ‘guard rails’: elementen om het model veilig te houden. Denk aan het geen hulp bieden bij het maken van explosieven, het niet reproduceren van materiaal waar auteursrecht op rust en het omgaan met gevoelige onderwerpen. Ook kan je denken aan stijl- en opmaakinstructies zoals tabellen en de lengte van de antwoorden. Waar de invoerfilter de prompt controleert en het proces kan stoppen, zijn de systeeminstructies bedoelt voor het gedrag van het model bij te sturen.
Naast je prompt en de systeeminstructies kan er ook andere informatie worden toegevoegd. Dit gebeurt via RAG (Retrieval Augmented Generation). RAG zorgt ervoor dat er nog andere bronnen worden toegevoegd bij het prompt. Deze bronnen kunnen zowel van de gebruiker of van de modelaanbieder zijn toegevoegd, denk aan pdf-bestanden, webresultaten en andere informatiebronnen.
Daarnaast wordt je gespreksgeschiedenis van je chat toegevoegd. De capaciteit voor de gespreksgeschiedenis, samen met de RAG-resultaten, de prompt, en de systeeminstructies worden ook wel het contextvenster, of gewoon ‘context,’ genoemd. Je kunt de context zien als een bakje dat gevuld wordt met verschillende elementen. Het laatste element van de context is altijd het prompt. Voor kleinere modellen (en modellen in het verleden) is het contextvenster beperkt: je komt redelijk snel aan het maximaal aantal tekens dat betrokken kan worden bij je prompt verzoek. Echter, voor grote(re) modellen, zoals de Claude- en ChatGPT-modellen, is dit al zo groot als een dik boek. Wanneer het contextvenster overschreden wordt, omdat bijvoorbeeld de gespreksgeschiedenis te lang wordt, kan niet alle informatie uit de context worden betrokken. Er wordt dan informatie uit de context verwijderd om ruimte te maken voor nieuwe. Dit kunnen, in een ernstig geval, onderdelen van de ‘guard rails’ in de systeeminstructies zijn.
Deze invoerprocedure vindt plaats bij het invoeren van de prompt waarbij er nog geen tokens zijn gegenereerd. Elke volgende lus voegt constant tokens toe aan de invoercontext. Dit blijft zich herhalen totdat de lus bij een zogenaamde ‘End of sequence’ (eos) token komt. Simpel gezegd, net als dat bij de training het model leert welke woorden elkaar vaak opvolgen, leert het ook wanneer een reactie klaar is, of anders gezegd, wanneer de kans op een eos-token zeer groot is. Wat tokens zijn en wat hun rol is in de generatieve lus, lees je later.
De invoer is meer dan alleen je prompt. Alle onderdelen die hierboven genoemd zijn, noemen we dus de context, het gevulde bakje. Alle elementen in dat bakje, de prompt, de systeeminstructies, de informatie via RAG, de chatgeschiedenis en eventueel eerder gecreëerde tokens worden gebruikt om het volgende token, vaak een woord, te bepalen. We spreken daarom over een context en niet over een vraag of instructie. Een generatief AI-model voorspelt namelijk het volgende woord in een reeks van woorden. Het geeft geen antwoord op je vraag, het genereert woorden die logisch volgen op de inhoud van de context.
Wanneer de invoer gereed is, wordt het bakje doorgeschoven naar de volgende fase: het tokeniseren.
2. Tokeniseren: het toekennen van een ID
We spreken voor een AI-model vaak niet over woorden maar over tokens. Een token kan namelijk ook iets anders zijn dan een woord, zoals een leesteken, een spatie of eos-token. Sommige woorden kunnen ook uit meerdere tokens bestaan.
Omdat AI-modellen alleen kunnen rekenen met getallen, moeten tokens van de context worden omgezet naar token-ID‘s. Een token-ID is een uniek nummer dat uit een standaard lijst, samengesteld door de ontwikkelaars, wordt gehaald. Deze lijst is ook gebruikt bij het training van het AI-model waardoor het model de getallen herkent.
Tijdens het tokeniseren verandert er niets aan de volgorde van de tekst in de context. Er vindt enkel een omzetting plaats van woorden en leestekens naar getallen.
De zin:
De kat zat op de mat.
wordt dan
[314, 892, 57, 1045, 521, 78, 6] (zes woorden en een punt)
Sommige langere woorden worden opgesplitst in meerdere tokens (zoals ‘onmogelijk’: [‘on’ + ‘mogelijk’= 258, 2065] en bij veel modellen hebben woorden met een hoofdletter een ander token dan woorden zonder (‘kat’ heeft dus een ander nummer dan ‘Kat’ of ‘KAT’). De spatie heeft in sommige modellen een apart token-ID, maar de spatie kan ook onderdeel zijn van een woord. Het token-ID voor “kat” is dan anders dan voor ” kat” (met voorloopspatie).
De tokenizer zit nog buiten het LLM en dient alleen maar om de context voor te bereiden op de verwerking in het model. Wanneer de context helemaal uit cijfers bestaat, wordt bakje doorgeschoven naar het LLM.
3. Embedding: het koppelen aan een reeks getallen
Wanneer de token-ID’s in het model komen wordt eerst voor ieder token de eigen vector opgezocht in een embeddingmatrix. Dit is, net als bij de token-ID’s, een grote, standaard tabel maar nu met token-ID’s gekoppeld aan hun embeddingsvector. Het vervangen van het token-ID voor een vector noemen we embedding. Een embeddingvector is een standaard reeks van getallen dat gecreëerd is tijdens de training van het model. Deze beginvector is altijd hetzelfde. Dus ‘kat’ (of beter gezegd token-ID ‘892’) heeft altijd dezelfde reeks in de embedding. Een reeks kan wel tot wel duizenden getallen bestaan maar ziet er ongeveer zo uit: [0.13, -0.82, 1.47, 0.05, -0.31, 0.94, -1.26, 0.58, …]. Nu de token-ID’s omgezet zijn naar hun eigen vector of getallenreeks, kunnen we beginnen met rekenen.
4. Transformer: invoer zo duidelijk mogelijk in context plaatsen
Nu gaat de transformer aan de slag, het hart een AI-model. We zullen het woord vector nu vaak gaan tegenkomen. Zoals we ondertussen weten, betekent een vector niet veel meer dan een hele lange getallenreeks, een soort kralenketting, waarbij we zullen zien dat de kralen continu worden aangepast. Ook ‘context’ wordt belangrijk. Wanneer we hier spreken over ‘context’, bedoelen we altijd het gevulde bakje uit de invoer: de prompt, de bronnen, de systeeminstellingen, de chatgeschiedenis en eventuele tokens gecreëerd in een vorige lus.
Het doel van de transformer is om de invoercontext zo goed mogelijk te representeren. Je zou ook kunnen zeggen dat het model probeert de invoer zo goed mogelijk te ‘begrijpen,’ al is dat natuurlijk iets anders dan menselijk begrip. Alle individuele tokens worden met elkaar in verband gebracht en vectoren uit de context worden constant aangepast om tot die representatie te komen.
Het creëren van de juiste representatie doet de transformer door de vectoren elke keer een klein beetje aan te passen. In het trainingsproces heeft het model geleerd waar alle vectoren zich in de vector space bevinden. De vector space is een fictief multidimensionaal coördinatenstelsel waar de relaties tussen vectoren worden weergegeven. Je kunt een vector dan zien als een coördinaat op een hele grote kaart. ‘Kat’ ligt bijvoorbeeld dichter bij ‘hond’ dan bij ‘democratie’. Anders gezegd, de vectoren liggen soms dichter en soms verder van elkaar, afhankelijk hoe vaak ze in vergelijkbare teksten zijn gevonden in de trainingsdata.
In de vector space hebben we het dus eigenlijk niet meer over woorden zoals ‘kat’ of ‘zat’, maar over getallenreeksen die in relatie tot elkaar staan. Het AI-model ‘begrijpt’ geen woorden maar alleen associaties tussen vectoren. De originele vector (getallenreeks) staat gelijk aan het woord ‘kat’ via het token-ID. We kunnen echter niet stellen dat het woord ‘kat’ constant wordt aangepast maar eerder het idee van ‘kat’ middels de getallenreeks. Zo heb je meerdere betekenissen voor het woord ‘bank’: een bank waarop je zit en een bank waar je geld wordt beheerd. Met behulp van de context bepaalt het model welk idee (getallenreeks) van ‘bank’ we bedoelen, met andere woorden er wordt gekeken wat het juiste punt in de vector space is in relatie tot de andere vectoren van de invoer.
Om een beter ‘begrip’ van de context te krijgen, laat de transformer de invoercontext (alle vectoren vanuit de embedding) door verschillende lagen gaan. De verschillende lagen leggen nadruk op verschillende aspecten van taal. Sommige modellen kunnen wel tachtig lagen hebben. In iedere laag worden de getallen in de vectoren een beetje aangepast zodat ze beter passen in de context (met andere woorden, tijdens elke laag worden alle vectoren zo verschoven in de vector space dat ze steeds meer een samenhangende context vormen).
Om de vectoren aan te passen heeft iedere laag in de transformer heeft vier mechanismen: self-attention, feed-forward-network, residual connection en layer normalization (in nieuwere modellen komt deze als eerste). Het wordt iets te technisch om hier heel diep op in te gaan, maar in principe komt het op het volgende neer. Als de context een lied is dat een koor gaat zingen, dan zorgt self-attention ervoor dat ieder koorlid goed naar de andere koorleden luistert en de eigen zang daarop aanpast om een harmonie te vinden. Bij de volgende stap, het Feed-Forward Netwerk, luistert het koorlid naar zichzelf en optimaliseert de persoonlijke zang. In stap drie gaat het koorlid de aanpassingen verwerken in de eigen bijdrage. Daar waar self-attention als ‘luisteren’ kan worden gezien en het Feed-Forward netwerk als ‘reflecteren’, kan de residual connection zien als het ‘verwerken.’ Als laatste zorgt de dirigent dat extremen, zoals iemand die een octaaf te hoog zingt, in lijn worden gebracht.

Het eerste mechanisme is dus self-attention, het gedeelte waar de verschillende vectoren vooral bezig zijn met het ‘luisteren’ naar elkaar. Self-attention kijkt naar relaties tussen de verschillende vectoren en bepaalt welke het meest samenhangen. Dit gebeurt voor alle vectoren van de context om tot de juiste ‘harmonie’ te komen. Voor ieder token wordt er tijdens de self-attention een werkvector gemaakt, een tijdelijk vectorvariant van de originele vector. Hier gaat het Feed-Forward netwerk verder mee aan de slag.
Het Feed-Forward netwerk (FFN) verwerkt en verankert de aanpassingen per individuele vector in een nieuwe, tweede werkvector. Het FFN baseert dit op waar de ‘kennis’ van deze vector meestal rondhangt in de vector space. Daar waar self-attention naar de relatie tussen vectoren kijkt, kijkt het FFN naar waar deze verandering naartoe wil en past de vector verder aan met eigen gewichten, alsof een koorlid naar zijn eigen stem luistert en deze optimaliseert.
Daarna zorgt de residual connection dat de tweede verwerkte werkvector wordt opgeteld bij de originele vector. Een kleine wijziging telt bijvoorbeeld 0,001 op bij een getal in de getallenreeks, een grote wijziging bijvoorbeeld 1.2 of 2.0. Op deze manier beweegt de originele vector getalsmatig naar de algehele representatie. Daarbij hebben we het dus niet over woorden als ‘kat’ of ‘zat’ maar het idee achter deze woorden die past binnen de gehele context en gerepresenteerd in een getallenreeks.
Als laatste strijkt de layer normalization (LayerNorm) de extremen uit een getallenreeks glad zodat deze stabiel genoeg is voor de volgende laag. Tegenwoordig zie je vaker dat deze stap juist de eerste stap is in de volgende laag.
Wanneer alle lagen zijn doorlopen wordt het om naar de uitvoeropties te kijken.
5. De outputlaag: logits
Nu we de invoercontext hebben verfijnd en het model de context ‘begrijpt’, wordt het eindelijk tijd om naar de kanshebbers voor het opvolgende woord te gaan kijken. Er wordt wel eens gezegd dat generatieve AI-modellen niets anders doen dan het volgende woord voorspellen aan de hand van het vorige woord. En technisch klopt dit.
In de outputlaag wordt alleen naar de laatste vector gekeken om het vervolg te bepalen. Dus als je “De kat zat op de” had, wordt er alleen naar de vector van het tweede woord ‘de’ gekeken om het volgende woord te bepalen. Dit kan omdat de vector van het woord ‘de’ zoveel is aangepast aan de hand van de andere tokens (de representatie van de gezamenlijke context) dat het vervolg makkelijker te raden is. De outputlaag geeft, na een aantal berekeningen met een standaard wiskundige formule, een score voor mogelijke vervolgwoorden. Deze scores noemen we logits.
Voor elk token-ID in de gehele woordenschat van het model (dat kan een woordenschat van 100,000 tokens zijn) wordt een logit bepaald. Dit is een tijdelijke score om in de volgende stap percentages mee te berekenen. Al het werk van de transformer was er dus voor om via de outputlaag te kijken welke token-ID’s een hoge logit krijgen en dus goede kanshebbers zijn voor de uitkomst van de lus. Hoe hoger de score, hoe meer dat token volgens het model past als volgende token. Bij het voorbeeld ‘De kat zat op de’ kunnen mogelijke tokens met logit zijn:

Het toewijzen van logits is het laatste wat de LLM doet. Hierna bevinden we ons weer buiten het model.
6. Temperatuur: hoe gek wil je het hebben?
De temperatuur heeft invloed op de kans dat een bepaalde token wordt gekozen. Elke logit wordt gedeeld door de temperatuur waardoor de afstand tussen logits verandert. Een lage temperatuur (lage deelfactor) zorgt voor voorspelbare, veilige antwoorden met minder variatie. Een hoge temperatuur (hoge deelfactor) maakt de verschillen kleiner zodat er meer variatie ontstaat, meer creatieve woordkeuze, maar ook grotere kans op minder passende tokens.

Een aanbieder van een model kan de temperatuur instellen en aanpassen. Hierdoor wordt een model voorspelbaarder of creatiever. Deze keuze ligt bij de aanbieder.
7. Softmax: de kansberekening
Softmax zet de logits om in een kansverdeling middels een vaste wiskundige formule. Alle tokens in de woordenschat van een model krijgen waarden die bij elkaar opgeteld 1.0 vormen. De reden dat de logit met de hoogste waarde niet 1,0 krijgt (100%) is om een minder voorspelbare uitkomst te krijgen. Door andere mogelijkheden die weliswaar (iets) minder goed zijn te betrekken als kanshebbers krijg je een meer gevarieerde tekst.

8. Top-k en top-p: de hoogste kanshebbers
Top-k en top-p zijn twee verschillende manieren om de kanshebbers voor het volgende token te beperken. Omdat Softmax aan alle tokens in het model een percentage koppelt, zijn er heel veel tokens met een extreem lage kans. Top-k zorgt ervoor dat een vast aantal mogelijkheden overblijven, bijvoorbeeld de 50 hoogste kanshebbers. Top-p, die tegenwoordig vaker voorkomt, kijkt naar welke hoogste tokens er nodig zijn om 90-95% van de totale kans te vertegenwoordigen. Er wordt dan niet meer naar de rest gekeken. Hierna worden de overgebleven kansen genormaliseerd zodat de optelsom weer 1,0 wordt. Top-k en top-p worden bij een aantal modellen vóór de temperatuur gedaan.

9. Sampling
Nu gaat er gekozen worden welk token (woord) volgt op het laatste gegeven woord uit de invoer. Hierbij wordt niet altijd het token met de hoogste kans gekozen, anders wordt het model wel heel voorspelbaar. Als we de volgende kansen hebben: ‘mat’ (78%), ‘stoel’ (13%) en vloer (9%), dan wordt ‘mat’ ongeveer 78 keer van de 100 gekozen, ‘stoel’ ongeveer 13 keer en ‘vloer’ ongeveer 9 keer. De keuze is dus niet willekeurig, maar is op basis van de percentages gekoppeld aan de tokens.

Wanneer de keuze is gemaakt, wordt deze toegevoegd aan de nieuwe invoer voor een nieuwe lus, tenzij een eos-token is bereikt en de lus wordt beëindigd.
Agentic AI: de aannemer van een AI-model
Steeds meer modellen maken gebruik van agents. Een agent is traditionele software die processen kan aansturen. Een agent kan je zien als een soort aannemer die na een prompt wordt geactiveerd en zelfstandig aan de slag gaat. De agent bepaalt of de RAG geactiveerd wordt, of informatie gezocht moet worden, een rekenmachine moet worden gebruikt, of een stappenplan moet worden gemaakt. De agent bepaalt dan ook wat er naar de LLM wordt gestuurd. Dit kan anders zijn dan de originele prompt van de gebruiker. Een agent kan ook aan het einde van een lus nog verdere acties ondernemen, zelfs op basis van de uitkomst van een LLM.
Conclusie
Het begrijpen van een AI-model helpt bij het begrijpen van de risico’s en de inzet van AI in onderwijsprocessen. Ook helpt het vernieuwingen in de AI-wereld beter te plaatsen omdat je weet wat er zich onder de motorkap afspeelt. Soms wekt AI de illusie dat het bewustzijn heeft, maar uiteindelijk is een LLM een automatiseringsproces dat met behulp van een multidimensionale kaart op basis van data uit het verleden associaties maakt om te voorspellen wat het volgende woord mogelijk zou zijn. Het is een zeer krachtig stuk technologie, maar net als elk stuk technologie heeft het risico’s en beperkingen. Door bewust te zijn van de architectuur en de beperkingen van die architectuur, ben je beter in staat AI degelijk en duurzaam in te zetten daar waar het meerwaarde heeft. En te besluiten waar generatieve AI geen plek heeft.





