- Je onderscheidt Unicode-codepunten, UTF-8-bytes en tokens.
- Je decodeert zonder stil gegevensverlies en legt normalisatie als keuze vast.
Je typt “café 🙂” en verwacht na opslaan precies dezelfde tekst terug. Toch zijn zichtbare tekens, Unicode-codepunten, bytes en modeltokens verschillende eenheden. Een tokenizer maakt die omzettingen expliciet voordat een taalmodel met token-ID's kan rekenen.
Denk aan tekst die in een envelop met genummerde bouwsteentjes wordt verstuurd. UTF-8 bepaalt hoe codepunten als bytes worden vastgelegd; een byte-BPE kan later meerdere bytes bundelen tot één token. Een token is dus niet automatisch een letter of een woord.
1. Inspecteer codepunten en bytes afzonderlijk
Python-len op een str telt Unicode-codepunten. De letter é is één codepunt, maar gebruikt de UTF-8-bytes 195 en 169; 🙂 is één codepunt met vier bytes. Een zichtbaar letterteken kan bovendien meerdere codepunten bevatten, zoals e met een apart accent.
text = 'é🙂'
ids = list(text.encode('utf-8'))
print(len(text),len(ids)) # 2 6
print(ids)
# [195,169,240,159,153,130]
Een codepuntentelling is daarom geen telling van zichtbare lettertekens, grapheme clusters genoemd. Deze les implementeert geen grapheemteller. We onderzoeken de exacte gegevens waarop onze tokenizer gaat werken.
| Tekst | Codepunten | UTF-8-bytes |
|---|---|---|
| é | 1 | 2 |
| e + los accent | 2 | 3 |
| 🙂 | 1 | 4 |
Maak utf8_ids(text) als een lijst exacte gehele bytewaarden, met de normale str.encode-validatie. Behoud alle hoofdletters, spaties en nieuwe regels. ID 0 tot en met 255 vertegenwoordigen in onze eerste woordenschat rechtstreeks de mogelijke bytes; grotere BPE-ID's komen pas na geleerde samenvoegingen.
Een zichtbaar teken kan meerdere codepunten bevatten. Bekijk codepunten en hun UTF-8-bytes; dit is nog geen geleerde BPE-tokenisatie.
U+006399U+006197U+0066102U+00E9195 · 169U+002032U+1F642240 · 159 · 153 · 130NFC verandert deze tekst niet. Onze BPE past geen normalisatie, trimmen of lowercasing toe.
2. Herstel de hele byteketen strikt
Een los deel van een meerbytecode hoeft geen geldige UTF-8 te zijn. Byte 195 begint bijvoorbeeld de codering van é, maar mist zonder 169 zijn vervolg. Verzamel daarom eerst alle bytes en decodeer daarna één keer.
Noem UTF-8-coderen E en strikt teruglezen D. Voor geldige Unicode-tekst s hoort deze roundtrip exact dezelfde string op te leveren. Een fout melden bij beschadigde data is iets anders dan stil een andere tekst teruggeven.
restored = bytes(ids).decode('utf-8')
print(restored == text) # True
try:
bytes([195]).decode('utf-8')
except UnicodeDecodeError:
print('Onvolledige UTF-8')
Maak from_utf8(ids) met alleen exacte integers van 0 tot 255: bool en float horen hier niet bij; behoud de invoerlijst. Geef ValueError bij ongeldige waarden en laat ongeldige UTF-8 een UnicodeDecodeError geven; die is ook een ValueError. Gebruik geen errors='ignore' of 'replace', want daarmee verlies of verander je gegevens.
023. Laat normalisatie een zichtbare keuze zijn
De strings é en e plus los accent kunnen hetzelfde ogen, terwijl hun codepunten verschillen. NFC-normalisatie kiest een canonieke vorm; voor dit voorbeeld voegt zij e en het accent samen. Dat is nuttig bij sommige toepassingen, maar past niet ongemerkt bij een belofte van exact tekstherstel.
import unicodedata
original = 'e\u0301'
nfc = unicodedata.normalize('NFC',
original)
print(len(original),len(nfc)) # 2 1
print(list(original.encode('utf-8')))
# [101,204,129]
print(list(nfc.encode('utf-8')))
# [195,169]
angstrom = '\u212b'
same_len = unicodedata.normalize('NFC',
angstrom)
print(len(angstrom),len(same_len),
angstrom != same_len) # 1 1 True
Verwar “changed” niet met een lengtetoename of -afname. Een normalisatie kan ook andere codepunten met dezelfde lengte geven. Vergelijk daarom de strings zelf; lowercasing en strip zijn bovendien weer andere bewerkingen dan NFC.
Maak normalization_report(text) met original,nfc,changed,original_codepoints,nfc_codepoints. Bewaar original exact en zet changed op text!=nfc. Onze BPE verandert de tekst niet; dit rapport laat alleen zien wat een expliciete NFC-keuze zou doen.
034. Controleer ook onopvallende witruimte
Een spatie aan het begin of een nieuwe regel kan in code en prompts betekenis hebben. De voorbeeldtekst hieronder heeft vier codepunten en zeven bytes: spatie, A, newline en een vierbyte-emoji. Na de roundtrip blijven alle vier posities staan.
sample = ' A\n🙂'
encoded = sample.encode('utf-8')
decoded = encoded.decode('utf-8')
print(len(sample),len(encoded)) # 4 7
print(decoded == sample) # True
print(repr(decoded))
# ' A\n🙂'
Probeer in de visualisatie eerst é en daarna e met los accent. Voorspel vóór je klikt wat verandert aan codepunten, bytes en NFC. De inspecteur toont die drie afzonderlijk; hij heeft nog geen BPE-merges geleerd.
Wat neem je mee?
- Codepunten, bytes en tokens tellen verschillende dingen.
- Strikt decoderen bewaart de volledige byteketen of meldt een fout.
- Normalisatie en witruimtebewerking vragen een expliciete productkeuze.
Hierna leer je terugkerende aangrenzende byteparen samenvoegen.