BenchmarkiPodstawy

Scorer a benchmark — co właściwie mierzysz

3 min czytaniaaktualizacja: 11 października 2026

Dwa obiekty, które lubimy mylić

„Model X pobił benchmark Y" — tak wygląda większość nagłówków, ale to zdanie zlewa w jedno dwa różne obiekty. Jeśli chcesz rozumieć tablice wyników, rozdziel je na starcie.

  • Benchmark to kontekst pomiaru: zbiór zadań, warunki, w jakich model odpowiada, oraz rozwiązania referencyjne. MMLU-Pro to tysiące pytań wielokrotnego wyboru z dziesięcioma opcjami; HumanEval to zadania programistyczne z testami jednostkowymi; SWE-bench to realne zgłoszenia błędów z repozytoriów Pythona. Benchmark mówi, na czym testujesz.
  • Scorer to funkcja, która zamienia odpowiedzi modelu w liczbę albo w relację porządku („ta odpowiedź jest lepsza"). Scorer mówi, jak zamieniasz odpowiedzi na wynik.

Konsekwencja jest praktyczna: ten sam benchmark można policzyć różnymi scorerami i dostać różne rankingi, nie zmieniając ani jednego zadania. Dlatego „wynik na benchmarku" bez nazwy scorera nie jest dobrze zdefiniowaną liczbą. Więcej o tym rozróżnieniu znajdziesz w nocie Scorer a benchmark.

Ten sam benchmark, inne scorery

MMLU-Pro: exact match kontra accuracy

Weź MMLU-Pro. Pytanie ma dziesięć opcji i model ma wskazać jedną literę. Ten sam zbiór można policzyć na co najmniej dwa sposoby:

  • Accuracy (wybór opcji) — wybierasz odpowiedź, której token ma największe prawdopodobieństwo, i sprawdzasz, czy litera się zgadza. To naturalny scorer dla API zwracających log-prawdopodobieństwa.
  • Exact match (dopasowanie tekstu) — porównujesz cały wygenerowany tekst z oczekiwaną odpowiedzią. Jeśli model poprzedza odpowiedź łańcuchem myślenia („Rozważmy… zatem odpowiedź to C)"), surowe porównanie tekstu zawiedzie, dopóki nie znormalizujesz wyjścia i nie wytniesz samej litery.

Formalnie Exact match to:

EM=1N∑i=1N1[y^i=yi]EM = \frac{1}{N}\sum_{i=1}^{N}\mathbb{1}\left[\hat{y}_i = y_i\right]

gdzie NN to liczba zadań, y^i\hat{y}_i — znormalizowana odpowiedź modelu, yiy_i — odpowiedź referencyjna, a wskaźnik 1[⋅]\mathbb{1}[\cdot] przyjmuje 1, gdy teksty są identyczne, i 0 w przeciwnym razie.

Minimalna implementacja wygląda tak:

def exact_match(predictions: list[str], references: list[str]) -> float:
    """Udział odpowiedzi identycznych z referencją (po normalizacji)."""
    assert len(predictions) == len(references)
    hits = sum(1 for pred, ref in zip(predictions, references) if pred == ref)
    return hits / len(references)
 
preds = ["C", "B", "A", "C"]
refs = ["C", "D", "A", "B"]
print("EM =", round(exact_match(preds, refs), 2))  # EM = 0.5

Na tym samym zbiorze accuracy liczona z log-prob i EM po normalizacji mogą się różnić — a przy odpowiedziach z łańcuchem myślenia o znacznie więcej. To wystarczy, żeby dwa zespoły raportowały różne liczby dla tego samego modelu.

SWE-bench: resolved rate

Zadania otwarte wymagają innego pomysłu. W SWE-bench model dostaje issue i repozytorium, a jego łatka przechodzi przez testy: wszystkie testy FAIL_TO_PASS muszą zacząć przechodzić, a PASS_TO_PASS nie mogą się zepsuć. Scorer resolved rate to odsetek zadań w pełni rozwiązanych:

resolved rate=zadania z przechodzącymi testamiwszystkie zadaniaresolved\ rate = \frac{\text{zadania z przechodzącymi testami}}{\text{wszystkie zadania}}

Nie ma punktów cząstkowych: zadanie jest rozwiązane albo nie. Dlatego „30% na SWE-bench" znaczy coś zupełnie innego niż „70% na MMLU-Pro" — pierwsza liczba dotyczy kompletnych, działających łatek, druga pojedynczych wskazań litery.

Dlaczego pass@k to co innego niż wynik quizu

Na HumanEval model ma dokończyć funkcję tak, by przechodziła testy. Surowe „czy ta jedna odpowiedź jest poprawna" bywa zbyt twarde dla modeli próbkujących różne rozwiązania, dlatego standardem jest pass@k: zadanie uznaje się za rozwiązane, jeśli wśród kk wylosowanych próbek znajdzie się choć jedna przechodząca testy. Nie oceniasz pojedynczej odpowiedzi — oceniasz zdolność modelu do wygenerowania poprawnego rozwiązania przy budżecie kk prób.

Nieobciążony estymator pass@k ma postać:

pass@k=1−(n−ck)(nk)pass@k = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}}

gdzie nn to liczba próbek wygenerowanych na zadanie, cc — liczba próbek poprawnych, a kk — rozmiar ocenianego podzbioru. Kombinatoryka liczy prawdopodobieństwo, że losowe kk próbek nie zawiera żadnej poprawnej; dopełnienie daje szansę trafienia.

Dwie konsekwencje. Po pierwsze, pass@k rośnie z kk — to parametr budżetu, nie właściwość modelu; pass@1, pass@5 i pass@100 to trzy różne liczby dla tego samego modelu. Po drugie, nie porównuj pass@k z wynikiem na MMLU: 80% pass@1 znaczy „osiem na dziesięć zadań da się rozwiązać za pierwszym podejściem", a 80% na MMLU znaczy „osiem na dziesięć pytań zamkniętych wskazano poprawnie". Inna skala trudności, inna natura odpowiedzi, inny scorer.

Zasady, które warto stosować

  • Podawaj parę (benchmark, scorer): „MMLU-Pro przez exact match", a nie „wynik na MMLU-Pro".
  • Porównuj modele wyłącznie w obrębie tego samego scorera — zmiana normalizacji potrafi przetasować ranking.
  • Nie mieszaj metryk zadań zamkniętych (EM, accuracy) z metrykami zadań otwartych (pass@k, resolved rate).
  • Nie ufaj liczbie bez wersji zbioru: SWE-bench Verified to nie to samo co pełny SWE-bench.

Źródła

Scorer a benchmark — co właściwie mierzysz — ashigiri