AI Engineering Academy · Lekcja

Metryki pobierania: hit rate, MRR i NDCG

Uczestnicy zbudują wzorcowy zbiór zapytań i odpowiednich dokumentów, a następnie obliczą hit rate, mean reciprocal rank i NDCG, aby zmierzyć, jak często mechanizm pobierania znajduje właściwe fragmenty.

Lekcja 2 z 413 kroki

Metryki pobierania: hit rate, MRR i NDCG to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Dlaczego istnieją różne metryki wyszukiwania?

Współczynnik trafień informuje, czy powiązany fragment pojawił się gdziekolwiek wśród K najważniejszych wyników, ale nie mówi, gdzie w rankingu się znalazł. System, który zawsze umieszcza najlepszy fragment na 5. miejscu, jest gorszy od systemu, który konsekwentnie umieszcza go na 1. miejscu, nawet jeśli oba mają taki sam współczynnik trafień. Bardziej zaawansowane metryki, takie jak MRR i NDCG, uwzględniają jakość rankingu, nagradzając systemy umieszczające najbardziej trafne fragmenty na początku, gdzie LLM i użytkownicy najprawdopodobniej z nich skorzystają.

Współczynnik trafień @K: przegląd i implementacja

Współczynnik trafień@K to najprostsza metryka: dla jakiego odsetka zapytań co najmniej jeden powiązany fragment pojawia się wśród K najważniejszych wyników? Daje ona binarny sygnał dla każdego zapytania i jest łatwa do interpretacji. Należy ją obliczyć, sprawdzając przecięcie zbiorów identyfikatorów pobranych fragmentów i znanych powiązanych identyfikatorów. Jako wartość domyślną należy przyjąć K=5, ponieważ większość systemów RAG pobiera 5 fragmentów. Warto porównać hit rate@1, @3 i @5, aby zrozumieć, jak zmienia się czułość wraz z poszerzaniem okna wyszukiwania.

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%}')

Średnia odwrotna pozycja (MRR)

MRR (Mean Reciprocal Rank) mierzy średnią odwrotność pozycji, na której pojawia się pierwszy powiązany fragment. Jeśli powiązany fragment znajduje się na 1. pozycji, odwrotność pozycji wynosi 1/1 = 1.0. Na 2. pozycji wynosi 0.5, a na 5. pozycji — 0.2. MRR jest uśredniane dla wszystkich zapytań. Wyższa wartość MRR oznacza, że mechanizm wyszukiwania konsekwentnie umieszcza powiązane fragmenty wysoko w rankingu, co jest istotne, ponieważ LLM zwraca większą uwagę na kontekst umieszczony wcześniej w prompcie.

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

Interpretacja wartości MRR

Wartości MRR mają intuicyjną interpretację: MRR = 1.0 oznacza, że pierwszy powiązany fragment zawsze znajduje się na 1. pozycji (wynik idealny). MRR = 0.5 oznacza, że zwykle znajduje się na 2. pozycji. MRR = 0.25 oznacza, że zwykle znajduje się na 4. pozycji — powiązane informacje pojawiają się na tyle daleko w rankingu, że mogą zostać obcięte z kontekstu. W systemach RAG należy dążyć do MRR > 0.7, aby zagwarantować, że najbardziej trafny fragment będzie konsekwentnie znajdował się wśród dwóch pierwszych pozycji.

# 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}')

Precyzja @K

Precision@K mierzy, jaki odsetek spośród K pobranych fragmentów jest rzeczywiście powiązany z zapytaniem. W przeciwieństwie do współczynnika trafień, który ma charakter binarny, precision@K mierzy stosunek sygnału do szumu w wynikach wyszukiwania. Niska precyzja oznacza, że LLM otrzymuje nieistotny kontekst wraz z istotnymi fragmentami, co zwiększa ryzyko pomyłek lub wstrzyknięcia promptu. W systemach RAG zdrowym celem jest 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

Zdyskontowany skumulowany zysk (DCG)

DCG to metryka oceny listy rankingowej, która nagradza umieszczanie bardziej trafnych fragmentów wyżej. Sumuje ona wartości trafności pobranych fragmentów, ale stosuje logarytmiczne pomniejszenie zależne od pozycji: 1. pozycja otrzymuje pełną wartość, 2. pozycja — pomniejszenie log(2), i tak dalej. Umieszczenie bardziej trafnych fragmentów na początku daje wyższą wartość DCG. Wersja znormalizowana (NDCG) dzieli wynik przez idealną wartość DCG (najlepszy możliwy ranking), uzyskując wynik od 0 do 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

Obliczanie NDCG dla całego zbioru danych

NDCG@K to złoty standard metryk wyszukiwania w systemach wyszukiwawczych. Jednocześnie uwzględnia trafność i pozycję w rankingu. NDCG@5 równe 0.85 oznacza, że mechanizm wyszukiwania osiąga średnio 85% teoretycznie najlepszego rankingu. NDCG obsługuje przypadki, w których dla jednego zapytania istnieje wiele powiązanych fragmentów (każdy otrzymuje ocenę trafności), i nakłada karę na systemy, które znajdują powiązane fragmenty, ale umieszczają je zbyt nisko w rankingu.

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)

Używanie RAGAS do automatycznych metryk

Biblioteka RAGAS zapewnia gotowy do użycia w środowisku produkcyjnym framework do oceny systemów RAG. Implementuje metryki precyzji kontekstu, kompletności kontekstu, wierności i trafności odpowiedzi, korzystając z podejścia LLM-as-judge. Należy przekazać do RAGAS pytania, odpowiedzi, pobrane konteksty i odpowiedzi wzorcowe, aby otrzymać pełny raport oceny z wynikami dla każdej metryki. To najszybszy sposób na skonfigurowanie kompleksowego potoku oceny 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)

Dzielenie metryk według kategorii zapytań

Średnie metryki dla całego zbioru ukrywają istotne wzorce. Należy podzielić złoty zbiór danych na kategorie — zapytania o fakty, porównania, proceduralne pytania typu „jak to zrobić” i pytania wykraczające poza zakres — oraz obliczyć metryki osobno dla każdej z nich. Mogą Państwo odkryć, że współczynnik trafień wynosi 95% dla zapytań o fakty, ale tylko 60% dla porównań wymagających przeskakiwania między wieloma fragmentami. Metryki na poziomie kategorii ujawniają konkretne tryby awarii, które ukrywają zagregowane wyniki.

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)})')

Gdy metryki się nie zgadzają

Czasami metryki wysyłają sprzeczne sygnały. Mogą Państwo poprawić NDCG (lepszy ranking), podczas gdy współczynnik trafień pozostanie bez zmian (ta sama liczba nietrafionych zapytań). Dzieje się tak, gdy optymalizacja przesuwa powiązane fragmenty z 6. na 2. pozycję, ale nie przenosi wcześniej pominiętych zapytań do pierwszej piątki. W takich przypadkach należy sprawdzić, czy optymalizacja pomogła w przypadku zapytań, które już działały poprawnie, a jednocześnie pominęła te, które kończyły się niepowodzeniem. Oprócz zagregowanych metryk należy zawsze analizować przykłady pojedynczych nieudanych przypadków.

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

Kompletność wyszukiwania: Recall @K

Recall@K mierzy, jaki odsetek wszystkich powiązanych fragmentów został pobrany wśród K najważniejszych wyników. Jeśli pytanie ma 3 powiązane fragmenty w indeksie, a mechanizm wyszukiwania zwróci 2 z nich w pierwszej piątce, recall@5 wynosi 2/3 = 0.67. Wysoki poziom recall ma znaczenie, gdy LLM potrzebuje wielu dowodów, aby utworzyć kompletną odpowiedź — pominięcie choćby jednego kluczowego fragmentu może sprawić, że odpowiedź będzie niepełna. Równowagę między precision i recall można uzyskać, dostrajając wartość K: większe K poprawia recall, ale obniża precision.

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

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat koncepcji inżynierii AI z tej lekcji.

Podsumowanie lekcji

W tej lekcji poznali Państwo: hit rate@K jako metrykę binarnej obecności, MRR do mierzenia średniej pozycji pierwszego powiązanego fragmentu, precision@K do mierzenia stosunku sygnału do szumu w wyszukiwaniu, NDCG@K jako złoty standard metryk rankingowych oraz bibliotekę RAGAS do automatyzacji oceny RAG za pomocą metryk precyzji kontekstu, kompletności kontekstu, wierności i trafności odpowiedzi. W następnej części zagłębimy się w metryki generowania i pomiar wierności.

Bezpłatny start

Ucz się Python dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
30
Lekcje
120

Często zadawane pytania

Czy lekcja „Metryki pobierania: hit rate, MRR i NDCG” jest bezpłatna?

Tak — pełny tekst „Metryki pobierania: hit rate, MRR i NDCG” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Metryki pobierania: hit rate, MRR i NDCG”?

Uczestnicy zbudują wzorcowy zbiór zapytań i odpowiednich dokumentów, a następnie obliczą hit rate, mean reciprocal rank i NDCG, aby zmierzyć, jak często mechanizm pobierania znajduje właściwe fragmen… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?

Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Metryki pobierania: hit rate, MRR i NDCG”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?

Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego ewaluacja ma znaczenie w RAG
  2. Metryki pobierania: hit rate, MRR i NDCG
  3. Metryki generowania: wierność i trafność odpowiedzi
  4. Tworzenie automatycznego środowiska ewaluacyjnego
← Powrót do AI Engineering Academy