Strona główna / Artykuły

Przegrupowanie w RAG: Wąski zakres, w którym kodery krzyżowe rzeczywiście pomagają

Przegrupowanie w RAG pomaga jedynie pomiędzy dolną granicą召回 a górną granicą K. Co to są Phantom Hits oraz jak zmierzyć obie te granice dla własnych zapytań w ciągu popołudnia.

Wydawca: REACTAPP.TOP13 min czytania3018 słów

Źródła: Reranking in RAG — Cross-Encoders, the K Threshold, and the Recall Ceiling in Production Systems

Kodownik krzyżowy pomaga procesowi RAG jedynie w wąskim zakresie: stopień odzyskiwania informacji na pierwszym etapie musi być wystarczająco wysoki, a liczba kandydatów, które przekazujesz, musi pozostać wystarczająco mała. Poza tym zakresem albo reranker nie ma nic do poprawienia, albo zaczyna powodować szkody. Poniżej omówiono mechanizm działania, tryb awarii zwany Phantom Hits oraz sposób pomiaru wszystkich tych aspektów na własnych danych.

Wyobraź sobie system RAG, który znalazł właściwą odpowiedź. Nie doszło do żadnego błędu ani utraty dokumentu. Odpowiedź znajduje się po prostu na pozycji 37 w liście wyników, podczas gdy model LLM czyta tylko pierwsze pięć, więc w praktyce nie istnieje. Nie wyzwala się żadnego alarmu, ponieważ według wszystkich wskaźników monitoringu żądanie zakończyło się pomyślnie.

To nie jest osobliwość jednej konfiguracji. Wynika to z sposobu budowy architektury. Wyszukiwanie wektorowe jest zaprojektowane tak, by prawidłowa odpowiedź znajdowała się gdzieś wśród pierwszych 50 do 200 kandydatów, niekoniecznie na samym szczycie, więc proces kończący się wydobyciem wyników pozostawia jakość odpowiedzi przypadkowi.

Zwykłym odruchem jest dodanie mechanizmu ponownego rankowania. Oznacza to użycie kodera krzyżowego, około dwunastu linijek kodu, niewielkiego wzrostu wartości nDCG, a sprawa zostaje zamknięta. Ten odruch nie jest błędny, ponieważ ponowne rankowanie rzeczywiście zawodzi. Jest błędny, ponieważ działa ono w wąskim zakresie warunków, który prawie nikt nie mierzy. Poniżej zobaczysz, jak funkcjonuje ta droższa druga faza, gdzie znajdują się granice tego zakresu oraz jak można dowiedzieć się w ciągu jednego popołudnia, po której stronie każdej z tych granic znajduje się twój system.

Ponowne rankowanie to nie lepsze wyszukiwanie wektorowe

Reranker to inna architektura, która włącza się dopiero po wyszukaniu i jest stosowana do małej grupy kandydatów.

Bi-encoder używany w istniejących systemach wyszukiwania wektorowego koduje zapytanie oraz każdy dokument osobno, z góry i offline. Dzięki temu proces jest szybki i umożliwia obsługę milionów dokumentów. Wadą jest to, że nigdy nie bierze pod uwagę zapytania i dokumentu razem.

Cross-encoder robi coś przeciwnego – czyta je oba jednocześnie, token po tokenie, co pozwala mu dostrzec negacje, sprzeczności liczbowe oraz subtelności, których bi-encoder nie jest w stanie dostrzec. W zamian musi powtarzać tę pracę dla każdego kandydata osobno, w momencie wysyłania zapytania.

To daje znany dwuetapowy proces. Etap pozyskiwania danych tanio zawęża zakres do 50–200 kandydatów i ma na celu zapewnienie pełnego odnalezienia odpowiedzi – by nie stracić właściwej odpowiedzi. Następnie etap ponownego sortowania porządkuje ten mały zbiór przy wyższych kosztach i dąży do precyzji – spośród tych, które przetrwały, wybiera te naprawdę najlepsze.

Drugi poziom również nie jest darmowym ulepszeniem. Po przekroczeniu określonej liczby kandydatów kodownik krzyżowy zaczyna traktować szum jako sygnał, co faktycznie powoduje spadek jakości. Decyzja o dodaniu etapu ponownego sortowania to obliczenia inżynieryjne przeprowadzane na Twoich danych, a nie instynktowny wybór.

Dlaczego pozyskiwanie danych nie może być ostatnim krokiem

Symptom, który stale pojawia się w produkcji systemów RAG, brzmi następująco: „model tworzy halucynacje, mimo że informacje znajdują się w naszej bazie danych”. W większości przypadków, gdy sprawę się dogłębnie bada, okazuje się, że model nie wymyślał niczego samodzielnie. Poprawny dokument był wśród pięćdziesięciu najlepszych wyników, po prostu nie należał do pięciu najlepszych, które zostały pokazane generatorowi.

Powodem jest aspekt architektoniczny. Bi-encoder oblicza embedding dokumentu w momencie indeksowania, zanim pojawi się jakiekolwiek pytanie. Podczas wyszukiwania porównuje dwa skompresowane streszczenia, a nie rzeczywisty tekst zapytania z rzeczywistym tekstem dokumentu.

Klasyczny przykład tego, co ginie: zapytanie mówi „tani”, a dokument podaje „500 dolarów za noc”. Dla wektorów obie informacje dotyczą ceny pokoju hotelowego, więc znajdują się blisko siebie. Sprzeczność staje się widoczna dopiero wtedy, gdy ktoś czyta je obok siebie, a bi-encoder z definicji tego nie robi.

Lepszy model embeddingu nie rozwiąże tego problemu, ponieważ jest to celowe kompromis. Proces wyszukiwania maksymalizuje stopień odnalezienia, czyli szansę, że właściwy dokument znajduje się wśród powrotnych wyników, i robi to tanio przy milionach dokumentów. Umieszczenie właściwego dokumentu na samym szczycie po prostu nie należy do jego zadań.

Recall@50 i precision@5 mierzą różne rzeczy i odpowiadają na różne pytania. Ich pomieszanie jest powodem, dla którego zespoły muszą płacić za dodatkową warstwę, aby rozwiązać problem, którego nie mogą poradzić sobie sami.

Wadą jest to, że bi-encoder nigdy nie analizuje pary łącznie, więc zobaczmy, jakie korzyści przynosi model, który faktycznie bierze pod uwagę tę parę.

Bi-encoder kataloguje, cross-encoder czyta

Pomyślmy o rekrutacji. Bi-encoder odgrywa rolę rekrutera, który sortuje tysiąc CV do folderów, zanim nawet istnieje opis stanowiska: szybko, powierzchownie, wcześnie kończy pracę. Cross-encoder odgrywa rolę kierownika rekrutacji, który ma dziesięciu finalistów i czyta każde CV obok ogłoszenia, linijka po linijce. Dopiero wtedy pojawiają się niezgodności, których rekruter nie mógł dostrzec.

Mechanicznie różnica jest oczywista. Cross-encoder łączy zapytanie i dokument w jedną sekwencję, w kanonicznym układzie [CLS] zapytanie [SEP] dokument [SEP], i przekazuje ją przez jeden transformator.

Wewnątrz to mechanizm cross-attention wykonywać ma pracę: każdy token zapytania może bezpośrednio reagować na każdy token dokumentu i odwrotnie. Wynikiem nie jest wektor do porównania, lecz pojedynczy logit – surowa, nienormalizowana liczba wyrażająca stopień pewności modelu co do tego, że dana para jest istotna.

Zatem bi-encoder dostarcza reprezentację, natomiast cross-encoder wydaje werdykt. Chodzi tu o różnicę w tym, co model w ogóle może dostrzec, a nie o różnicę w stopniu.

Następuje natychmiastowa konsekwencja. Wynik cross-encodera nie może być obliczony z góry ani zindeksowany, ponieważ zależy od konkretnej pary zapytania i dokumentu, a nie tylko od samego dokumentu. Każde zapytanie wymaga pełnego przetworzenia dla każdego kandydata, co jest dziesięć do stu razy droższe na parę w porównaniu z porównaniem wektorowym.

Wizualizacja przedstawiająca proces dwuetapowego projektowania ułatwia zrozumienie skali zadania. Znalezienie najbardziej podobnej pary wśród 10 000 zdań wymaga około 50 milionów porównań, co przy pełnym modelu BERT odpowiada mniej więcej 65 godzinom, natomiast przy architekturze w stylu SBERT – około 5 sekund, jak wynika z artykułu Sentence-BERT z 2019 roku autorstwa Reimersa i Gurevycha.

Pamiętaj o tej dokładnej wartości, ponieważ krążą jej zaokrąglone wersje, np. „10 milionów dokumentów i 50 godzin”. Różnica jest jakościowa: kluczowym czynnikiem jest liczba par, które trzeba ocenić, a nie wielkość korpusu.

Dlatego cross-encoder nigdy nie służy jako pierwszy etap. Nie jest gorszy, po prostu jego uruchomienie na całym korpusie jest zbyt kosztowne. A ponieważ kosztuje dużo za każdą parę, wszystko zależy od liczby par, które mu podasz, oraz od tego, w którym miejscu znajduje się w procesie.

Dwa etapy, dwa budżety

Reranker znajduje się po etapie wyszukiwania i przed LLM. Wyszukiwarka zwraca najlepsze k kandydatów, przy czym k to liczba dokumentów przekazywanych z jednego etapu na następny, zazwyczaj od 50 do 200. Następnie reranker przetasowuje tę listę i przekazuje ją generatorowi na pozycji od 3 do 20.

Ta kaskada nie powstała wraz z LLM, co świadczy o jej dojrzałości. Model „tani filtr, potem drogi sędzia” pochodzi z ery Learning-to-Rank, gdy drugim etapem była grupa drzew decyzyjnych opartych na ręcznie stworzonych cechach, a nie model transformer. Silnik się zmienił, ale architektura pozostała taka sama.

Neural reranking ma określoną datę powstania. W 2019 roku artykuł „Passage Re-ranking with BERT” wykorzystał BERT jako klasyfikator binarny do analizy par zapytania i tekstu, co przyniosło poprawę wskaźnika MRR@10 o 27 procent w porównaniu z najlepszym wynikiem dostępnym w tamtym czasie. Wtedy to druga faza przestała być elementem optymalizacji i stała się odrębną warstwą.

Najbardziej znaną obecnie komercyjną implementacją jest flagowy narzędzie do rerankingu firmy Cohere. W różnych wersjach największe zmiany dotyczyły jednego ustawienia – okno wejściowe rozszerzyło się z kilkuset tokenów do ponad 32 000, przy czym w wersjach v3 i v3.5 granica ta wynosiła 4 096 tokenów. Jest to prawdziwa zmiana w projekcie, ponieważ zwiększa rozmiar fragmentów tekstu, które mogą być oceniane bez skracania, dzięki czemu ich dzielenie na fragmenty może być mniej agresywne, a mniej kontekstu jest tracione.

Dostawcy rzadziej mówią o tym, co będzie dalej. Ceny podawane dla tego samego modelu w różnych źródłach różnią się dziesięciokrotnie lub nawet bardziej – od ułamka centa do dwóch i pół dolara za tysiąc wyszukiwań, w zależności od tego, kto je liczy i co wlicza do pojęcia „wyszukiwania”.

Obowiązuje prosta zasada: jeśli nie możesz samodzielnie powtórzyć obliczeń dostawcy dla swojego ruchu, to nie masz dokładnego wyniku, a jedynie przypuszczenie. Istnieje również pułapka w fakturowaniu – pliki dłuższe od ustalonego limitu są dzielone na fragmenty, a każdy z nich jest fakturowany oddzielnie, więc jedna logiczna prośba o ponowne uporządkowanie może skończyć się kilkoma osobnymi fakturami.

Kształt, który należy zapamiętać, jest następujący: pozyskiwanie danych tanio na dużą skalę, ponowna klasyfikacja drogo w wąskim zbiorze, oraz umożliwienie modelowi LLM widzenia tylko tych danych, które przetrwają oba te procesy. Brzmi to jak bezpieczny standard, jednak istnieje pewien próg, po którym przestaje to działać, i to w kierunku, którego niewielu się spodziewa.

Fantomowe wyniki i moment, w którym ponowna klasyfikacja zaczyna szkodzić

Zgodnie z powszechnym mniemaniem, im więcej kandydatów podasz modelowi do ponownej klasyfikacji, tym bezpieczniej jest. Model jest dokładniejszy od wyszukiwania wektorowego, więc przy liczbie K wynoszącej 500 lub 1000 z pewnością nie może popełnić błędu. Ta intuicja jest błędna, a dowody przeciwko niej są udokumentowane.

W artykule z 2024 roku „Drowning in Documents” (Jacob i współpracownicy) stwierdzono, że wraz ze wzrostem wartości K jakość ponownego rankowania rośnie, osiąga szczyt, a następnie wyraźnie spada. Gdy cała kolekcja była skanowana w trybie ślepym, zwykły algorytm BM25 potrafił przewyższyć najlepszy dostępny kodownik krzyżowy. Należy pamiętać o jednej zastrzeżeniu: ten wynik pochodzi ze skanów ślepych na danych akademickich i korporacyjnych i niewiele mówi o typowej procedurze, w której wartość K wynosi co najwyżej kilkaset.

To zjawisko ma nazwę – Phantom Hits. Algorytm ponownego rankowania z confidencją przyznaje najwyższą ocenę dokumentowi, który nie ma żadnego związku z zapytaniem, ani leksykalnego, ani semantycznego.

Autorzy proponują prawdopodobne źródło i uważają, by określać je jako hipotezę, a nie potwierdzoną przyczynę. Wskazują na ustawienia treningowe, które nazywają uprzedzeniem ekspozycyjnym. Modele te są szkolone w sposób kontrastowy na trudnych negatywach – dokumentach, które pozornie przypominają prawidłową odpowiedź, ale w rzeczywistości są błędne – dzięki czemu model staje się ekspertem od mikroskopijnych różnic.

W warunkach produkcyjnych ta specjalistyczna wiedza okazuje się szkodliwa. Gdy modelowi pokazuje się łatwy negatyw, zwykły szum, którego ledwo doświadczył podczas treningu, nie ma żadnego punktu odniesienia i zaczyna zgadywać. Model szkolony do rozróżniania bliźniąt jest bezradny, gdy wchodzi obcy człowiek.

Przeciwny brzeg jest równie rzeczywisty i nie unieważnia pierwszego. Kilka niezależnych analiz wskazuje, że dolna granica punktu równowagi dla ponownego sortowania wynosi około recall@50 = 0,85. Poniżej tej wartości nie ma nic wartego sortowania, ponieważ prawidłowy dokument nigdy nie trafił do zbioru. Dokładne progi różnią się w zależności od źródeł, ponieważ korpusy są różne, a ich pochodzenie ma znaczenie: blogi i doświadczenie praktyczne, a nie recenzje rówieśnicze. Należy je traktować jako hipotezy do sprawdzenia na własnych danych, a nigdy jako ustalone fakty.

Te dwa ustalenia nie są sprzeczne. Stanowią one dwa brzegi jednego okna operacyjnego. Poniżej progu召回 nie ma czego mógłby wykorzystać proces ponownego sortowania. Powyżej progu K zaczyna on powodować szkody. Ponowne sortowanie nie jest monotonnym ulepszeniem, a zakładanie, że lepiej jest zawsze, powtarza błąd ślepego zaufania do standardów dostawcy, ponieważ oba przypadki zastępują pomiary folklorem.

Biorąc pod uwagę, że zbyt wiele ponownych sortowań może być szkodliwe, a zbyt mało może nie wystarczyć, potrzebujemy jasnego pojęcia o granicy tego, co ta warstwa może naprawić.

Granica efektywności, a to, co naprawdę oznaczają 67 procent

Ponowny sorter to redaktor, a nie reporter. Może przearanżować materiały już zebrane przez kogoś innego, ale nie może stworzyć tego, czego nikt nie dostarczył.

To zasada, czyli granica efektywności, wydaje się błahe, dopóki nie spotka się z najbardziej imponującą statystyką w branży. Anthropic opisał w swoim opracowaniu na temat kontekstowego wyszukiwania proces z 2024 roku, w którym wskaźnik niepowodzeń przy wyświetlaniu pierwszych 20 wyników spadł z 5,7 do 1,9 procenta, co stanowi redukcję o 67 procent.

Tę liczbę często przytacza się na poparcie zastosowania ponownego sortowania. Gdy rozdzieli się ją według etapów, ukazuje się inna historia, choć nie dlatego, że jest fałszywa.

Ścieżka była taka: embeddingi kontekstowe obniżyły wskaźnik do 3,7 procenta, dodanie BM25 doprowadziło go do 2,9, a dopiero wtedy reranking osiągnął 1,9. Sam reranking pokrywa zatem ostatni etap, od 2,9 do 1,9 procenta. Obie wartości są istotne, ponieważ mierzą różne zakresy – 67 procent to wynik całego zestawu mechanizmów, a nie pojedynczej warstwy.

To rozróżnienie ma rzeczywiste znaczenie praktyczne. Zanim przedstawisz wartość „67 procent” podczas przeglądu architektury, pamiętaj, że obiecujesz efekt czterech zmian łącznie, przy czym trzy z nich występują wcześniej w łańcuchu niż reranking i przyczyniły się do większości poprawy.

Autorzy sami wskazują na tę ograniczenie: wykres przedstawia średnie wartości z różnych dziedzin, a rozbieżności między nimi są duże. Kolejna uwaga wynikająca z dostępnych dowodów: każda wartość jakości dla komercyjnych narzędzi do ponownego rankowania jest podawana przez dostawców, na podstawie ich własnych zbiorów danych i przy użyciu ich własnych metod pierwszego etapu wyszukiwania. Nie istnieje żadna niezależna, ogólnorynkowa ocena.

Żaden zewnętrzny punkt odniesienia nie pokaże ci, jak zachowuje się ponowne rankowanie w twoim systemie, dlatego musisz to sprawdzić sam.

Obliczenie, które możesz przeprowadzić w poniedziałek rano

Cała złożoność tego tematu sprowadza się do jednej powtarzalnej procedury. To, jak rzadko zespoły ją stosują, jest powodem, dla którego argument „czy ponowne rankowanie pomaga?” wciąż się pojawia.

Krok pierwszy: zmierz wartości recall@50 i precision@5 na około stu oznaczonych zapytaniach produkcyjnych. Użyj własnych zapisów oraz własnej kombinacji terminów i intencji, a nie publicznego benchmarku. Jeśli wartość recall@50 jest wyraźnie poniżej 0.85, najpierw popraw mechanizm wyszukiwania poprzez dzielenie na fragmenty, hybrydowe wyszukiwanie lub użycie embeddingów, zamiast dodawania dodatkowej warstwy.

Krok drugi: poszukaj specyficznego profilu. Wysoki wskaźnik recall@50 w połączeniu z niską precyzją@5 to dokładnie ten profil, dla którego istnieje funkcja rerankingu. Odpowiedni dokument znajduje się w zbiorze, ale znajduje się zbyt nisko w rankingu.

Krok trzeci: świadomie wybierz model. API hostowane w chmurze zapewnia szybki start i brak kosztów operacyjnych, ale dane opuszczają twoją infrastrukturę. Samodzielnie hostowany narzędzie do rerankingu daje kontrolę nad kosztami i prywatnością, ale wymaga inwestycji w karty graficzne i konieczności ich utrzymania. To raczej decyzja związana z zgodnością i budżetem niż jakością modelu.

Próba jest również tania. Lokalne ponowne sortowanie mieści się w około tuzinie linijek kodu:

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-large", max_length=512)

def rerank(query, docs):
    pairs = [(query, d.page_content) for d in docs]   # pair: query + candidate
    scores = reranker.predict(pairs)                  # one logit per pair
    return sorted(zip(docs, scores), key=lambda x: x[1], reverse=True)

Najczęściej występują trzy błędy. Jeden polega na zawyżaniu wyników K „tylko na wszelki wypadek”. Inny to ponowna klasyfikacja po skróceniu kontekstu, przez co model ocenia tylko prefiks, a nie cały dokument. Trzeci to traktowanie wyników z kodera krzyżowego jako absolutnych progów porównywanych pomiędzy zapytaniami, co jest szczególnie niebezpieczne, ponieważ działa w wersji demonstracyjnej, ale zawodzi w środowisku produkcyjnym.

Istnieje jeszcze jeden mniej oczywisty błąd: stwierdzenie „ponowny klasyfikator nie pomaga” bywa czasami błędem numerycznym, a nie wnioskiem dotyczącym architektury. Przypadek opisany w przewodniku migracji Sentence-Transformers dotyczył sygmoidy obliczanej w formacie bfloat16, która doszła do stanu nasycenia, łączyła najwyższe wyniki i faktycznie losowała ich kolejność. Gdy zamiast niej zaczęto używać formatu float32 przed procesem aktywacji, klasyfikacja wróciła do normalnego stanu, pozbawiona elementów losowych.

Wszystko to wymaga jedynie pomiarów, bez konieczności zmiany architektury.

Co to oznacza dla architekta

Z powrotem do pozycji 37. Dokument, który tam utknął, nigdy nie był problemem modelu generującego – był to problem uporządkowania.

  • Kros-encoder nie jest lepszym embeddingiem. To odrębna architektura, uruchamiana po wyszukiwaniu, która czyta zapytanie i dokument razem i zwraca werdykt zamiast reprezentacji. Żadna poprawa jakości embeddingu nie daje tego, co osiąga się poprzez czytanie pary obok siebie.
  • Reranking należy do jednego miejsca. Wyszukiwanie pozostaje tanie i szerokie, reranking drogi i wąski, a struktura kosztów tej kaskady stanowi całą ideę.
  • Więcej kandydatów nie jest bezpiecznym standardem. Po przekroczeniu pewnej wartości K jakość rzeczywiście spada, a powód leży w sposobie szkolenia tych modeli.
  • Strop jest sztywny. To, czego pierwszy etap nie przywrócił, nie może zostać odzyskane za pomocą ponownego rankowania, bez względu na to, co twierdzą materiały dostawców.
  • Twoje dane są punktem odniesienia. Jeden popołudnie spędzone na obliczaniu recall@50 i precision@5 dla stu oznaczonych zapytań kończy debatę, która w przeciwnym razie mogłaby trwać tygodniami.

Wiele zespołów traktuje ponowne rankowanie jako standardową funkcję, dodawaną do każdego systemu RAG w taki sam sposób jak pamięć cache. To błędny nawyk. Jaśniejszy obraz pojawia się, gdy spojrzy się na oba progi razem: dolną granicę, poniżej której warstwa nie ma nic do naprawy, oraz górną granicę, powyżej której zaczyna psuć wyniki. Krótko mówiąc, ponowne rankowanie jest warunkowe, nigdy automatyczne.

Jedna luka pozostaje otwarta. W przeanalizowanym tutaj materiale nie podano oddzielnych wskaźników dla języka polskiego, a każda liczba dotycząca wielojęzyczności to średnia dla dziesiątek języków. W przypadku korpusu w języku polskim lub jakiegokolwiek innego mniejszego języka pomiar na własnych danych nie jest tylko zalecany – to jedyna możliwość.