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

Verdeel een rekenbudget tussen model en trainingsdata

Fit een eenvoudige machtswet, onderzoek een verliesmodel en bereken een analytisch optimum onder een vast FLOP-budget.

75 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je onderscheidt een empirisch schaalmodel van een gegarandeerde natuurwet.
  • Je balanceert parameter- en tokenaantallen bij een vast benaderd trainingsbudget.

Je hebt een vast trainingsbudget. Een groter model verwerkt dan minder tokens: waar ligt de beste balans? Scaling laws beschrijven empirische verbanden waarmee je zulke experimenten kunt plannen, zonder ieder mogelijk model te trainen.

De onderzoeken van Kaplan en Hoffmann gebruikten verschillende meetopzetten en vonden verschillende optimale verhoudingen. Hun fits zijn geen natuurwet voor iedere architectuur of datamix. Hier gebruiken we eigen synthetische coëfficiënten; de uitkomsten zijn rekenvoorbeelden, geen actuele benchmark of trainingsadvies.

1. Maak een machtswet zichtbaar

Neem verlies L(N)=E+A/N^α bij parameteromvang N. E is een vooraf gegeven verliesvloer, A een positieve schaal en α de exponent. Trek de vloer af voordat je logaritmeert; anders fit je een andere relatie.

log(L−E)=logA−αlogN

Voor E=1, A=2 en α=.5 geven N=[1,4,16] verliezen [3,2,1.5]. De resten zijn [2,1,.5]; iedere verviervoudiging van N halveert het resterende verlies. Een rechte lijn in logruimte heeft hier helling −.5 en intercept log(2).

n = np.array([1.,4.,16.])
losses = np.array([3.,2.,1.5])
a,alpha = fit_powerlaw(n,losses,1.)
print(round(a,4),round(alpha,4))
# 2.0 .5

Maak fit_powerlaw met minstens twee verschillende positieve groottes en verliezen boven E. Onze gecentreerde kleinste-kwadratenfit gebruikt een bekende vloer; hij is niet de robuuste gezamenlijke meerparameterfit uit het Chinchilla-paper. Een verkeerde vloer of extrapolatie buiten het meetbereik kan misleiden.

01
CHECKPOINTEen bekende verliesvloer maakt een eenvoudige log-fit mogelijk

2. Geef model en data een eigen foutterm

Nu gebruiken we L(N,D)=E+A/N^α+B/D^β. D is het aantal verwerkte trainingstokens. Groter N verkleint de modelterm en groter D de dataterm, zolang deze gekozen benadering bruikbaar blijft.

loss = scaling_loss(
    10.,10.,1.,1.,1.,1.,1.)
print(round(loss,4))  # 1.2

De laatste vijf argumenten zijn A, B, α, β en E. Bij N=D=10 dragen beide fouttermen .1 bij aan vloer één. Maak scaling_loss voor de opgegeven eindige, positieve scalars; alleen E mag nul zijn.

Coëfficiënten horen bij hun eenheden. Duizenden parameters gebruiken in plaats van losse parameters zonder A aan te passen verandert het verliesmodel. Verwerkte tokens zijn bovendien niet noodzakelijk unieke tokens: herhaling vervangt datadiversiteit niet vanzelf.

ÉÉN BUDGET, TWEE KEUZESEen groter model laat minder trainingstokens over

Synthetisch model: L = 1 + 4/√N + 9/√D, met C = 6ND. N en D zijn continue rekenwaarden.

voorspelde loss1.03.56.01101001000N · logaritmische as
Jouw keuzeBerekend optimum
Jouw N / D44.67 / 223.87Optimaal N / D44.44 / 225.00Jouw voorspelde loss2.200

Budget C = 60.000 benaderde FLOPs. Geen echte modelmeting of tijdsraming; buiten het gemeten bereik is extrapolatie onzeker. De verticale schaal past zich aan het budget aan.

02
CHECKPOINTMeer model en meer data verkleinen verschillende fouttermen

3. Houd het budget vast

Een ruwe dichte-transformerraming gebruikt C≈6ND FLOPs. C telt benaderd rekenwerk, geen doorlooptijd; exacte architectuur-, context-, communicatie- en hardwarekosten vallen erbuiten. Bij vast C volgt D=C/(6N), dus meer parameters laten minder tokens over.

Voor C=600 en A=B=α=β=1 geeft N=D=10 verlies 1.2. N=20 en D=5 gebruikt hetzelfde budget, maar verlies 1+.05+.2=1.25. Alleen groter kiezen is hier dus niet beter.

n,d = compute_allocation(
    600.,1.,1.,1.,1.)
print(np.round([n,d],4))  # [10. 10.]
print(round(6*n*d,4))  # 600.0
other = scaling_loss(
    20.,5.,1.,1.,1.,1.,1.)
print(round(other,4))  # 1.25

Om het continue optimum te vinden vervang je D door C/(6N). De modelterm daalt met N, terwijl de dataterm stijgt. Op het evenwicht zijn de gewogen bijdragen gelijk: αA/N^α=βB/D^β.

Noem h de logverhouding van de gewogen coëfficiënten. De losse logaritmen voorkomen dat producten al overlopen vóór je log toepast. Oplossen van de evenwichtsvoorwaarde geeft:

h=logα+logA−logβ−logBlogN=h+β(logC−log6)α+β

Bij ongelijke exponenten hoef je de twee ruwe fouttermen dus niet gelijk te maken. Neem C=192, A=B=1, α=1 en β=2. N=8 en D=4 geeft 1/8=2/16 voor de gewogen termen en loss 1.1875.

n,d = compute_allocation(
    192.,1.,1.,1.,2.)
print(np.round([n,d],4))  # [8. 4.]
loss = scaling_loss(
    n,d,1.,1.,1.,2.,1.)
print(round(loss,4))  # 1.1875

Maak compute_allocation in logruimte, ook voor de producten en verhoudingen van coëfficiënten. De vloer E verschuift de loss, maar niet dit optimum. Een echt ontwerp moet vervolgens discrete architecturen, dataplafonds, inferentiekosten en nieuwe meetresultaten meenemen.

03
CHECKPOINTEen grotere parameterkeuze laat minder tokens over

Wat neem je mee?

  • Een schaalfit is een empirisch model binnen een bepaalde meetopzet.
  • Parameter- en datatermen reageren anders op dezelfde budgetkeuze.
  • Een analytisch optimum helpt plannen en vraagt daarna praktische verificatie.

Je hebt nu de taalmodelroute van tokenisatie tot aanpassing doorlopen. Hierna werk je aan betrouwbare invoer, uitvoer en evaluatie bij het gebruiken van LLM’s.

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 bekende verliesvloer maakt een eenvoudige log-fit mogelijk

Schrijf fit_powerlaw(sizes,losses,floor) met (A,alpha), beide Python-floats. Valideer eindige evenlange 1D-vectoren met minstens twee verschillende positieve sizes; floor≥0 en losses>floor. Anders ValueError.

Een hint

Centreer log(sizes). slope=sum((x−mean(x))×(y−mean(y)))/sum((x−mean(x))²).

02
Meer model en meer data verkleinen verschillende fouttermen

Schrijf scaling_loss(parameters,tokens,a,b,alpha,beta,floor). Alle scalars zijn eindig en positief, behalve floor die nul mag zijn. Retourneer de voorspelde loss, anders ValueError.

Een hint

Gebruik de twee afzonderlijke machtswetbijdragen en tel de vloer erbij. Houd N en D in dezelfde eenheden als de coëfficiënten.

03
Een grotere parameterkeuze laat minder tokens over

Schrijf compute_allocation(budget,a,b,alpha,beta) met (N,D) als Python-floats. Alle vijf scalars zijn eindig en positief; anders ValueError. Bereken N via logaritmen. Dit is een continu optimum, geen afgerond modelontwerp.

Een hint

log(N)=[log(αA/(βB))+βlog(C/6)]/(α+β). Controleer dat 6ND weer het budget oplevert.

Vastgelopen? Bekijk de voorbeeldoplossing
import numpy as np

def fit_powerlaw(sizes,losses,floor):
    scalars=[np.asarray(v) for v in [floor]]
    if any(v.ndim!=0 or v.dtype.kind not in 'biuf' for v in scalars):
        raise ValueError('Gebruik eindige scalaire getallen')
    floor,=map(float,scalars)
    sizes=np.asarray(sizes,float);losses=np.asarray(losses,float)
    if sizes.ndim!=1 or sizes.size<2 or sizes.shape!=losses.shape or not np.isfinite(floor) or floor<0 or not np.isfinite(sizes).all() or not np.isfinite(losses).all() or np.any(sizes<=0) or np.any(losses<=floor) or np.unique(sizes).size<2:
        raise ValueError('Ongeldige schaalmetingen of verliesvloer')
    x=np.log(sizes);y=np.log(losses-floor);center=x-x.mean()
    slope=np.sum(center*(y-y.mean()))/np.sum(center**2)
    return float(np.exp(y.mean()-slope*x.mean())),float(-slope)

def scaling_loss(parameters,tokens,a,b,alpha,beta,floor):
    scalars=[np.asarray(v) for v in [parameters,tokens,a,b,alpha,beta,floor]]
    if any(v.ndim!=0 or v.dtype.kind not in 'biuf' for v in scalars):
        raise ValueError('Gebruik eindige scalaire getallen')
    parameters,tokens,a,b,alpha,beta,floor=map(float,scalars)
    if any(not np.isfinite(v) or v<=0 for v in [parameters,tokens,a,b,alpha,beta]) or not np.isfinite(floor) or floor<0:
        raise ValueError('Ongeldige schaalparameters')
    return floor+a/parameters**alpha+b/tokens**beta

def compute_allocation(budget,a,b,alpha,beta):
    scalars=[np.asarray(v) for v in [budget,a,b,alpha,beta]]
    if any(v.ndim!=0 or v.dtype.kind not in 'biuf' for v in scalars):
        raise ValueError('Gebruik eindige scalaire getallen')
    budget,a,b,alpha,beta=map(float,scalars)
    if any(not np.isfinite(v) or v<=0 for v in [budget,a,b,alpha,beta]):
        raise ValueError('Ongeldige positieve parameters')
    logn=(np.log(alpha)+np.log(a)-np.log(beta)-np.log(b)+beta*(np.log(budget)-np.log(6.)))/(alpha+beta)
    n=float(np.exp(logn));d=float(np.exp(np.log(budget)-np.log(6.)-logn))
    return n,d

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