Datenqualität & -menge
Die wichtigste Grundlage für jedes erfolgreiche Training
Garbage In, Garbage Out – das eiserne Gesetz des ML
Wie viele Daten brauche ich?
Die benötigte Datenmenge hängt stark von der Aufgabe, dem Basismodell und der gewünschten Qualität ab. Als grobe Richtlinien (für Fine-Tuning auf vortrainierten Modellen):
Hinweis: Diese Werte gelten für Fine-Tuning auf starken vortrainierten Modellen. Training von Scratch benötigt 10–100× mehr Daten.
Qualität > Quantität
Bei LLMs ist Qualität deutlich wichtiger als Quantität. 100 perfekt kuratierte Beispiele schlagen oft 1.000 schlechte. Das InstructGPT-Paper zeigte: 13.000 sorgfältig ausgewählte RLHF-Beispiele reichten, um GPT-3 zu einem hilfreichen Assistenten zu machen.
Datenqualitäts-Checkliste
Keine oder wenige Duplikate
Exact-Match oder Fuzzy-Dedup (MinHash). Duplikate lassen das Modell diese Beispiele stärker gewichten.
Korrekte Labels (manuelle Stichproben)
5–10% des Datasets manuell prüfen. Fehlerhafte Labels sind häufiger als gedacht.
Konsistente Formatierung
Einheitliches Encoding (UTF-8), konsistente Trennzeichen, einheitliche Label-Schreibweise.
Keine korrupten/extremen Einträge
Filter: min_length=10 Zeichen, max_length=2048 Token. HTML-Tags entfernen.
Ausgewogene Klassenverteilung
Imbalance-Ratio messen. Wenn >5:1: Balancing erwägen (Kapitel 4 in diesem Abschnitt).
Repräsentativer Inhalt
Testdaten sollen echter späterer Nutzung entsprechen. Kein "einfacheres" Dataset als in Produktion.
Kein Data Leakage
Strikte Trennung Train/Val/Test. Kein Test-Material im Training.
Datenschutz und Lizenzen geprüft
Keine PII (personenbezogene Daten) ohne Anonymisierung. Lizenzen der Quelldaten prüfen.
Datenquellen und ihre Qualität
Preprocessing
Daten optimal vorbereiten für maximale Trainingsqualität
Gutes Preprocessing kann die Modell-Performance um 10–30% verbessern. Es ist die am meisten unterschätzte Phase des ML-Pipelines.
Text-Preprocessing Pipeline
# Vollständige Text-Preprocessing Pipeline:
import re
from bs4 import BeautifulSoup
def preprocess_text(text: str) -> str | None:
# 1. Encoding normalisieren
text = text.encode('utf-8', errors='ignore').decode('utf-8')
# 2. HTML-Tags entfernen
text = BeautifulSoup(text, 'html.parser').get_text()
# 3. URLs entfernen (optional)
text = re.sub(r'https?://\S+', '[URL]', text)
# 4. Whitespace normalisieren
text = ' '.join(text.split())
# 5. Sonderzeichen bereinigen (vorsichtig!)
# Achtung: Nicht zu aggressiv – Satzzeichen sind wichtig!
text = re.sub(r'[^\w\s\.,!?;:\'\"()\-–—€$%@#]', '', text)
# 6. Längen filtern
word_count = len(text.split())
if word_count < 5:
return None # Zu kurz
if word_count > 512:
text = ' '.join(text.split()[:512]) # Truncate
return text.strip()
# Labels normalisieren
def normalize_label(label: str) -> str:
return label.strip().lower().replace(' ', '_')Deduplication – unterschätzter Qualitätsbooster
Duplikate im Training lassen das Modell diese Beispiele überproportional stark gewichten. Bei Web-Scraping kann bis zu 30% des Datasets dupliziert sein!
# Exact Deduplication:
import hashlib
def deduplicate_exact(dataset: list[dict]) -> list[dict]:
seen = set()
unique = []
for item in dataset:
h = hashlib.md5(item['text'].encode()).hexdigest()
if h not in seen:
seen.add(h)
unique.append(item)
return unique
# Fuzzy Deduplication (ähnliche Texte):
# Nutze MinHash / LSH für effiziente Near-Duplicate Detection
# Library: datasketch
from datasketch import MinHash, MinHashLSH
# → findet auch Duplikate mit kleinen AbweichungenTokenisierung verstehen
Tokenisierung wandelt Text in Zahlen (Token-IDs) um. Jedes Modell hat seinen eigenen Tokenizer – falsche Tokenisierung = Garbage-Daten.
# Tokenisierung mit dem richtigen Tokenizer:
from transformers import AutoTokenizer
# WICHTIG: Immer den Tokenizer des Basismodells nutzen!
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
# Token-Länge messen VOR dem Training:
lengths = [len(tokenizer(text)['input_ids']) for text in texts]
print(f"Median: {sorted(lengths)[len(lengths)//2]} Token")
print(f"P95: {sorted(lengths)[int(len(lengths)*0.95)]} Token")
print(f"Max: {max(lengths)} Token")
# max_length basierend auf P95 wählen (nicht Max!)
# → spart Speicher, verliert nur 5% der LängeSequenzlänge (max_length) richtig wählen
max_length = 64–128max_length = 256–512max_length = 512–1.024max_length = 1.024–2.048max_length = 4.096–8.192VRAM und Sequenzlänge
Dataset-Format für FrameTrain
# Klassifikation (JSON):
{"text": "Das Produkt ist toll!", "label": "positiv"}
{"text": "Lieferung kam nie an.", "label": "negativ"}
# Instruction Following / Chat (JSON):
{"instruction": "Übersetze auf Englisch:",
'input': "Das Wetter ist schön.",
'output': "The weather is nice."}
# NER (JSON mit Spans):
{"text": "Apple wurde 1976 in Cupertino gegründet.",
'entities': [{"start": 0, "end": 5, "label": "ORG"},
{"start": 22, "end": 31, "label": "LOC"}]}Data Augmentation
Dataset künstlich vergrößern und diversifizieren
Data Augmentation erstellt neue, sinnvolle Trainingsbeispiele aus bestehenden. Besonders wertvoll wenn Daten knapp sind oder die Verteilung diversifiziert werden soll.
Text-Augmentation Techniken im Detail
Back-Translation ⭐
Sehr hochText in eine andere Sprache übersetzen, dann zurückübersetzten. Erstellt semantisch identische, sprachlich diverse Varianten.
# Mit Helsinki-NLP Modellen:
from transformers import pipeline
de_to_en = pipeline("translation_de_to_en")
en_to_de = pipeline("translation_en_to_de")
def back_translate(text):
en = de_to_en(text)[0]['translation_text']
return en_to_de(en)[0]['translation_text']✓ Vorteile:
Semantisch korrekt, natürliche Sprachvarianz
✗ Nachteile:
Langsam (2× Übersetzung), API-Kosten
LLM-basierte Paraphrase ⭐
Sehr hochEin LLM generiert semantisch äquivalente Varianten eines Textes. Aktuell beste Methode für NLP-Augmentation.
# Mit lokalem LLM (Ollama/LM Studio):
prompt = """Erstelle 3 verschiedene Formulierungen
mit der exakt gleichen Bedeutung:
{original_text}
Gib nur die 3 Varianten aus, eine pro Zeile."""✓ Vorteile:
Höchste Qualität, sehr divers
✗ Nachteile:
API-Kosten oder lokale GPU nötig
Synonym-Replacement
MittelWörter durch Synonyme ersetzen (via WordNet, GermaNet für Deutsch). Einfach, aber Bedeutung kann sich leicht verschieben.
# Mit NLTK WordNet:
from nltk.corpus import wordnet
def replace_synonyms(text, n_replace=2):
words = text.split()
# Zufällige Wörter durch Synonyme ersetzen
...✓ Vorteile:
Sehr schnell, kein LLM nötig
✗ Nachteile:
Kann Bedeutung ändern, begrenzte Vielfalt
Random Deletion / Insertion
Niedrig–MittelZufällig Wörter löschen, hinzufügen oder tauschen. Macht das Modell robuster gegen Rauschen.
def random_deletion(text, p=0.1):
words = text.split()
if len(words) == 1: return text
return ' '.join([w for w in words
if random.random() > p])✓ Vorteile:
Sehr schnell, einfach implementiert
✗ Nachteile:
Kann Texte sinnlos machen, niedrige Qualität
Augmentation-Strategie
Empfohlene Augmentation-Pipeline: 1. Originaldaten bereinigen (Preprocessing) 2. Back-Translation für wichtige/seltene Klassen (×2) 3. LLM-Paraphrase für kritische Beispiele (×2–3) 4. Synonym-Replacement für schnelle Vervielfachung (×1) Ergebnis: Dataset-Größe ×5–8 mit hoher Qualität Wichtig: Augmentierte Daten IMMER separat labeln → z.B. source: "original" vs. source: "augmented" → ermöglicht Qualitäts-Analyse nach dem Training
Augmentation richtig einsetzen
Klassen-Balancing
Unbalancierte Datasets korrekt behandeln
Ein unbalanciertes Dataset – z.B. 95% negative, 5% positive Beispiele – führt dazu, dass das Modell die häufige Klasse bevorzugt. Es lernt, "fast immer negativ" zu sagen: trivial, aber nützlos.
Imbalance erkennen und messen
# Imbalance messen:
import pandas as pd
df = pd.read_csv('train.csv')
counts = df['label'].value_counts()
print(counts)
# negativ 4750
# positiv 250
# → Ratio 19:1 → starke Imbalance!
# Faustregel:
# < 3:1 → meist okay, kein Balancing nötig
# 3:1–10:1 → Balancing empfohlen
# > 10:1 → Balancing dringend nötig
# Imbalance Ratio:
ratio = counts.max() / counts.min()
print(f"Imbalance Ratio: {ratio:.1f}:1")Lösungsstrategien im Detail
Class Weights ⭐ (Empfohlen)
Im Training werden seltene Klassen stärker gewichtet. Keine Datenveränderung nötig. Einfachste und sauberste Methode.
# Class Weights berechnen:
from sklearn.utils.class_weight import compute_class_weight
import numpy as np
weights = compute_class_weight(
'balanced',
classes=np.unique(labels),
y=labels
)
class_weights = dict(enumerate(weights))
# {0: 0.53, 1: 10.0} ← seltene Klasse 19× stärker
# In FrameTrain: "Class Weighting" aktivieren✓
Keine Datenverfälschung, einfach, direkt in den Optimizer
✗
Funktioniert nicht bei extremen Ratios (> 100:1)
Oversampling (Minderheitsklasse vervielfachen)
Seltene Klasse wird mehrfach im Training verwendet (mit oder ohne Augmentation). Einfach zu implementieren.
# Random Oversampling: from imblearn.over_sampling import RandomOverSampler ros = RandomOverSampler(random_state=42) X_res, y_res = ros.fit_resample(X, y) # SMOTE (synthetische Beispiele): from imblearn.over_sampling import SMOTE smote = SMOTE(random_state=42) X_res, y_res = smote.fit_resample(X, y) # SMOTE für Text: Back-Translation besser!
✓
Mehr Trainingsdaten, kein Informationsverlust
✗
Bei Random Oversampling: exakte Duplikate → leichtes Overfitting
Undersampling (Mehrheitsklasse reduzieren)
Häufige Klasse wird auf die Größe der seltenen Klasse reduziert. Einfach, verliert aber Informationen.
# Random Undersampling:
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=42)
X_res, y_res = rus.fit_resample(X, y)
# Kontrollierter: stratifiziert
min_class_size = y.value_counts().min()
balanced = df.groupby('label').sample(
min_class_size, random_state=42)✓
Schnell, kein Overfitting durch Duplikate
✗
Verliert Daten. Bei kleinen Datasets problematisch
Kombination: Oversample + Undersample
Hybrid-Ansatz: Minderheitsklasse leicht hochgezogen, Mehrheitsklasse leicht reduziert. Oft beste Lösung.
# Ziel: 3:1 Ratio statt 19:1
n_minority = len(df[df.label=='positiv']) # 250
target_majority = n_minority * 3 # 750
majority_down = df[df.label=='negativ'].sample(target_majority)
minority_up = df[df.label=='positiv'].sample(
n_minority * 3, replace=True) # ×3 oversample
balanced = pd.concat([majority_down, minority_up])✓
Gute Balance zwischen Informationsverlust und Overfitting
✗
Mehr Konfiguration nötig
Metriken bei unbalancierten Daten
Bei unbalancierten Datasets ist Accuracy täuschend. Nutze stattdessen:
FrameTrain Empfehlung