- Je past trainingrangen toe op nieuwe tekst, zonder opnieuw frequenties te leren.
- Je bewaart en herstelt een tokenizer met begrensde, geldige merge-regels.
Je tokenizer heeft geleerd welke byteparen vaak voorkwamen. Bij een nieuwe prompt mag hij die afspraken gebruiken, maar niet stil opnieuw een andere woordenschat maken. Anders zou hetzelfde token-ID ineens iets anders betekenen voor de embeddingtabel van het taalmodel.
Vergelijk de geleerde merges met een vast woordenboek van bouwinstructies. Elke regel maakt een nieuw kaartje uit twee al bekende kaartjes. Training leerde de volgorde; coderen past die rangorde toe zonder frequenties uit de nieuwe tekst te leren.
1. Bouw tokens uit bekende bytes
Begin de woordenschat V met ID's 0 tot en met 255, ieder gekoppeld aan precies één byte. Regel r maakt ID 256+r uit twee eerder bekende ID's a en b. Het nieuwe tokenstuk bestaat uit hun bytes achter elkaar, met behoud van volgorde.
Hier betekent ∥ concatenatie van bytes. Voor (97,98,256) ontstaat b'ab'; (98,99,257) maakt b'bc'. De twee nieuwe ID's hebben dus een vaste betekenis, ook als je ze later op een andere tekst gebruikt.
merges = [(97,98,256),(98,99,257)]
vocab = {i:bytes([i])
for i in range(256)}
for a,b,new_id in merges:
vocab[new_id] = vocab[a]+vocab[b]
print(vocab[256],vocab[257])
# b'ab' b'bc'
Maak vocabulary(merges) met maximaal 128 regels van drie exacte integers. Merges is een lijst; iedere regel is een lijst of tuple. Weiger onbekende verwijzingen, dubbele paren en onjuiste nieuwe ID's met ValueError. Controleer vóór concatenatie dat ieder samengesteld tokenstuk hoogstens 4096 bytes wordt, zodat opeenvolgende verdubbelingen begrensd blijven.
012. Laat de trainingsrang kiezen
In “abcbc” komt bc aanvankelijk twee keer voor en ab één keer. Toch hoort eerst regel 0, ab→256, te worden toegepast; daarna kan bc→257. Je krijgt [256,99,257], niet [97,257,257].
De onderstaande code gebruikt merge_pair uit de vorige les. Voor deze twee regels laat opeenvolgende toepassing precies zien welke tokens verdwijnen. In de encoder kies je steeds de vroegst geleerde regel die nog op een aanwezig paar past; iedere vervanging blijft links-naar-rechts en niet-overlappend.
tokens = list(b'abcbc')
for a,b,new_id in merges:
tokens = merge_pair(tokens,
(a,b),new_id)
print(tokens) # [256,99,257]
raw = b''.join(vocab[t] for t in tokens)
print(raw.decode('utf-8')) # abcbc
Maak encode(text,merges) en decode(tokens,merges), na validatie met vocabulary. Encode begint bij de UTF-8-bytes; decode verzamelt eerst alle tokenbytes en decodeert dan strikt. Een los token kan slechts een deel van een meerbytecode bevatten, dus decodeer niet ieder token apart.
Behoud de aangeleverde merge-regels en decoder-ID-lijsten zonder wijzigingen. Ongeldige Unicode, zoals een losse surrogate, blijft bij encode een normale UnicodeEncodeError; vervang die invoer niet stilzwijgend.
Een onbekend of niet-geheel token-ID geeft ValueError; ook bool is geen geldig ID. Ongeldige UTF-8 mag de gewone UnicodeDecodeError geven. Behoud hoofdletters, CRLF, spaties en onbekende woorden: de basisbytes blijven de terugval.
023. Bewaar dezelfde afspraken als JSON-data
Een bestand met dezelfde aantallen tokens is nog geen uitwisselbare tokenizer. De ID-betekenissen en regelvolgorde moeten gelijk blijven. Bewaar daarom een versie en de concrete merges, zonder uitvoerbare Python-objecten in het bestand.
import json
saved = json.dumps({'version':1,
'merges':merges})
data = json.loads(saved)
restored = [tuple(r) for r in
data['merges']]
print(restored == merges) # True
Maak save_tokenizer en load_tokenizer. Valideer vóór opslaan en na laden; accepteer uitsluitend version=1 en merges, een echte integer-versie en maximaal 32768 teksttekens. Pretty JSON is ook JSON: witruimte buiten strings mag verschillen zolang de data gelijk blijven.
Controleer na json.loads opnieuw het hele vocabulary-contract. Retourneer een lijst tuples en meld ongeldige bestanden als ValueError, inclusief een RecursionError die een te diep genest maar kort bestand bij het parsen kan geven. Een extra dieptelimiet is niet nodig voor dit contract; vang die specifieke parserfout af.
034. Test tekst, niet alleen aantallen
Gebruik de encoder en decoder om echte strings te vergelijken. De losse accenten, emoji, CRLF en twee spaties in dit voorbeeld zijn geen versiering: een implementatie die normaliseert of trimt kan hetzelfde aantal tokens rapporteren en toch tekst verliezen. De markerachtige tekst hieronder is gewone tekst, geen verborgen besturingstoken.
sample = 'e\u0301 🙂\r\n <|end|>'
ids = encode(sample,restored)
text = decode(ids,restored)
print(text == sample) # True
print(encode('abcbc',restored))
# [256,99,257]
Een decodefout negeren of tijdens laden de regels sorteren kan zo'n test breken. Test daarom na een save/load niet alleen de tekst-roundtrip, maar ook dezelfde encoderuitkomst vóór en na herstel. Dat bewaakt de vaste rijbetekenissen die een taalmodel zou gebruiken.
Wat neem je mee?
- Tokenstukken bestaan uit eerder bekende bytes en blijven begrensd.
- Bij coderen kiest trainingsrang, niet de frequentie in de nieuwe prompt.
- Gevalideerde JSON bewaart vaste ID-betekenissen en exacte tekst-roundtrips.
Hierna bouw je training, opslag en metingen samen in één notebook.