Ocena LLM bez zależności od zewnętrznych narzędzi, z sędzią, któremu naprawdę można zaufać
Stwórz prostą ocenę małego modelu LLM na podstawie rzeczywistych logów, sprawdzeń kodu oraz jednego kryterium oceny, a następnie dostosuj to kryterium do ludzkich etykiet, aby uzyskane wyniki miały sens.
Testy jednostkowe działają, ponieważ kod deterministyczny zawsze daje ten sam wynik: jeśli założymy, że funkcja ma zwracać cztery, to zawsze tak robi. Wyniki generowane przez modele językowe zmieniają sformułowania przy każdym wywołaniu, więc nie ma żadnej oczekiwanej ciągu znaków do porównania, dlatego zespoły często stosują platformy oceniania wykorzystujące inne modele do oceny rezultatów. To właśnie w modelu oceniania większość procesów oceny w praktyce przestaje mierzyć cokolwiek, ponieważ nikt nie potwierdził, że jego wyniki są zgodne z oceną człowieka. Ten przewodnik pozwala stworzyć kompletny system oceniania przy użyciu zwykłego Pythona w ciągu kilku godzin, a jego najważniejszy fragment poświęcony jest kalibracji modelu oceniania, aby uzyskany wynik mógł być uzasadniony.
Trzy elementy każdej oceny
Gdy usuniemy narzędzia, ocena składa się z trzech komponentów, z których żaden nie wymaga biblioteki:
- Zbioru danych wejściowych, najlepiej pochodzących z rzeczywistego użycia, a nie wymyślonych.
Paneliki sterowania, śledzenie i hostowane zbiory danych to udogodnienia stworzone wokół tych elementów. Niektóre z nich są rzeczywiście przydatne, ale sama istota rozwiązania mieści się w około osiemdziesięciu liniach kodu Pythona, a tak krótki kod będzie nadal funkcjonować długo po tym, jak dana platforma ulegnie zmianie lub zniknie.
Krok 1: Zbierz sto rzeczywistych wejść
Weź dane z swoich logów produkcyjnych. Unikaj przykładów syntetycznych, przypadków, które już wiesz, że działają pomyślnie, oraz przykładów pooczyszczonych. Sto przypadków to wystarczająca liczba, by odkryć prawdziwe problemy, a jednocześnie taka mała, by później móc je ręcznie oznaczyć, co okazuje się kluczowe. Celowo dodaj trudne i skomplikowane dane wejściowe – zbiór przypadków łatwych daje wyniki stałe, nawet gdy twoje systemy tracą wydajność.
Ładowanie ich jest proste:
import json
with open("requests.jsonl") as f:
cases = [json.loads(line) for line in f][:100]
Zauważ, że wybieranie pierwszych stu wierszy obejmuje wszystko, co znajduje się na początku pliku – może to pochodzić z jednego dnia lub od jednego klienta. Przemieszczenie danych przed ich wybraniem lub pobieranie przykładów z różnych okresów czasowych daje bardziej reprezentatywny zbiór.
Jeśli jeszcze nie masz żadnych logów, sam napisz te sto przypadków, ale spodziewaj się, że wyniki będą lepsze od rzeczywistości, dopóki nie zastąpią ich prawdziwy ruch użytkowników.
Krok 2: Oddzielenie sprawdzeń kodu od decyzji
To krok, który zespoły najczęściej pomijają, a który wpływa na wszystko, co następuje później. Wiele właściwości można zweryfikować w sposób deterministyczny, a takie wartości nigdy nie powinny być przekazywane modelowi:
- Wynik to ważny JSON.
- Kategoria należy do znanego zbioru.
- Liczba mieści się w dozwolonym zakresie.
- Obecne są wymagane pola.
- Odpowiedź nie przekracza limitu długości.
Każda z tych kwestii stanowi twierdzenie. Poniższa funkcja (w Pythonie, niezależnie od oznaczenia) wykonuje kilka z nich i zwraca słownik z nazwami wyników:
def code_checks(output):
checks = {}
try:
parsed = json.loads(output)
checks["valid_json"] = True
checks["has_fields"] = all(k in parsed for k in ("answer", "confidence"))
except json.JSONDecodeError:
checks["valid_json"] = False
checks["has_fields"] = False
checks["under_limit"] = len(output) < 2000
return checks
Zwracanie nazwanych sprawdzeń zamiast pojedynczej wartości logicznej pozwala zobaczyć, która właściwość zawiodła w przypadku błędu. To, co pozostaje po sprawdzeniach deterministycznych, to część wymagająca osądu: czy odpowiedź odpowiadała na rzeczywiste pytanie, czy zawierała fakty nieobecne w źródle, czy odmówiła, gdy powinna była odpowiedzieć? Te właściwości potrzebują sędziego, a sędzia wymaga weryfikacji.
Krok 3: Napisz sędziego, który ocenia jedną rzecz
Dla każdej funkcji sędziego podaj dokładnie jeden kryterium. Rubryka wymagająca jednocześnie pięciu cech daje połączoną ocenę, a w przypadku błędu nie można stwierdzić, która cecha była brakująca. Poniższy sędzia sprawdza jedną właściwość, niepoparte twierdzenia oraz wymaga określonego formatu odpowiedzi:
JUDGE = """You are grading one property of a response.
PROPERTY: Does the response contain any claim that is not supported by the
source text provided?
SOURCE:
{source}
RESPONSE:
{response}
Answer with exactly one word, PASS or FAIL, then a new line, then one
sentence explaining your verdict. Do not explain anything else."""
def judge(source, response, call_model):
out = call_model(JUDGE.format(source=source, response=response))
verdict = out.strip().split("\n")[0].strip().upper()
return verdict == "PASS", out
Warunek formatu ma takie samo znaczenie jak sam kryterium. Wymaganie umieszczenia na pierwszym wierszu pojedynczego słowa – PASS lub FAIL – ułatwia analizę wyniku. Werdykty w formie swobodnej różnią się strukturą w zależności od przypadku, a jeśli błąd podczas analizy zostanie potraktowany jako sukces, wszystkie późniejsze oceny będą zawyżone, bez żadnej reakcji. Ta implementacja wybiera drogę ostrożności: wszystko inne niż dokładne PASS, w tym PASS. lub werdykt umieszczony w ramkach formatowania, jest uznawane za niepowodzenie. Warto rejestrować, jak często to się zdarza, ponieważ nagły wzrost liczby nieprzetworzonych odpowiedzi sam w sobie jest sygnałem.
Krok 4: Uruchom wszystko i zachowaj surowy wynik
Zachowaj pełny wynik oraz wyjaśnienie sędziego dla każdego przypadku, a nie tylko werdykt. Gdy ocena się zmieni, będziesz chciał wiedzieć, co dokładnie się zmieniło, a zapisany wartość logiczna tego nie pokaże.
results = []
for case in cases:
output = call_model(case["prompt"])
passed, reasoning = judge(case["source"], output, call_model)
results.append({
"id": case["id"],
"output": output,
"code_checks": code_checks(output),
"judge_pass": passed,
"judge_reasoning": reasoning,
})
rate = sum(r["judge_pass"] for r in results) / len(results)
print(f"{rate:.1%} pass on {len(results)} cases")
Pętla rejestruje sprawdzenia deterministyczne wraz z wynikiem sędziego, chociaż podstawowa stopa uwzględnia tylko sędziego. W praktyce należałoby podać oba wyniki, ponieważ odpowiedź, która nie przechodzi walidacji JSON, jest błędem bez względu na opinię sędziego. Należy również zauważyć, że ta sama funkcja call_model jest tu używana do generowania i oceniania wyników; powszechnym rozwiązaniem jest użycie innego lub lepszego modelu dla sędziego, ale niezależnie od wyboru, to kolejny krok decyduje o jego wiarygodności.
W tym momencie masz procent. Nie oznacza on jeszcze nic konkretnego.
Krok 5: Kalibracja sędziego na podstawie własnych etykiet
To krok, którego najczęściej pomijają przewodniki, i trwa on około trzydziestu minut. Wybierz pięćdziesiąt z stu przypadków i sam oznacz każdy z nich jako PASS lub FAIL, nie patrząc najpierw na werdykt sędziego. Następnie porównaj obie grupy etykiet:
agree = sum(1 for r, human in zip(results[:50], human_labels)
if r["judge_pass"] == human)
print(f"judge agrees with me on {agree}/50 = {agree/50:.0%}")
both_fail = sum(1 for r, h in zip(results[:50], human_labels)
if not r["judge_pass"] and not h)
judge_fails = sum(1 for r in results[:50] if not r["judge_pass"])
human_fails = sum(1 for h in human_labels if not h)
print(f"judge caught {both_fail}/{human_fails} of the failures I found")
print(f"judge flagged {judge_fails - both_fail} things I considered fine")
Skrypt pokazuje trzy wskaźniki: ogólną zgodność, liczbę błędów, które wykrył również sędzia, oraz liczbę przypadków, w których sędzia pomylił się, a które uznaliśmy za akceptowalne. Kod zakłada, że human_labels to lista wartości logicznych w tej samej kolejności co pierwsze pięćdziesiąt wyników, i doprowadzi do dzielenia przez zero, jeśli w ogóle nie znaleziono żadnych błędów, co samo w sobie jest wskazówką, że zestaw danych jest zbyt łatwy.
Dlaczego surowa zgodność myli
Ogólna zgodność jest mniej wiarygodnym wskaźnikiem, mimo że najczęściej jest cytowana. Jeśli dziewięćdziesiąt procent Twoich przypadków zostanie uznanych za poprawne, sędzia, który na wszystko odpowiada „TAK”, osiągnie dziewięćdziesiąt procent zgodności, nie wykrywając przy tym niczego.
Najważniejsza liczba: wykryte błędy
To, na co należy zwrócić uwagę, to udział twoich własnych etykiet FAIL, które również zostały zaznaczone przez sędziego. W terminologii klasyfikacji jest to stopień wykrywania błędów przez sędziego. Sędzia, który zauważy dwa z jedenastu twoich błędów, w rzeczywistości nic nie ocenia – po prostu zatwierdza wszystko, dołączając przekonujące wyjaśnienie. Trzeci element, fałszywe alarmy, również ma znaczenie, ponieważ sędzia, który zaznacza poprawne odpowiedzi, może skłonić cię do „naprawiania” rzeczy, które nigdy nie były uszkodzone.
Gdy ty i sędzia się nie zgadzacie
Przeczytaj każdy przypadek, w którym twoja etykieta różni się od tej sędziego. Istnieją tylko trzy możliwe wyjaśnienia, a każde z nich ma swoje rozwiązanie.
Kryteria oceny są niejednoznaczne
Sędzia się myli, ponieważ opis właściwości pozostawia miejsce na interpretację. Musisz go sprecyzować. Dobrym testem jest to, czy potrafisz sformułować tę właściwość w jednym zdaniu, tak aby nowy kolega mógł ją zastosować dokładnie tak samo jak ty.
Wasze własne etykiety są błędne
Dzieje się to częściej, niż ludzie przypuszczają. Ludzkie oceny zmieniają się w ciągu pięćdziesięciu przypadków, szczególnie w przypadku przykładów na granicy między kategoriami pod koniec sesji. Przed obwinianiem sędziego przeprojektujcie etykiety dla spornych przypadków z nowej perspektywy.
Ta cecha jest z natury subiektywna
Niektóre właściwości nie mogą być spójnie oceniane przez żadną osobę. Jeśli tak jest, żaden sędzia nie będzie wiarygodny. Albo podzielcie tę cechę na mniejsze, możliwe do sprawdzenia podcechy, albo zaakceptujcie, że nie da się jej ocenić.
Powtarzaj proces: dostosuj rubrykę, przemień etykiety tam, gdzie to konieczne, i uruchom ponownie, aż sędzia zauważy wystarczająco dużo twoich błędów, byś musiał je bronić podczas spotkania. Następnie zamroź rubrykę i przechowaj jej wersję razem z każdym kolejnym wynikiem. Wyniki oceniane za pomocą różnych rubryk nie mogą być porównywane. Aby dowiedzieć się, jak utrzymać „zdrowie” sędziego podczas ciągłego używania go w produkcji, zapoznaj się z zarządzaniem LLM jako sędzią jako żywym systemem produkcyjnym.
Co daje Ci skalibrowane ocenianie
Wynikiem jest liczba, którą możesz umieścić obok danej zmiany i w której możesz mieć pewność. Przed modyfikacją promptu, zmianą modelu lub kroku wyszukiwania wykonujesz pomiar; potem robisz kolejny. Różnica ta zawiera informacje tylko dlatego, że najpierw potwierdziłeś, iż narzędzie zgadza się z oceną osoby.
Ponieważ kod nie zależy od niczego poza funkcją, która wywołuje twój model, nadal działa przy zmianie dostawcy lub gdy ekosystem narzędzi ulega przetasowaniu, co jest większym atutem niż w przypadku większości rozwiązań zalecanych do tego zadania.
Ograniczenia oceny stu przypadków
Przed zastosowaniem tego podejścia warto zrozumieć trzy ograniczenia.
Mniejsze regresje są niewidoczne
Spadek z 91 na 89 procent oznacza dwa przypadki na sto, co przy takiej wielkości próby jest nierozróżnialne od szumu. Aby wykryć małe efekty, potrzebujesz albo znacznie większej ilości danych, albo porównania parowego: uruchom obie wersje na tych samych danych i policz tylko te przypadki, których wynik uległ zmianie. Porównania parowe eliminują większość zmiennych wynikających z trudności przypadków.
Ręczne etykietowanie nie jest skalowalne
Etykiety ręczne tracą praktyczność po kilkuset wierszach, a jakość pogarsza się w miarę trwania sesji. Twoja ocena przypadku numer czterdzieści osiem nie jest taka sama jak w przypadku numer dwa, co wprowadza rzeczywisty błąd do samej kalibracji. Etykietowanie w kilku krótkich sesjach zamiast jednej długiej zmniejsza ten odchylenie.
Kalibracja wygasa
Sędzia zweryfikowany na podstawie dzisiejszych wyników może nie być już odpowiedni dla jutrzejszych. Gdy zmieniasz model używany do analizy danych, zmieniają się również jego sposoby awarii, dlatego sędziego trzeba ponownie zweryfikować, zamiast go używać ponownie. To pracochłonne zadanie i właśnie to odróżnia pomiary od rytuału.
Dwie uzupełnienia po uruchomieniu
- Zapisuj wersję wszystkiego. Przechowuj wersję modelu oraz wersję kryteriów oceny razem z każdym wynikiem. Wynik, w którym brakuje jednej z tych informacji, nie może być porównany z żadnym innym wynikiem. Z tego samego powodu wartość referencyjna bez odpowiednich uzupełnień niewiele mówi.
- Zachowuj zestaw przypadków niezgody. Przypadki, w których ty i sędzia mieliście różne opinie, to najbardziej informacyjne wiersze w całym zadaniu. Ponowne przeprowadzenie ich po każdej zmianie kryteriów to najszybszy sposób, aby sprawdzić, czy zmiana przyniosła korzyść.
Tutaj przedstawione przykłady kładą nacisk na klarowność, a nie na maksymalną odporność systemu. Traktuj je jako szablon do przeczytania i dostosowania, dodając powtórzenia prób, obsługę błędów oraz mechanizmy przechowywania danych w zależności od wymagań twojego setupu.
Główne wnioski
- Ocena to zbiór danych, generator wyników oraz narzędzie do ich oceny; wszystko inne to opcjonalne elementy.