0Pricing
AI Engineering Academy · Lección

Métricas de recuperación: hit rate, MRR y NDCG

Creará un conjunto de datos de referencia con consultas y documentos relevantes, y calculará hit rate, reciprocal rank medio y NDCG para medir con qué frecuencia el recuperador encuentra los fragmentos correctos.

Métricas de recuperación: hit rate, MRR y NDCG es una lección gratuita de AI Engineering Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Engineering Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Engineering Academy incluye 4 lecciones en total.

¿Por qué existen distintas métricas de recuperación?

La tasa de aciertos indica si un fragmento relevante apareció en algún lugar de los K resultados principales, pero no indica dónde apareció en la clasificación. Un sistema que siempre coloca el mejor fragmento en la posición 5 es peor que uno que lo coloca sistemáticamente en la posición 1, aunque ambos tengan la misma tasa de aciertos. Métricas más precisas, como MRR y NDCG, capturan la calidad de la clasificación y recompensan a los sistemas que colocan los fragmentos más relevantes al principio, donde es más probable que el LLM y los usuarios los utilicen.

Tasa de aciertos @K: revisión e implementación

La tasa de aciertos@K es la métrica más sencilla: ¿para qué fracción de las consultas aparece al menos un fragmento relevante entre los K resultados principales? Proporciona una señal binaria por consulta y es fácil de interpretar. Calcule esta métrica comprobando la intersección entre el conjunto de identificadores recuperados y el conjunto de identificadores relevantes conocidos. Utilice K=5 como valor predeterminado, ya que la mayoría de los sistemas RAG recuperan 5 fragmentos. Compare la tasa de aciertos@1, @3 y @5 para comprender cómo cambia la sensibilidad al ampliar la ventana de recuperación.

def hit_rate_at_k(golden_dataset, retriever, k=5):
    hits = 0
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=k)
        retrieved_ids = [r['id'] for r in retrieved[:k]]
        relevant_ids = set(item['relevant_chunk_ids'])
        if any(rid in relevant_ids for rid in retrieved_ids):
            hits += 1
    return hits / len(golden_dataset)

for k in [1, 3, 5, 10]:
    hr = hit_rate_at_k(golden_dataset, retriever, k=k)
    print(f'Hit rate@{k}: {hr:.1%}')

Rango recíproco medio (MRR)

MRR (Mean Reciprocal Rank) mide el promedio del inverso de la posición en la que aparece el primer fragmento relevante. Si el fragmento relevante ocupa la posición 1, el rango recíproco es 1/1 = 1.0. En la posición 2 es 0.5 y en la posición 5 es 0.2. El MRR se calcula como promedio de todas las consultas. Un MRR más alto significa que el recuperador coloca sistemáticamente los fragmentos relevantes cerca del principio, lo cual es importante porque el LLM presta más atención al contexto situado al principio del prompt.

def mean_reciprocal_rank(golden_dataset, retriever, top_k=10):
    reciprocal_ranks = []
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=top_k)
        retrieved_ids = [r['id'] for r in retrieved]
        relevant_ids = set(item['relevant_chunk_ids'])

        rr = 0.0
        for rank, rid in enumerate(retrieved_ids, start=1):
            if rid in relevant_ids:
                rr = 1.0 / rank
                break  # only the first relevant result counts
        reciprocal_ranks.append(rr)

    mrr = sum(reciprocal_ranks) / len(reciprocal_ranks)
    print(f'MRR@{top_k}: {mrr:.3f}')
    return mrr

Interpretación de las puntuaciones MRR

Las puntuaciones MRR tienen interpretaciones intuitivas: MRR = 1.0 significa que el primer fragmento relevante siempre ocupa la posición 1 (resultado perfecto). MRR = 0.5 significa que normalmente ocupa la posición 2. MRR = 0.25 significa que normalmente ocupa la posición 4; la información relevante aparece lo bastante abajo como para que se trunque al formar el contexto. En RAG, el objetivo es MRR > 0.7 para garantizar que el fragmento más relevante se encuentre sistemáticamente entre las dos primeras posiciones.

# MRR interpretation table
mrr_interpretations = {
    1.0:  'Perfect — relevant chunk always at rank 1',
    0.5:  'Good — typically at rank 2',
    0.33: 'Acceptable — typically at rank 3',
    0.25: 'Weak — typically at rank 4',
    0.1:  'Poor — relevant chunk rarely near the top'
}

for score, description in mrr_interpretations.items():
    print(f'MRR {score:.2f}: {description}')

Precisión @K

Precision@K mide qué fracción de los K fragmentos recuperados es realmente relevante. A diferencia de la tasa de aciertos (que es binaria), precision@K mide la relación señal-ruido de los resultados de recuperación. Una precisión baja significa que el LLM recibe contexto irrelevante junto con los fragmentos relevantes, lo que aumenta el riesgo de confusión o de inyección de prompts. En RAG, un objetivo saludable es precision@5 > 0.6.

def precision_at_k(golden_dataset, retriever, k=5):
    precisions = []
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=k)
        retrieved_ids = [r['id'] for r in retrieved[:k]]
        relevant_ids = set(item['relevant_chunk_ids'])

        relevant_retrieved = sum(
            1 for rid in retrieved_ids if rid in relevant_ids
        )
        precision = relevant_retrieved / k
        precisions.append(precision)

    mean_precision = sum(precisions) / len(precisions)
    print(f'Precision@{k}: {mean_precision:.3f}')
    return mean_precision

Ganancia acumulada descontada (DCG)

DCG es una métrica de evaluación de listas clasificadas que recompensa colocar los fragmentos más relevantes en las posiciones superiores. Suma las puntuaciones de relevancia de los fragmentos recuperados, pero las descuenta logarítmicamente según la posición: la posición 1 recibe todo el crédito, la posición 2 recibe el descuento de log(2), y así sucesivamente. Los fragmentos más relevantes en las primeras posiciones producen un DCG mayor. La versión normalizada (NDCG) divide el resultado entre el DCG ideal (la mejor clasificación posible) para producir una puntuación entre 0 y 1.

import math

def dcg_at_k(relevances, k):
    '''relevances[i] = 1 if chunk at rank i+1 is relevant, else 0'''
    dcg = 0.0
    for i, rel in enumerate(relevances[:k]):
        # rank is i+1, discount is log2(rank + 1)
        dcg += rel / math.log2(i + 2)
    return dcg

def ndcg_at_k(retrieved_ids, relevant_ids, k):
    relevances = [1 if rid in relevant_ids else 0
                  for rid in retrieved_ids[:k]]
    actual_dcg = dcg_at_k(relevances, k)
    ideal_dcg = dcg_at_k([1] * min(len(relevant_ids), k), k)
    return actual_dcg / ideal_dcg if ideal_dcg > 0 else 0.0

Cálculo de NDCG en todo el conjunto de datos

NDCG@K es la métrica de referencia para evaluar la recuperación en sistemas de búsqueda. Captura simultáneamente la relevancia y la posición en la clasificación. Un NDCG@5 de 0.85 significa que, en promedio, su recuperador alcanza el 85 % de la mejor clasificación teórica. NDCG gestiona los casos en los que hay varios fragmentos relevantes por consulta (cada uno recibe una puntuación de relevancia) y penaliza a los sistemas que encuentran los fragmentos relevantes, pero los clasifican demasiado abajo.

def mean_ndcg_at_k(golden_dataset, retriever, k=5):
    ndcg_scores = []
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=k)
        retrieved_ids = [r['id'] for r in retrieved[:k]]
        relevant_ids = set(item['relevant_chunk_ids'])
        score = ndcg_at_k(retrieved_ids, relevant_ids, k)
        ndcg_scores.append(score)
    mean = sum(ndcg_scores) / len(ndcg_scores)
    print(f'NDCG@{k}: {mean:.4f}')
    return mean

# Compute all retrieval metrics together
hit_rate = hit_rate_at_k(golden_dataset, retriever, k=5)
mrr = mean_reciprocal_rank(golden_dataset, retriever, top_k=5)
ndcg = mean_ndcg_at_k(golden_dataset, retriever, k=5)

Uso de RAGAS para métricas automatizadas

La biblioteca RAGAS proporciona un framework de evaluación listo para producción para sistemas RAG. Implementa métricas de precisión del contexto, exhaustividad del contexto, fidelidad y relevancia de la respuesta mediante un enfoque de LLM como evaluador. Pase a RAGAS sus preguntas, respuestas, contextos recuperados y respuestas de referencia, y recibirá un informe de evaluación completo con las puntuaciones de cada métrica. Esta es la forma más rápida de configurar un pipeline integral de evaluación de RAG.

from ragas import evaluate
from ragas.metrics import (
    context_precision,
    context_recall,
    faithfulness,
    answer_relevancy
)
from datasets import Dataset

eval_data = Dataset.from_list([
    {
        'question': item['question'],
        'answer': item['generated_answer'],
        'contexts': item['retrieved_texts'],
        'ground_truth': item['expected_answer']
    }
    for item in golden_dataset_with_answers
])

results = evaluate(
    eval_data,
    metrics=[context_precision, context_recall, faithfulness, answer_relevancy]
)
print(results)

Segmentación de métricas por categoría de consulta

Las métricas promedio generales ocultan patrones importantes. Divida su conjunto de datos de referencia en categorías —consultas de datos fácticos, comparaciones, preguntas procedimentales de tipo «cómo hacerlo» y preguntas fuera del alcance— y calcule las métricas por separado para cada una. Puede descubrir que la tasa de aciertos es del 95 % para las consultas de datos fácticos, pero solo del 60 % para las comparaciones de varios pasos. Las métricas por categoría revelan los modos de fallo específicos que las puntuaciones agregadas ocultan.

from collections import defaultdict

def evaluate_by_category(golden_dataset, retriever):
    by_category = defaultdict(list)
    for item in golden_dataset:
        category = item.get('category', 'unknown')
        retrieved = retriever.retrieve(item['question'], top_k=5)
        retrieved_ids = {r['id'] for r in retrieved}
        hit = bool(retrieved_ids & set(item['relevant_chunk_ids']))
        by_category[category].append(hit)

    print('Hit rate by category:')
    for cat, hits in sorted(by_category.items()):
        hr = sum(hits) / len(hits)
        print(f'  {cat}: {hr:.1%} ({sum(hits)}/{len(hits)})')

Cuando las métricas no coinciden

A veces, las métricas envían señales contradictorias. Puede mejorar el NDCG (una mejor clasificación) mientras la tasa de aciertos permanece estable (el mismo número de fallos). Esto ocurre cuando la optimización mueve fragmentos relevantes de la posición 6 a la 2, sin incluir en los cinco primeros resultados consultas que antes no tenían aciertos. En estos casos, compruebe si la optimización ayudó en las consultas que ya funcionaban correctamente y descuidó las que fallaban. Examine siempre ejemplos de fallos individuales junto con las métricas agregadas.

def analyze_failures(golden_dataset, retriever, top_k=5):
    failures = []
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=top_k)
        retrieved_ids = {r['id'] for r in retrieved}
        relevant_ids = set(item['relevant_chunk_ids'])
        if not (retrieved_ids & relevant_ids):
            failures.append({
                'question': item['question'],
                'expected_chunks': list(relevant_ids),
                'retrieved_chunks': [r['id'] for r in retrieved],
                'top_score': retrieved[0]['score'] if retrieved else None
            })
    print(f'Failures: {len(failures)}/{len(golden_dataset)}')
    return failures

Exhaustividad @K: integridad de la recuperación

Recall@K mide qué fracción de todos los fragmentos relevantes se recuperó entre los K resultados principales. Si una pregunta tiene 3 fragmentos relevantes en el índice y su recuperador devuelve 2 de ellos entre los 5 primeros resultados, recall@5 es 2/3 = 0.67. Una exhaustividad alta es importante cuando el LLM necesita varias evidencias para sintetizar una respuesta completa: omitir incluso un fragmento clave puede hacer que la respuesta quede incompleta. Equilibre precisión y exhaustividad ajustando K: un valor de K mayor mejora la exhaustividad, pero reduce la precisión.

def recall_at_k(golden_dataset, retriever, k=5):
    recalls = []
    for item in golden_dataset:
        retrieved = retriever.retrieve(item['question'], top_k=k)
        retrieved_ids = set(r['id'] for r in retrieved[:k])
        relevant_ids = set(item['relevant_chunk_ids'])
        if not relevant_ids:
            continue  # skip items with no annotated relevant chunks
        retrieved_relevant = retrieved_ids & relevant_ids
        recall = len(retrieved_relevant) / len(relevant_ids)
        recalls.append(recall)
    mean_recall = sum(recalls) / len(recalls)
    print(f'Recall@{k}: {mean_recall:.3f}')
    return mean_recall

Comprobación rápida

Compruebe su comprensión de los conceptos de Ingeniería de IA de esta lección.

Resumen de la lección

En esta lección ha aprendido: la tasa de aciertos@K como métrica binaria de presencia; MRR para medir la posición promedio del primer elemento relevante; precision@K para medir la relación señal-ruido de la recuperación; NDCG@K como métrica de referencia para listas clasificadas; y la biblioteca RAGAS para automatizar la evaluación de RAG con métricas de precisión del contexto, exhaustividad, fidelidad y relevancia de la respuesta. A continuación, profundizaremos en las métricas de generación y la medición de la fidelidad.

Preguntas frecuentes

¿La lección «Métricas de recuperación: hit rate, MRR y NDCG» es gratis?

Sí — el texto completo de «Métricas de recuperación: hit rate, MRR y NDCG» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Engineering Academy, actualiza a CoddyKit PRO. El curso de AI Engineering Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Métricas de recuperación: hit rate, MRR y NDCG»?

Creará un conjunto de datos de referencia con consultas y documentos relevantes, y calculará hit rate, reciprocal rank medio y NDCG para medir con qué frecuencia el recuperador encuentra los fragment… Practicas AI Engineering Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Engineering Academy?

No se requiere experiencia previa. AI Engineering Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Métricas de recuperación: hit rate, MRR y NDCG»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Engineering Academy?

Sí. Cada lección de AI Engineering Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Por qué es importante la evaluación en RAG
  2. Métricas de recuperación: hit rate, MRR y NDCG
  3. Métricas de generación: fidelidad y relevancia de la respuesta
  4. Creación de un sistema automatizado de evaluación
← Volver a AI Engineering Academy