Solo Mode : comment un seul annotateur peut étiqueter 10 000 exemples
Tutoriel pas à pas sur le Solo Mode de Potato : étiquetez vous-même un jeu initial, laissez un LLM étiqueter le reste et servez-vous des contrôles de précision pour décider quand le résultat est assez bon pour vous arrêter.
Vous avez 10 000 avis de produits à étiqueter selon le sentiment (Positif, Neutre, Négatif). Engager trois annotateurs pour les étiqueter tous prend des semaines et coûte des milliers de dollars. Le Solo Mode permet à un seul expert du domaine d'obtenir une qualité comparable en n'étiquetant que 500 à 1000 instances ; un LLM se charge du reste, et l'humain revoit tout ce sur quoi le LLM a un doute.
Ce tutoriel parcourt l'ensemble du processus.
Ce dont vous aurez besoin
- Potato 2.3.0 ou ultérieur, avec les extras IA pour les fournisseurs de LLM :
pip install potato-annotation[ai] - Une clé d'API OpenAI ou Anthropic (pour le composant LLM)
- Votre jeu de données au format JSONL
- Un annotateur qui connaît le domaine (ce peut être vous)
Étape 1 : Préparer vos données
Créez data/reviews.jsonl avec un avis par ligne :
{"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 :
annotation_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
# The model that labels. A list, so you can give fallbacks.
labeling_models:
- endpoint_type: openai
model: "gpt-4o"
api_key: ${OPENAI_API_KEY}
temperature: 0.1
max_tokens: 1024
embedding:
model_name: "all-MiniLM-L6-v2"
uncertainty:
strategy: direct_confidence
# When you are allowed to stop labeling
thresholds:
end_human_annotation_agreement: 0.93
minimum_validation_sample: 50
confidence_low: 0.5
confidence_high: 0.85
periodic_review_interval: 100
# What decides which instance you see next
instance_selection:
low_confidence_weight: 0.30
disagreement_weight: 0.25
diversity_weight: 0.20
llm_predicted_weight: 0.15
random_weight: 0.10
batches:
llm_labeling_batch: 25
max_parallel_labels: 200
labeling_functions:
enabled: true
auto_extract: true
max_functions: 15
min_confidence: 0.92
min_coverage: 3
edge_case_rules:
enabled: true
auto_extract_on_labeling: true
confusion_analysis:
enabled: true
min_instances_for_pattern: 3
max_patterns: 20
prompt_optimization:
enabled: true
target_accuracy: 0.93
refinement_loop:
enabled: true
trigger_interval: 50
min_improvement: 0.02
max_cycles: 3
state_dir: "solo_state_reviews"
# --- 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/"
export_annotation_format:
- jsonl
- parquetÉtape 3 : Démarrer le serveur
potato start config.yaml -p 8000Ouvrez http://localhost:8000 et connectez-vous. Le tableau de bord du Solo Mode s'affiche et montre le projet en cours de configuration, prêt pour que vous relisiez le prompt qu'il a synthétisé.
Étape 4 : Annotation en parallèle (vos 50 premières)
Potato a choisi 50 avis variés au moyen d'un clustering fondé sur les embeddings. Ils ne sont pas aléatoires : ils sont choisis pour couvrir la distribution de vos données aussi largement que possible.
Étiquetez chacun d'eux. C'est la phase qui compte le plus, car la qualité de vos étiquettes initiales fixe le plafond de ce que le LLM peut apprendre. Prenez votre temps et restez cohérent.
Estimation de temps : 15-25 minutes, à 20-30 secondes par instance.
Une fois qu'il y en a assez, le LLM commence à étiqueter en parallèle avec vous, sans que vous le demandiez.
Étape 5 : Le LLM rattrape son retard
Cette phase se déroule toute seule. Potato envoie au LLM un lot de 200 instances avec vos 50 étiquettes initiales comme exemples few-shot, puis compare les prédictions à 10 étiquettes initiales mises de côté pour estimer la précision de départ.
Un indicateur de progression apparaît dans le tableau de bord. Cela prend généralement 1 à 2 minutes selon le fournisseur de LLM.
Résultat typique : Le LLM atteint 75-85% de précision à la première calibration. C'est attendu. Il n'a pas encore appris votre style d'annotation.
Étape 6 : Analyse de confusion
Potato affiche une matrice de confusion qui montre où le LLM est en désaccord avec vos étiquettes. Un résultat typique :
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 met en évidence la principale faiblesse du LLM ici : il continue de faire passer des avis neutres en positifs. C'est un défaut courant, car les LLM ont tendance à pencher vers le positif.
Votre action : Parcourez les paires de confusion. Cliquez sur chaque paire pour voir les instances précises sur lesquelles le LLM s'est trompé, ce qui est le moyen le plus rapide de comprendre comment il échoue.
Étape 7 : Retour à la relecture du prompt
À partir de l'analyse de confusion, Potato rédige des directives affinées pour le LLM et les présente côte à côte : le prompt actuel d'un côté et, de l'autre, les modifications précises qu'il propose d'après les schémas d'erreurs.
Par exemple, Potato pourrait suggérer d'ajouter :
"Les avis qui décrivent un produit comme 'correct', 'ok' ou 'convenable' sans émotion forte doivent être étiquetés Neutre, même s'ils mentionnent un nouvel achat."
Passez en revue chaque modification suggérée et approuvez-la, modifiez-la ou rejetez-la. Vous pouvez aussi rédiger vos propres précisions.
Estimation de temps : 5-10 minutes.
Étape 8 : Fonctions d'étiquetage
Potato génère des fonctions d'étiquetage programmatiques à partir des régularités de vos étiquettes initiales. Ce sont des règles rapides et déterministes qui traitent les cas faciles :
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 jeu de données avec une précision supérieure à 92%. Ces instances sont étiquetées automatiquement, ce qui libère le LLM et l'humain pour les cas plus difficiles.
Votre action : Relisez les fonctions générées et désactivez celles qui semblent peu fiables. Cette étape est facultative, car Potato ne conserve de toute façon que les fonctions au-dessus du seuil de précision que vous avez configuré.
Étape 9 : Annotation active (125-375 instances)
C'est ici que vous faites l'essentiel de l'étiquetage. Potato choisit les instances à l'aide des pools pondérés définis plus haut dans instance_selection :
low_confidence_weight(0,30) : avis sur lesquels la confiance du LLM est la plus faibledisagreement_weight(0,25) : avis sur lesquels le LLM et les fonctions d'étiquetage ne sont pas d'accorddiversity_weight(0,20) : avis choisis pour se répartir dans l'espace des embeddings plutôt que de se regrouperllm_predicted_weight(0,15) : avis étiquetés par le LLM que l'on ramène pour une comparaison humainerandom_weight(0,10) : avis aléatoires pour la calibration
Deux autres poids existent et restent ici à leur valeur par défaut de zéro, si bien qu'ils ne contribuent en rien : edge_case_rule_weight, pour les instances qui correspondent à une règle de cas limite découverte, et cartography_weight, pour les instances dont la confiance oscille d'un tour à l'autre. Potato vérifie que la somme des poids fait 1,0 et consigne un avertissement dans le cas contraire, mais il utilise les valeurs telles qu'elles sont écrites ; faites donc en sorte vous-même qu'elles totalisent 1,0.
Vous étiquetez ces instances par lots de 25. Après chaque lot, Potato met à jour l'estimation de précision du LLM et décide s'il faut continuer.
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 l'accord atteint votre end_human_annotation_agreement, le Solo Mode s'oriente vers l'étiquetage autonome. Sinon, il continue d'alterner entre relecture et affinement.
Estimation de temps : 45-90 minutes au total, selon le nombre de lots nécessaires.
Étape 10 : La boucle d'affinement
Si la précision reste sous le seuil après l'étiquetage actif, Potato lance un nouveau tour de la boucle d'affinement :
- Le LLM réétiquette l'ensemble du jeu de données avec les directives mises à jour et davantage d'exemples few-shot
- Potato recalcule la précision par rapport à toutes les étiquettes humaines
- Il repère les nouveaux schémas de confusion
- Il affine de nouveau les directives
Cette phase demande très peu d'intervention. Vous n'avez qu'à approuver les modifications des directives.
Résultat typique : La précision progresse de 2-4% par tour d'affinement.
Étape 11 : Résolution des désaccords
Potato présente les instances les plus litigieuses : 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 du LLM et sa confiance
- Les votes des fonctions d'étiquetage
- Les 3 exemples étiquetés les plus proches avec leurs étiquettes
- Le raisonnement chain-of-thought du LLM
Ce sont les cas difficiles, et vos étiquettes y pèsent davantage qu'ailleurs dans le processus.
Estimation de temps : 20-30 minutes pour 100-150 instances.
Étape 12 : Synthèse de cas limites
Potato génère des avis synthétiques qui ciblent les schémas de confusion restants. Par exemple, si le LLM a encore du mal avec les "avis neutres qui mentionnent un nouvel achat", il génère des exemples comme :
"C'est un produit correct pour le prix. J'en reprendrai peut-être un s'il y a une promotion."
Vous étiquetez ces exemples synthétiques et Potato les ajoute au contexte few-shot du LLM.
Estimation de temps : 10-15 minutes pour 30 exemples.
Étape 13 : Relecture périodique
À ce stade, le LLM a étiqueté la plus grande partie du jeu de données. Potato classe toutes ses étiquettes par confiance et vous envoie les moins sûres par lots de 25.
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 la précision ne bouge plus sur trois lots consécutifs, le Solo Mode considère les étiquettes restantes à confiance élevée comme fiables. Notez qu'il s'agit de stabilité, pas de perfection : l'exécution ci-dessus s'arrête après 92%, 96% et 100% parce que le chiffre s'est stabilisé, pas parce que le dernier lot était sans erreur.
Estimation de temps : 15-20 minutes.
Étape 14 : Optimisation du prompt
Cette phase se déroule toute seule. Potato essaie 8 variantes du prompt et garde celle qui obtient le meilleur score F1 sur les étiquettes humaines que vous avez accumulées :
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
Il utilise ensuite le meilleur prompt pour une dernière passe de réétiquetage.
Étape 15 : Validation finale
Potato tire 100 instances aléatoires étiquetées par le LLM pour que vous les relisiez. Vous les étiquetez, et Potato compare vos étiquettes à celles du LLM.
Final Validation:
Reviewed: 100 instances
LLM correct: 94/100 (94%)
Threshold: 93%
-> PASSED
Si le LLM dépasse votre seuil, le projet est terminé. Sinon, il revient à l'annotation active pour un nouveau tour. Ce retour est la seule transition autorisée depuis la validation finale.
Estimation de temps : 10-15 minutes.
Résumé des résultats
À la fin, le tableau de bord indique d'où viennent les étiquettes. Le Solo Mode n'a pas d'interface en ligne de commande (il s'exécute dans le serveur et se pilote depuis ses propres pages), donc ces chiffres se lisent sur l'instance en cours d'exécution et non dans un terminal :
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 instances sur 10 000, soit environ 6%. Le LLM et les fonctions d'étiquetage ont traité le reste avec une précision supérieure à 94%.
Exporter les résultats
Le Solo Mode écrit dans le même stockage que n'importe quel autre projet, donc les étiquettes sortent par l'exportateur habituel :
python -m potato.export -c config.yaml -f jsonl -o ./export/Chaque ligne contient l'étiquette et sa source :
{"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 :
import pandas as pd
df = pd.read_parquet("output/exports/parquet/annotations.parquet")
print(df["sentiment"].value_counts())annotations.parquet comporte une ligne par instance et par annotateur, chaque schéma d'annotation étant aplati dans sa propre colonne ; vous sélectionnez donc le schéma par son nom plutôt que par une colonne générique value.
Assurance qualité : vérification hybride
Pour des jeux de données de qualité publication, ajoutez un second annotateur qui relit un échantillon. La documentation source du Solo Mode décrit plus en détail les options de vérification.
Le Solo Mode n'a pas de bloc de vérification propre, il s'agit donc d'une seconde passe ordinaire : exportez les étiquettes, repassez-les dans Potato comme un projet normal avec un relecteur sur un échantillon de 10%, et calculez l'accord entre le relecteur et les étiquettes du Solo Mode comme vous le feriez pour deux annotateurs quelconques. (icl_labeling possède bien un bloc verification, mais il appartient à l'étiquetage en contexte, pas au Solo Mode.)
Dépannage
La précision du LLM plafonne sous le seuil
- Augmentez le nombre d'instances initiales : essayez 75-100 instances initiales au lieu de 50
- Changez de LLM : essayez
claude-sonnet-4-20250514au lieu de GPT-4o (ou l'inverse) - Abaissez le seuil : si 93% n'est pas atteignable, demandez-vous si 90% est acceptable pour votre cas d'usage
- Vérifiez vos données : certains jeux de données sont ambigus par nature. Si l'accord entre deux humains ne dépasserait pas 90%, n'attendez pas mieux du LLM
L'annotation active demande trop de lots
- Augmentez la taille des lots : passez
batch_sizede 25 à 50 - Ajustez les poids des pools : si la plupart des instances escaladées viennent du pool à faible confiance, réduisez
low_confidence_weightet augmentezdisagreement_weightoudiversity_weight
Les fonctions d'étiquetage ont une faible couverture
- C'est normal pour les tâches sans signaux lexicaux forts (par ex. détection du sarcasme, sentiment implicite)
- Les fonctions d'étiquetage fonctionnent mieux pour des schémas explicites fondés sur des mots-clés
- Le Solo Mode fonctionne toujours sans fonctions d'étiquetage ; le LLM couvre alors ces instances
Pour aller plus loin
- Documentation du Solo Mode : référence complète de la configuration
- Apprentissage actif : l'algorithme de sélection sous-jacent
- Support IA : configuration des fournisseurs de LLM
- Contrôle qualité : options supplémentaires d'assurance qualité
- Export Parquet : export de données efficace