Saltar al contenido
Lección 2 de 12

Embeddings a Fondo

8 min read

Que Son los Embeddings?

Un embedding es una representacion numerica de texto como un vector -- una lista de numeros de punto flotante que captura el significado semantico de la entrada. Dos fragmentos de texto que significan cosas similares produciran vectores que estan cerca en el espacio de embeddings. Dos fragmentos que significan cosas diferentes produciran vectores que estan lejos.

Esta es la base de todo en RAG. Cuando buscas documentos relevantes, no estas haciendo coincidencia de palabras clave. Estas comparando el significado de una consulta contra el significado del texto almacenado, medido por la distancia entre sus vectores.

Considera estas tres oraciones:

  1. "El gato se sento en la alfombra."
  2. "Un felino descanso sobre el tapete."
  3. "Los ingresos trimestrales superaron las proyecciones."

Las oraciones 1 y 2 casi no tienen palabras en comun, pero sus embeddings estaran muy cerca porque significan casi lo mismo. La oracion 3 comparte algunos patrones estructurales pero su embedding estara lejos de ambas porque el significado es completamente diferente.

Esto es lo que hace a la busqueda semantica tan poderosa. La busqueda por palabras clave fallaria en conectar las oraciones 1 y 2. La busqueda basada en embeddings las conecta sin esfuerzo.

Como el Texto se Convierte en un Vector

El proceso de generar un embedding implica pasar texto a traves de una red neuronal (el modelo de embedding) que ha sido entrenada con cantidades masivas de datos de texto. Durante el entrenamiento, el modelo aprende a posicionar texto semanticamente similar cerca en un espacio de alta dimensionalidad.

La salida es un arreglo de longitud fija de numeros de punto flotante. Por ejemplo, text-embedding-3-small de OpenAI produce un vector de 1536 dimensiones. Esto significa que cada fragmento de texto -- ya sea una sola palabra o un parrafo completo -- se mapea a una lista de 1536 numeros.

# Una vista simplificada de como luce un embedding
embedding = [0.0023, -0.0142, 0.0381, ..., -0.0091]  # 1536 flotantes

Los numeros individuales en el vector no son interpretables por humanos. No puedes mirar la dimension 47 y decir "esto representa el concepto de animales." El significado esta distribuido a traves de todas las dimensiones colectivamente. Pero matematicamente, estos vectores codifican informacion semantica rica que puede ser comparada, agrupada y buscada.

Modelos de Embedding Populares

Elegir el modelo de embedding correcto es una de las decisiones mas importantes en tu sistema RAG. Estas son las opciones principales:

APIs Comerciales

OpenAI text-embedding-3-small -- 1536 dimensiones, excelente relacion calidad-costo. Esta es la opcion predeterminada para la mayoria de los sistemas RAG en produccion. Maneja bien texto en ingles y multilingue, cuesta $0.02 por millon de tokens y tiene una ventana de contexto de 8191 tokens.

OpenAI text-embedding-3-large -- 3072 dimensiones, mayor precision para tareas de recuperacion complejas. Cuesta $0.13 por millon de tokens. Usalo cuando la precision de recuperacion es critica y el costo es secundario.

Cohere embed-v3 -- Disponible en multiples tamanos (1024 dimensiones para la version light, hasta 1024 para la version completa). Fuerte soporte multilingue con mas de 100 idiomas. Ofrece tipos de entrada especializados para consultas de busqueda vs. documentos.

Modelos de Codigo Abierto

BGE (BAAI General Embedding) -- Familia de modelos de la Academia de IA de Beijing. BGE-large-en-v1.5 es una opcion solida para ingles. BGE-m3 maneja bien casos multilingues. Gratis para ejecutar localmente.

E5 (EmbEddings from bidirEctional Encoder rEpresentations) -- La familia de embeddings de Microsoft. E5-large-v2 y E5-mistral-7b-instruct son de alto rendimiento. Las variantes ajustadas por instruccion permiten anteponer prefijos especificos de tarea.

GTE (General Text Embeddings) -- La oferta de Alibaba. GTE-large compite con modelos comerciales en muchos benchmarks. Bueno para despliegues autoalojados.

all-MiniLM-L6-v2 -- Un clasico de sentence-transformers. Solo 384 dimensiones y rapido de ejecutar. No es la mayor calidad, pero extremadamente practico para prototipado y entornos con recursos limitados.

Como Elegir

Para la mayoria de los equipos que comienzan: usa text-embedding-3-small de OpenAI. Es barato, rapido y suficientemente bueno para la gran mayoria de los casos de uso. Cambia a modelos de codigo abierto cuando necesites evitar dependencias de API, reducir costos a escala, o manejar dominios especializados donde un modelo ajustado supera a los de proposito general.

Tradeoffs de Dimensionalidad

Mas dimensiones generalmente capturan mas matices pero tienen costos:

  • Almacenamiento: Un vector de 3072 dimensiones ocupa el doble de espacio que uno de 1536 dimensiones.
  • Velocidad de busqueda: Mas dimensiones significan computaciones de similitud mas lentas, especialmente a escala.
  • Tamano del indice: Los indices de bases de datos vectoriales crecen con la dimensionalidad.

En la practica, 1536 dimensiones es el punto optimo para la mayoria de las aplicaciones. Bajar de 384 dimensiones comienza a degradar la calidad de recuperacion. Subir por encima de 3072 raramente proporciona mejoras significativas para los casos de uso tipicos de RAG.

Los modelos embedding-3 de OpenAI soportan un parametro dimensions que te permite truncar la salida a una dimensionalidad menor. Esto es util para experimentacion:

from openai import OpenAI

client = OpenAI()

# 1536 dimensiones completas
response = client.embeddings.create(
    model="text-embedding-3-small",
    input="Que es retrieval-augmented generation?"
)
full_embedding = response.data[0].embedding
print(f"Dimensiones completas: {len(full_embedding)}")  # 1536

# Truncado a 512 dimensiones
response = client.embeddings.create(
    model="text-embedding-3-small",
    input="Que es retrieval-augmented generation?",
    dimensions=512
)
truncated_embedding = response.data[0].embedding
print(f"Dimensiones truncadas: {len(truncated_embedding)}")  # 512

Metricas de Similitud

Una vez que tienes dos vectores, necesitas una forma de medir que tan similares son. Las dos metricas mas comunes son:

Similitud Coseno

Mide el angulo entre dos vectores, ignorando la magnitud. Retorna un valor entre -1 y 1, donde 1 significa direccion identica (mismo significado) y 0 significa ortogonal (sin relacion).

import numpy as np

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

La similitud coseno es la opcion predeterminada para la mayoria de los modelos de embedding porque los embeddings tipicamente se normalizan durante el entrenamiento, haciendo la magnitud irrelevante.

Producto Punto

Un calculo mas simple que no normaliza por magnitud. Si tus embeddings ya estan normalizados (vectores unitarios), el producto punto y la similitud coseno producen resultados identicos.

def dot_product(a, b):
    return np.dot(a, b)

En la practica, la mayoria de las bases de datos vectoriales te permiten elegir la metrica al momento de crear el indice. Usa similitud coseno a menos que tengas una razon especifica para usar producto punto o distancia euclidiana.

Generando Embeddings: Codigo Practico

Con OpenAI

from openai import OpenAI

client = OpenAI()  # Usa la variable de entorno OPENAI_API_KEY

def get_embeddings(texts: list[str], model: str = "text-embedding-3-small") -> list[list[float]]:
    """Genera embeddings para una lista de textos."""
    response = client.embeddings.create(
        model=model,
        input=texts
    )
    return [item.embedding for item in response.data]

# Embed una sola consulta
query_embedding = get_embeddings(["Como funciona la fotosintesis?"])[0]

# Embed multiples documentos en lote
documents = [
    "La fotosintesis convierte la luz solar en energia quimica en las plantas.",
    "La bolsa de valores cerro al alza el martes.",
    "Los cloroplastos son los organelos donde ocurre la fotosintesis.",
]
doc_embeddings = get_embeddings(documents)

# Comparar consulta con cada documento
for i, doc_emb in enumerate(doc_embeddings):
    similarity = np.dot(query_embedding, doc_emb)
    print(f"Documento {i}: similitud = {similarity:.4f}")

Con Sentence Transformers (Codigo Abierto)

from sentence_transformers import SentenceTransformer
import numpy as np

# Cargar modelo localmente (se descarga en la primera ejecucion)
model = SentenceTransformer("BAAI/bge-large-en-v1.5")

# Generar embeddings
query = "Como funciona la fotosintesis?"
documents = [
    "La fotosintesis convierte la luz solar en energia quimica en las plantas.",
    "La bolsa de valores cerro al alza el martes.",
    "Los cloroplastos son los organelos donde ocurre la fotosintesis.",
]

query_embedding = model.encode(query, normalize_embeddings=True)
doc_embeddings = model.encode(documents, normalize_embeddings=True)

# Calcular similitudes
similarities = np.dot(doc_embeddings, query_embedding)
for i, sim in enumerate(similarities):
    print(f"Documento {i}: similitud = {sim:.4f}")

Consejos para Embeddings en Produccion

  • Agrupa tus solicitudes. Enviar 100 textos en una sola llamada API es mucho mas barato y rapido que 100 llamadas individuales.
  • Almacena embeddings en cache. Nunca re-generes el embedding de texto que no ha cambiado. Almacena el embedding junto con el texto fuente en tu base de datos.
  • Normaliza consistentemente. Si normalizas embeddings al almacenarlos, asegurate de tambien normalizar al momento de la consulta.
  • Usa el mismo modelo. Siempre usa el mismo modelo de embedding para documentos y consultas. Mezclar modelos produce puntajes de similitud sin sentido.
  • Haz benchmark con tus datos. Los rankings del leaderboard MTEB son utiles, pero el mejor modelo para tu caso de uso depende de tu dominio especifico, idioma y patrones de consulta. Siempre prueba con datos reales.

Los embeddings son el puente entre el lenguaje humano y la similitud matematica. En la siguiente leccion, aprenderas donde almacenar estos vectores y como buscarlos a escala.