Werkplaats / De werkplaats
In jouw eigen tempo
← Werken met LLM’s
MODULE 02 · LES 07

Controleer ook de beoordelaar

Verwijder modelnamen, wissel antwoordposities en meet overeenstemming met een aparte referentie.

60 min3 checkpoints
DIT GA JE ONTDEKKEN
  • Je koppelt A/B-stemmen terug naar de juiste variant na het omwisselen van antwoordposities.
  • Je onderscheidt positieconsistentie, overeenstemming en inhoudelijke juistheid.

Een tweede taalmodel kan antwoorden beoordelen met een rubric: een LLM-as-judge. Dat kan helpen bij het screenen van veel uitvoer, maar introduceert ook een tweede foutgevoelig model. Een overtuigende beoordeling is nog geen onafhankelijk bewijs.

Onderzoek beschrijft onder meer voorkeur voor antwoordpositie, lengte en eigen stijl. Maak criteria concreet, verberg irrelevante modelnamen en controleer een steekproef door mensen. De stemmen hieronder zijn synthetisch; we meten geen echte modelkwaliteit.

1. Vertaal de positie terug naar de identiteit

A betekent het eerste getoonde antwoord en B het tweede. Toon je de antwoorden in omgekeerde volgorde, dan wisselt de betekenis van die letters mee. Bewaar daarom de presentatievolgorde buiten de beoordelaarsprompt.

ToonStemID
m1, m2Am1
m2, m1Am2
m2, m1Bm1
print(map_judge_vote(['m1','m2'],'A'))
# m1
print(map_judge_vote(['m2','m1'],'A'))
# m2
print(map_judge_vote(('m2','m1'),'B'))
# m1
print(map_judge_vote(['m1','m2'],'tie'))
# None

Maak map_judge_vote voor een lijst of tuple met twee verschillende model-IDs. Een stem is exact A, B of tie. Een tie betekent geen voorkeur en wordt None, niet automatisch een stem voor de eerste variant.

Blindering verwijdert modelnamen, maar niet alle herkenbare stijlkenmerken. De mapping lost de koppeling in je applicatie op; ze bewijst niet dat de beoordelaar vrij van voorkeuren is. Vergelijk daarom beide presentatievolgordes.

01
CHECKPOINTA is een positie, geen modelnaam

2. Vergelijk winnaars na de terugmapping

Forward toont model-1 gevolgd door model-2; reverse toont model-2 gevolgd door model-1. A/B kiest dan tweemaal model-1 en is consistent. A/A kiest twee verschillende modellen en is inconsistent, hoewel de letters gelijk zijn.

votes = [
    dict(id='a',forward='A',
         reverse='B'),
    dict(id='b',forward='A',
         reverse='A'),
    dict(id='c',forward='tie',
         reverse='tie')]
r = judge_order_audit(votes)
print(r['consistent'],r['total'])  # 2 3
for row in r['rows']:
    print(row['winner'])
# model-1
# None
# None

Maak judge_order_audit met één resultaat per uniek geval. Twee ties zijn consistent zonder winnaar. Bij tegenspraak blijft winner None; forceer geen beslissing uit een conflict.

Consistentie is het aantal consistente gevallen gedeeld door alle gevallen. Inconsistentie kan ook door sampling of onduidelijke criteria ontstaan. Deze proef maakt een probleem zichtbaar, maar stelt de oorzaak niet automatisch vast.

SYNTHETISCHE STEMMEN · GEEN MODELKWALITEITSMETINGWissel posities, behoud de identiteit

Hetzelfde antwoordpaar staat twee keer in een andere volgorde. Kies stemmen en vergelijk de terugvertaalde model-ID’s.

Forward
Positie Amodel-1Positie Bmodel-2

Gekozen identiteitmodel-1

Reverse
Positie Amodel-2Positie Bmodel-1

Gekozen identiteitmodel-2

Verschillende winnaarsGeen winnaar vastleggen: onderzoek het verschil.

A/A is hier inconsistent. A/B kiest tweemaal model-1. Consistentie bewijst geen inhoudelijke juistheid en inconsistentie bewijst niet automatisch positievoorkeur.

02
CHECKPOINTTwee gelijke letters kunnen juist inconsistent zijn

3. Vergelijk met een afzonderlijke referentie

Een consistente beoordelaar kan systematisch verkeerd zitten. Vergelijk daarom met een apart vastgelegde referentie, bijvoorbeeld een zorgvuldig geannoteerde steekproef. In de volgende arrays staan A en B al voor vaste variantidentiteiten, na de terugmapping.

Neem referentie [A,A,B,B] en judge [A,B,B,B]. Drie van vier stemmen komen overeen: pₒ=.75. De referentie kiest A en B elk voor de helft; de judge kiest A voor een kwart en B voor driekwart.

De verwachte overeenkomst uit die twee verdelingen is pₑ=.5×.25+.5×.75=.5. Cohen-kappa corrigeert de waargenomen overeenkomst voor deze verwachte overeenkomst:

κ=po−pe1−pe
r = judge_agreement(
    ['A','A','B','B'],
    ['A','B','B','B'])
print(r['agreement'])  # .75
print(r['chance_agreement'])  # .5
print(r['kappa'])  # .5

Maak judge_agreement met agreement, chance_agreement en kappa. Bereken pₑ over A, B én tie met hun afzonderlijke marginale frequenties. Hier wordt κ=(.75−.5)/(1−.5)=.5.

print(judge_agreement(
    ['A','A'],['A','A'])['kappa'])
# None
print(judge_agreement(
    ['A','B'],['B','A'])['kappa'])
# -1.0

Als pₑ één is, is de noemer nul en geven we None. Kappa kan negatief zijn en hangt af van de labelverdeling. Het is geen universele kwaliteitsschaal; ook de menselijke referentie kan onjuist of onvolledig zijn.

03
CHECKPOINTOvereenstemming is nog geen waarheid

Wat neem je mee?

  • Consistentie vergelijkt terugvertaalde winnaars tussen presentatievolgordes.
  • Overeenstemming vergelijkt met een aparte referentie.
  • Geen van beide bewijst op zichzelf feitelijke waarheid.

Hierna verbind je gevallen, voorspellingen en beoordeling in een eigen evaluatieharnas.

Verder lezen & bronnen

Inhoud gecontroleerd op 2026-10-28.
DE VOLGENDE STAPBouw een evaluatieharnas met een echte eindtest
JOUW EXPERIMENT

Probeer het zelf

Python
main.py

Python start zodra jij code uitvoert.

UITVOER

Je uitvoer verschijnt hier.
Alles rekent in je eigen browser.

Jouw checkpoints0 / 3
01
A is een positie, geen modelnaam

Schrijf map_judge_vote(order,vote). order is een lijst/tuple van twee verschillende niet-lege model-ID-strings ≤100 tekens. vote is exact A/B/tie. Geef de gekozen model-ID of None; anders ValueError.

Een hint

A wijst naar index 0 van de daadwerkelijk getoonde volgorde, B naar index 1.

02
Twee gelijke letters kunnen juist inconsistent zijn

Schrijf judge_order_audit(votes). 1–1000 dictionaries exact id/forward/reverse, unieke niet-lege string-IDs ≤100 en geldige stemmen. Geef consistent (aantal), total, consistency en rows met id/consistent/winner. winner is None bij conflict of tie; anders de model-ID. Ongeldig: ValueError.

Een hint

Gebruik map_judge_vote voor beide volgordes en vergelijk de gemapte waarden; None==None is een consistente tie.

03
Overeenstemming is nog geen waarheid

Schrijf judge_agreement(reference,judge). Twee uitgelijnde lijsten met 1–1000 stemmen exact A/B/tie. Geef agreement, chance_agreement en kappa. Anders ValueError.

Een hint

Bereken eerst de werkelijk gelijke labels en daarna per label het product van de twee aantallen. Verwar dit niet met positieconsistentie.

Vastgelopen? Bekijk de voorbeeldoplossing
def map_judge_vote(order, vote):
    if not isinstance(order, (list, tuple)) or len(order) != 2 or any(not isinstance(s, str) or not s or len(s) > 100 for s in order) or order[0] == order[1]:
        raise ValueError('Twee verschillende model-IDs vereist')
    if not isinstance(vote, str) or vote not in {'A', 'B', 'tie'}:
        raise ValueError('Stem is exact A, B of tie')
    return None if vote == 'tie' else order[0 if vote == 'A' else 1]

def judge_order_audit(votes):
    if not isinstance(votes, list) or not 1 <= len(votes) <= 1000:
        raise ValueError('Geef gepaarde stemmen per geval')
    ids, rows = set(), []
    for row in votes:
        if not isinstance(row, dict) or set(row) != {'id', 'forward', 'reverse'} or not isinstance(row['id'], str) or not 1 <= len(row['id']) <= 100 or row['id'] in ids:
            raise ValueError('Geval-ID en beide presentatievolgordes vereist')
        ids.add(row['id'])
        first = map_judge_vote(['model-1', 'model-2'], row['forward'])
        reverse = map_judge_vote(['model-2', 'model-1'], row['reverse'])
        agrees = first == reverse
        rows.append({'id': row['id'], 'consistent': agrees, 'winner': first if agrees else None})
    return {'consistent': sum(row['consistent'] for row in rows), 'total': len(rows),
            'consistency': sum(row['consistent'] for row in rows) / len(rows), 'rows': rows}

def judge_agreement(reference, judge):
    if not isinstance(reference, list) or not isinstance(judge, list) or not 1 <= len(reference) <= 1000 or len(judge) != len(reference):
        raise ValueError('Geef uitgelijnde referentie- en beoordelaarstemmen')
    labels = ['A', 'B', 'tie']
    if any(not isinstance(v, str) or v not in labels for v in reference + judge):
        raise ValueError('Stemmen zijn exact A, B of tie')
    n = len(reference)
    observed = sum(a == b for a, b in zip(reference, judge)) / n
    expected = sum(reference.count(label) * judge.count(label) for label in labels) / n**2
    return {'agreement': observed, 'chance_agreement': expected,
            'kappa': (observed - expected) / (1 - expected) if expected < 1 else None}

Voer de code uit en verklaar voor jezelf waarom elke stap werkt.