Stos rankingu wyszukiwania: BM25, embeddings i reranking

Tłumaczenie automatyczne Ten artykuł został automatycznie przetłumaczony z angielskiego oryginału.

Wyszukiwanie musi uwzględniać zarówno intencję dokładną, jak i semantyczną. Zapytanie „wireless headphones” powinno dopasowywać te słowa, ale ostateczna kolejność może również zależeć od jakości produktu, preferencji użytkownika i dostępności. Żadna pojedyncza metoda rankingu nie radzi sobie dobrze ze wszystkimi tymi sygnałami.

W tym artykule budujemy stos etapami: wyszukiwanie BM25, gęste embeddings, Reciprocal Rank Fusion, reranking cross-encoderem i na końcu listwise ranking z użyciem LLM. Repozytorium demonstracyjne zawiera działający kod dla poszczególnych etapów na próbkowanym wycinku danych wyszukiwania produktów Amazon ESCI.

Krótki przewodnik po wyborze etapów znajdziesz w sekcji BM25 vs Embeddings vs Rerankers.


Dobieraj etapy do rodzaju błędu

Produkcyjny stos ma postać lejka, ale właściwy lejek zależy od zapytania i powierzchni biznesowej.

Przypadek użyciaProponowany stos początkowyCo zweryfikować
Wyszukiwanie produktówBM25 + dense retrieval + RRF + cross-encoderRecall atrybutów, zamienniki, opóźnienie, ograniczenia biznesowe
Wyszukiwanie w dokumentacjiHybrid retrieval + cross-encoderDokładne identyfikatory, pytania semantyczne, filtry wersji
Odciążanie wsparciaHybrid retrieval + kontrole cytowańRecall wyszukiwania, grounding, abstention
Marketplace lub ogłoszeniaFiltry leksykalne + dense retrieval + business rerankerDostępność, aktualność, zgodność z zasadami, różnorodność sprzedawców
Mały korpus wewnętrznyBazowy BM25, następnie rerankerCzy rozbieżność słownictwa uzasadnia indeks gęsty
Wyszukiwanie prawne lub medyczne o wysokiej stawceRetrieval skupiony na recallu plus weryfikacja eksperckaPokrycie, proweniencja, skalibrowane abstention

Zacznij od BM25 jako baseline’u. Dodaj dense retrieval, gdy rozbieżność słownictwa obniża recall. Dodaj cross-encoder, gdy na pierwszej stronie znajdują się właściwi kandydaci, ale w złej kolejności. Dodaj LLM dopiero wtedy, gdy możesz zaakceptować opóźnienie i potrafisz ewaluować decyzje rankingowe.

Jak do tego doszliśmy

Stos łatwiej zrozumieć jako trzy warstwy. Retrieval leksykalny wyszukuje dokładne terminy, dense retrieval niweluje luki w słownictwie, a rerankery szczegółowo porównują najlepszych kandydatów.

BM25 i retrieval leksykalny

Przez dekady domyślną metodą był BM25. To model probabilistyczny, który ocenia dokumenty na podstawie częstości terminów zapytania w dokumencie, z normalizacją względem długości dokumentu i inverse document frequency (IDF).

BM25 jest skuteczny, gdy intencję wyrażają dosłowne terminy: kody błędów, SKU produktów, nazwy i identyfikatory API. Jego głównym ograniczeniem jest rozbieżność słownictwa. Zapytanie „cheap laptop” może nie znaleźć dokumentu o „budget notebook computer”, jeśli zaindeksowany tekst nie zapewnia przejścia między tymi wyrażeniami.

Mimo to BM25 stanowi solidny baseline. Ranking BEIR podaje średnią wartość nDCG@10 równą 0.429 dla 18 zbiorów danych dla swojego uruchomienia BM25 multifield. Reprodukcja Pyserini wykorzystuje wielopolowy indeks Lucene z contents=1.0 i title=1.0, przeszukiwany za pomocą --bm25. Nie jest to implementacja rank_bm25 z tego dema, oparta na zwykłej tokenizacji białymi znakami. BM25 również przewyższa niektóre modele neuronowe w zadaniach retrieval argumentacyjnego, takich jak Touche-2020.

Dense retrieval i embeddings

Enkodery w stylu BERT uczyniły dense retrieval praktycznym. Mapują zapytania i dokumenty do wspólnej przestrzeni wektorowej, a następnie szeregują kandydatów za pomocą funkcji podobieństwa, takiej jak podobieństwo cosinusowe lub iloczyn skalarny.

Architektura bi-encodera (lub „two-tower”) przetwarza zapytanie i dokument niezależnie przez osobne wieże enkodera, tworząc embeddings o stałej długości. Wektory dokumentów można obliczyć z wyprzedzeniem i zaindeksować offline, a następnie szybko wyszukiwać za pomocą algorytmów Approximate Nearest Neighbor (ANN). Dzięki temu „cheap laptop” i „budget notebook” trafiają blisko siebie w przestrzeni wektorowej.

Niektóre bi-encodery wykorzystują architekturę Siamese, jak w Sentence-BERT, gdzie obie strony współdzielą wagi. Inne używają osobnych wież zapytania i dokumentu. Pooling, rozmiar wektora, funkcja podobieństwa i cel treningowy są wyborami dotyczącymi modelu, a nie właściwościami każdego dense retrievera.

Modele te są trenowane za pomocą contrastive learning, zwykle z funkcją straty InfoNCE. Dla batcha par (query, positive_document) cel maksymalizuje sim(query, positive_doc), jednocześnie minimalizując sim(query, negative_docs). Negatywy pochodzą z pozytywów innych zapytań w tym samym batchu (in-batch negatives). Parametr temperatury τ\tau określa, jak zdecydowanie model musi rozdzielić te dwa przypadki.

Dane treningowe często mają większe znaczenie niż wymiar embeddings. Modele retrieval uczą się na parach query-positive oraz starannie dobranych hard negatives: wiarygodnych, lecz nieistotnych dokumentach. W dalszej części dotyczącej treningu pokazujemy, jak SimANS unika zarówno trywialnych negatywów, jak i prawdopodobnych false negatives.

Kosztem jest wąskie gardło reprezentacji. Bi-encodery kompresują całą semantyczną subtelność do pojedynczego wektora o stałym rozmiarze, dlatego często pomijają drobnoziarniste interakcje między konkretnymi terminami zapytania a konkretną treścią dokumentu.

Cross-encodery i LLM

Cross-encodery (Nogueira & Cho, 2019) przekazują zapytanie i dokument razem do Transformera jako połączoną sekwencję ([CLS] Query [SEP] Document), dzięki czemu każdy token zapytania może kierować uwagę na każdy token dokumentu przez pełną self-attention. Ta głęboka interakcja wychwytuje niuanse pomijane przez niezależne kodowanie.

LLM reranking wykorzystuje model sterowany promptem do jednoczesnego porównania kilku kandydatów. RankGPT wykazał dobre wyniki zero-shot listwise z GPT-4 na ocenianych benchmarkach, ale stabilność wyjścia, koszt i dopasowanie do domeny nadal wymagają osobnych testów.

Tych wyników nie można obliczyć z wyprzedzeniem dla dowolnych zapytań, dlatego reranking umieszcza się po etapie retrieval. Ta asymetria kosztów uzasadnia wieloetapowy lejek.


Wieloetapowy lejek

Uruchamianie kosztownego cross-encodera lub LLM dla milionów dokumentów nie jest wykonalne, dlatego współczesne stosy wyszukiwania używają lejka. Każdy etap zmniejsza pulę kandydatów, podczas gdy rośnie złożoność modelu.

Tani retriever nie zapewnia również końcowej precyzji, dlatego lejek wykorzystuje każdy model tylko tam, gdzie jego koszt jest uzasadniony.

Wieloetapowy lejek rankinguWieloetapowy lejek rankingu

EtapSkala wejściaGłówny celTypowe metodyPomiar wyjściowy
RetrievalKorpus lub indeksRecall kandydatówBM25, bi-encoderyRecall przy odcięciu kandydatów
Pre-rankingDuży zbiór kandydatówTanie filtrowanieLekkie modele, regułyZachowany recall na milisekundę
Full rankingKrótka listaJakość czołówkiCross-encodery, LLMNDCG/MRR, opóźnienie, koszt
BlendingKońcowe listy rankingowe lub slotyOgraniczenia i miksReguły, ranking wielokryterialnyPolityka, różnorodność, guardrails biznesowe

Retrieval wyznacza górną granicę, a reranking optymalizuje wynik w jej obrębie. Jeśli istotny dokument nie przejdzie etapu retrieval, żaden późniejszy model nie będzie w stanie go odzyskać.


Demo: pipeline z pięciu etapów

Aby to zilustrować, zbudowałem demo search-ranking-stack, które uruchamia pięcioetapowy pipeline na benchmarku wyszukiwania produktów Amazon ESCI. Każdy etap jest mierzony niezależnie, dzięki czemu można zobaczyć, skąd rzeczywiście biorą się przyrosty jakości.

Architektura pipeline’u demonstracyjnegoArchitektura pipeline’u demonstracyjnego

Pipeline:

  1. Rzadki retrieval BM25 — baseline leksykalny (rank_bm25)
  2. Retrieval dense bi-encoderem — semantyczne generowanie kandydatów (all-MiniLM-L6-v2)
  3. Hybrydowa fuzja RRF — fuzja wyników rzadkich i gęstych na podstawie pozycji
  4. Reranking cross-encoderem — parowe oceny trafności (ms-marco-MiniLM-L-12-v2)
  5. Listwise reranking z użyciem LLM — porównanie shortlisty za pomocą promptu (Ollama, API lub model lokalny)

Kroki 1–3 to etap retrieval lejka (maksymalizacja recallu); kroki 4–5 to etap full ranking (maksymalizacja precision). Demo pomija pre-ranking i blending. Przy około 8500 dokumentach można przekazywać wszystkie wyniki hybrydowe bezpośrednio do rerankingu.

Szybki start

git clone https://github.com/slavadubrov/search-ranking-stack.git
cd search-ranking-stack
uv sync

# Download and sample ESCI dataset (~2.5GB download, ~5MB sample)
uv run download-data

# Run the full pipeline (without LLM reranking)
uv run run-all

# Run with LLM reranking via Ollama
uv run run-all --llm-mode ollama

Zbiór danych i próbkowanie: Amazon ESCI

Demo korzysta ze zbioru Amazon Shopping Queries Dataset (ESCI) z KDD Cup 2022 — realistycznego benchmarku wyszukiwania produktów z czteropoziomowymi etykietami stopnia trafności:

EtykietaGainZnaczeniePrzykład (Query: „wireless headphones”)
Exact (E)3Spełnia wszystkie wymagania zapytaniaSony WH-1000XM5 Wireless Headphones
Substitute (S)2Funkcjonalna alternatywaSłuchawki przewodowe z adapterem Bluetooth
Complement (C)1Powiązany, użyteczny produktEtui na słuchawki
Irrelevant (I)0Brak istotnej relacjiKabel do ładowania USB

Stopniowana trafność jest istotna, ponieważ pozwala używać NDCG (Normalized Discounted Cumulative Gain), które odróżnia ranking „idealny” od „wystarczającego”. Metryki binarne oceniłyby oba tak samo.

Użyłem próbki small_version z dema: około 500 zapytań, 8500 produktów i 12 000 ocen. Downloader odczytuje pojedynczy split train z tasksource/esci, filtruje angielski locale us i small_version == 1, a następnie za pomocą seeda 42 losuje bez powtórzeń do 500 unikalnych identyfikatorów zapytań. Korpus zawiera unikalne produkty z wybranych wierszy ocen. Nie jest to split held-out ani niezależny korpus. Próbka jest wystarczająco mała, by uruchomić ją na laptopie, ale zbyt mała i zbyt specyficzna domenowo, by uzasadniać produkcyjny ranking. Użyj jej do odtworzenia etapów i analizy trybów błędów; decyzje wdrożeniowe podejmuj na podstawie reprezentatywnych zapytań held-out.


Retrieval: wyszukiwanie hybrydowe

Zadaniem warstwy retrieval jest maksymalizacja recallu: wprowadzenie do zbioru kandydatów jak największej liczby istotnych dokumentów.

BM25: baseline leksykalny

BM25 ocenia dokumenty na podstawie nakładania się terminów z zapytaniem, z nasyceniem częstości terminów i normalizacją długości dokumentu:

BM25(q,d)=tqIDF(t)tf(t,d)(k1+1)tf(t,d)+k1(1b+bd/avgdl)\text{BM25}(q, d) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{tf(t,d) \cdot (k_1 + 1)}{tf(t,d) + k_1 \cdot (1 - b + b \cdot |d|/\text{avgdl})}

Gdzie IDF(t)\text{IDF}(t) to inverse document frequency terminu tt, tf(t,d)tf(t,d) to częstość terminu w dokumencie dd, d|d| to długość dokumentu, a avgdl\text{avgdl} to średnia długość dokumentu w korpusie. Znaczenie mają dwa parametry: k1k_1 (zwykle 1,2–2,0) steruje nasyceniem TF — tym, jak szybko kolejne powtórzenia terminu przestają wnosić wartość — a bb (zwykle 0,75) steruje normalizacją długości dokumentu.

Implementacja jest krótka. Zwykła tokenizacja białymi znakami z rank_bm25:

# src/search_ranking_stack/stages/s01_bm25.py

from rank_bm25 import BM25Okapi

def run_bm25(data: ESCIData, top_k: int = 100):
    doc_ids = list(data.corpus.keys())
    tokenized_corpus = [text.lower().split() for text in data.corpus.values()]

    bm25 = BM25Okapi(tokenized_corpus)

    results = {}
    for query_id, query_text in data.queries.items():
        scores = bm25.get_scores(query_text.lower().split())
        top_indices = np.argsort(scores)[::-1][:top_k]
        results[query_id] = {doc_ids[idx]: float(scores[idx]) for idx in top_indices}

    return results

BM25 osiąga Recall@100 równy 0,741 — 74% istotnych produktów pojawia się gdzieś w pierwszej setce. To dobry wynik jak na metodę czysto leksykalną, ale 26% istotnych elementów pozostaje niewidocznych dla wszystkich kolejnych etapów.

Retrieval dense bi-encoderem

Bi-encoder niezależnie mapuje zapytania i dokumenty do wspólnej przestrzeni embeddings:

# src/search_ranking_stack/stages/s02_dense.py

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

# Encode corpus once, cache to disk
corpus_embeddings = model.encode(
    doc_texts,
    batch_size=128,
    normalize_embeddings=True,  # Cosine sim = dot product
    convert_to_numpy=True,
)

# At query time: encode query, compute dot product
query_embeddings = model.encode(query_texts, normalize_embeddings=True)
similarity_matrix = np.dot(query_embeddings, corpus_embeddings.T)

Dla znormalizowanych embeddings podobieństwo cosinusowe redukuje się do iloczynu skalarnego. Demo oblicza pełną macierz query-by-corpus, ponieważ 8500 dokumentów mieści się bez problemu w pamięci; produkcyjny korpus zwykle korzystałby z indeksu approximate-nearest-neighbor. W tej próbce all-MiniLM-L6-v2 zwiększa Recall@100 z 0,741 do 0,825.

Jak bi-encodery uczą się dobrych reprezentacji

Trening bi-encodera zwykle przebiega w dwóch fazach. Najpierw model jest pretrenowany na zbiorach Natural Language Inference (NLI) i Semantic Textual Similarity (STS), które uczą go ogólnego rozumienia semantycznego. Na tym etapie model uczy się, że „a cat sits on a mat” i „a feline rests on a rug” powinny mieć podobne embeddings. Następnie jest dostrajany na danych specyficznych dla retrieval, takich jak MS MARCO, gdzie uczy się, że zapytanie wyszukiwawcze i odpowiedni fragment powinny znajdować się bliżej siebie niż zapytanie i nieistotne fragmenty.

Druga faza zależy od hard negative mining. Losowe negatywy, na przykład dokument o gotowaniu sparowany z zapytaniem o słuchawki, są trywialnie łatwe do rozróżnienia, więc model niewiele się z nich uczy. Zamiast tego używa się bieżącego modelu do znalezienia dokumentów, które szereguje wysoko, ale które w rzeczywistości nie są istotne.

Podejście SimANS (Simple Ambiguous Negatives Sampling) formalizuje ten proces. Najpierw szeregujemy wszystkie dokumenty za pomocą bieżącego bi-encodera, a następnie wykluczamy łatwe negatywy, które są zbyt nisko w rankingu, by model mógł się z nich uczyć, oraz potencjalne false negatives, które są tak wysoko, że mogą być istotne, lecz nie zostały oznaczone. Pozostałe dokumenty ze środkowego zakresu niosą najwięcej sygnału treningowego.

# What a training triplet looks like after hard negative mining
training_triplet = {
    "query": "wireless noise canceling headphones",
    "positive": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
    "negative": "Sony headphone replacement ear pads",  # Hard negative: same brand, related product, but wrong intent
}
# The bi-encoder must learn that "ear pads" is NOT what the user wants,
# even though it shares many tokens with the positive document.

Funkcja straty kontrastowej (InfoNCE) spina te elementy. Dla każdego zapytania qq z pozytywnym dokumentem d+d^+ i zbiorem dokumentów negatywnych {d1,,dn}\{d^-_1, \ldots, d^-_n\}:

L=logesim(q,d+)/τesim(q,d+)/τ+i=1nesim(q,di)/τ\mathcal{L} = -\log \frac{e^{\text{sim}(q, d^+) / \tau}}{e^{\text{sim}(q, d^+) / \tau} + \sum_{i=1}^{n} e^{\text{sim}(q, d^-_i) / \tau}}

Gdzie sim(q,d)\text{sim}(q, d) to podobieństwo cosinusowe między embeddings zapytania i dokumentu, a τ\tau to parametr temperatury (zwykle 0,05–0,1). Niższe wartości zwiększają wrażliwość straty na hard negatives. To zasadniczo softmax cross-entropy: zwiększ podobieństwo pary pozytywnej względem wszystkich negatywów. Gdy τ\tau jest małe, nawet niewielkie różnice podobieństwa generują duże gradienty, co wymusza na modelu bardziej szczegółowe rozróżnienia.

Pipeline treningu bi-encoderaPipeline treningu bi-encodera

Udostępnianie embeddings bi-encodera na dużą skalę

Architektoniczną zaletą bi-encodera jest podział offline/online. Embeddings dokumentów są obliczane podczas indeksowania i przechowywane w indeksie wektorowym. W czasie zapytania system koduje zapytanie i przeszukuje zapisane wektory. Opóźnienie zależy od enkodera, sprzętu, indeksu, filtrów i docelowego recallu, dlatego oba kroki należy profilować osobno.

W demie obliczenia są umiarkowane: 8500 dokumentów ×\times 384 wymiary ×\times 4 bajty na float = około 13 MB embeddings. Przy skali produkcyjnej liczby przestają być umiarkowane: 1 miliard dokumentów z embeddings o 768 wymiarach wymaga około 3 TiB przestrzeni. Wtedy przydają się kwantyzacja (kompresja 32-bitowych floatów do 8-bitowych liczb całkowitych), product quantization (dekompozycja wektorów na podprzestrzenie) oraz indeksy wspierane przez SSD, takie jak DiskANN. Sekcja indeksowania dense-vector omawia algorytmy indeksowania.

Pipeline udostępniania bi-encoderaPipeline udostępniania bi-encodera

Dlaczego testować retrieval hybrydowy

Obie metody często zawodzą w różny sposób. BM25 dobrze sprawdza się w przypadku nazw własnych, SKU produktów i kodów błędów. Dense retrieval może odzyskać wyniki przy rozbieżności słownictwa, na przykład między „cheap laptop” a „budget notebook computer”. To, czy fuzja pomoże, zależy od tego, jak często takie komplementarne przypadki występują w docelowym zbiorze zapytań.

Typowym kolejnym eksperymentem jest hybrid search: uruchomienie obu metod retrieval, a następnie połączenie ich rankingów.

Reciprocal rank fusion (RRF)

BM25 i dense retrieval generują wyniki o różnych znaczeniach i skalach. Liniowa kombinacja wymaga więc kalibracji i walidacji za każdym razem, gdy zmieniają się retrievery lub korpus.

Wyszukiwanie hybrydowe z RRFWyszukiwanie hybrydowe z RRF

Reciprocal Rank Fusion (Cormack et al., 2009) całkowicie pomija surowe wyniki i wykorzystuje wyłącznie pozycję w rankingu:

RRF(d)=rRankings1k+rank(d,r)\text{RRF}(d) = \sum_{r \in \text{Rankings}} \frac{1}{k + \text{rank}(d, r)}

W tym wzorze kk jest stałą wygładzającą; 60 to często używana wartość początkowa. RRF premiuje elementy zajmujące wysokie pozycje na wielu listach wejściowych, bez porównywania ich surowych wyników. Unika kalibracji skali wyników, ale wartości odcięcia retrieval, wagi i kk nadal wymagają ewaluacji.

Implementacja:

# src/search_ranking_stack/stages/s03_hybrid_rrf.py

def reciprocal_rank_fusion(ranked_lists, k=60, top_k=100):
    fused_results = {}

    for query_id in all_query_ids:
        rrf_scores = defaultdict(float)

        for results in ranked_lists:
            sorted_docs = sorted(results[query_id].items(),
                                 key=lambda x: x[1], reverse=True)

            for rank, (doc_id, _score) in enumerate(sorted_docs, start=1):
                rrf_scores[doc_id] += 1.0 / (k + rank)

        sorted_rrf = sorted(rrf_scores.items(),
                            key=lambda x: x[1], reverse=True)[:top_k]
        fused_results[query_id] = dict(sorted_rrf)

    return fused_results

Hybrid RRF osiąga Recall@100 równy 0,842 oraz NDCG@10 równe 0,628 — więcej niż BM25 (0,585) i Dense (0,611) użyte osobno. Dokument musi zajmować wysoką pozycję tylko w jednej metodzie, aby przetrwać fuzję.


Reranking cross-encoderem

Przy 100 hybrydowych kandydatach na zapytanie można pozwolić sobie na droższy model. Cross-encoder przetwarza zapytanie i dokument razem przez pojedynczy Transformer, z pełnym cross-attention między wszystkimi tokenami.

Bi-encoder a cross-encoderBi-encoder a cross-encoder

Interakcja na poziomie tokenów

Różnica tkwi w macierzy attention. W bi-encoderze attention ma strukturę blokowo-diagonalną: tokeny zapytania zwracają uwagę wyłącznie na inne tokeny zapytania, a tokeny dokumentu wyłącznie na inne tokeny dokumentu. Obie reprezentacje nigdy nie spotykają się na poziomie tokenów — przecinają się dopiero na końcu, przez iloczyn skalarny. Cross-encoder oblicza pełną macierz attention, w której każdy token zapytania zwraca uwagę na każdy token dokumentu i odwrotnie. To cross-attention umożliwia głęboką interakcję na poziomie tokenów.

Architektura attention cross-encoderaArchitektura attention cross-encodera

W bi-encoderze zapytanie „apple” jest kodowane, zanim zobaczony zostanie jakikolwiek dokument. Cross-encoder widzi zapytanie i kandydata razem, więc może wykorzystać ich relację na poziomie tokenów. Może to pomóc w przypadkach takich jak:

  • Negacja: „headphones that are not wireless”. Pooled embedding może nadać negacji zbyt małą wagę, podczas gdy wspólne kodowanie zapewnia modelowi bezpośrednią interakcję zapytania z dokumentem. To hipoteza, którą należy zweryfikować na odpowiednio dobranym wycinku, a nie gwarancja.
  • Ograniczenie: „laptop under $500”. Wspólne kodowanie może powiązać ograniczenie z ceną w tekście produktu, choć bezpieczniejsze są strukturalne filtry ceny, jeśli dane pole jest dostępne.

Wejście cross-encodera ma format [CLS] query tokens [SEP] document tokens [SEP]. [CLS] to token klasyfikacyjny, którego końcowy stan ukryty jest przekazywany przez liniową głowicę w celu wygenerowania pojedynczego wyniku trafności. Embeddings segmentów rozróżniają tokeny zapytania od tokenów dokumentu, a [SEP] oznacza granicę między segmentami.

Jak trenowane są cross-encodery

Cross-encodery mogą uczyć się na przykładach (query, document, relevance_label) z celami pointwise, pairwise lub listwise. Poniższy przykład pointwise używa pojedynczej etykiety trafności; nie jest to jedyny możliwy projekt treningu.

# Cross-encoder training data format
training_example = {
    "query": "wireless headphones",
    "document": "Sony WH-1000XM5 Wireless Headphones",
    "label": 1.0,  # Relevant
}
# Forward pass: [CLS] hidden state → Linear layer → sigmoid → score
# Loss: binary cross-entropy between predicted score and label

Typowy klasyfikator mapuje końcową reprezentację [CLS] na wynik. Etykiety binarne mogą używać binary cross-entropy; stopniowana trafność może wykorzystywać straty regresyjne, porządkowe, pairwise lub listwise. Wybieraj na podstawie metryk rankingowych na zbiorze held-out, zamiast zakładać, że jeden cel jest uniwersalnie lepszy.

Hard negative mining ma jeszcze większe znaczenie w cross-encoderach niż w bi-encoderach. Cross-encodery są kosztowne w treningu — każdy przykład treningowy wymaga pełnego forward pass przez połączoną sekwencję — dlatego nie można marnować obliczeń na trywialnie łatwe negatywy. Praktyczny przepis: użyj bi-encodera do pobrania top-K kandydatów dla każdego zapytania treningowego, a następnie wybierz hard negatives z określonych zakresów pozycji (np. 10–100). Otrzymasz w ten sposób przykłady, w których odróżnienie wyników istotnych od nieistotnych rzeczywiście wymaga głębokiej interakcji na poziomie tokenów.

# src/search_ranking_stack/stages/s04_cross_encoder.py

from sentence_transformers import CrossEncoder

model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-12-v2")

def run_cross_encoder(data, hybrid_results, top_k_rerank=50):
    reranked_results = {}

    for query_id, query_text in data.queries.items():
        candidates = list(hybrid_results[query_id].items())[:top_k_rerank]

        # Form (query, document) pairs for joint encoding
        pairs = []
        doc_ids = []
        for doc_id, _ in candidates:
            doc_text = data.corpus.get(doc_id, "")[:2048]
            pairs.append([query_text, doc_text])
            doc_ids.append(doc_id)

        # Score all pairs with full cross-attention
        scores = model.predict(pairs, batch_size=64)

        # Rerank by cross-encoder score
        scored_docs = sorted(zip(doc_ids, scores),
                             key=lambda x: x[1], reverse=True)
        reranked = {
            doc_id: float(score) for doc_id, score in scored_docs
        }

        # Keep original hybrid candidates at ranks 51--100 for Recall@100.
        for doc_id, score in list(hybrid_results[query_id].items())[top_k_rerank:100]:
            if doc_id not in reranked:
                reranked[doc_id] = float(score) * 0.01

        reranked_results[query_id] = reranked

    return reranked_results

W zarejestrowanym uruchomieniu dema ms-marco-MiniLM-L-12-v2 rerankuje 50 kandydatów na zapytanie i zwiększa NDCG@10 z 0,628 do 0,645. Zmierz opóźnienie na sprzęcie wdrożeniowym; na wynik wpływają model, długość sekwencji, rozmiar batcha i runtime.

Kompromis między szybkością a jakością

Dlaczego nie używać cross-encoderów do wszystkiego? Ponieważ nie można obliczyć ich wyników z wyprzedzeniem. Embeddings dokumentów bi-encodera są niezależne od zapytania, więc oblicza się je raz i przechowuje. Wynik cross-encodera zależy jednocześnie od zapytania i dokumentu. Wynik trafności dla pary „wireless headphones” i produktu Sony wynika z pełnego cross-attention między tymi konkretnymi tokenami. Nie można go cache’ować ani ponownie wykorzystać dla innego zapytania.

Bi-encoder potrzebuje jednego kodowania zapytania oraz wyszukiwania wektorowego po wstępnie obliczonych embeddings dokumentów. Cross-encoder ocenia każdą parę zapytanie–dokument na shortliście, a koszt rośnie zarówno wraz z liczbą kandydatów, jak i długością sekwencji. Batching pomaga, ale ocenianie 100 000 kandydatów nadal jest niewłaściwym punktem pracy; najpierw wykonaj retrieval i zmierz największy rozmiar shortlisty spełniający cele jakościowe i opóźnieniowe.

W demie Recall@100 pozostaje na poziomie 0,842 przez etap cross-encodera. Reranking może zmieniać kolejność wyników, ale nie może dodawać dokumentów. Retrieval wyznacza górną granicę.


Listwise reranking z użyciem LLM

Końcowy etap dema wykorzystuje LLM do listwise rerankingu. Zamiast niezależnie oceniać każdy dokument, model widzi top 10 i zwraca uporządkowaną listę. Zainspirowany RankGPT prompt explicite wymusza porównanie względne, ale wprowadza też limity kontekstu, position bias, błędy parsowania i zmienność między uruchomieniami.

Podejścia do rerankingu z użyciem LLMPodejścia do rerankingu z użyciem LLM

Prompt listwise

Szablon promptu prosi LLM o uwzględnienie hierarchii trafności ESCI:

# src/search_ranking_stack/stages/s05_llm_rerank.py

def _create_listwise_prompt(query, documents, max_words=200):
    n = len(documents)

    doc_texts = []
    for i, (doc_id, doc_text) in enumerate(documents, start=1):
        words = doc_text.split()[:max_words]
        doc_texts.append(f"[{i}] {' '.join(words)}")

    return (
        f"I will provide you with {n} product listings, each indicated by "
        f"a numerical identifier [1] to [{n}]. Rank the products based on "
        f'their relevance to the search query: "{query}"\n\n'
        "Consider:\n"
        "- Exact matches should rank highest\n"
        "- Substitutes should rank above complements\n"
        "- Irrelevant products should rank lowest\n\n"
        f"{chr(10).join(doc_texts)}\n\n"
        "Output ONLY a comma-separated list of identifiers: [3], [1], [2], ...\n"
        "Do not explain your reasoning."
    )

Trzy tryby wykonania

Demo obsługuje trzy backendy dla rerankingu LLM:

TrybModelSposób uruchomienia
ollamallama3.2:3b (konfigurowalny)Lokalnie przez API Ollama
apiclaude-haiku-4-5-20251001Anthropic API
localQwen/Qwen2.5-1.5B-InstructHuggingFace Transformers

Parsowanie i fallback

Nie ma gwarancji, że wyjście LLM będzie zgodne z żądanym schematem, dlatego znaczenie mają parsowanie i ścieżka fallback:

def _parse_ranking(output: str, n: int) -> list[int] | None:
    """Parse LLM output to extract ranking order."""
    matches = re.findall(r"\[(\d+)\]", output)

    if not matches:
        return None

    positions = [int(m) - 1 for m in matches]

    # Pad with remaining positions if LLM returned partial output
    if len(positions) < n:
        seen = set(positions)
        for i in range(n):
            if i not in seen:
                positions.append(i)

    return positions[:n]

Jeśli parsowanie całkowicie się nie powiedzie, demo wraca do kolejności cross-encodera. Produkcyjny parser powinien również odrzucać identyfikatory spoza zakresu i duplikaty, dopisywać pominiętych kandydatów w ich wcześniejszej kolejności, logować błąd oraz porównywać odsetek fallbacków z progiem wymaganym przed uruchomieniem.


Wyniki z tej próbki ESCI

Zarejestrowane wyniki sprzed etapu LLM wykorzystują small_version z dema: angielski us locale po filtrze small_version == 1, z maksymalnie 500 identyfikatorami zapytań próbkowanymi bez powtórzeń za pomocą seeda 42 oraz korpusem zbudowanym na podstawie ocenionych produktów. Wartości MRR wykorzystują binarną regułę ewaluatora dema relevance > 0: wynik Complement, Substitute lub Exact jest uznawany za istotny, natomiast wynik Irrelevant — nie. recip_rank ocenia każdą zwróconą listę kandydatów, zawierającą do 100 kandydatów; nie jest to MRR@10.

EtapNDCG@10MRRRecall@100Różnica NDCG
BM250,5850,8120,741
Dense Bi-Encoder0,6110,8080,825+0,026
Hybrid (RRF)0,6280,8340,842+0,017
+ Cross-Encoder0,6450,8600,842+0,017

Najważniejsze obserwacje

Hybrid search przewyższa każdą z metod używaną osobno. NDCG RRF (0,628) jest wyższe niż zarówno BM25 (0,585), jak i Dense (0,611). Obie metody mogą zawodzić dla różnych zapytań, a ich połączenie odzyskuje dokumenty pomijane przez każdą z nich osobno.

Recall jest ustalany na etapie retrieval. Recall@100 pozostaje na poziomie 0,842 przez etap cross-encodera. Rerankery zmieniają kolejność, ale nie dodają dokumentów. Jeśli chcesz zwiększyć recall, popraw warstwę retrieval. Wartości MRR powyżej wykorzystują tę samą regułę co całe demo: Complement lub wyżej oznacza wynik istotny.

Wynik LLM nie jest raportowany. Parser akceptuje duplikaty i identyfikatory spoza zakresu, a następnie ścieżka dopełniania może po cichu zmienić listę kandydatów. Wcześniejsze porównanie LLM ma więc charakter ilustracyjny, a nie audytowalny. Uruchom je ponownie ze ścisłą walidacją identyfikatorów, logowaniem fallbacków, wielokrotnymi uruchomieniami, pomiarami opóźnienia i kosztu oraz na zbiorze domenowym held-out, zanim przypiszesz jakikolwiek przyrost rerankerowi.

Repozytorium towarzyszące jest przydatne do konfiguracji i pracy z kodem, ale jego README nadal raportuje niepoprawną wartość + LLM Reranker NDCG@10 równą 0,717. Do czasu przeprowadzenia audytowalnej ewaluacji LLM w repozytorium za wiarygodne należy uznawać tabelę wyników sprzed LLM z tego artykułu oraz opisane powyżej zastrzeżenie dotyczące LLM.

Dense retrieval przewyższa BM25 w tej próbce. Przeanalizuj wycinki zapytań, zanim przypiszesz przyczynę. Rozbieżność słownictwa jest jednym z możliwych czynników, ale wpływ mają również konstrukcja próbki, tokenizer, domena treningowa modelu i pola korpusu.


Ewaluacja: mierzenie tego, co istotne

Demo wykorzystuje trzy metryki, z których każda patrzy na ranking z innej perspektywy:

NDCG@10 (metryka główna)

Normalized Discounted Cumulative Gain mierzy jakość rankingu top 10 z użyciem stopniowanej trafności. Premiowane jest umieszczanie wysoce istotnych dokumentów wysoko, z logarytmicznym obniżaniem wagi wraz z pozycją:

DCG@k=i=1k2reli1log2(i+1)NDCG@k=DCG@kIDCG@k\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{rel_i} - 1}{\log_2(i + 1)} \qquad \text{NDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

Spośród tych trzech metryk NDCG jako jedyna w pełni wykorzystuje czteropoziomową stopniowaną trafność ESCI. System, który umieszcza wynik Exact na pozycji 1, otrzymuje wyższą ocenę niż system, który umieszcza tam Substitute. Dlatego jest to tutaj główna metryka.

MRR (pierwszy wynik Complement lub wyższy)

Mean Reciprocal Rank wykorzystuje pozycję pierwszego wyniku uznanego za istotny. W tym demie ewaluator przekazuje stopniowane qrels ESCI do pytrec_eval i stosuje recip_rank do każdej zwróconej listy zawierającej do 100 kandydatów, więc relevance > 0 jest progiem binarnym: Complement (1), Substitute (2) i Exact (3) są istotne. Irrelevant (0) nie jest. Istotny wynik na pozycji 1 daje reciprocal rank 1,0; na pozycji 3 — 0,333. Jest to MRR dla zwróconych list kandydatów, a nie MRR@10.

Recall@100 (pokrycie retrieval)

Recall mierzy, jaka część ocenionych istotnych dokumentów pojawia się w pierwszej setce. Jest to metryka górnej granicy zbioru kandydatów dla ocenianych etykiet: reranker nie może dodać dokumentu pominiętego przez retrieval, a niekompletne oceny mogą powodować niepewność co do obserwowanej granicy.


Indeksowanie gęstych wektorów poza demem

Gęste embeddings stają się użyteczne na dużą skalę dopiero po zastosowaniu indeksu Approximate Nearest Neighbor (ANN). Demo wykorzystuje brute-force cosine similarity (co jest odpowiednie przy około 8500 dokumentach), ale systemy produkcyjne potrzebują wyspecjalizowanych indeksów.

HNSW (hierarchical navigable small world)

HNSW buduje graf wielowarstwowy: rzadkie warstwy górne umożliwiają szeroką nawigację, a gęstsze warstwy dolne doprecyzowują sąsiedztwo. M steruje spójnością grafu, natomiast efSearch wymienia koszt wyszukiwania na recall. Użyteczne wartości zależą od wymiaru, rozkładu odległości, filtrów, implementacji i docelowego recallu.

Aktualizacje i usuwanie danych są kwestią operacyjną, ponieważ indeksy grafowe mogą wymagać naprawy w tle. Zachowanie zależy od bazy danych. Na przykład issue w Qdrant opisuje pogorszenie jakości wyszukiwania filtrowanego w konfiguracji HNSW dla wielu tenantów. Raport podaje średnią precision@100 równą 0,597 ± 0,0541 z filtrem library_id, podczas gdy wyszukiwanie dokładne osiągnęło 100% recall w dalszej analizie. Zmiana payload_m wymusiła przebudowę, która przywróciła jakość wyszukiwania filtrowanego. Raport dotyczy filtrów tenantów, konfiguracji HNSW i wymuszonej przebudowy indeksu HNSW, a nie obciążenia z dużą liczbą usunięć. Odtwórz docelowy wzorzec zmian danych i uwzględnij w ewaluacji zachowanie podczas compaction oraz rebuildów.

IVF (inverted file)

Indeksy IVF dzielą przestrzeń wektorową na klastry, a następnie przeszukują klastry nprobe najbliższe zapytaniu. Mogą zapewniać użyteczny kompromis między pamięcią, czasem budowania i recall, szczególnie w połączeniu z kompresją. Semantyka aktualizacji i wydajność zależą od implementacji, a nie wyłącznie od rodziny indeksu.

Przy ekstremalnej skali IVF_RaBitQ (Gao & Long, SIGMOD 2024) kompresuje wektory zmiennoprzecinkowe do reprezentacji jednobitowych. W przestrzeni o wysokim wymiarze znak współrzędnej (+/−) niesie wystarczająco dużo informacji kątowej do obliczania podobieństwa.

WymiarGraf HNSWKlastry IVF
Sterowanie zapytaniemefSearchnprobe
Sterowanie budowaniemSpójność i beam konstrukcyjnyLiczba klastrów i próbka treningowa
Profil pamięciKrawędzie grafu plus wektoryCentroidy, listy i zapisane wektory
Zachowanie przy aktualizacjachNaprawa/czyszczenie zależne od bazyUtrzymanie list zależne od bazy
Ewaluacja za pomocąKrzywa recall–opóźnienie–pamięć–churnKrzywa recall–opóźnienie–pamięć–churn

W jednym studium przypadku wyszukiwania dostaw Uber zmniejszenie parametru wyszukiwania na poziomie sharda z 1200 do 200 skróciło raportowane opóźnienie o 34% i obniżyło użycie CPU o 17%, przy niewielkiej zmierzonej utracie recallu. Uniwersalna lekcja brzmi: dostrajaj krzywą recall–koszt na ruchu zbliżonym do produkcyjnego, zamiast kopiować wartość 200.


Opcjonalne rozszerzenia po podstawowym pipeline

Gdy retrieval i reranking mają osobne pomiary, łatwiej oceniać kolejne rozszerzenia bez zaciemniania podstawowego pipeline’u.

Rozumienie zapytań

Rozszerzanie i przepisywanie zapytań może niwelować rozbieżność słownictwa przed retrieval. Query2doc generował pseudo-dokumenty i raportował wzrosty BM25 w eksperymentach MS MARCO. Rozszerzanie może również wprowadzać błędną intencję, dlatego porównuj recall i precision na wycinkach zapytań niejednoznacznych, nawigacyjnych i zawierających dokładne identyfikatory.

Praktyczne wzorce obejmują rozwijanie skrótów, wzbogacanie encji, dekompozycję podzapytań dla rozumowania wieloetapowego oraz RAG-Fusion — generowanie wielu wariantów zapytania i łączenie wyników za pomocą RRF.

Etykietowanie trafności wspomagane przez LLM

LLM mogą tworzyć robocze etykiety trafności, gdy brakuje ocen ludzkich. TALEC oraz prace Pinterest nad etykietowaniem trafności przedstawiają dwa ocenione projekty. Etykieta LLM nadal jest wynikiem modelu: kalibruj ją względem zaślepionych ocen ludzkich, analizuj wycinki rozbieżności i utrzymuj ludzki zbiór gold do testów regresyjnych.

Przydatne mechanizmy kontrolne obejmują:

  • rubric z konkretnymi granicami trafności i przykładami;
  • zaślepioną kalibrację ludzką oraz okresowe ponowne kontrole;
  • losowanie kolejności i powtarzane oceny dla niestabilnych przypadków;
  • panele modeli, gdy ich dodatkowy koszt poprawia zgodność; oraz
  • jawne kontrole position bias i biasu tendencji centralnej.

Knowledge distillation

Gdy nauczyciel LLM zapewnia dodatkową wartość, ale nie spełnia ograniczeń serwowania, jedną z opcji jest destylacja:

  1. Użyj wydajnego LLM (nauczyciela) do rerankingu tysięcy zapytań treningowych
  2. Wytrenuj mały, szybki cross-encoder (ucznia, około 100–200M parametrów), aby naśladował rozkład rankingu LLM
  3. Porównaj ucznia zarówno z nauczycielem, jak i baseline’em pod względem jakości, kalibracji i kosztu serwowania

InRanker destyluje MonoT5-3B do modeli o 60M i 220M parametrów — redukcja rozmiaru 50× przy konkurencyjnej wydajności. Podejście Rank-Without-GPT tworzy otwarte listwise rerankery 7B, które dzięki fine-tuningowi QLoRA osiągają 97% skuteczności GPT-4.

Opublikowane wyniki kompresji są punktami wyjścia, a nie oczekiwanymi współczynnikami produkcyjnymi. Destylacja może odziedziczyć biasy nauczyciela i pogorszyć jakość na rzadkich wycinkach zapytań, dlatego należy zachować oryginalne oceny trafności w pętli ewaluacji.


Personalizacja i position bias

Ogólna trafność ma swoje ograniczenia. Wyszukiwanie „apple” powinno zwracać iPhone’y entuzjaście technologii, a przepisy z jabłkami osobie przeglądającej treści kulinarne.

Typowa architektura retrieval dla personalizacji wykorzystuje model two-tower embeddings: wieża zapytania koduje zapytanie i kontekst użytkownika, a wieża elementu koduje elementy i metadane. Podział offline/online wspiera retrieval approximate-nearest-neighbor; jego opóźnienie nadal zależy od enkodera, indeksu, filtrów i systemu serwowania.

Embeddings ofert Airbnb, OmniSearchSage firmy Pinterest oraz systemy two-tower Ubera pokazują różne projekty produkcyjne. Ich skala i raportowane wzrosty dotyczą tych konkretnych systemów; przenośnym wzorcem jest offline’owa wieża elementów oraz online’owa wieża zapytania/użytkownika.

Dane kliknięć zawierają bias pozycji i ekspozycji. PAL jest jednym z podejść do debiasingu: używa pozycji podczas treningu, a następnie utrzymuje ją stałą podczas serwowania. Nie jest to uniwersalne rozwiązanie; dla innego produktu bardziej odpowiednie mogą być randomizowane interwencje, metody inverse-propensity i ewaluacja kontrfaktyczna.


Adaptacja domenowa za pomocą zapytań syntetycznych

Częstym błędem w strategii wyszukiwania jest założenie, że model wytrenowany na ogólnych danych webowych, takich jak MS MARCO, będzie dobrze działał w wyspecjalizowanej domenie. To problem out-of-domain (OOD).

LLM mogą zmniejszyć, ale nie wyeliminować, wąskie gardło związane z oznaczonymi danymi za pomocą Generative Pseudo-Labeling (GPL, InPars):

  1. Weź domenowy korpus dokumentów
  2. Poproś LLM: „Generate a search query that this document would answer”
  3. Użyj syntetycznych par (query, document) do dostrojenia retrievera i rerankera

Pary syntetyczne mogą pomóc, gdy brakuje rzeczywistych zapytań, ale odzwierciedlają generator i prompt. Usuń duplikaty, odfiltruj nieprawdopodobne zapytania i waliduj wyniki na rzeczywistym ruchu held-out.

Sekwencja eksperymentów

Dodawaj złożoność tylko wtedy, gdy poprzedni etap ujawni zmierzony problem:

Praktyczna ścieżka dojrzałościPraktyczna ścieżka dojrzałości

Krok 1 (baseline): zaimplementuj BM25 lub bieżący system leksykalny i zbuduj oceniony zbiór zapytań. Rejestruj recall, NDCG, opóźnienie i wycinki błędów.

Krok 2 (recall kandydatów): testuj dense retrieval i fuzję tylko wtedy, gdy baseline pomija istotne dokumenty. Dostrajaj odcięcie kandydatów względem recallu i kosztu.

Krok 3 (precision rankingu): dodaj cross-encoder, jeśli właściwi kandydaci istnieją, ale pojawiają się w złej kolejności. Wybierz rozmiar shortlisty na podstawie krzywej jakości i opóźnienia.

Krok 4 (dopasowanie do domeny): dostrajaj lub destyluj model dopiero wtedy, gdy modele ogólne wykazują stabilne, specyficzne dla domeny błędy. Trzymaj rzeczywiste oceny held-out oddzielnie od syntetycznych danych treningowych.

Krok 5 (opcjonalna kosztowna warstwa): testuj listwise lub oparte na rozumowaniu reranking dopiero wtedy, gdy jego przyrost jakości utrzymuje się w powtarzanych uruchomieniach i uzasadnia dodatkowe opóźnienie, koszt, wymagania prywatności oraz złożoność fallbacków.


Kierunki badawcze do osobnej ewaluacji

Rerankery oparte na rozumowaniu i agenci wykorzystujący wyszukiwanie są obiecujący, ale odpowiadają na inne pytania niż pięcioetapowe demo wyszukiwania produktów.

Rerankery oparte na rozumowaniu

Rank1 trenuje rerankery z trace’ami rozumowania i raportuje dobre wyniki na benchmarku BRIGHT. Dowody te dotyczą retrieval wymagającego rozumowania, a nie są bezpośrednią prognozą dla wyszukiwania produktów ESCI.

W wyszukiwaniu prawnym lub naukowym porównuj rerankery oparte na rozumowaniu z silnymi baseline’ami cross-encoderowymi i listwise pod względem ocen eksperckich, cytowań, opóźnienia i spójności błędów.

Wyszukiwanie agentowe

Search-o1 bada model, który wykonuje dodatkowe wyszukiwania podczas odpowiadania na pytania wieloetapowe. To problem orkiestracji — generowania zapytań, warunku zakończenia, wykorzystania dowodów i ewaluacji odpowiedzi — a nie kolejny etap rerankingu. Oceniaj go pod kątem poprawności zadania końcowego i wsparcia cytowaniami, a nie wyłącznie metryk retrieval.


Najważniejsze wnioski

  1. Traktuj stos jako sekwencję eksperymentów. Ustal baseline leksykalny i oceniony zbiór zapytań przed dodaniem dense retrieval, fuzji lub rerankingu.

  2. Mierz recall kandydatów oddzielnie od precision rankingu. W tej próbce ESCI Recall@100 osiąga 0,842 po fuzji i pozostaje na tym poziomie przez oba etapy rerankingu.

  3. Używaj retrieval hybrydowego, gdy błędy są komplementarne. RRF poprawił zarówno Recall@100, jak i NDCG@10 w demie, ale inny korpus może nie uzasadniać utrzymywania dwóch indeksów.

  4. Dodaj cross-encoder, gdy shortlista jest właściwa, ale kolejność błędna. Liczbę kandydatów wybieraj na podstawie zmierzonej krzywej jakości i opóźnienia.

  5. Traktuj reranking LLM jako opcjonalny końcowy eksperyment. Bieżące porównanie LLM w demie ma charakter ilustracyjny, ponieważ parser nie sprawdza kompletnej permutacji identyfikatorów kandydatów. Ścisłe parsowanie, logowane fallbacki, powtarzane uruchomienia i koszt powinny zostać uwzględnione w ewaluacji przed zaraportowaniem przyrostu.

  6. Rozdzielaj typy dowodów. Wynik z publikacji, studium przypadku dostawcy, demo uruchomione na laptopie i produkcyjny test A/B odpowiadają na różne pytania.

Kompletny kod pipeline’u znajduje się w repozytorium towarzyszącym. Sklonuj je, aby odtworzyć etapy sprzed LLM lub przetestować inne modele i parametry; nie traktuj nieaktualnego wiersza LLM jako wyniku.

Referencje

Publikacje

Zbiory danych i benchmarki

Modele użyte w demie

Narzędzia i platformy

  • rank_bm25 — implementacja BM25 w Pythonie
  • Pyserini BEIR Reproductionsbm25-multifield polecenia indeksowania i ewaluacji
  • pytrec_eval — toolkit ewaluacyjny TREC
  • Elasticsearch — wyszukiwanie hybrydowe z Retrievers API
  • Vespa — zunifikowany silnik wyszukiwania i rekomendacji
  • Weaviate — wektorowa baza danych z wyszukiwaniem hybrydowym
  • Qdrant — wektorowa baza danych z zapytaniami wieloetapowymi

Referencje branżowe

Projekt demonstracyjny