Best-Worst Scaling
Effiziente vergleichende Annotation mit Best-Worst Scaling, automatischer Tupelgenerierung und Bewertung.
Best-Worst Scaling
Neu in v2.3.0
Best-Worst Scaling (BWS), auch bekannt als Maximum Difference Scaling (MaxDiff), ist eine vergleichende Annotationsmethode, bei der Annotatoren ein Tupel aus Elementen (typischerweise 4) präsentiert bekommen und gebeten werden, das beste und das schlechteste Element gemäß einem bestimmten Kriterium auszuwählen. BWS erzeugt zuverlässige Skalenwerte aus einfachen binären Urteilen und benötigt dabei wesentlich weniger Annotationen als direkte Bewertungsskalen, um dieselbe statistische Aussagekraft zu erreichen.
BWS ist besonders nützlich, wenn:
- Direkte numerische Bewertungen unter Annotator-Bias leiden (Skalennutzung variiert zwischen Personen)
- Eine zuverlässige Rangordnung von Hunderten oder Tausenden von Elementen benötigt wird
- Die Qualitätsdimension inhärent relativ ist (z. B. „Welche Übersetzung ist am flüssigsten?")
- Die Informationsmenge pro Annotation maximiert werden soll (jedes BWS-Urteil liefert mehr Bits als eine Likert-Bewertung)
Grundkonfiguration
annotation_schemes:
- annotation_type: best_worst_scaling
name: fluency
description: "Select the BEST and WORST translation by fluency"
# Items to compare
items_key: "translations" # key in instance data containing the list of items
# Tuple size (how many items shown at once)
tuple_size: 4 # typically 4; valid range is 3-8
# Labels for best/worst buttons
best_label: "Most Fluent"
worst_label: "Least Fluent"
# Display options
show_item_labels: true # show "A", "B", "C", "D" labels
randomize_order: true # randomize item order within each tuple
show_source: false # optionally show which system produced each item
# Validation
label_requirement:
required: true # must select both best and worstDatenformat
Jede Instanz in der Datendatei sollte eine Liste von zu vergleichenden Elementen enthalten. Potato generiert automatisch Tupel aus dieser Liste.
Option 1: Alle Elemente in einer Instanz
Wenn ein einzelner Satz von Elementen gerankt werden soll (z. B. Übersetzungen eines Satzes):
{
"id": "sent_001",
"source": "The cat sat on the mat.",
"translations": [
{"id": "sys_a", "text": "Le chat s'est assis sur le tapis."},
{"id": "sys_b", "text": "Le chat a assis sur le tapis."},
{"id": "sys_c", "text": "Le chat etait assis sur le mat."},
{"id": "sys_d", "text": "Le chat se tenait sur le tapis."}
]
}Option 2: Vorab generierte Tupel
Wenn volle Kontrolle darüber gewünscht wird, welche Elemente zusammen erscheinen, können vorab generierte Tupel bereitgestellt werden:
{
"id": "tuple_001",
"translations": [
{"id": "sys_a", "text": "Le chat s'est assis sur le tapis."},
{"id": "sys_b", "text": "Le chat a assis sur le tapis."},
{"id": "sys_c", "text": "Le chat etait assis sur le mat."},
{"id": "sys_d", "text": "Le chat se tenait sur le tapis."}
]
}Automatische Tupelgenerierung
Wenn die Elementliste länger als die Tupelgröße ist, generiert Potato automatisch Tupel. Der Generierungsalgorithmus stellt sicher:
- Jedes Element erscheint in ungefähr gleich vielen Tupeln
- Jedes Elementpaar kommt in mindestens einem Tupel vor (für zuverlässige relative Bewertung)
- Tupel sind ausgewogen, sodass kein Element immer an erster oder letzter Stelle erscheint
Tupelgenerierung konfigurieren:
annotation_schemes:
- annotation_type: best_worst_scaling
name: fluency
items_key: "translations"
tuple_size: 4
tuple_generation:
method: balanced_incomplete # balanced_incomplete or random
tuples_per_item: 5 # each item appears in ~5 tuples
seed: 42 # for reproducibility
ensure_pair_coverage: true # every pair co-occurs at least onceFür N Elemente mit Tupelgröße T und tuples_per_item = K generiert Potato ungefähr N * K / T Tupel insgesamt.
Generierungsmethoden
balanced_incomplete (Standard): Verwendet ein ausgeglichenes unvollständiges Blockdesign zur Maximierung der statistischen Effizienz. Jedes Element erscheint gleich oft, und die gemeinsame Häufigkeit von Paaren ist so gleichmäßig wie möglich. Für die meisten Anwendungsfälle empfohlen.
random: Tupel werden zufällig mit Wiederholung gesampelt. Schneller für sehr große Elementmengen (N > 10.000), aber weniger statistisch effizient. Verwenden, wenn exaktes Gleichgewicht nicht entscheidend ist.
Tupel vorab über CLI generieren
Für groß angelegte Projekte Tupel im Voraus generieren:
python -m potato.bws generate-tuples \
--items data/items.jsonl \
--tuple-size 4 \
--tuples-per-item 5 \
--output data/tuples.jsonl \
--seed 42Bewertungsmethoden
Nach der Annotation berechnet Potato Elementbewertungen aus BWS-Urteilen mit drei Methoden.
1. Zählung (Standard)
Die einfachste Methode. Der Score jedes Elements ist der Anteil der Male, in denen es als „bestes" ausgewählt wurde, minus den Anteil der Male, in denen es als „schlechtestes" ausgewählt wurde:
Score(Element) = (Beste-Anzahl - Schlechteste-Anzahl) / Gesamterscheinungen
Scores reichen von -1,0 (immer schlechtestes) bis +1,0 (immer bestes).
python -m potato.bws score \
--config config.yaml \
--method counting \
--output scores.csv2. Bradley-Terry
Passt ein Bradley-Terry-Modell an die paarweisen Vergleiche an, die durch BWS-Urteile impliziert werden. Jede „beste" Auswahl impliziert, dass das beste Element gegenüber allen anderen Elementen im Tupel bevorzugt wird; jede „schlechteste" Auswahl impliziert, dass alle anderen Elemente gegenüber dem schlechtesten bevorzugt werden.
Bradley-Terry produziert Scores auf einer Log-Odds-Skala mit besseren statistischen Eigenschaften als die Zählung, insbesondere bei spärlichen Daten.
python -m potato.bws score \
--config config.yaml \
--method bradley_terry \
--max-iter 1000 \
--tolerance 1e-6 \
--output scores.csv3. Plackett-Luce
Eine Verallgemeinerung von Bradley-Terry, die das vollständige Ranking modelliert, das durch jedes Tupelurteil impliziert wird (bestes > mittlere Elemente > schlechtestes). Plackett-Luce extrahiert mehr Informationen aus jeder Annotation als Bradley-Terry.
python -m potato.bws score \
--config config.yaml \
--method plackett_luce \
--output scores.csvBewertungsmethoden im Vergleich
| Methode | Geschwindigkeit | Dateneffizienz | Umgang mit spärlichen Daten | Statistisches Modell |
|---|---|---|---|---|
| Zählung | Schnell | Niedrig | Ja | Keines (deskriptiv) |
| Bradley-Terry | Mittel | Mittel | Mittel | Paarweiser Vergleich |
| Plackett-Luce | Langsamer | Hoch | Mittel | Vollständiges Ranking |
Für die meisten Projekte ist Bradley-Terry der beste Standard. Zählung für schnelle explorative Analysen und Plackett-Luce, wenn maximale statistische Effizienz bei begrenzten Annotationen benötigt wird.
Bewertungskonfiguration in YAML
Bewertung kann auch direkt in der Projektkonfiguration für automatische Berechnung konfiguriert werden:
annotation_schemes:
- annotation_type: best_worst_scaling
name: fluency
items_key: "translations"
tuple_size: 4
scoring:
method: bradley_terry
auto_compute: true # compute scores after each annotation session
output_file: "output/fluency_scores.csv"
include_confidence: true # include confidence intervals
bootstrap_iterations: 1000 # for confidence interval estimationAdmin-Dashboard-Integration
Das Admin-Dashboard enthält einen dedizierten BWS-Tab mit:
- Score-Verteilung: Histogramm der aktuellen Elementscores
- Annotationsfortschritt: Wie viele Tupel annotiert wurden vs. Gesamtanzahl
- Abdeckung pro Element: Wie oft jedes Element gesehen wurde
- Annotator-Konsistenz: Split-Half-Reliabilität der BWS-Scores
- Score-Konvergenz: Liniendiagramm, das zeigt, wie sich Scores mit mehr Annotationen stabilisieren
BWS-Analysen über die Befehlszeile aufrufen:
python -m potato.bws stats --config config.yamlBWS Statistics
==============
Schema: fluency
Items: 200
Tuples: 250 (annotated: 180 / 250)
Annotations: 540 (3 annotators)
Score Summary (Bradley-Terry):
Mean: 0.02
Std: 0.43
Range: -0.91 to +0.87
Top 5 Items:
sys_d: 0.87 (±0.08)
sys_a: 0.72 (±0.09)
sys_f: 0.65 (±0.10)
sys_b: 0.51 (±0.11)
sys_k: 0.48 (±0.09)
Split-Half Reliability: r = 0.94
Mehrere BWS-Dimensionen
Mehrere BWS-Schemata können auf demselben Elementsatz ausgeführt werden, um verschiedene Qualitätsdimensionen zu bewerten:
annotation_schemes:
- annotation_type: best_worst_scaling
name: fluency
description: "Select BEST and WORST by fluency"
items_key: "translations"
tuple_size: 4
best_label: "Most Fluent"
worst_label: "Least Fluent"
- annotation_type: best_worst_scaling
name: adequacy
description: "Select BEST and WORST by meaning preservation"
items_key: "translations"
tuple_size: 4
best_label: "Most Accurate"
worst_label: "Least Accurate"Beide Schemata teilen dieselben Tupel (Potato generiert einen Tupelsatz pro items_key), sodass Annotatoren jedes Tupel einmal sehen, aber zwei Urteile abgeben.
Ausgabeformat
BWS-Annotationen werden pro Tupel gespeichert:
{
"id": "tuple_001",
"annotations": {
"fluency": {
"best": "sys_d",
"worst": "sys_c"
},
"adequacy": {
"best": "sys_a",
"worst": "sys_c"
}
},
"annotator": "user_1",
"timestamp": "2026-03-01T14:22:00Z"
}Vollständiges Beispiel
Vollständige Konfiguration zur Bewertung von maschinellen Übersetzungssystemen:
task_name: "MT System Ranking (BWS)"
task_dir: "."
data_files:
- "data/mt_tuples.jsonl"
item_properties:
id_key: id
text_key: source
instance_display:
fields:
- key: source
type: text
display_options:
label: "Source Sentence"
annotation_schemes:
- annotation_type: best_worst_scaling
name: overall_quality
description: "Select the BEST and WORST translation"
items_key: "translations"
tuple_size: 4
best_label: "Best Translation"
worst_label: "Worst Translation"
randomize_order: true
show_item_labels: true
tuple_generation:
method: balanced_incomplete
tuples_per_item: 5
seed: 42
scoring:
method: bradley_terry
auto_compute: true
output_file: "output/quality_scores.csv"
include_confidence: true
output_annotation_dir: "output/"
output_annotation_format: "jsonl"Weiterführende Lektüre
- Paarweiser Vergleich -- einfacherer Zwei-Element-Vergleich
- Likert-Skalen -- direkte Bewertungsalternative
- Multirate -- mehrdimensionale direkte Bewertungen
- Exportformate -- BWS-Daten für die Analyse exportieren
Implementierungsdetails sind in der Quelldokumentation zu finden.