0Pricing
AI Engineering Academy · Lekcja

Pomiar wpływu re-rankingu

Wykonaj benchmark przed i po zmianie, porównując wyszukiwanie jednoetapowe z dwuetapowym re-rankingiem oraz mierząc NDCG, MRR i jakość odpowiedzi od początku do końca.

Pomiar wpływu re-rankingu to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 4 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 mierzyć wpływ ponownego szeregowania?

Ponowne szeregowanie zwiększa opóźnienie i koszt potoku. Bez pomiarów nie można stwierdzić, czy dodatkowa złożoność jest tego warta. Benchmarki pozwalają ilościowo określić poprawę jakości wyszukiwania i jakości odpowiedzi od początku do końca, dzięki czemu mogą Państwo podjąć świadomą decyzję. Pokazują również, które typy zapytań odnoszą największe korzyści, umożliwiając stosowanie ponownego szeregowania wybiórczo, zamiast przy każdym żądaniu.

Tworzenie złotego zbioru testowego

Wiarygodny benchmark wymaga złotego zbioru testowego: kolekcji zapytań powiązanych z identyfikatorami dokumentów, o których wiadomo, że są istotne. Należy utworzyć go przez wylosowanie rzeczywistych zapytań użytkowników z dzienników aplikacji, ręczne wskazanie odpowiednich dokumentów lub skorzystanie z adnotacji ekspertów, a następnie uporządkowanie ich w ustrukturyzowanym formacie. Zbiór testowy zawierający 50–200 zapytań wystarcza do większości zastosowań związanych z oceną systemów RAG.

golden_test_set = [
    {
        'query': 'How does pgvector HNSW indexing improve search speed?',
        'relevant_doc_ids': ['doc_042', 'doc_107'],
    },
    {
        'query': 'What is the difference between BM25 and dense retrieval?',
        'relevant_doc_ids': ['doc_015'],
    },
    {
        'query': 'How to implement reciprocal rank fusion in Python?',
        'relevant_doc_ids': ['doc_093', 'doc_094'],
    },
    # ... 47 more entries
]

print(f'Test set size: {len(golden_test_set)} queries')
print(f'Avg relevant docs per query: {sum(len(e["relevant_doc_ids"]) for e in golden_test_set) / len(golden_test_set):.1f}')

Metryki wyszukiwania: NDCG, MRR, hit rate

Do oceny jakości wyszukiwania należy użyć trzech uzupełniających się metryk. Hit Rate at K mierzy, czy co najmniej jeden istotny dokument znajduje się wśród K najwyżej sklasyfikowanych wyników. MRR (Mean Reciprocal Rank) mierzy średnią odwrotność pozycji pierwszego istotnego dokumentu. NDCG at K (Normalized Discounted Cumulative Gain) mierzy jakość szeregowania, przypisując wyższą wagę wyższym pozycjom niż niższym.

def compute_retrieval_metrics(results: list[str], relevant_ids: set, k: int = 5):
    results_at_k = results[:k]
    relevant_found = [r for r in results_at_k if r in relevant_ids]

    # Hit rate
    hit = 1 if relevant_found else 0

    # MRR
    rr = 0
    for i, doc_id in enumerate(results_at_k, start=1):
        if doc_id in relevant_ids:
            rr = 1.0 / i
            break

    # NDCG (binary relevance)
    import math
    dcg = sum(
        1.0 / math.log2(i + 1)
        for i, doc_id in enumerate(results_at_k, start=1)
        if doc_id in relevant_ids
    )
    ideal = sum(1.0 / math.log2(i + 1) for i in range(1, min(len(relevant_ids), k) + 1))
    ndcg = dcg / ideal if ideal > 0 else 0

    return {'hit': hit, 'rr': rr, 'ndcg': ndcg}

Punkt odniesienia: jednofazowe wyszukiwanie gęste

Przed zmierzeniem wpływu ponownego szeregowania należy ustalić punkt odniesienia za pomocą jednofazowego wyszukiwania gęstego. Należy przepuścić każde zapytanie ze zbioru testowego przez retriever typu bi-encoder, zebrać identyfikatory dokumentów w kolejności rankingowej oraz obliczyć średnie wartości NDCG, MRR i hit rate. Ten punkt odniesienia pokazuje, od jakiego poziomu Państwo zaczynają — jeśli bazowa wartość NDCG@5 wynosi już 0,95, ponowne szeregowanie ma niewielkie możliwości poprawy wyniku.

def evaluate_pipeline(retriever_fn, test_set: list[dict], k: int = 5) -> dict:
    all_metrics = []

    for entry in test_set:
        query = entry['query']
        relevant = set(entry['relevant_doc_ids'])

        results = retriever_fn(query, top_k=k)
        result_ids = [r['id'] for r in results]

        metrics = compute_retrieval_metrics(result_ids, relevant, k)
        all_metrics.append(metrics)

    n = len(all_metrics)
    return {
        f'hit_rate@{k}': sum(m['hit'] for m in all_metrics) / n,
        f'mrr@{k}': sum(m['rr'] for m in all_metrics) / n,
        f'ndcg@{k}': sum(m['ndcg'] for m in all_metrics) / n,
    }

Uruchamianie benchmarku przed i po zmianie

Należy uruchomić tę samą funkcję oceny zarówno dla retrievera jednofazowego, jak i dla retrievera dwufazowego z ponownym szeregowaniem. Wyniki należy wyświetlić obok siebie, aby poprawa — lub jej brak — była od razu widoczna. Oprócz metryk jakości należy śledzić opóźnienie dla każdego zapytania — poprawa jakości wyszukiwania o 5 procent może nie być warta zwiększenia opóźnienia o 400 ms, zależnie od wymagań SLA aplikacji.

import time

def evaluate_with_latency(retriever_fn, test_set, k=5):
    metrics_list = []
    latencies = []

    for entry in test_set:
        t0 = time.perf_counter()
        results = retriever_fn(entry['query'], top_k=k)
        latencies.append((time.perf_counter() - t0) * 1000)

        result_ids = [r['id'] for r in results]
        metrics_list.append(compute_retrieval_metrics(
            result_ids, set(entry['relevant_doc_ids']), k
        ))

    n = len(metrics_list)
    return {
        f'hit_rate@{k}': sum(m['hit'] for m in metrics_list) / n,
        f'ndcg@{k}': sum(m['ndcg'] for m in metrics_list) / n,
        'p50_latency_ms': sorted(latencies)[n // 2],
        'p99_latency_ms': sorted(latencies)[int(n * 0.99)],
    }

baseline = evaluate_with_latency(dense_retrieval_fn, golden_test_set)
two_stage = evaluate_with_latency(two_stage_fn, golden_test_set)
print('Baseline:', baseline)
print('Two-stage:', two_stage)

Interpretowanie poprawy NDCG

Typowa poprawa po dodaniu ponownego szeregowania za pomocą cross-encodera do wyszukiwania gęstego wynosi od 0,05 do 0,15 bezwzględnej wartości NDCG@5, co przekłada się na około 5–15 punktów procentowych. Poprawa jest większa, gdy: (1) zapytania są zróżnicowane i zawierają wiele parafraz, (2) korpus zawiera wiele fragmentów niemal istotnych lub (3) retriever pierwszej fazy jest słaby. Jeśli poprawa NDCG wynosi mniej niż 0,02, korzyść może nie uzasadniać dodatkowej złożoności.

# Interpreting benchmark results

example_results = {
    'baseline': {'hit_rate@5': 0.78, 'ndcg@5': 0.64, 'p99_latency_ms': 35},
    'two_stage': {'hit_rate@5': 0.89, 'ndcg@5': 0.77, 'p99_latency_ms': 287},
}

delta_ndcg = example_results['two_stage']['ndcg@5'] - example_results['baseline']['ndcg@5']
delta_latency = example_results['two_stage']['p99_latency_ms'] - example_results['baseline']['p99_latency_ms']

print(f'NDCG improvement: +{delta_ndcg:.2f} (+{delta_ndcg/example_results["baseline"]["ndcg@5"]*100:.0f}%)')
print(f'Latency increase: +{delta_latency}ms')
# NDCG improvement: +0.13 (+20%) — clearly worth the 252ms latency cost

Pomiar jakości odpowiedzi od początku do końca

Metryki wyszukiwania mierzą, czy pobrano właściwe dokumenty, ale ostateczną miarą jest jakość odpowiedzi od początku do końca. Należy użyć sędziego LLM, aby ocenić, czy odpowiedzi wygenerowane na podstawie kontekstu po ponownym szeregowaniu są bardziej poprawne i wierne niż odpowiedzi oparte na kontekście jednofazowym. Ocenę należy przeprowadzić w skali od 1 do 5 dla poprawności, wierności i trafności, a następnie uśrednić wyniki dla całego zbioru testowego.

from openai import OpenAI

client = OpenAI()

JUDGE_PROMPT = '''
Rate the following answer on a scale of 1-5 for correctness and faithfulness to the context.

Question: {question}
Context: {context}
Answer: {answer}
Ground truth: {ground_truth}

Return a JSON with fields: {"correctness": int, "faithfulness": int, "explanation": str}
'''

def judge_answer(question, context, answer, ground_truth):
    prompt = JUDGE_PROMPT.format(
        question=question, context=context,
        answer=answer, ground_truth=ground_truth,
    )
    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[{'role': 'user', 'content': prompt}],
        response_format={'type': 'json_object'},
    )
    import json
    return json.loads(response.choices[0].message.content)

Analiza warstwowa według typu zapytania

Średnie metryki ukrywają istotne różnice między typami zapytań. Należy podzielić zbiór testowy na kategorie — zapytania faktograficzne (kto, co, kiedy), zapytania proceduralne (jak coś zrobić), zapytania konceptualne (dlaczego, wyjaśnij) oraz zapytania techniczne (kody błędów, nazwy API) — i obliczyć metryki osobno dla każdej grupy. Ponowne szeregowanie często najbardziej pomaga w przypadku zapytań konceptualnych i proceduralnych, gdzie rozumienie semantyki ma większe znaczenie niż dopasowanie słów kluczowych.

def stratified_eval(retriever_fn, test_set, k=5):
    groups = {'factual': [], 'procedural': [], 'conceptual': [], 'technical': []}

    for entry in test_set:
        q = entry['query'].lower()
        if any(w in q for w in ['how to', 'how do', 'steps to']):
            groups['procedural'].append(entry)
        elif any(w in q for w in ['why', 'explain', 'what is the reason']):
            groups['conceptual'].append(entry)
        elif any(c.isupper() for c in q.split()) or 'error' in q:
            groups['technical'].append(entry)
        else:
            groups['factual'].append(entry)

    for group_name, group_entries in groups.items():
        if group_entries:
            metrics = evaluate_pipeline(retriever_fn, group_entries, k)
            print(f'{group_name} ({len(group_entries)} queries): ndcg@{k}={metrics[f"ndcg@{k}"]:.3f}')

Testy regresji z integracją CI

Benchmark wyszukiwania należy uruchamiać jako test regresji w CI. Należy ustawić minimalne akceptowalne wartości progowe dla NDCG@5, MRR i hit rate. Każda zmiana potoku, która powoduje spadek metryk poniżej wartości progowej, kończy się niepowodzeniem kompilacji CI, zapobiegając wdrożeniu regresji jakości wyszukiwania na produkcję. Jest to szczególnie ważne po zmianie rozmiarów fragmentów, modeli embeddingów lub modeli ponownego szeregowania.

# pytest integration for retrieval quality gates
import pytest

MIN_NDCG_5 = 0.70
MIN_HIT_RATE_5 = 0.85

def test_retrieval_quality_meets_threshold():
    metrics = evaluate_pipeline(production_retriever_fn, golden_test_set, k=5)
    assert metrics['ndcg@5'] >= MIN_NDCG_5, (
        f'NDCG@5 {metrics["ndcg@5"]:.3f} below threshold {MIN_NDCG_5}'
    )
    assert metrics['hit_rate@5'] >= MIN_HIT_RATE_5, (
        f'Hit rate {metrics["hit_rate@5"]:.3f} below threshold {MIN_HIT_RATE_5}'
    )

# Run with: pytest tests/test_retrieval.py -v

Wizualizacja metryk wyszukiwania

Surowe liczby trudno interpretować w przypadku wielu eksperymentów. Należy utworzyć prostą tabelę porównawczą lub wykres słupkowy przedstawiający obok siebie wartości NDCG, MRR, hit rate i opóźnienia dla potoków bazowego, wyłącznie hybrydowego oraz hybrydowego z ponownym szeregowaniem. Śledzenie tych metryk w czasie, podczas wprowadzania ulepszeń, tworzy historię poprawy wyszukiwania, która pomaga podejmować przyszłe decyzje dotyczące optymalizacji.

def print_comparison_table(results: dict[str, dict]):
    headers = ['Pipeline', 'NDCG@5', 'MRR@5', 'Hit@5', 'P99 ms']
    print('|'.join(f'{h:20}' for h in headers))
    print('-' * (len(headers) * 21))
    for pipeline_name, metrics in results.items():
        row = [
            pipeline_name,
            f'{metrics.get("ndcg@5", 0):.3f}',
            f'{metrics.get("mrr@5", 0):.3f}',
            f'{metrics.get("hit_rate@5", 0):.3f}',
            f'{metrics.get("p99_latency_ms", 0):.0f}',
        ]
        print('|'.join(f'{v:20}' for v in row))

results = {
    'Dense only': {'ndcg@5': 0.64, 'mrr@5': 0.68, 'hit_rate@5': 0.78, 'p99_latency_ms': 35},
    'Hybrid RRF': {'ndcg@5': 0.71, 'mrr@5': 0.74, 'hit_rate@5': 0.84, 'p99_latency_ms': 55},
    'Hybrid + Rerank': {'ndcg@5': 0.77, 'mrr@5': 0.81, 'hit_rate@5': 0.89, 'p99_latency_ms': 287},
}
print_comparison_table(results)

Wykorzystanie wyników benchmarku

Po uruchomieniu benchmarków należy wykorzystać wyniki do podjęcia konkretnych decyzji. Jeśli ponowne szeregowanie poprawia NDCG o mniej niż 0,03, należy z niego zrezygnować i skupić się na ulepszeniu pierwszej fazy. Jeśli hit rate jest niski, pierwsza faza pomija istotne dokumenty — należy zwiększyć rozmiar zbioru kandydatów lub przełączyć się na wyszukiwanie hybrydowe. Jeśli jakość odpowiedzi od początku do końca znacznie się poprawia pomimo niewielkiej poprawy wyników wyszukiwania, model ponownego szeregowania może wydobywać bardzo istotne zdania, które LLM skutecznie wykorzystuje, nawet jeśli pozycja w rankingu pozostaje niezmieniona.

Szybkie sprawdzenie

Sprawdź swoją wiedzę na temat mierzenia wpływu wyszukiwania i ponownego szeregowania z tej lekcji.

Podsumowanie lekcji

W tej lekcji nauczyli się Państwo, że utworzenie złotego zbioru testowego ze znanymi istotnymi dokumentami jest niezbędne przed pomiarem jakości wyszukiwania, NDCG, MRR i hit rate to trzy podstawowe metryki wyszukiwania, które razem kompleksowo mierzą jakość szeregowania, a jakość odpowiedzi od początku do końca oceniana przez sędziego LLM stanowi ostateczną miarę poprawy potoku. Benchmarki wyszukiwania należy zawsze uruchamiać jako testy regresji w CI. Następnie omówimy strumieniowanie LLM, aby wyświetlać tokeny w miarę ich generowania.

Często zadawane pytania

Czy lekcja „Pomiar wpływu re-rankingu” jest bezpłatna?

Tak — pełny tekst „Pomiar wpływu re-rankingu” 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 „Pomiar wpływu re-rankingu”?

Wykonaj benchmark przed i po zmianie, porównując wyszukiwanie jednoetapowe z dwuetapowym re-rankingiem oraz mierząc NDCG, MRR i jakość odpowiedzi od początku do końca. Ć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 4 z 4.

Ile czasu zajmuje lekcja „Pomiar wpływu re-rankingu”?

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 wyszukiwanie dwuetapowe działa
  2. Re-ranking za pomocą cross-encodera z Cohere i BGE
  3. Kompresja kontekstowa i filtrowanie trafności
  4. Pomiar wpływu re-rankingu
← Powrót do AI Engineering Academy