Phase 11 - Lesson 04

Embeddings y Representaciones Vectoriales

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

El texto es discreto. Las matemáticas son continuas. Cada vez que le pides a un LLM que encuentre documentos "similares", compare significados o busque más allá de las palabras clave, estás confiando en un puente entre estos dos mundos. Ese puente es un embedding. Si no entiendes los embeddings, no entiendes la IA moderna. Simplemente la usas.

Tipo: Build Lenguajes: Python Prerrequisitos: Fase 11, Lección 01 (Ingeniería de Prompts) Tiempo: ~75 minutos Relacionado: La Fase 5 · 22 (Inmersión Profunda en Modelos de Embedding) cubre denso vs disperso vs multi-vector, truncamiento Matryoshka y selección de modelo por eje. Esta lección se centra en el pipeline de producción (bases de datos vectoriales, HNSW, matemática de similitud). Lee la Fase 5 · 22 antes de elegir un modelo.

Objetivos de Aprendizaje

  • Generar embeddings de texto utilizando proveedores de API y modelos de código abierto, y calcular la similitud del coseno entre ellos
  • Explicar por qué los embeddings resuelven el problema de la incompatibilidad de vocabulario que la búsqueda por palabras clave no puede manejar
  • Construir un índice de búsqueda semántica que recupere documentos por significado en lugar de por coincidencia exacta de palabras clave
  • Evaluar la calidad de los embeddings utilizando benchmarks de recuperación (precisión@k, recall) y elegir el modelo de embedding adecuado para tu tarea

El Problema

Tienes 10,000 tickets de soporte. Un cliente escribe "mi pago no se procesó". Necesitas encontrar tickets anteriores similares. La búsqueda por palabras clave encuentra tickets que contienen "pago" y "no se procesó". Pasa por alto "fallo en la transacción", "el cargo fue rechazado" y "error de facturación". Estos tickets describen exactamente el mismo problema con palabras completamente diferentes.

Este es el problema de la incompatibilidad de vocabulario (vocabulary mismatch). El lenguaje humano tiene docenas de formas de decir lo mismo. La búsqueda por palabras clave trata cada palabra como un símbolo independiente sin significado. No puede saber que "rechazado" y "no se procesó" se refieren al mismo concepto.

Necesitas una representación de texto donde el significado, y no la ortografía, determine la similitud. Necesitas una forma de colocar "mi pago no se procesó" y "la transacción fue rechazada" cerca en algún espacio matemático, mientras alejas "mi pago llegó a tiempo", a pesar de que compartan la palabra "pago".

Esa representación es un embedding.

El Concepto

¿Qué es un Embedding?

Un embedding es un vector denso de números de punto flotante que representa el significado de un texto. La palabra "denso" importa: cada dimensión contiene información, a diferencia de las representaciones dispersas (bag-of-words, TF-IDF) donde la mayoría de las dimensiones son cero.

"The cat sat on the mat" se convierte en algo como [0.023, -0.041, 0.087, ..., 0.012], una lista de 768 a 3072 números según el modelo. Estos números codifican el significado. Nunca los inspeccionas directamente. Los comparas.

El Gran Avance de Word2Vec

En 2013, Tomas Mikolov y sus colegas de Google publicaron Word2Vec. La idea central: entrenar una red neuronal para predecir una palabra a partir de sus vecinas (or las vecinas a partir de una palabra), y los pesos de la capa oculta se convierten en representaciones vectoriales significativas.

El famoso resultado:

king - man + woman = queen

La aritmética vectorial en embeddings de palabras captura relaciones semánticas. La dirección de "man" a "woman" es aproximadamente la misma dirección que la de "king" a "queen". Este fue el momento en que el campo se dio cuenta de que la geometría podía codificar el significado.

Word2Vec producía vectores de 300 dimensiones. Cada palabra obtenía un solo vector independientemente del contexto. "Bank" en "river bank" (orilla del río) y "bank account" (cuenta bancaria) tenía el mismo embedding. Esta limitación impulsó la siguiente década de investigación.

De Palabras a Sentencias

Los embeddings de palabras representan tokens individuales. Los sistemas en producción necesitan incrustar sentencias completas, párrafos o documentos. Surgieron cuatro enfoques:

Promedio (Averaging): toma la media de todos los vectores de palabras en la oración. Económico, con pérdida de información, pero sorprendentemente decente para textos cortos. Pierde el orden de las palabras por completo: "dog bites man" (el perro muerde al hombre) y "man bites dog" (el hombre muerde al perro) obtienen embeddings idénticos.

Token CLS: los modelos transformer (BERT, 2018) generan un embedding de token especial [CLS] que representa toda la entrada. Es mejor que el promedio, pero el token [CLS] se entrenó para la predicción de la siguiente oración, no para la similitud.

Aprendizaje contrastivo (Contrastive learning): entrena el modelo explícitamente para acercar pares similares y alejar pares disímiles. Sentence-BERT (Reimers & Gurevych, 2019) utilizó este enfoque y se convirtió en la base de los modelos de embedding modernos. Dados "How do I reset my password?" y "I need to change my password", el modelo aprende que estos deben tener vectores casi idénticos.

Embeddings ajustados por instrucciones (Instruction-tuned embeddings): el enfoque más reciente. Modelos como E5 y GTE aceptan un prefijo de tarea ("search_query:", "search_document:") que le dice al modelo qué tipo de embedding producir. Esto permite que un único modelo sirva para múltiples tareas.

graph LR
    subgraph "2013: Word2Vec"
        W1["king"] --> V1["[0.2, -0.1, ...]"]
        W2["queen"] --> V2["[0.3, -0.2, ...]"]
    end

    subgraph "2019: Sentence-BERT"
        S1["¿Cómo restablezco mi contraseña?"] --> E1["[0.04, 0.12, ...]"]
        S2["Necesito cambiar mi contraseña"] --> E2["[0.05, 0.11, ...]"]
    end

    subgraph "2024: Ajustado por Instrucciones"
        I1["search_query: restablecer contraseña"] --> T1["[0.08, 0.09, ...]"]
        I2["search_document: Para restablecer su contraseña, haga clic..."] --> T2["[0.07, 0.10, ...]"]
    end

Modelos de Embedding Modernos

El mercado se ha consolidado en un puñado de opciones de grado de producción (puntuaciones MTEB a principios de 2026, MTEB v2):

Modelo Proveedor Dimensiones MTEB Contexto Costo / 1M tokens
Gemini Embedding 2 Google 3072 (Matryoshka) 67.7 (recuperación) 8192 $0.15
embed-v4 Cohere 1024 (Matryoshka) 65.2 128K $0.12
voyage-4 Voyage AI 1024/2048 (Matryoshka) 66.8 32K $0.12
text-embedding-3-large OpenAI 3072 (Matryoshka) 64.6 8192 $0.13
text-embedding-3-small OpenAI 1536 (Matryoshka) 62.3 8192 $0.02
BGE-M3 BAAI 1024 (dense+sparse+ColBERT) 63.0 multilingüe 8192 Pesos abiertos
Qwen3-Embedding Alibaba 4096 (Matryoshka) 66.9 32K Pesos abiertos
Nomic-embed-v2 Nomic 768 (Matryoshka) 63.1 8192 Pesos abiertos

MTEB (Massive Text Embedding Benchmark) v2 cubre más de 100 tareas en recuperación, clasificación, agrupamiento (clustering), reordenación (reranking) y resumen. Más alto es mejor. Para 2026, los modelos de pesos abiertos (Qwen3-Embedding, BGE-M3) igualan o superan a los modelos alojados patentados en la mayoría de los ejes. Gemini Embedding 2 lidera en recuperación pura; Voyage/Cohere lideran en dominios específicos (finanzas, derecho, código). Realiza siempre benchmarks con tus propias consultas antes de comprometerte.

Métricas de Similitud

Dados dos vectores de embedding, hay tres formas de medir qué tan similares son:

Similitud del coseno (Cosine similarity): el coseno del ángulo entre dos vectores. Varía de -1 (opuesto) a 1 (dirección idéntica). Ignora la magnitud: una oración de 10 palabras y un documento de 500 palabras pueden obtener una puntuación de 1.0 si apuntan en la misma dirección. Este es el valor predeterminado para el 90% de los casos de uso.

cosine_sim(a, b) = dot(a, b) / (||a|| * ||b||)

Producto escalar (Dot product): el producto interno bruto de dos vectores. Es idéntico a la similitud del coseno cuando los vectores están normalizados (longitud unitaria). Es más rápido de calcular. Los embeddings de OpenAI están normalizados, por lo que el producto escalar y el coseno ofrecen la misma clasificación.

dot(a, b) = sum(a_i * b_i)

Distancia Euclidiana (L2): distancia en línea recta en el espacio vectorial. Menor = más similar. Sensible a las diferencias de magnitud. Úsala cuando importe la posición absoluta en el espacio, no solo la dirección.

L2(a, b) = sqrt(sum((a_i - b_i)^2))

Cuando usar | Evitar cuando:

Métrica Cuándo usar Cuándo evitar
Similitud del coseno Al comparar textos de diferentes longitudes; en la mayoría de las tareas de recuperación Cuando la magnitud contiene información
Producto escalar Cuando los embeddings ya están normalizados; velocidad máxima Cuando los vectores tienen magnitudes variables
Distancia Euclidiana Agrupamiento (clustering); problemas espaciales de vecinos más cercanos Al comparar documentos de longitudes extremadamente diferentes

Bases de Datos Vectoriales y HNSW

Una búsqueda de similitud por fuerza bruta compara la consulta con cada vector almacenado. Con 1 millón de vectores de 1536 dimensiones, eso representa 1.5 mil millones de operaciones de multiplicación y suma por consulta. Demasiado lento.

Las bases de datos vectoriales resuelven esto con algoritmos de Vecino Más Cercano Aproximado (ANN). El algoritmo dominante es HNSW (Hierarchical Navigable Small World):

  1. Construye un grafo de vectores de múltiples capas
  2. Las capas superiores son dispersas: conexiones de largo alcance entre clústeres distantes
  3. Las capas inferiores son densas: conexiones detalladas entre vectores cercanos
  4. La búsqueda comienza en la capa superior, desciendo de forma codiciosa (greedy) para refinar los resultados
  5. Devuelve resultados top-k aproximados en un tiempo de O(log n) en lugar de O(n)

HNSW sacrifica una pequeña pérdida de precisión (generalmente del 95 al 99% de recall) a cambio de enormes aumentos de velocidad. Con 10 millones de vectores, la fuerza bruta tarda segundos; HNSW tarda milisegundos.

graph TD
    subgraph "Capas HNSW"
        L2["Capa 2 (dispersa)"] -->|"saltos largos"| L1["Capa 1 (media)"]
        L1 -->|"saltos más cortos"| L0["Capa 0 (densa, todos los vectores)"]
    end

    Q["Vector de consulta"] -->|"entrar en la parte superior"| L2
    L0 -->|"vecinos más cercanos"| R["Resultados top-k"]

Opciones de producción:

Base de datos Tipo Mejor para Escala máxima
Pinecone SaaS administrado Producción sin operaciones (zero-ops) Miles de millones
Weaviate Código abierto Autoalojado, búsqueda híbrida 100M+
Qdrant Código abierto Alto rendimiento, filtrado 100M+
ChromaDB Embebido Prototipado, desarrollo local 1M
pgvector Extensión de Postgres Si ya utilizas Postgres 10M
FAISS Biblioteca En proceso, investigación 1B+

Estrategias de Fragmentación (Chunking)

Los documentos son demasiado largos para incrustarse como vectores únicos. Un PDF de 50 páginas cubre docenas de temas: su embedding se convierte en un promedio de todo, sin parecerse a nada en particular. Debes dividir los documentos en fragmentos (chunks) e incrustar cada uno.

Fragmentación de tamaño fijo (Fixed-size chunking): divide el texto cada N tokens con una superposición de M tokens. Simple y predecible. Funciona bien cuando los documentos no tienen una estructura clara. Un fragmento de 512 tokens con una superposición de 50 tokens: el fragmento 1 corresponde a los tokens 0-511, el fragmento 2 a los tokens 462-973.

Fragmentación basada en oraciones (Sentence-based chunking): divide en los límites de las oraciones, agrupando oraciones hasta alcanzar el límite de tokens. Cada fragmento es al menos una oración completa. Es mejor que el tamaño fijo porque nunca corta una idea por la mitad.

Fragmentación recursiva (Recursive chunking): intenta dividir primero en el límite más grande (encabezados de sección). Si sigue siendo demasiado grande, intenta con los límites de párrafo, luego de oración y finalmente con límites de caracteres. Así es como funciona RecursiveCharacterTextSplitter de LangChain y funciona bien para conjuntos de documentos con formatos mixtos.

Fragmentación semántica (Semantic chunking): incrusta cada oración y luego agrupa oraciones consecutivas cuyas representaciones vectoriales sean similares. Cuando la similitud de los embeddings cae por debajo de un umbral, comienza un nuevo fragmento. Es costosa (requiere incrustar cada oración individualmente) pero produce los fragmentos más coherentes.

Estrategia Complejidad Calidad Mejor para
Tamaño fijo Baja Aceptable Texto no estructurado, logs
Basada en oraciones Baja Buena Artículos, correos electrónicos
Recursiva Media Buena Markdown, HTML, documentos mixtos
Semántica Alta Excelente Calidad de recuperación crítica

El punto ideal para la mayoría de los sistemas: fragmentos de 256 a 512 tokens con una superposición de 50 tokens.

Bi-Encoders vs Cross-Encoders

Un bi-encoder incrusta la consulta y los documentos de forma independiente, y luego compara los vectores. Es rápido: incrustas la consulta una vez y la comparas con los embeddings de documentos precalculados. Esto es lo que se utiliza para la recuperación.

Un cross-encoder toma la consulta y un documento como una única entrada y genera una puntuación de relevancia. Es lento: procesa cada par consulta-documento a través del modelo completo. Sin embargo, es mucho más preciso porque puede prestar atención a los tokens de la consulta y del documento simultáneamente.

El patrón de producción: el bi-encoder recupera los 100 mejores candidatos, y el cross-encoder los reordena para obtener los 10 mejores. Este es el pipeline de recuperación y reordenamiento (retrieve-then-rerank).

graph LR
    Q["Consulta"] --> BE["Bi-Encoder: incrustar consulta"]
    BE --> VS["Búsqueda vectorial: top 100"]
    VS --> CE["Cross-Encoder: reordenar"]
    CE --> R["Resultados top 10"]

Modelos de reordenación (reranking): Cohere Rerank 3.5 (

por cada 1,000 consultas), BGE-reranker-v2 (gratuito, código abierto), Jina Reranker v2 (gratuito, código abierto).

Embeddings Matryoshka

Los embeddings tradicionales son todo o nada. Un vector de 1536 dimensiones utiliza 1536 números de punto flotante. No se puede truncar a 256 dimensiones sin volver a entrenar el modelo.

El Aprendizaje de Representación Matryoshka (Matryoshka Representation Learning, Kusupati et al., 2022) soluciona esto. El modelo se entrena de modo que las primeras N dimensiones capturen la información más importante, como una muñeca rusa matryoshka. Truncar un embedding Matryoshka de 1536 dimensiones a 256 dimensiones pierde algo de precisión, pero sigue siendo funcional.

Los modelos text-embedding-3-small y text-embedding-3-large de OpenAI admiten el truncamiento Matryoshka mediante el parámetro dimensions. Solicitar 256 dimensiones en lugar de 1536 reduce el almacenamiento en 6 veces con una pérdida de precisión de aproximadamente el 3-5% en los benchmarks de MTEB.

Cuantización Binaria

Un embedding de 1536 dimensiones almacenado como float32 utiliza 6,144 bytes. Multiplicado por 10 millones de documentos: 61 GB solo para los vectores.

La cuantización binaria convierte cada número de punto flotante en un solo bit: los valores positivos se convierten en 1 y los negativos en 0. El almacenamiento se reduce de 6,144 bytes a 192 bytes, una reducción de 32 veces. La similitud se calcula utilizando la distancia de Hamming (contar los bits que difieren), algo que las CPU pueden realizar en una sola instrucción.

El impacto en la precisión es de alrededor del 5 al 10% en el recall de recuperación. El patrón común: cuantización binaria para la búsqueda de primera pasada sobre millones de vectores, y luego reordenación de los 1,000 mejores con vectores de precisión completa. Esto te permite obtener más del 95% de la precisión total con 32 veces menos memoria.

Constrúyelo

Construiremos un motor de búsqueda semántica desde cero. Sin bases de datos vectoriales. Sin APIs de embedding externas. Python puro con numpy para la matemática.

Paso 1: Fragmentación de Texto

def chunk_text(text, chunk_size=200, overlap=50):
    words = text.split()
    chunks = []
    start = 0
    while start < len(words):
        end = start + chunk_size
        chunk = " ".join(words[start:end])
        chunks.append(chunk)
        start += chunk_size - overlap
    return chunks


def chunk_by_sentences(text, max_chunk_tokens=200):
    sentences = text.replace("\n", " ").split(".")
    sentences = [s.strip() + "." for s in sentences if s.strip()]
    chunks = []
    current_chunk = []
    current_length = 0
    for sentence in sentences:
        sentence_length = len(sentence.split())
        if current_length + sentence_length > max_chunk_tokens and current_chunk:
            chunks.append(" ".join(current_chunk))
            current_chunk = []
            current_length = 0
        current_chunk.append(sentence)
        current_length += sentence_length
    if current_chunk:
        chunks.append(" ".join(current_chunk))
    return chunks

Paso 2: Construcción de Embeddings desde Cero

Implementamos un embedding denso simple utilizando TF-IDF con normalización L2. Este no es un embedding neuronal, pero sigue el mismo contrato: entra texto, sale un vector de tamaño fijo, y los textos similares producen vectores similares.

import math
import numpy as np
from collections import Counter

class SimpleEmbedder:
    def __init__(self):
        self.vocab = []
        self.idf = []
        self.word_to_idx = {}

    def fit(self, documents):
        vocab_set = set()
        for doc in documents:
            vocab_set.update(doc.lower().split())
        self.vocab = sorted(vocab_set)
        self.word_to_idx = {w: i for i, w in enumerate(self.vocab)}
        n = len(documents)
        self.idf = np.zeros(len(self.vocab))
        for i, word in enumerate(self.vocab):
            doc_count = sum(1 for doc in documents if word in doc.lower().split())
            self.idf[i] = math.log((n + 1) / (doc_count + 1)) + 1

    def embed(self, text):
        words = text.lower().split()
        count = Counter(words)
        total = len(words) if words else 1
        vec = np.zeros(len(self.vocab))
        for word, freq in count.items():
            if word in self.word_to_idx:
                tf = freq / total
                vec[self.word_to_idx[word]] = tf * self.idf[self.word_to_idx[word]]
        norm = np.linalg.norm(vec)
        if norm > 0:
            vec = vec / norm
        return vec

Paso 3: Funciones de Similitud

def cosine_similarity(a, b):
    dot = np.dot(a, b)
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return float(dot / (norm_a * norm_b))


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


def euclidean_distance(a, b):
    return float(np.linalg.norm(a - b))

Paso 4: Índice Vectorial con Búsqueda por Fuerza Bruta

class VectorIndex:
    def __init__(self):
        self.vectors = []
        self.texts = []
        self.metadata = []

    def add(self, vector, text, meta=None):
        self.vectors.append(vector)
        self.texts.append(text)
        self.metadata.append(meta or {})

    def search(self, query_vector, top_k=5, metric="cosine"):
        scores = []
        for i, vec in enumerate(self.vectors):
            if metric == "cosine":
                score = cosine_similarity(query_vector, vec)
            elif metric == "dot":
                score = dot_product(query_vector, vec)
            elif metric == "euclidean":
                score = -euclidean_distance(query_vector, vec)
            else:
                raise ValueError(f"Unknown metric: {metric}")
            scores.append((i, score))
        scores.sort(key=lambda x: x[1], reverse=True)
        results = []
        for idx, score in scores[:top_k]:
            results.append({
                "text": self.texts[idx],
                "score": score,
                "metadata": self.metadata[idx],
                "index": idx
            })
        return results

    def size(self):
        return len(self.vectors)

Paso 5: El Motor de Búsqueda Semántica

class SemanticSearchEngine:
    def __init__(self, chunk_size=200, overlap=50):
        self.embedder = SimpleEmbedder()
        self.index = VectorIndex()
        self.chunk_size = chunk_size
        self.overlap = overlap

    def index_documents(self, documents, source_names=None):
        all_chunks = []
        all_sources = []
        for i, doc in enumerate(documents):
            chunks = chunk_text(doc, self.chunk_size, self.overlap)
            all_chunks.extend(chunks)
            name = source_names[i] if source_names else f"doc_{i}"
            all_sources.extend([name] * len(chunks))
        self.embedder.fit(all_chunks)
        for chunk, source in zip(all_chunks, all_sources):
            vec = self.embedder.embed(chunk)
            self.index.add(vec, chunk, {"source": source})
        return len(all_chunks)

    def search(self, query, top_k=5, metric="cosine"):
        query_vec = self.embedder.embed(query)
        return self.index.search(query_vec, top_k, metric)

    def search_with_scores(self, query, top_k=5):
        results = self.search(query, top_k)
        return [
            {
                "text": r["text"][:200],
                "source": r["metadata"].get("source", "unknown"),
                "score": round(r["score"], 4)
            }
            for r in results
        ]

Paso 6: Comparación de Métricas de Similitud

def compare_metrics(engine, query, top_k=3):
    results = {}
    for metric in ["cosine", "dot", "euclidean"]:
        hits = engine.search(query, top_k=top_k, metric=metric)
        results[metric] = [
            {"score": round(h["score"], 4), "preview": h["text"][:80]}
            for h in hits
        ]
    return results

Úsalo

Con una API de embedding de producción, la arquitectura se mantiene idéntica. Solo cambia el generador de embeddings (embedder):

from openai import OpenAI

client = OpenAI()

def openai_embed(texts, model="text-embedding-3-small", dimensions=None):
    kwargs = {"model": model, "input": texts}
    if dimensions:
        kwargs["dimensions"] = dimensions
    response = client.embeddings.create(**kwargs)
    return [item.embedding for item in response.data]

Truncamiento Matryoshka con OpenAI: mismo modelo, menos dimensiones, menor almacenamiento:

full = openai_embed(["semantic search query"], dimensions=1536)
compact = openai_embed(["semantic search query"], dimensions=256)

El vector de 256 dimensiones utiliza 6 veces menos almacenamiento. Para 10 millones de documentos, eso representa 10 GB en comparación con 61 GB. La pérdida de precisión es de aproximadamente un 3-5% en los benchmarks estándar.

Para la reordenación con Cohere:

import cohere

co = cohere.ClientV2()

results = co.rerank(
    model="rerank-v3.5",
    query="What is the refund policy?",
    documents=["Full refund within 30 days...", "No refunds after 90 days..."],
    top_n=3
)

Para embeddings locales sin dependencia de API:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("BAAI/bge-small-en-v1.5")
embeddings = model.encode(["semantic search query", "another document"])

La clase VectorIndex de nuestra construcción funciona con cualquiera de ellos. Intercambia la función de embedding, mantén la lógica de búsqueda.

Envíalo

Esta lección produce:

Ejercicios

  1. Comparación de métricas: ejecuta las mismas 5 consultas sobre los documentos de muestra utilizando la similitud del coseno, el producto escalar y la distancia euclidiana. Registra los 3 mejores resultados para cada una. ¿En qué consultas no coinciden las métricas? ¿Por qué?

  2. Experimento del tamaño de fragmento: indexa los documentos de muestra con tamaños de fragmento de 50, 100, 200 y 500 palabras. Para cada uno, ejecuta 5 consultas y registra la puntuación de similitud más alta (top-1). Grafica la relación entre el tamaño del fragmento y la calidad de recuperación. Encuentra el punto donde los fragmentos más grandes comienzan a perjudicar el rendimiento.

  3. Simulación de Matryoshka: construye un SimpleEmbedder que produzca vectores de 500 dimensiones. Trunca a 50, 100, 200 y 500 dimensiones. Mide cómo se degrada el recall de recuperación en cada truncamiento. Esto simula el comportamiento de Matryoshka sin necesidad del truque de entrenamiento real.

  4. Cuantización binaria: toma los embeddings del motor de búsqueda, conviértelos a binarios (1 si es positivo, 0 si es negativo) e implementa la búsqueda por distancia de Hamming. Compara los 10 mejores resultados con la similitud del coseno de precisión completa. Mide el porcentaje de coincidencia (overlap).

  5. Fragmentación basada en oraciones: reemplaza la fragmentación de tamaño fijo por chunk_by_sentences. Ejecuta las mismas consultas y compara las puntuaciones de recuperación. ¿Respetar los límites de las oraciones mejora los resultados?

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Embedding "Texto a números" Un vector denso donde la proximidad geométrica codifica la similitud semántica
Word2Vec "El embedding original" Modelo de 2013 que aprendió vectores de palabras prediciendo palabras de contexto; demostró que la aritmética vectorial codifica el significado
Similitud del coseno "Qué tan similares son dos vectores" Coseno del ángulo entre vectores; 1 = dirección idéntica, 0 = ortogonal, -1 = opuesta
HNSW "Búsqueda vectorial rápida" Grafo Hierarchical Navigable Small World: estructura de múltiples capas que permite la búsqueda de vecinos más cercanos aproximados en O(log n)
Bi-encoder "Incrustar por separado, comparar rápido" Codifica la consulta y el documento de forma independiente en vectores; permite el precalculado y la recuperación rápida
Cross-encoder "Reordenador lento pero preciso" Procesa el par consulta-documento conjuntamente a través del modelo completo; mayor precisión, sin precalculado
Embeddings Matryoshka "Vectores truncables" Embeddings entrenados para que las primeras N dimensiones capturen la información más importante, permitiendo un almacenamiento de tamaño variable
Cuantización binaria "Embeddings de 1 bit" Conversión de vectores de punto flotante a binarios (solo bit de signo) para una reducción de 32 veces del almacenamiento con búsqueda por distancia de Hamming
Chunking (Fragmentación) "Dividir documentos para embedding" División de documentos en segmentos de 256 a 512 tokens para que cada uno pueda ser incrustado y recuperado de forma independiente
Base de datos vectorial "Motor de búsqueda para embeddings" Almacén de datos optimizado para almacenar vectores y realizar búsquedas de vecinos más cercanos aproximados a escala
Aprendizaje contrastivo "Entrenar por comparación" Enfoque de entrenamiento que acerca los embeddings de pares similares y aleja los de pares disímiles
MTEB "El benchmark de embeddings" Massive Text Embedding Benchmark: 56 conjuntos de datos en 8 tareas; el estándar para comparar modelos de embedding

Lecturas Adicionales