Fine-Tune kleiner Modelle mit Hugging Face Trainer
1. Übersicht
Dieser Artikel führt Sie durch das Fine-Tuning eines kleinen Modells (DistilBERT) zur Klassifizierung von Stimmungen in Produktbewertungen mit der Trainer-API der transformers-Bibliothek. Nach dem Lesen werden Sie wissen, wie Sie einen Datensatz vorbereiten, tokenisieren, TrainingArguments konfigurieren, trainieren, evaluieren und Modelle für die Inferenz speichern.
Laufendes Beispiel in diesem Artikel: OrderReview – eine Bewertung eines Kunden (Customer) für eine Bestellung (Order), gekennzeichnet mit positive/negative.
2. Umgebung einrichten
Installieren Sie die erforderlichen Bibliotheken. Sie benötigen keine leistungsstarke GPU; kleine Modelle wie DistilBERT laufen auf der CPU auch mit kleinen Datensätzen ordnungsgemäß, allerdings langsamer.
pip install transformers datasets evaluate accelerate torch scikit-learn
Hinweis: Wenn Ihr Rechner eine GPU hat, installieren Sie die entsprechende PyTorch-Version mit CUDA für deutlich schnelleres Training. Mit CPU sollten Sie die Anzahl der Datenzeilen und Epochen begrenzen, um die Demo nicht zu lange zu machen.
3. Datensatz vorbereiten
Anstatt einen riesigen Datensatz zu laden, erstellen wir einen kleinen Beispieldatensatz mit simulierten Bestellbewertungen, um das Konzept zu veranschaulichen. In der Praxis können Sie stattdessen load_dataset("imdb") oder Ihre eigenen CSV-Daten verwenden.
from datasets import Dataset
reviews = [
{"text": "Lieferung schnell, sorgfältig verpackt, sehr zufrieden", "label": 1},
{"text": "Produkt fehlerhaft, langsame Lieferung, nicht wie beschrieben", "label": 0},
{"text": "Gute Qualität, angemessener Preis, werde es wieder kaufen", "label": 1},
{"text": "Schlechter Service, langsamer Kundensupport", "label": 0},
{"text": "Sehr zufrieden mit dieser Bestellung, 5 Sterne", "label": 1},
{"text": "Produkt beschädigt erhalten, sehr enttäuscht", "label": 0},
]
dataset = Dataset.from_list(reviews)
dataset = dataset.train_test_split(test_size=0.33, seed=42)
print(dataset)
DatasetDict({
train: Dataset({features: ['text', 'label'], num_rows: 4})
test: Dataset({features: ['text', 'label'], num_rows: 2})
})
Dataset.from_list konvertiert eine Liste von Dictionaries in einen Hugging Face Dataset. train_test_split teilt ihn in Train- und Test-Sets auf — bei echten Datensätzen sollten Sie mehrere tausend Zeilen haben, dies ist nur ein Beispiel für den Verarbeitungsablauf.
4. Daten tokenisieren
Das Modell versteht Text nicht direkt, ein Tokenizer ist erforderlich, um ihn in input_ids und attention_mask umzuwandeln.
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
def tokenize_fn(batch):
return tokenizer(batch["text"], truncation=True, padding="max_length", max_length=32)
tokenized_dataset = dataset.map(tokenize_fn, batched=True)
print(tokenized_dataset["train"][0].keys())
dict_keys(['text', 'label', 'input_ids', 'attention_mask'])
truncation=True: schneidet Sätze ab, die länger alsmax_lengthsind.padding="max_length": füllt Token auf, damit alle Sätze die gleiche Länge haben, was die parallele Batch-Verarbeitung erleichtert.map(..., batched=True): wendet die Tokenize-Funktion batchweise an, schneller als Zeile für Zeile.
Hinweis: Das englische DistilBERT ist in der Praxis nicht für das Fine-Tuning von vietnamesischen Reviews geeignet. Es wird hier zur technischen Demonstration verwendet; für vietnamesische Daten wählen Sie einen Checkpoint wie
vinai/phobert-base.
5. Modell laden und Labels definieren
from transformers import AutoModelForSequenceClassification
label2id = {"negative": 0, "positive": 1}
id2label = {v: k for k, v in label2id.items()}
model = AutoModelForSequenceClassification.from_pretrained(
checkpoint,
num_labels=2,
id2label=id2label,
label2id=label2id,
)
num_labels=2 erstellt einen neuen Classification-Head mit 2 Ausgängen (positive/negative), der auf dem vortrainierten Encoder-Teil aufgesetzt wird. id2label/label2id helfen dem Modell, menschenlesbare Labels statt nur Zahlen 0/1 zurückzugeben.
6. TrainingArguments konfigurieren
TrainingArguments sammelt alle Hyperparameter und Konfigurationen der Trainingsschleife.
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="./order-review-model",
eval_strategy="epoch",
save_strategy="epoch",
learning_rate=2e-5,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
num_train_epochs=3,
weight_decay=0.01,
load_best_model_at_end=True,
metric_for_best_model="f1",
logging_steps=1,
)
eval_strategy="epoch": bewertet das Modell nach jeder Epoch statt nach jedem Step.load_best_model_at_end=True: Nach dem Training lädt der Trainer automatisch den besten Checkpoint entsprechendmetric_for_best_modelerneut.per_device_train_batch_size=4: kleine Batch-Größe, weil der Demo-Datensatz auch klein ist; mit echten Daten erhöhen Sie dies auf 16/32 je nach verfügbarem VRAM.
7. Metrik-Berechnungsfunktion definieren
Der Trainer benötigt eine compute_metrics-Funktion, um zu wissen, anhand welcher Kriterien er das Modell evaluieren soll.
import numpy as np
import evaluate
accuracy = evaluate.load("accuracy")
f1 = evaluate.load("f1")
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
acc = accuracy.compute(predictions=predictions, references=labels)
f1_score = f1.compute(predictions=predictions, references=labels)
return {"accuracy": acc["accuracy"], "f1": f1_score["f1"]}
eval_pred ist ein Tuple (logits, labels), das der Trainer nach jeder Evaluation übergibt. np.argmax wählt das Label mit der höchsten Wahrscheinlichkeit (Logit) als Vorhersage.
8. Trainer initialisieren und trainieren
from transformers import Trainer, DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset["train"],
eval_dataset=tokenized_dataset["test"],
data_collator=data_collator,
compute_metrics=compute_metrics,
)
trainer.train()
Epoch Training Loss Validation Loss Accuracy F1
1 0.693100 0.671204 0.500000 0.500000
2 0.654200 0.598311 1.000000 1.000000
3 0.581900 0.520447 1.000000 1.000000
DataCollatorWithPadding handhabt dynamisches Padding pro Batch (effizienter als festes Padding auf max_length beim Tokenisieren, aber hier ist bereits gepolstert, also kümmert sich der Collator hauptsächlich um die Batch-Tensor-Erstellung). trainer.train() führt die Schleife Forward → Backward → Optimizer-Step → Loss-Logging aus und evaluiert automatisch gemäß der konfigurierten eval_strategy.
8.1. Trainingsfortschritt verfolgen
Wenn Sie detailliertere Protokolle an TensorBoard ausgeben möchten, fügen Sie report_to="tensorboard" zu TrainingArguments hinzu und führen Sie dann tensorboard --logdir ./order-review-model/runs aus. Mit einem kleinen Datensatz wie in dieser Demo ist die Console-Ausgabe ausreichend, um zu überprüfen, ob der Verlust kontinuierlich sinkt.
9. Modell evaluieren und speichern
metrics = trainer.evaluate()
print(metrics)
trainer.save_model("./order-review-model/final")
tokenizer.save_pretrained("./order-review-model/final")
{'eval_loss': 0.5204, 'eval_accuracy': 1.0, 'eval_f1': 1.0, 'eval_runtime': 0.12, ...}
trainer.evaluate() führt das Training erneut auf eval_dataset aus und gibt ein Metric-Dictionary zurück. save_model speichert Config und Gewichte; vergessen Sie nicht, den dazugehörigen Tokenizer mit save_pretrained zu speichern, um Vokabularprobleme beim erneuten Laden zu vermeiden.
10. Inferenz mit dem Fine-tuned Modell
Verwenden Sie pipeline, um das gerade trainierte Modell schnell zu testen.
from transformers import pipeline
classifier = pipeline(
"text-classification",
model="./order-review-model/final",
tokenizer="./order-review-model/final",
)
result = classifier("Lieferung pünktlich, Produkt schöner als erwartet")
print(result)
[{'label': 'positive', 'score': 0.9123}]
pipeline übernimmt das Tokenisieren, Forward-Pass und Softmax, um das Label mit Score (Konfidenzwahrscheinlichkeit) auszugeben. Dies ist der schnellste Weg, um ein Modell in einen kleineren Service zu integrieren, beispielsweise einen PaymentService, der Kundenfeedback automatisch klassifizieren muss.
11. Tests für Modellverhalten schreiben
Mit dem Demo-Datensatz können Sie einfache Tests mit pytest schreiben, um sicherzustellen, dass die Pipeline korrekt funktioniert und keine Regression auftritt, wenn Sie den Checkpoint ändern.
import pytest
from transformers import pipeline
@pytest.fixture(scope="module")
def classifier():
return pipeline(
"text-classification",
model="./order-review-model/final",
tokenizer="./order-review-model/final",
)
def test_positive_review_is_classified_correctly(classifier):
result = classifier("Großartiges Produkt, schnelle Lieferung")[0]
assert result["label"] == "positive"
assert result["score"] > 0.5
def test_negative_review_is_classified_correctly(classifier):
result = classifier("Produkt fehlerhaft, Lieferung verspätet, sehr enttäuscht")[0]
assert result["label"] == "negative"
assert result["score"] > 0.5
scope="module" sorgt dafür, dass die Pipeline das Modell nur einmal für alle Tests in der Datei lädt und verhindert so zeitraubende erneute Ladevorgänge für jeden Test. Dieser Test überprüft nur das High-Level-Verhalten (korrekte Label-Richtung) und sollte nicht zur Modellqualitätsbewertung verwendet werden — das ist Aufgabe des Evaluationsschritts oben.
12. Häufig auftretende Fehler
- Vergessenes
padding/truncation: Sätze, die länger alsmax_lengthsind, führen zu Laufzeitfehlern, da die Tensoren im Batch unterschiedliche Größen haben. - Falsches
num_labels: Wenn der Datensatz 3 Klassen hat, aber Sienum_labels=2deklarieren, funktioniert der Trainer immer noch, aber die Ergebnisse sind völlig falsch, da der Classification-Head nicht genug Ausgänge hat. - Zu kleiner Datensatz und hohe F1-Erwartungen bei echten Daten: Das Beispiel in diesem Artikel dient nur zur Illustration des Code-Flows, 4 Trainingszeilen reichen nicht für das Modell, um zu verallgemeinern. Nehmen Sie nicht das hier erreichte accuracy=1.0 als Maßstab.
seednicht gesetzt: Trainingsergebnisse unterscheiden sich zwischen Läufen, was den Vergleich bei der Hyperparameter-Abstimmung erschwert. Verwenden Sieset_seed(42)austransformersvor dem Training.- Großes Modell (base/large) für schwache Hardware: Beginnen Sie mit der
distilbert/tiny-Version, um schnelle Entwicklungszyklen zu ermöglichen, wechseln Sie nur zu größeren Modellen, wenn Sie Ihre Pipeline finalisiert haben.
Wann man Trainer NICHT verwenden sollte: Wenn Sie die Trainingsschleife vollständig anpassen müssen (z. B. komplexe Multi-Task-Verluste, benutzerdefinierte Scheduler, die nicht einfach über Callbacks ausdrückbar sind), ist das Schreiben einer reinen PyTorch-Schleife oder die direkte Verwendung von
accelerateflexibler als der Versuch, alles in denTrainerzu pressen.
13. Fazit
Die Trainer-API ermöglicht es Ihnen, kleine Modelle zu fine-tunen, ohne selbst eine Train/Eval-Schleife schreiben zu müssen – Sie müssen nur Datensatz, Tokenizer, Modell und TrainingArguments vorbereiten. Bei einfachen Klassifizierungsproblemen wie Bestellbewertungen ist der Standard-Workflow: Tokenisieren → TrainingArguments konfigurieren → compute_metrics definieren → Trainer initialisieren → train() → evaluate() → save_model(). Wenn Sie später mit komplexeren echten Daten arbeiten, müssen Sie nur den Datensatz und Checkpoint austauschen; der Rest der Pipeline bleibt praktisch unverändert.