Los embeddings son una de las ideas más poderosas de la inteligencia artificial moderna. En esencia, un embedding es una representación numérica de datos (texto, imágenes, audio) en un espacio vectorial de alta dimensión, donde las relaciones semánticas se preservan como relaciones geométricas. En este artículo aprenderás qué son los embeddings, la matemática detrás de ellos, y cómo usarlos en Python con aplicaciones prácticas como búsqueda semántica y clasificación de texto.
¿Qué es un Embedding?
Un embedding es una función que mapea un objeto discreto (una palabra, una oración, un documento) a un vector de números reales en un espacio de dimensión \( d \):
\[ f: \text{Objeto} \rightarrow \mathbb{R}^d \]Por ejemplo, la palabra "gato" podría mapearse al vector \( [0.23, -0.45, 0.89, \ldots] \) de 1536 dimensiones. Lo fascinante es que en este espacio vectorial, las palabras con significados similares quedan cerca entre sí:
- \( \text{embedding}(\text{"gato"}) \approx \text{embedding}(\text{"felino"}) \) — vectores cercanos
- \( \text{embedding}(\text{"gato"}) \neq \text{embedding}(\text{"automóvil"}) \) — vectores lejanos
Esta propiedad se resume en la famosa frase de la lingüística computacional: "Conocerás una palabra por la compañía que mantiene" (J.R. Firth, 1957).
La Matemática detrás de los Embeddings
Espacios Vectoriales y Similitud
Los embeddings viven en un espacio vectorial \( \mathbb{R}^d \), donde \( d \) es la dimensionalidad del embedding (típicamente entre 100 y 3072 dimensiones). Para medir qué tan "parecidos" son dos embeddings, usamos la similitud coseno:
\[ \text{sim}(\mathbf{a}, \mathbf{b}) = \cos(\theta) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \, \|\mathbf{b}\|} = \frac{\sum_{i=1}^{d} a_i b_i}{\sqrt{\sum_{i=1}^{d} a_i^2} \cdot \sqrt{\sum_{i=1}^{d} b_i^2}} \]El resultado es un valor entre -1 y 1:
| Valor | Interpretación | Ejemplo |
|---|---|---|
| \( \approx 1 \) | Muy similares (misma dirección) | "perro" vs "cachorro" |
| \( \approx 0 \) | Sin relación (ortogonales) | "perro" vs "contabilidad" |
| \( \approx -1 \) | Opuestos semánticos | "bueno" vs "malo" (en algunos modelos) |
Aritmética de Vectores Semánticos
Uno de los descubrimientos más sorprendentes de Word2Vec (Mikolov et al., 2013) fue que los embeddings capturan relaciones analógicas mediante operaciones vectoriales:
\[ \text{embedding}(\text{"rey"}) - \text{embedding}(\text{"hombre"}) + \text{embedding}(\text{"mujer"}) \approx \text{embedding}(\text{"reina"}) \]Esto demuestra que los embeddings no solo capturan similitud, sino que codifican relaciones semánticas estructuradas como género, tiempo verbal, relaciones geográficas (país-capital), y más.
Tipos de Embeddings
La evolución de los embeddings ha sido rápida. Aquí un resumen de las principales técnicas:
| Técnica | Año | Dimensiones | Características |
|---|---|---|---|
| Word2Vec | 2013 | 100-300 | Embeddings por palabra, CBOW y Skip-gram |
| GloVe | 2014 | 50-300 | Factorización de matriz de co-ocurrencia |
| FastText | 2016 | 100-300 | Sub-palabras, maneja palabras desconocidas |
| BERT | 2018 | 768 | Contextuales, bidireccionales |
| Sentence-BERT | 2019 | 384-768 | Optimizados para oraciones completas |
| OpenAI text-embedding-3 | 2024 | 256-3072 | Estado del arte, API cloud |
Tip: Los embeddings modernos como los de OpenAI o Sentence-BERT generan vectores para oraciones completas, no solo palabras individuales. Esto los hace mucho más útiles para aplicaciones reales como búsqueda semántica y RAG (Retrieval-Augmented Generation).
Embeddings en Python: Implementación Práctica
Opción 1: Sentence-Transformers (Local, Gratuito)
La librería sentence-transformers permite generar embeddings de alta calidad localmente, sin depender de APIs externas:
from sentence_transformers import SentenceTransformer
import numpy as np
# Cargar modelo pre-entrenado (se descarga la primera vez ~90MB)
modelo = SentenceTransformer('all-MiniLM-L6-v2')
# Textos de ejemplo
textos = [
"Python es un lenguaje de programación versátil",
"La serpiente pitón es un reptil grande",
"JavaScript se usa para desarrollo web",
"Django es un framework web de Python",
"Los reptiles son animales de sangre fría"
]
# Generar embeddings
embeddings = modelo.encode(textos)
print(f"Forma de los embeddings: {embeddings.shape}")
# Output: (5, 384) → 5 textos, 384 dimensiones cada uno
# Calcular similitud coseno entre todos los pares
from sklearn.metrics.pairwise import cosine_similarity
similitudes = cosine_similarity(embeddings)
print("\nMatriz de similitud:")
for i, texto_i in enumerate(textos):
for j, texto_j in enumerate(textos):
if j > i:
print(f" {similitudes[i][j]:.3f} | '{texto_i[:40]}' ↔ '{texto_j[:40]}'")
En este ejemplo verás que "Python es un lenguaje..." y "Django es un framework web de Python" tendrán alta similitud, mientras que "La serpiente pitón..." estará más cerca de "Los reptiles son animales...".
Opción 2: API de OpenAI
Si necesitas embeddings de máxima calidad y no te importa el costo (es muy económico), la API de OpenAI es excelente:
from openai import OpenAI
import numpy as np
client = OpenAI() # Usa OPENAI_API_KEY del entorno
def obtener_embedding(texto, modelo="text-embedding-3-small"):
"""Obtiene el embedding de un texto usando la API de OpenAI."""
respuesta = client.embeddings.create(
input=texto,
model=modelo
)
return np.array(respuesta.data[0].embedding)
# Generar embeddings
textos = [
"¿Cómo instalar Python en Windows?",
"Tutorial de instalación de Python para principiantes",
"Receta de pastel de chocolate",
"Guía para configurar un entorno de desarrollo Python"
]
embeddings = [obtener_embedding(t) for t in textos]
print(f"Dimensiones: {len(embeddings[0])}")
# Output: 1536 (text-embedding-3-small)
# Similitud coseno manual
def similitud_coseno(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Comparar el primer texto con todos los demás
consulta = textos[0]
print(f"\nConsulta: '{consulta}'")
for i, texto in enumerate(textos[1:], 1):
sim = similitud_coseno(embeddings[0], embeddings[i])
print(f" {sim:.4f} → '{texto}'")
Aplicación Práctica: Búsqueda Semántica
La aplicación más común de los embeddings es la búsqueda semántica: encontrar documentos relevantes no por coincidencia exacta de palabras, sino por significado. A diferencia de una búsqueda por keywords, la búsqueda semántica entiende que "cómo resolver errores en código" es similar a "debugging y depuración de programas".
from sentence_transformers import SentenceTransformer
import numpy as np
modelo = SentenceTransformer('all-MiniLM-L6-v2')
# Base de conocimiento (simulando artículos de un blog)
articulos = [
{"titulo": "Guía de Pandas para análisis de datos",
"contenido": "Pandas es la librería principal para manipulación de datos tabulares en Python. Permite leer CSV, filtrar datos y crear gráficas."},
{"titulo": "Introducción a Machine Learning",
"contenido": "El aprendizaje automático permite a las computadoras aprender patrones a partir de datos sin ser programadas explícitamente."},
{"titulo": "Django REST Framework tutorial",
"contenido": "Aprende a crear APIs REST con Django. Incluye serializers, viewsets, autenticación por tokens y permisos."},
{"titulo": "Redes neuronales desde cero",
"contenido": "Las redes neuronales son modelos inspirados en el cerebro humano, compuestos por capas de neuronas artificiales con pesos y sesgos."},
{"titulo": "Web scraping con BeautifulSoup",
"contenido": "Extrae datos de páginas web usando Python. BeautifulSoup parsea HTML y permite navegar el DOM con selectores CSS."},
{"titulo": "Visualización de datos con Matplotlib",
"contenido": "Crea gráficas profesionales en Python. Matplotlib permite generar gráficos de barras, líneas, dispersión y más."},
]
# Pre-calcular embeddings de los artículos
textos_articulos = [f"{a['titulo']}. {a['contenido']}" for a in articulos]
embeddings_articulos = modelo.encode(textos_articulos)
def buscar(consulta, top_k=3):
"""Búsqueda semántica: encuentra los artículos más relevantes."""
embedding_consulta = modelo.encode([consulta])[0]
# Calcular similitud coseno con todos los artículos
similitudes = np.dot(embeddings_articulos, embedding_consulta) / (
np.linalg.norm(embeddings_articulos, axis=1) * np.linalg.norm(embedding_consulta)
)
# Ordenar por similitud descendente
indices = np.argsort(similitudes)[::-1][:top_k]
print(f"Búsqueda: '{consulta}'\n")
for idx in indices:
print(f" [{similitudes[idx]:.3f}] {articulos[idx]['titulo']}")
print()
# Ejemplos de búsqueda semántica
buscar("quiero aprender a trabajar con tablas y datos")
buscar("cómo hacer inteligencia artificial")
buscar("necesito extraer información de internet")
buscar("crear un backend con Python")
Tip: En producción, no calculas similitudes comparando contra todos los vectores. Usas bases de datos vectoriales como Pinecone, ChromaDB, Qdrant o pgvector (PostgreSQL) que optimizan la búsqueda con índices como HNSW o IVF.
Aplicación Práctica: Clasificación de Texto
Otra aplicación poderosa es usar embeddings como features para clasificación. En lugar de usar TF-IDF o bag-of-words, representas cada texto como su embedding y entrenas un clasificador simple encima:
from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import numpy as np
modelo = SentenceTransformer('all-MiniLM-L6-v2')
# Dataset de ejemplo: clasificar preguntas por categoría
datos = [
("¿Cómo instalar NumPy?", "python"),
("¿Qué es un DataFrame?", "python"),
("¿Cómo crear una lista en Python?", "python"),
("¿Para qué sirve pip?", "python"),
("Explica las funciones lambda", "python"),
("¿Qué es una red neuronal?", "ia"),
("¿Cómo funciona GPT?", "ia"),
("Diferencia entre supervised y unsupervised", "ia"),
("¿Qué es transfer learning?", "ia"),
("¿Cómo entrenar un modelo de clasificación?", "ia"),
("¿Cómo hacer un SELECT en SQL?", "datos"),
("¿Qué es un JOIN?", "datos"),
("Diferencia entre SQL y NoSQL", "datos"),
("¿Cómo conectar Python a PostgreSQL?", "datos"),
("¿Qué es un índice en base de datos?", "datos"),
]
textos = [d[0] for d in datos]
etiquetas = [d[1] for d in datos]
# Generar embeddings
embeddings = modelo.encode(textos)
# Entrenar clasificador
X_train, X_test, y_train, y_test = train_test_split(
embeddings, etiquetas, test_size=0.3, random_state=42
)
clf = LogisticRegression(max_iter=1000)
clf.fit(X_train, y_train)
# Evaluar
print(classification_report(y_test, clf.predict(X_test)))
# Probar con textos nuevos
nuevas_preguntas = [
"¿Cómo usar decoradores en Python?",
"Explica el algoritmo de backpropagation",
"¿Cómo crear una tabla en MySQL?"
]
nuevos_embeddings = modelo.encode(nuevas_preguntas)
predicciones = clf.predict(nuevos_embeddings)
for pregunta, pred in zip(nuevas_preguntas, predicciones):
print(f" '{pregunta}' → {pred}")
Visualización de Embeddings
Los embeddings tienen cientos de dimensiones, pero podemos proyectarlos a 2D para visualizarlos usando técnicas como t-SNE o UMAP:
from sentence_transformers import SentenceTransformer
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import numpy as np
modelo = SentenceTransformer('all-MiniLM-L6-v2')
# Palabras agrupadas por categoría
palabras = {
'Programación': ['Python', 'JavaScript', 'función', 'variable', 'algoritmo', 'compilador'],
'Cocina': ['receta', 'horno', 'ingredientes', 'sartén', 'cocinar', 'chef'],
'Deportes': ['fútbol', 'gol', 'cancha', 'jugador', 'entrenamiento', 'torneo'],
}
textos = []
colores = []
color_map = {'Programación': 'blue', 'Cocina': 'red', 'Deportes': 'green'}
for categoria, lista in palabras.items():
textos.extend(lista)
colores.extend([color_map[categoria]] * len(lista))
# Generar embeddings y reducir a 2D
embeddings = modelo.encode(textos)
tsne = TSNE(n_components=2, random_state=42, perplexity=5)
coords = tsne.fit_transform(embeddings)
# Graficar
plt.figure(figsize=(10, 8))
for categoria, color in color_map.items():
mask = [c == color for c in colores]
plt.scatter(coords[mask, 0], coords[mask, 1], c=color, label=categoria, s=100)
for i, texto in enumerate(textos):
plt.annotate(texto, (coords[i, 0], coords[i, 1]), fontsize=9, ha='center', va='bottom')
plt.legend(fontsize=12)
plt.title('Visualización t-SNE de Embeddings')
plt.tight_layout()
plt.savefig('embeddings_tsne.png', dpi=150)
plt.show()
En la visualización resultante verás que las palabras se agrupan naturalmente por su campo semántico: las de programación formarán un cluster, las de cocina otro, y las de deportes otro.
Consideraciones Prácticas
Al trabajar con embeddings en proyectos reales, ten en cuenta estos aspectos:
- Dimensionalidad vs costo: Más dimensiones capturan más matices pero requieren más almacenamiento y cómputo. Para la mayoría de aplicaciones, 384 dimensiones (MiniLM) son suficientes.
- Normalización: Muchos modelos ya devuelven embeddings normalizados (\( \|\mathbf{v}\| = 1 \)). En ese caso, la similitud coseno se simplifica a un producto punto: \( \text{sim}(\mathbf{a}, \mathbf{b}) = \mathbf{a} \cdot \mathbf{b} \)
- Batching: Siempre genera embeddings en lotes, no uno por uno. Es significativamente más rápido.
- Caché: Los embeddings son determinísticos para un modelo dado. Guárdalos en una base de datos para no recalcularlos.
- Modelo según idioma: Para español, considera modelos multilingües como
paraphrase-multilingual-MiniLM-L12-v2o los embeddings de OpenAI que soportan español nativamente.
Conclusión
Los embeddings son la piedra angular de la inteligencia artificial moderna para procesamiento de lenguaje natural. Transforman texto en vectores numéricos donde la proximidad geométrica refleja similitud semántica, lo que habilita aplicaciones como búsqueda semántica, clasificación de texto, sistemas de recomendación y RAG.
En este artículo cubrimos:
- La definición matemática de embeddings como funciones \( f: \text{Objeto} \rightarrow \mathbb{R}^d \)
- La similitud coseno como métrica fundamental para comparar vectores
- La evolución desde Word2Vec hasta los modelos modernos como OpenAI text-embedding-3
- Implementación práctica con Sentence-Transformers (local y gratuito) y la API de OpenAI
- Aplicaciones concretas: búsqueda semántica, clasificación de texto y visualización con t-SNE
- Consideraciones prácticas sobre dimensionalidad, normalización y caché
Con estas herramientas y conceptos puedes empezar a integrar embeddings en tus proyectos de Python. Te recomendamos como siguiente paso explorar las bases de datos vectoriales (ChromaDB, pgvector) para llevar la búsqueda semántica a escala de producción.