Hoppa till innehållet
AI-grafen
E· Universitetsprakmodeller· ca 60 min· utvecklande· verifierad 2026-09-20

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:

PPL=eL,L=−1N∑ilog⁡p(xi∣x<i)\mathrm{PPL} = e^{\mathcal{L}}, \qquad \mathcal{L} = -\frac{1}{N}\sum_i \log p(x_i \mid x_{<i})

Tolkningen: hur många alternativ modellen i praktiken väljer mellan vid varje token.

PerplexitetBetyder
1modellen vet alltid exakt nästa token
10den är lika osäker som vid ett val mellan tio likvärdiga
50 000den 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 identisktVarför
Tokenizernfler tokens per ord ger lägre perplexitet per token — samma modell, annan siffra
Testmängdenolika text har olika svårighetsgrad
Kontextlängdenmer kontext gör varje token lättare att gissa
Normaliseringsenhetenper 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:

EgenskapFångas av perplexitet?
Nästa-token-osäkerhetja — det är precis vad den mäter
Faktakorrekthetnej
Instruktionsföljsamhetnej
Resonemangsförmåganej
Hjälpsamhet och tonnej
Säkerhetnej

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:

  1. Under förträning — som ett kontinuerligt mått på att det går framåt.
  2. Vid jämförelse av samma modell före och efter en ändring, med allt annat lika.
  3. För att upptäcka domänskifte — perplexiteten stiger när modellen möter text olik träningsdatan.
  4. 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

Alla källor och licenser