Módulo 1: ¿Qué son Embeddings?

Definición de Embeddings

Descripción de la cápsula

Un embedding es una representación vectorial (numérica) de un dato—típicamente texto—que captura su significado semántico en un espacio de alta dimensionalidad.

En esta cápsula aprenderás la definición formal de embeddings, cómo se transforman palabras y frases en vectores numéricos, qué significa "dimensionalidad", y por qué esta representación es tan poderosa para sistemas de AI.

También verás ejemplos concretos de embeddings reales (usando OpenAI) y entenderás cómo vectores de 1536 números pueden capturar el significado completo de una frase.


¿Qué es un embedding?

Definición simple:

Un embedding es un vector numérico que representa texto de forma que captura su significado semántico.

Descomponiendo la definición:

  1. Vector numérico: Lista de números (e.g., [0.023, -0.145, 0.892, ...])
  2. Representa texto: "Python es un lenguaje de programación" → [vector de números]
  3. Captura significado: Textos con significado similar tienen vectores similares

Visualización conceptual:

Texto original:
"El gato duerme en el sofá"

           ↓  (Embedding Model)

Vector (1536 dimensiones):
[0.0234, -0.1456, 0.8923, -0.2341, 0.5678, ..., 0.1234]
          ^         ^         ^
       dim 1     dim 2     dim 3     ... 1536 dimensiones

El modelo aprende automáticamente qué significan estas dimensiones (no son interpretables por humanos).


De texto a números: La transformación

Por qué necesitamos representación numérica:

Las computadoras no entienden texto directamente:

# ❌ La computadora no puede "calcular" con esto:
text_1 = "perro"
text_2 = "gato"

# ¿Cuán similares son? No hay operación matemática para saberlo

Las computadoras SÍ entienden números:

# ✅ Con embeddings:
embedding_perro = [0.23, 0.45, -0.12, ...]   # 1536 números
embedding_gato = [0.25, 0.43, -0.10, ...]    # 1536 números

# Ahora SÍ puedes calcular similaridad:
similarity = cosine_similarity(embedding_perro, embedding_gato)
# → 0.85 (muy similares porque ambos son animales domésticos)

Propiedades de un embedding

1. Dimensionalidad fija

Todos los embeddings del mismo modelo tienen la misma dimensión:

# OpenAI text-embedding-3-small = 1536 dimensiones
embedding_corto = embed("Hola")
embedding_largo = embed("Esta es una frase muy larga con muchas palabras...")

print(len(embedding_corto))  # 1536
print(len(embedding_largo))  # 1536  ← Mismo tamaño

No importa la longitud del texto: 1 palabra o 1000 palabras → mismo tamaño de vector.


2. Representación densa

Un embedding es un vector "denso" (casi todos los valores son no-cero):

# Embedding típico (simplificado a 5 dimensiones):
[0.234, -0.156, 0.893, -0.234, 0.567]
  ^       ^       ^       ^       ^
 No-zero No-zero No-zero No-zero No-zero

Contrasta con representaciones "sparse" (mayoría ceros):

# One-hot encoding (sparse):
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]  # Solo 1 valor != 0

Ventaja: Embeddings densos capturan mucha más información.


3. Captura semántica

La clave: textos similares → vectores similares

# Embeddings (conceptual):
embed("rey") = [0.5, 0.3, 0.8, ...]
embed("reina") = [0.5, 0.3, 0.7, ...]  ← Muy similar
embed("monarca") = [0.4, 0.3, 0.8, ...]  ← Similar
embed("pizza") = [-0.2, 0.9, -0.1, ...]  ← Muy diferente

Textos relacionados semánticamente están "cerca" en el espacio vectorial.


Dimensionalidad: ¿Por qué 1536 dimensiones?

Comparación de dimensionalidades:

ModeloDimensionesUse Case
Word2Vec (clásico)300Palabras individuales (legacy)
GloVe300Palabras individuales (legacy)
BERT-base768Frases cortas (general)
OpenAI text-embedding-3-small1536Frases/párrafos (moderno)
OpenAI text-embedding-3-large3072Alta precisión (costoso)
Sentence-BERT384-768Open-source (popular)

Trade-off: Más dimensiones = más información capturada, pero más costo computacional.


¿Qué representan esas dimensiones?

Respuesta honesta: No lo sabemos exactamente.

El modelo aprende automáticamente qué captura cada dimensión:

  • Dimensión 1 podría capturar "formalidad" del texto
  • Dimensión 2 podría capturar "tema técnico vs conversacional"
  • Dimensión 500 podría capturar "sentimiento positivo/negativo"
  • ...

Pero no podemos inspeccionar manualmente y decir "esta dimensión significa X".

Lo importante: Funcionan empíricamente. No necesitas interpretarlas.


Ejemplo real con OpenAI

Código básico para generar embeddings:

from openai import OpenAI
import os
from dotenv import load_dotenv

# Setup
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Generar embedding
response = client.embeddings.create(
    model="text-embedding-3-small",
    input="Python es un lenguaje de programación"
)

# Extraer vector
embedding = response.data[0].embedding

# Inspeccionar
print(f"Dimensiones: {len(embedding)}")
print(f"Primeros 10 valores: {embedding[:10]}")

Output (ejemplo real):

Dimensiones: 1536
Primeros 10 valores: [0.0234, -0.1456, 0.8923, -0.2341, 0.5678, -0.3421, 0.1234, -0.6789, 0.4321, 0.9876]

Comparando embeddings de textos similares:

# 3 textos relacionados
texts = [
    "Python es un lenguaje de programación",
    "Python es un lenguaje para programar",
    "JavaScript es un lenguaje de programación"
]

# Generar embeddings
embeddings = []
for text in texts:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    embeddings.append(response.data[0].embedding)

# Los primeros 5 valores de cada embedding:
print("Texto 1:", embeddings[0][:5])
print("Texto 2:", embeddings[1][:5])
print("Texto 3:", embeddings[2][:5])

Output (ejemplo conceptual):

Texto 1: [0.023, -0.145, 0.892, -0.234, 0.567]
Texto 2: [0.025, -0.143, 0.895, -0.230, 0.570]  ← Muy similar a Texto 1
Texto 3: [0.012, -0.120, 0.750, -0.190, 0.490]  ← Algo similar (ambos son lenguajes)

Observa: Texto 1 y Texto 2 son casi idénticos (solo cambia "programación" → "programar"), y sus embeddings son muy similares.


Embeddings vs otras representaciones

1. One-Hot Encoding (legacy)

# Vocabulario: ["perro", "gato", "casa"]
one_hot_perro = [1, 0, 0]
one_hot_gato = [0, 1, 0]
one_hot_casa = [0, 0, 1]

# Problema: "perro" y "gato" son iguales de diferentes a "casa"
# No captura que "perro" y "gato" son animales (relacionados)

Limitaciones:

  • ❌ No captura similaridad semántica
  • ❌ Tamaño = tamaño del vocabulario (ineficiente)
  • ❌ No maneja palabras nuevas (out-of-vocabulary)

2. TF-IDF (keyword-based)

# TF-IDF: Frecuencia de términos × Inverso de frecuencia en documentos
tfidf_doc = [0.5, 0.0, 0.3, 0.0, 0.8, 0.0, ...]  # Sparse (muchos 0s)

# Captura importancia de palabras, pero NO significado

Limitaciones:

  • ❌ No entiende sinónimos ("car" ≠ "automobile")
  • ❌ No entiende contexto ("bank" = orilla vs banco)
  • ⚠️ Útil para keyword matching (BM25), no para semantic search

3. Embeddings (moderno)

# Embeddings: Vector denso que captura significado
embedding = [0.023, -0.145, 0.892, ..., 0.567]  # 1536 valores

# ✅ Captura similaridad semántica
# ✅ Entiende sinónimos (car ≈ automobile)
# ✅ Entiende contexto (diferentes "bank" → diferentes embeddings)

Ventajas:

  • ✅ Captura significado semántico profundo
  • ✅ Maneja sinónimos, paráfrasis
  • ✅ Tamaño fijo independiente del vocabulario

Tipos de embeddings

1. Word Embeddings (legacy)

Un embedding por palabra:

embed("rey") = [0.5, 0.3, 0.8]
embed("reina") = [0.5, 0.3, 0.7]

Problema: No captura contexto.

# "banco" tiene múltiples significados:
embed("banco")  # ¿Orilla de río o institución financiera?
# → Mismo embedding para ambos contextos ❌

Ejemplos: Word2Vec, GloVe (pre-2018)


2. Contextual Embeddings (moderno)

Un embedding por frase completa:

embed("Voy al banco del río") = [0.2, 0.5, 0.1, ...]
embed("Voy al banco a sacar dinero") = [0.8, -0.3, 0.6, ...]
                                        ↑ Diferente porque contexto es diferente

Ventaja: Mismo palabra → diferentes embeddings según contexto.

Ejemplos: BERT, GPT, OpenAI embeddings, Sentence-BERT (post-2018)


¿Cómo se generan embeddings?

High-level flow (se profundiza en Módulo 2):

Texto:
"Python es un lenguaje de programación"

           ↓ (1) Tokenización

Tokens:
["Python", "es", "un", "lenguaje", "de", "programación"]

           ↓ (2) Transformer Encoder

Hidden states:
[[0.1, 0.2, ...], [0.3, 0.4, ...], [0.5, 0.6, ...], ...]
 (cada token tiene su propio vector intermedio)

           ↓ (3) Pooling (mean, CLS)

Embedding final:
[0.023, -0.145, 0.892, ..., 0.567]  (1536 dims)

Paso crítico: Pooling reduce múltiples vectores (uno por token) en un solo vector (embedding de la frase).

Módulo 2 profundizará en cada paso.


Ejemplo: Visualización conceptual 2D

En realidad: 1536 dimensiones (imposible visualizar)

Pero podemos reducir a 2D para ilustrar:

       Alta tecnología
              ^
              |
        "Python" ●
              |    "JavaScript" ●
              |
─────────────────────────────────────> Formalidad
              |
         "Perro" ●    "Gato" ●
              |
              v
         Animales

Conceptual:

  • "Python" y "JavaScript" están cerca (ambos lenguajes)
  • "Perro" y "Gato" están cerca (ambos animales)
  • "Python" y "Perro" están lejos (diferentes dominios)

En realidad: Espacio de 1536 dimensiones captura MUCHAS más relaciones.


Propiedades matemáticas de embeddings

1. Embeddings como puntos en espacio

# Cada embedding es un punto en espacio de 1536 dimensiones:
embedding_1 = [x1, x2, x3, ..., x1536]  # Punto en R^1536
embedding_2 = [y1, y2, y3, ..., y1536]  # Otro punto

Distancia entre puntos = similaridad semántica


2. Operaciones vectoriales

Suma, resta, promedio:

import numpy as np

# Promedio de embeddings:
avg_embedding = np.mean([emb_1, emb_2, emb_3], axis=0)
# → Captura "significado promedio" de múltiples textos

Aritmética semántica (conceptual):

embed("rey") - embed("hombre") + embed("mujer") ≈ embed("reina")

Nota: Esta aritmética es más efectiva con word embeddings (Word2Vec). Sentence embeddings modernos son más complejos.


Inmutabilidad de embeddings

Propiedad crítica:

# Mismo texto → SIEMPRE mismo embedding (mismo modelo)
emb_1 = embed("Python es genial")
emb_2 = embed("Python es genial")

assert emb_1 == emb_2  # ✅ True (determinista)

Ventaja: Puedes cachear embeddings.

# Embed una vez, guarda, reutiliza:
embeddings = {}
embeddings["doc_1"] = embed("Contenido del documento 1")
embeddings["doc_2"] = embed("Contenido del documento 2")

# Guardar en archivo:
np.save("embeddings.npy", embeddings)

# Cargar después (no necesitas re-calcular):
embeddings = np.load("embeddings.npy", allow_pickle=True).item()

Implicación: Generar embeddings es caro (API call), pero solo lo haces una vez.


Analogías para entender embeddings

Analogía 1: Coordenadas GPS

"París" → (48.8566, 2.3522)  # Latitud, Longitud (2D)
"Londres" → (51.5074, -0.1278)

# Ciudades cercanas geográficamente → coordenadas cercanas
# París y Londres están relativamente cerca en Europa

Embeddings:
"París" → [0.2, 0.5, -0.3, ..., 0.8]  (1536D)
"Londres" → [0.1, 0.6, -0.4, ..., 0.7]
# Ciudades conceptualmente cercanas → embeddings cercanos

Analogía 2: ADN genético

ADN humano = secuencia de 3 mil millones de pares de bases
→ Define características biológicas

Embedding = secuencia de 1536 números
→ Define características semánticas del texto

Similaridad genética → especies relacionadas Similaridad vectorial → textos relacionados


Analogía 3: Huella digital

Huella digital = patrón único que identifica a una persona

Embedding = "huella semántica" que identifica el significado del texto

Misma persona → misma huella (invariante) Mismo texto → mismo embedding (invariante)


Limitaciones de embeddings

1. No son perfectos

# A veces textos similares tienen embeddings ligeramente distantes
embed("El gato duerme") vs embed("El felino descansa")
# → Similar pero no idéntico (porque palabras son diferentes)

Solución: Modelos más grandes (OpenAI large) capturan mejor, pero son más costosos.


2. Dependencia del modelo

# Diferentes modelos → diferentes embeddings
openai_emb = embed_openai("Python")
sbert_emb = embed_sbert("Python")

# NO puedes compararlos directamente:
similarity(openai_emb, sbert_emb)  # ❌ Sin sentido (espacios diferentes)

Solución: Usa el mismo modelo siempre.


3. Costo de generación

# OpenAI API: $0.00002 / 1K tokens
# 1 millón de documentos × 500 tokens promedio = $10 USD

# No es gratis, pero es una vez (luego cacheas)

Solución: Batch processing + caching (Módulo 6).


Ejercicios

Ejercicio 1: Identificar embeddings

¿Cuál de estos es un embedding válido?

A:

[1, 0, 0, 0, 0, 0]  # 6 dimensiones, sparse (mayormente 0s)

B:

[0.023, -0.145, 0.892, -0.234, 0.567, 0.123]  # 6 dims, denso

C:

["Python", "programming", "language"]  # Lista de palabras
Ver solución

Respuesta: B

Explicación:

  • A es sparse (mayormente 0s), probablemente one-hot encoding
  • B es denso (todos valores != 0), típico de embeddings
  • C es texto sin procesar, no embedding

Un embedding válido es:

  • Vector numérico (lista de floats)
  • Denso (casi todos valores != 0)
  • Dimensionalidad fija (e.g., 1536 para OpenAI)

Ejercicio 2: Predecir similaridad

Dados estos textos, ¿cuáles deberían tener embeddings más similares?

A: "El perro ladra" B: "El can aúlla" C: "La pizza es deliciosa"

Ver solución

Respuesta: A y B son más similares

Explicación:

  • A y B hablan de animales (perro/can) y sonidos (ladra/aúlla)
  • C habla de comida (dominio completamente diferente)

Embeddings capturan: Dominio semántico (animales vs comida)

Nota: "perro" y "can" son sinónimos, embeddings los capturan como cercanos.


Ejercicio 3: Corregir el código

¿Qué está mal aquí?

from openai import OpenAI
client = OpenAI()

# Generar embeddings para 2 textos
embedding_1 = client.embeddings.create(
    model="text-embedding-3-small",
    input="Python"
)

embedding_2 = client.embeddings.create(
    model="text-embedding-3-large",  # ← Modelo diferente
    input="JavaScript"
)

# Calcular similaridad
similarity = cosine_similarity(embedding_1, embedding_2)
Ver solución

Problema: Modelos diferentes (small vs large) generan embeddings en espacios diferentes.

No puedes compararlos directamente:

  • text-embedding-3-small → 1536 dimensiones
  • text-embedding-3-large → 3072 dimensiones

Corrección:

# Usar MISMO modelo para ambos:
embedding_1 = client.embeddings.create(
    model="text-embedding-3-small",  # ← Mismo
    input="Python"
).data[0].embedding

embedding_2 = client.embeddings.create(
    model="text-embedding-3-small",  # ← Mismo
    input="JavaScript"
).data[0].embedding

# Ahora SÍ puedes comparar:
similarity = cosine_similarity(embedding_1, embedding_2)

Ejercicio 4: Debugging

¿Por qué este código da error?

import numpy as np

embedding = [0.1, 0.2, 0.3]
similarity = np.dot(embedding, embedding)  # Error?
Ver solución

No hay error técnico, pero el resultado no es útil:

similarity = np.dot(embedding, embedding)
# → 0.14  (dot product de un vector consigo mismo)

Problema conceptual: Calcular similaridad de un embedding consigo mismo siempre da 1.0 (usando cosine similarity):

from numpy.linalg import norm

embedding = np.array([0.1, 0.2, 0.3])
similarity = np.dot(embedding, embedding) / (norm(embedding) * norm(embedding))
# → 1.0 (siempre)

Corrección: Compara dos embeddings diferentes:

emb_1 = np.array([0.1, 0.2, 0.3])
emb_2 = np.array([0.2, 0.3, 0.4])

similarity = np.dot(emb_1, emb_2) / (norm(emb_1) * norm(emb_2))
# → ~0.998 (muy similares pero no idénticos)

Resumen

Qué aprendiste:

  • Definición: Embedding = vector numérico que captura significado semántico
  • Transformación: Texto → Tokenización → Transformer → Pooling → Embedding
  • Dimensionalidad: Típicamente 300-3072 dimensiones (OpenAI: 1536)
  • Propiedades: Denso, tamaño fijo, determinista, captura semántica
  • Ventajas vs legacy: Mejor que one-hot, TF-IDF para semantic search
  • Tipos: Word embeddings (legacy) vs Contextual embeddings (moderno)
  • Limitaciones: No perfectos, dependientes del modelo, costo de generación

Conceptos clave:

  1. Textos similares → embeddings similares (proximity = similarity)
  2. Embedding space de N dimensiones (e.g., 1536D)
  3. Mismo texto → mismo embedding (determinista, cacheable)

Recursos adicionales

  1. OpenAI Embeddings Guide - Docs oficiales
  2. What are Embeddings? (Video) - Explicación visual
  3. Word Embeddings (Paper) - Word2Vec original (legacy pero histórico)
  4. BERT Explained - Embeddings contextuales
  5. Sentence Transformers - Open-source sentence embeddings
  6. Embeddings at Scale - Aplicaciones production

En la siguiente cápsula

Cápsula 03: Propiedades Vectoriales

Aprenderás:

  • Similaridad semántica (cosine similarity)
  • Direccionalidad en espacio vectorial
  • Magnitud vs dirección (cuál importa)
  • Clustering natural de embeddings

De definición conceptual a propiedades matemáticas.


Módulo 1 - Embeddings Deep Dive Guide Transformando texto en vectores que las máquinas entienden