<Terug naar overzicht Artikelen:
🔧 Deel 2: Praktische stap voor stap guide
Achtergrond:
🤖 LLM’s lokaal draaien - 2 simpele methodes
🛣️ LLM’s lokaal draaien - De weg naar ondevice LLM’s
💻 LLM’s lokaal draaien - Je systeem in relatie tot de modelspecificaties
⚖️ Wat is lora?
Inhoudsopgave
- Inhoudsopgave
- Inleiding
- In welke situaties?
- Overzicht
- Hoe finetuning werkt in het kort
- Achtergrond
- Stap 1: Tokenization
- Sidenote 1: Byte pair encoding explainer
- Stap 2: Embeddings
- Voorbeeld
- Sidenote 2: Word2Vec Skip-gram
- Stap 3: Pre-training
- Sidenote 3: MLM en CLM
- Masked Language Modeling (MLM)
- Causal Language Modeling (CLM)
- Finetuning
- 1. Data Preparatiefase
- Trainingsfase
- Evaluatiefase
- Datapreparatie voor finetuning
- De Trainingsfase van finetuning
- De loss berekening
- Cross-entropy loss
- Cross-entropy loss en Perplexity
- Backpropagation
- Belangrijke instellingen en termen tijdens je training
- De evaluatiefase van finetuning
- Trainen, testen en valideren
- Conclusie
Inleiding
LLM’s zijn fantastisch. Ze helpen je met je dagelijkse werkzaamheden, het samenstelen van weekendjes weg, het antwoorden van moeilijke vragen, en we vinden op dit moment nog elke week nieuwe usecases voor het gebruik van deze modellen. Het is super leuk om je in te verdiepen omdat bijna iedereen nog in deze ontdekkingsfase zit. Nog geen 2 jaar geleden zette OpenAI met de release van ChatGPT een verandering in gang in onze manier van werken die wat mij betreft de meest significante is sinds de komst van het internet.
In het kielsoog van OpenAI volgden al snel vele andere bedrijven, zoals Google, Anthropic, Cohere, Mistral, Meta etcetera. om deze bedrijven heen ontstond een heel ecosysteem van cloud diensten, tools, frameworks en een hele boel wrapper companies die gebouwd waren met en op de LLM modellen, zoals Perplexity, Cursor, etc.
Wat gaaf is om te zien, is dat een groot deel van deze ontwikkelingen plaatsvindt in een opensource omgeving. Iedereen kan bijdragen en leren , en de opensource wereld weet tot nu toe redelijk goed bij te blijven ten opzichte van de grote AI bedrijven. Iedereen voelt dat de waarde die je uit generatieve AI kunt halen potentieel gigantisch is, en dus zijn we samen opzoek naar de beste manier waarop we LLM’s kunnen gebruiken.
Ik verdiep me inmiddels al een tijdje in methodes waarop je de output van een LLM kunt optimaliseren. Bij mijn eerste blogs ging dat metname via RAG (Retrieved Augmented Generation), maar ook technieken als toolcalling zijn al wel langs gekomen. Die beide technieken hebben als overeenkomst dat ze zijn ontworpen om op een slimme manier informatie toe te voegen aan een bestaand AI systeem. Bij RAG door een deel van het prompt te voorzien van relevante stukjes context of informatie die op basis van coseine similarity en eventuele reranking mechanismes hoogstwaarschijnlijk bij de vraag of user query past. Bij tool calling door de LLM te voorzien van instructies en gereedschappen waarmee de LLM bepaalde informatie kan verkrijgen.
In sommige situaties kan het echter nuttiger zijn om niet een bestaand model te gebruiken en te optimaliseren op de context, maar om daadwerkelijk aan de gewichten en parameters van het model zelf te sleutelen. In deze guide gaan we bestuderen hoe dat werkt.
In welke situaties?
Er zijn dus verschillende methodes om LLMs zo te configureren dat je precies de output krijgt die jij nodig hebt. Welke methode je kiest hangt heel erg af van je usecase.
Overzicht
Methode | Voordelen | Nadelen | Voorbeeld use case |
Prompt engineering | - Eenvoudig te implementeren
- Geen aanpassingen aan het model nodig
- Snel resultaat | - Beperkte impact en controle
- Vereist ervaring en experimentatie | Door een opdracht als “leg het uit alsof ik een kind van 5 ben” bij het invoeren van een ChatGPT opdracht, krijg je een simpelere uitleg. |
Prompt engineering met parameter-aanpassing | - Relatief eenvoudig
- Geen modelwijzigingen nodig
- Snel resultaat | - Vereist begrip van parameters
-Beperkte impact en controle
| Wanneer je een LLM vragen stelt als “Geef me 10 ideeen voor het schrijven van een blog” kun je de temperature verhogen om willekeurigere output te krijgen, deze instelling kan bij opdrachten waarvoor creativiteit nodig is helpen om originelere output te krijgen. |
RAG (Retrieval-Augmented Generation) | - Verbetert contextbegrip aanzienlijk
- Kan up-to-date informatie toevoegen | - Vereist goede brondata
- Er zijn veel strategieeën voor chunking, reranking, etc. dus het kan wat complex zijn. | Wanneer je de benodigde informatie om een vraag te beantwoorden wel hebt, maar je deze hebt opgeslagen in grote pdf bestanden, of andere ongestructureerde databronnen. |
Tool calling | - Breidt mogelijkheden van het model uit
- Kan real-time data verwerken | - Vereist ontwikkeling en onderhoud van tools
- Kan soms minder stabiel zijn | Een AI-assistent die weersinformatie moet opvragen of berekeningen moet uitvoeren |
Multi-agent systemen | - Zeer krachtig voor complexe taken
- Kan verschillende expertises combineren
-Kan real-time data verwerken | - Kan complex zijn
- Kan zorgen voor een hoge latency tussen vraag en antwoord | Een systeem dat een groter proces met meerdere stappen, waarbij in verschillende stappen een LLM output moet genereren , moet faciliteren |
Fine-tuning | - Kan model specifiek maken voor een taak
- Significant verbeterde prestaties
- Modellen met een hele specifieke taak hebben minder parameters nodig en kunnen eenvoudiger lokaal draaien | - Vereist training datasets
- Het trainen zelf kost veel rekenkracht
- Risico op overfitting
-Lastiger om up to date te houden dan de methodes hierboven | Een gespecialiseerde AI-assistent die antwoord in een hele specifieke stijl geeft |
Foundational model vanaf scratch bouwen | - Volledige controle over het model
- Kan geoptimaliseerd worden voor specifieke use-cases | - Extreem duur en tijdrovend
- Vereist enorme datasets en rekenkracht | Een taalmodel ontwikkelen voor een zeer specifieke industrie of een zeldzame taal |
Hoe finetuning werkt in het kort
Kortgezegd is finetuning het proces waarbij je een bestaand taalmodel (LLM) verder traint op een specifieke dataset of taak. Het doel is om het model te specialiseren en beter te laten presteren op een bepaald onderwerp of in een specifieke context. Bij finetuning behoud je de algemene kennis en capaciteiten van het oorspronkelijke model, maar pas je de gewichten en parameters aan om het model te optimaliseren voor jouw specifieke use-case.
Dit proces omvat meestal de volgende stappen:
- Voorbereiden van een gespecialiseerde dataset voor de gewenste taak of domein.
- Het model verder trainen op deze nieuwe dataset, waarbij de oorspronkelijke gewichten voorzichtig worden aangepast.
- Evalueren van het gefinetuned model op relevante metrics en eventueel verder aanpassen.
Door finetuning kan een algemeen taalmodel worden omgevormd tot een expert op een specifiek gebied, zoals medische diagnoses, juridische teksten, of klantenservice in een bepaalde industrie. Het resultaat is een model dat nauwkeuriger, relevanter en vaak efficiënter presteert op de beoogde taak dan het originele, algemene model.
Achtergrond
Om te begrijpen hoe dat precies gaat, is het handig om eerst wat uit te leggen over de werking van LLM’s en het bouwen er van in algemene zin. Ik ga 3 onderwerpen toelichten: Tokenization, Embedding en Pre Training. Daarna gaan we verder met het finetunen zelf.
Voor een super fijne grondige uitleg over de werking van LLM’s kan ik deze blog aanraden.
Ook het Twitter account van Professor Tom Yeh is een aanrader.
Stap 1: Tokenization
Om natuurlijke taal te converteren naar een format waarmee computers kunnen werken, moeten we van deze taal een cijfermatige representatie maken. Een cijfermatige representatie van een klein stukje geschreven tekst noemen we een token
. Er zijn verschillende strategieeën voor het opdelen van tekst in tokens, bij LLM’s zijn tokens meestal een groepje letters; soms van een woord, soms van een deel van een woord. Soms misschien ook een enkel karakter.
Sidenote 1: Byte pair encoding explainer
Hoeveel verschillende tokens een model heeft verschilt per model. De meest gebruikte techniek om te tokenizen heet byte pair encoding
, maar er zijn meer methodes. Bij byte pair encoding wordt vooraf bepaald hoeveel “plek” er in het “woordenboek” van het model gaat zijn voor verschillende tokens. Het aantal dat hier gekozen wordt moet in verhouding staan tot de grootte van de trainingsdataset, de taak waarvoor je het model traint, de taal van de brondata, de rekenkracht die je beschikbaar hebt en nog vele factoren meer.
Nadat de grootte van het woordenboek bepaald is wordt alle brondata allereerst opgeknipt in een minimaal aantal tokens, dit zijn meestal alle letters en cijfers van het alfabet en alle unieke speciale karakters uit de trainingsdataset. Daarnaast zijn er nog wat speciale tokens zoals [start]
, [end]
en een token om ombekende woorden te representeren ([unk]
).
Deze basis set aan tokens wordt toegevoegd aan het woordenboek en vanaf dit basis lexicon bouwt het algoritme geleidelijk steeds grotere chunks op, door steeds de meestvoorkomende paren van betaande tokens samen te voegen, tot de totale grootte van het woordenboek bereikt is.
Door deze methodiek ontstaat er een token vocabulaire waarbij veelvoorkomende woorden vaak als 1 token terug zullen komen en uniekere woordcombinaties vaak zijn opgesplitst in kleinere tokens, wat zorgt voor een efficiente balans tussen een beheersbare woordenboekgrootte en het vermogen om een brede variëteit aan woorden en subwoorden te representeren.
Stap 2: Embeddings
Over embeddings heb ik al eens geschreven in mijn blogs over RAG maar voor de volledigheid zal ik nu een wat grondigere omschrijving geven van embeddings in de context van NLP in algemene zin in plaats van RAG specifieke uitleg.
In stap 1 hebben we de trainingsdataset voor onze LLM getokenized. Maar met enkel een token, zonder verdere relationele informatie kunnen we natuurlijk nog vrij weinig. Eigenlijk hebben we daarmee slechts een index van veel voorkomende stukjes tekst. Om deze stukjes tekst betekenis te geven komen vector embeddings om de hoek kijken.
Heel kort gezegd zijn vector embeddings de multidimensionele ruimtelijke representatie van een bepaalde token, waarbij elke token wordt geplot bij tokens die er mee te maken hebben.
Dat klinkt ongetwijfeld nog wat vaag, dus laten we dat concretiseren met een extreem versimpeld voorbeeld..
Voorbeeld
Laten we een vereenvoudigd voorbeeld nemen met slechts drie dimensies om het concept van vector embeddings te illustreren. We gebruiken de woorden "koning", "koningin", "man" en "vrouw" als onze tokens.
In dit voorbeeld stellen we ons voor dat de drie dimensies het volgende vertegenwoordigen:
- Koninklijkheid (0 = niet koninklijk, 1 = zeer koninklijk)
- Mannelijkheid (0 = vrouwelijk, 1 = mannelijk)
- Leeftijd (0 = jong, 1 = oud)
Onze vereenvoudigde embeddings zouden er dan zo uit kunnen zien:
- koning: [0.9, 1.0, 0.7]
- koningin: [0.9, 0.0, 0.7]
- man: [0.1, 1.0, 0.5]
- vrouw: [0.1, 0.0, 0.5]
Als we deze embeddings zouden visualiseren in een 3D-ruimte, zouden we zien dat:
- "koning" en "koningin" dicht bij elkaar staan op de "koninklijkheid"-as.
- "koning" en "man" dicht bij elkaar staan op de "mannelijkheid"-as.
- "koningin" en "vrouw" dicht bij elkaar staan op zowel de "mannelijkheid"- als de "koninklijkheid"-as.
Deze ruimtelijke relaties stellen het model in staat om verbanden te leggen:
- Het verschil tussen "koning" en "koningin" is vrijwel identiek aan het verschil tussen "man" en "vrouw".
- "koning" is voor "man" wat "koningin" is voor "vrouw".
In de praktijk gebruiken taalmodellen veel meer dimensies (vaak honderden of duizenden) om complexere relaties tussen woorden vast te leggen. Waarbij ze elk woord ook nog heel precies plotten op elke dimensies (ver achter de komma). Dit stelt ze in staat om subtiele verschillen en overeenkomsten tussen woorden te begrijpen en te gebruiken bij het genereren of analyseren van tekst.
Bij moderne, grote, LLM systemen worden vele verschillende embeddingstechnieken gebruikt om de optimale vector representaties van tokens te genereren, en het gaat te diep om deze technieken 1 voor 1 door te nemen, bovendien vermoed ik dat de wiskunde van veel van deze technieken mij de pet te boven gaat, maar om toch een wat beter begrip te krijgen van deze embeddings stap heb ik 1 basis embeddings algoritme uitgewerkt:
Sidenote 2: Word2Vec Skip-gram
Bij Word2Vec Skip-Gram wordt geprobeerd om de context van een woord te voorspellen. In het geval van deze methodiek wordt met de context de letterlijk omringende woorden bedoeld. Je schuift als het ware een “venster” over je trainingsdata, waarbij je steeds een aantal woorden highlight. het middelste woord is het input woord, en de woorden ervoor en erna zijn de contextwoorden.
Stel, we hebben een kleine dataset:
"De kat zit op de mat"
Ons vocabulaire is: ["de", "kat", "zit", "op", "mat"]
We kiezen voor dit voorbeeld een embedding grootte van 3 dimensies.
In het begin stellen we voor elk van deze woorden willekeurige dimensies in, bijvoorbeeld:
"de" : [0.2, -0.5, 0.1]
"kat" : [-0.3, 0.2, 0.4]
"zit" : [0.1, 0.3, -0.2]
"op" : [-0.4, 0.1, 0.5]
"mat" : [0.3, -0.4, 0.2]
Laten we focussen op één trainingsstap voor het woord "kat" met een context venster van 1.
Input: "kat" Context (doel): "de", "zit"
- We nemen de embedding voor "kat": [-0.3, 0.2, 0.4]
- Deze gaat door een neuraal netwerk met één verborgen laag. Laten we zeggen dat de output layer 5 neuronen heeft (één voor elk woord in ons vocabulaire).
- Het netwerk geeft een voorspelling voor elk contextwoord:
"de" : 0.2
"kat" : 0.1
"zit" : 0.3
"op" : 0.15
"mat" : 0.25
- We vergelijken dit met de werkelijke context: "de" zou 1 moeten zijn, "zit" zou 1 moeten zijn, de rest 0.
- We berekenen de fout en gebruiken backpropagation om de gewichten in het netwerk aan te passen, inclusief onze oorspronkelijke embedding voor "kat".
- Na deze aanpassing zou de embedding voor "kat" iets kunnen veranderen naar bijvoorbeeld: [-0.31, 0.22, 0.39]
We herhalen dit proces voor elk woord in onze zin, meerdere keren (epochs) over onze gehele dataset.
- Woorden die vaak in vergelijkbare contexten voorkomen, zullen vergelijkbare embeddings hebben.
- In ons voorbeeld zouden "de" en "mat" mogelijk dichterbij elkaar liggen omdat ze vaak naast elkaar voorkomen.
- De exacte betekenis van elke dimensie is niet vooraf bepaald, maar ontstaat tijdens het trainingsproces.
❗NOTE: zoals ik hierboven ook al aangaf, is het voorbeeld van Word2Vec Skipgram handig om een idee te krijgen bij het soort technieken dat we bij Embeddings gebruiken, maar is het, zeker voor moderne LLM’s, zeker niet de meest gebruikte techniek. Voor modernere LLM’s is het heel gebruikelijk dat 1 woord meerdere embeddings kan hebben, afhankelijk van de context van de gehele zin. Net zoals bij ons in de echte taal. Dit worden contextuele embedding methodes genoemd, waar Word2Vec een statische embeddingmethode is.
Stap 3: Pre-training
Nadat we onze dataset hebben getokenized en vector embeddings hebben gecreeërd is pre-training meestal de volgende stap. In sommige aspecten lijken bepaalde technieken in stap 3 best wel op die in stap 2. Ook bij pretraining proberen we primair te voorspellen op basis van context. De schaal, benodigde rekenkracht en complexiteit zijn in stap 3 echter veel groter.
In de pre-training fase wordt een groot taalmodel getraind op een zeer omvangrijke dataset om de onderliggende structuren van de taal te leren begrijpen. De pre-training wordt vaak uitgevoerd op een ongecontroleerde dataset, bestaande uit tekst afkomstig van allerlei bronnen, zoals boeken, artikelen, websites, en meer. Het doel van deze fase is om het model algemene kennis over taal te laten opbouwen door middel van een zelf-supervised leermethode. Twee veelvoorkomende technieken bij pre-training zijn Masked Language Modeling (MLM) en Causal Language Modeling (CLM).
Sidenote 3: MLM en CLM
Masked Language Modeling (MLM)
Bij MLM worden willekeurige woorden in een zin gemaskeerd en probeert het model de ontbrekende woorden te voorspellen op basis van de omliggende woorden. Een bekend voorbeeld van deze techniek wordt gebruikt in BERT (Bidirectional Encoder Representations from Transformers).
Voorbeeld:
Zin: De kat zit op de mat.
Gemaskerde versie: De kat [MASK] op de mat.
Het model moet het gemaskeerde woord (zit) voorspellen. Door deze methode leert het model een diepgaand begrip van context, waarbij het in staat is om verbanden te leggen tussen de woorden in een zin en hun onderliggende betekenissen.
Causal Language Modeling (CLM)
Bij CLM voorspelt het model elk volgend woord in een zin, waarbij alleen de voorgaande woorden als context worden gebruikt. GPT-modellen, zoals GPT-3, maken gebruik van deze techniek.
Voorbeeld:
Zin: De kat zit op de mat.
Het model moet het volgende woord na "De kat zit" voorspellen, en vervolgens "op", enzovoorts, tot de hele zin voltooid is.
Beide methoden zorgen ervoor dat het model leert hoe taalstructuren werken, zodat het deze kennis kan toepassen tijdens het genereren van nieuwe teksten. Pre-training is meestal erg tijdrovend en vereist veel rekenkracht en data, omdat het model van nul af aan leert over de taal.
Finetuning
bovenstaande achtergrond beschrijving is wat langer geworden dan ik vooraf had geanticipeerd, maar ik denk dat het goed is om te begrijpen welke stappen er grofweg plaatsvinden bij het bouwen van een LLM om het finetunings-proces optimaal te kunnen doorgronden.
Het pre-trainen van modellen is voor een hobbyist eigenlijk niet weg gelegd. de dataresources, energie resources en GPU’s en TPU’s die nodig zijn, zijn echt gigantisch.
Gelukkig zijn er inmiddels vrij veel open source taalmodellen beschikbaar, die we als basis kunnen gebruiken om te gaan finetunen, waarmee we deze stap overslaan.
In dit laatste deel van de eerste blog zoomen we wat dieper in op wat er precies gebeurt met finetuning.
Finetuning is een proces waarbij een vooraf getraind model wordt aangepast voor een specifieke taak of domein. Tijdens de finetuning wordt het model blootgesteld aan een nieuwe, gespecialiseerde dataset. Het model maakt voorspellingen op basis van deze data en vergelijkt deze met de werkelijke waarden. Op basis van de fouten die het model maakt, worden de gewichten aangepast. Dit proces wordt herhaald totdat het model de gewenste prestaties bereikt. Het proces kan worden opgedeeld in drie hoofdfasen:
1. Data Preparatiefase
- Verzamelen van een gespecialiseerde dataset voor de doeltaak.
- Opschonen en voorbewerken van de data.
- Labelen van de data (indien nodig voor de specifieke taak).
- Opsplitsen van de data in trainings-, validatie- en testsets.
Trainingsfase
- Laden van het vooraf getrainde model.
- Aanpassen van de laatste laag of lagen van het model voor de nieuwe taak.
- Instellen van hyperparameters zoals leersnelheid en batchgrootte.
- Trainen van het model op de nieuwe dataset, waarbij de gewichten geleidelijk worden aangepast.
- Monitoren van de prestaties op de validatieset om overfitting te voorkomen.
Evaluatiefase
- Testen van het gefinetuned model op de testset.
- Evalueren van de prestaties met relevante metrics (bijv. accuraatheid, F1-score).
- Vergelijken van de resultaten met het originele model en/of andere benchmarks.
- Indien nodig, herhalen van de trainingsfase met aangepaste hyperparameters.
Datapreparatie voor finetuning
Voor ik aan deze blog begon, was mijn inschatting dat van de drie stappen die bij finetuning komen kijken stap 1 het makkelijkst was en stap 2 en 3 het moeilijkst. Dit kwam vooral omdat veel van de termen en begrippen uit stap 2 en 3 nieuw voor mij zijn waardoor het bij het inlezen wat overweldigend overkwam. Achteraf gezien kan ik zeggen dat het eerder andersom is. Zoals wel vaker bij data gerelateerde werkzaamheden bleek het prepareren van de data de meest intensieve stap van de 3 stappen.
Hoe je de data van je trainingsset dient te prepareren verschilt een beetje per model en per taak die je het model geeft. In deel 2 van deze blog serie, waarin we daadwerkelijk een model gaan finetunen laat ik een handige methode zien hoe je dit kunt achterhalen.
In grote lijnen kun je zeggen dat we bij de pretraining een gigantische bak data in het model goten zonder specifieke instructies of verwachtingen, bij het finetunen werken we vaak met een veel preciezer gecureerde set van data, die we niet als 1 grote string als input gebruiken maar die we op de een of andere manier verdelen in een voorbeeld input en een verwachte output. Vaak houden we een klein deel van de dataset apart, van dit deel maken we validatiedata en testdata, daarover later meer.
De Trainingsfase van finetuning
Wanneer we onze dataset hebben kunnen we gaan trainen. Ondanks het gegeven dat de benodigde rekenkracht voor finetuning lager ligt dan voor pre-training, kan dit nog steeds een zware operatie zijn. Met name bij modellen met grotere hoeveelheden parameters.
Hierbij maakt het ook behoorlijk veel uit met welke hardware je werkt. In mijn eerste paar pogingen volgde ik een turtorial die was gericht op gebruikers met NVidia chips terwijl ik een Apple Silicon machine had, waardoor het RAM gebruik steeds groter was dan wat ik beschikbaar had. Meer over deze wat praktischere kant in het volgende deel. Daarin ook een toelichting op veel gebruikte library’s zoals Pytorch en Tensorflow.
Tijdens de trainingsfase gaan we dus de gewichten van het model veranderen, zodanig dat het model beter reageert op specifieke taken waar we voor gaan finetunen. Hieronder een versimpelde schematische weergave van de training die plaatsvindt (geen zorgen: onduidelijke termen ligt ik zo toe):
De loss berekening
We vergelijken de door het model geproduceerde output voor elke input dus met onze verwachte output. Het verschil tussen die twee noemen we de loss
. De manier waarop Loss berekend wordt hangt af van de taak waarvoor je je model traint. De loss berekening is een afstandsberekening, enigszins vergelijkbaar met de in de embeddingsblog besproken cosine distance. Bekende methodes zijn: Cross-empty loss, Mean squarred error, mean absolute error en cross-entropy loss. Bij geen van die termen had ik voor deze blog reeks enig idee wat ze betekenden, maar inmiddels kan ik me bij de laatste wat voorstellen, cross-entropy loss is de meest gangbare metric voor loss bij grote taalmodelen zoals BERT en GPT.
Cross-entropy loss
De cross-entropy loss vertelt ons hoe groot de fout is tussen de voorspelling van het model en de werkelijke output. Hoe kleiner de loss, hoe beter het model presteert.
Cross-entropy loss en Perplexity
De cross-entropy loss wordt vaak vertaald naar een andere term: perplexity
.
Dat gebeurt met de volgende formule:
Ik heb vroeger met wiskunde onvoldoende opgelet om precies te doorgronden wat hier eigenlijk staat maar wat ik er van begrijp is dat Perplexity eigenlijk een exponentiële transformatie van de cross-entropy loss is. De termen zijn nauw met elkaar verbonden in de zin dat: -De cross-entropy loss voor een specifieke trainingsoutput bekijkt wat de afstand is tenopzichte van de verwachte output in de trainingsdataset (minder afstand = nauwkeurigere voorspelling) -De Perplexity geeft de mate van “verwarring” aan van het model als geheel aangaande de zekerheid voor een gedane voorspelling. Hoe lager de perplexity is, hoe “zekerder” het model is dat de voorspelde output klopt. (lagere plerplexity = meer zekerheid over output)
Backpropagation
Net als bij Word2Vec embeddings is een belangrijke mechaniek die gebruikt wordt om gewichten in het model daadwerkelijk aan te passen “backpropagation”. Bij backpropagation tweak je de waarde van een bepaalde node van je LLM een heel klein beetje om deze toe te bewegen naar de waarde van de trainingsdata output. De mate waarin je de node tweakt kun je instellen als hyperparameter bij de start van je training. Wanneer je bij de start van je finetuning de leersnelheid moet opgeven dan geef je eigenlijk op in welke mate er gecorrigeerd moet worden in de nodes om de loss te verminderen. Hoe groter deze waarde hoe sneller het model “leert”, maar ook hoe groter de kans dat je het model belangrijke dingen laat vergeten. Doordat het model bij elke input uit de trainingsdataset dit proces herhaalt, zal de model output steeds dichter bij de trainingsoutput komen te liggen.
Bij Backpropagation pas je niet alleen de waarde van de laatste node aan, maar de waarde van alle bovenliggende nodes in het LLM systeem. Elk gewicht in het systeem wordt naar rato van de mate waarin dit gewicht bijdroeg aan de fout aangepast. Dit wordt bepaald aan de hand van de kettingregel uit de calculus:
Weer zo’n regel die ik in de research voor deze blog even opzocht maar waar ik na de eerste blik er op te werpen niet direct iets van begreep. Gelukkig is deze regel met de kennis die we inmiddels hebben wel goed te begrijpen, en ik vond de uitleg van Anthropic en OpenAI super waardevol voor mijn eigen begrip, dus in plaats van het vertalen van die output naar een uitleg in deze blog is het msschien ook wel leuk om de betreffende chatsessie gewoon even te delen: Uitleg van ChatGPT over backpropagation en de kettingregel
Belangrijke instellingen en termen tijdens je training
We hebben nu al een aantal belangrijke termen behandeld die je tijdens je training nodig hebt:
- De loss → Het verschil tussen de door het model voorspelde output en de verwachte output voor een bepaalde trainingsstep
- De perplexity → de mate van “onzekerheid” die het model heeft over de output
- de leersnelheid → de mate waarin tijdens de training gecorrigeerd moet worden voor loss
Naast deze termen zijn er nog een aantal andere begrippen en instellingen die goed zijn om kort langs te lopen:
- Epoch → Een epoch is het aantal iteraties van finetuning runs dat tijdens een trainingsset gedaan wordt. Tijdens 1 epoch ga je elke trainingssample 1x langs.
- Batchgrootte (batch size) → In plaats van het model te trainen op één sample per keer, trainen we het model vaak op meerdere samples tegelijk. Dit noem je een batch. De batchgrootte bepaalt hoeveel trainingsvoorbeelden tegelijk aan het model worden aangeboden. Een grotere batchgrootte kan leiden tot stabielere updates, maar vereist meer geheugen. Een kleinere batchgrootte kan sneller zijn en kan helpen om uit lokale minima te ontsnappen, maar kan onstabiele updates geven.
- Validatieset en Testset → Tijdens het trainen splitsen we de data in drie delen: een trainingsset (om het model te trainen), een validatieset (om tijdens het trainen de prestaties van het model te monitoren en hyperparameters te tunen), en een testset (om de uiteindelijke prestaties van het model te beoordelen). De validatieset helpt bij het vinden van het optimale punt om de training te stoppen, terwijl de testset het model beoordeelt op data die het nog nooit eerder heeft gezien.
- Hyperparameter Tuning → Hyperparameters zijn instellingen die je instelt voordat de training begint (zoals leersnelheid, batchgrootte, aantal epochs). Het proces van hyperparameter tuning is het uitproberen van verschillende combinaties van deze waarden om te vinden welke combinatie de beste prestaties geeft.
- Overfitting → Dit gebeurt wanneer het model te nauwkeurig de trainingsdata leert, inclusief de ruis en uitzonderingen, in plaats van de onderliggende patronen. Een overfit model presteert vaak goed op de trainingsdata maar slecht op nieuwe, ongeziene data. Er zijn allerlei technieken waarmee je dit fenomeen kunt tegengaan, maar die heb ik nog onvoldoende verkend om er echt iets waardevols over te kunnen zeggen .
De evaluatiefase van finetuning
Tijdens de evaluatiefase van het finetunen ga je het gefinetunede model testen en beoordelen. Het testen van je model doe je op de test dataset die je hebt gegenereerd in de data preparatie stap. De test dataset bevat een aantal testgegevens die het model nog niet eerder heeft gezien. Door gebruik te maken van nieuwe data kunnen we de output van het model beoordelen op de gegeneraliseerde learnings van tijdens de training in plaats van de mate waarin het model een specifiek voorbeeld herinnert. Er zijn vele metrics die je als uitgangspunt kunt nemen voor dit evaluatieproces, maar in ons geval is perplexity de belangrijkste.
Trainen, testen en valideren
In een flow ziet dat er zo uit:
- Testdata: De testdata dient eigenlijk als een finale check op de modelprestaties en is tijdens de hele training verder niet gebruikt. de uitkomsten met de testdata dienen als input voor evaluatie.
- Validatiedata: Om de x aantal trainingsiteraties checken we een voorspelling met de validatiedataset. Deze dataset heeft geen invloed op de gewichten. We doen dit om te valideren of het model niet teveel “overfitting” toepast, maar dingen ook kan generaliseren.
- Trainingsdata: Dit is de data die we al kenden en daadwerkelijk gebruikt wordt voor het aanpassen van gewichten
Kortgezegd trainen we met 3 datasets:
Het is denk ik handig om nog even een kleine verduidelijking te schrijven over het verschil tussen training, testen en valideren. Ik had die in de initiele weergave weg gelaten om het niet in 1x te onduidelijk te maken. Maar nu we de basis van finetuning training snappen is het denk ik wel goed om dit iets te verdiepen.
Conclusie
In een steeds breder worden scala aan gereedschappen die we hebben om de output van LLM’s te optimaliseren, heeft finetuning een belangrijke plaats. Finetuning kan voordelen bieden die technieken die enkel de context van generieke modellen beïnvloeden niet bieden. Finetuning is echter wel een wat tijdrovendere en minder flexibele operatie dan veel van die technieken, en dus niet in elk scenario de beste keus.
Het finetunen van een bestaand model is een haalbare manier om toegang te krijgen tot krachtige AI zonder de immense middelen die nodig zijn voor het pre-trainen van een taalmodel vanaf nul. Uiteindelijk kan finetuning een brug slaan tussen de brede capaciteiten van algemene LLM's en de specifieke behoeften van diverse industrieën en toepassingen. Het stelt ons in staat om de kracht van grote, vooraf getrainde modellen te benutten en deze te verfijnen voor nauwkeurige, taakspecifieke toepassingen.