Patchare i Pesi LLM a Mano

Ho scritto un’implementazione da zero di ROME — modifica di modelli di rango uno — usando nient’altro che torch e transformers. L’obiettivo: riscrivere un singolo fatto all’interno di GPT-2 Medium con una sola addizione di matrice, e vedere cosa succede a tutto il resto che il modello conosce.

Ho eseguito quattro modifiche. Tre sono state chirurgiche. La quarta ha rivelato che la “chirurgicità” di ROME è estremamente sensibile ai dettagli di implementazione che non si considerano necessariamente in anticipo.

Le Quattro Modifiche

Ho scelto fatti che GPT-2 Medium conosce davvero con sicurezza (P > 0,5 sulla risposta corretta) in quattro domini diversi:

SoggettoFatto originaleModificato in
Harvard Universitynel MassachusettsCalifornia
Googlein CaliforniaTexas
Tacosdal MessicoGiappone
Statua della Libertàa New YorkLas Vegas

Tutte e quattro le modifiche hanno centrato il loro obiettivo. Dopo l’aggiornamento, il modello ha previsto la nuova risposta con probabilità ≥ 0,98 per il prompt di modifica esatto. Quindi ROME stesso funziona. Ciò che varia è tutto il resto.

Le Tre Modifiche Pulite

Tre delle quattro si sono comportate all’incirca come il paper ROME prevedrebbe. Ecco cosa è successo ai fatti non correlati dopo ogni modifica:

Tacos → Giappone (norma di aggiornamento: 9% della norma dei pesi)

ControlloDopo
Sushi dal Giappone✓ invariato
Pizza dall’Italia✓ invariato
Ramen dal Giappone✓ invariato
Burritos dal Messico→ Giappone

Solo i Burritos, il vicino più prossimo nello spazio del cibo messicano, è stato trascinato.

Harvard → California (norma di aggiornamento: 13%)

ControlloDopo
MIT nel Massachusetts✓ invariato (ancora sfumato)
Capitale del Massachusetts = Boston✓ invariato
Boston nel Massachusetts✓ invariato
Yale nel Connecticut→ California

Yale — il vicino più prossimo di Harvard nello spazio dell’Ivy League — è andato con lei. Nient’altro si è mosso.

Statua della Libertà → Las Vegas (norma di aggiornamento: 16%)

ControlloDopo
Times Square a New York✓ invariato
New York City a New York✓ invariato
Empire State Buildingconteso (New 0,49 vs Las 0,26)
Liberty Bell a Philadelphia→ Las Vegas

Due monumenti hanno vacillato. Il Liberty Bell è particolarmente divertente — non è a NY, ma condivide la parola “Liberty” e GPT-2 li ha confusi.

Quindi: modifiche dirette pulite, danno collaterale limitato, grandezze di aggiornamento ragionevoli (9–16%).

Il modello ha anche inventato allegramente storie alternative corrispondenti. La mia preferita: dopo aver spostato Harvard in California, alla domanda quando fu fondata Harvard, il modello ha risposto “1776 dal padre gesuita francese Charles de Montesquieu.” Frase completa, internamente coerente, completamente falsa. Sono i prior del modello (“Harvard è prestigiosa, i luoghi famosi hanno fondatori famosi”) che riempiono il vuoto dove c’era un fatto reale.

La Disordinata: Google

La modifica di Google è andata male. La mia prima esecuzione ha riportato una norma di aggiornamento del 117% — la modifica di rango 1 era più grande della norma della matrice dei pesi stessa — e ha fatto collassare l’intero cluster tecnologico californiano:

Ho scritto un post del blog con questo come scoperta principale. Poi ci ho pensato di più e ho realizzato che 117% era sospetto. Una modifica di rango 1 non dovrebbe essere più grande di ciò che sta modificando.

Debug di Google

Due cose si sono rivelate errate.

Problema 1: la mia covarianza era mal preparata

La formula di aggiornamento di ROME si basa su C, la covarianza dei vettori intermedi (post-GELU) al livello target:

u = torch.linalg.solve(C + lambda * I, h_star)
delta_W = (u / (h_star @ u)).unsqueeze(1) @ (v_star - W @ h_star).unsqueeze(0)

La direzione C⁻¹ @ h* è ciò che rende la modifica selettiva — è allineata con h* ma ortogonale alle chiavi tipiche. Se C è mal condizionata, C⁻¹ @ h* esplode nelle direzioni a basso autovalore, e l’aggiornamento diventa enorme.

Stavo stimando C da 200 campioni di WikiText — circa 10.600 token. Per una matrice di covarianza 4096×4096, sono ~2,5 campioni per dimensione. La matrice era gravemente deficiente in rango. Avevo una regolarizzazione 1e-4 * I, che era ben lontana dall’essere sufficiente.

Soluzione: 2000 campioni (~118.000 token, ~29× per dimensione) e regolarizzazione scalata per traccia (1e-2 × mean(diag(C))).

Risultato per la stessa modifica: la norma di aggiornamento è scesa dal 117% al 50,5%. Migliore condizionamento, metà della grandezza dell’aggiornamento.

Ma 50% è ancora enorme, e i controlli erano ancora rotti:

ControlloDopo (covarianza corretta)
HQ AppleTexas (1,00)
HQ MicrosoftTexas (0,99)
Silicon ValleyTexas (0,92)
StanfordTexas (0,97)

Quindi parte della “catastrofe” era un bug — ma non tutta. La perdita del cluster era reale.

Problema 2: la posizione conta, molto

“Google” è un singolo token BPE. Nel mio prompt — “Google is a company headquartered in the state of” — è alla posizione 0. Ciò significa che h* (il vettore intermedio usato per la modifica) viene calcolato da un token che non ha visto alcun contesto precedente. È una rappresentazione nuda.

E se mettessi Google da qualche parte diversa dalla posizione 0? Ho cambiato il prompt in “The technology company known as Google is headquartered in the state of” — ora Google è alla posizione 5, con “The technology company known as” come contesto precedente.

Stesso obiettivo di modifica. Stessa nuova covarianza. Risultato:

Quindi solo dando al token soggetto un po’ di contesto prima di calcolare h*, la modifica diventa abbastanza chirurgica da far sopravvivere Silicon Valley e Stanford. Apple e Microsoft sono state ancora spostate, quindi esiste una vera perdita adiacente a Google — ma niente come l’apocalisse originale.

Cosa Insegna Davvero

Volevo che questo post fosse “guarda, ROME non può modificare i concetti hub — guarda come Google ha distrutto tutto.” Quel frame era sbagliato. La verità è più interessante e meno drammatica:

  1. ROME è sensibile ai dettagli di implementazione che non vedi nel paper. Il conteggio dei campioni per la covarianza. La forza della regolarizzazione. Dove si trova il token soggetto nel prompt. Sbagliare uno qualsiasi di questi e il tuo “danno collaterale catastrofico” potrebbe essere il tuo stesso codice.

  2. I soggetti a token singolo in posizione 0 sono il caso peggiore. Il loro h* è il meno discriminativo, e qualsiasi imprecisione numerica in C si inverte in un aggiornamento sovradimensionato. Se vuoi una modifica pulita, dai al soggetto del contesto precedente.

  3. La perdita di concetti hub è reale ma modesta. Anche con una covarianza corretta e un contesto precedente, modificare Google sposta leggermente Apple e Microsoft. “Google” si trova in un denso vicinato semantico, e la modifica di rango 1 tocca quel vicinato. Puoi ridurre questo di un altro 2–4× con la distribuzione multi-strato in stile MEMIT, ma non puoi eliminarlo completamente.

  4. La norma di aggiornamento è una diagnostica affidabile. Sotto il 15% della norma del peso: probabilmente a posto. Sopra il 50%: probabilmente rotto, o a causa di un bug o perché stai modificando un hub. Controlla prima di fidarti della modifica.

Le Confabulazioni Sono Reali Comunque

In ogni modifica riuscita, il modello ha inventato fatti alternativi coerenti per corrispondere:

Questi non sono rumori — sono il modello che applica i suoi prior al fatto modificato. Una volta che crede che Google sia texana, “fondata da Steve Jobs” non è un’allucinazione casuale; è la migliore ipotesi del modello su come dovrebbe essere la storia del fondatore di una famosa azienda tecnologica texana.

La conoscenza all’interno di un modello linguistico non è una lista di fatti indipendenti. È un grafo di fatti che si rinforzano a vicenda. Modifica un nodo e il grafo produce una nuova regione coerente (e completamente falsa) attorno ad esso.

La Configurazione

L’intera cosa è ~500 righe in pochi file: tracciamento causale, stima della covarianza, discesa del gradiente di v*, l’aggiornamento del peso di rango 1 e uno script end-to-end per le quattro modifiche.

Dipendenze: torch, transformers, datasets. Niente di specifico per ROME.

Gira su CPU. Ogni modifica richiede ~3 minuti con covarianza a 200 campioni, ~15 minuti con covarianza a 2000 campioni. L’impostazione con covarianza corretta vale l’attesa.