La similitud semántica es lo que los modelos de embeddings están construidos para medir, pero esas mediciones están influenciadas por muchos factores de sesgo. En este artículo, vamos a analizar una fuente generalizada de sesgo en los modelos de embeddings de texto: el tamaño de entrada.
Los embeddings de textos más largos generalmente muestran puntuaciones de similitud más altas cuando se comparan con otros embeddings de texto, independientemente de qué tan similar sea el contenido real. Si bien los textos verdaderamente similares seguirán teniendo puntuaciones de similitud más altas que los no relacionados, los textos más largos introducen un sesgo: haciendo que sus embeddings parezcan más similares en promedio simplemente debido a su longitud.
Esto tiene consecuencias reales. Significa que los modelos de embeddings, por sí solos, no pueden medir muy bien la relevancia. Con la búsqueda basada en embeddings, siempre hay una mejor coincidencia, pero el sesgo de tamaño significa que no se puede usar la puntuación de similitud para decidir si la mejor coincidencia, o cualquier coincidencia menor, son realmente relevantes. No se puede decir que, por ejemplo, cualquier coincidencia con un coseno superior a 0.75 es relevante porque puede haber fácilmente un documento largo que coincida a ese nivel a pesar de ser completamente irrelevante.
Vamos a demostrar esto con algunos ejemplos simples y mostrar cómo la similitud del coseno entre embeddings de texto no puede servir como una forma general de evaluar
tagVisualizando el Sesgo de Tamaño
Para mostrar cómo se manifiesta el sesgo de tamaño, vamos a usar el último modelo de embeddings de Jina AI jina-embeddings-v3 con la opción de tarea text-matching
. También usaremos documentos de texto de un conjunto de datos de IR ampliamente utilizado: El conjunto de datos CISI, que puedes descargar desde Kaggle.

Este conjunto de datos se usa para entrenar sistemas de IR, por lo que contiene tanto consultas como documentos para emparejarlas. Solo vamos a usar los documentos, que están todos en el archivo CISI.ALL
. Puedes descargarlo desde la línea de comandos en una fuente alternativa en GitHub con el comando:
wget https://raw.githubusercontent.com/GianRomani/CISI-project-MLOps/refs/heads/main/CISI.ALL
CISI contiene 1,460 documentos. Las estadísticas básicas sobre los tamaños de los textos y sus distribuciones de tamaño se resumen en la tabla e histogramas a continuación:
en Palabras | en Oraciones | |
---|---|---|
Tamaño promedio del documento | 119.2 | 4.34 |
Desviación estándar | 63.3 | 2.7 |
Tamaño máximo | 550 | 38 |
Tamaño mínimo | 8 | 1 |


Vamos a leer los documentos en Python y obtener embeddings para ellos. El código siguiente asume que el archivo CISI.ALL
está en el directorio local:
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
Esto llenará la lista docs
con 1,460 documentos. Puedes inspeccionarlos:
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.
Ahora, vamos a construir embeddings para cada texto usando jina-embeddings-v3. Para esto, necesitarás una clave API del sitio web de Jina AI. Puedes obtener una clave gratuita para hasta 1 millón de tokens de embeddings, lo cual es suficiente para este artículo.
Coloca tu clave en una variable:
api_key = "<Your Key>"
Ahora, genera embeddings usando la tarea text-matching
con jina-embeddings-v3. Este código procesa los textos en docs
en lotes de 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
Para cada texto, habrá un embedding de 1024 dimensiones en la lista embeddings
. Puedes ver cómo se ve:
print(embeddings[0])
array([ 0.0352382 , -0.00594871, 0.03808545, ..., -0.01147173,
-0.01710563, 0.01109511], shape=(1024,))),
Ahora, calculamos los cosenos entre todos los pares de embeddings. Primero, definamos la función del 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)))
Luego, calculamos los cosenos de cada uno de los 1,460 embeddings comparados con los otros 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))
El resultado es una lista de 2,130,140 valores. Su distribución debería aproximarse a los cosenos entre documentos "aleatorios" en el mismo idioma y registro. La tabla e histograma a continuación resumen los resultados.
Número de textos | 1,460 |
---|---|
Número de cosenos | 2,130,140 |
Promedio | 0.343 |
Desviación estándar | 0.116 |

Estos documentos, aunque no están relacionados entre sí, típicamente tienen cosenos muy por encima de cero. Podríamos estar tentados a establecer un umbral de 0.459 (promedio + 1 desviación estándar), o tal vez redondearlo a 0.5, y decir que cualquier par de documentos con un coseno menor que ese debe estar en gran parte no relacionado.
Pero hagamos el mismo experimento con textos más pequeños. Usaremos la biblioteca nltk
para dividir cada documento en oraciones:
import nltk
sentences = []
for doc in docs:
sentences.extend(nltk.sent_tokenize(doc))
Esto produce 6,331 oraciones con una longitud promedio de 27.5 palabras y una desviación estándar de 16.6. En el histograma siguiente, la distribución de tamaño de las oraciones está en rojo, y para documentos completos, está en azul, para que puedas compararlos.

Usaremos el mismo modelo y métodos para obtener embeddings para cada oración:
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
Y luego calcular el coseno del embedding de cada oración con cada una de las otras oraciones:
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))
El resultado es una cantidad bastante mayor de valores de coseno: 40.075.230, como se resume en la tabla siguiente:
Number of sentences | 6,331 |
---|---|
Number of cosines | 40,075,230 |
Average | 0.254 |
Std. Deviation | 0.116 |
Los cosenos entre oraciones son considerablemente más bajos en promedio que los cosenos entre documentos completos. El histograma siguiente compara sus distribuciones, y se puede ver claramente que los pares de oraciones forman una distribución casi idéntica a la de los pares de documentos pero desplazada hacia la izquierda.

Para comprobar que esta dependencia del tamaño es robusta, calculemos todos los cosenos entre oraciones y documentos y añadámoslos al histograma. Su información se resume en la tabla siguiente:
Number of texts | 6,331 sentences & 1,460 documents |
---|---|
Number of cosines | 9,243,260 |
Average | 0.276 |
Std. Deviation | 0.119 |
La línea verde de abajo muestra la distribución de los cosenos entre oraciones y documentos. Podemos ver que esta distribución se ajusta perfectamente entre los cosenos documento-documento y los cosenos oración-oración, mostrando que el efecto del tamaño involucra a ambos textos que se están comparando, tanto el más grande como el más pequeño.

Hagamos otra prueba concatenando los documentos en grupos de diez, creando 146 documentos mucho más grandes y midiendo sus cosenos. El resultado se resume a continuación:
Number of texts | 146 documents |
---|---|
Number of cosines | 21,170 |
Average | 0.658 |
Std. Deviation | 0.09 |

Esto está muy a la derecha de las otras distribuciones. Un umbral de coseno de 0.5 nos diría que casi todos estos documentos están relacionados entre sí. Para excluir documentos irrelevantes de este tamaño, tendríamos que establecer el umbral mucho más alto, tal vez hasta 0.9, lo que sin duda excluiría buenas coincidencias entre los documentos más pequeños.
Esto demuestra que no podemos usar umbrales mínimos de coseno para estimar qué tan buena es una coincidencia, al menos no sin tener en cuenta de alguna manera el tamaño del documento.
tag¿Qué Causa el Sesgo por Tamaño?
El sesgo por tamaño en los embeddings no es como los sesgos posicionales en modelos de contexto largo. No es causado por las arquitecturas. Tampoco se trata inherentemente del tamaño. Por ejemplo, si hubiéramos creado documentos más largos simplemente concatenando copias del mismo documento una y otra vez, no mostraría un sesgo por tamaño.
El problema es que los textos largos dicen más cosas. Incluso si están limitados por un tema y propósito, el objetivo de escribir más palabras es decir más cosas.
Los textos más largos, al menos del tipo que la gente normalmente crea, naturalmente producirán embeddings que se "dispersan" sobre más espacio semántico. Si un texto dice más cosas, su embedding tendrá un ángulo menor con otros vectores en promedio, independientemente del tema del texto.
tagMidiendo la Relevancia
La lección de este post es que no se pueden usar cosenos entre vectores semánticos por sí solos para determinar si algo es una buena coincidencia, solo que es la mejor coincidencia entre las disponibles. Hay que hacer algo más además de calcular cosenos para verificar la utilidad y validez de las mejores coincidencias.
Podrías intentar la normalización. Si puedes medir el sesgo por tamaño empíricamente, puede ser posible compensarlo. Sin embargo, este enfoque podría no ser muy robusto. Lo que funciona para un conjunto de datos probablemente no funcionará para otro.
La codificación asimétrica consulta-documento, proporcionada en jina-embeddings-v3, reduce el sesgo por tamaño en los modelos de embedding pero no lo elimina. El propósito de la codificación asimétrica es codificar los documentos para que estén menos "dispersos" y codificar las consultas para que lo estén más.
La línea roja en el histograma siguiente es la distribución de los cosenos documento-documento usando codificación asimétrica con jina-embeddings-v3 – cada documento se codifica usando las banderas retrieval.query
y retrieval.passage
, y cada embedding de consulta de documento se compara con cada embedding de pasaje de documento que no sea del mismo documento. El coseno promedio es 0.200, con una desviación estándar de 0.124.
Estos cosenos son considerablemente más pequeños que los que encontramos arriba para los mismos documentos usando la bandera text-matching
, como se muestra en el histograma siguiente.

Sin embargo, la codificación asimétrica no ha eliminado el sesgo por tamaño. El histograma siguiente compara los cosenos para documentos completos y oraciones usando codificación asimétrica.

El promedio para los cosenos de oraciones es 0.124, por lo que usando codificación asimétrica, la diferencia entre el coseno promedio de oraciones y el coseno promedio de documentos es 0.076. La diferencia en promedios para la codificación simétrica es 0.089. El cambio en el sesgo por tamaño es insignificante.
Aunque la codificación asimétrica mejora los embeddings para la recuperación de información, no es mejor para medir la relevancia de las coincidencias.
tagPosibilidades Futuras
El enfoque del reranker, por ejemplo jina-reranker-v2-base-multilingual y jina-reranker-m0, es una forma alternativa de puntuar las coincidencias consulta-documento que ya sabemos que mejora la precisión de las consultas. Las puntuaciones del reranker no están normalizadas, por lo que tampoco funcionan como medidas de similitud objetivas. Sin embargo, se calculan de manera diferente, y podría ser posible normalizar las puntuaciones del reranker de manera que sean buenos estimadores de relevancia.
Otra alternativa es usar modelos de lenguaje grandes, preferiblemente con fuertes capacidades de razonamiento, para evaluar directamente si un candidato es una buena coincidencia para una consulta. De manera simplista, podríamos preguntarle a un modelo de lenguaje grande específico para la tarea: "¿En una escala del 1 al 10, este documento es una buena coincidencia para esta consulta?" Los modelos existentes podrían no estar bien adaptados para la tarea, pero el entrenamiento focalizado y técnicas de prompt más sofisticadas son prometedores.
No es imposible que los modelos midan la relevancia, pero requiere un paradigma diferente de los modelos de embedding.
tagUsa tus Modelos para lo que son Buenos
El efecto de sesgo por tamaño que hemos documentado arriba muestra una de las limitaciones fundamentales de los modelos de embedding: Son excelentes para comparar cosas pero poco fiables para medir la relevancia absoluta. Esta limitación no es un defecto en el diseño—es una característica inherente de cómo funcionan estos modelos.
Entonces, ¿qué significa esto para ti?
Primero, sé escéptico con los umbrales de coseno. Simplemente no funcionan. Las medidas de similitud por coseno producen números de punto flotante que parecen tentadoramente objetivos. Pero el hecho de que algo produzca números no significa que esté midiendo algo objetivamente.
Segundo, considera soluciones híbridas. Los embeddings pueden reducir eficientemente un gran conjunto de elementos a candidatos prometedores, después de lo cual puedes aplicar técnicas más sofisticadas (y computacionalmente intensivas) como rerankers o LLMs, o incluso evaluadores humanos para determinar la relevancia real.
Tercero, al diseñar sistemas, piensa en términos de tareas en lugar de capacidades. El modelo objetivamente más inteligente, con las puntuaciones más altas en benchmarks sigue siendo un desperdicio de dinero si no puede hacer el trabajo para el que lo conseguiste.
Entender las limitaciones de nuestros modelos no es pesimista – refleja un principio más amplio en las aplicaciones: Entender para qué son buenos tus modelos, y para qué no, es crítico para construir sistemas confiables y efectivos. Al igual que no usaríamos un martillo para apretar un tornillo, no deberíamos usar modelos de embedding para tareas que no pueden manejar. Respeta para qué son buenos tus herramientas.