- Je verandert een logitverdeling zonder modelgewichten aan te passen.
- Je onderscheidt filteren, renormaliseren en daadwerkelijk een token trekken.
Je mini-GPT kan steeds dezelfde zin herhalen. Kun je andere vervolgen krijgen zonder opnieuw te trainen? Een decodeerstrategie (decoding strategy) verandert hoe je uit de voorspelde logits een volgende token kiest; de modelgewichten blijven gelijk.
Denk aan een schaal met kaartjes. Temperatuur verandert de verhouding tussen de stapels, filters halen kandidaten weg en pas de trekking kiest één kaartje. Meer toeval maakt de uitvoer gevarieerder, maar controleert geen feiten.
1. Maak scherpe of vlakke kansen
De logits zijn scores, nog geen kansen. Bij positieve temperatuur deel je de verschillen tussen scores door τ, voordat je softmax toepast. Een lage τ versterkt verschillen; een hoge τ verkleint ze, terwijl de rangorde gelijk blijft.
Hier is z de logitvector, i de gekozen positie, j iedere mogelijke token en m de hoogste logit. Dezelfde m aftrekken verandert de kansen niet en voorkomt grote positieve exponenten. De formule geldt voor τ>0.
Neem logits [0,log(2),log(4)]. Bij τ=1 worden de exponenten evenredig met [1,2,4], dus de laatste token krijgt 4/7 kans. Bij τ=2 worden die verhoudingen [1,√2,2] en daalt zijn kans naar ongeveer .4531.
z = np.log([1.,2.,4.])
for temp in [1.,2.,0.]:
p = temperature_probs(z,temp)
print(np.round(p,4))
# [.1429 .2857 .5714]
# [.2265 .3204 .4531]
# [0. 0. 1.]
Maak temperature_probs voor eindige, niet-lege 1D-logits en een eindige scalaire temperatuur vanaf nul. Bij τ=0 spreken we greedy af: de eerste hoogste logit krijgt kans één. Dat is een afzonderlijke tak; delen door nul is geen implementatie van greedy.
012. Filter kandidaten en normaliseer opnieuw
Top-k houdt maximaal k kandidaten met de hoogste kansen over. Nucleus sampling (top-p) houdt de kleinste gesorteerde prefix met samen minstens p kansmassa. De kandidaat die de grens passeert moet blijven, anders haal je de gewenste massa niet.
Neem [.5,.3,.2]. Top-k met k=2 bewaart .5 en .3; delen door .8 geeft [.625,.375,0]. Top-p met p=.6 heeft op de oorspronkelijke verdeling eveneens twee kandidaten nodig, want .5 alleen is te weinig.
p = np.array([.5,.3,.2])
k = top_k(p,2)
print(k) # [.625 .375 0.]
print(top_p(p,.6))
# [.625 .375 0.]
print(top_p(k,.6)) # [1. 0. 0.]
Het laatste resultaat laat zien waarom de volgorde telt: na top-k is de eerste kans al .625 en haalt één kandidaat de grens .6. Onze les gebruikt temperatuur → top-k → top-p. Bij gelijke kansen wint de laagste token-ID de eerdere sorteerplek.
Zes verzonnen logits. Volgorde: temperatuur → top-k → top-p → trekking.
Verander eerst alleen temperatuur en daarna één filter in de visualisatie. Bekijk zowel de percentages als het aantal kandidaten; een grotere kans is nog geen daadwerkelijke keuze. Maak top_k en top_p met het opgegeven kanscontract en renormaliseer na ieder filter.
023. Trek uit intervallen
Voor [0,.25,0,.75] krijgt ID 1 interval [0,.25) en ID 3 interval [.25,1). Een trekking precies op .25 valt dus in het tweede interval. De nulkansen hebben geen interval en mogen nooit winnen.
p = [0.,.25,0.,.75]
for u in [0.,.249,.25,.999]:
print(inverse_sample(p,u))
# 1 (u=0)
# 1 (u=.249)
# 3 (u=.25)
# 3 (u=.999)
Maak inverse_sample met u in [0,1). Bescherm de laatste cumulatieve grens tegen afronding en retourneer een Python-int. Een vaste seed kan buiten deze functie reproduceerbare u-waarden leveren; één herhaalbare uitkomst bewijst nog geen goede taalvervolging.
03Wat neem je mee?
- Temperatuur verandert kansen, filters veranderen de kandidatenverzameling.
- Renormaliseren en de filtervolgorde bepalen de uiteindelijke verdeling.
- Pas een trekking maakt van kansen één gekozen token.
Hierna versnel je het opnieuw voorspellen met een cache voor keys en values.