Agrupación no supervisada de documentos con Elasticsearch + incrustaciones de Jina

Un enfoque práctico y reproducible para la agrupación no supervisada de documentos con Elasticsearch y embeddings de Jina.

La búsqueda vectorial empieza con una consulta, pero ¿qué pasa si no tienes una?

Las organizaciones acumulan grandes colecciones de documentos, como tickets de soporte, presentaciones legales, feeds de noticias y trabajos de investigación, pero para poder hacer las preguntas correctas, primero necesitan entender lo que contienen. Sin etiquetas o datos de entrenamiento, revisar manualmente miles de documentos es poco práctico. La búsqueda tradicional no es útil cuando no sabes qué buscar.

Esta publicación describe un enfoque nativo de Elasticsearch para la agrupación no supervisada de documentos y el seguimiento temporal de temas que aborda este problema de descubrimiento. Después de leerla, podrás seguir la evolución de distintos temas a lo largo de varios días:

Lo que descubrirás:

  • Por qué las incrustaciones de agrupación (y no las incrustaciones de recuperación) son fundamentales para descubrir temas sin una consulta.
  • Cómo la clasificación de centroides con sonda de densidad agrupa documentos por tema usando Elasticsearch k vecinos más cercanos (kNN) y lotes de msearch.
  • ¿Cómo puede significant_text autoetiquetar clústeres para que los temas sean legibles sin entrenar un modelo?
  • De qué forma las cadenas temporales de temas conectan los clústeres de datos diarios para mostrar la evolución de los temas día a día.

Esta publicación se genera a partir de un Jupyter Notebook ejecutable. Las salidas en línea que ves a lo largo del contenido son resultados reales de la pipeline. Clona el cuaderno complementario para ejecutarlo por tu cuenta.

El pipeline utiliza unos 8500 artículos de febrero de 2025 de BBC News y The Guardian como corpus de prueba. Si bien las noticias son convenientes porque tienen un comportamiento temporal claro, el patrón es aplicable si el descubrimiento de documentos es importante: revisión legal, monitoreo del cumplimiento normativo, síntesis de investigación, triage de atención al cliente.

Stack:

  • Jina v5 incrustaciones de agrupación: adaptadores específicos de Low-Rank Adaptation (LoRA) para la agrupación de temas. Jina se unió a Elastic, y sus modelos están disponibles de forma nativa a través del Elastic Inference Service (EIS).
  • Elasticsearch: kNN escalable, etiquetado significant_text y almacenamiento de vectores.
  • DiskBBQ: un formato de índice vectorial basado en disco que combina Better Binary Quantization (BBQ) con una partición jerárquica k-medios para la aceleración aproximada de los vecinos más cercanos (ANN). Esta partición de índice es interna a la búsqueda vectorial y separada del algoritmo de agrupación con sonda de densidad utilizado en esta publicación. bbq_disk almacena vectores cuantificados en el disco y mantiene solo los metadatos de partición en el heap, lo que reduce drásticamente los requisitos de recursos en comparación con bbq_hnsw, mientras mantiene un alto nivel de recuperación.
  • Agrupación global + vinculación temporal diaria: descubrimiento y evolución del tema.

Lo que necesitarás:

  • Un despliegue de Elasticsearch (Elastic Cloud, Elasticsearch Serverless o Elastic Self-Managed 8.18+/9.0+): bbq_disk requiere la versión 8.18 o posterior. La sección opcional de recuperación diversificada requiere 9.3+ o sin servidor.
  • Una clave de API de Jina: la capa gratuita incluye 10 millones de tokens, lo que cubre la pipeline de agrupación del núcleo (~4.25 millones de tokens). La comparación opcional entre recuperación y agrupación usa una segunda pasada de incrustación.
  • Una clave API de Guardian (gratis).

Configuración

Instala los paquetes requeridos:

Opcional (solo si ejecutas los asistentes de raspado desde este repositorio):

Luego configura las claves API en un archivo .env en la raíz del proyecto:

Este cuaderno llama a load_dotenv(override=True), por lo que los valores locales .env tienen prioridad.

Parte 1: agrupación de descubrimiento - ¿Por qué agrupar incrustaciones?

La mayoría de las búsquedas vectoriales usan incrustaciones de recuperación entrenadas para hacer coincidir una consulta con documentos relevantes. Eso es ideal para la búsqueda, pero no para el descubrimiento. Cuando quieras encontrar qué temas existen en un corpus sin ninguna consulta, necesitas incrustaciones que agrupen documentos similares.

Jina v5 resuelve esto con adaptadores de Low-Rank Adaptation (LoRA) específicos para cada tarea. LoRA agrega pequeñas actualizaciones de bajo rango a las capas internas específicas mientras mantiene la mayoría de los pesos del modelo base congelados, por lo que el comportamiento del modelo se desplaza hacia una tarea específica sin repetir el entrenamiento completo. El mismo modelo base produce diferentes incrustaciones según el parámetro task:

TareaCapacitado paraCaso de uso
retrieval.passageCoincidencia búsqueda-documentoBúsqueda, Retrieval-Augmented Generation (RAG)
agrupaciónAgrupación de temas (optimizada para clústeres estrechos)Descubrimiento, categorización

El adaptador de agrupación está entrenado para hacer que los documentos sobre el mismo tema estén más cerca en el espacio de incrustaciones y que los documentos sobre temas diferentes estén más separados. La comparación visual a continuación muestra la diferencia de forma concreta.

Recuperación frente a agrupación: una comparación visual

Para ver la diferencia, se incrusta una muestra de documentos con ambos tipos de tareas. La agrupación se realiza en el espacio de incrustación original de 1024 dimensiones; Uniform Manifold Approximation and Projection (UMAP) se usa solo para proyectar esas incrustaciones en 2D para su visualización. UMAP preserva la estructura de vecindad local, por lo cual es útil para comparar la separación entre clústeres.

A continuación, se muestra la misma muestra de 480 documentos con ambos tipos de tareas y proyectada en 2D con UMAP. Busca grupos de colores más compactos y mejor diferenciados en el panel de agrupación.

Las incrustaciones de recuperación (izquierda) distribuyen los temas ampliamente; las incrustaciones de agrupación (derecha) producen grupos más ajustados y separados de los mismos documentos.

Las incrustaciones de agrupación producen grupos más compactos y visualmente más distintivos. Las incrustaciones de recuperación distribuyen los temas de manera más uniforme, ideales para la búsqueda (similitud de grano fino); pero para el descubrimiento, los clústeres temáticos compactos son lo que importa.

Esta es la razón por la que task="clustering" se usa para el resto de este recorrido.

Carga de los sets de datos

El corpus combina dos fuentes de noticias para febrero de 2025:

Tener varias fuentes permite validar que la agrupación encuentra temas en lugar de estilo específico de la fuente.

Incrustar con la tarea de agrupación

La API de Jina v5 se llama con task="clustering" para todos los documentos. Las incrustaciones se almacenan en caché en disco, por lo que las ejecuciones posteriores se saltan la API por completo.

La llamada a la API es muy sencilla. El parámetro task es la diferencia clave con respecto al uso típico de incrustación:

El tiempo que se muestra a continuación refleja un acierto de caché. La primera ejecución contra la API lleva más tiempo, según el tamaño del corpus.

Indexar un índice único de Elasticsearch

Para la agrupación por descubrimiento, el mes completo se destina a un índice (docs-clustering-all). La partición diaria se realiza más tarde para la vinculación temporal de temas.

El mapeo de índices emplea bbq_disk para el campo vectorial:

Un vector float32 de 1024 dimensiones es de 4 KB. bbq_disk usa k-medios jerárquicos para particionar vectores en pequeños clústeres, los cuantifica en binario y almacena los vectores de precisión completa en el disco para volver a guardarlos. Solo los metadatos de partición permanecen en el heap, por lo que los requisitos de memoria permanecen bajos incluso para corpus grandes. Para las cargas de trabajo que pueden permitirse más memoria dinámica, bbq_hnsw crea un grafo HNSW (Hierarchical Navigable Small World) para agilizar las búsquedas, aunque a costa de un mayor consumo de recursos.

El tipo de campo dense_vector brinda soporte para múltiples estrategias de cuantización: bbq_disk y bbq_hnsw son las más adecuadas para embeddings de alta dimensión, como los vectores de 1024 dimensiones usados aquí.

Agrupación: clasificación del centroide sondeada por densidad

Los algoritmos de agrupación tradicionales como HDBSCAN asumen que puedes mantener la matriz de vectores N × d completa en memoria y ejecutar actualizaciones de pasada completa repetidas. Para 8495 documentos con 1024 dimensiones, esto es manejable (~35 MB), pero el enfoque no escala a millones de documentos sin infraestructura adicional.

Este algoritmo es conceptualmente similar a la inicialización de KMedios++ con asignación de Voronoi y un nivel de ruido, pero emplea la búsqueda kNN de Elasticsearch como primitiva de cálculo, manteniendo casi todo el trabajo en el servidor:

  1. Muestrea el 5 % de los documentos como sondas de densidad (muestra aleatoria, mínimo 50).
  2. Densidad de sondas vía lotes de msearch kNN. Cada sonda dispara una búsqueda kNN y registra la similitud media de sus vecinos. Alta similitud media = región densa del espacio de incrustación. msearch envía múltiples solicitudes de búsqueda en una sola llamada HTTP, lo cual es fundamental en este caso: el sondeo de densidad genera cientos de consultas kNN, y agruparlas evita la sobrecarga por solicitud.
  3. Seleccione semillas de alta densidad con diversificación: los candidatos por encima de la densidad mediana se ordenan por densidad descendente y se aceptan de manera ávida solo cuando su similitud del coseno con cada semilla existente está por debajo de un umbral de separación. Este es el único cómputo del lado del cliente (~0.01s para 8k docs).
  4. Clasifica todos los documentos frente a los centroides mediante msearch kNN: cada semilla actúa como un centroide; una búsqueda kNN recupera documentos cercanos por encima de un umbral de similitud. Cada documento se asigna al centroide que lo devolvió con la puntuación más alta. Los clústeres pequeños se disuelven en ruido.

Elasticsearch se encarga del trabajo pesado: msearch para sondas de densidad, msearch para clasificación y significant_text para etiquetado. Para este corpus (8495 documentos), la muestra del 5 % de sondeo de densidad lanza 425 consultas kNN de sondeo, que msearch agrupa en nueve llamadas HTTP (con lotes de 50), lo que evita la sobrecarga de una solicitud por sondeo. Combinado con la búsqueda ANN de bbq_disk, esto mantiene la etapa de agrupación rápida y escalable. Las consultas kNN usan un valor mínimo de num_candidates para mayor velocidad durante la pasada de agrupación; las consultas de búsqueda de producción deben usar valores más altos de num_candidates para mejorar la recuperación a costa de la latencia.

Los clústeres tienen tamaños naturales determinados por la densidad del espacio de incrustación alrededor de cada centroide, no por un límite rígido de k. Las regiones temáticas densas producen clústeres más grandes; los temas de nicho producen clústeres más pequeños.

¿Por qué no KMeans o HDBSCAN?

KMedios asume clústeres esféricos y requiere la matriz N×d completa en memoria. Para corpus que caben en memoria, HDBSCAN es una alternativa estable. Maneja formas arbitrarias de clústeres y tiene una semántica de densidad bien entendida.

El enfoque de centroides con sondeo de densidad apunta a un nicho diferente: corpus con almacenamiento, recuperación y agrupación en un solo sistema, o en los que la escala hace que las operaciones matriciales del lado del cliente sean poco prácticas. Usa Elasticsearch kNN como primitiva de cómputo, maneja tamaños de clúster arbitrarios y mantiene casi todo el procesamiento del lado del servidor.

Comprender la tasa de ruido

La tasa de ruido de ~28 % es intencional, no un modo de falla. Los documentos que no encajan en ningún clúster denso en el similarity_threshold configurado quedan sin asignar en vez de ser forzados a una coincidencia deficiente. Esto actúa como un control de calidad: las columnas de opinión, los artículos cortos y las historias aisladas resisten naturalmente la acción de agrupar porque carecen de la densidad temática que define un grupo coherente.

El umbral es ajustable: reducir similarity_threshold produce una agrupación más agresiva (más documentos asignados, pero clústeres más dispersos), mientras que aumentarlo ajusta los clústeres e incrementa la fracción de ruido. Para este corpus de contenido de noticias mixtas, ~30 % de ruido es un punto de operación razonable. Los despliegues de producción deben ajustar el umbral según criterios de calidad específicos del dominio.

Etiquetas automáticas con significant_text

Ahora cada clúster necesita una etiqueta legible para humanos. La agregación significant_text de Elasticsearch encuentra términos que aparecen inusualmente a menudo en un conjunto en primer plano (el clúster) en comparación con un conjunto de fondo (el corpus completo).

En el fondo, utiliza una heurística estadística (puntuación JLH de forma predeterminada) que equilibra los cambios de frecuencia absoluta y relativa, sin machine learning, sin llamadas a modelos de lenguaje grandes (LLM). Un clúster sobre política del Reino Unido podría mostrar términos como starmer, labour, downing porque esos términos son desproporcionadamente comunes en ese clúster en comparación con el corpus de noticias general.

Para esta pasada global, las etiquetas se calculan directamente con respecto a docs-clustering-all, por lo que tanto el primer plano como el fondo se extraen del mes completo. En la parte 2, el etiquetado usa el patrón de índice diario (docs-clustering-*), un comodín que permite que las búsquedas abarquen todos los índices coincidentes simultáneamente, para darle a significant_text un fondo más amplio y lograr un mejor contraste.

Una forma mínima de consulta se ve así:

significant_text también sirve como control de calidad: los clústeres que no producen términos importantes no tienen vocabulario distintivo. Son agrupaciones incoherentes que deberían disolverse de nuevo en ruido en lugar de recibir una etiqueta engañosa.

Un paso de limpieza determinista y ligero elimina los términos de etiqueta ruidosos (tokens numéricos, palabras genéricas) y recurre a un titular representativo cuando es necesario. Esto mantiene las etiquetas nativas de Elasticsearch y, al mismo tiempo, mejora la legibilidad.

Visualizar los clústeres

Las visualizaciones a continuación muestran lo que descubrió la pasada global de agrupación: un desglose por fecha de los documentos agrupados frente a los documentos de ruido, una proyección UMAP del mes completo y un gráfico de combinación de fuentes que confirma que los clústeres reflejan temas en lugar de fuentes.

Distribución diaria de documentos agrupados frente a documentos de ruido a lo largo de febrero de 2025.

Cada isla coloreada en el UMAP representa un clúster: un grupo de artículos sobre el mismo tema descubiertos únicamente a partir de la similitud de incrustación. Los puntos de ruido gris son artículos que no encajaban perfectamente en ningún clúster (a menudo artículos cortos, artículos de opinión o historias únicas).

El gráfico de desglose de fuentes confirma que los clústeres contienen artículos de ambas BBC News y The Guardian. La agrupación está encontrando temas, no fuentes: exactamente lo que el descubrimiento no supervisado debería arrojar.

Explorar la amplitud del clúster con el recuperador diversificado

El kNN simple devuelve los documentos más similares al centroide de un clúster (el núcleo compacto). Pero los clústeres reales cubren subtemas. El recuperador diversificado utiliza Maximal Marginal Relevance (MMR) para mostrar documentos que son relevantes para el centroide pero también diferentes entre sí.

El parámetro clave es λ (lambda):

  • λ = 1.0 → pura relevancia (igual que kNN simple).
  • λ = 0.0 → diversidad pura (resultados de máxima distribución).
  • λ = 0.5 → equilibrado: es relevante para el tema, pero cubre diferentes ángulos.

Nota de versión: el diversificador está disponible en Elastic Cloud Serverless y en Elasticsearch autogestionado 9.3+. Las versiones anteriores aún pueden seguir las secciones de agrupación y vinculación temporal; solo este paso de exploración requiere el diversificador.

Una solicitud mínima de recuperador tiene el siguiente aspecto:

Los parámetros type, field y query_vector son necesarios a nivel de diversificación: field indica al MMR qué campo dense_vector usar para la similitud entre resultados, y query_vector proporciona el punto de referencia para el puntaje de relevancia.

Esto te permite responder: “¿Qué abarca realmente este clúster?” en vez de solo “¿Qué hay en su centro?”

Los resultados simples de kNN se agrupan en torno a un ángulo del tema: los documentos más similares al centroide y entre sí. El recuperador diversificado muestra diferentes facetas del mismo clúster: subtemas, fuentes diferentes y perspectivas variadas.

La métrica de diversidad confirma esto cuantitativamente: la similitud promedio por pares es menor en los resultados del recuperador diversificado, lo que significa que los documentos arrojados tienen mayor alcance.

Esto es útil para:

  • Comprender el alcance real de un clúster: no solo su centro, sino también sus bordes.
  • Generar resúmenes. Los documentos diversos y representativos le dan a un LLM mejor material.
  • Encontrar ejemplos representativos para revisión humana o etiquetado posterior.
  • Controles de calidad. Si los diversos resultados parecen incoherentes, es posible que el clúster deba dividirse.

Parte 2: cadenas temáticas temporales

Seguimiento de temas con el paso de los días

La parte 1 agrupó todo el mes a nivel global para el descubrimiento de temas. Para el flujo temporal, la misma clasificación de centroides con sonda de densidad se ejecuta independientemente por día en los índices diarios, y luego los clústeres se vinculan en días adyacentes. Tenga en cuenta que los clústeres diarios son independientes de los clústeres globales de la parte 1; cada día produce sus propias asignaciones y etiquetas de clúster ajustadas al contenido de ese día.

El enfoque de enlazado: muestra y consulta

Para cada clúster el día A:

  1. Muestra algunos documentos representativos.
  2. Ejecuta kNN contra el índice del día B.
  3. Cuenta cuántas coincidencias se registran en cada clúster del día B.
  4. Si la fracción de aciertos excede un umbral (fracción kNN ≥ 0.4), registra un enlace.

Esto es rápido (solo se consultan unos pocos documentos por clúster, no todos) y usa el kNN nativo de Elasticsearch, sin necesidad de herramientas externas.

Una fracción kNN del 100 % significa que cada documento muestreado del clúster de origen llegó al mismo clúster de destino, el vínculo entre días más fuerte posible. La mayoría de los vínculos anteriores están relacionados con el fútbol, lo que tiene sentido: la cobertura de la Premier League se ejecuta a diario con alta consistencia temática.

El enlace score | operator | gedlingleague | striker | season es un ejemplo de un clúster de fútbol local de nicho (Gedling es un club de liga no profesional) que se integra en el clúster más amplio de la Premier League al día siguiente, una consecuencia natural de la reagrupación diaria a diferentes niveles de granularidad.

Construyendo cadenas de historias

Una cadena de temas es una secuencia de agrupaciones vinculadas en días consecutivos.

Los enlaces individuales por pares indican que el clúster "Política del Reino Unido" del lunes se conecta con el del martes. Las cadenas revelan la evolución completa: una historia que comienza el lunes, evoluciona a lo largo de la semana y se desvanece el viernes.

Las cadenas se construyen de forma voraz a partir de enlaces con una fracción kNN ≥ 0.4, lo que significa que al menos el 40 % de los documentos muestreados del clúster de origen terminaron en un único clúster de destino. Comenzando desde el clúster más antiguo, el algoritmo siempre sigue el enlace saliente más fuerte.

La cadena más larga rastrea la cobertura Ucrania-Rusia durante 19 días consecutivos, lo cual no es sorprendente dado el sostenido nivel de intensidad geopolítica en febrero de 2025. El segundo más largo sigue al fútbol de la Premier League durante 19 días del mes. Las cadenas más cortas capturan la temporada de premios (cine/premios, seis días), el rugby de las Seis Naciones (10 días) y la cobertura del liderazgo político del Reino Unido (siete días). Cada cadena representa una evolución temática que el algoritmo descubrió puramente a partir de la similitud de incrustaciones a través de índices diarios.

Sankey: visualizando el flujo de la historia

Un diagrama de Sankey es una visualización de un flujo en la que el grosor de los enlaces indica la intensidad de la conexión. Aquí, cada banda vertical es un día, cada nodo es un clúster diario (dimensionado por el recuento de documentos) y cada ruta de color traza una cadena de temas a lo largo del tiempo. El ancho del enlace codifica la fuerza de superposición de kNN: los enlaces más gruesos significan que más documentos muestreados llegaron al clúster de destino. Los colores son consistentes por cadena, así que una sola ruta de color de izquierda a derecha se lee como la progresión de un tema.

Por ejemplo, la cadena Ucrania-Rusia (que se ve como una de las rutas más largas) fluye de manera continua desde principios de febrero hasta la tercera semana, con enlaces que se mantienen gruesos, lo que indica una fuerte continuidad temática a lo largo de los días.

Cadenas temporales de temas que se desarrollan a lo largo de febrero de 2025. Cada camino coloreado es un tema que persiste a lo largo de los días; el ancho del enlace indica la fuerza de superposición kNN.

Qué ofrece este enfoque

Esta guía cubrió una pipeline completa de agrupación de documentos sin supervisión desarrollada sobre Elasticsearch:

  1. Agrupar incrustaciones: los adaptadores específicos de la tarea de Jina v5 producen incrustaciones optimizadas para la agrupación por temas, no solo para la coincidencia entre consulta y documento.
  2. Agrupación de descubrimiento global: agrupar el mes completo en un índice maximiza el descubrimiento de temas entre días.
  3. Clasificación de centroides con densidad probada: muestrea el 5 %, sondea densidad vía msearch kNN, selecciona semillas diversas de alta densidad, clasifica todos los documentos contra los centroides. Elasticsearch maneja el cómputo pesado; solo la selección de semillas se ejecuta del lado del cliente (~0.01 s).
  4. significant_text etiquetado: las pruebas de significancia producen etiquetas de clúster significativas sin ningún modelo de ML o anotación manual. Los clústeres que no producen términos significativos son incoherentes y se degradan a ruido: una barrera de calidad integrada.
  5. Vinculación temporal de temas: los índices diarios y el rastreo de muestras y consultas de kNN entre índices permiten rastrear cómo evolucionan los temas a lo largo del tiempo.

Conclusiones clave:

  • El tipo de tarea de incrustación es importante: las incrustaciones de agrupación producen grupos temáticos notablemente más compactos.
  • Elasticsearch puede funcionar tanto como capa de almacenamiento como motor de agrupación mediante búsqueda kNN.
  • La clasificación de centroides con sonda de densidad mantiene casi todos los datos del lado del servidor y produce clústeres con tamaños naturales determinados por la densidad de espacio de incrustaciones.
  • significant_text es rápido, interpretable y efectivo tanto para el etiquetado automático como para el control de calidad.

Este enfoque es útil en los siguientes casos:

  • Tienes texto con fecha y quieres descubrir un tema sin datos de entrenamiento etiquetados.
  • Deseas una pila para almacenamiento, búsqueda de vectores, etiquetado y vinculación temporal.

Extensiones para explorar:

  • Agrupación de varios períodos (resúmenes semanales y mensuales).
  • Ingesta en tiempo real con asignación incremental de clúster.
  • Resúmenes de clústeres generados por LLM usando los términos de significant_text como semillas.
  • A mayor escala, los centroides de K-Medios obtenidos mediante muestreo pueden servir como semillas de inicio rápido para la agrupación basada en densidad, lo que reduce el costo de la fase de exploración.

Pruébalo tú mismo

Sustituye el corpus de documentos con marcas de tiempo por el tuyo; cualquier colección de texto con fechas funciona con este pipeline. El cuaderno completo y el código de soporte están disponibles en el repositorio complementario.

¿Te ha sido útil este contenido?

No es útil

Algo útil

Muy útil

Contenido relacionado

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo