Skip to content

Échelle best-worst

Annotation comparative efficace avec l'échelle best-worst dans Potato — génération automatique des tuples de comparaison et conversion des choix en scores de qualité continus.

Nouveau en v2.3.0

L'échelle best-worst (BWS), aussi appelée Maximum Difference Scaling (MaxDiff), est une méthode d'annotation comparative où l'on montre aux annotateurs un tuple d'éléments (typiquement 4) en leur demandant de choisir le meilleur et le pire selon un critère donné. La BWS produit des scores scalaires fiables à partir de jugements binaires simples, et exige bien moins d'annotations que les échelles de notation directe pour atteindre la même puissance statistique.

La BWS est particulièrement utile quand :

  • Les notations numériques directes souffrent d'un biais d'annotateur (l'usage de l'échelle varie d'une personne à l'autre)
  • Vous avez besoin d'un classement fiable de centaines ou de milliers d'éléments
  • La dimension de qualité est relative par nature (par exemple, « quelle traduction est la plus fluide ? »)
  • Vous voulez maximiser l'information par annotation (chaque jugement BWS apporte plus de bits qu'une note de Likert)

Interface d'annotation en échelle best-worstInterface d'échelle best-worst pour l'annotation comparative dans Potato

Configuration de base

yaml
annotation_schemes:
  - annotation_type: bws
    name: fluency
    description: "Select the BEST and WORST translation by fluency"
 
    # Items to compare
    # Tuple size (how many items shown at once)
    tuple_size: 4                # typically 4; valid range is 3-8
 
    # Labels for best/worst buttons
    best_description: "Most Fluent"
    worst_description: "Least Fluent"
 
    # Display options
    # Validation
    label_requirement:
      required: true             # must select both best and worst

Format des données

Chaque instance de votre fichier de données doit contenir une liste d'éléments à comparer. Potato en génère les tuples automatiquement.

Option 1 : tous les éléments dans une instance

Si vous avez un seul ensemble d'éléments à classer (par exemple les traductions d'une même phrase) :

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 : tuples pré-générés

Si vous voulez maîtriser entièrement quels éléments apparaissent ensemble, fournissez des tuples pré-générés :

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."}
  ]
}

Génération automatique des tuples

Quand votre liste d'éléments dépasse la taille du tuple, Potato génère les tuples automatiquement. L'algorithme de génération garantit que :

  • Chaque élément apparaît dans à peu près le même nombre de tuples
  • Chaque paire d'éléments se retrouve ensemble dans au moins un tuple (pour un score relatif fiable)
  • Les tuples sont équilibrés de sorte qu'aucun élément ne soit toujours affiché en premier ou en dernier

Configurez la génération de tuples :

yaml
annotation_schemes:
  - annotation_type: bws
    name: fluency
    tuple_size: 4

Pour un ensemble de N éléments avec une taille de tuple T et tuples_per_item = K, Potato génère environ N * K / T tuples au total.

Méthodes de génération

balanced_incomplete (par défaut) : utilise un plan en blocs incomplets équilibrés pour maximiser l'efficacité statistique. Chaque élément apparaît aussi souvent que les autres, et la co-occurrence des paires est aussi uniforme que possible. Recommandé dans la plupart des cas.

random : échantillonne les tuples au hasard avec remise. Plus rapide pour de très grands ensembles d'éléments (N > 10 000) mais statistiquement moins efficace. À utiliser quand l'équilibre exact n'est pas critique.

Pré-générer les tuples en ligne de commande

Pour des projets à grande échelle, générez les tuples à l'avance :

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

Méthodes de calcul des scores

Après l'annotation, Potato calcule les scores des éléments à partir des jugements BWS selon trois méthodes.

1. Comptage (par défaut)

La méthode la plus simple. Le score d'un élément est la proportion de fois où il a été choisi comme « meilleur » moins la proportion de fois où il a été choisi comme « pire » :

Score(item) = (best_count - worst_count) / total_appearances

Les scores vont de -1,0 (toujours le pire) à +1,0 (toujours le meilleur).

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

2. Bradley-Terry

Ajuste un modèle de Bradley-Terry aux comparaisons par paires impliquées par les jugements BWS. Chaque choix de « meilleur » implique que cet élément est préféré à tous les autres du tuple ; chaque choix de « pire » implique que tous les autres lui sont préférés.

Bradley-Terry produit des scores sur une échelle de log-cotes, aux propriétés statistiques meilleures que le comptage, en particulier avec des données éparses.

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

3. Plackett-Luce

Une généralisation de Bradley-Terry qui modélise le classement complet impliqué par chaque jugement de tuple (meilleur > éléments intermédiaires > pire). Plackett-Luce extrait plus d'information de chaque annotation que Bradley-Terry.

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

Comparaison des méthodes de score

MéthodeVitesseEfficacité en donnéesGère les données éparsesModèle statistique
ComptageRapideFaibleOuiAucun (descriptif)
Bradley-TerryMoyenneMoyenneModérémentComparaison par paires
Plackett-LucePlus lenteÉlevéeModérémentClassement complet

Pour la plupart des projets, Bradley-Terry est le meilleur choix par défaut. Prenez le comptage pour une analyse exploratoire rapide et Plackett-Luce quand il faut tirer le maximum d'efficacité statistique d'annotations limitées.

Configuration du score en YAML

Vous pouvez aussi configurer le calcul des scores directement dans la configuration du projet, pour un calcul automatique :

yaml
annotation_schemes:
  - annotation_type: bws
    name: fluency
    tuple_size: 4

Intégration au tableau de bord d'administration

Le tableau de bord d'administration comporte un onglet BWS dédié qui affiche :

  • Distribution des scores : histogramme des scores actuels des éléments
  • Avancement de l'annotation : combien de tuples ont été annotés sur le total
  • Couverture par élément : combien de fois chaque élément a été vu
  • Cohérence inter-annotateurs : fidélité par bissection des scores BWS
  • Convergence des scores : courbe montrant comment les scores se stabilisent à mesure que les annotations s'accumulent

Consultez les analyses BWS en ligne de commande :

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

Plusieurs dimensions BWS

Vous pouvez faire tourner plusieurs schémas BWS sur le même ensemble d'éléments pour évaluer différentes dimensions de qualité :

yaml
annotation_schemes:
  - annotation_type: bws
    name: fluency
    description: "Select BEST and WORST by fluency"
    tuple_size: 4
    best_description: "Most Fluent"
    worst_description: "Least Fluent"
 
  - annotation_type: bws
    name: adequacy
    description: "Select BEST and WORST by meaning preservation"
    tuple_size: 4
    best_description: "Most Accurate"
    worst_description: "Least Accurate"

Les deux schémas partagent les mêmes tuples (Potato génère un jeu de tuples par items_key) : les annotateurs voient chaque tuple une fois mais rendent deux jugements.

Format de sortie

Les annotations BWS sont enregistrées par tuple :

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"
}

Exemple complet

Configuration complète pour évaluer des systèmes de traduction automatique :

yaml
annotation_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: bws
    name: overall_quality
    description: "Select the BEST and WORST translation"
    tuple_size: 4
    best_description: "Best Translation"
    worst_description: "Worst Translation"
output_annotation_dir: "output/"
export_annotation_format: "jsonl"

Pour aller plus loin

Pour les détails d'implémentation, voir la documentation source.