Metriken: Accuracy, Recall@k und MRR

Track KI · M3 Evaluation, Baustein 02 und Abschluss-Check · ca. 50 Min. plus Projektaufgabe

Worum es geht

Mit einem Golden Set misst du. Dafür brauchst du Zahlen, die dich nicht anlügen. In dieser Lektion rechnest du die Standardmetriken in reinem Python: Accuracy, Precision und Recall für einen Fehler-Prüfer, Recall@k und MRR für die Suche und eine eigene Messung für Fragen, auf die das System gar nicht antworten soll. Die Projektaufgabe am Ende ist der Abschluss-Check von Baustein 01 und 02.

Was du aus Teil a brauchst: Du weißt, was ein Golden Set ist (feste Fragen mit erwarteter Quelle, erwartete_quelle=None heißt: keine Antwort im Korpus) und dass Randfälle hineingehören. Die Datenstruktur TestFall und golden_set_probleme aus Teil a brauchst du nur für die Projektaufgabe.

Alle Fälle in dieser Lektion sind erfunden, es läuft kein echtes LLM. Zeitplan: etwa 15 Minuten Lesen und 35 Minuten für die drei Übungen, zusammen rund 50 Minuten plus Projektaufgabe.

Was du am Ende kannst:

  • Accuracy, Precision, Recall, Recall@k und MRR von Hand und im Code berechnen
  • erklären, warum Accuracy bei seltenen Fehlern täuscht
  • Fragen ohne Antwort im Korpus getrennt auswerten

Von JS/TS her gedacht

Idee JS/TS Python
Position in Liste arr.indexOf(x) gibt -1, wenn nicht gefunden liste.index(x) wirft ValueError, wenn nicht gefunden
Anteil berechnen n / total (bei 0 / 0 kommt NaN) n / total (bei 0 / 0 wirft Python ZeroDivisionError)
Mittelwert xs.reduce((a, b) => a + b, 0) / xs.length sum(xs) / len(xs)

Beide Unterschiede zählen bei Recall@k und MRR: Prüfe mit x in liste, bevor du .index(x) aufrufst, sonst stürzt dein Evaluationslauf an der ersten Frage ab, die nicht gefunden wurde. Genau die willst du ja zählen. Und eine Division durch null musst du selbst abfangen.

Konzept

Schritt 1: Klassifikations-Metriken

Die Quelle nennt für Baustein 01 und 02 noch keine Formeln, hier die Standarddefinitionen (über die Quelle hinaus, allgemeines Fachwissen). Zuerst die Klassifikations-Metriken. Beispiel: Ein Prüfer soll melden, ob eine Antwort fehlerhaft ist. “Positiv” heißt hier: Fehler gemeldet. Es gibt vier Fälle:

Prüfer meldet Fehler Prüfer meldet keinen Fehler
Antwort hat wirklich einen Fehler TP (true positive) FN (false negative)
Antwort ist in Ordnung FP (false positive) TN (true negative)
  • Accuracy = (TP + TN) / alle Fälle: Anteil der richtigen Urteile
  • Precision = TP / (TP + FP): Von allem, was der Prüfer meldet, wie viel ist wirklich ein Fehler?
  • Recall = TP / (TP + FN): Von allen echten Fehlern, wie viele findet der Prüfer?

TP 2, FP 2, FN 1, TN 5: Accuracy 0.7, Precision 0.5, Recall etwa 0.667. Achtung: tp / (tp + fp) teilt durch null, wenn der Prüfer nie etwas meldet. In eigenem Code brauchst du dafür eine Regel (z. B. 0.0 zurückgeben) und musst sie dokumentieren.

Schritt 2: Metriken für die Suche

Jetzt die Metriken für die Suche (retrieval). Hier hat jede Frage eine erwartete Quelle (aus dem Golden Set) und die Suche liefert eine Rangliste gefundener Dokumente:

  • Recall@k: Anteil der Fragen, bei denen die erwartete Quelle unter den ersten k Treffern steht
  • MRR (mean reciprocal rank): Mittelwert von 1 / Rang des ersten richtigen Treffers (Rang zählt ab 1, nicht gefunden ergibt 0)

Nicht verwechseln mit dem Recall in Lektion 09: Dort war Recall der Anteil der wirklichen nächsten Nachbarn, den ein Näherungsindex findet. Hier ist es der Anteil der Fragen, deren erwartetes Dokument unter den ersten k auftaucht.

Rang 2, 1, nicht gefunden, 3. Recall@3 ist 0.75 (drei von vier), Recall@2 ist 0.5, MRR ist (0.5 + 1.0 + 0 + 0.333) / 4, also etwa 0.458. Recall@k fragt nur “unter den ersten k, ja oder nein”. MRR belohnt zusätzlich, dass der richtige Treffer weit vorn steht. Fragen ohne Antwort im Korpus (erwartete_quelle=None) haben keinen richtigen Treffer und gehören nicht in diese beiden Metriken. Die prüfst du anders (z. B. “hat das System korrekt gesagt, dass es keine Antwort gibt?”).

Schritt 3: Fragen ohne Antwort getrennt auswerten

Fragen ohne Antwort im Korpus fehlen in Recall@k und MRR. Sie brauchen eine eigene Messung: Für jede Frage notierst du, ob das System ausdrücklich keine Antwort gegeben hat (“Dazu steht nichts im Korpus”, abgelehnt=True). Daraus ergeben sich zwei Zahlen, jede mit einem eigenen Nenner:

  • korrekt abgelehnt: Von den Fragen ohne Antwort im Korpus (erwartet is None): Anteil, bei denen das System abgelehnt hat. Hoch ist gut. Das Gegenstück ist die erfundene Antwort.
  • zu Unrecht abgelehnt: Von den Fragen mit Antwort im Korpus: Anteil, bei denen das System trotzdem abgelehnt hat. Niedrig ist gut.

Von 3 Fragen ohne Antwort hat das System 2 korrekt abgelehnt (0.667), und von 2 Fragen mit Antwort hat es 1 zu Unrecht abgelehnt (0.5). Beide Zahlen zusammen zeigen, ob ein System nur “vorsichtig” ist (lehnt alles ab) oder wirklich zwischen beiden Fällen unterscheidet.

Falle

Falle 1: Metrik ohne Varianz (über die Quelle hinaus, allgemeines Fachwissen). Eine Metrik, die für alle Varianten denselben Wert liefert, kann keine Änderung unterscheiden. Typische Ursachen: ein zu leichtes Set (alle bestehen) oder ein Prüfer, der immer dasselbe sagt. Zwei erfundene Prompt-Varianten auf einem leichten und auf einem Set mit Randfällen:

Auf dem leichten Set sind beide 1.0: Du kannst nicht sagen, welcher Prompt besser ist. Erst die Randfälle (hier zwei Fälle, die A verpasst und B zum Teil schafft) trennen sie. Eng verwandt: Bei sehr seltenen Fehlern täuscht die Accuracy. Wie die drei Klassifikations-Metriken zueinander stehen, rechnest du in Übung 1.

Falle 2: Accuracy bei seltenen Fehlern. Ist nur 1 von 20 Antworten fehlerhaft, hat ein Prüfer, der nie etwas meldet, eine Accuracy von 0.95. Er findet keinen einzigen Fehler. Darum gehören Precision und Recall immer daneben.

Falle 3: Division durch null. tp / (tp + fp) ist undefiniert, wenn nie etwas gemeldet wird. Lege eine Regel fest (z. B. 0.0) und dokumentiere sie.

Übungen

Übung 1: Metriken berechnen (mittel, ca. 12 Min.)

Von 20 Antworten hat genau eine einen echten Fehler. Zwei Prüfer sollen Fehler melden (wie in Schritt 1). Prüfer A meldet nie etwas. Prüfer B meldet bei vier Antworten einen Fehler, darunter ist der echte. Trage ein Tupel mit fünf Zahlen ein: (accuracy_A, recall_A, accuracy_B, precision_B, recall_B). Du darfst rechnen, auch im Code. Eine Toleranz von 0.005 ist erlaubt (zwei Nachkommastellen reichen).

Zähle für jeden Prüfer getrennt die vier Felder der Tabelle (TP, FP, FN, TN). Dann hat jede Metrik einen anderen Nenner: alle Fälle, alle Meldungen, alle echten Fehler. Vergleiche am Ende die Accuracy der beiden Prüfer mit dem, was sie tatsächlich leisten.

echt = [0] * 20
echt[7] = 1
gemeldet_a = [0] * 20
gemeldet_b = [0] * 20
for i in (2, 7, 11, 15):
    gemeldet_b[i] = 1
antwort = (0.95, 0.0, 0.85, 0.25, 1.0)
antwort

Übung 2: Recall@k und MRR schreiben (mittel, ca. 15 Min.)

Schreibe retrieval_metriken(faelle, k). faelle ist eine Liste von Dicts {"erwartet": <Quelle oder None>, "gefunden": [<Quelle>, ...]} (Rangliste, bester Treffer zuerst). Rückgabe: Tupel (recall_at_k, mrr). Regeln:

  • Fälle mit erwartet is None (Frage ohne Antwort im Korpus) werden übersprungen und zählen nicht im Nenner.
  • Recall@k: Anteil der Fälle, bei denen erwartet unter den ersten k Einträgen von gefunden steht.
  • MRR: Mittelwert von 1 / Rang (Rang ab 1) des ersten Auftretens von erwartet in der ganzen Liste, 0 wenn es nicht vorkommt.
  • Bleibt kein Fall übrig, gib (0.0, 0.0) zurück.

Beispiele: [{"erwartet": "A", "gefunden": ["B", "A"]}] mit k=1 ergibt (0.0, 0.5). Mit k=2 ergibt es (1.0, 0.5).

Überlege, welche Fälle in den Nenner gehören und welche nicht. Der Rang des ersten Treffers entscheidet über beide Zahlen. Schau dir das Rechenbeispiel zu Recall@3 und MRR in Schritt 2 noch einmal an.

def retrieval_metriken(faelle, k):
    gueltig = [f for f in faelle if f["erwartet"] is not None]
    if not gueltig:
        return (0.0, 0.0)
    treffer = 0
    summe_rr = 0.0
    for f in gueltig:
        if f["erwartet"] in f["gefunden"]:
            rang = f["gefunden"].index(f["erwartet"]) + 1
            summe_rr += 1 / rang
            if rang <= k:
                treffer += 1
    return (treffer / len(gueltig), summe_rr / len(gueltig))
retrieval_metriken

Übung 3: Fragen ohne Antwort auswerten (mittel, ca. 8 Min.)

Schreibe ohne_antwort_auswerten(fragen). fragen ist eine Liste von Dicts {"erwartet": <Quelle oder None>, "abgelehnt": <True oder False>} (wie in Schritt 3). Rückgabe: Tupel (korrekt_abgelehnt, zu_unrecht_abgelehnt), beide als Anteil zwischen 0 und 1. Hat eine der beiden Gruppen keine Fragen, ist ihre Zahl 0.0.

Beispiel: [{"erwartet": None, "abgelehnt": True}, {"erwartet": "A", "abgelehnt": False}] ergibt (1.0, 0.0).

Teile die Fragen zuerst in zwei Gruppen. Jede Zahl hat ihren eigenen Nenner, nämlich die Größe ihrer Gruppe, nicht die aller Fragen. Was passiert bei einer leeren Gruppe?

def ohne_antwort_auswerten(fragen):
    ohne = [f for f in fragen if f["erwartet"] is None]
    mit = [f for f in fragen if f["erwartet"] is not None]
    korrekt = sum(1 for f in ohne if f["abgelehnt"]) / len(ohne) if ohne else 0.0
    unrecht = sum(1 for f in mit if f["abgelehnt"]) / len(mit) if mit else 0.0
    return (korrekt, unrecht)
ohne_antwort_auswerten

Projektaufgabe (Abschluss-Check der Quelle, ohne Browser-Check)

Die Quelle gibt zwei Aufgaben vor, beide mit echten Daten aus dem RAG-System aus M2:

  1. Fehleranalyse: Sammle 20 echte Antworten, kategorisiere jeden Fehler in höchstens 5 selbst gewählten Kategorien und zähle, welche am häufigsten ist.
  2. Golden Set: Baue 20 Fälle aus dem CGMES/BDEW-Korpus: 15 typische Fragen und 5 bewusste Randfälle, davon mindestens eine Frage ohne Antwort im Korpus.

Sinnvoller Aufbau: eine Tabelle (z. B. fehler.csv) mit den vier Beobachtungen aus Schritt 1 von Teil a, kategorie() und Counter aus Teil a, dazu die Klasse TestFall und golden_set_probleme() aus den Übungen 2 und 3 von Teil a als Prüfung deines eigenen Sets. Mit retrieval_metriken und ohne_antwort_auswerten aus diesem Teil misst du dein Set danach. Die Fälle ins Golden Set kommen aus echten Fragen und aus Fehlern, die du in der Fehleranalyse gefunden hast. Die Kategorien und die Reihenfolge der Entscheidung bei Mehrfachmängeln legst du selbst fest und schreibst sie auf.

Fertig, wenn:

Die Dateien gehören in dein M2-Projekt, nicht in die Lernplattform. Eine Lokal-Übung gibt es in dieser Lektion nicht, weil alles im Browser läuft und kein echtes LLM gebraucht wird.

Merksatz

Accuracy allein täuscht bei seltenen Fehlern: Lies immer Precision und Recall daneben, nimm für die Suche Recall@k und MRR, und werte Fragen ohne Antwort mit eigenem Nenner aus.

Prüfstein

Ein Prüfer meldet bei 100 Antworten (5 davon fehlerhaft) nie einen Fehler und hat 95 Prozent Accuracy. Dein Chef findet das gut. Was antwortest du, welche zwei Zahlen forderst du zusätzlich, und was bedeutet für die Suche “Recall@3 ist 0.8, aber MRR nur 0.4”?


Quelle: quellen/kursbuch-lerninhalte.md, Modul M3, Baustein “02 Testdatensätze (Golden Set)” (Zeilen 611 bis 655) und der Abschluss-Check mit den beiden Aufgaben (Fehleranalyse mit 20 echten Antworten, Golden Set mit 20 Fällen). Die Quelle nennt für Baustein 01 und 02 keine Formeln. Über die Quelle hinaus (allgemeines Fachwissen): die Definitionen von Accuracy, Precision, Recall, Recall@k und MRR, die Regel, dass Fragen ohne Antwort aus Recall@k und MRR herausfallen, die getrennte Auswertung “korrekt abgelehnt” und “zu Unrecht abgelehnt”, die Stolperfallen “Metrik ohne Varianz”, “Accuracy bei seltenen Fehlern” und “Division durch null”. Alle Fallbeispiele (Fälle, Quellen-IDs, Prüfer) sind von Hand ausgedacht und keine echten Messungen. Alle Python-Beispiele wurden mit Python 3.13 ausgeführt.