🧠 Fondamenti14 minuti di lettura

Embeddings: Come l'AI Capisce che 'Cane' e 'Cucciolo' Sono Simili

Come funzionano gli embedding moderni? Cosine similarity, BERT, vector database e RAG: tutto quello che serve per capire come l'AI vede il significato.

AS

Alessandro Saiani

Human in the Loop

Embeddings: Come l'AI Capisce che 'Cane' e 'Cucciolo' Sono Simili

Nell'articolo su Word2Vec abbiamo visto l'idea fondamentale: trasformare le parole in vettori di numeri dove la distanza rappresenta il significato. "Re - uomo + donna = regina" — matematica con le parole.

Ma Word2Vec ha un problema serio: ogni parola ha un solo vettore, fisso per sempre. "Pesca" è un unico punto nello spazio — che tu stia parlando del frutto o dell'attività al lago. E non sa nulla di frasi intere: "il cane morde l'uomo" e "l'uomo morde il cane" producono gli stessi vettori.

Gli embeddings moderni risolvono tutto questo. Cambiano in base al contesto. Rappresentano frasi intere. E sono il motore invisibile dietro la ricerca semantica, il RAG, i vector database, e ogni volta che Claude "capisce" cosa intendi.

Questo articolo parte dove Word2Vec si ferma.


Come Si Misura la Similitudine: Cosine Similarity

Prima di parlare di modelli, serve capire come si confrontano due vettori. Perché dire "gatto e cane sono vicini" richiede una definizione matematica di "vicino".

Il Problema della Distanza Euclidea

La prima idea sarebbe la distanza classica — la linea retta tra due punti (distanza euclidea). Ma ha un difetto: dipende dalla lunghezza del vettore, non solo dalla direzione.

Immagina due testi sullo stesso argomento. Uno è lungo (tanti token, vettore "grande"), l'altro breve. La distanza euclidea li vedrebbe lontani — ma il significato è lo stesso.

Cosine Similarity: Conta Solo la Direzione

La cosine similarity misura l'angolo tra due vettori, ignorando la lunghezza. Il valore va da -1 a +1:

cos(θ) = (A · B) / (|A| × |B|)
  • +1.0: vettori nella stessa direzione → significato identico
  • 0.0: vettori perpendicolari → nessuna relazione
  • -1.0: direzioni opposte → significato opposto

In pratica:

similarity("cane", "cucciolo")     = 0.92  → molto simili
similarity("cane", "gatto")        = 0.85  → simili (entrambi animali)
similarity("cane", "automobile")   = 0.15  → poco in comune
similarity("cane", "felicità")     = 0.08  → quasi nessuna relazione

Perché funziona? Perché nel vettore di un embedding, la direzione codifica il significato e la lunghezza dipende da fattori come la frequenza della parola o la lunghezza del testo. La cosine similarity cattura il primo e ignora il secondo.

Nella Pratica Quotidiana

Ogni volta che usi la ricerca semantica, il RAG, o un vector database, dietro c'è una cosine similarity. Scrivi "come faccio a autenticarmi?" e il sistema trova un documento intitolato "Guida alla Login" — perché i due embedding puntano nella stessa direzione, anche se non condividono nessuna parola.


Da Statici a Contestuali: Il Salto Fondamentale

Word2Vec, GloVe e FastText producono embedding statici: un vettore per parola, calcolato una volta, uguale per sempre. Nel 2018 è cambiato tutto.

ELMo: Il Primo Embedding Contestuale (2018)

ELMo (Embeddings from Language Models) di Allen NLP è stato il primo a generare vettori diversi per la stessa parola in contesti diversi. Usava una rete LSTM bidirezionale: leggeva la frase da sinistra a destra e da destra a sinistra, poi combinava i risultati.

"Andiamo a pesca al lago"    → vec("pesca") = [0.21, -0.45, 0.83, ...]  (attività)
"La pesca è un frutto dolce" → vec("pesca") = [-0.12, 0.67, 0.31, ...]  (frutto)

Stesso token, vettori diversi. Il contesto cambia la rappresentazione. È il principio che poi BERT e GPT porteranno alle estreme conseguenze.

BERT: L'Attention Cambia Tutto (2018)

BERT (Google) ha sostituito le LSTM con il meccanismo di self-attention dei Transformer. Ogni token "guarda" tutti gli altri token della frase contemporaneamente — non sequenzialmente.

Il risultato: embedding che catturano relazioni a lunga distanza. In "il programmatore che ha scritto il codice ieri ha trovato un bug", BERT collega "bug" a "programmatore" e "codice" anche se sono distanti nella frase.

BERT è bidirezionale: legge il contesto in entrambe le direzioni. GPT è unidirezionale: legge solo da sinistra a destra (perché genera token uno alla volta). Entrambi producono embedding contestuali, ma con caratteristiche diverse.

Cosa Cambia in Pratica

AspettoEmbedding StaticoEmbedding Contestuale
PolisemiaUn vettore per "banco"Vettore diverso per "banco di scuola" vs "banco di nebbia"
Negazione"buono" ≈ "non buono""buono" ≠ "non buono"
Ordine paroleIgnoratoConsiderato
CalcoloLookup in tabellaForward pass di un modello
Dimensione50-300768-4096+
Uso tipicoFeature per modelli MLRicerca semantica, RAG, classificazione

Il prezzo: un embedding statico è un lookup istantaneo. Un embedding contestuale richiede un forward pass dell'intero modello — millisecondi per frase, che diventano ore su milioni di documenti.


Embedding di Frasi: SBERT e il Problema del Cross-Encoding

BERT produce un vettore per ogni token. Ma cosa fai se ti serve un vettore per un'intera frase o un paragrafo?

Il Problema: BERT È Lento per la Ricerca

Il modo "diretto" di usare BERT per la similarità tra frasi è il cross-encoding: dai in input entrambe le frasi al modello, che produce un punteggio di similarità.

Funziona bene. Ma è O(n²): per confrontare una query con 10.000 documenti, servono 10.000 forward pass di BERT. Con un milione di documenti, diventa impraticabile.

SBERT: Un Vettore per Frase

Sentence-BERT (2019) ha risolto il problema con un'idea semplice: fare fine-tuning di BERT per produrre un singolo vettore che rappresenta l'intera frase. Due frasi simili hanno vettori vicini.

Come funziona:

  1. Passa la frase nel modello BERT
  2. Prendi il vettore del token [CLS] o la media di tutti i token (mean pooling)
  3. Usa quel vettore come embedding della frase

Il vantaggio: calcoli l'embedding di ogni documento una volta sola, lo salvi, e poi confronti le query con una cosine similarity — operazione quasi istantanea. Da O(n²) a O(n).

SBERT è stato il modello che ha reso la ricerca semantica praticabile su larga scala. Prima di SBERT, la ricerca full-text con keyword era l'unica opzione per milioni di documenti.


I Modelli di Embedding Moderni

Dal 2023 in poi, le aziende AI hanno rilasciato modelli di embedding specializzati — più potenti di SBERT e ottimizzati per casi d'uso specifici.

OpenAI: text-embedding-3

Il modello più usato nell'ecosistema commerciale.

  • text-embedding-3-small: 1536 dimensioni, $0.02/1M token — il workhorse
  • text-embedding-3-large: 3072 dimensioni, $0.13/1M token — il più preciso

Innovazione chiave: Matryoshka embeddings. Puoi troncare il vettore a dimensioni inferiori (es. 256 invece di 3072) perdendo poco in qualità ma risparmiando spazio e velocità. Come le matrioske russe: le informazioni più importanti stanno nelle prime dimensioni.

Dimensione 3072: accuracy 100% (baseline)
Dimensione 1024: accuracy ~99%
Dimensione  256: accuracy ~95%

Cohere: Embed v4

Primo modello mainstream a supportare embedding multimodali: testo e immagini nello stesso spazio vettoriale. Una foto di un gatto e la frase "gatto che dorme" producono vettori vicini.

Supporta 100+ lingue — rilevante per l'italiano — e ha un punteggio alto sul benchmark MTEB.

Voyage AI: Modelli Verticali

Voyage ha scommesso sulla specializzazione:

  • voyage-3: general-purpose, top su MTEB
  • voyage-code-3: ottimizzato per codice — cerca per significato, non per syntax match
  • voyage-finance-2: ottimizzato per documenti finanziari
  • voyage-law-2: ottimizzato per testi legali

Il vantaggio dei modelli verticali: un embedding generico potrebbe non distinguere "derivative" (derivata matematica) da "derivative" (strumento finanziario). Uno specializzato sì.

Google: Gemini Embedding

Integrato nativamente nell'ecosistema Google Cloud, supporta fino a 8192 token di input — molto più della media. Multilingua.

Jina AI: jina-embeddings-v3

Modello open-weight (Apache 2.0) con un'idea interessante: task-specific adapters. Lo stesso modello base può essere indirizzato verso retrieval, classificazione o similarità con un parametro.


MTEB: Come Si Misurano gli Embedding

Il Massive Text Embedding Benchmark (MTEB) è il benchmark standard per valutare i modelli di embedding. Copre 8 task diversi su 58 dataset:

TaskCosa MisuraEsempio
RetrievalTrovare documenti rilevantiRicerca semantica
STSSimilarità tra frasi"Il cane corre" ≈ "Il cucciolo scappa"
ClassificationClassificare testiSentiment, topic
ClusteringRaggruppare testi similiOrganizzare ticket di supporto
RerankingRiordinare risultatiMigliorare risultati di ricerca
Pair ClassificationRelazione tra coppieParaphrase detection
SummarizationQualità dei riassuntiEmbedding del riassunto vs originale
BitextMiningAllineamento traduzioniTrovare frasi corrispondenti

Perché serve un benchmark così ampio? Perché un modello eccellente in retrieval potrebbe essere mediocre in clustering. MTEB dà una visione completa. La classifica aggiornata è pubblica su Hugging Face.


Embedding per il Codice

Se lavori con il codice, gli embedding aprono possibilità che la ricerca testuale non può raggiungere.

Il Problema della Ricerca Sintattica

Cerchi "autenticazione utente" nel codebase. La ricerca testuale trova file che contengono quelle parole esatte. Non trova validateUserCredentials(), checkJWTToken() o passport.authenticate() — che fanno esattamente la cosa che cerchi.

Come Funzionano gli Embedding per il Codice

Un modello di code embedding trasforma funzioni, classi e snippet in vettori dove la distanza rappresenta la similarità funzionale, non lessicale.

embedding("function login(email, password) { ... }")
≈
embedding("def authenticate_user(credentials): ...")

Stessa funzionalità, linguaggi diversi, nomi diversi — ma vettori vicini.

Modelli specializzati:

  • Voyage-code-3: stato dell'arte nel code retrieval
  • Qodo-Embed-1: ottimizzato per comprensione semantica del codice
  • CodeBERT: il BERT del codice, fine-tunato su coppie NL-Code

Casi d'uso reali:

  • Code search: "trova dove gestiamo il rate limiting" → trova il middleware corretto
  • Deduplicazione: identificare funzioni duplicate o quasi-duplicate in codebase grandi
  • Code review: trovare pattern simili a bug noti
  • Documentation matching: collegare codice alla documentazione rilevante

CLIP: Quando Testo e Immagini Condividono lo Spazio

Abbiamo visto che Cohere Embed v4 supporta embedding multimodali. Ma l'idea di mettere testo e immagini nello stesso spazio vettoriale non nasce lì — nasce con CLIP.

CLIP (Contrastive Language-Image Pre-training, OpenAI 2021) è il modello che ha aperto la strada. L'idea: se una foto e una didascalia descrivono la stessa cosa, i loro vettori devono essere vicini.

Come è stato addestrato:

  1. Prendi 400 milioni di coppie (immagine, testo) dal web
  2. Per ogni coppia, avvicina i vettori dell'immagine e del testo corrispondente
  3. Allontana i vettori delle coppie non corrispondenti

Il risultato: puoi cercare immagini con testo e testo con immagini — senza mai aver visto quella specifica combinazione in training.

query_text = "tramonto sul mare con barca a vela"
→ trova foto di tramonti con barche, anche se nessuna aveva quella didascalia

CLIP è la base di:

  • Stable Diffusion: il text encoder che trasforma il prompt in un vettore che guida la generazione
  • Google Lens: ricerca visuale
  • Ricerca multimodale: cercare in database misti di testo, immagini e video

Vector Database: Dove Vivono gli Embedding

Produrre embedding è metà del lavoro. L'altra metà è salvare e cercare tra milioni (o miliardi) di vettori in modo efficiente.

Il Problema: Nearest Neighbor è Lento

Data una query, trovare il vettore più vicino tra N vettori richiede N confronti — una cosine similarity per ognuno. Con un milione di vettori da 1536 dimensioni, è già lento. Con un miliardo, è impossibile.

ANN: Approximate Nearest Neighbor

La soluzione: non cercare il più vicino, ma uno abbastanza vicino. Gli algoritmi ANN (Approximate Nearest Neighbor) sacrificano una precisione minima per un guadagno enorme in velocità.

Le tecniche principali:

HNSW (Hierarchical Navigable Small World): costruisce un grafo multi-livello dove i nodi sono i vettori. La ricerca parte dai livelli alti (pochi nodi, ricerca grossa) e scende ai livelli bassi (molti nodi, ricerca fine). È come cercare un indirizzo: prima la città, poi il quartiere, poi la via.

IVF (Inverted File Index): divide lo spazio in cluster. Prima trova il cluster più vicino alla query, poi cerca solo dentro quel cluster.

Product Quantization: comprime i vettori per ridurre memoria e accelerare i confronti.

I Database Specializzati

DatabaseTipoPunto di forza
PineconeCloud managedZero-ops, scala automaticamente
WeaviateOpen sourceSupporto nativo per moduli ML
QdrantOpen source (Rust)Performance raw
Milvus/ZillizOpen source / CloudScalabilità enterprise (miliardi di vettori)
ChromaOpen sourceSemplicità, ottimo per prototyping
pgvectorEstensione PostgreSQLNessun database aggiuntivo

pgvector merita una menzione speciale: è un'estensione di PostgreSQL che aggiunge il tipo vector e operatori di similarità. Se hai già Postgres, puoi fare ricerca vettoriale senza aggiungere infrastruttura. Per meno di qualche milione di vettori, è spesso sufficiente.


Dimensioni: Quante Ne Servono?

I modelli di embedding producono vettori di dimensione variabile: da 384 a 4096. Più dimensioni = più informazione = più spazio e più tempo di ricerca.

ModelloDimensioniNote
MiniLM-L6-v2384Veloce, buono per prototyping
text-embedding-3-small1536Standard OpenAI
text-embedding-3-large3072Max qualità OpenAI
Cohere Embed v41024Multimodale
Voyage-31024Top MTEB
jina-embeddings-v31024Open source

Matryoshka Representation Learning

L'innovazione più pratica degli ultimi anni. L'idea: durante il training, il modello viene ottimizzato per produrre vettori dove le prime N dimensioni contengono le informazioni più importanti.

Puoi quindi troncare il vettore a dimensioni inferiori senza ri-addestrare il modello:

Vettore completo (3072 dim): massima qualità
Troncato a 1024 dim:         ~99% della qualità
Troncato a 256 dim:          ~95% della qualità
Troncato a 64 dim:           ~85% della qualità

Perché è utile? Perché lo spazio di storage e la velocità di ricerca dipendono dalla dimensione del vettore. Se stai indicizzando 100 milioni di documenti, passare da 3072 a 256 dimensioni riduce lo storage di 12 volte — e la ricerca è proporzionalmente più veloce.

text-embedding-3 di OpenAI e jina-embeddings-v3 supportano nativamente Matryoshka.


Visualizzare gli Embedding: t-SNE e UMAP

I vettori di embedding vivono in spazi a centinaia di dimensioni — impossibili da visualizzare. Ma possiamo proiettarli in 2D per "vedere" la struttura.

t-SNE

t-SNE (t-distributed Stochastic Neighbor Embedding) proietta vettori ad alta dimensionalità in 2D/3D preservando le relazioni di vicinanza locale: punti vicini nello spazio originale restano vicini nella proiezione.

Il risultato sono mappe dove i cluster emergono visivamente: le parole sugli animali formano un gruppo, quelle sulla tecnologia un altro, quelle sulle emozioni un altro ancora.

Limiti: t-SNE è lento su grandi dataset, non preserva le distanze globali (due cluster lontani nella mappa potrebbero non esserlo nello spazio reale), e il risultato cambia con i parametri (perplexity).

UMAP

UMAP (Uniform Manifold Approximation and Projection) è l'alternativa più moderna: più veloce, preserva meglio la struttura globale, e i risultati sono più riproducibili.

Per uso pratico: se devi visualizzare embedding per debug o presentazione, UMAP è la scelta standard nel 2026.


Un Esempio Concreto: Come Funziona il RAG

Mettiamo tutto insieme. Il RAG (Retrieval-Augmented Generation) è il caso d'uso killer degli embedding — e usa ogni concetto che abbiamo visto.

Il Flusso Completo

Fase 1 — Indicizzazione (una tantum):

  1. Prendi i tuoi documenti (documentazione, wiki, codebase)
  2. Spezzali in chunk di 200-500 token
  3. Passa ogni chunk nel modello di embedding → ottieni un vettore
  4. Salva i vettori nel vector database (con il testo originale come metadata)

Fase 2 — Query (in tempo reale):

  1. L'utente fa una domanda: "Come configuro l'autenticazione JWT?"
  2. La domanda passa nello stesso modello di embedding → vettore della query
  3. Il vector database trova i 5 chunk con vettori più vicini (ANN + cosine similarity)
  4. I chunk recuperati vengono aggiunti al prompt di Claude come contesto
  5. Claude genera una risposta basata solo sui documenti rilevanti

Perché funziona: la domanda "Come configuro l'autenticazione JWT?" e il chunk "### Setup JWT: Installa il pacchetto jsonwebtoken..." hanno embedding vicini — anche se non condividono le stesse parole. La cosine similarity cattura la relazione semantica.

Perché è meglio della ricerca keyword: "autenticazione JWT" non troverebbe un documento che parla di "token di accesso con firma crittografica" — ma gli embedding sì.


Cosa Viene Dopo

Questo articolo ha coperto l'evoluzione da vettori statici a contestuali, la cosine similarity, i modelli moderni, il codice, il multimodale, i vector database, e come tutto questo si connette nel RAG.

Ma c'è un livello ancora più profondo. I prossimi articoli della serie copriranno:

  • BPE: come funziona l'algoritmo che taglia le parole in token prima che diventino embedding
  • Pre-training: come un modello impara a generare embedding contestuali leggendo internet
  • RLHF/DPO: come si insegna al modello a produrre output utili e sicuri

Ogni volta che Claude legge il tuo messaggio, la prima operazione è un embedding lookup — esattamente come Word2Vec, solo con contesto, attenzione multi-head, e miliardi di parametri. Il principio di Firth del 1957 — "conoscerai una parola dalla compagnia che tiene" — è ancora lì, alla base di tutto.

Solo che oggi la "compagnia" non è una finestra di 4 parole. È un milione di token di contesto.


Fonti