Skip to content
Tutorials5 Min. Lesezeit

Eine Aufgabe zur Sentimentanalyse aufsetzen

So baust du eine vollständige Aufgabe zur Sentimentklassifikation mit Radiobuttons, Tooltips und Tastenkürzeln für zügiges Labeln.

Potato Team

Sentimentanalyse gehört zu den häufigsten NLP-Aufgaben, und saubere Labels dafür lassen sich in Potato ohne Umwege einsammeln. Dieses Tutorial baut eine Oberfläche zur Sentiment-Annotation, die du Annotierenden tatsächlich vorlegen kannst, samt der Tastenkürzel und Qualitätsprüfungen, die das Labeln beschleunigen.

Überblick über das Projekt

Wir annotieren Beiträge aus sozialen Medien. Die Oberfläche umfasst:

  • dreistufige Sentimentklassifikation (Positive, Negative, Neutral)
  • eine Sicherheitsbewertung zu jeder Annotation
  • optionale Begründungen als Freitext
  • Tastenkürzel für mehr Tempo
  • Maßnahmen zur Qualitätskontrolle

Vollständige Konfiguration

Hier die komplette config.yaml:

yaml
annotation_task_name: "Social Media Sentiment Analysis"
 
# Data configuration
data_files:
  - "data/tweets.json"
 
item_properties:
  id_key: id
  text_key: text
 
# Annotation interface
annotation_schemes:
  # Primary sentiment label
  - annotation_type: radio
    name: sentiment
    description: "What is the overall sentiment of this post?"
    labels:
      - name: Positive
        tooltip: "Expresses happiness, satisfaction, or approval"
        key_value: "1"
      - name: Negative
        tooltip: "Expresses sadness, frustration, or disapproval"
        key_value: "2"
      - name: Neutral
        tooltip: "Factual, objective, or lacks emotional content"
        key_value: "3"
    label_requirement:
      required: true
 
  # Confidence rating
  - annotation_type: likert
    name: confidence
    description: "How confident are you in your sentiment label?"
    size: 5
    min_label: "Not confident"
    max_label: "Very confident"
    label_requirement:
      required: true
 
  # Optional explanation
  - annotation_type: text
    name: explanation
    description: "Why did you choose this label? (Optional)"
    rows: 4
    label_requirement:
      required: false
    placeholder: "Explain your reasoning..."
 
# Guidelines
annotation_instructions: |
  ## Sentiment Annotation Guidelines
 
  ### Your Task
  Classify the sentiment expressed in each social media post.
 
  ### Labels
 
  **Positive**: The author expresses positive emotions or opinions
  - Happiness, excitement, gratitude
  - Praise, recommendations, approval
  - Examples: "Love this!", "Best day ever!", "Highly recommend"
 
  **Negative**: The author expresses negative emotions or opinions
  - Anger, frustration, sadness
  - Complaints, criticism, disapproval
  - Examples: "Terrible service", "So disappointed", "Worst experience"
 
  **Neutral**: Factual or lacking clear sentiment
  - News, announcements, questions
  - Mixed or balanced opinions
  - Examples: "The store opens at 9am", "Has anyone tried this?"
 
  ### Tips
  - Focus on the author's sentiment, not the topic
  - Sarcasm should be labeled based on intended meaning
  - When unsure, lower your confidence rating
 
# User management
automatic_assignment:
  on: true
  sampling_strategy: random
  labels_per_instance: 1
  instance_per_annotator: 100

Format der Beispieldaten

Lege data/tweets.json an:

json
{"id": "t001", "text": "Just got my new laptop and I'm absolutely loving it! Best purchase of the year! #happy"}
{"id": "t002", "text": "Waited 2 hours for customer service and they still couldn't help me. Never shopping here again."}
{"id": "t003", "text": "The new coffee shop on Main Street opens tomorrow at 7am."}
{"id": "t004", "text": "This movie was okay I guess. Some good parts, some boring parts."}
{"id": "t005", "text": "Can't believe how beautiful the sunset was tonight! Nature is amazing."}

Die Aufgabe starten

Starte den Annotationsserver:

bash
potato start config.yaml

Öffne http://localhost:8000 und melde dich an, um mit dem Annotieren zu beginnen.

Das sehen deine Annotierenden, sobald der Server läuft:

Oberfläche zur Sentiment-Annotation mit Radiobuttons für die Label Positive, Negative und NeutralDie Oberfläche zur Sentimentklassifikation mit Radiobutton-Labels, Sicherheitsskala und optionalem Begründungsfeld

Die Oberfläche verstehen

Der Annotationsbereich

Die Oberfläche zeigt:

  1. den zu annotierenden Text (mit hervorgehobenen URLs, Erwähnungen und Hashtags)
  2. Radiobuttons für das Sentiment, mit Tooltips
  3. die Likert-Skala für die Sicherheit
  4. das optionale Textfeld für die Begründung

Ablauf über die Tastatur

Wenn die Annotierenden erst einmal im Takt sind, geht es so am schnellsten:

  1. Text lesen
  2. 1, 2 oder 3 für das Sentiment drücken
  3. Sicherheitsstufe anklicken (oder mit der Maus wählen)
  4. Enter zum Absenden drücken

Fortschrittsanzeige

Die Oberfläche zeigt:

  • den aktuellen Fortschritt (etwa „15 / 100“)
  • die geschätzte Restzeit
  • Statistiken zur Sitzung

Ausgabeformat

Die Arbeit jeder annotierenden Person wird in <output_annotation_dir>/<user>/user_state.json gespeichert, unabhängig davon, was in export_annotation_format steht. Diese Einstellung fügt daneben einen regelmäßigen Export hinzu. Mit jsonl ist die zusammengeführte Datei also <output_annotation_dir>/exports/jsonl/annotations.jsonl, mit einem Datensatz pro annotierender Person und Instanz:

json
{
  "instance_id": "t001",
  "user_id": "john_doe",
  "labels": {
    "sentiment": {"Positive": "true"},
    "confidence": {"5": "true"},
    "explanation": {"text_box": "Clear expression of happiness with the purchase"}
  },
  "spans": {},
  "links": {}
}

Qualitätskontrolle ergänzen

Aufmerksamkeitstests

Misch Goldstandard-Items unter die Daten, um zu prüfen, ob die Annotierenden bei der Sache sind. Alle Optionen der hier verwendeten Schemata radio und likert stehen in der Quelldokumentation.

yaml
attention_checks:
  enabled: true
  frequency: 10          # Every 10th item
  items_file: data/attention_checks.json
  failure_handling:
    warn_threshold: 2
    block_threshold: 5

Die Items stehen in items_file statt direkt in der Konfiguration, und jedes trägt die Antwort, die es bekommen soll:

json
[
  {
    "id": "chk1",
    "text": "I am extremely happy and satisfied! This is the best!",
    "expected_answer": { "sentiment": "Positive" }
  },
  {
    "id": "chk2",
    "text": "This is absolutely terrible and I hate it completely.",
    "expected_answer": { "sentiment": "Negative" }
  }
]

Goldstandard-Items verwenden gold_label statt expected_answer und gehören in gold_standards.items_file. Beachte, dass beide Blöcke auf die oberste Ebene gehören. Potato liest attention_checks aus der Wurzel der Konfiguration. Steckst du den Block in einen quality_control:-Block, wird er nicht gelesen, und es erscheint keine Fehlermeldung.

Inter-Annotator-Übereinstimmung

Für Forschungsprojekte lässt du mehrere Annotationen einsammeln:

yaml
automatic_assignment:
  on: true
  sampling_strategy: random
  labels_per_instance: 3  # Each item annotated by 3 people
  instance_per_annotator: 50

Ergebnisse auswerten

Annotationen exportieren und auswerten:

python
import json
from collections import Counter
 
records = []
with open('annotation_output/exports/jsonl/annotations.jsonl') as f:
    for line in f:
        records.append(json.loads(line))
 
def chosen(record, scheme):
    """The label picked for a single-choice scheme: the key whose value is "true"."""
    for name, value in record["labels"].get(scheme, {}).items():
        if value == "true":
            return name
    return None
 
sentiments = Counter(chosen(r, "sentiment") for r in records)
print(f"Sentiment distribution: {dict(sentiments)}")
 
# A likert stores its scale position the same way, as the label name
points = [int(p) for r in records if (p := chosen(r, "confidence"))]
print(f"Average confidence: {sum(points)/len(points):.2f}")

Nächste Schritte


Mehr Annotationstypen findest du in unserer Dokumentation.