Vai al contenuto

Etichettatura con il modello nel ciclo, tra pre-etichette e riaddestramento

Mostra le risposte di un modello addestrato come pre-etichette, rivedi per prime le pre-etichette meno sicure e riaddestra il modello mentre gli annotatori correggono le risposte.

Novità nella v2.14.0

Questo tutorial percorre il ciclo con il modello nel ciclo su un solo studio di sentiment. Un modello si addestra sulle prime etichette, le predizioni del modello compaiono agli annotatori come pre-etichette, i revisori correggono le pre-etichette meno sicure e le correzioni alimentano l'esecuzione successiva. Il tutorial è per i responsabili di studio che hanno addestrato un primo modello, come in Addestra il tuo primo modello, e ora vogliono che gli annotatori lavorino accanto al modello. Ogni passo tranne uno gira su un portatile con sklearn-text. Il passo di acquisizione addestra un piccolo transformer e richiede qualche minuto su una CPU.

Il progetto di sentiment

Il progetto è l'esempio di addestramento di modelli di Potato, 160 recensioni di prodotti sintetiche con un solo schema radio chiamato sentiment. Una pre-etichetta è un'etichetta che un modello ha proposto per un item prima che una persona lo etichettasse. Il write-back è il passo che trasforma le predizioni di un'esecuzione di addestramento in pre-etichette nella pagina di annotazione. Alla fine del tutorial, il progetto addestra un classificatore sulle prime 120 etichette, pubblica pre-etichette sulle recensioni restanti, serve per prime le recensioni meno sicure, riaddestra man mano che arrivano nuove etichette e conserva una partizione esclusa dall'addestramento come versione di un dataset.

Le pre-etichette rendono l'annotazione più veloce e spingono anche gli annotatori verso la risposta del modello. Etichettatura assistita dal modello con un modello addestrato sulle tue etichette tratta quando vale la pena accettare questo scambio, e Come distinguere la revisione dall'approvazione automatica nei dati pre-etichettati tratta gli annotatori che accettano le pre-etichette senza leggerle. La configurazione seguente esclude fin dall'inizio da ogni pre-etichetta una quota di annotatori. Le risposte di chi è escluso permettono allo studio di misurare l'influenza.

Preparare il progetto

Installa Potato. sklearn-text non richiede nulla oltre al pacchetto di base:

bash
pip install --upgrade potato-annotation

Copia examples/ai-assisted/model-training/ dal repository di Potato in una nuova cartella. La cartella contiene data/reviews.json, con le 160 recensioni, e data/gold.json, le etichette da cui risponde il simulatore. Sostituisci il config.yaml dell'esempio con questa configurazione:

yaml
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.6

Ogni blocco aggiunto alla configurazione dell'esempio svolge un compito:

  • assignment_strategy: llm_confidence serve ogni item, a partire dalla pre-etichetta meno sicura. Gli item senza predizione vengono per ultimi, nell'ordine della coda.
  • ai_support.holdout nega ogni forma di assistenza dell'IA, pre-etichette comprese, al 20% delle coppie annotatore-item. Imposta l'holdout prima che l'annotazione inizi, perché cambiare fraction o salt a studio avviato rimescola i gruppi.
  • datasets.enabled permette di salvare la partizione di un'esecuzione come versione di un dataset nell'ultimo passo.
  • writeback.schemas indica gli schemi le cui predizioni vedono gli annotatori. L'elenco è vuoto per impostazione predefinita, e attivare l'addestramento non cambia nulla nella pagina di annotazione finché l'elenco non indica uno schema.
  • include_unlabeled: true fa predire a ogni esecuzione anche le recensioni che nessuno ha ancora etichettato. Senza questa impostazione, un'esecuzione predice solo gli item val e test esclusi dall'addestramento. Quegli item hanno già etichette umane.
  • min_confidence: 0.6 tiene fuori dalla vista degli annotatori una predizione con confidenza inferiore a 0,6. Potato memorizza comunque la predizione con l'esecuzione.

writeback.field mantiene il valore predefinito, predictions, il campo dell'item in cui vengono scritte le pre-etichette. Con il valore predefinito non serve alcun blocco pre_annotation. Controlla la configurazione prima di avviare il server:

bash
potato validate config.yaml
potato start config.yaml -p 8000

Etichettare un insieme iniziale

Un modello addestrato sulle tue etichette ha prima bisogno di etichette. sklearn-text si rifiuta di eseguire l'adattamento con meno di 10 item nella partizione di addestramento. Etichetta alcune recensioni su http://localhost:8000, oppure fai etichettare a tre annotatori simulati 40 recensioni ciascuno dal file gold:

bash
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 assegna a ogni annotatore simulato il profilo good, che risponde correttamente all'80–90% delle recensioni. Con num_annotators_per_item: 1, i tre annotatori coprono 120 recensioni diverse e ne lasciano 40 senza etichetta per il modello.

Addestrare e leggere il rapporto di write-back

Apri http://localhost:8000/admin/training con la chiave di amministrazione, scegli sklearn-text e lo schema sentiment e fai clic su Train now. La stessa esecuzione parte dalla riga di comando:

bash
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'esecuzione richiede pochi secondi. Potato divide le recensioni etichettate in train, val e test (70%, 15% e 15% per impostazione predefinita), adatta il classificatore su train e valuta il classificatore su val e test. La route di stato riporta poi i punteggi e il rapporto di write-back:

bash
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/training/api/status

Un'esecuzione di questo tutorial ha restituito quanto segue, ridotto ai campi che qui contano:

json
{
  "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'esecuzione ha predetto 84 recensioni, le 44 recensioni di val e test più le 40 che nessuno aveva etichettato. Sei predizioni sono scese sotto la soglia di 0,6, e le altre 78 sono diventate pre-etichette. I tuoi punteggi e conteggi differiranno da un'esecuzione all'altra, perché gli annotatori simulati commettono errori casuali ed etichettano recensioni diverse. Ogni contatore di item saltati nomina uno dei quattro controlli del write-back, che la configurazione elenca in ordine.

Il write-back non cambia mai un'etichetta umana. Le predizioni vanno nei dati dell'item, e le annotazioni restano nello stato di ciascun annotatore. Accedi con un nuovo nome utente per vedere il risultato. La recensione successiva arriva con la risposta del modello selezionata:

Una recensione di prodotto nella pagina di annotazione di Potato con Positive già selezionato dal modello, prima che l'annotatore abbia rispostoUn nuovo annotatore vede selezionata la risposta del modello e la conferma o la cambia

Una pre-etichetta non viene salvata finché l'annotatore si limita a guardarla. La pre-etichetta diventa la risposta dell'annotatore quando l'annotatore la modifica o passa alla recensione successiva. Una recensione senza risposta non conta quindi come completata, e la risposta di un modello non torna all'addestramento come etichetta umana.

Leggi il punteggio held-out prima di mostrare le pre-etichette agli annotatori. Un punteggio val di 0,73 di macro-F1 su 20 recensioni lascia molto spazio a pre-etichette sbagliate. Il riferimento di Potato sul write-back elenca gli altri contatori di item saltati e come ogni tipo di annotazione mostra una pre-etichetta.

Rivedere le pre-etichette meno sicure

Con llm_confidence, il prossimo annotatore riceve le 40 recensioni senza etichetta ordinate per confidenza della pre-etichetta pubblicata, dalla più bassa. Una seconda strategia serve le pre-etichette per il controllo di qualità e non per l'etichettatura. assignment_strategy: model_review serve solo gli item che portano una pre-etichetta, a partire dalla meno sicura, ed esclude gli item senza predizione. Passa a model_review per un giro di revisione dell'output del modello, e resta su llm_confidence finché ogni item ha ancora bisogno di un'etichetta. Il riferimento di Potato sulla revisione del modello spiega entrambe le code.

Il rapporto di revisione del modello mostra la coda e ciò che il modello ha lasciato fuori:

bash
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/model-review

Il rapporto dell'esecuzione qui sopra, con gli elenchi accorciati:

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

La posizione di un item in queue è la confidenza di predizione più bassa dell'item tra gli schemi. worst_first è il 20% meno sicuro della coda, mai meno di un item. empty_prediction_ids elenca gli item senza pre-etichetta. In questo progetto i 82 item vuoti sono le 76 recensioni di addestramento e le 6 predizioni sotto la soglia. Le recensioni di addestramento non ricevono mai una pre-etichetta.

provenance registra l'origine di ogni pre-etichetta. Per ogni item della coda la cui pre-etichetta è stata scritta da un'esecuzione di addestramento, la voce indica l'esecuzione, il trainer e il punteggio held-out dell'esecuzione. Una pre-etichetta arrivata con il file di dati non ha alcuna voce. Quando una pre-etichetta sembra sbagliata, l'ID dell'esecuzione porta alla pagina dell'esecuzione in /admin/training/runs/<run_id>, che mostra i parametri e la partizione di ogni item.

Registrare i verdetti

Un verdetto registra il giudizio di un revisore su una pre-etichetta, separatamente dall'annotazione del revisore. "Il modello aveva ragione" e "una persona ha scelto questa etichetta in modo indipendente" sono fatti diversi, e tenerli distinti mantiene misurabile la precisione del modello. Potato riceve i verdetti attraverso una sola route, dall'annotatore a cui l'item è assegnato o da un amministratore con la chiave API:

bash
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/verdict

accept significa che il modello aveva ragione, correct significa che una persona ha cambiato la risposta e reject significa che il modello aveva torto. Un reject toglie subito la pre-etichetta dall'item, e le esecuzioni successive non pubblicano più una predizione per quell'item e quello schema. Il reject conta comunque contro la precisione del modello, come pre-etichetta rifiutata e non come etichetta che il modello ha mancato, e inviare di nuovo lo stesso verdetto non cambia alcun conteggio. Un verdetto correct sposta l'item nella partizione train di ogni esecuzione successiva, come descrive più sotto la sezione sulle correzioni. Registrare un secondo verdetto sullo stesso item sostituisce il primo, e un verdetto sconosciuto viene rifiutato con HTTP 400.

Ordinare la coda con un punteggio di acquisizione

L'ordine per confidenza serve per primi gli item su cui la risposta principale del modello è più debole. Una funzione di acquisizione nell'apprendimento attivo classifica gli item non etichettati in base a quanto un'etichetta su ciascun item aiuterebbe il modello successivo. hf-text-classification e hf-image-classification accettano un parametro acquisition che scrive un punteggio simile su ogni predizione, e llm_confidence serve poi per primo il punteggio più alto. Il trainer ha bisogno dell'extra per l'addestramento su testo:

bash
pip install "potato-annotation[train-text]"

Avvia un'esecuzione con il parametro, dal campo Parameters della pagina Training oppure dall'API:

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

Il parametro accetta cinque valori:

acquisitionPunteggio scritto su ogni predizione
least_confidentIl valore predefinito. Non viene scritto alcun punteggio, e gli item mantengono l'ordine per confidenza.
margin1 meno la differenza tra le prime due probabilità.
entropyEntropia della distribuzione predetta, scalata da 0 a 1.
baldInformazione mutua tra l'etichetta e i pesi del modello, ricavata da mc_samples (10 per impostazione predefinita) passaggi in avanti con il dropout attivo. Il metodo è di Houlsby et al. (2011). Il punteggio è alto dove al modello mancano dati ed è basso dove un item è ambiguo qualunque siano i dati.
coresetLa distanza di ogni item dall'item di addestramento più vicino o dall'item già scelto più vicino, sugli embedding del modello. Il metodo è di Sener e Savarese (2018). Il punteggio distribuisce la coda sui dati, così dieci quasi-copie di un solo item difficile non occupano i primi dieci posti.

Una predizione dell'esecuzione con margin porta il punteggio accanto alla confidenza:

json
{"label": "positive", "confidence": 0.5207,
 "probabilities": {"negative": 0.4793, "positive": 0.5207},
 "acquisition": 0.9586}

Con llm_confidence, vengono prima gli item con un punteggio di acquisizione, poi gli item con la sola confidenza, poi gli item la cui predizione non ha confidenza, poi gli item senza predizione. acquisition funziona sugli schemi a etichetta singola (radio, select, likert). L'ordine legge le pre-etichette pubblicate, quindi lo schema deve stare in writeback.schemas, e include_unlabeled: true deve essere impostato perché gli item non etichettati ricevano un punteggio. model_review ignora il punteggio e mantiene l'ordine per confidenza, perché un giro di revisione cerca le risposte sbagliate del modello. La sezione di Potato su come scegliere che cosa chiedere dopo ha i dettagli.

Confronta il punteggio held-out della nuova esecuzione con quello dell'esecuzione sklearn-text prima di tenere le pre-etichette della nuova esecuzione. Potato conserva una predizione per item e schema, e la predizione di un'esecuzione più recente sostituisce quella di una più vecchia. Dove la predizione dell'esecuzione più recente è memorizzata ma non mostrata, ad esempio sotto min_confidence, anche la pre-etichetta dell'esecuzione più vecchia viene tolta dall'item, come descrive la configurazione. In un'esecuzione di questo tutorial su una CPU, tre epoche del modello predefinito (epochs: 3 in params) hanno ottenuto 0,52 di macro-F1 su test con 84 recensioni di addestramento, sotto l'esecuzione sklearn-text sulle stesse etichette, e l'esecuzione ha pubblicato 37 pre-etichette. Per ritirare le pre-etichette di un'esecuzione, elimina le predizioni dell'esecuzione:

bash
curl -X DELETE -H "X-API-Key: demo-admin-key" \
  http://localhost:8000/admin/training/api/runs/<run_id>/predictions

Il ritiro rimuove le predizioni memorizzate dell'esecuzione e toglie dagli item le pre-etichette dell'esecuzione. Le pre-etichette di un'esecuzione precedente non tornano, quindi addestra di nuovo per ripubblicare.

Riaddestrare automaticamente

auto_retrain avvia da solo un'esecuzione man mano che arrivano le annotazioni. Aggiungi il blocco a model_training quando il punteggio held-out di un'esecuzione giustifica la visualizzazione delle pre-etichette:

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

Potato controlla dopo ogni salvataggio di un'annotazione. Un'esecuzione parte quando sono arrivate 25 annotazioni dall'ultima esecuzione automatica e sono passati 600 secondi dalla fine dell'ultima esecuzione. Entrambi i valori sono quelli predefiniti, e trainer e schemas sono obbligatori. params passa i parametri del trainer, come fa il campo Parameters della pagina Training.

Attiva auto_retrain dopo la prima esecuzione manuale e non dalla prima annotazione. Un progetto senza ancora alcuna esecuzione si addestra non appena il progetto ha update_frequency annotazioni. Con update_frequency: 20 impostato fin dall'inizio, la prima esecuzione automatica di questo progetto si è addestrata su 12 recensioni, ha ottenuto 1,0 di macro-F1 su val con 3 recensioni e ha pubblicato 72 pre-etichette. Un punteggio held-out su 3 recensioni dice quasi nulla sulle 72 successive.

Quando un controllo decide di non avviare un'esecuzione, skip_reason in /admin/training/api/status ne dà il motivo, come "waiting for 3 more annotation(s)" o "waiting 299 more second(s) since the last run". Le esecuzioni automatiche non entrano nella coda delle esecuzioni. La prima annotazione salvata dopo che la coda si è svuotata fa partire un nuovo controllo. I contatori stanno in memoria, e dopo un riavvio un progetto che ha già avuto esecuzioni conta le nuove annotazioni a partire dal riavvio. Un trainer la cui licenza richiede conferma (ultralytics-detect, ultralytics-segment, layoutlmv3) richiede anche accept_licence: true nel blocco, altrimenti ogni esecuzione automatica viene saltata. La configurazione completa con entrambi i blocchi supera potato validate config.yaml --strict.

Item corretti e fissati nell'addestramento

Due tipi di item vanno nella partizione train di ogni esecuzione successiva, qualunque sia il posto dell'item nella partizione stabile. Il primo tipo è un item con un verdetto correct. Le correzioni vengono dagli item su cui il modello è andato peggio, e in val o test le correzioni valuterebbero il modello su un campione dei suoi stessi errori. I verdetti accept e reject non spostano un item. Il secondo tipo è un item fissato, che un amministratore invia all'addestramento a mano:

bash
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 fissato si addestra una volta che qualcuno lo ha etichettato, e fino ad allora ogni esecuzione lo conta come in attesa. Con curation: {enabled: true}, il pulsante Train on these accanto a una sezione nella pagina Catalog fissa tutto ciò che la sezione include, come la sezione integrata model-found-nothing. DELETE /admin/training/api/pinned rimuove la fissazione.

Un'esecuzione che ha spostato item esclusi dall'addestramento in train registra un avviso con il conteggio. Dopo un verdetto correct, l'esecuzione successiva di questo tutorial ha registrato quanto segue:

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

I conteggi sono nel manifest.json del bundle dell'esecuzione, sotto stats.forced_train. Confronta i punteggi held-out tra le esecuzioni tenendo presente l'avviso, perché le due esecuzioni hanno valutato item diversi.

Salvare la partizione test di un'esecuzione come dataset

Un punteggio held-out cambia al cambiare delle partizioni, e un insieme fisso di item è ciò che rende confrontabili due modelli. La sezione Keep this split as a dataset della pagina dell'esecuzione salva gli item val, test o train di un'esecuzione, con le etichette umane concordate degli item, come nuova versione di un dataset. Scegli la partizione, digita il nome di un dataset e fai clic su Save as dataset version. La route dietro il pulsante accetta la stessa richiesta dalla riga di comando:

bash
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 risposta nomina la nuova versione e il numero di item, qui 23 recensioni di test salvate come versione v0001. Una versione non cambia dopo il salvataggio, quindi modelli e prompt di LLM successivi nel runner di esperimenti dei dataset possono essere valutati esattamente su quegli item. Salvare in un dataset che ha già versioni avvia una nuova versione che contiene solo quella partizione, così gli item val di un'esecuzione successiva non possono mescolarsi con gli item su cui l'esecuzione successiva si è addestrata. Gli input di ogni esempio escludono il campo delle pre-etichette, quindi una valutazione non può leggere la risposta del modello. La route restituisce HTTP 400 quando datasets.enabled non è impostato.

Escludere gli annotatori dalle pre-etichette

Il blocco ai_support.holdout nella configurazione tiene il 20% delle coppie annotatore-item libere da assistenza fin dalla prima annotazione. Per una coppia esclusa, la pagina di annotazione non mostra alcuna pre-etichetta, e i dati dell'item inviati al browser escludono sia il writeback.field (qui predictions) sia il pre_annotation.field. L'estrazione è un hash con sale per annotatore e item, quindi una coppia resta nello stesso gruppo a ogni visita e dopo un riavvio. Con più annotatori per recensione, la maggior parte delle recensioni ha alcuni annotatori assistiti e alcuni esclusi. La scheda AI influence della dashboard di amministrazione confronta i due gruppi su quanto spesso le risposte coincidono con il modello, quanto spesso coincidono con il gold e l'accordo all'interno di ciascun gruppo. Con num_annotators_per_item: 1, ogni recensione cade in un solo gruppo, quindi alza il valore sopra 1 per uno studio che misura l'effetto. Etichettatura assistita dal modello con un modello addestrato sulle tue etichette spiega come leggere il confronto.

Ulteriori letture

Riferimenti

  • 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