Étiquetage avec un modèle dans la boucle, pré-étiquettes et réentraînement
Montrez les réponses d'un modèle entraîné sous forme de pré-étiquettes, relisez d'abord les pré-étiquettes les moins sûres et réentraînez le modèle à mesure que les annotateurs corrigent ses réponses.
Nouveauté de la v2.14.0
Ce tutoriel fait passer une étude de sentiment par le cycle du modèle dans la boucle. Un modèle s'entraîne sur les premières étiquettes, ses prédictions apparaissent aux annotateurs sous forme de pré-étiquettes, des relecteurs corrigent les pré-étiquettes les moins sûres, et les corrections alimentent l'exécution suivante. Il s'adresse aux responsables d'étude qui ont entraîné un premier modèle, comme dans Entraîner votre premier modèle, et veulent maintenant que les annotateurs travaillent aux côtés du modèle. Toutes les étapes sauf une tournent sur un portable avec sklearn-text. L'étape d'acquisition entraîne un petit transformeur et prend quelques minutes sur un CPU.
Le projet de sentiment
Le projet est l'exemple d'entraînement de modèles de Potato, 160 avis de produits synthétiques avec un schéma radio nommé sentiment. Une pré-étiquette est une étiquette qu'un modèle a proposée pour un item avant que quiconque l'étiquette. L'écriture en retour est l'étape qui transforme les prédictions d'une exécution d'entraînement en pré-étiquettes sur la page d'annotation. À la fin du tutoriel, le projet entraîne un classifieur sur les 120 premières étiquettes, publie des pré-étiquettes sur les avis restants, sert d'abord les avis les moins sûrs, réentraîne à mesure que de nouvelles étiquettes arrivent et conserve une partition réservée comme version de jeu de données.
Les pré-étiquettes accélèrent l'annotation et attirent aussi les annotateurs vers la réponse du modèle. Étiquetage assisté par un modèle entraîné sur vos étiquettes explique quand le compromis vaut la peine, et Distinguer relecture et validation mécanique dans des données pré-étiquetées traite des annotateurs qui acceptent les pré-étiquettes sans les lire. La configuration ci-dessous met à l'écart dès le départ une part des annotateurs de toute pré-étiquette. Les réponses de ces annotateurs permettent à l'étude de mesurer l'attraction.
Préparer le projet
Installez Potato. sklearn-text ne demande rien de plus que le paquet de base.
pip install --upgrade potato-annotationCopiez examples/ai-assisted/model-training/ du dépôt Potato dans un nouveau dossier. Le dossier contient data/reviews.json, avec les 160 avis, et data/gold.json, les étiquettes à partir desquelles répond le simulateur. Remplacez le config.yaml de l'exemple par cette configuration.
annotation_task_name: "Review Sentiment (model in the loop)"
data_files:
- "data/reviews.json"
item_properties:
id_key: "id"
text_key: "text"
task_dir: "."
output_annotation_dir: "annotation_output"
require_password: false
admin_api_key: "demo-admin-key"
num_annotators_per_item: 1
assignment_strategy: llm_confidence
annotation_schemes:
- annotation_type: "radio"
name: "sentiment"
description: "Is this review positive or negative?"
labels:
- "positive"
- "negative"
sequential_key_binding: true
ai_support:
holdout:
fraction: 0.2
salt: "review-sentiment"
datasets:
enabled: true
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6Chaque bloc ajouté à la configuration de l'exemple remplit une fonction.
assignment_strategy: llm_confidencesert chaque item, en commençant par la pré-étiquette la moins sûre. Les items sans prédiction viennent en dernier, dans l'ordre de la file.ai_support.holdoutretient toute forme d'assistance par IA, pré-étiquettes comprises, pour 20 % des paires annotateur-item. Définissez la réserve avant le début de l'annotation, car changerfractionousalten cours d'étude redistribue les groupes.datasets.enabledpermet d'enregistrer la partition d'une exécution comme version de jeu de données à la dernière étape.writeback.schemasnomme les schémas dont les prédictions sont montrées aux annotateurs. La liste est vide par défaut, et activer l'entraînement ne change rien sur la page d'annotation tant que la liste ne nomme pas un schéma.include_unlabeled: truefait prédire à chaque exécution les avis que personne n'a encore étiquetés. Sans ce paramètre, une exécution ne prédit que sur les items réservés val et test de l'exécution. Ces items portent déjà des étiquettes humaines.min_confidence: 0.6garde hors de la vue des annotateurs une prédiction dont la confiance est inférieure à 0,6. Potato conserve tout de même la prédiction avec l'exécution.
writeback.field garde la valeur par défaut, predictions, le champ d'item dans lequel les pré-étiquettes sont écrites. Avec cette valeur, aucun bloc pre_annotation n'est nécessaire. Vérifiez la configuration avant de démarrer le serveur.
potato validate config.yaml
potato start config.yaml -p 8000Étiqueter un ensemble de départ
Un modèle entraîné sur vos étiquettes a d'abord besoin d'étiquettes. sklearn-text refuse de s'ajuster avec moins de 10 items dans la partition d'entraînement. Étiquetez quelques avis sur http://localhost:8000, ou laissez trois annotateurs simulés étiqueter chacun 40 avis à partir du fichier de référence.
python -m potato.simulator --server http://localhost:8000 --users 3 \
--strategy gold_standard --competence good=1.0 --max-annotations 40 \
--gold-file data/gold.json --sequential --fast-mode --no-export--competence good=1.0 donne à chaque annotateur simulé le profil good, qui répond correctement à 80 à 90 % des avis. Avec num_annotators_per_item: 1, les trois annotateurs couvrent 120 avis différents et en laissent 40 sans étiquette pour le modèle.
Entraîner et lire le rapport d'écriture en retour
Ouvrez http://localhost:8000/admin/training avec la clé d'administration, choisissez sklearn-text et le schéma sentiment, puis cliquez sur Train now. La même exécution se lance depuis la ligne de commande.
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'L'exécution dure quelques secondes. Potato répartit les avis étiquetés entre train, val et test (70 %, 15 % et 15 % par défaut), ajuste le classifieur sur train, puis évalue le classifieur sur val et test. La route d'état rapporte ensuite les scores et le rapport d'écriture en retour.
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/training/api/statusUne exécution de ce tutoriel a renvoyé ce qui suit, réduit aux champs utiles ici.
{
"metrics": {
"heldout_metric": "val macro_f1",
"heldout_score": 0.7333,
"n_train": 76.0,
"val_n": 20.0,
"test_n": 24.0,
"test_macro_f1": 0.8222
},
"writeback": {
"n_read": 84,
"n_written": 78,
"n_skipped_low_confidence": 6,
"n_skipped_train_split": 0,
"n_skipped_rejected": 0,
"n_skipped_schema_not_enabled": 0,
"summary": "Published 78 prediction(s) as prelabels."
}
}L'exécution a prédit sur 84 avis, les 44 avis val et test plus les 40 que personne n'avait étiquetés. Six prédictions sont tombées sous le seuil de 0,6, et les 78 autres sont devenues des pré-étiquettes. Vos scores et vos comptes différeront d'une exécution à l'autre, car les annotateurs simulés commettent des erreurs aléatoires et étiquettent des avis différents. Chaque compteur d'items écartés correspond à l'une des quatre vérifications de l'écriture en retour, que La configuration liste dans l'ordre.
L'écriture en retour ne modifie jamais une étiquette humaine. Les prédictions vont dans les données de l'item, et les annotations restent dans l'état propre à chaque annotateur. Connectez-vous sous un nouveau nom d'utilisateur pour voir le résultat. L'avis suivant arrive avec la réponse du modèle sélectionnée.
Un nouvel annotateur voit la réponse du modèle sélectionnée et la confirme ou la change
Une pré-étiquette n'est pas enregistrée tant que l'annotateur se contente de la regarder. La pré-étiquette devient la réponse de l'annotateur quand celui-ci la modifie ou passe à l'avis suivant. Un avis sans réponse ne compte donc pas comme terminé, et la réponse d'un modèle ne revient pas à l'entraînement comme une étiquette humaine.
Lisez le score sur items réservés avant de montrer des pré-étiquettes aux annotateurs. Un score val de 0,73 en macro-F1 sur 20 avis laisse beaucoup de place à de fausses pré-étiquettes. La référence amont sur l'écriture en retour liste les autres compteurs d'items écartés et la façon dont chaque type d'annotation montre une pré-étiquette.
Relire les pré-étiquettes les moins sûres
Avec llm_confidence, l'annotateur suivant reçoit les 40 avis sans étiquette classés par la confiance de la pré-étiquette publiée, de la plus basse à la plus haute. Une seconde stratégie sert les pré-étiquettes pour le contrôle qualité plutôt que pour l'étiquetage. assignment_strategy: model_review ne sert que les items qui portent une pré-étiquette, en commençant par la moins sûre, et laisse de côté les items sans prédiction. Passez à model_review pour une passe de relecture de la sortie du modèle, et restez sur llm_confidence tant que chaque item a encore besoin d'une étiquette. La référence amont sur la relecture des modèles explique les deux files.
Le rapport de relecture des modèles montre la file et ce que le modèle a laissé de côté.
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/model-reviewLe rapport de l'exécution ci-dessus, avec les listes abrégées.
{
"n_items": 160,
"n_prelabelled": 78,
"queue": ["review_062", "review_024", "review_044", "review_122", "..."],
"worst_first": ["review_062", "review_024", "review_044", "..."],
"n_empty": 82,
"empty_prediction_ids": ["review_006", "review_007", "review_008", "..."],
"provenance": {
"review_062": {
"sentiment": {
"run_id": "20261104T043431-de5686",
"trainer": "sklearn-text",
"heldout_metric": "val macro_f1",
"heldout_score": 0.7333
}
}
}
}Le rang d'un item dans queue est la confiance de prédiction la plus basse de l'item sur tous les schémas. worst_first est la tranche des 20 % les moins sûrs de la file, jamais moins d'un item. empty_prediction_ids liste les items sans pré-étiquette. Dans ce projet, les 82 items vides sont les 76 avis d'entraînement et les 6 prédictions sous le seuil. Les avis d'entraînement ne reçoivent jamais de pré-étiquette.
provenance consigne l'origine de chaque pré-étiquette. Pour chaque item de la file dont la pré-étiquette a été écrite par une exécution d'entraînement, l'entrée nomme l'exécution, le module d'entraînement et le score sur items réservés de l'exécution. Une pré-étiquette arrivée avec le fichier de données n'a pas d'entrée. Quand une pré-étiquette paraît fausse, l'identifiant d'exécution mène à la page de l'exécution à l'adresse /admin/training/runs/<run_id>, qui montre les paramètres et la partition de chaque item.
Consigner des verdicts
Un verdict consigne le jugement d'un relecteur sur une pré-étiquette, séparément de l'annotation du relecteur. « Le modèle avait raison » et « une personne a choisi cette étiquette de façon indépendante » sont deux faits différents, et les garder séparés permet de continuer à mesurer la précision du modèle. Potato reçoit les verdicts par une seule route, de l'annotateur à qui l'item est attribué ou d'un administrateur avec la clé d'API.
curl -X POST -H "Content-Type: application/json" -H "X-API-Key: demo-admin-key" \
-d '{"instance_id": "review_062", "verdict": "correct", "schema_name": "sentiment"}' \
http://localhost:8000/api/model-review/verdictaccept signifie que le modèle avait raison, correct signifie qu'une personne a changé la réponse, et reject signifie que le modèle avait tort. Un reject retire aussitôt la pré-étiquette de l'item, et les exécutions suivantes ne publient plus de prédiction pour cet item et ce schéma. Le reject compte toujours contre la précision du modèle, comme une pré-étiquette rejetée et non comme une étiquette que le modèle a manquée, et renvoyer le même verdict ne change aucun compte. Un verdict correct fait passer l'item dans la partition train de toutes les exécutions suivantes, comme le décrit la section sur les corrections plus bas. Consigner un second verdict sur le même item remplace le premier, et un verdict inconnu est refusé avec le code HTTP 400.
Ordonner la file par un score d'acquisition
L'ordre de confiance sert d'abord les items pour lesquels la meilleure réponse du modèle est la plus faible. Une fonction d'acquisition en apprentissage actif classe les items sans étiquette selon l'aide qu'apporterait une étiquette sur chacun au modèle suivant. hf-text-classification et hf-image-classification acceptent un paramètre acquisition qui écrit un tel score sur chaque prédiction, et llm_confidence sert alors d'abord le score le plus élevé. Le module d'entraînement a besoin de l'extra d'entraînement de texte.
pip install "potato-annotation[train-text]"Lancez une exécution avec le paramètre, depuis le champ Parameters de la page Training ou depuis l'API.
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs \
-d '{"trainer": "hf-text-classification", "schemas": ["sentiment"],
"params": {"acquisition": "margin"}}'Le paramètre accepte cinq valeurs.
acquisition | Score écrit sur chaque prédiction |
|---|---|
least_confident | La valeur par défaut. Aucun score n'est écrit, et les items gardent l'ordre de confiance. |
margin | 1 moins l'écart entre les deux premières probabilités. |
entropy | Entropie de la distribution prédite, ramenée à l'échelle 0 à 1. |
bald | Information mutuelle entre l'étiquette et les poids du modèle, calculée à partir de mc_samples (10 par défaut) passes avant avec le dropout activé. La méthode vient de Houlsby et al. (2011). Le score est élevé là où le modèle manque de données et bas là où un item est ambigu quelles que soient les données. |
coreset | La distance de chaque item à l'item d'entraînement ou à l'item déjà choisi le plus proche, dans l'espace des plongements du modèle. La méthode vient de Sener et Savarese (2018). Le score répartit la file sur l'ensemble des données, donc dix quasi-copies d'un même item difficile ne prennent pas les dix premières places. |
Une prédiction de l'exécution avec marge porte le score à côté de la confiance.
{"label": "positive", "confidence": 0.5207,
"probabilities": {"negative": 0.4793, "positive": 0.5207},
"acquisition": 0.9586}Avec llm_confidence, les items qui ont un score d'acquisition viennent d'abord, puis les items qui n'ont qu'une confiance, puis les items dont la prédiction n'a pas de confiance, puis les items sans prédiction. acquisition fonctionne sur les schémas à étiquette unique (radio, select, likert). L'ordre lit les pré-étiquettes publiées, donc le schéma doit figurer dans writeback.schemas, et include_unlabeled: true doit être défini pour que les items sans étiquette reçoivent un score. model_review ignore le score et garde l'ordre de confiance, car une passe de relecture cherche les mauvaises réponses du modèle. La section amont sur le choix de la prochaine question donne les détails.
Comparez le score sur items réservés de la nouvelle exécution à celui de l'exécution sklearn-text avant de conserver les pré-étiquettes de la nouvelle. Potato conserve une prédiction par item et par schéma, et la prédiction d'une exécution plus récente remplace une plus ancienne. Quand la prédiction de l'exécution plus récente est conservée mais non montrée, par exemple sous min_confidence, la pré-étiquette de l'exécution plus ancienne est aussi retirée de l'item, comme le décrit La configuration. Dans une exécution de ce tutoriel sur un CPU, trois époques du modèle par défaut (epochs: 3 dans params) ont obtenu 0,52 de macro-F1 sur test avec 84 avis d'entraînement, en dessous de l'exécution sklearn-text sur les mêmes étiquettes, et l'exécution a publié 37 pré-étiquettes. Pour retirer les pré-étiquettes d'une exécution, supprimez les prédictions de l'exécution.
curl -X DELETE -H "X-API-Key: demo-admin-key" \
http://localhost:8000/admin/training/api/runs/<run_id>/predictionsLe retrait supprime les prédictions conservées de l'exécution et enlève ses pré-étiquettes des items. Les pré-étiquettes d'une exécution antérieure ne reviennent pas, donc entraînez à nouveau pour republier.
Réentraîner automatiquement
auto_retrain lance une exécution de lui-même à mesure que les annotations arrivent. Ajoutez le bloc à model_training quand le score sur items réservés d'une exécution justifie de montrer des pré-étiquettes.
model_training:
enabled: true
writeback:
schemas: ["sentiment"]
include_unlabeled: true
min_confidence: 0.6
auto_retrain:
enabled: true
trainer: sklearn-text
schemas: ["sentiment"]
update_frequency: 25
min_interval_s: 600Potato vérifie après chaque enregistrement d'annotation. Une exécution démarre dès que 25 annotations sont arrivées depuis la dernière exécution automatique et que 600 secondes se sont écoulées depuis la fin de la dernière exécution. Les deux valeurs sont les valeurs par défaut, et trainer et schemas sont obligatoires. params transmet les paramètres du module d'entraînement, comme le fait le champ Parameters de la page Training.
Activez auto_retrain après la première exécution manuelle plutôt que dès la première annotation. Un projet sans aucune exécution s'entraîne dès qu'il compte update_frequency annotations. Avec update_frequency: 20 défini dès le départ, la première exécution automatique de ce projet s'est entraînée sur 12 avis, a obtenu un macro-F1 sur val de 1,0 sur 3 avis et a publié 72 pré-étiquettes. Un score sur items réservés fondé sur 3 avis ne dit presque rien des 72 suivants.
Quand une vérification décide de ne pas lancer d'exécution, skip_reason dans /admin/training/api/status donne la raison, par exemple « waiting for 3 more annotation(s) » ou « waiting 299 more second(s) since the last run ». Les exécutions automatiques ne rejoignent pas la file d'attente des exécutions. La prochaine annotation enregistrée après que la file s'est vidée déclenche une nouvelle vérification. Les compteurs vivent en mémoire, et après un redémarrage un projet qui a déjà eu une exécution compte les nouvelles annotations à partir du redémarrage. Un module d'entraînement dont la licence doit être confirmée (ultralytics-detect, ultralytics-segment, layoutlmv3) demande aussi accept_licence: true dans le bloc, sinon chaque exécution automatique est ignorée. La configuration complète avec les deux blocs passe potato validate config.yaml --strict.
Items corrigés et épinglés dans l'entraînement
Deux sortes d'items vont dans la partition train de toutes les exécutions suivantes, quelle que soit la place de l'item dans la partition stable. La première sorte est un item avec un verdict correct. Les corrections viennent des items sur lesquels le modèle était le moins bon, et dans val ou test elles évalueraient le modèle sur un échantillon de ses propres erreurs. Les verdicts accept et reject ne déplacent pas un item. La seconde sorte est un item épinglé, qu'un administrateur envoie à l'entraînement à la main.
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/pinned \
-d '{"instance_ids": ["review_007", "review_008"]}'Un item épinglé entre dans l'entraînement dès que quelqu'un l'a étiqueté, et jusque-là chaque exécution le compte comme en attente. Avec curation: {enabled: true}, le bouton Train on these à côté d'une tranche de la page Catalog épingle tout ce que la tranche contient, par exemple la tranche intégrée model-found-nothing. DELETE /admin/training/api/pinned retire l'épinglage.
Une exécution qui a déplacé des items réservés vers train consigne un avertissement avec leur nombre. Après un verdict correct, l'exécution suivante de ce tutoriel a consigné ce qui suit.
1 held-out item(s) moved to train (corrected: 1). Held-out scores are computed without them, so they are not directly comparable with a run that had them.Les comptes figurent dans le manifest.json du lot de l'exécution, sous stats.forced_train. Comparez les scores sur items réservés d'une exécution à l'autre en gardant l'avertissement en tête, puisque les deux exécutions ont évalué des items différents.
Enregistrer la partition test d'une exécution comme jeu de données
Un score sur items réservés change quand les partitions changent, et un ensemble fixe d'items est ce qui rend deux modèles comparables. La section Keep this split as a dataset de la page de l'exécution enregistre les items val, test ou train d'une exécution, avec leurs étiquettes humaines consolidées, comme nouvelle version d'un jeu de données. Choisissez la partition, saisissez un nom de jeu de données et cliquez sur Save as dataset version. La route derrière le bouton accepte la même requête depuis la ligne de commande.
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
http://localhost:8000/admin/training/api/runs/<run_id>/pin-split \
-d '{"dataset": "sentiment-test", "split": "test"}'La réponse nomme la nouvelle version et le nombre d'items, ici 23 avis test enregistrés comme version v0001. Une version ne change plus après l'enregistrement, donc des modèles et des prompts de LLM ultérieurs peuvent être évalués dans l'exécuteur d'expériences des jeux de données sur exactement ces items. Enregistrer dans un jeu de données qui a déjà des versions crée une nouvelle version qui ne contient que cette partition, donc les items val d'une exécution ultérieure ne peuvent pas se mêler à des items sur lesquels cette exécution s'est entraînée. Les entrées de chaque exemple omettent le champ des pré-étiquettes, donc une évaluation ne peut pas lire la réponse du modèle. La route renvoie le code HTTP 400 quand datasets.enabled n'est pas défini.
Mettre des annotateurs à l'écart des pré-étiquettes
Le bloc ai_support.holdout de la configuration garde 20 % des paires annotateur-item sans aucune assistance dès la première annotation. Pour une paire mise à l'écart, la page d'annotation ne montre aucune pré-étiquette, et les données d'item envoyées au navigateur omettent à la fois le writeback.field (ici predictions) et le pre_annotation.field. Le tirage est un hachage salé par annotateur et par item, donc une paire reste dans le même groupe à chaque visite et après un redémarrage. Avec plusieurs annotateurs par avis, la plupart des avis ont des annotateurs assistés et des annotateurs mis à l'écart. L'onglet AI influence du tableau de bord d'administration compare les deux groupes sur la fréquence à laquelle les réponses correspondent au modèle, la fréquence à laquelle elles correspondent à la référence et l'accord à l'intérieur de chaque groupe. Avec num_annotators_per_item: 1, chaque avis tombe dans un seul groupe, donc relevez la valeur au-dessus de 1 pour une étude qui mesure l'effet. Étiquetage assisté par un modèle entraîné sur vos étiquettes explique comment lire la comparaison.
Pour aller plus loin
- L'entraînement de modèles explique la boucle d'entraînement et la page Training.
- La configuration de l'entraînement de modèles liste chaque clé
writebacketauto_retrainavec ses valeurs par défaut. - Entraîner votre premier modèle prépare ce projet et une première exécution
sklearn-text. - Entraîner des modèles de détection et de segmentation sur des annotations d'images exécute la même boucle avec un détecteur de boîtes sur des images.
- Étiquetage assisté par un modèle entraîné sur vos étiquettes explique quand les pré-étiquettes font gagner du temps et quand elles ancrent les réponses.
- Distinguer relecture et validation mécanique dans des données pré-étiquetées repère les annotateurs qui acceptent les pré-étiquettes sans les lire.
- Apprentissage actif explique comment choisir l'item suivant sans montrer la réponse d'un modèle.
- La référence amont sur l'entraînement de modèles donne chaque vérification et chaque route de l'écriture en retour.
Références
- Houlsby, N., Huszár, F., Ghahramani, Z., and Lengyel, M. (2011). Bayesian Active Learning for Classification and Preference Learning. arXiv:1112.5745. https://arxiv.org/abs/1112.5745
- Sener, O., and Savarese, S. (2018). Active Learning for Convolutional Neural Networks: A Core-Set Approach. ICLR 2018. https://arxiv.org/abs/1708.00489