Skip to content
Tutorials12 min read

Solo Mode : Comment un seul annotateur peut étiqueter 10 000 exemples

Tutoriel étape par étape sur l'utilisation du Solo Mode de Potato pour étiqueter efficacement de grands ensembles de données grâce à la collaboration humain-LLM, réduisant les coûts d'annotation jusqu'à 90%.

Potato Team·

Solo Mode : Comment un seul annotateur peut étiqueter 10 000 exemples

Vous avez 10 000 avis de produits à étiqueter pour le sentiment (Positif, Neutre, Négatif). Engager trois annotateurs pour tout étiqueter prendrait des semaines et coûterait des milliers de dollars. Avec le Solo Mode, un seul expert du domaine peut atteindre une qualité comparable en n'étiquetant que 500 à 1 000 instances tandis qu'un LLM gère le reste -- l'humain ne révisant que les cas où le LLM a des difficultés.

Ce tutoriel vous guide à travers l'ensemble du processus de bout en bout.


Ce dont vous aurez besoin

  • Potato 2.3.0+ avec les extras Solo Mode : pip install potato-annotation[solo]
  • Une clé API OpenAI ou Anthropic (pour le composant LLM)
  • Votre ensemble de données au format JSONL
  • Un annotateur compétent (ce pourrait être vous)

Étape 1 : Préparer vos données

Créez data/reviews.jsonl avec un avis par ligne :

json
{"id": "rev_001", "text": "Absolutely love this product! Best purchase I've made all year.", "source": "amazon"}
{"id": "rev_002", "text": "It works fine. Nothing special but gets the job done.", "source": "amazon"}
{"id": "rev_003", "text": "Broke after two weeks. Complete waste of money.", "source": "amazon"}
{"id": "rev_004", "text": "The quality is decent for the price point. I might buy again.", "source": "amazon"}
{"id": "rev_005", "text": "Arrived damaged and customer service was unhelpful.", "source": "amazon"}

Pour ce tutoriel, imaginez que ce fichier contient 10 000 avis.


Étape 2 : Créer la configuration

Créez config.yaml :

yaml
task_name: "Product Review Sentiment (Solo Mode)"
task_dir: "."
 
data_files:
  - "data/reviews.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
# --- Solo Mode Configuration ---
solo_mode:
  enabled: true
 
  llm:
    endpoint_type: openai
    model: "gpt-4o"
    api_key: ${OPENAI_API_KEY}
    temperature: 0.1
    max_tokens: 64
 
  # Quality targets
  seed_count: 50
  accuracy_threshold: 0.93
  confidence_threshold: 0.85
 
  # Phase-specific settings
  phases:
    seed:
      count: 50
      selection: diversity
      embedding_model: "all-MiniLM-L6-v2"
 
    calibration:
      batch_size: 200
      holdout_fraction: 0.2
 
    labeling_functions:
      enabled: true
      max_functions: 15
      min_precision: 0.92
      min_coverage: 0.01
 
    active_labeling:
      batch_size: 25
      strategy: hybrid
      max_batches: 15
 
    refinement_loop:
      max_iterations: 3
      improvement_threshold: 0.02
 
    disagreement_exploration:
      max_instances: 150
      show_llm_reasoning: true
      show_nearest_neighbors: 3
 
    edge_case_synthesis:
      enabled: true
      count: 30
 
    confidence_escalation:
      escalation_budget: 150
      batch_size: 25
      stop_when_stable: true
 
    prompt_optimization:
      enabled: true
      candidates: 8
      metric: f1_macro
 
    final_validation:
      sample_size: 100
      min_accuracy: 0.93
 
  # Instance prioritization
  prioritization:
    pools:
      - name: uncertain
        weight: 0.30
      - name: disagreement
        weight: 0.25
      - name: boundary
        weight: 0.20
      - name: novel
        weight: 0.10
      - name: error_pattern
        weight: 0.10
      - name: random
        weight: 0.05
 
# --- Annotation Schema ---
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the overall sentiment of this review?"
    labels:
      - "Positive"
      - "Neutral"
      - "Negative"
    label_requirement:
      required: true
    sequential_key_binding: true
 
output_annotation_dir: "output/"
output_annotation_format: "jsonl"
 
parquet_export:
  enabled: true
  output_dir: "output/parquet/"

Étape 3 : Démarrer le serveur

bash
potato start config.yaml -p 8000

Ouvrez http://localhost:8000 et connectez-vous. Le tableau de bord Solo Mode apparaîtra, indiquant que vous êtes en Phase 1 : Annotation initiale.


Étape 4 : Phase 1 -- Annotation initiale (50 instances)

Potato a sélectionné 50 avis diversifiés en utilisant un clustering basé sur les embeddings. Ce ne sont pas des avis aléatoires ; ils sont choisis pour maximiser la couverture de la distribution de vos données.

Étiquetez chacun d'eux. C'est la phase la plus importante -- la qualité de vos étiquettes initiales détermine dans quelle mesure le LLM apprendra. Prenez votre temps et soyez cohérent.

Estimation de temps : 15-25 minutes à 20-30 secondes par instance.

Lorsque vous terminez la 50e instance, Potato passe automatiquement à la Phase 2.


Étape 5 : Phase 2 -- Calibration initiale du LLM

Cette phase s'exécute automatiquement. Potato envoie au LLM un lot de 200 instances avec vos 50 étiquettes initiales comme exemples few-shot. Il compare ensuite les prédictions du LLM à 10 étiquettes initiales de validation pour estimer la précision de base.

Vous verrez un indicateur de progression dans le tableau de bord. Cela prend généralement 1 à 2 minutes selon le fournisseur LLM.

Résultat typique : Le LLM atteint 75-85% de précision à la première calibration. C'est attendu -- le LLM n'a pas encore appris votre style d'annotation spécifique.


Étape 6 : Phase 3 -- Analyse de confusion

Potato affiche une matrice de confusion montrant où le LLM est en désaccord avec vos étiquettes. Un résultat typique :

text
Confusion Analysis (Round 1)
============================
Overall Accuracy: 0.82 (target: 0.93)

Top Confusion Pairs:
  Neutral -> Positive:  14 instances (7.0%)
  Negative -> Neutral:   9 instances (4.5%)
  Positive -> Neutral:   4 instances (2.0%)

Cela vous indique la principale faiblesse du LLM : il a tendance à surclasser les avis neutres en positifs. C'est courant -- les LLM ont souvent un biais vers le sentiment positif.

Votre action : Examinez les paires de confusion. Cliquez sur chaque paire pour voir les instances spécifiques que le LLM a mal classées. Cela vous aide à comprendre les modes de défaillance du LLM.


Étape 7 : Phase 4 -- Affinement des directives

Sur la base de l'analyse de confusion, Potato génère des directives affinées pour le LLM. Vous voyez une vue côte à côte :

  • Directives actuelles : le prompt initial utilisé pour le LLM
  • Modifications suggérées : des changements spécifiques que le LLM propose basés sur les schémas d'erreurs

Par exemple, Potato pourrait suggérer d'ajouter :

"Les avis qui décrivent un produit comme 'correct', 'ok' ou 'décent' sans émotion forte doivent être étiquetés Neutre, même s'ils mentionnent racheter."

Examinez chaque modification suggérée. Approuvez, modifiez ou rejetez chacune. Vous pouvez aussi ajouter vos propres clarifications.

Estimation de temps : 5-10 minutes.


Étape 8 : Phase 5 -- Génération de fonctions d'étiquetage

Potato génère des fonctions d'étiquetage programmatiques à partir des schémas dans vos étiquettes initiales. Ce sont des règles rapides et déterministes qui gèrent les cas faciles :

text
Generated Labeling Functions:
  LF1: Strong positive words (love, amazing, best, excellent)
       Precision: 0.97, Coverage: 0.06
  LF2: Strong negative words (terrible, awful, worst, waste)
       Precision: 0.95, Coverage: 0.04
  LF3: Exclamation + positive adjective
       Precision: 0.94, Coverage: 0.03
  LF4: Return/refund mention + negative context
       Precision: 0.92, Coverage: 0.02
  ...
  Total coverage: 0.18 (1,800 of 10,000 instances)

Les fonctions d'étiquetage couvrent 18% de votre ensemble de données avec une précision de 92%+. Ces instances sont étiquetées automatiquement, libérant l'effort du LLM et de l'humain pour les cas plus difficiles.

Votre action : Examinez les fonctions générées. Désactivez celles qui semblent peu fiables. C'est optionnel -- Potato ne garde que les fonctions au-dessus de votre seuil de précision configuré.


Étape 9 : Phase 6 -- Étiquetage actif (125-375 instances)

C'est la phase principale d'étiquetage humain. Potato sélectionne les instances en utilisant le système de priorisation à six pools :

  • Incertain (30%) : avis où la confiance du LLM est inférieure à 85%
  • Désaccord (25%) : avis où le LLM et les fonctions d'étiquetage donnent des étiquettes différentes
  • Frontière (20%) : avis proches de la frontière de décision dans l'espace des embeddings
  • Nouveau (10%) : avis différents de tout ce que vous avez étiqueté jusqu'ici
  • Schéma d'erreur (10%) : avis correspondant à des schémas de confusion connus (par ex. positif tiède)
  • Aléatoire (5%) : avis aléatoires pour la calibration

Vous étiquetez par lots de 25. Après chaque lot, Potato met à jour l'estimation de précision du LLM et décide de continuer ou non.

Trajectoire typique :

  • Lots 1-3 (75 instances) : la précision passe de 82% à 87%
  • Lots 4-6 (150 instances) : la précision atteint 90%
  • Lots 7-10 (250 instances) : la précision plafonne à 91-92%

Si la précision atteint 93% (votre seuil), le Solo Mode saute directement à la Phase 10. Sinon, il continue à la Phase 7.

Estimation de temps : 45-90 minutes au total, selon le nombre de lots nécessaires.


Étape 10 : Phase 7 -- Boucle de raffinement automatisée

Si la précision est toujours en dessous du seuil après l'étiquetage actif, Potato lance un nouveau tour de la boucle de raffinement :

  1. Le LLM ré-étiquette l'ensemble de données complet avec des directives mises à jour et plus d'exemples few-shot
  2. La précision est recalculée par rapport à toutes les étiquettes humaines
  3. De nouveaux schémas de confusion sont identifiés
  4. Les directives sont affinées à nouveau

Cette phase est essentiellement automatique. Vous n'avez qu'à approuver les changements de directives.

Résultat typique : la précision s'améliore de 2 à 4% par tour de raffinement.


Étape 11 : Phase 8 -- Exploration des désaccords

Potato présente les instances les plus controversées : les cas où le LLM, les fonctions d'étiquetage et l'analyse des plus proches voisins donnent tous des réponses différentes. Pour chaque instance, vous voyez :

  • Le texte de l'avis
  • La prédiction et la confiance du LLM
  • Les votes des fonctions d'étiquetage
  • Les 3 exemples étiquetés les plus proches avec leurs étiquettes
  • Le raisonnement chaîne-de-pensée du LLM

Ce sont des cas véritablement difficiles. Vos étiquettes ici ont la valeur marginale la plus élevée de toute annotation dans l'ensemble du processus.

Estimation de temps : 20-30 minutes pour 100-150 instances.


Étape 12 : Phase 9 -- Synthèse de cas limites

Potato génère des avis synthétiques ciblant les schémas de confusion restants. Par exemple, si le LLM a encore du mal avec les « avis neutres qui mentionnent racheter », il génère des exemples comme :

"C'est un produit correct pour le prix. J'en reprendrais peut-être s'il y a une promo."

Vous étiquetez ces exemples synthétiques, et ils sont ajoutés au contexte few-shot du LLM.

Estimation de temps : 10-15 minutes pour 30 exemples.


Étape 13 : Phase 10 -- Escalade de confiance en cascade

Le LLM a maintenant étiqueté la majorité de l'ensemble de données. Potato classe toutes les instances étiquetées par le LLM par confiance et vous envoie celles à plus faible confiance par lots de 25.

text
Confidence Escalation Progress:
  Batch 1: 25 instances, 23/25 correct (92%)
  Batch 2: 25 instances, 24/25 correct (96%)
  Batch 3: 25 instances, 25/25 correct (100%)
  -> Stopping: last 3 batches stable

Une fois que vous voyez trois lots consécutifs où le LLM a tout réussi, le Solo Mode conclut que les étiquettes à haute confiance restantes sont fiables.

Estimation de temps : 15-20 minutes.


Étape 14 : Phase 11 -- Optimisation de prompt

Cette phase s'exécute automatiquement. Potato essaie 8 variantes de prompt et sélectionne celle avec le meilleur score F1 sur vos étiquettes humaines accumulées :

text
Prompt Optimization Results:
  Variant 1 (direct, 5 examples):     F1=0.91
  Variant 2 (CoT, 5 examples):        F1=0.93
  Variant 3 (direct, 10 examples):    F1=0.92
  Variant 4 (CoT, 10 examples):       F1=0.94  <-- selected
  Variant 5 (direct, 15 examples):    F1=0.92
  Variant 6 (CoT, 15 examples):       F1=0.93
  Variant 7 (self-consistency, 5x):   F1=0.94
  Variant 8 (self-consistency, 10x):  F1=0.94

Le meilleur prompt est utilisé pour une passe finale de ré-étiquetage.


Étape 15 : Phase 12 -- Validation finale

Potato sélectionne 100 instances aléatoires étiquetées par le LLM pour votre révision. Vous les étiquetez, et Potato compare avec les étiquettes du LLM.

text
Final Validation:
  Reviewed: 100 instances
  LLM correct: 94/100 (94%)
  Threshold: 93%
  -> PASSED

Si la précision du LLM atteint votre seuil, l'ensemble de données est complet. Sinon, le Solo Mode revient à la Phase 6 pour un autre tour d'étiquetage actif.

Estimation de temps : 10-15 minutes.


Résumé des résultats

Après avoir traversé les 12 phases, vérifiez les statistiques finales :

bash
python -m potato.solo status --config config.yaml
text
Solo Mode Complete
==================
Dataset: 10,000 instances
Total human labels: 612
  Seed: 50
  Active labeling: 275
  Disagreement exploration: 137
  Edge case synthesis: 30
  Confidence escalation: 75
  Final validation: 45

LLM labels: 8,200 (accuracy: 94.1%)
LF labels: 1,800 (precision: 95.3%)
Unlabeled: 0

Final label distribution:
  Positive: 4,823 (48.2%)
  Neutral:  3,011 (30.1%)
  Negative: 2,166 (21.7%)

Total human time: ~3.5 hours
Estimated multi-annotator cost (3x): ~$4,500
Solo Mode cost: ~$450 (API fees) + ~$175 (annotator time)
Savings: ~88%

L'humain a étiqueté 612 des 10 000 instances (6,1%). Le LLM et les fonctions d'étiquetage ont géré le reste à plus de 94% de précision.


Exporter les résultats

Exportez l'ensemble de données étiqueté final :

bash
python -m potato.solo export --config config.yaml --output final_labels.jsonl

Chaque ligne inclut l'étiquette et sa source :

json
{"id": "rev_001", "sentiment": "Positive", "source": "human", "confidence": 1.0}
{"id": "rev_002", "sentiment": "Neutral", "source": "llm", "confidence": 0.91}
{"id": "rev_003", "sentiment": "Negative", "source": "labeling_function", "confidence": 0.97}

Pour l'export Parquet :

python
import pandas as pd
df = pd.read_parquet("output/parquet/annotations.parquet")
print(df["value"].value_counts())

Assurance qualité : vérification hybride

Pour des ensembles de données de qualité publication, ajoutez un second annotateur pour réviser un échantillon :

yaml
solo_mode:
  verification:
    enabled: true
    sample_fraction: 0.10
    annotator: "reviewer_1"

Cela assigne 1 000 instances aléatoires à un second annotateur. Vous pouvez ensuite calculer l'accord inter-annotateurs entre les étiquettes du Solo Mode et celles du réviseur.


Dépannage

La précision du LLM plafonne en dessous du seuil

  • Augmentez le nombre initial : essayez 75-100 instances initiales au lieu de 50
  • Changez de LLM : essayez claude-sonnet-4-20250514 au lieu de GPT-4o (ou vice versa)
  • Baissez le seuil : si 93% n'est pas atteignable, considérez si 90% est acceptable pour votre cas d'usage
  • Vérifiez vos données : certains ensembles de données sont intrinsèquement ambigus. Si l'accord humain-humain ne serait que de 90%, n'attendez pas du LLM qu'il fasse mieux

La Phase 6 prend trop de lots

  • Augmentez la taille des lots : changez batch_size de 25 à 50
  • Ajustez les poids des pools : si la plupart des instances escaladées viennent du pool « incertain », réduisez son poids et augmentez « désaccord » et « schéma d'erreur »

Les fonctions d'étiquetage ont une faible couverture

  • C'est normal pour les tâches sans signaux lexicaux forts (par ex. détection de sarcasme, sentiment implicite)
  • Les fonctions d'étiquetage fonctionnent mieux pour les schémas explicites basés sur les mots-clés
  • Le Solo Mode fonctionne quand même sans fonctions d'étiquetage -- le LLM prend le relais

Pour aller plus loin