La similarità semantica è ciò che i modelli di embedding sono costruiti per misurare, ma queste misurazioni sono influenzate da molti fattori di bias. In questo articolo, esamineremo una fonte pervasiva di bias nei modelli di embedding del testo: la dimensione dell'input.
Gli embedding di testi più lunghi mostrano generalmente punteggi di similarità più alti quando confrontati con altri embedding di testo, indipendentemente da quanto sia simile il contenuto effettivo. Mentre i testi veramente simili avranno comunque punteggi di similarità più alti rispetto a quelli non correlati, i testi più lunghi introducono un bias—facendo apparire i loro embedding più simili in media semplicemente a causa della loro lunghezza.
Questo ha conseguenze reali. Significa che i modelli di embedding, da soli, non sono in grado di misurare molto bene la rilevanza. Con la ricerca basata su embedding, c'è sempre una corrispondenza migliore, ma il bias dimensionale significa che non si può usare il punteggio di similarità per decidere se la corrispondenza migliore, o qualsiasi corrispondenza minore, sia effettivamente rilevante. Non si può dire, per esempio, che qualsiasi corrispondenza con un coseno superiore a 0.75 sia rilevante perché ci può facilmente essere un documento lungo che corrisponde a quel livello nonostante sia completamente irrilevante.
Dimostreremo questo con alcuni esempi semplici e mostreremo come la similarità del coseno tra gli embedding di testo non possa servire come modo generale per valutare
tagVisualizzare il Bias Dimensionale
Per mostrare come si manifesta il bias dimensionale, useremo l'ultimo modello di embedding di Jina AI jina-embeddings-v3 con l'opzione text-matching
. Useremo anche documenti di testo da un dataset IR ampiamente utilizzato: Il dataset CISI, che puoi scaricare da Kaggle.

Questo dataset è utilizzato per addestrare sistemi IR, quindi contiene sia query che documenti da abbinare. Useremo solo i documenti, che si trovano tutti nel file CISI.ALL
. Puoi scaricarlo dalla riga di comando da una fonte alternativa su GitHub con il comando:
wget https://raw.githubusercontent.com/GianRomani/CISI-project-MLOps/refs/heads/main/CISI.ALL
CISI contiene 1.460 documenti. Le statistiche di base sulle dimensioni dei testi e le loro distribuzioni sono riassunte nella tabella e negli istogrammi seguenti:
in Parole | in Frasi | |
---|---|---|
Dimensione media documento | 119,2 | 4,34 |
Deviazione standard | 63,3 | 2,7 |
Dimensione massima | 550 | 38 |
Dimensione minima | 8 | 1 |


Leggiamo i documenti in Python e otteniamo gli embedding per essi. Il codice seguente presuppone che il file CISI.ALL
sia nella directory locale:
with open("CISI.ALL", "r", encoding="utf-8") as inp:
cisi_raw = inp.readlines()
docs = []
current_doc = ""
in_text = False
for line in cisi_raw:
if line.startswith("."):
in_text = False
if current_doc:
docs.append(current_doc.strip())
current_doc = ""
if line.startswith(".W"):
in_text = True
else:
if in_text:
current_doc += line
Questo riempirà la lista docs
con 1.460 documenti. Puoi ispezionarli:
print(docs[0])
The present study is a history of the DEWEY Decimal
Classification. The first edition of the DDC was published
in 1876, the eighteenth edition in 1971, and future editions
will continue to appear as needed. In spite of the DDC's
long and healthy life, however, its full story has never
been told. There have been biographies of Dewey
that briefly describe his system, but this is the first
attempt to provide a detailed history of the work that
more than any other has spurred the growth of
librarianship in this country and abroad.
Ora, costruiremo gli embedding per ogni testo usando jina-embeddings-v3. Per questo, avrai bisogno di una chiave API dal sito web di Jina AI. Puoi ottenere una chiave gratuita per fino a 1 milione di token di embedding, che è sufficiente per questo articolo.
Metti la tua chiave in una variabile:
api_key = "<Your Key>"
Ora, genera gli embedding usando il task text-matching
con jina-embeddings-v3. Questo codice elabora i testi in docs
in batch di 10.
import requests
import json
from numpy import array
embeddings = []
url = "https://api.jina.ai/v1/embeddings"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer " + api_key
}
i = 0
while i < len(docs):
print(f"Got {len(embeddings)}...")
data = {
"model": "jina-embeddings-v3",
"task": "text-matching",
"late_chunking": False,
"dimensions": 1024,
"embedding_type": "float",
"input": docs[i:i+10]
}
response = requests.post(url, headers=headers, data=json.dumps(data))
for emb in response.json()['data']:
embeddings.append(array(emb['embedding']))
i += 10
Per ogni testo, ci sarà un embedding di 1024 dimensioni nella lista embeddings
. Puoi vedere come appare:
print(embeddings[0])
array([ 0.0352382 , -0.00594871, 0.03808545, ..., -0.01147173,
-0.01710563, 0.01109511], shape=(1024,))),
Ora, calcoliamo i coseni tra tutte le coppie di embedding. Prima, definiamo la funzione coseno cos_sim
usando numpy
:
from numpy import dot
from numpy.linalg import norm
def cos_sim(a, b):
return float((a @ b.T) / (norm(a)*norm(b)))
Quindi, calcoliamo i coseni di ciascuno dei 1.460 embedding confrontati con gli altri 1.459:
all_cosines = []
for i, emb1 in enumerate(embeddings):
for j, emb2 in enumerate(embeddings):
if i != j:
all_cosines.append(cos_sim(emb1, emb2))
Il risultato è una lista di 2.130.140 valori. La loro distribuzione dovrebbe approssimare i coseni tra documenti "casuali" nella stessa lingua e registro. La tabella e l'istogramma seguenti riassumono i risultati.
Numero di testi | 1.460 |
---|---|
Numero di coseni | 2.130.140 |
Media | 0,343 |
Deviazione standard | 0,116 |

Questi documenti, anche se non correlati tra loro, hanno tipicamente coseni ben al di sopra dello zero. Potremmo essere tentati di impostare una soglia di 0,459 (media + 1 deviazione standard), o magari arrotondarla a 0,5, e dire che qualsiasi coppia di documenti con un coseno inferiore a quello deve essere in gran parte non correlata.
Ma facciamo lo stesso esperimento su testi più piccoli. Useremo la libreria nltk
per dividere ogni documento in frasi:
import nltk
sentences = []
for doc in docs:
sentences.extend(nltk.sent_tokenize(doc))
Questo produce 6.331 frasi con una lunghezza media di 27,5 parole e una deviazione standard di 16,6. Nell'istogramma seguente, la distribuzione delle dimensioni delle frasi è in rosso, e per i documenti completi è in blu, così puoi confrontarle.

Useremo lo stesso modello e metodi per ottenere gli embedding per ogni frase:
sentence_embeddings = []
i = 0
while i < len(sentences):
print(f"Got {len(sentence_embeddings)}...")
data = {
"model": "jina-embeddings-v3",
"task": "text-matching",
"late_chunking": False,
"dimensions": 1024,
"embedding_type": "float",
"input": sentences[i:i+10]
}
response = requests.post(url, headers=headers, data=json.dumps(data))
for emb in response.json()['data']:
sentence_embeddings.append(array(emb['embedding']))
i += 10
E poi calcolare il coseno tra l'embedding di ogni frase con quello di ogni altra frase:
sent_cosines = []
for i, emb1 in enumerate(sentence_embeddings):
for j, emb2 in enumerate(sentence_embeddings):
if i != j:
sent_cosines.append(cos_sim(emb1, emb2))
Il risultato è un numero piuttosto elevato di valori coseno: 40.075.230, come riassunto nella tabella seguente:
Number of sentences | 6,331 |
---|---|
Number of cosines | 40,075,230 |
Average | 0.254 |
Std. Deviation | 0.116 |
I coseni frase-frase sono considerevolmente più bassi in media rispetto a quelli documento-documento completi. L'istogramma sottostante confronta le loro distribuzioni, e si può facilmente vedere che le coppie di frasi formano una distribuzione quasi identica a quella delle coppie di documenti ma spostata verso sinistra.

Per verificare che questa dipendenza dalla dimensione sia robusta, calcoliamo tutti i coseni tra frasi e documenti e aggiungiamoli all'istogramma. Le loro informazioni sono riassunte nella tabella seguente:
Number of texts | 6,331 sentences & 1,460 documents |
---|---|
Number of cosines | 9,243,260 |
Average | 0.276 |
Std. Deviation | 0.119 |
La linea verde sottostante è la distribuzione dei coseni frase-documento. Possiamo vedere che questa distribuzione si colloca ordinatamente tra i coseni documento-documento e i coseni frase-frase, mostrando che l'effetto della dimensione coinvolge entrambi i testi più grandi e più piccoli che vengono confrontati.

Facciamo un'altra prova concatenando i documenti in gruppi di dieci, creando 146 documenti molto più grandi e misurando i loro coseni. Il risultato è riassunto di seguito:
Number of texts | 146 documents |
---|---|
Number of cosines | 21,170 |
Average | 0.658 |
Std. Deviation | 0.09 |

Questo è molto più a destra delle altre distribuzioni. Una soglia coseno di 0,5 ci direbbe che quasi tutti questi documenti sono correlati tra loro. Per escludere documenti irrilevanti di questa dimensione, dovremmo impostare la soglia molto più alta, forse fino a 0,9, che indubbiamente escluderebbe buone corrispondenze tra i documenti più piccoli.
Questo dimostra che non possiamo usare le soglie minime del coseno per stimare quanto sia buona una corrispondenza, almeno non senza tenere conto in qualche modo della dimensione del documento.
tagCosa Causa il Bias della Dimensione?
Il bias della dimensione negli embedding non è come i bias posizionali nei modelli con contesto lungo. Non è causato dalle architetture. Non riguarda intrinsecamente la dimensione. Se, per esempio, avessimo creato documenti più lunghi semplicemente concatenando copie dello stesso documento più e più volte, non mostrerebbe un bias della dimensione.
Il problema è che i testi lunghi dicono più cose. Anche se sono vincolati da un argomento e uno scopo, il punto di scrivere più parole è dire più cose.
I testi più lunghi, almeno del tipo che le persone normalmente creano, produrranno naturalmente embedding che si "distribuiscono" su più spazio semantico. Se un testo dice più cose, il suo embedding avrà un angolo più basso con altri vettori in media, indipendentemente dall'argomento del testo.
tagMisurare la Rilevanza
La lezione di questo post è che non si possono usare i coseni tra vettori semantici da soli per dire se qualcosa è una buona corrispondenza, ma solo che è la migliore corrispondenza tra quelle disponibili. Bisogna fare qualcosa oltre al calcolo dei coseni per verificare l'utilità e la validità delle migliori corrispondenze.
Si potrebbe provare la normalizzazione. Se si può misurare empiricamente il bias della dimensione, potrebbe essere possibile compensarlo. Tuttavia, questo approccio potrebbe non essere molto robusto. Ciò che funziona per un dataset probabilmente non funzionerà per un altro.
La codifica asimmetrica query-documento, fornita in jina-embeddings-v3, riduce il bias della dimensione nei modelli di embedding ma non lo elimina. Lo scopo della codifica asimmetrica è codificare i documenti per essere meno "distribuiti" e codificare le query per esserlo di più.
La linea rossa nell'istogramma sottostante è la distribuzione dei coseni documento-documento usando la codifica asimmetrica con jina-embeddings-v3 – ogni documento viene codificato usando i flag retrieval.query
e retrieval.passage
, e ogni embedding di query del documento viene confrontato con ogni embedding di passaggio del documento che non proviene dallo stesso documento. Il coseno medio è 0,200, con una deviazione standard di 0,124.
Questi coseni sono considerevolmente più piccoli di quelli che abbiamo trovato sopra per gli stessi documenti usando il flag text-matching
, come mostrato nell'istogramma sottostante.

Tuttavia, la codifica asimmetrica non ha eliminato il bias della dimensione. L'istogramma sottostante confronta i coseni per documenti completi e frasi usando la codifica asimmetrica.

La media per i coseni delle frasi è 0,124, quindi usando la codifica asimmetrica, la differenza tra il coseno medio delle frasi e il coseno medio dei documenti è 0,076. La differenza nelle medie per la codifica simmetrica è 0,089. Il cambiamento nel bias della dimensione è insignificante.
Sebbene la codifica asimmetrica migliori gli embedding per il recupero delle informazioni, non è migliore per misurare la rilevanza delle corrispondenze.
tagPossibilità Future
L'approccio del reranker, ad esempio jina-reranker-v2-base-multilingual e jina-reranker-m0, è un modo alternativo di valutare le corrispondenze query-documento che sappiamo già migliora la precisione delle query. I punteggi del reranker non sono normalizzati, quindi non funzionano nemmeno come misure di similitudine oggettive. Tuttavia, vengono calcolati in modo diverso, e potrebbe essere possibile normalizzare i punteggi del reranker in modi che li rendano buoni stimatori della rilevanza.
Un'alternativa è utilizzare modelli linguistici di grandi dimensioni, preferibilmente con forti capacità di ragionamento, per valutare direttamente se un candidato è una buona corrispondenza per una query. In modo semplicistico, potremmo chiedere a un modello linguistico di grandi dimensioni specifico per il compito: "Su una scala da 1 a 10, questo documento è una buona corrispondenza per questa query?" I modelli esistenti potrebbero non essere ben adatti al compito, ma l'addestramento mirato e tecniche di prompting più sofisticate sono promettenti.
Non è impossibile per i modelli misurare la rilevanza, ma richiede un paradigma diverso dai modelli di embedding.
tagUsa i Tuoi Modelli per Ciò in cui Sono Bravi
L'effetto del bias della dimensione che abbiamo documentato sopra mostra uno dei limiti fondamentali dei modelli di embedding: sono eccellenti nel confrontare le cose ma inaffidabili nel misurare la rilevanza assoluta. Questa limitazione non è un difetto nel design—è una caratteristica intrinseca di come funzionano questi modelli.
Quindi cosa significa questo per te?
Primo, sii scettico sulle soglie dei coseni. Semplicemente non funzionano. Le misure di similarità del coseno producono numeri in virgola mobile che sembrano tentantemente oggettivi. Ma solo perché qualcosa produce numeri non significa che stia misurando qualcosa oggettivamente.
Secondo, considera soluzioni ibride. Gli embedding possono efficacemente restringere un grande insieme di elementi a candidati promettenti, dopo di che puoi applicare tecniche più sofisticate (e computazionalmente intensive) come reranker o LLM, o anche valutatori umani per determinare la rilevanza effettiva.
Terzo, quando progetti sistemi, pensa in termini di compiti piuttosto che di capacità. Il modello oggettivamente più intelligente, con i punteggi più alti nei benchmark è ancora uno spreco di denaro se non può fare il lavoro per cui lo hai acquistato.
Comprendere i limiti dei nostri modelli non è pessimistico – riflette un principio più ampio nelle applicazioni: Comprendere in cosa i tuoi modelli sono bravi, e in cosa non lo sono, è fondamentale per costruire sistemi affidabili ed efficaci. Proprio come non useremmo un martello per stringere una vite, non dovremmo usare modelli di embedding per compiti che non sono in grado di gestire. Rispetta ciò in cui i tuoi strumenti sono bravi.