Salta ai contenuti

Matrici come trasformazioni

Una matrice non è una tabella di numeri: è la macchina che agisce sui vettori. Capirla così rende ovvio cosa fanno un layer Dense, l’attention, una proiezione PCA.

Quando un developer apre il codice di una rete neurale e legge y = self.linear(x), sotto sta accadendo una sola cosa: un vettore x viene moltiplicato per una matrice W (e sommato a un bias). Quel linear può essere il proiettore Q dell’attention, l’output head di un transformer, l’ultimo strato di un classificatore. Sempre la stessa operazione, sempre la stessa idea: una matrice trasforma il vettore di input in un nuovo vettore.

L’errore frequente è pensare alla matrice come a una griglia di numeri, da memorizzare e manipolare con regole sintattiche (“riga per colonna”). Quella vista funziona per fare i conti, ma non spiega niente. Spiega perché una rete profonda senza non-linearità collassa a una sola matrice. Spiega perché QKV sono “tre angolazioni” della stessa rappresentazione. Spiega perché l’embedding lookup è formalmente identico a una moltiplicazione matrice-vettore. Spiega perché PCA è la trasformazione lineare ottima e niente di più. Spiega perché RoPE può ruotare i vettori posizionali e ottenere proprietà eleganti.

Questo capitolo costruisce la vista che serve. Le matrici sono rappresentazioni concrete di trasformazioni lineari, scelte una volta fissata una base. Le colonne di una matrice dicono dove vengono mandati i vettori della base. Il prodotto matrice-matrice modella la composizione di due trasformazioni, e dalla composizione discende la non commutatività. Da qui escono in modo naturale rango, kernel, determinante, inversa, condizionamento. Sono le stesse parole che usano i textbook di algebra lineare; il punto è che ognuna ha un significato operativo che il lettore può tradurre nel codice di una rete.

Il termine “matrix” in senso matematico moderno lo conia James Joseph Sylvester (matematico inglese, 1814-1897, una vita divisa fra Inghilterra e Stati Uniti, fondatore dell’American Journal of Mathematics) in un breve articolo del 1850 sul Philosophical Magazine, Additions to the articles “On a New Class of Theorems” and “On Pascal’s Theorem”. La parola viene dal latino: “matrix”, grembo, utero, contenitore. Per Sylvester una matrix è la “scatola rettangolare” di numeri da cui si possono estrarre minori e determinanti. Il termine è descrittivo, non ancora algebrico: non c’è ancora una teoria del prodotto di matrici.

La sistemazione algebrica completa arriva otto anni dopo. Arthur Cayley (matematico inglese, 1821-1895, amico stretto di Sylvester, lavorò come avvocato per quattordici anni prima di diventare professore a Cambridge) pubblica nel 1858 A Memoir on the Theory of Matrices nelle Philosophical Transactions of the Royal Society of London. È il paper fondativo. Cayley definisce la somma, il prodotto, l’inversa di matrici quadrate; tratta in dettaglio i casi 2x2 e 3x3; enuncia, e dimostra per i casi piccoli, il teorema che oggi chiamiamo di Cayley-Hamilton (ogni matrice quadrata annulla il proprio polinomio caratteristico). Il prodotto definito da Cayley è esattamente il “righe per colonne” che si insegna oggi, ma la motivazione è già quella moderna: rendere coerente il calcolo composto di sostituzioni lineari.

L’identificazione esplicita “matrice = trasformazione lineare rispetto a una base” è successiva. Diventa standard nei testi del Novecento (Hermann Grassmann e Giuseppe Peano avevano nel frattempo costruito gli spazi vettoriali astratti, vedi vettori-spazi). Birkhoff e MacLane, A Survey of Modern Algebra (1941), e i testi di Halmos, fissano il modo moderno di presentare la materia: prima la trasformazione lineare astratta, poi la matrice come sua rappresentazione. È la presentazione che adottiamo qui, perché è quella che paga di più quando si passa dal manuale al codice.

Una nota di onestà storica. La regola “righe per colonne” non è una scoperta originale di Cayley nel senso che era già implicita nei lavori sulle sostituzioni lineari di Augustin-Louis Cauchy (matematico francese, 1789-1857) e Carl Friedrich Gauss (matematico tedesco, 1777-1855) di inizio Ottocento. Cayley è chi le dà struttura algebrica autonoma, separandola dalla teoria delle equazioni e dei determinanti. Questa è una filiazione documentata, non un’analogia: i moderni textbook citano Cayley 1858 come fonte canonica.

flowchart LR
  S[Sylvester 1850
conia il termine 'matrice'] --> C[Cayley 1858
Memoir on the Theory of Matrices]
  C --> M[Novecento
matrice = rappresentazione di una trasformazione lineare]
  M --> ML[Oggi
linear layer, attention QKV, embedding]

Figura 1 — concept graph: matrix as transformation, history Sylvester 1850 to Cayley 1858 to modern presentation

Tre angoli, tutti utili, da tenere insieme. Il primo costruisce l’intuizione geometrica, il secondo la coordinata-per-coordinata, il terzo la più importante per il deep learning.

Una matrice quadrata 2x2 A agisce sui punti del piano R^2. Pesco un punto x = (1, 0); lo do in pasto ad A; ne esce un altro punto Ax. Faccio lo stesso con tutti i punti del piano. Ho una funzione T:R2>R2T: R^2 -> R^2 che prende ogni punto e lo manda da qualche parte. Una trasformazione.

La proprietà che rende questa trasformazione “lineare” è una sola, ed è la più importante del capitolo:

T(au+bv)=aT(u)+bT(v)T(a u + b v) = a T(u) + b T(v)

per ogni coppia di vettori u, v e ogni coppia di scalari a, b. In parole: trasformare prima e combinare dopo dà lo stesso risultato che combinare prima e trasformare dopo. Questo è ciò che separa le trasformazioni lineari dal resto. Una rotazione è lineare. Una scala è lineare. Uno shear (taglio obliquo) è lineare. Una traslazione non è lineare (manda l’origine fuori dall’origine). Elevare al quadrato non è lineare. Applicare una sigmoide non è lineare.

Geometricamente, una trasformazione lineare ha tre proprietà visibili a occhio: l’origine resta ferma; le rette restano rette (non si curvano); il reticolo cartesiano resta un reticolo (le linee parallele restano parallele e equispaziate, anche se ruotate o stirate). Tutte e tre derivano dalla stessa formula T(au + bv) = aT(u) + bT(v).

flowchart LR
  S[Sylvester 1850
conia il termine 'matrice'] --> C[Cayley 1858
Memoir on the Theory of Matrices]
  C --> M[Novecento
matrice = rappresentazione di una trasformazione lineare]
  M --> ML[Oggi
linear layer, attention QKV, embedding]

Figura 1 — unit grid before and after a linear transformation; lines stay lines, origin fixed

Secondo angolo: la tabella di pesi righe per colonne

Sezione intitolata “Secondo angolo: la tabella di pesi righe per colonne”

L’altro angolo, quello che arriva per primo a chi ha studiato algebra a scuola, è la matrice come tabella m x n di numeri:

A=[[a11a12...a1n][a21a22...a2n][...][am1am2...amn]]A = [[ a_11 a_12 ... a_1n ] [ a_21 a_22 ... a_2n ] [ ... ] [ a_m1 a_m2 ... a_mn ]]

La regola che si memorizza al liceo: per moltiplicare A per un vettore x=(x1,...,xn)Tx = (x_1, ..., x_n)^T, la i-esima componente del risultato è il prodotto scalare della riga i di A con x:

(Ax)i=ai1x1+ai2x2+...+ainxn(A x)_i = a_i1 * x_1 + a_i2 * x_2 + ... + a_in * x_n

È una procedura di calcolo, non una spiegazione. Funziona, ma non racconta perché un layer di una rete neurale fa quello che fa, né perché QKV abbiano la struttura che hanno. Per quello serve il terzo angolo.

Terzo angolo: le colonne dicono dove finiscono i vettori della base

Sezione intitolata “Terzo angolo: le colonne dicono dove finiscono i vettori della base”

Considera la base canonica di R^n: i vettori e1=(1,0,...,0)Te_1 = (1, 0, ..., 0)^T, e2=(0,1,0,...,0)Te_2 = (0, 1, 0, ..., 0)^T, e così via. Calcoliamo AejA e_j. Per la regola riga-per-colonna, il risultato è esattamente la j-esima colonna di A:

Aej=colonnaj(A)A e_j = colonna_j(A)

Detto in modo operativo: le colonne di una matrice sono le immagini dei vettori della base. Se conosco dove A manda e1,...,ene_1, ..., e_n, conosco la matrice. E poiché ogni vettore x si scrive come combinazione lineare x1e1+...+xnenx_1 e_1 + ... + x_n e_n, per linearità:

Ax=x1(Ae1)+x2(Ae2)+...+xn(Aen)=x1c1+x2c2+...+xncnA x = x_1 (A e_1) + x_2 (A e_2) + ... + x_n (A e_n) = x_1 c_1 + x_2 c_2 + ... + x_n c_n

Dove cjc_j è la j-esima colonna di A. Cioè: moltiplicare A per x significa combinare le colonne di A, usando le componenti di x come pesi.

Questo è il risultato più produttivo del capitolo. Da solo basta a vedere cosa sia il rango (quante colonne sono linearmente indipendenti, cioè quante “direzioni vere” produce la trasformazione), cosa sia l’immagine (lo spazio generato dalle colonne), perché un layer Dense con outfeatures=1out_features = 1 produca solo combinazioni di una colonna (uno scalare per posizione), e così via.

flowchart LR
  S[Sylvester 1850
conia il termine 'matrice'] --> C[Cayley 1858
Memoir on the Theory of Matrices]
  C --> M[Novecento
matrice = rappresentazione di una trasformazione lineare]
  M --> ML[Oggi
linear layer, attention QKV, embedding]

Figura 1 — Ax as weighted sum of columns of A, with x components as weights

Stabiliti i tre angoli, formalizziamo gli oggetti. Lavoriamo in RnR^n per concretezza; tutto si estende a spazi vettoriali astratti senza variazioni sostanziali.

Una funzione T:Rn>RmT: R^n -> R^m è lineare se T(au + bv) = a T(u) + b T(v) per ogni u, v, a, b. Conseguenze immediate:

  • T(0) = 0. Basta porre a = b = 0.
  • T è completamente determinata dalle immagini di una qualsiasi base. Conoscere T(e1),...,T(en)T(e_1), ..., T(e_n) basta a conoscere T(x) per ogni x, per linearità.
  • L’insieme delle trasformazioni lineari Rn>RmR^n -> R^m è esso stesso uno spazio vettoriale: si possono sommare e moltiplicare per scalari.

Fissate la base canonica di RnR^n e quella di RmR^m, la matrice di T è l’unica matrice m x n la cui j-esima colonna è T(ej)T(e_j). Per costruzione:

T(x)=AxT(x) = A x

per ogni x. La matrice A rappresenta la trasformazione T. Cambiando base cambia la matrice; la trasformazione astratta resta la stessa. Su questa distinzione torneremo con il cambio di base.

Due trasformazioni si compongono. Se TB:Rn>RpT_B: R^n -> R^p ha matrice B e TA:Rp>RmT_A: R^p -> R^m ha matrice A, la composizione TAoTB:Rn>RmT_A o T_B: R^n -> R^m definita da (TAoTB)(x)=TA(TB(x))(T_A o T_B)(x) = T_A(T_B(x)) è ancora lineare, e la sua matrice si ottiene proprio così:

(TAoTB)(x)=TA(TB(x))=A(Bx)=(AB)x(T_A o T_B)(x) = T_A(T_B(x)) = A (B x) = (A B) x

dove A B è il prodotto matriciale. La regola “righe per colonne” deriva da questa identità, non è un punto di partenza. Detta in modo operativo: si applica prima la trasformazione di destra (B), poi quella di sinistra (A). Convenzione di scrittura matematica, opposta all’ordine in cui si leggono i nomi.

Da qui cadono due conseguenze fondamentali.

Non commutatività. In generale A B è diverso da B A. Esempio in 2D. Ruotare di 90° antiorari poi fare uno shear orizzontale di parametro 1:

R90=[[0,1],[1,0]]H=[[1,1],[0,1]]HR90=[[1,1],[1,0]]R90H=[[0,1],[1,1]]R_90 = [[0, -1], [1, 0]] H = [[1, 1], [0, 1]] H R_90 = [[1, -1], [1, 0]] R_90 H = [[0, -1], [1, 1]]

Sono diverse. Geometricamente è ovvio: ruotare un quadrato e poi inclinarlo non dà la stessa figura di inclinarlo e poi ruotarlo. La non commutatività è la regola, non l’eccezione. I casi commutativi (matrici diagonali fra loro, una matrice con la sua inversa, matrici simultaneamente diagonalizzabili) sono speciali.

Associatività. A (B C) = (A B) C. Le composizioni si raggruppano come si vuole, ma l’ordine sequenziale non si tocca.

flowchart LR
  S[Sylvester 1850\nconia il termine 'matrice'] --> C[Cayley 1858\nMemoir on the Theory of Matrices]
  C --> M[Novecento\nmatrice = rappresentazione di una trasformazione lineare]
  M --> ML[Oggi\nlinear layer, attention QKV, embedding]

Figura 1 — composing two transformations in 2D: rotate then shear vs shear then rotate, side by side

La trasposta ATA^T di una matrice m x n è la matrice n x m ottenuta scambiando righe e colonne. Proprietà:

  • (AB)T=BTAT(A B)^T = B^T A^T (l’ordine si inverte).
  • (AT)T=A(A^T)^T = A.
  • Per il prodotto scalare standard, <Ax,y>=<x,ATy><Ax, y> = <x, A^T y>. La trasposta è la “aggiunta” rispetto a quel prodotto scalare.

Una matrice è simmetrica se A=ATA = A^T. Le matrici simmetriche hanno autovalori reali, autovettori ortogonali, decomposizione spettrale particolarmente semplice — torneremo su questo.

Per una matrice quadrata A di ordine n, l’inversa A1A^-1 è (se esiste) l’unica matrice tale che:

AA1=A1A=InA A^-1 = A^-1 A = I_n

dove InI_n è la matrice identità (1 sulla diagonale, 0 altrove; rappresenta la trasformazione che lascia ogni vettore fermo). Quando esiste? Le seguenti condizioni sono tutte equivalenti, ed è un teorema standard di algebra lineare (“invertible matrix theorem”):

  • A è invertibile;
  • det(A)!=0det(A) != 0;
  • rank(A)=nrank(A) = n;
  • ker(A)=0ker(A) = {0} (l’unico vettore che A manda in zero è zero stesso);
  • le colonne di A sono linearmente indipendenti;
  • A è biiettiva come funzione Rn>RnR^n -> R^n.

La dimostrazione classica della catena di equivalenze è in qualunque manuale di algebra lineare. Per il nostro scopo basta ricordare che invertibilità, rango pieno, determinante non nullo e indipendenza lineare delle colonne sono lo stesso fatto detto in cinque modi diversi. È un’equivalenza vera, non un’analogia.

In pratica numerica raramente si calcola A1A^-1 esplicitamente. Per risolvere A x = b si usa una fattorizzazione (LU, QR, Cholesky se simmetrica positiva definita) e si fa back-substitution. Il motivo è doppio: efficienza (il calcolo dell’inversa è più costoso di una sola back-substitution) e stabilità numerica (l’inversa esplicita amplifica l’errore di arrotondamento più di un solve).

Tre sottospazi associati a A.

Immagine (image, range, column space). L’insieme degli y per cui esiste un x con A x = y. Equivale al sottospazio di RmR^m generato dalle colonne di A — esattamente la vista del terzo angolo: A x è una combinazione lineare delle colonne, quindi l’insieme delle uscite possibili è lo span delle colonne.

Nucleo (kernel, null space). L’insieme degli x in RnR^n tali che A x = 0. È un sottospazio di RnR^n. Se è ridotto al solo zero, A è iniettiva.

Rango. La dimensione dell’immagine. Equivale al numero massimo di colonne linearmente indipendenti — e, per un risultato classico, al numero massimo di righe linearmente indipendenti (rank riga = rank colonna; non ovvio, ma vero).

Vale il teorema rank-nullity:

dim(kerA)+rank(A)=ndim(ker A) + rank(A) = n

dove n è il numero di colonne (cioè la dimensione dell’input). In parole: i gradi di libertà che A schiaccia (kernel) più i gradi di libertà che A preserva (rango) fanno sempre la dimensione dell’input. È un teorema, dimostrabile in poche righe contando con cura le basi. Lo marchiamo come tale.

Significato operativo in ML: una matrice di pesi W con rango deficiente significa che il layer sta collassando informazione. Ci sono direzioni dell’input che W non distingue dall’altro. È una patologia diagnosticabile con SVD (vedi sotto) e capita in pratica: neuroni “morti” dopo training, embedding tying che vincola due matrici a essere uguali, dropout che azzera righe, low-rank adaptation (LoRA) che impone esplicitamente rango basso.

Per una matrice quadrata A, il determinante det(A)det(A) è un numero che riassume due cose:

  • det(A)|det(A)| è il fattore di scala dei volumi. Se prendi un cubo unitario in RnR^n e lo trasformi con A, il volume del cubo trasformato è det(A)|det(A)|. In R2R^2 si parla di aree, in R3R^3 di volumi.
  • Il segno di det(A)det(A) indica se la trasformazione preserva l’orientazione (+) o la inverte (-).

Proprietà chiave:

  • det(I)=1det(I) = 1.
  • det(AB)=det(A)det(B)det(A B) = det(A) det(B). Se due trasformazioni in fila scalano i volumi di fattori dAd_A e dBd_B, la composizione li scala di dAdBd_A d_B.
  • det(AT)=det(A)det(A^T) = det(A).
  • det(A1)=1/det(A)det(A^-1) = 1 / det(A). Quindi se A è invertibile, det(A)!=0det(A) != 0. Vale anche il viceversa.
  • det di una rotazione è 1; det di una riflessione è -1; det di una scala assiale (s1,...,sn)(s_1, ..., s_n) è il prodotto s1...sns_1 ... s_n.

unit square transformed by 2x2 matrix, area equals |det|, orientation by sign

In ML pratico, il determinante esplicito serve di rado, ma quando serve è dirimente. Nei normalizing flow (modelli generativi che compongono trasformazioni invertibili) il termine log |det Jacobian| è il prezzo da pagare per il cambio di variabile e va calcolato per ogni layer. Nei modelli gaussiani la log-likelihood contiene -1/2 log |det Sigma|. Per altre architetture il determinante è solo un attrezzo diagnostico.

Se P è la matrice quadrata invertibile le cui colonne sono i vettori di una nuova base espressi nella vecchia, allora la matrice della stessa trasformazione T rispetto alla nuova base è:

A' = P^-1 A P

Operazione detta coniugazione. Conseguenze: traccia (somma degli elementi diagonali) e determinante sono invarianti per cambio di base — sono caratteristiche di T, non della rappresentazione. Anche gli autovalori lo sono. La matrice cambia, la trasformazione no.

Geometricamente: cambiare base è cambiare il sistema di coordinate. La cosa fisica (la trasformazione) resta la stessa, cambiano i numeri con cui la descriviamo. Caso particolarmente utile: se esiste una base in cui A diventa diagonale, A si dice diagonalizzabile. La base giusta è quella degli autovettori di A. Ne parla il capitolo dedicato.

Ogni matrice A di forma m x n (anche non quadrata) si fattorizza come:

A=USigmaVTA = U Sigma V^T

dove U è ortogonale m x m, V è ortogonale n x n, Sigma è “diagonale” m x n con entries non negative sigma1>=sigma2>=...>=0sigma_1 >= sigma_2 >= ... >= 0, i valori singolari di A. Lettura geometrica: ogni trasformazione lineare si scompone in tre passi puri — una rotazione (VTV^T), una scala assiale (Sigma), un’altra rotazione (U). Nient’altro.

La decomposizione ai valori singolari (SVD) esiste sempre, è unica essenzialmente, e racconta tutto: rango = numero di sigmaisigma_i non nulli, condition number = sigma1/sigmaminsigma_1 / sigma_min (rapporto fra il più grande e il più piccolo non nullo), pseudoinversa di Moore-Penrose costruita da Sigma^+, miglior approssimazione di rango basso (teorema di Eckart-Young) ottenuta troncando ai primi k valori. È lo strumento universale per fare diagnostica di una matrice. Capitolo dedicato altrove.

SVD as rotate-stretch-rotate decomposition of a 2D linear map

Esempio numerico: rotazione di 30° applicata a un punto

Sezione intitolata “Esempio numerico: rotazione di 30° applicata a un punto”

Costruiamo a mano la matrice che ruota i punti del piano di 30° antiorari attorno all’origine. Il terzo angolo dice: basta sapere dove vanno e1=(1,0)Te_1 = (1, 0)^T e e2=(0,1)Te_2 = (0, 1)^T.

e1e_1 ruotato di 30° finisce in (cos30°,sin30°)=(sqrt(3)/2,1/2)(0.866,0.5)(cos 30°, sin 30°) = (sqrt(3)/2, 1/2) ≈ (0.866, 0.5).

e2e_2 ruotato di 30° finisce in (sin30°,cos30°)=(1/2,sqrt(3)/2)(0.5,0.866)(-sin 30°, cos 30°) = (-1/2, sqrt(3)/2) ≈ (-0.5, 0.866).

Quindi la matrice di rotazione è:

R = [[ cos 30° -sin 30° ] ≈ [[ 0.866 -0.5 ]
[ sin 30° cos 30° ]] [ 0.5 0.866 ]]

Applichiamola al punto x=(1,0)Tx = (1, 0)^T. Per il terzo angolo, R x è una combinazione delle colonne con pesi (1, 0): prende solo la prima colonna, (0.866,0.5)T(0.866, 0.5)^T. Coerente con “ruotare (1, 0) di 30° dà (cos 30°, sin 30°)”.

Applichiamola a x=(1,1)Tx = (1, 1)^T. Combinazione delle colonne con pesi (1, 1):

Rx=1(0.866,0.5)T+1(0.5,0.866)T=(0.366,1.366)TR x = 1 * (0.866, 0.5)^T + 1 * (-0.5, 0.866)^T = (0.366, 1.366)^T

Verifica geometrica: il punto (1, 1) ha modulo sqrt(2)sqrt(2) e angolo 45°. Ruotato di 30° ha lo stesso modulo e angolo 75°. Le sue coordinate diventano sqrt(2)(cos75°,sin75°)(0.366,1.366)sqrt(2) (cos 75°, sin 75°) ≈ (0.366, 1.366). Tornano.

Determinante di R: cos2+sin2=1cos^2 + sin^2 = 1. Le rotazioni preservano area e orientazione.

Inversa di R: la rotazione inversa è quella di -30°, cioè:

R^-1 = [[ cos 30° sin 30° ]
[-sin 30° cos 30° ]] = R^T

Vale per ogni rotazione: l’inversa è la trasposta. Più in generale, le matrici ortogonali (QTQ=IQ^T Q = I) hanno inversa uguale alla trasposta, e rappresentano isometrie (preservano lunghezze e angoli) — vedi norme-distanze per il quadro su lunghezze e angoli.

Esempio in codice: layer Dense come trasformazione

Sezione intitolata “Esempio in codice: layer Dense come trasformazione”

Un layer Dense in PyTorch esegue y = W x + b. La parte lineare W x è esattamente una matrice di trasformazione.

import torch
import torch.nn as nn
# layer Dense: in_features=4, out_features=3
layer = nn.Linear(4, 3, bias=False)
# i pesi sono una matrice di shape (out_features, in_features) = (3, 4)
W = layer.weight.data
print(W.shape) # torch.Size([3, 4])
x = torch.tensor([1., 2., 3., 4.])
y = layer(x)
# equivalente a:
y_manual = W @ x
assert torch.allclose(y, y_manual)

Le tre viste della moltiplicazione tornano tutte:

# Vista A: prodotti scalari riga per riga
y_a = torch.stack([torch.dot(W[i], x) for i in range(W.shape[0])])
# Vista B: combinazione lineare delle colonne con pesi x
y_b = sum(x[j] * W[:, j] for j in range(W.shape[1]))
# Vista C: applicare W ai vettori della base
e = torch.eye(4)
columns = torch.stack([W @ e[:, j] for j in range(4)], dim=1)
# columns coincide con W
assert torch.allclose(columns, W)

In una rete neurale profonda, una sequenza di soli layer lineari WL...W2W1W_L ... W_2 W_1 collassa a una sola matrice Weff=WL...W1W_eff = W_L ... W_1 per associatività. Senza non-linearità (ReLU, GELU, sigmoid, ecc.) tra i layer, la rete profonda ha la stessa capacità espressiva di un singolo layer. Le non-linearità sono ciò che spezza la composizione lineare e dà alla rete il suo potere rappresentativo.

Nello scaled dot-product attention del transformer (vedi capitolo dedicato all’attention), data una sequenza di hidden states H (shape seq x d) si calcolano tre proiezioni:

Q=HWQK=HWKV=HWVQ = H W_Q K = H W_K V = H W_V

Le matrici WQ,WK,WVW_Q, W_K, W_V (shape dxdkd x d_k ciascuna) sono trasformazioni lineari della stessa rappresentazione H in tre spazi diversi. Geometricamente: tre angolazioni dello stesso oggetto. Q è la “domanda” che ogni posizione fa, K è l‘“etichetta” che ogni posizione espone, V è il “contenuto” che ogni posizione trasporta. Q e K vivono nello stesso spazio (devono essere confrontati con un prodotto scalare, vedi prodotto-scalare in preparazione); V vive in uno spazio “valori” che può differire.

Vista alla luce di questo capitolo: l’attention non sta facendo niente di esotico nelle proiezioni iniziali. Sta solo applicando tre trasformazioni lineari indipendenti alla stessa input, una per ruolo. La parte non lineare (e geniale) viene dopo, nel softmax dei prodotti scalari pesati. Le matrici sono ordinarie matrici, di rango al più min(d,dk)min(d, d_k), addestrate insieme al resto della rete.

Multi-head attention replica questa struttura h volte, con h triple di matrici di dimensione ridotta (dk=d/hd_k = d / h), in parallelo. Dal punto di vista lineare è solo un raggruppamento conveniente di tre proiezioni lineari su h sottospazi.

flowchart LR
  H[H: hidden state
(seq x d)] --> WQ[W_Q] --> Q[Q: queries]
  H --> WK[W_K] --> K[K: keys]
  H --> WV[W_V] --> V[V: values]

Figura 7 — formazione di Q, K, V dell’attention come tre proiezioni lineari indipendenti dello stesso hidden state H, ovvero tre viste sullo stesso oggetto

flowchart LR
  H[H: hidden state
(seq x d)] --> WQ[W_Q] --> Q[Q: queries]
  H --> WK[W_K] --> K[K: keys]
  H --> WV[W_V] --> V[V: values]

Figura 7 — H to Q,K,V via three linear projections W_Q,W_K,W_V; same input, three views

Una mappa rapida di dove le matrici-come-trasformazioni compaiono nei sistemi AI moderni. Alcune sono già entrate negli esempi; le ricapitoliamo qui per completezza.

Layer lineari (Dense, Linear, fully connected). La trasformazione affine y = W x + b. Il caso più diretto: la matrice W è la rappresentazione esplicita del layer. È equivalenza, nel senso che la mappa Rin>RoutR^in -> R^out codificata da W (dimenticando il bias) coincide identicamente con la trasformazione lineare di matrice W. Tutto ciò che diciamo sulle matrici si applica: rango di W limita le direzioni distinguibili, condizionamento di W controlla la stabilità del gradient, SVD di W decompone il layer in tre operazioni geometriche.

QKV dell’attention. Tre proiezioni lineari WQ,WK,WVW_Q, W_K, W_V della stessa rappresentazione, costruite per giocare ruoli distinti. Già coperto sopra.

Output head. Lo strato finale di un classificatore o di un language model è una matrice WoutW_out (shape: vocabsizexdvocab_size x d per un LM) che proietta la rappresentazione nascosta su uno spazio di logit. Spesso “tied” con la matrice di embedding Wout=ETW_out = E^T, scelta che riduce parametri e impone una struttura geometrica precisa.

Embedding lookup. Una matrice di embedding E (shape: vocabsizexdvocab_size x d) è formalmente una matrice come ogni altra. Per un token intero t, l’embedding E[t]E[t] è la riga t-esima. Equivalentemente: E[t]=ETetE[t] = E^T e_t dove ete_t è il vettore one-hot con 1 in posizione t. Quindi l’embedding lookup è una moltiplicazione matrice-vettore con un one-hot, cioè una trasformazione lineare. La forma “lookup” esiste solo per efficienza; un one-hot con un milione di zeri non si moltiplica davvero, si indicizza.

PCA. Dato un dataset X (shape: N x d) centrato, la matrice di covarianza C=(1/N)XTXC = (1/N) X^T X è simmetrica positiva semi-definita. I suoi autovettori sono le “direzioni principali”, ordinate per autovalore (varianza spiegata) decrescente. La trasformazione PCA che proietta su k direzioni è X>XVkX -> X V_k, con VkV_k matrice d x k delle prime k colonne di autovettori. Per il teorema di Eckart-Young, è la trasformazione lineare di rango k che minimizza l’errore di ricostruzione in norma L2. Niente magia: la matrice di trasformazione di rango k ottima per preservare varianza.

Convoluzione come matrice strutturata. Una convoluzione 1D con kernel fissato è una trasformazione lineare. La sua matrice è una Toeplitz (entries costanti lungo le diagonali) o circolante (con condizioni periodiche). Le ConvNet implementano questa moltiplicazione con tecniche efficienti (im2col, FFT) ma matematicamente è ancora una matrice, con il vincolo di parameter sharing che riduce il numero di parametri liberi e impone equivariance per traslazione.

Rotary Position Embedding (RoPE). Applica a Q e K una rotazione 2D (a coppie di dimensioni) che dipende dalla posizione del token. La matrice usata è esattamente la rotazione [[cos t, -sin t], [sin t, cos t]] con t funzione della posizione e dell’indice di dimensione. La proprietà chiave di RoPE — il prodotto scalare Qpos1.Kpos2Q_pos1 . K_pos2 dipende solo dalla differenza pos2 - pos1 — discende dal fatto che le rotazioni sono ortogonali e formano un gruppo: R(a) R(b) = R(a + b). Esempio diretto di matrice di trasformazione canonica usata nel cuore dei transformer moderni.

Spectral normalization. Tecnica usata in GAN per stabilizzare il training: si normalizza una matrice di pesi W dividendo per il suo valore singolare massimo (calcolato approssimativamente con power iteration). L’effetto è limitare il “gain” della trasformazione a 1 in norma operatore, stabilizzando i gradienti. Applicazione esplicita di SVD.

LoRA (Low-Rank Adaptation). Per fine-tuning efficiente, si aggiunge alla matrice di pesi pre-addestrata W una correzione di rango basso delta W = A B con A di shape d x r, B di shape r x d e r << d. La somma W + A B è ancora una matrice di trasformazione, ma il numero di parametri da aggiornare è 2 d r invece di d2d^2. Vincolo strutturale di rango imposto a fini di efficienza.

Le matrici sono uno strumento potente, ma vanno con avvertenze precise. Le ignoriamo a nostre spese.

Costo computazionale O(n3)O(n^3). La moltiplicazione naive di due matrici n x n richiede n3n^3 operazioni; lo stesso vale per inversione, fattorizzazione LU, decomposizione QR. Esistono algoritmi più veloci asintoticamente (Strassen O(n2.81)O(n^2.81), Coppersmith-Winograd e successori O(n2.37)O(n^2.37)), ma in pratica si usano blas/lapack ottimizzati con costante piccola e O(n3)O(n^3). Per n=104n = 10^4 il costo è ai limiti di un singolo nodo. Per n=106n = 10^6 è impossibile. Le matrici di pesi grandi nei modelli moderni si gestiscono con batch, sparsità, struttura (low-rank, blocchi, sliding-window), tensor parallelism che le distribuisce su più device.

Numerica float e condizionamento. Le matrici sono oggetti continui ma vivono in aritmetica float a precisione finita. Una matrice “quasi singolare” — con un valore singolare molto piccolo — è formalmente invertibile, ma la sua inversa numerica è amplificata dal condition number k(A)=sigmamax/sigmamink(A) = sigma_max / sigma_min. Per k=108k = 10^8 un errore di 10710^-7 in input diventa un errore di 10 in output: numericamente inutile. La regola pratica: non si invertono matrici. Si risolvono sistemi A x = b con LU/QR/Cholesky e back-substitution. Per problemi mal-condizionati si regolarizza (Tikhonov: (ATA+lambdaI)x=ATb(A^T A + lambda I) x = A^T b, equivalente alla regressione ridge / L2).

Inversione raramente necessaria. Più ampio del punto sopra: anche quando una matrice è ben condizionata, l’inversa esplicita non serve quasi mai. Se devi calcolare A1bA^-1 b, fai una solve. Se devi calcolare A1BA^-1 B, fai n solve. Se devi calcolare diagnose o autovalori, usa SVD/eigen-decomposition. L’inversa esplicita è un’idea da textbook che il codice di produzione raramente realizza.

Non commutatività che inganna. L’errore tipico è assumere che cambiare l’ordine di due trasformazioni non cambi il risultato. In 2D è ovvio (rotazione e shear non commutano); in RdR^d con d grande è meno visibile ma altrettanto vero. In particolare, due layer lineari consecutivi W2W1W_2 W_1 non sono in generale equivalenti a W1W2W_1 W_2. Lo sono solo in casi speciali (matrici diagonali, matrici simmetriche commutanti, ecc.).

Rank deficiency in pratica. Una matrice di pesi che dovrebbe avere rango pieno e invece ce l’ha basso è un sintomo. Cause tipiche: neuroni “morti” dopo training (righe identicamente zero), saturazione di non-linearità, init instabile, dropout aggressivo. Diagnosi: SVD e ispezione dello spettro dei valori singolari. Se i sigmaisigma_i cadono a zero molto prima di min(m, n), il layer sta sotto-utilizzando la sua capacità.

Limite della linearità. Una trasformazione lineare può fare molto, ma non tutto. Non può classificare punti che non sono linearmente separabili, non può modellare interazioni moltiplicative, non può saturare. In una rete neurale, la matrice fa il “trasporto” dei dati, le non-linearità (ReLU, GELU, softmax, layer norm) fanno la separazione e la regolazione. Ignorare questo equilibrio porta a errori sia di disegno (non mettere non-linearità tra layer) sia di interpretazione (attribuire alla matrice capacità che vengono dalla non-linearità a valle).

Confondere matrice di T e matrice di cambio di base. Errore frequente nei textbook studenti. Il cambio di base P non è “una trasformazione” applicata ai vettori: è una traduzione di coordinate. La trasformazione T resta T; cambia la matrice che la rappresenta, secondo A=P1APA' = P^-1 A P. Tenere separate le due cose.

Composizione vs prodotto element-wise. In ML compaiono entrambe. Il prodotto matriciale A B (composizione di trasformazioni lineari) e il prodotto Hadamard A * B (entries-wise) sono operazioni completamente diverse. Il gating h * sigma(g) in LSTM/GRU usa Hadamard; un layer Dense usa prodotto matriciale. Confonderli è un bug silenzioso che dà shape che combaciano e numeri sbagliati.

Memoria. Una matrice 104x10410^4 x 10^4 in float32 sono 400 MB. In modelli grandi le matrici di pesi superano questa soglia routinariamente, e l’attivazione intermedia H W può essere ancora più grande (proporzionale alla lunghezza di sequenza). Tecniche standard: tensor parallelism (sharding della matrice su più device), low-rank (LoRA), quantization (int8, int4, fp8), weight streaming. Sono tutte trasformazioni che lavorano sull’oggetto-matrice senza alterare la sua semantica di trasformazione.

Estensione a tensori. Una matrice è un tensore di rango 2. Nei modelli moderni si lavora con tensori di rango maggiore (batch, sequence, head, dim). Le operazioni come einsum generalizzano il prodotto matrice-matrice a contrazioni arbitrarie su indici. La semantica geometrica “trasformazione lineare” si estende ma diventa meno visiva. Quando si dubita, si “appiattisce” mentalmente a matrici riducendo gli assi.

import numpy as np
import math
# Costruire una matrice e tre viste della moltiplicazione
A = np.array([[1, 2], [3, 4], [5, 6]]) # 3x2
x = np.array([1, 1])
# Vista A: righe per vettore (prodotti scalari)
y_a = np.array([np.dot(A[i], x) for i in range(A.shape[0])])
# Vista B: combinazione lineare delle colonne
y_b = x[0] * A[:, 0] + x[1] * A[:, 1]
# Vista C: API standard
y = A @ x
assert np.allclose(y, y_a) and np.allclose(y, y_b)
# Rotazione 30 gradi applicata a (1, 0)
t = math.radians(30)
R = np.array([[math.cos(t), -math.sin(t)],
[math.sin(t), math.cos(t)]])
print(R @ np.array([1.0, 0.0])) # ~ [0.866, 0.5]
# Composizione non-commutativa
H = np.array([[1, 1], [0, 1]]) # shear orizzontale
print(R @ H)
print(H @ R) # diversi
# Rango e determinante
M = np.array([[1, 2], [2, 4]]) # colonne dipendenti: rank 1
print(np.linalg.matrix_rank(M)) # 1
print(np.linalg.det(R)) # ~ 1.0 (rotazione preserva area)
# Risolvere A x = b senza invertire
A_sq = np.array([[3., 1.], [1., 2.]])
b = np.array([9., 8.])
x_sol = np.linalg.solve(A_sq, b) # NON inv(A) @ b
print(x_sol)
# SVD: tre operazioni geometriche
U, S, Vt = np.linalg.svd(A)
print("singular values:", S)
print("rank from SVD:", np.sum(S > 1e-10))
  • vettori-spazi: la grammatica vettoriale su cui poggia tutto il capitolo. Senza spazi vettoriali non c’è “trasformazione lineare”. Il quadro storico (Grassmann, Peano) si chiude qui.
  • norme-distanze: le norme misurano lunghezze; le matrici trasformano vettori. Il condition number di una matrice si definisce in termini di norma; le rotazioni sono le matrici che preservano la norma L2.
  • prodotto-scalare: alla base della vista “righe per colonne” della moltiplicazione e del meccanismo di attention. La connessione <Ax,y>=<x,ATy><Ax, y> = <x, A^T y> lega trasposta e prodotto scalare.
  • autovalori-autovettori-intuito: il caso speciale in cui una matrice si riduce a pura scala su una direzione. Diagonalizzazione, decomposizione spettrale.
  • embedding-concetto: la matrice di embedding come trasformazione lineare degenerata (lookup = moltiplicazione per one-hot).
  • attention-bahdanau-2014 e transformer-2017: le tre matrici W_Q, W_K, W_V sono trasformazioni lineari della stessa rappresentazione, e il capitolo dà gli strumenti per capire cosa fanno.
  • discesa-gradiente: il gradient di una loss rispetto a una matrice di pesi è esso stesso una matrice; la “regola della catena per matrici” è la backpropagation.
  • curse-dimensionalita: le matrici grandi vivono in alta dimensione, dove distanze e direzioni si comportano in modo controintuitivo.
  • Strang, G. (2016). Introduction to Linear Algebra, 5a ed. Wellesley-Cambridge Press. Riferimento didattico universale per la presentazione “row picture vs column picture”. Lezioni MIT 18.06 disponibili gratuitamente.
  • Axler, S. (2015). Linear Algebra Done Right, 3a ed. Springer. Approccio coordinate-free: trasformazioni lineari prima delle matrici, determinanti rinviati alla fine. Utile per consolidare la distinzione “matrice = rappresentazione”.
  • 3Blue1Brown — Sanderson, G. (2016-2018). Essence of Linear Algebra. Serie YouTube. Standard de facto per le intuizioni geometriche; gli episodi 3 e 4 (linear transformations and matrices, matrix multiplication as composition) sono allineati al cuore di questo capitolo.
  • Trefethen, L. N., Bau, D. (1997). Numerical Linear Algebra. SIAM. Per il lato numerico: condizionamento, stabilità, perché non si invertono matrici.
  • Goodfellow, I., Bengio, Y., Courville, A. (2016). Deep Learning. MIT Press, capitolo 2. Notazione e collegamenti diretti tra algebra lineare e reti neurali.