Skip to content
Tutorial6 min di lettura

Costruire il tuo primo task di annotazione NER

Guida passo passo per costruire in Potato un task di riconoscimento di entità nominate, con configurazione dell'annotazione di span, colori delle etichette, span sovrapposti, scorciatoie da tastiera ed esportazione CoNLL.

Potato Team

Questo tutorial costruisce in Potato un task di riconoscimento di entità nominate (NER) con cinque tipi di entità, scorciatoie sui tasti numerici, istruzioni per gli annotatori e un'esportazione CoNLL-2003 per l'addestramento dei modelli. Per le opzioni di configurazione degli span che ci stanno dietro, vedi la documentazione sull'annotazione a span.

Che cosa riceve l'annotatore

Il task finito mostra all'annotatore una frase con sopra una fila di pulsanti etichettati. L'annotatore trascina il cursore su un'espressione per selezionarla, preme un tasto numerico o clicca un pulsante per etichettarla, e clicca di nuovo uno span etichettato per cambiarlo o eliminarlo. Ogni span viene salvato con i suoi offset di carattere, quindi lo stesso file di annotazioni alimenta sia un'esportazione CoNLL-2003 sia un calcolo dell'accordo a livello di span.

Prerequisiti

Ti servono Potato installato, un file di dati testuali e abbastanza YAML da saper modificare una configurazione:

bash
pip install potato-annotation

Passo 1: lo schema di span

L'annotazione a span è il tipo span di Potato. Ogni etichetta ha bisogno di un nome, e i campi opzionali utili sono color, che distingue visivamente i tipi di entità in un testo denso, key_value, che associa una scorciatoia da tastiera all'etichetta, e tooltip, che mostra la definizione dell'etichetta al passaggio del mouse, così gli annotatori non devono lasciare la pagina per consultare il codebook.

Cinque tipi permettono di imparare il task in una sola sessione. Oltre le otto etichette circa, gli annotatori cominciano a consultare le linee guida a ogni item, ed è lì che il ritmo cala. sequential_key_binding attiva i tasti numerici, e key_value fissa quale numero va con quale etichetta invece di lasciarlo all'ordine della lista. Crea config.yaml:

yaml
annotation_task_name: "Named Entity Recognition"
task_dir: "."
 
data_files:
  - data/sentences.json
 
item_properties:
  id_key: id
  text_key: text
 
output_annotation_dir: "annotation_output/"
export_annotation_format: "jsonl"
 
annotation_schemes:
  - annotation_type: span
    name: entities
    description: "Highlight and label named entities in the text"
    sequential_key_binding: true
    labels:
      - name: PER
        tooltip: "Person names, including fictional characters"
        color: "#FF6B6B"
        key_value: "1"
      - name: ORG
        tooltip: "Companies, institutions, agencies"
        color: "#4ECDC4"
        key_value: "2"
      - name: LOC
        tooltip: "Places, including countries, cities, landmarks"
        color: "#45B7D1"
        key_value: "3"
      - name: DATE
        tooltip: "Dates, times, and temporal expressions"
        color: "#96CEB4"
        key_value: "4"
      - name: MISC
        tooltip: "Named entities that fit none of the above"
        color: "#FFEAA7"
        key_value: "5"
 
user_config:
  allow_all_users: true

Passo 2: il file di dati

Potato legge un oggetto JSON per riga e prende i campi indicati in item_properties. Crea data/sentences.json:

json
{"id": "1", "text": "Apple Inc. announced that CEO Tim Cook will visit Paris next Tuesday."}
{"id": "2", "text": "The United Nations headquarters in New York hosted delegates from Japan."}
{"id": "3", "text": "Dr. Sarah Johnson published her research at Stanford University in March 2024."}

Tre frasi bastano per verificare che l'interfaccia funzioni. Carica il corpus vero quando l'insieme di etichette ha superato un pilota.

Passo 3: istruzioni a portata dell'annotatore

Quasi tutto il disaccordo nel NER nasce dalle regole sui confini più che dai tipi di entità stessi, quindi le regole che decidono il maggior numero di item vanno messe nella schermata di annotazione. annotation_instructions viene mostrato come un pannello comprimibile in cima a ogni item, aperto per impostazione predefinita, che gli annotatori richiudono quando hanno preso confidenza con il task. Aggiungi questo blocco alla configurazione precedente:

yaml
annotation_instructions: |
  ## Entity types
 
  - **PER** covers people, including fictional characters ("John Smith", "Dr. Johnson", "Batman")
  - **ORG** covers companies, institutions, and agencies ("Apple Inc.", "United Nations", "Stanford University")
  - **LOC** covers places, including countries, cities, and landmarks ("Paris", "New York", "Mount Everest")
  - **DATE** covers dates, times, and temporal expressions ("Tuesday", "March 2024", "next week")
  - **MISC** covers named entities that fit none of the above ("Nobel Prize", "iPhone", "COVID-19")
 
  ## Boundary rules
 
  1. Include titles (Dr., Mr.) with person names.
  2. For nested entities, annotate the largest meaningful span.
  3. Leave articles (the, a) outside the span.

Qui il Markdown viene interpretato, quindi lo stesso testo si può riprendere dal codebook senza riscriverlo in HTML. Limita questo pannello alle regole che risolvono la maggior parte degli item e metti il codebook completo dietro annotation_codebook_url, che si apre in una scheda a parte.

L'interfaccia a span mostra poi le etichette come pulsanti sopra il testo:

Interfaccia di annotazione a span per entità nominate, con tipi di entità coloratiGli annotatori evidenziano span di testo e assegnano etichette di entità come PER, ORG, LOC e DATE

Passo 4: avviare il task

Avvia il server sulla configurazione del Passo 1:

bash
potato start config.yaml

Apri l'indirizzo che stampa ed effettua l'accesso. A quel punto l'annotatore compie quattro azioni per ogni entità:

  1. Seleziona il testo. Clicca e trascina sull'espressione.
  2. Assegna il tipo. Premi il tasto numerico o clicca il pulsante dell'etichetta.
  3. Correggi un errore. Clicca uno span esistente per cambiarne l'etichetta o eliminarlo.
  4. Invia. Passa all'item successivo quando la frase è completa.

Passo 5: che cosa contiene il file di output

Ogni item annotato viene scritto in annotation_output/ come un oggetto JSON, con il nome dello schema come chiave e una voce per ogni span:

json
{
  "id": "1",
  "text": "Apple Inc. announced that CEO Tim Cook will visit Paris next Tuesday.",
  "entities": [
    {"start": 0, "end": 10, "label": "ORG", "text": "Apple Inc."},
    {"start": 30, "end": 38, "label": "PER", "text": "Tim Cook"},
    {"start": 50, "end": 55, "label": "LOC", "text": "Paris"},
    {"start": 61, "end": 73, "label": "DATE", "text": "next Tuesday"}
  ]
}

Sono gli offset di carattere a rendere il file utile a valle. Resistono ai cambi di tokenizzazione, quindi le stesse annotazioni si possono riallineare a qualunque tokenizer usi il tuo modello.

Passo 6: esportare in CoNLL-2003

I modelli di sequence labeling leggono token etichettati invece di offset di carattere, quindi Potato converte gli span in fase di esportazione:

bash
python -m potato.export -c config.yaml -f conll_2003 -o ./conll_export/

Il risultato è un token per riga con il suo tag, con righe vuote tra una frase e l'altra. L'opzione tag_scheme scrive BIO, BIOES o IOB, con BIO come valore predefinito, quindi allineala a quello che si aspetta il tuo codice di addestramento prima di lanciare l'esportazione.

Rendere più precise le linee guida

Regole vaghe producono decisioni divergenti sullo stesso span, quindi conviene rivederle finché il pilota è ancora piccolo. Accompagna gli annotatori nei casi difficili prima che comincino, non dopo, e tieni una lista condivisa dei casi limite risolti man mano che emergono. Un calo dell'accordo tra annotatori è il segnale che una regola va resa più precisa, ed è meno costoso accorgersene su 50 item che su 5000.

Prossimi passi