Predizione del next-token: embedding e cosa significa la perplexity
Allena un modello a caratteri su 32.033 nomi: gradient descent riscopre i conteggi a 4 decimali, poi perché le perplexity raramente coincidono.
In questa pagina
Ecco dieci nomi prodotti da un programma che non ha mai visto una parola:
cexze momakurailezitynn konimittain llayn ka
da moliellavo emia sade ftlspNessuno di questi è un nome. Quasi tutti ci stanno provando. Sono pronunciabili, finiscono dove finiscono i nomi, e uno di loro — emia — è a una sola lettera da uno reale. Il programma che li ha prodotti contiene 729 numeri, non ha alcuna nozione di parola, sillaba o persona, ed è stato adattato con un solo passaggio di conteggio delle coppie di lettere adiacenti.
Alla fine di questo capitolo una rete neurale avrà ridotto di un terzo il punteggio di quel programma sulla stessa misura. La parte per cui vale la pena restare è ciò che la rete fa per prima: riproduce la tabella dei conteggi con tre cifre decimali su ogni riga ben popolata, senza prompt, perché i due oggetti sono risposte alla stessa domanda. Tutto ciò che viene dopo è ciò che il conteggio non avrebbe mai potuto fare.
L'obiettivo è un'identità, non una scelta di design
Link alla sezione: L'obiettivo è un'identità, non una scelta di designIl Capitolo 7 ti ha lasciato con una sequenza di interi e senza alcun motivo perché uno debba seguirne un altro. Ecco il motivo, ed è una riga del Capitolo 2.
Un modello linguistico è una funzione che prende i token visti finora e restituisce una distribuzione su quale token viene dopo: un numero per ogni voce del vocabolario, non negativo, con somma pari a uno. Nient'altro. Per passare da questo a una probabilità per un intero documento, applica la regola della catena della probabilità:
Questa è un'identità, vera per qualsiasi sequenza di qualunque cosa, senza ipotesi aggiunte. Quindi un modello che svolge il compito piccolo — next token dati i precedenti — ha già svolto il compito grande di assegnare una probabilità a ogni documento possibile, esattamente e gratis. L'inquadramento popolare di tutto questo come un trucco economico («predice solo la parola successiva») rovescia la logica: prevedere il token successivo è modellare la distribuzione congiunta. Non c'è mai stata una seconda cosa da fare.
La loss segue in modo altrettanto meccanico. A ogni posizione il modello produce una distribuzione e la verità è un singolo token noto, quindi la cross-entropy del Capitolo 4 si applica senza modifiche:
Questa è la log-verosimiglianza negativa media — la ricetta del Capitolo 2 con una distribuzione categoriale nello slot in cui prima stava la gaussiana. E poiché la distribuzione vera è one-hot, la sua entropia è zero, quindi per l'identità del Capitolo 4 la cross-entropy è uguale alla divergenza KL: abbassare questo numero e avvicinare le convinzioni del modello ai dati sono lo stesso atto.
Una conseguenza merita una frase tutta sua, perché è il fatto economico sotto l'intero campo. Le etichette sono i dati, spostati di una posizione. Nessuno annota nulla. Un trilione di token di testo è un trilione di esempi già etichettati, ed è per questo che il corpus di addestramento di un modello moderno è «internet» e non «un dataset costruito da qualcuno».
Il baseline onesto: contare
Link alla sezione: Il baseline onesto: contarePrima di qualsiasi rete, il baseline: 32.033 nomi, uno per riga, e il compito di produrne altri una lettera alla volta.1
Il vocabolario è composto da 26 lettere più un simbolo di confine . che marca sia l'inizio sia la fine di un nome, quindi il modello deve imparare dove i nomi cominciano e dove si fermano. Sono 27 simboli, e il modello più piccolo possibile è una tabella di quante volte ogni simbolo ha seguito ogni altro simbolo.
N = torch.zeros((27, 27), dtype=torch.int32)
for w in words:
cs = ["."] + list(w) + ["."]
for a, b in zip(cs, cs[1:]):
N[stoi[a], stoi[b]] += 1
P = N.float()
P = P / P.sum(1, keepdim=True) # one distribution per row Due righe di aritmetica e il modello è adattato — e non è un'euristica: dividere i conteggi per i totali di riga è la stima di massima verosimiglianza per una distribuzione categoriale, cioè la ricetta del Capitolo 2 con il calcolo già fatto.
names: 32033 train/val/test: 25626 / 3203 / 3204
training bigrams: 182583
the six most likely letters after 'a':
a -> '.' 0.1944 a -> 'n' 0.1600 a -> 'r' 0.0967
a -> 'l' 0.0749 a -> 'h' 0.0690 a -> 'y' 0.0606Campiona da essa — scegli una lettera dalla riga della lettera corrente, spostati su quella riga, ripeti finché non compare il simbolo di confine — e ottieni i nomi all'inizio di questo capitolo. Falliscono in modo specifico e informativo: plausibili localmente, assurdi globalmente. Ogni coppia di lettere adiacenti in momakurailezitynn è una coppia che compare in nomi reali; ce ne sono soltanto diciassette di fila. Il modello ha memoria di una lettera, quindi non può sapere che sta andando avanti da troppo tempo.
Perplexity, e come leggerla
Link alla sezione: Perplexity, e come leggerlaLa loss sui nomi tenuti da parte è 2,4546 nats. Quel numero da solo non significa nulla, ed è per questo che esiste la perplexity:
Scritta per esteso, senza librerie che facciano il lavoro:
@torch.no_grad()
def perplexity(logits, Y):
logp = F.log_softmax(logits, dim=1) # log q for every symbol
chosen = logp[torch.arange(len(Y)), Y] # log q of the one that came next
return torch.exp(-chosen.mean()) L'esponenziazione annulla il logaritmo e riporta il numero alle unità del contare cose. Il modo pulito per vedere cosa conta è misurare un modello che non sa assolutamente nulla — uno che assegna probabilità a ogni simbolo indipendentemente dal contesto:
uniform over 27 symbols loss 3.2958 nats ppl 27.000
bigram counts, add-one smoothed loss 2.4546 nats ppl 11.642Esattamente 27,000, perché . La perplexity è il numero effettivo di opzioni ugualmente probabili tra cui il modello sta scegliendo. Una perplexity di 27 significa «nessuna idea, potrebbe essere qualunque cosa». L'11,642 del modello a conteggi significa che una lettera di contesto lo lascia incerto quanto qualcuno che sceglie alla cieca tra circa dodici opzioni invece che ventisette — ed è per questo che si cita la perplexity e non la loss grezza.
Due cose vanno storte con questa misura, e la seconda va storta anche negli articoli pubblicati.
Le probabilità zero sono fatali. Delle 729 celle nella tabella, 113 non compaiono mai in addestramento — il 15,5 % è vuoto. Va bene finché il set tenuto da parte non finisce in una di esse, e sette bigrammi nella validazione lo fanno, tra cui d→q, z→j e q→o due volte. Probabilità zero significa log , che significa loss infinita e perplexity infinita: un nome su tremila distrugge la metrica. La toppa abituale è aggiungere 1 a ogni conteggio prima di normalizzare, cosa che qui costa quasi nulla (2,4546 invece di 2,4524). Ma la toppa è una confessione. Un modello a conteggi non può generalizzare affatto. Non ha modo di sospettare che q→o sia plausibile perché q→u è comune e o si comporta come u altrove, dato che non ha alcuna nozione che due simboli possano somigliarsi. Ogni cella viene appresa da sola, e risolvere questo problema è lo scopo del resto del capitolo.
La perplexity è un prezzo per token, e il token è un parametro libero. Questo è l'errore che compare continuamente quando si confrontano modelli, ed è facile da vedere appena guardi. Prendi lo stesso corpus di prosa inglese del Capitolo 7, lo stesso modello a bigrammi interpolato, e cambia soltanto come il testo viene spezzato:
| unità | vocabolario | token nel test | cross-entropy | perplexity | bit per carattere |
|---|---|---|---|---|---|
| caratteri | 76 | 14.469 | 2,5217 | 12,45 | 3,6378 |
| BPE, 512 merge | 329 | 6.871 | 3,8547 | 47,21 | 2,6407 |
| BPE, 2.048 merge | 1.820 | 4.233 | 5,7468 | 313,20 | 2,4254 |
| parole | 2.991 | 6.284 | 3,5627 | 35,26 | 2,2322 |
La perplexity varia di un fattore 25 tra queste righe. Nulla del modello è cambiato; solo la dimensione della cosa da predire. Predire una parola intera è più difficile che predire una lettera, quindi costa di più per previsione — e ci sono meno previsioni da fare.
Ora leggi l'ultima colonna, che divide invece il costo totale per il numero di caratteri e lo converte in bit. Riordina la tabella. Per perplexity la classifica è caratteri, parole, BPE-512, BPE-2048; per bit per carattere è parole, BPE-2048, BPE-512, caratteri. Il modello a caratteri passa dal primo all'ultimo posto. Il modello a 2.048 merge, che per perplexity sembra 6,6 volte peggiore di quello a 512 merge, è in realtà il migliore dei due con 2,4254 bit contro 2,6407.
Quindi una perplexity è confrontabile solo tra due modelli che condividono un tokenizer, e modelli con tokenizer diversi possono essere confrontati solo in bit per carattere — la quantità che Shannon misurò nel 1951 facendo indovinare a soggetti umani la lettera successiva di un testo inglese, e che delimitò a circa un bit per carattere.2 Il nostro miglior bigramma sta a 2,23 bit, un buon riassunto di quanta strada deve ancora fare questo capitolo.
La stessa cosa, appresa
Link alla sezione: La stessa cosa, appresaOra costruiamo lo stesso modello come rete. Serviranno ordini di grandezza in più di aritmetica per arrivare nello stesso posto, e arrivare nello stesso posto è il punto.
Sostituisci la tabella con una matrice di pesi di forma . Trasforma la lettera corrente in un vettore one-hot, moltiplica, e chiama il risultato logits — i punteggi non normalizzati del Capitolo 4. Poi softmax, poi cross-entropy, poi gradient descent.
W = torch.randn((27, 27), requires_grad=True)
for step in range(3000):
logits = W[xs]
loss = F.cross_entropy(logits, ys)
W.grad = None
loss.backward()
W.data -= 50.0 * W.gradLa riga evidenziata contiene una definizione utile da avere. Moltiplicare un vettore one-hot per una matrice ne seleziona una riga, quindi la moltiplicazione è un lookup — e ogni implementazione salta l'aritmetica e fa direttamente il lookup, che è ciò che è W[xs].
Questa è una tabella di embedding. Una matrice con una riga per ogni voce del vocabolario, indicizzata per id del token. Nessuna geometria, nessuna semantica, nessun algoritmo separato: una tabella di lookup i cui contenuti vengono appresi da gradient descent insieme a tutto il resto. Ogni affermazione mistica sullo «spazio di embedding» arriva qui al suo fondamento.
Addestrala e osserva dove va:
step 1 train 3.7550 val 3.3882 max gap to the count table 0.757269
step 100 train 2.4732 val 2.4726 max gap to the count table 0.388354
step 1000 train 2.4557 val 2.4549 max gap to the count table 0.041862
step 3000 train 2.4547 val 2.4544 max gap to the count table 0.004048L'ultima colonna è la più grande differenza assoluta tra una qualunque cella di softmax(W) e la cella corrispondente della tabella dei conteggi, e va a zero. Dopo 3.000 step il disaccordo più grande in una qualunque delle 729 celle è 0,004048 e la media è 0,000224. La cella peggiore è q→i, vista dodici volte nell'intero set di addestramento; tra le 22 righe con più di mille occorrenze, il disaccordo peggiore è 0,000562.
count table network
a -> '.' 0.1945 0.1945
a -> 'n' 0.1601 0.1601
a -> 'r' 0.0967 0.0967Gradient descent, partendo da numeri casuali e senza ricevere altro che «rendi grande la log-probabilità della prossima lettera», ha riscoperto la tabella dei conteggi. E doveva farlo: i conteggi sono la stima di massima verosimiglianza, la cross-entropy è la log-verosimiglianza negativa, quindi entrambe le procedure ottimizzano lo stesso obiettivo e quell'obiettivo ha un unico ottimo. La rete non ha imparato qualcosa simile al conteggio. È convergita al conteggio, lentamente.
Il che solleva la domanda legittima: perché qualcuno dovrebbe preoccuparsene? Perché la tabella dei conteggi da qui non ha nessun posto dove andare, mentre la rete sì.
Il contesto è il collo di bottiglia, non la capacità
Link alla sezione: Il contesto è il collo di bottiglia, non la capacitàEstendi il modello perché guardi più di un carattere precedente. Questa è l'architettura di Bengio del 2003, l'antenata diretta di ogni modello nel resto di questo corso:4 prendi gli ultimi tre caratteri, mappa ciascuno attraverso una tabella di embedding in una riga a 10 dimensioni, concatena le righe in 30 numeri, spingili attraverso il layer nascosto del Capitolo 5, e termina con un layer di output che produce un logit per ogni voce del vocabolario.
C = torch.randn((27, 10)) # the embedding table
W1 = torch.randn((3 * 10, 200)) # the hidden layer from Chapter 5
W2 = torch.randn((200, 27)) # one output per vocabulary entry
emb = C[X].view(-1, 30) # three lookups, concatenated
h = torch.tanh(emb @ W1 + b1)
logits = h @ W2 + b2
loss = F.cross_entropy(logits, Y)Nota cosa è nuovo e cosa no. Il layer nascosto è quello del Capitolo 5, invariato; la loss è quella del Capitolo 4, invariata. Le novità sono la tabella di embedding all'inizio e un layer di output largo quanto il vocabolario del Capitolo 7 — e questa seconda cosa è la parte costosa di ogni modello linguistico mai costruito, perché un vocabolario reale ha 100.000 voci e questa moltiplicazione di matrici gira a ogni posizione.
Lo stesso codice, addestrato nello stesso modo, cambiando solo la dimensione della context window:
| contesto | parametri | loss di validazione | perplexity di validazione |
|---|---|---|---|
| conteggio, 1 carattere | 729 | 2,4546 | 11,642 |
| neurale, 1 carattere | 7.897 | 2,4577 | 11,678 |
| neurale, 3 caratteri | 11.897 | 2,1145 | 8,285 |
| neurale, 8 caratteri | 21.897 | 2,0506 | 7,773 |
La seconda riga è quella interessante. Una rete con un layer nascosto da 200 unità e undici volte più parametri della tabella dei conteggi rende esattamente quanto la tabella dei conteggi e non meglio. La capacità non è mai stata il limite. Un carattere di contesto permette una certa loss e nulla di ciò che ci innesti sopra può scendere al di sotto, perché l'informazione non c'è.
Dalle tre caratteri e la perplexity scende da 11,68 a 8,29 — un taglio del 29 %, comprato con 4.000 parametri extra. Batte il conteggio qui esattamente per il motivo diagnosticato prima: un modello a conteggi su contesti di tre caratteri ha bisogno di righe, la maggior parte vuote o con una sola osservazione, e le impara tutte separatamente. La rete condivide. Se a, e e i finiscono con righe di embedding simili, ciò che impara dopo bra si trasferisce a bre senza che abbia mai visto bre. Quel trasferimento è l'intero valore della tabella di embedding, ed è il divario tra la seconda e la terza riga.
I campioni migliorano di conseguenza:
deliah nellara joce kael quintis
salayson reety khyrmin mahnen madiaryxiaAncora non è una lista di nomi reali. Ma deliah, nellara e kael non stonerebbero in una lista del genere, e i mostri interminabili sono spariti: il più lungo di venti campioni del modello a conteggi è di diciannove lettere, il più lungo di venti di questo modello è di tredici.
Cosa c'è davvero dentro la tabella di embedding
Link alla sezione: Cosa c'è davvero dentro la tabella di embeddingLa tabella è : una riga di dieci numeri per carattere, tutte inizializzate casualmente e mosse solo dal gradient della loss sul carattere successivo. Nessuno ci ha messo dentro nulla. Quindi cosa ci è finito?
Lo strumento per chiederlo è la similarità coseno, cioè il prodotto scalare del Capitolo 1 con le lunghezze divise via:
Misura l'angolo tra due vettori e ignora le loro lunghezze, che è ciò che vuoi quando la lunghezza di una riga riflette quanto spesso è apparso il suo token invece di ciò che significa. Normalizza prima ogni vettore a lunghezza 1 — come fanno i sistemi reali, una volta, al momento dell'indicizzazione — e la similarità coseno è semplicemente il prodotto scalare.
Ecco i vicini più prossimi di alcuni caratteri nella tabella addestrata:
'c' -> 'k':+0.598 'j' -> 'z':+0.650 'i' -> 'y':+0.541
'u' -> 'e':+0.482 'a' -> 'h':+0.367 '.' -> 'q':+0.077Una parte è ciò che il folklore promette. c e k sono intercambiabili nei nomi, e lo stesso vale per i e y; j e z sono entrambe consonanti rare, perlopiù iniziali, che si comportano in modo simile. Il simbolo di confine . non è vicino a quasi nulla — 0,077 dalla lettera più vicina — perché è l'unico simbolo che marca una posizione invece di un suono.
E una parte no. Il vicino più prossimo di a è h, non un'altra vocale. Mediando su tutte le coppie:
mean cosine, vowel to vowel : +0.1889
mean cosine, consonant to consonant : +0.0765
mean cosine, vowel to consonant : -0.0042Le vocali si assomigliano più tra loro che alle consonanti, e l'effetto è reale ma piccolo. Testato contro 2.000 gruppi di cinque lettere scelti casualmente, 58 di quei gruppi si separano almeno altrettanto nettamente — un divario significativo a circa . Reale, quindi, ma nulla che somigli alla nitida isola geometrica che i racconti popolari sugli embeddings lasciano intendere.
Questa è la descrizione onesta di una tabella di embedding e vale la pena tenerla a mente per il resto del corso. Non è una mappa del significato. È un cambio di coordinate, appreso invece che progettato, il cui unico compito è rendere facile il lavoro del layer successivo — la stessa frase che il Capitolo 5 usava per il layer nascosto che piegava il piano per risolvere XOR. Qualunque struttura ci trovi è lì perché ha abbassato la loss, e la struttura che non abbassa la loss semplicemente non c'è.
word2vec, GloVe e l'aritmetica che tutti citano
Link alla sezione: word2vec, GloVe e l'aritmetica che tutti citanoSe la parte utile è la tabella, puoi puntare direttamente a quella. Questo è word2vec: tieni il lookup di embedding, butta via il modello linguistico.5
L'obiettivo skip-gram con negative sampling è una riga. Per una coppia reale (centro, contesto) estratta dal corpus, spingi in alto il loro prodotto scalare; per coppie finte estratte da una distribuzione di rumore, spingilo in basso:6
Questa è una classificazione binaria — «queste due parole sono davvero apparse insieme?» — ed è economica proprio perché non tocca mai l'intero vocabolario, il che rese pratico l'addestramento su miliardi di parole nel 2013. GloVe arriva a vettori simili dall'altra direzione, fattorizzando la matrice dei conteggi globali di co-occorrenza invece di scorrere esempi.7 Entrambi sono adattati esattamente alla statistica da cui è stata costruita la tabella dei conteggi. Sono conteggio, compresso.
Addestrati su text8 — 17.005.207 parole di Wikipedia in inglese, 71.290 delle quali compaiono almeno cinque volte, 100 dimensioni, tre passaggi — i vettori escono con la proprietà che li ha resi famosi:
king -> charles 0.700, son 0.693, queen 0.686, henry 0.669, throne 0.667
physics -> chemistry 0.672, electromagnetism 0.661, quantum 0.654, theoretical 0.624
guitar -> bass 0.733, vocals 0.732, acoustic 0.728, guitars 0.703, drums 0.685
three -> seven 0.892, two 0.877, one 0.875, five 0.871, four 0.870Nessuno ha fornito una categoria per strumenti o numerali. Ora la parte famosa: prendi king, sottrai man, aggiungi woman, e trova il vettore più vicino al risultato.
king - man + woman
nothing excluded : king 0.693, elizabeth 0.657, wife 0.629, woman 0.607
a, b, c excluded : elizabeth 0.657, wife 0.629, mary 0.607 (queen is 4th, 0.604)Il vettore più vicino a king - man + woman è king. Non è una stranezza di un esempio. Il set di valutazione di Mikolov pone domande della forma a : b :: c : ? — 8.869 semantiche (paris : france :: rome : italy) e 10.675 sintattiche (walking : walked :: swimming : swam) — e tra le 4.103 domande semantiche a cui questo vocabolario può rispondere, il vincitore è una delle tre parole in input nel 99,8 % dei casi. Le dimostrazioni pubblicate non lo menzionano, perché la regola di scoring standard elimina a, b e c prima di guardare. È una regola legittima, e sta facendo più lavoro dell'aritmetica:
| come viene scelta la risposta | semantico | sintattico |
|---|---|---|
| offset, con gli input esclusi (standard) | 17,0 % | 11,9 % |
| offset, senza escludere nulla | 0,1 % | 0,4 % |
vicino più prossimo di solo c, input esclusi | 13,1 % | 9,3 % |
vicino più prossimo di solo b, input esclusi | 2,3 % | 0,4 % |
La terza riga è quella su cui soffermarsi. Butta via a e b, non fare alcuna aritmetica, restituisci qualunque cosa sia più vicina a c — e mantieni il 77 % del punteggio semantico. Gran parte di ciò che sembra ragionamento analogico è prossimità più una regola che vieta le risposte ovvie, cioè ciò che Linzen ha misurato su vettori addestrati correttamente e ciò che i baseline sopra replicano.8 Questi vettori specifici sono piccoli — 17 milioni di parole contro i miliardi dietro i modelli pubblicati — quindi leggi le percentuali come una forma, non come lo stato dell'arte. La forma è ciò che sopravvive a ogni scala: l'aritmetica è reale, e molto più debole dell'unica dimostrazione che tutti citano.
Statici e contestuali: un vettore per parola, o uno per occorrenza
Link alla sezione: Statici e contestuali: un vettore per parola, o uno per occorrenzaTutto ciò che abbiamo visto finora ha un limite rigido incorporato nella struttura dati. Una tabella ha una riga per token. La parola bank ottiene un vettore, lo stesso in una frase su un fiume e in una frase su un mutuo — necessariamente, perché un lookup per id non può dipendere da nient'altro.
La soluzione è smettere di leggere il vettore dalla tabella e iniziare a calcolarlo dalla frase. Questo è un embedding contestuale, introdotto da ELMo nel 2018 e reso standard da BERT nello stesso anno.910 Misurati sul modello reale, i numeri sono più nitidi della spiegazione:
sentence A: "He sat on the bank of the river and watched the water go by."
sentence B: "She deposited the cheque at the bank on the corner of the street."
static vector for 'bank' (a row of the input embedding table)
cosine A vs B ........................ 1.000000
contextual vector for 'bank', layer by layer
layer | A vs B | A vs another river sentence | B vs another money sentence
0 | 0.9512 | 0.9512 | 0.9359
4 | 0.5647 | 0.8987 | 0.7716
9 | 0.4284 | 0.8699 | 0.7568
12 | 0.5278 | 0.8702 | 0.7335La prima riga è esatta, non approssimata: il vettore statico per bank è composto dagli stessi 768 numeri in entrambe le frasi, quindi il coseno è 1 per costruzione. Nove layer dopo, le due occorrenze stanno a 0,43, mentre bank in due diverse frasi sul fiume resta a 0,87. Nessuno ha etichettato un senso in alcun punto del processo; i sensi si sono separati perché separarli rende l'obiettivo di addestramento — indovinare un token nascosto dai suoi vicini — più facile da soddisfare.
Due dettagli ripagano l'attenzione. Il layer 0 è già 0,9512 invece di 1,0, perché sono stati aggiunti embedding di posizione e la parola sta in un punto diverso in ciascuna frase. E la similarità risale ai layer 11 e 12: i layer finali di un modello pretrained sono specializzati sul suo obiettivo di addestramento, e spesso non sono il posto migliore da cui prendere una rappresentazione.
Mostra dettagli
Opzionale: weight tying.
In bert-base-uncased la tabella di embedding è — 23.440.896 numeri, il 21,4 % dei 109.482.240 parametri del modello. In un modello linguistico piccolo la frazione è ancora più grande, ed è per questo che un trucco è quasi universale: la tabella di input e il layer di output che produce i logits sono la stessa matrice, usata una volta per lookup di riga e una volta trasposta.11 Il layer di output assegna già un vettore a ogni voce del vocabolario — prende un prodotto scalare contro ciascuna — e il tying dice che il vettore usato per leggere un token e il vettore usato per scriverlo dovrebbero essere lo stesso oggetto. Taglia parametri e migliora la perplexity insieme, cosa abbastanza rara da notare.
Un modello di embedding non è un modello linguistico
Link alla sezione: Un modello di embedding non è un modello linguisticoPer cercare in un corpus per significato ti serve un vettore per frase. Dati quelli, la ricerca è banale — questo è tutto il recupero semantico, e il Capitolo 19 riguarda tutto ciò che ci sta attorno:
E = normalise(embed(sentences)) # (200, d), every row of length 1
q = normalise(embed([query])) # (1, d)
scores = q @ E.T # one matrix multiply
top5 = scores[0].argsort()[::-1][:5]Quindi l'unica vera domanda è da dove venga embed. La mossa ovvia è prendere un modello linguistico pretrained, passare ogni frase al suo interno e fare la media dei vettori dei token. Ecco quel metodo contro quattro alternative, valutate in due modi: la correlazione di rango tra coseno e giudizi umani di similarità sulle 1.379 coppie del benchmark STS, e il recupero top-1 su un indice costruito dalle 200 coppie più fortemente parafrasate tra quelle — un lato di ogni coppia indicizzato, l'altro usato come query.
| come viene embedded la frase | correlazione di rango | top-1 su un indice da 200 frasi |
|---|---|---|
| sovrapposizione binaria di parole (nessun modello) | 0,5500 | 89,0 % |
| media dei vettori statici addestrati sopra | 0,5263 | 85,5 % |
BERT, il token [CLS] | 0,2030 | 67,0 % |
| BERT, media dei vettori dei token | 0,4729 | 84,0 % |
| MiniLM, addestrato contrastivamente | 0,8203 | 92,0 % |
Leggi le tre righe centrali rispetto alle prime due. Un transformer pretrained da 109 milioni di parametri, usato nel modo ovvio, è peggiore nel giudicare la similarità tra frasi rispetto al contare quante parole due frasi condividono — e peggiore della media dei vettori text8 a 100 dimensioni addestrati un momento fa. Il token [CLS], che i tutorial raccomandano ancora perché BERT è stato pretrained con un obiettivo a livello di frase collegato a esso, è peggiore della metà di quello.
Questo non è un difetto di BERT. È l'obiettivo. Un modello linguistico viene addestrato affinché i suoi stati nascosti predicano un token; lì nulla chiede che due parafrasi finiscano vicine, e nulla premia una geometria in cui il coseno significhi «stesso significato». L'ultima riga è un modello grande un quinto (22.713.216 parametri) addestrato su una loss completamente diversa: apprendimento contrastivo, in cui gli esempi sono coppie — una domanda e la sua risposta, una frase e la sua parafrasi — e l'obiettivo avvicina le coppie vere mentre allontana i negativi campionati. Questo è il contributo di Sentence-BERT e l'origine dell'intera industria degli embedding model.12 Dense Passage Retrieval applica la stessa ricetta direttamente alla ricerca, con un encoder per le query e uno per i passaggi.13
Quindi, la regola pratica:
Un modello di embedding non è un modello linguistico con l'ultimo layer rimosso. È un modello diverso su un obiettivo diverso, di solito molto più piccolo, il cui coseno significa ciò che vuoi che significhi perché è stato addestrato su coppie in cui quello era il target. La tabella sopra è il costo di sostituire l'uno all'altro.
E la famiglia fallisce sull'ordine delle parole. «The dog bit the man» e «the man bit the dog» hanno bag of words identiche, quindi la sovrapposizione di parole e la media dei vettori statici danno loro coseno esattamente 1,000000, e il BERT con mean pooling, che vede la posizione, arriva comunque quasi lì — e il MiniLM addestrato contrastivamente le mette comunque a 0,979. Se il tuo task di retrieval dipende da chi ha fatto cosa a chi, nessuna soglia sul coseno ti salverà.
Il Capitolo 19 costruisce un sistema di retrieval di produzione su queste basi e arriva a un cut-off concreto sul coseno. L'ultima misura in questo capitolo è ciò che rende un numero del genere difendibile invece che magico.
La maledizione della dimensionalità, in una tabella
Link alla sezione: La maledizione della dimensionalità, in una tabellaGli embeddings reali hanno centinaia o migliaia di componenti, e lassù le distanze si comportano in modo strano. Prendi 1.000 punti casuali nel cubo unitario di dimensioni e guarda il rapporto tra la distanza massima e minima tra due qualunque di essi:
| dimensioni | coppia più vicina | coppia più lontana | rapporto |
|---|---|---|---|
| 2 | 0,0007 | 1,3612 | 1921,66 |
| 10 | 0,2361 | 2,3397 | 9,91 |
| 100 | 3,0047 | 5,1752 | 1,72 |
| 1.000 | 11,7809 | 14,0306 | 1,19 |
| 10.000 | 39,6152 | 42,0125 | 1,06 |
In diecimila dimensioni la coppia di punti più lontana è solo il 6 % più distante della coppia più vicina. Tutto è grossomodo equidistante da tutto il resto, «vicino più prossimo» smette di portare molta informazione, e questa è la maledizione della dimensionalità — oltre a essere uno dei motivi per cui i grandi database vettoriali non fanno ricerca esatta del nearest neighbour. L'altro lato della stessa medaglia è ciò che rende utilizzabili le soglie sul coseno: misurato su mille coppie di vettori unitari casuali, il coseno medio sta a in 100 dimensioni e a in 768, con deviazioni standard di 0,0968 e 0,0357 — e in 768 dimensioni solo lo 0,2 % delle coppie casuali supera 0,1 in valore assoluto. Una similarità misurata di 0,4 quindi non è «simile al 40 %»; è molto al di fuori di qualunque cosa prodotta dal caso, ed è per questo che soglie tra 0,3 e 0,7 separano segnale da rumore invece di stare nel mezzo.
Dove si va adesso
Link alla sezione: Dove si va adessoIl modello in questo capitolo legge un numero fisso di caratteri precedenti, fa il lookup di ciascuno e incolla insieme i risultati in ordine. Questo design ha due problemi, e sono lo stesso problema.
Guarda di nuovo la tabella del contesto: passare da tre caratteri a otto ha quasi raddoppiato i parametri e ha comprato 0,06 nats. Il costo cresce linearmente con il contesto — ogni posizione extra ha bisogno della propria lastra della prima matrice di pesi — e il beneficio no. Spingilo a mille token e il primo layer da solo pesa più del resto del modello, perlopiù speso su posizioni che non contano per una data previsione.
Che è il secondo problema: il modello non ha modo di decidere quali dei token precedenti contano. La posizione due ottiene i propri pesi e la posizione sette i propri, permanentemente, qualunque cosa contengano. Quando il modello sta scrivendo nell, il carattere decisivo è quello immediatamente precedente. Quando una frase contiene un pronome, la parola che ne fissa il referente può essere quaranta token indietro — e nessuno slot fisso può essere assegnato a «quaranta indietro», perché la volta dopo saranno sei.
Quello che vogliamo è un modello che calcoli, per ogni previsione, quanto debba contare ogni token precedente — pesi sul contesto prodotti dal contenuto invece che fissati dal layout. Scrivilo con cura e inizia come qualcosa di del tutto ordinario: una media sui token precedenti. Poi lascia che i pesi di quella media vengano appresi, e lascia che dipendano da quale token sta facendo la domanda.
Questa è attention, ed è il Capitolo 9.
Fonti e metodo
Link alla sezione: Fonti e metodoVale la pena leggere insieme anche: il capitolo 3 di Speech and Language Processing di Jurafsky e Martin, che tratta modelli n-gram, smoothing e perplexity con molta più cura di quanta ne consenta lo spazio qui, incluso perché interpolazione e back-off battono l'aggiungere uno; gli appunti Stanford CS229 §17.1–17.2 per il modelling linguistico dal lato probabilistico; e l'articolo di Linzen sopra, che è breve e merita di essere letto per intero.
Riferimenti
Link alla sezione: Riferimenti-
L'esempio di generazione di nomi, il dataset e la progressione da una tabella di conteggi a una rete in stile Bengio seguono la serie building makemore di Andrej Karpathy, le cui prime due parti sono il miglior complemento a questo capitolo. ↩
-
Shannon, C. E. Prediction and Entropy of Printed English. Bell System Technical Journal 30(1), pp. 50–64 (1951). Soggetti umani che indovinano la lettera successiva dell'inglese, e la misura originale dei bit per carattere. ↩
-
Shannon, C. E. A Mathematical Theory of Communication. Bell System Technical Journal 27 (1948). Il teorema della codifica di sorgente, e l'identificazione della predizione con la compressione. ↩
-
Bengio, Y., Ducharme, R., Vincent, P. and Jauvin, C. A Neural Probabilistic Language Model. Journal of Machine Learning Research 3, pp. 1137–1155 (2003). L'architettura usata sopra: un embedding per parola, concatenato su una finestra fissa, attraverso un layer nascosto, fino a una softmax sul vocabolario. ↩
-
Mikolov, T., Chen, K., Corrado, G. and Dean, J. Efficient Estimation of Word Representations in Vector Space. arXiv:1301.3781 (2013). CBOW e skip-gram, e il set di analogie usato sopra. ↩
-
Mikolov, T., Sutskever, I., Chen, K., Corrado, G. and Dean, J. Distributed Representations of Words and Phrases and their Compositionality. arXiv:1310.4546 (2013). Negative sampling, subsampling delle parole frequenti, e la distribuzione di rumore elevata alla potenza 3/4 usata sopra. ↩
-
Pennington, J., Socher, R. and Manning, C. GloVe: Global Vectors for Word Representation. EMNLP 2014. Vettori di parole da una fattorizzazione della matrice globale di co-occorrenza invece che da finestre locali in streaming. ↩
-
Linzen, T. Issues in evaluating semantic spaces using word analogies. RepEval 2016, arXiv:1606.07736. La fonte dei baseline senza offset replicati sopra. ↩
-
Peters, M. et al. Deep contextualized word representations. arXiv:1802.05365 (2018). ELMo: un vettore per occorrenza, calcolato da un modello linguistico bidirezionale. ↩
-
Devlin, J., Chang, M.-W., Lee, K. and Toutanova, K. BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv:1810.04805 (2018). Il modello misurato nell'esperimento su bank. ↩
-
Press, O. and Wolf, L. Using the Output Embedding to Improve Language Models. arXiv:1608.05859 (2016), e Inan, H., Khosravi, K. and Socher, R. Tying Word Vectors and Word Classifiers. arXiv:1611.01462 (2016). Due argomentazioni indipendenti per lo stesso trucco. ↩
-
Reimers, N. and Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. arXiv:1908.10084 (2019). La sua misura iniziale — BERT con mean pooling che rende peggio dei vettori statici mediati sulla similarità tra frasi — è ciò che la tabella sopra riproduce. ↩
-
Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906 (2020). Addestramento contrastivo di un retriever a due encoder; l'antenato diretto dello stack di retrieval del Capitolo 19. ↩