Skip to content
Tutorials6 Min. Lesezeit

Deine erste NER-Annotationsaufgabe bauen

Schritt-für-Schritt-Anleitung für eine Named-Entity-Recognition-Aufgabe in Potato, mit Span-Konfiguration, Labelfarben, überlappenden Spans, Tastenkürzeln und CoNLL-Export.

Potato Team

Dieses Tutorial baut in Potato eine Aufgabe zur Named Entity Recognition (NER) mit fünf Entitätstypen, Kürzeln auf den Zahlentasten, Anweisungen für Annotierende und einem CoNLL-2003-Export für das Modelltraining. Die Span-Optionen dahinter findest du in der Dokumentation zur Span-Annotation.

Was Annotierende bekommen

Die fertige Aufgabe zeigt Annotierenden einen Satz mit einer Reihe beschrifteter Schaltflächen darüber. Sie ziehen über eine Wortgruppe, um sie auszuwählen, drücken eine Zahlentaste oder klicken auf eine Schaltfläche, um sie zu labeln, und klicken einen gelabelten Span erneut an, um ihn zu ändern oder zu entfernen. Jeder Span wird mit seinen Zeichenoffsets gespeichert, sodass dieselbe Annotationsdatei sowohl einen CoNLL-2003-Export als auch eine Übereinstimmungsberechnung auf Span-Ebene speist.

Voraussetzungen

Du brauchst ein installiertes Potato, eine Textdatendatei und genug YAML-Kenntnisse, um eine Konfiguration zu bearbeiten:

bash
pip install potato-annotation

Schritt 1: Das Span-Schema

Span-Annotation ist in Potato der Typ span. Jedes Label braucht einen Namen. Die nützlichen optionalen Felder sind color, das Entitätstypen in dichtem Text visuell trennt, key_value, das dem Label ein Tastenkürzel zuordnet, und tooltip, das beim Überfahren mit der Maus die Definition des Labels anzeigt, damit Annotierende die Seite nicht verlassen müssen, um im Codebuch nachzusehen.

Mit fünf Typen lässt sich die Aufgabe in einer Sitzung lernen. Ab etwa acht beginnen Annotierende, bei jedem Item die Richtlinien nachzuschlagen, und genau dort sinkt der Durchsatz. sequential_key_binding schaltet die Zahlentasten ein, und key_value legt fest, welche Zahl zu welchem Label gehört, statt das der Reihenfolge der Liste zu überlassen. Lege config.yaml an:

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

Schritt 2: Die Datendatei

Potato liest ein JSON-Objekt pro Zeile und übernimmt die in item_properties benannten Felder. Lege data/sentences.json an:

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."}

Drei Sätze reichen, um zu prüfen, ob die Oberfläche funktioniert. Lade das echte Korpus erst, wenn der Labelsatz einen Pilotlauf überstanden hat.

Schritt 3: Anweisungen in Reichweite der Annotierenden

Die meisten Meinungsverschiedenheiten bei NER entstehen durch Abgrenzungsregeln und nicht durch die Entitätstypen selbst. Die Regeln, die über die meisten Items entscheiden, gehören deshalb auf den Annotationsbildschirm. annotation_instructions erscheint als einklappbares Panel oben auf jedem Item, standardmäßig geöffnet, und Annotierende klappen es weg, sobald sie mit der Aufgabe vertraut sind. Füge diesen Block der obigen Konfiguration hinzu:

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.

Markdown wird hier gerendert, sodass du denselben Text aus deinem Codebuch übernehmen kannst, ohne ihn als HTML neu zu schreiben. Beschränke dieses Panel auf die Regeln, die die meisten Items entscheiden, und hinterlege das vollständige Codebuch über annotation_codebook_url, das sich in einem eigenen Tab öffnet.

Die Span-Oberfläche zeigt die Labels dann als Schaltflächen über dem Text:

Oberfläche zur Span-Annotation benannter Entitäten mit farbig unterschiedenen EntitätstypenAnnotierende heben Textspans hervor und weisen Entitätslabels wie PER, ORG, LOC und DATE zu

Schritt 4: Die Aufgabe starten

Starte den Server mit der Konfiguration aus Schritt 1:

bash
potato start config.yaml

Öffne die ausgegebene Adresse und melde dich an. Annotierende arbeiten dann pro Entität vier Schritte ab:

  1. Text auswählen. Über die Wortgruppe klicken und ziehen.
  2. Typ zuweisen. Die Zahlentaste drücken oder auf die Label-Schaltfläche klicken.
  3. Fehler korrigieren. Einen vorhandenen Span anklicken, um sein Label zu ändern oder ihn zu entfernen.
  4. Absenden. Zum nächsten Item wechseln, wenn der Satz fertig ist.

Schritt 5: Was die Ausgabedatei enthält

Jedes annotierte Item wird als ein JSON-Objekt nach annotation_output/ geschrieben, mit dem Schemanamen als Schlüssel und einem Eintrag pro 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"}
  ]
}

Die Zeichenoffsets machen die Datei für die Weiterverarbeitung nützlich. Sie überstehen Änderungen an der Tokenisierung, sodass sich dieselben Annotationen an jeden Tokenizer anpassen lassen, den dein Modell verwendet.

Schritt 6: Export nach CoNLL-2003

Modelle für Sequenz-Labeling lesen getaggte Tokens statt Zeichenoffsets, deshalb wandelt Potato die Spans beim Export um:

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

Das Ergebnis enthält ein Token pro Zeile mit seinem Tag und Leerzeilen zwischen den Sätzen. Die Option tag_scheme schreibt BIO, BIOES oder IOB, mit BIO als Standard. Stimme sie also vor dem Export auf das ab, was dein Trainingscode erwartet.

Die Richtlinien schärfen

Unscharfe Regeln führen zu abweichenden Entscheidungen beim selben Span, deshalb lohnt es sich, die Regeln zu überarbeiten, solange der Pilot noch klein ist. Geh die schwierigen Fälle mit den Annotierenden vor dem Start durch statt danach, und führe eine gemeinsame Liste geklärter Grenzfälle, sobald sie auftauchen. Sinkende Übereinstimmung zwischen Annotierenden ist das Signal, dass eine Regel geschärft werden muss, und das bei 50 Items zu bemerken ist billiger als bei 5000.

Nächste Schritte