Cómo medir y mejorar la recuperación de búsqueda de Elasticsearch: de 0,43 a 0,75 con búsqueda híbrida

Aprende a medir y mejorar la recuperación de búsqueda en Elasticsearch combinando la búsqueda léxica BM25 con incrustaciones vectoriales de Jina AI, usando la API rank_eval para validar la mejora con cifras reales.

La búsqueda léxica mediante el algoritmo de clasificación BM25 es económica, rápida y muy eficaz para una amplia gama de consultas. Sin embargo, tiene un punto ciego: las consultas que no comparten tokens con tus documentos. En este artículo, mencionaremos con precisión en qué aspectos se queda corto el BM25. Emplearemos la API de evaluación de clasificación (rank_eval) de Elasticsearch y cerraremos esa brecha agregando incrustaciones de Jina AI mediante Elastic Inference Service (EIS). Verás cómo la puntuación de recuperación pasa de 0.43 a 0.75 y entenderás por qué.

¿Qué es la recuperación?

La recuperación mide en una escala de 0 a 1 cuántos de los documentos que realmente quieren tus usuarios aparecen en algún lugar de los resultados de búsqueda. Si una consulta debe mostrar tres productos y la búsqueda solo arroja dos de ellos en los 10 principales, recall@10 = 0.67 para esa consulta. Es una métrica basada en conjuntos: no le importa la posición de los documentos relevantes dentro de esos k resultados. Un documento relevante en la posición 10 cuenta igual que uno en la posición 1. Tener una recuperación alta significa que no estás perdiendo resultados relevantes.


El diagrama muestra dos conjuntos: todos los documentos relevantes (izquierda) y lo que BM25 realmente recuperó (10 principales, derecha). Solo la intersección cuenta para la recuperación, se encontraron prod_1 y prod_2, mientras que prod_3, prod_4 y prod_6 se perdieron por completo. Resultado: Recall@10 = 2/5 = 0.40.

Requisitos previos

Pongamos manos a la obra para comprender mejor cómo funciona la recuperación. Esta demostración utiliza Python. Puedes seguirlo en el cuaderno complementario (notebook.ipynb), donde cada bloque de código es una celda lista para ejecutarse.

El código proporcionado utiliza lo siguiente:

  • Elasticsearch 9.3+
  • Python 3.10+
  • Un archivo .env con tus credenciales de Elasticsearch

El set de datos

Emplearemos un catálogo con 1000 productos, que abarcan categorías como calzado, electrónica, herramientas y más.

Cada documento tiene cuatro campos:

CampoTipo
`título`texto
'descripción'texto
`marca`palabra clave
'categoría'palabra clave

El set de datos se carga desde dataset.csv.

BM25 es el algoritmo de clasificación predeterminado en Elasticsearch y en la mayoría de los motores de búsqueda. Califica los documentos según la frecuencia con la que aparecen tus términos de consulta en ellos, ajustado por la longitud del documento y la frecuencia de esos términos en todo el índice. Los analizadores aparecen en la parte superior: normalización de minúsculas, derivación y eliminación de palabras vacías. Una búsqueda de "zapatillas para correr" coincidirá con "Zapatillas para correr" y probablemente también con "correr".

Esto funciona bien para una amplia clase de consultas:

  • "zapatillas de correr" muestra de inmediato los productos que contienen exactamente esas palabras en el título.
  • "parlante con bluetooth" muestra productos de audio portátiles porque los tokens aparecen textualmente.

Los resultados son deterministas y explicables: un documento se clasifica alto porque los términos de la consulta aparecen en él. Depurar la relevancia es sencillo.

Cuándo falla

Ahora probemos estas búsquedas en el mismo catálogo:

  • "rutina de skincare": la palabra "rutina" no aparece en ningún título de producto. BM25 puede hacer una coincidencia parcial con "skincare", pero los sueros faciales, aceites corporales y humectantes se describen utilizando términos como "vitamina C", "retinol" o "aclarante", ninguno de los cuales se superpone con la consulta. Los productos que forman una rutina completa de skincare están dispersos en el índice sin ningún token compartido para anclarlos.
  • "accesorios de viaje para mascotas": esta es una agrupación de casos de uso, no una categoría de producto. Un transportín para perros, un asiento para mascotas en el automóvil y una jaula de viaje son todos relevantes, pero sus descripciones hablan sobre portabilidad, seguridad y comodidad en vez de "accesorios de viaje". BM25 coincide con "mascota" de manera amplia, pero no tiene ninguna señal para distinguir los productos específicos de viaje del resto del catálogo de mascotas.

Este es un problema de recuperación. Los documentos relevantes están en tu índice. BM25 simplemente no puede encontrarlos porque las palabras del usuario y las del documento no coinciden lo suficiente.

Agregar sinónimos es útil en casos conocidos. Pero no se puede enumerar todas las formas en que un usuario podría expresar una intención. Ahí es donde entran los vectores.

Por qué deberías medir el recall

Antes de solucionar un problema, necesitas cuantificarlo.

Recall@k mide cuántos de los documentos que realmente buscan tus usuarios aparecen en algún lugar de los resultados de búsqueda. Es decir:

Precision@k mide los mejores k resultados y cuántos son realmente relevantes:

La alta precisión significa que los resultados que se devuelven son buenos. En el comercio electrónico, no mostrar un producto relevante (baja recuperación) suele ser peor que mostrar un resultado ligeramente imperfecto (menor precisión), porque un producto oculto es una venta perdida.

La API rank_eval de Elasticsearch te permite medir ambos de forma sistemática. Proporcionas una lista de consultas, cada una con un conjunto de documentos calificados, y Elasticsearch calcula las métricas por ti en todas las consultas.

Configuración de la evaluación

La API rank_eval necesita un set de datos de calificaciones: un mapeo de consultas a los documentos que son relevantes para cada uno, junto con un grado de relevancia (0 = no relevante, 1 = relevante, 2 = altamente relevante).

En el cuaderno, esta es la lista de evaluaciones:

La combinación es intencionada: q1 es una consulta que BM25 maneja bien (tokens exactos en los títulos de productos), mientras que q2, q3 y q4 son consultas basadas en la intención donde la intención del usuario se expresa como un concepto en lugar de palabras clave específicas de producto.

Medición de la recuperación basal de BM25

Primero, configura el cliente de Elasticsearch e indexa los datos de texto sin procesar:

Ahora crea la solicitud rank_eval para BM25. Cada solicitud en la lista combina una consulta con sus calificaciones:

Resultado:

0.43 significa que en las cuatro consultas, BM25 encuentra solo el 43 % de los documentos que debería encontrar. La deficiencia se concentra en las consultas basadas en la intención: "rutina de skincare" no encuentra sueros faciales ni aceites corporales porque la palabra "rutina" nunca aparece en los títulos de los productos, y "accesorios de viaje para mascotas" recupera productos para mascotas que no tienen relación con el tema, mientras que no encuentra transportines ni jaulas descritos en términos de portabilidad y seguridad en vez de "accesorios de viaje".

Esta es nuestra referencia base. Ahora tenemos un número que superar.

Agregar búsqueda vectorial con embeddings de Jina

Vector search Codifica documentos y consultas como vectores de alta dimensión, un tipo de vector compuesto por cientos o miles de valores numéricos, cada uno de los cuales codifica una característica específica de los datos que representa. Los documentos con significado similar terminan cerca unos de otros en el espacio vectorial, incluso si no comparten palabras. "Equipo de gimnasio" y "conjunto de mancuernas" estarán cerca porque los conceptos están relacionados. Elegí Elasticsearch como mi base de datos vectorial porque admite la búsqueda híbrida, lo que me brinda comprensión semántica y precisión de palabras clave de inmediato.

EIS incluye soporte integrado para la incorporación de modelos a través de su API de inferencia.

Paso 1: usar las incrustaciones de Jina v5 como endpoint de inferencia

Si tu clúster tiene recursos de GPU (disponibles en Elastic Cloud y Elasticsearch 9.3+), las incrustaciones se generan en GPU, lo cual es mucho más rápido que la inferencia de CPU y elimina el compromiso de rendimiento que históricamente hacía que los vectores fueran caros a gran escala.

¿Por qué las incrustaciones de Jina en particular? jina-embeddings-v5-text es un modelo multilingüe (más de 119 idiomas) con una ventana de contexto de 32 000 tokens y soporte para adaptadores de Adaptación de Rango Bajo (LoRA) específicos para tareas. Funciona bien para descripciones cortas de productos que están listas para usar. Lee más sobre el modelo jina-embeddings-v5-text aquí.

Paso 2: Crear el índice con un campo semántico

El tipo de campo semantic_text es la clave aquí. Es una abstracción de mayor nivel sobre dense_vector: la apuntas a un endpoint de inferencia, y Elasticsearch se encarga de generar incrustaciones de forma automática.

La propiedad copy_to en title y description significa que el contenido de ambos campos fluye hacia semantic_field para su incrustación, por lo que un solo vector captura la representación completa del producto.

Paso 3: Indexar los productos

En tiempo de indexación, Elasticsearch llama al endpoint de inferencia para cada documento y almacena la incrustación resultante en semantic_field. No necesitas agregar ningún código adicional.

Búsqueda híbrida: combinación de BM25 y vectores con RRF

Agregar vectores mejora la recuperación, pero usar solo vectores corre el riesgo de perder precisión en las consultas de coincidencia exacta; "zapatillas para correr" aún debe clasificar primero las coincidencias literales. La búsqueda híbrida retiene el componente léxico específicamente para preservar esa precisión.

La búsqueda híbrida con Reciprocal Rank Fusion (RRF) combina lo mejor de ambos:

  • BM25 se ocupa de consultas exactas y casi exactas con alta precisión.
  • La búsqueda semántica gestiona consultas basadas en la intención y multilingües con alto nivel de recuperación.
  • RRF combina las dos listas clasificadas en una sola clasificación.

La fórmula RRF asigna a cada documento una puntuación basada en su clasificación en cada lista de resultados:

Un documento que se clasifica en una posición alta en ambas listas obtiene una puntuación combinada más alta. El rank_constant controla cuánto peso reciben los documentos de menor rango.

Resultado:

La búsqueda híbrida mejora sustancialmente sobre BM25 (0.43) y preserva la precisión para consultas de coincidencia exacta como "zapatillas para correr".

Resultados: Antes y después

Aquí está la comparación completa entre los tres enfoques:

Resultado:

MétodoRecall@10
BM25 (Lexical)0,43
Híbrido (BM25 + Vectores)0,75

Desglose por consulta:

Conclusión

A lo largo de esta publicación, vimos que la búsqueda léxica BM25 es confiable cuando los usuarios teclean consultas exactas, pero pierde recuperación cuando buscan por intención en lugar de palabras clave. Usando rank_eval, establecimos una línea de base reproducible para medir esa brecha con números reales. A partir de ahí, agregamos un semantic_text campo impulsado por incrustaciones de Jina y volvimos a ejecutar la evaluación. El resultado: la búsqueda híbrida mejoró la recuperación de 0.43 a 0.75 a la vez que conservó la precisión en las consultas de coincidencia exacta, aunque el margen real dependerá de tu combinación de consultas.

El patrón escala más allá de este ejemplo: recopila juicios de las consultas reales de tus usuarios, ejecuta rank_eval como línea de base, agrega semantic_text y vuelve a medir. Sabrás exactamente qué mejoró y en qué medida.

Pasos siguientes

¿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