Perplexitet
Kunna beräkna perplexitet och förklara vad den mäter — och inte mäter.
Förkunskaper
Intuition
Perplexitet är korsentropin, förpackad så att den går att tolka:
Tolkningen: hur många alternativ modellen i praktiken väljer mellan vid varje token.
| Perplexitet | Betyder |
|---|---|
| 1 | modellen vet alltid exakt nästa token |
| 10 | den är lika osäker som vid ett val mellan tio likvärdiga |
| 50 000 | den gissar helt slumpmässigt ur vokabulären |
En förbättring från förlust 2,3 till 2,0 låter marginell. I perplexitet är det 10,0 → 7,4 — en fjärdedels minskning av den effektiva osäkerheten. Det är därför perplexitet rapporteras: den gör små förluständringar begripliga.
Formellt
Perplexitet går bara att jämföra under stränga villkor. Detta missas ofta, och gör publicerade jämförelser missvisande.
| Måste vara identiskt | Varför |
|---|---|
| Tokenizern | fler tokens per ord ger lägre perplexitet per token — samma modell, annan siffra |
| Testmängden | olika text har olika svårighetsgrad |
| Kontextlängden | mer kontext gör varje token lättare att gissa |
| Normaliseringsenheten | per token, per ord eller per tecken ger helt olika tal |
Den första är den lömskaste: en modell med en tokenizer som delar upp texten i fler, kortare tokens får automatiskt lägre perplexitet per token, utan att vara bättre. Perplexitet per tecken (bits per character) är därför måttet man använder när tokenizern skiljer sig.
Vad perplexitet inte mäter:
| Egenskap | Fångas av perplexitet? |
|---|---|
| Nästa-token-osäkerhet | ja — det är precis vad den mäter |
| Faktakorrekthet | nej |
| Instruktionsföljsamhet | nej |
| Resonemangsförmåga | nej |
| Hjälpsamhet och ton | nej |
| Säkerhet | nej |
En instruktionsjusterad modell har ofta högre perplexitet på råtext än basmodellen den kommer från — den har lärt sig att svara i stället för att fortsätta texten, och det är en förbättring som måttet straffar.
Där perplexitet fortfarande är användbar:
- Under förträning — som ett kontinuerligt mått på att det går framåt.
- Vid jämförelse av samma modell före och efter en ändring, med allt annat lika.
- För att upptäcka domänskifte — perplexiteten stiger när modellen möter text olik träningsdatan.
- För kvalitetsfiltrering av träningsdata — extremt hög eller extremt låg perplexitet indikerar skräp respektive upprepningar.
Den fjärde är en verklig och underskattad användning: att köra en liten modell över ett korpus och filtrera bort dokument i svansarna är en billig och effektiv rensning.
Kontaminering är det som gör publicerade perplexitetstal opålitliga: har testmängden råkat ingå i träningsdatan blir perplexiteten artificiellt låg. Kontrollera alltid överlapp mellan test och träning innan du tror på en siffra.
Kod
import math, torch
def perplexitet(modell, tokens, kontext=1024, steg=512):
"""Glidande fönster med överlapp; bara de nya tokenen räknas."""
modell.eval()
nll, antal = 0.0, 0
for start in range(0, len(tokens) - 1, steg):
slut = min(start + kontext, len(tokens))
bit = tokens[start:slut]
mal = bit.clone()
mal[:-steg] = -100 # räkna bara de nya tokenen
with torch.no_grad():
ut = modell(bit.unsqueeze(0), labels=mal.unsqueeze(0))
n = int((mal != -100).sum())
nll += float(ut.loss) * n
antal += n
if slut == len(tokens):
break
return math.exp(nll / antal)
# Förlust → perplexitet
for loss in (1.6, 2.0, 2.3, 3.0, 4.6):
print(f" förlust {loss:.1f} → perplexitet {math.exp(loss):>8.1f}")
# förlust 1.6 → perplexitet 5.0
# förlust 2.3 → perplexitet 10.0
# förlust 4.6 → perplexitet 99.5
# Bits per character — jämförbart ÖVER tokenizers
def bpc(modell, text, tokenizer):
tokens = tokenizer(text, return_tensors="pt").input_ids[0]
with torch.no_grad():
loss = float(modell(tokens.unsqueeze(0), labels=tokens.unsqueeze(0)).loss)
return loss * len(tokens) / (len(text) * math.log(2))
# Domänskifte syns direkt i perplexiteten
def domankontroll(modell, tokenizer, texter: dict):
for namn, t in texter.items():
ids = tokenizer(t, return_tensors="pt").input_ids
with torch.no_grad():
p = math.exp(float(modell(ids, labels=ids).loss))
print(f" {namn:<22} PPL {p:>8.1f}")
# nyhetstext PPL 18.3
# medicinska journaler PPL 142.7 ← långt utanför träningsdomänen
# Kvalitetsfiltrering: kasta svansarna
def filtrera_korpus(modell, tokenizer, dokument, lag=10, hog=1000):
behall = []
for d in dokument:
ids = tokenizer(d, return_tensors="pt", truncation=True, max_length=512).input_ids
with torch.no_grad():
p = math.exp(float(modell(ids, labels=ids).loss))
if lag <= p <= hog:
behall.append(d)
return behall
# för LÅG perplexitet = upprepningar, boilerplate; för HÖG = skräp, kodslask
Behärskning innebär
- Beräknar perplexitet från korsentropi
- Tolkar värdet
- Vet när perplexitet inte går att jämföra
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0
- Hugging Face — dokumentation (Apache-2.0) — Apache-2.0
- arXiv — The Curious Case of Neural Text Degeneration — arXiv (öppen åtkomst; licens per artikel)