Embeddings: del texto a las coordenadas del significado

Cómo un modelo convierte una frase en una lista de números y por qué eso permite buscar por significado en lugar de por palabras.

Embeddings: del texto a las coordenadas del significado
Foto de Francisco Ferreira en Pexels

Tercera entrega de la serie. Toda la recuperación de un sistema RAG descansa sobre una idea: que el significado de un texto se puede escribir como una lista de números y que la proximidad entre esas listas se parece a la proximidad entre significados. Conviene entender hasta dónde llega esa idea y dónde empieza a fallar.

Un vector es una posición, no una definición

Un modelo de embeddings recibe un texto y devuelve una lista de números en coma flotante. Si el modelo produce vectores de 768 dimensiones, la frase «el plazo de devolución es de catorce días» se convierte en 768 cifras. Nada en esas cifras es legible: no hay una dimensión que signifique «plazo» ni otra que signifique «devolución». Lo único que importa es la posición relativa.

Esa posición se aprende durante el entrenamiento. Al modelo se le muestran millones de pares de textos y se le enseña a acercar los que están relacionados y a separar los que no. El resultado es un espacio donde la geometría codifica semántica: los textos sobre devoluciones acaban en una región, los textos sobre facturación en otra, y la distancia entre dos puntos aproxima cuánto tienen que ver entre sí.

Una consecuencia que conviene interiorizar pronto: un vector no significa nada por sí solo. Sólo significa algo comparado con otros vectores del mismo espacio, producidos por el mismo modelo. Vectores de modelos distintos, o de versiones distintas del mismo modelo, no son comparables aunque tengan el mismo número de dimensiones. Compararlos no da error, da ruido.

Cómo se mide la proximidad

Hay tres formas habituales de medir cuánto se parecen dos vectores, y la diferencia entre ellas importa más de lo que parece.

Producto escalar

Se multiplican las componentes una a una y se suman. Para dos vectores a y b: a·b = a₁b₁ + a₂b₂ + … + aₙbₙ. Es la operación más barata y la que ejecutan por debajo casi todos los índices. Tiene un sesgo: crece con la magnitud de los vectores, así que un texto largo con vector «grande» puede ganar a uno corto más relevante.

Similitud coseno

Es el producto escalar dividido por el producto de las longitudes de ambos vectores. Mide sólo el ángulo entre ellos, ignorando su magnitud. Va de -1 (opuestos) a 1 (misma dirección), y en la práctica, con embeddings de texto, los valores casi nunca bajan de 0 porque el espacio aprendido no usa direcciones opuestas de forma sistemática. Es la métrica por defecto en la inmensa mayoría de sistemas RAG.

Distancia euclídea

La distancia en línea recta: la raíz de la suma de las diferencias al cuadrado. Tiene en cuenta magnitud y dirección a la vez. Se usa menos en texto, más en visión y en datos numéricos.

El truco de la normalización

Normalizar un vector es dividirlo por su propia longitud, dejándolo con magnitud 1. Sobre vectores normalizados, el producto escalar y la similitud coseno dan exactamente el mismo número, y el orden que produce la distancia euclídea es el inverso del que produce el coseno. Es decir: si normalizas al indexar y al consultar, las tres métricas ordenan igual y puedes usar la más rápida.

Por eso muchos modelos devuelven ya vectores normalizados y muchas bases vectoriales recomiendan el producto escalar cuando lo están. El error clásico es normalizar los documentos pero no las consultas, o al revés: los rangos de puntuación dejan de ser comparables y cualquier umbral fijo que hayas puesto se vuelve arbitrario.

Un ejemplo con números pequeños

Los espacios reales tienen cientos de dimensiones y no se pueden dibujar. Con tres sí. Imagina un espacio juguete donde las dimensiones fuesen, aproximadamente, «trata de plazos», «trata de dinero» y «trata de envíos». Cuatro textos:

Texto Vector
A: «tienes catorce días para devolver el pedido» (0,9 · 0,1 · 0,4)
B: «¿cuánto tiempo tengo para hacer una devolución?» (0,8 · 0,2 · 0,5)
C: «el reembolso se abona en la tarjeta original» (0,2 · 0,9 · 0,3)
D: «el envío estándar tarda tres días laborables» (0,4 · 0,1 · 0,9)

Calculemos el coseno entre la pregunta B y cada uno de los otros. Primero las longitudes:

|A| = √(0,9² + 0,1² + 0,4²) = √(0,81 + 0,01 + 0,16) = √0,98 ≈ 0,990
|B| = √(0,8² + 0,2² + 0,5²) = √(0,64 + 0,04 + 0,25) = √0,93 ≈ 0,964
|C| = √(0,2² + 0,9² + 0,3²) = √(0,04 + 0,81 + 0,09) = √0,94 ≈ 0,970
|D| = √(0,4² + 0,1² + 0,9²) = √(0,16 + 0,01 + 0,81) = √0,98 ≈ 0,990

B·A = 0,8·0,9 + 0,2·0,1 + 0,5·0,4 = 0,72 + 0,02 + 0,20 = 0,94
B·C = 0,8·0,2 + 0,2·0,9 + 0,5·0,3 = 0,16 + 0,18 + 0,15 = 0,49
B·D = 0,8·0,4 + 0,2·0,1 + 0,5·0,9 = 0,32 + 0,02 + 0,45 = 0,79

cos(B,A) = 0,94 / (0,964 · 0,990) = 0,94 / 0,954 ≈ 0,985
cos(B,C) = 0,49 / (0,964 · 0,970) = 0,49 / 0,935 ≈ 0,524
cos(B,D) = 0,79 / (0,964 · 0,990) = 0,79 / 0,954 ≈ 0,828

El resultado es exactamente lo que se busca. La pregunta B no comparte con A ni el verbo ni el sustantivo principal —«¿cuánto tiempo tengo?» frente a «tienes catorce días»— y aun así queda pegada a ella (0,985). El texto C comparte el dominio de las devoluciones pero habla de otra cosa, y baja a 0,524.

Y aparece el primer aviso: D, que trata de envíos y no de devoluciones, saca 0,828. Más que C. La razón es que D habla de plazos y de días, y en este espacio esa componente pesa. Si recuperas los dos primeros resultados, te llevas A y D. Uno correcto y uno plausible pero equivocado. Esto no es un defecto del ejemplo: es exactamente lo que pasa en producción, sólo que con 768 dimensiones y sin que puedas verlo.

Dimensionalidad: cuántos números hacen falta

Los modelos actuales producen vectores de entre 384 y unos pocos miles de dimensiones. Más dimensiones permiten codificar distinciones más finas, pero cuestan en tres frentes a la vez: memoria del índice (que crece linealmente), tiempo de cálculo de cada comparación y tiempo de construcción del índice.

La ganancia de calidad, en cambio, no es lineal: entre 384 y 768 suele notarse, entre 1.536 y 3.072 mucho menos, y depende del dominio. Para un corpus de decenas de miles de fragmentos en un dominio acotado, un modelo de 384 dimensiones bien elegido puede rendir igual que uno de 1.536 y ocupar la cuarta parte.

Algunos modelos recientes admiten truncar el vector a menos dimensiones sin reentrenar, porque están entrenados para concentrar la información en las primeras componentes. Es una salida cómoda cuando la memoria aprieta, pero conviene medir la pérdida sobre tu propio corpus antes de darla por buena.

Por qué a veces queda cerca lo que no debería

El caso incómodo es el de los antónimos y las negaciones. «El contrato incluye mantenimiento» y «el contrato no incluye mantenimiento» son afirmaciones opuestas, y sus vectores quedan muy cerca. Igual pasa con «se permite el reembolso» y «se prohíbe el reembolso», o con «caliente» y «frío».

No es un error de implementación, es una consecuencia del entrenamiento. Los modelos de embeddings aprenden a partir de qué textos aparecen en contextos similares, y una palabra y su contraria aparecen exactamente en los mismos contextos: hablan del mismo tema, del mismo objeto, con la misma estructura. El modelo captura muy bien de qué trata un texto y bastante peor qué afirma sobre ello.

Hay dos consecuencias prácticas. La primera es que un sistema RAG puede recuperar el fragmento que dice lo contrario de lo que el usuario pregunta, y el modelo generador, si es obediente, responderá lo contrario con toda seguridad. La segunda es que no se arregla cambiando de modelo de embeddings: se mitiga recuperando más candidatos y dejando que un reordenador —un modelo que sí lee la pregunta y el fragmento juntos— decida cuál responde de verdad.

Otros casos de proximidad engañosa que conviene conocer: los números y las fechas, que el modelo trata como texto y no como cantidades, de modo que «14 días» y «40 días» quedan casi igual de cerca de una pregunta sobre plazos; los códigos y referencias de producto, donde la similitud semántica es prácticamente inútil; y los textos muy cortos, cuyos vectores tienden a ser inestables porque hay poco contenido del que extraer señal.

Límites que conviene tener presentes

Un embedding es una compresión con pérdida. Un fragmento de 500 palabras reducido a 768 números pierde información necesariamente, y lo que se pierde primero es el detalle concreto: la cifra exacta, la excepción, la condición. Cuanto más largo es el fragmento, más se promedia su contenido y menos representativo es el vector de cualquiera de sus partes. Este es el argumento técnico contra los fragmentos enormes, y tiene más peso que la comodidad de meter más texto en cada uno.

Además, la similitud no es relevancia. Que un fragmento sea el más cercano no significa que contenga la respuesta: significa que es el más cercano de los que hay. Si la respuesta no está en el corpus, el sistema devolverá igualmente cinco fragmentos con puntuaciones razonables. Un umbral mínimo de similitud ayuda a detectar estos casos, pero es un valor que hay que calibrar contra un conjunto de preguntas reales, no fijar a ojo.

Qué viene ahora

Con los vectores ya calculados hace falta un sitio donde guardarlos y buscarlos. La siguiente entrega explica por qué una base de datos convencional no sirve para esto: qué aporta una base vectorial frente a un LIKE o una búsqueda de texto completo, qué operaciones ofrece, cómo se combina con el filtrado por metadatos y qué problema nuevo introduce al dejar de ser exacta.

Si prefieres leerlo todo de golpe y en orden, los tres libros gratuitos de los que nace esta serie están disponibles en PDF y EPUB: RAG para tu negocio (nivel básico), RAG por dentro (intermedio) y RAG en producción (avanzado).


Aviso de contenido generado con inteligencia artificial.
Este artículo ha sido redactado con la asistencia de sistemas de inteligencia artificial y revisado antes de su publicación. Se informa de ello de forma expresa en cumplimiento de la normativa vigente sobre transparencia en el uso de IA.