Werkplaats / De werkplaats
In jouw eigen tempo
← Taalmodellen
MODULE 06 · LES 15

Kies wat een basismodel tijdens pretraining ziet

Bereken een domeinmix, evalueer met vaste gewichten en pak documenten zonder onbedoelde informatieoverdracht.

60 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je onderscheidt pretraining, voortgezette pretraining en taakgerichte fine-tuning.
  • Je controleert de invloed van datamix en documentgrenzen op het trainingsdoel.

Je wilt een basismodel meer Nederlandse medische teksten laten zien, zonder dat algemene taal verdwijnt. Dat vraagt eerst een keuze over trainingsdata. Een groter model of een andere prompt bepaalt niet automatisch welke teksten tijdens leren vaak meetellen.

Pretraining begint met leren uit een brede verzameling, vaak via volgende-tokenvoorspelling. Voortgezette pretraining (continued pretraining) gaat vanuit bestaande gewichten door op bijvoorbeeld een ander domein. Fine-tuning past die gewichten aan voor een gekozen taak of gedrag; een gesprek tijdens inferentie verandert ze niet.

Onze mini-GPT startte willekeurig, maar zijn kleine synthetische corpus is geen brede pretraining. Hier bouw je drie kleine hulpmiddelen voor een bewust gekozen trainings- en evaluatiebeleid.

1. Kies eerst een domein

Stel dat twee domeinen 100 en 900 tokens bevatten. Proportioneel kiezen geeft kansen .1 en .9; beide domeinen even vaak kiezen geeft .5 en .5. Met vierkantswortels krijg je een tussenweg: gewichten 10 en 30, dus kansen .25 en .75.

pi=niα∑jnjα

n is het positieve tokenaantal van een domein, i het gekozen domein en j ieder domein in de som. α ligt hier tussen nul en één. Deze exponent is ons mengbeleid, geen universeel optimale waarde.

for alpha in [1.,.5,0.]:
    p = domain_mix([100,900],alpha)
    print(np.round(p,2))
# [.1 .9]
# [.25 .75]
# [.5 .5]

Maak domain_mix met logaantallen voor stabiele berekening. De kans kiest eerst een domein; daarna trek je daar een trainingsvenster. Gelijke domeinkansen maken de individuele tokens van kleine en grote domeinen dus niet automatisch even waarschijnlijk.

Gebruik vooraf gecontroleerde trainingsdata met toestemming. Houd evaluatiedata apart voordat je de mix afstemt. De helper kan niet controleren of de teksten representatief, rechtmatig verkregen of goed opgeschoond zijn.

01
CHECKPOINTEen datamix bepaalt hoe vaak ieder domein meetelt

2. Bevries de meetlat

Model A haalt verliezen [1,4] op twee domeinen, model B [2,3]. Op een 90/10-mix krijgt A 1×.9+4×.1=1.3, tegenover 2.1 voor B. Draai de mix om en B wint met 2.9 tegenover 3.7.

a = [1.,4.];b = [2.,3.]
for weights in [[9.,1.],[1.,9.]]:
    la = weighted_domain_loss(a,weights)
    lb = weighted_domain_loss(b,weights)
    print(round(la,1),round(lb,1))
# 1.3 2.1
# 3.7 2.9

Maak weighted_domain_loss voor reeds per-token gemiddelde domeinverliezen. Normaliseer de niet-negatieve gewichten en rapporteer ook de afzonderlijke domeinmetingen. Vergelijk modellen met dezelfde tokenizer en hetzelfde evaluatieprotocol; verander de testmix niet achteraf om een winnaar te maken.

02
CHECKPOINTEen veranderd gemiddelde kan een andere mix verbergen

3. Maak documentgrenzen werkelijk werkzaam

Een eindtoken (EOS) tussen documenten is nog geen attentionmasker. Onze eigen pakregel voegt EOS toe, laat iedere query alleen eerdere posities van hetzelfde document zien en leert niet de overgang van EOS naar een nieuw document. Het voorspellen van EOS na de laatste inhoudstoken telt wel mee.

x,y,allowed,mask,pos = pack_documents(
    [[10,11],[20]],2)
print(x)  # [10 11 2 20]
print(y)  # [11 2 20 2]
print(mask.astype(int))  # [1 1 0 1]
print(pos)  # [0 1 2 0]

De derde inputpositie bevat EOS=2, maar zijn doel 20 hoort bij het volgende document en krijgt mask=False. De query op input 20 mag alleen zichzelf zien; zijn doel EOS telt mee. Lokale posities beginnen per document opnieuw.

Maak pack_documents met de opgegeven int64-ID’s, gereserveerde EOS en bovengrens van 128 gepakte tokens. Andere pipelines kunnen een ander grensbeleid kiezen. Onze mini-GPT heeft een expliciete adapter voor deze posities, attention en loss nodig; alleen deze helper aanroepen verandert zijn forward niet.

03
CHECKPOINTPak documenten met een expliciet grensbeleid

Wat neem je mee?

  • De trainingsmix bepaalt welke domeinen vaak aan bod komen.
  • Een eerlijke modelvergelijking houdt evaluatiegewichten vast.
  • Documentgrenzen vragen een expliciet attention- en verliesbeleid.

Hierna leer je een kleine gewichtsaanpassing met een bevroren basismatrix.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-28.
DE VOLGENDE STAPLeer een kleine aanpassing met bevroren basisgewichten
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
Een datamix bepaalt hoe vaak ieder domein meetelt

Schrijf domain_mix(token_counts,alpha=1.), retourneer een kansvector. token_counts is een niet-lege, positieve, eindige 1D-vector; alpha is eindig tussen 0 en 1. Anders ValueError. Bereken via logaantallen om grote aantallen stabiel te verwerken.

Een hint

Normaliseer exp(alpha×log(counts)−maximum). Bij alpha=0 zijn alle gewichten gelijk.

02
Een veranderd gemiddelde kan een andere mix verbergen

Schrijf weighted_domain_loss(losses,weights) met een Python-float. Beide arrays zijn niet-leeg, 1D, even lang, eindig en niet-negatief; de gewichtsom is eindig en positief. Normaliseer de gewichten; anders ValueError.

Een hint

Bereken de som van losses×(weights/sum(weights)). Verliezen en gewichten hebben verschillende betekenis.

03
Pak documenten met een expliciet grensbeleid

Schrijf pack_documents(documents,eos_id) met (x,y,allowed,loss_mask,positions). Geef een niet-lege lijst van niet-lege 1D-reeksen met niet-negatieve int64-ID’s; EOS is een gereserveerde exacte int64-geschikte Python-int. Maximaal 128 tokens inclusief EOS. Anders ValueError. allowed is (T,T), x/y/posities/loss_mask zijn (T,).

Een hint

Bewaar naast de samengevoegde tokens ook document-ID’s. Attention combineert gelijke document-ID’s met een onderste driehoek. Loss telt alleen paren met dezelfde document-ID.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

def domain_mix(token_counts,alpha=1.):
    scalars=[np.asarray(v) for v in [alpha]]
    if any(v.ndim!=0 or v.dtype.kind not in 'biuf' for v in scalars):
        raise ValueError('Gebruik eindige scalaire getallen')
    alpha,=map(float,scalars)
    counts=np.asarray(token_counts,float)
    if counts.ndim!=1 or not counts.size or not np.isfinite(counts).all() or np.any(counts<=0) or not np.isfinite(alpha) or not 0<=alpha<=1:
        raise ValueError('Ongeldige aantallen of exponent')
    logs=alpha*np.log(counts);weights=np.exp(logs-logs.max())
    return weights/weights.sum()

def weighted_domain_loss(losses,weights):
    losses=np.asarray(losses,float);weights=np.asarray(weights,float)
    if losses.ndim!=1 or not losses.size or losses.shape!=weights.shape or not np.isfinite(losses).all() or not np.isfinite(weights).all() or np.any(losses<0) or np.any(weights<0) or weights.sum()<=0 or not np.isfinite(weights.sum()):
        raise ValueError('Ongeldige domeinmeting')
    return float(np.sum(losses*(weights/weights.sum())))

def pack_documents(documents,eos_id):
    if not isinstance(documents,list) or not documents or type(eos_id) is not int or not 0<=eos_id<=np.iinfo(np.int64).max:
        raise ValueError('Ongeldige documenten of EOS')
    ids=[];groups=[];positions=[]
    for d,document in enumerate(documents):
        a=np.asarray(document)
        if a.ndim!=1 or a.size==0 or a.dtype.kind not in 'iu' or int(a.min())<0 or int(a.max())>np.iinfo(np.int64).max or np.any(a==eos_id):
            raise ValueError('Ongeldige tokenreeks of gereserveerde EOS')
        ids.extend([int(t) for t in a]+[eos_id]);groups.extend([d]*(len(a)+1));positions.extend(range(len(a)+1))
    if len(ids)>128:
        raise ValueError('Maximaal 128 gepakte tokens')
    ids=np.array(ids,dtype=np.int64);groups=np.array(groups);positions=np.array(positions)
    g=groups[:-1];allowed=(g[:,None]==g[None,:])&np.tril(np.ones((len(g),len(g)),bool))
    return ids[:-1],ids[1:],allowed,groups[:-1]==groups[1:],positions[:-1]

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.