Skip to content
Guides5 min read

Annotationen nach Hugging Face Datasets exportieren

So wandelst du Potato-Annotationen in das Format von Hugging Face Datasets um: Aufbau des JSON-Exports, Dataset Card, Upload auf den Hub und Anbindung an das Training mit transformers.

Potato Team

Wer Modelle trainiert, landet früher oder später bei einem Hugging-Face-Datensatz. Der Rest des Ökosystems erwartet es so. Dieser Leitfaden zeigt, wie du die Annotationsausgabe von Potato mit ein paar kurzen Python-Skripten in dieses Format bringst, egal ob du lokal trainierst oder den Datensatz auf dem Hub teilst.

Warum überhaupt das Hugging-Face-Format

Es ist das Format, das die Werkzeuge ohnehin sprechen, du schreibst also keinen Klebecode für jede Bibliothek. Datensätze liegen als Arrow vor und laden auch dann noch schnell, wenn sie groß werden. Zum Teilen genügt ein einzelner Aufruf von push_to_hub. Und Trainer liest sie direkt, vor dem Training fällt also kein weiterer Umwandlungsschritt an.

Einfacher Export mit Python

Potato schreibt Annotationen als JSONL. Die Bibliothek datasets macht daraus einen Hugging-Face-Datensatz.

Potato-Annotationen laden

python
import json
from datasets import Dataset
 
# Load Potato annotation output
annotations = []
with open("annotation_output/annotated_instances.jsonl", "r") as f:
    for line in f:
        annotations.append(json.loads(line))
 
# Convert to Hugging Face Dataset
dataset = Dataset.from_list([
    {
        "text": ann["text"],
        "label": ann["label_annotations"]["sentiment"]["label"]
    }
    for ann in annotations
])
 
# Save locally
dataset.save_to_disk("my_dataset")
 
# Or push to Hub
dataset.push_to_hub("username/my-dataset")

Splits für Training und Test anlegen

python
from sklearn.model_selection import train_test_split
 
# Split annotations
train_data, temp_data = train_test_split(annotations, test_size=0.2, random_state=42)
val_data, test_data = train_test_split(temp_data, test_size=0.5, random_state=42)
 
# Create datasets
train_dataset = Dataset.from_list(train_data)
val_dataset = Dataset.from_list(val_data)
test_dataset = Dataset.from_list(test_data)
 
# Combine into DatasetDict
from datasets import DatasetDict
dataset = DatasetDict({
    "train": train_dataset,
    "validation": val_dataset,
    "test": test_dataset
})

Exporte je nach Aufgabe

Textklassifikation

python
from datasets import Dataset, ClassLabel
 
# Load and process sentiment annotations
dataset = Dataset.from_dict({
    "text": [ann["text"] for ann in annotations],
    "label": [ann["label_annotations"]["sentiment"]["label"] for ann in annotations]
})
 
# Define label mapping
dataset = dataset.cast_column(
    "label",
    ClassLabel(names=["Positive", "Negative", "Neutral"])
)

Eigennamenerkennung

python
# Convert span annotations to IOB format
def convert_to_iob(text, spans):
    tokens = text.split()
    labels = ["O"] * len(tokens)
 
    for span in spans:
        # Map character offsets to token indices
        start_token, end_token = char_to_token(text, span["start"], span["end"])
        labels[start_token] = f"B-{span['annotation']}"
        for i in range(start_token + 1, end_token):
            labels[i] = f"I-{span['annotation']}"
 
    return tokens, labels
 
# Potato stores span annotations in span_annotations field
dataset = Dataset.from_dict({
    "tokens": [convert_to_iob(a["text"], a.get("span_annotations", {}).get("entities", []))[0] for a in annotations],
    "ner_tags": [convert_to_iob(a["text"], a.get("span_annotations", {}).get("entities", []))[1] for a in annotations]
})

Audioklassifikation

python
from datasets import Audio
 
# For audio annotation tasks
dataset = Dataset.from_dict({
    "audio": [ann["audio"] for ann in annotations],
    "label": [ann["label_annotations"]["emotion"]["label"] for ann in annotations]
})
 
# Cast to Audio feature
dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))

Bildklassifikation

python
from datasets import Image
 
# For image annotation tasks
dataset = Dataset.from_dict({
    "image": [ann["image"] for ann in annotations],
    "label": [ann["label_annotations"]["category"]["label"] for ann in annotations]
})
 
dataset = dataset.cast_column("image", Image())

Export bei mehreren Annotierenden

Wenn pro Element mehrere Personen annotiert haben, stehen dir verschiedene Exportformen offen:

python
# Long format (one row per annotation)
# Each annotator's work is saved in a separate file: annotator_{id}.jsonl
import glob
 
records = []
for filepath in glob.glob("annotation_output/annotator_*.jsonl"):
    annotator_id = filepath.split("_")[-1].replace(".jsonl", "")
    with open(filepath) as f:
        for line in f:
            ann = json.loads(line)
            records.append({
                "id": ann["id"],
                "text": ann["text"],
                "label": ann["label_annotations"]["sentiment"]["label"],
                "annotator": annotator_id
            })
 
dataset = Dataset.from_list(records)
 
# Or aggregate annotations per item
from collections import defaultdict
from statistics import mode
 
items = defaultdict(list)
for record in records:
    items[record["id"]].append(record)
 
aggregated = []
for item_id, anns in items.items():
    labels = [a["label"] for a in anns]
    aggregated.append({
        "id": item_id,
        "text": anns[0]["text"],
        "label": mode(labels),  # Majority vote
        "num_annotators": len(labels)
    })
 
dataset = Dataset.from_list(aggregated)

Auf den Hugging Face Hub laden

python
from huggingface_hub import login
 
# Login (or use HF_TOKEN env var)
login()
 
# Push dataset
dataset.push_to_hub(
    "username/my-sentiment-dataset",
    private=False,
    token=None  # Uses cached token
)
 
# With dataset card
dataset.push_to_hub(
    "username/my-sentiment-dataset",
    commit_message="Initial upload of sentiment annotations",
)

Dataset Card

Leg eine README.md für deinen Datensatz an:

markdown
---
license: cc-by-4.0
task_categories:
  - text-classification
language:
  - en
size_categories:
  - 1K<n<10K
---
 
# My Sentiment Dataset
 
## Dataset Description
 
Sentiment annotations collected using [Potato](https://potato.iro.umich.edu).
 
## Dataset Structure
 
- **train**: 8,000 examples
- **validation**: 1,000 examples
- **test**: 1,000 examples
 
### Labels
 
- Positive
- Negative
- Neutral
 
## Annotation Process
 
Annotated by 3 workers per item on Prolific.
Inter-annotator agreement (Fleiss' Kappa): 0.75
 
## Citation
 
@article{...}

Den eigenen Datensatz laden

python
from datasets import load_dataset
 
# From Hub
dataset = load_dataset("username/my-sentiment-dataset")
 
# From local
dataset = load_dataset("my_dataset/")
 
# Use for training
from transformers import Trainer
 
trainer = Trainer(
    model=model,
    train_dataset=dataset["train"],
    eval_dataset=dataset["validation"],
    ...
)

Ein paar Gewohnheiten, die sich lohnen

Halte fest, woher die Daten stammen, wie annotiert wurde und welche Übereinstimmung du gemessen hast, denn wer den Datensatz weiterverwendet, wird danach fragen. Beschreibe jedes Label in klaren Worten, statt anzunehmen, der Name erkläre sich von selbst. Versioniere den Datensatz, damit erkennbar ist, was sich zwischen zwei Ständen geändert hat. Nenne die verwendete Annotationsmethodik. Und schreib die Lizenz gleich dazu, damit niemand raten muss, ob er die Daten nutzen darf.

Welche Exportoptionen Potato direkt mitbringt, steht in der Dokumentation zum Hugging-Face-Export.


Vollständige Dokumentation zum Export unter Datenformate.