Skip to content
Diese Seite ist in Ihrer Sprache noch nicht verfügbar. Englische Version wird angezeigt.

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

yaml
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 worst

Datenformat

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):

json
{
  "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:

json
{
  "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:

yaml
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 once

Fü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:

bash
python -m potato.bws generate-tuples \
  --items data/items.jsonl \
  --tuple-size 4 \
  --tuples-per-item 5 \
  --output data/tuples.jsonl \
  --seed 42

Bewertungsmethoden

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).

bash
python -m potato.bws score \
  --config config.yaml \
  --method counting \
  --output scores.csv

2. 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.

bash
python -m potato.bws score \
  --config config.yaml \
  --method bradley_terry \
  --max-iter 1000 \
  --tolerance 1e-6 \
  --output scores.csv

3. 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.

bash
python -m potato.bws score \
  --config config.yaml \
  --method plackett_luce \
  --output scores.csv

Bewertungsmethoden im Vergleich

MethodeGeschwindigkeitDateneffizienzUmgang mit spärlichen DatenStatistisches Modell
ZählungSchnellNiedrigJaKeines (deskriptiv)
Bradley-TerryMittelMittelMittelPaarweiser Vergleich
Plackett-LuceLangsamerHochMittelVollstä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:

yaml
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 estimation

Admin-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:

bash
python -m potato.bws stats --config config.yaml
text
BWS 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:

yaml
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:

json
{
  "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:

yaml
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

Implementierungsdetails sind in der Quelldokumentation zu finden.