Inhaltsverzeichnis
- Was ist Natural Language Processing?
- Textvorverarbeitung
- Texte als Zahlen – Vektorisierung
- Word Embeddings
- Rekurrente Netze – RNN und LSTM
- Die Transformer-Architektur
- BERT und vortrainierte Sprachmodelle
- Praktisches Projekt: Sentiment-Analyse
- Praktisches Projekt: Textklassifikation mit BERT
- Große Sprachmodelle und ChatGPT
- Ethische Aspekte in NLP
- Zusammenfassung und Ausblick
- Anhang: Schnellreferenz NLP
Natural Language Processing – Textverarbeitung und Transformer
Fach: Data Science & Artificial Intelligence
Schulstufe: 12. Schulstufe – HTL Informatik
Voraussetzungen: Deep Learning, Python, NumPy, grundlegendes Keras
Autor: HTL Pinkafeld – IF/IT
1. Was ist Natural Language Processing?
Natural Language Processing (NLP) ist das Teilgebiet der KI, das sich mit dem Verstehen, Verarbeiten und Generieren von menschlicher Sprache befasst.
1.1 Warum ist Sprache schwierig für Computer?
"Die Bank ist hart." → Sitzbank oder Geldinstitut?
"Das geht nicht ohne Weiteres." → Idiom, keine wörtliche Bedeutung
"Er sah sie mit dem Fernrohr." → Wer hatte das Fernrohr?
Sprache ist mehrdeutig, kontextabhängig, kulturell geprägt und ständig im Wandel – all das macht sie für Computer extrem anspruchsvoll.
1.2 Typische NLP-Aufgaben
| Aufgabe | Beschreibung | Beispiel |
|---|---|---|
| Textklassifikation | Text einer Kategorie zuordnen | Spam-Erkennung, Themenklassifikation |
| Sentiment-Analyse | Stimmung/Meinung erkennen | Produktbewertungen: positiv/negativ |
| Named Entity Recognition | Eigennamen erkennen | „Wien ist die Hauptstadt von Österreich" |
| Maschinelle Übersetzung | Text in andere Sprache übersetzen | DeepL, Google Translate |
| Textzusammenfassung | Langen Text kürzen | Nachrichtenartikel zusammenfassen |
| Frage-Antwort-Systeme | Fragen auf Basis von Texten beantworten | ChatGPT, Siri |
| Textgenerierung | Kohärenten Text erzeugen | GPT-4, Gemini |
1.3 NLP-Pipeline Überblick
Roher Text
│
▼
Vorverarbeitung (Tokenisierung, Stopwörter, Stemming)
│
▼
Vektorisierung (Text → Zahlen)
│
▼
Modell (klassisch oder Neural)
│
▼
Ausgabe (Klasse, Sentiment, Übersetzung, ...)
2. Textvorverarbeitung
Roher Text muss vor der Modellierung bereinigt und vereinheitlicht werden.
2.1 Tokenisierung
Tokenisierung zerlegt Text in kleinere Einheiten – Tokens (Wörter, Satzzeichen oder Subwörter):
# Einfache Wort-Tokenisierung
text = "Deep Learning ist ein Teilgebiet der KI!"
tokens = text.lower().split()
print(tokens)
# ['deep', 'learning', 'ist', 'ein', 'teilgebiet', 'der', 'ki!']
# Besser: mit NLTK
import nltk
nltk.download('punkt', quiet=True)
from nltk.tokenize import word_tokenize
tokens = word_tokenize(text.lower(), language='german')
print(tokens)
# ['deep', 'learning', 'ist', 'ein', 'teilgebiet', 'der', 'ki', '!']
2.2 Stopwörter entfernen
Stopwörter sind häufige Wörter ohne inhaltlichen Beitrag (z.B. „ist", „der", „ein"):
import nltk
nltk.download('stopwords', quiet=True)
from nltk.corpus import stopwords
stop_de = set(stopwords.words('german'))
tokens_clean = [t for t in tokens if t not in stop_de and t.isalpha()]
print(tokens_clean)
# ['deep', 'learning', 'teilgebiet', 'ki']
2.3 Stemming und Lemmatisierung
Beide Verfahren reduzieren Wörter auf ihre Grundform:
| Verfahren | Beschreibung | Beispiel |
|---|---|---|
| Stemming | Rohes Kürzen des Wortendes | „lernend" → „lern" |
| Lemmatisierung | Rückführung auf korrekte Grundform | „lernend" → „lernen" |
from nltk.stem.snowball import GermanStemmer
stemmer = GermanStemmer()
woerter = ['lernend', 'gelernt', 'lernte', 'Lernen']
for w in woerter:
print(f"{w:12} → {stemmer.stem(w)}")
2.4 Vollständige Vorverarbeitungs-Pipeline
import re
import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.stem.snowball import GermanStemmer
nltk.download(['punkt', 'stopwords'], quiet=True)
def preprocess(text, language='german', stem=False):
"""Vollständige Textvorverarbeitung."""
# Kleinschreibung
text = text.lower()
# Sonderzeichen und Zahlen entfernen
text = re.sub(r'[^a-züäöß\s]', '', text)
# Tokenisieren
tokens = word_tokenize(text, language=language)
# Stopwörter entfernen
stop = set(stopwords.words(language))
tokens = [t for t in tokens if t not in stop and len(t) > 2]
# Optional: Stemming
if stem:
stemmer = GermanStemmer()
tokens = [stemmer.stem(t) for t in tokens]
return tokens
# Beispiel
texte = [
"Das neue iPhone ist wirklich beeindruckend und sehr schnell!",
"Dieses Produkt ist eine absolute Katastrophe, nie wieder!",
"Ganz okay, nicht besonders gut aber auch nicht schlecht."
]
for t in texte:
print(preprocess(t))
3. Texte als Zahlen – Vektorisierung
Modelle arbeiten mit Zahlen, nicht mit Text. Wir brauchen eine Darstellung, die den Inhalt eines Textes numerisch erfasst.
3.1 Bag of Words (BoW)
Der Text wird als „Wortsack" dargestellt – nur Worthäufigkeiten zählen, die Reihenfolge wird ignoriert:
from sklearn.feature_extraction.text import CountVectorizer
texte = [
"das auto ist rot",
"das fahrrad ist blau",
"das auto und das fahrrad"
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(texte)
print("Vokabular:", vectorizer.get_feature_names_out())
print(X.toarray())
# Jede Zeile = ein Text, jede Spalte = ein Wort
# Wert = Anzahl Vorkommen
Ausgabe:
Vokabular: ['auto' 'blau' 'das' 'fahrrad' 'ist' 'rot' 'und']
[[1 0 1 0 1 1 0]
[0 1 1 1 1 0 0]
[1 0 2 1 0 0 1]]
Problem: Häufige Wörter wie „das" dominieren, obwohl sie wenig aussagen.
3.2 TF-IDF
TF-IDF (Term Frequency – Inverse Document Frequency) gewichtet Wörter nach ihrer Informationsrelevanz:
TF(t, d) = Häufigkeit von Term t in Dokument d
IDF(t) = log(N / df(t)) ... N = Gesamtzahl Dokumente, df = Dokumente mit Term t
TF-IDF(t, d) = TF(t, d) × IDF(t)
- Häufiges Wort in vielen Texten → niedriger IDF → niedrige Gewichtung
- Seltenes Wort, das in einem Text oft vorkommt → hohe Gewichtung
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
# Beispiel-Datensatz
texte = [
"das auto ist sehr schnell",
"ich liebe mein fahrrad",
"das auto verbraucht viel benzin",
"fahrrad fahren ist gesund",
"neues auto gekauft sehr günstig",
"mit dem fahrrad durch den park"
]
labels = [0, 1, 0, 1, 0, 1] # 0 = Auto, 1 = Fahrrad
X_train, X_test, y_train, y_test = train_test_split(
texte, labels, test_size=0.33, random_state=42
)
# Pipeline: TF-IDF + Naive Bayes (klassischer NLP-Baseline)
pipeline = Pipeline([
('tfidf', TfidfVectorizer(max_features=1000)),
('clf', MultinomialNB())
])
pipeline.fit(X_train, y_train)
print(f"Accuracy: {pipeline.score(X_test, y_test):.2%}")
# Vorhersage für neuen Text
neu = ["schnelles elektroauto gekauft"]
print(f"Klasse: {'Fahrrad' if pipeline.predict(neu)[0] else 'Auto'}")
3.3 Grenzen klassischer Vektorisierung
| Problem | Beschreibung |
|---|---|
| Keine Semantik | „König" und „Herrscher" haben keine Ähnlichkeit |
| Keine Reihenfolge | „Hund beißt Mann" = „Mann beißt Hund" |
| Hohe Dimensionalität | Vokabular kann 100.000+ Wörter haben |
| Sparse Vektoren | Meiste Einträge sind 0 |
4. Word Embeddings
4.1 Grundidee
Word Embeddings repräsentieren Wörter als dichte Vektoren in einem niedrigdimensionalen Raum (z.B. 100–300 Dimensionen). Ähnliche Wörter haben ähnliche Vektoren.
"König" → [0.25, -0.12, 0.87, ...]
"Königin" → [0.23, -0.08, 0.84, ...] (ähnlich!)
"Auto" → [-0.45, 0.33, -0.12, ...] (sehr verschieden)
4.2 Word2Vec
Word2Vec (Google, 2013) lernt Embeddings aus großen Textmengen. Die Kernidee: Wörter, die in ähnlichen Kontexten vorkommen, haben ähnliche Bedeutungen.
from gensim.models import Word2Vec
# Trainingsdaten (Sätze als Token-Listen)
saetze = [
['der', 'könig', 'regiert', 'das', 'land'],
['die', 'koenigin', 'regiert', 'das', 'reich'],
['das', 'auto', 'faehrt', 'schnell'],
['das', 'fahrrad', 'faehrt', 'langsam'],
['der', 'hund', 'bellt', 'laut'],
['die', 'katze', 'schlaeft', 'viel'],
]
model = Word2Vec(saetze, vector_size=50, window=3, min_count=1, epochs=100)
# Ähnliche Wörter finden
print(model.wv.most_similar('koenig', topn=3))
# Vektorarithmetik: König - Mann + Frau ≈ Königin
result = model.wv.most_similar(
positive=['koenig', 'frau'],
negative=['mann'],
topn=1
)
print(f"König - Mann + Frau ≈ {result}")
4.3 Die berühmte Vektorarithmetik
vec("König") - vec("Mann") + vec("Frau") ≈ vec("Königin")
vec("Paris") - vec("Frankreich") + vec("Deutschland") ≈ vec("Berlin")
vec("laufen") - vec("laufe") + vec("schwimme") ≈ vec("schwimmen")
Diese Eigenschaft zeigt, dass Embeddings semantische Beziehungen kodieren.
4.4 Keras Embedding Layer
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# Embedding Layer direkt in Keras verwenden
vocab_size = 10000 # Vokabulargröße
embedding_dim = 64 # Dimensionen des Embedding-Vektors
max_length = 200 # Maximale Textlänge (in Tokens)
model = keras.Sequential([
layers.Embedding(vocab_size, embedding_dim, input_length=max_length),
# Output: (batch_size, max_length, embedding_dim)
layers.GlobalAveragePooling1D(), # Mittelwert über alle Token
layers.Dense(64, activation='relu'),
layers.Dense(1, activation='sigmoid') # Binäre Klassifikation
])
model.summary()
5. Rekurrente Netze – RNN und LSTM
5.1 Das Problem mit Sequenzen
Ein MLP verarbeitet jeden Eingabevektor unabhängig – es hat kein Gedächtnis. Für Texte ist aber die Reihenfolge der Wörter entscheidend.
"Der Hund beißt den Mann." ≠ "Den Mann beißt der Hund."
5.2 Recurrent Neural Networks (RNN)
RNNs verarbeiten Sequenzen schrittweise und übergeben einen Hidden State von einem Schritt zum nächsten:
x₁ x₂ x₃ x₄
│ │ │ │
[RNN] → [RNN] → [RNN] → [RNN]
│ │ │ │
h₁ h₂ h₃ h₄ → Ausgabe
hₜ = tanh(Wₕ · hₜ₋₁ + Wₓ · xₜ + b)
Problem: Bei langen Sequenzen „vergisst" das RNN frühe Informationen (Vanishing Gradient über Zeit).
5.3 Long Short-Term Memory (LSTM)
LSTM (Hochreiter & Schmidhuber, 1997) löst das Vergessen-Problem durch ein explizites Gedächtnis (Cell State) und drei Gates, die steuern, was gespeichert, vergessen oder ausgegeben wird:
┌─────────────────────────────────┐
│ Cell State Cₜ │
│ ┌──────────┐ ┌──────────┐ │
Forget ──►│ │ Forget │ │ Input │ │
Gate │ │ Gate │──►│ Gate │ │
Input ──►│ └──────────┘ └──────────┘ │
│ ↓ │
Output ◄──│ Output Gate │
└─────────────────────────────────┘
- Forget Gate: Was soll aus dem Gedächtnis gelöscht werden?
- Input Gate: Was soll neu gespeichert werden?
- Output Gate: Was soll ausgegeben werden?
5.4 LSTM für Sentiment-Analyse
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
# ── Daten laden: IMDB Film-Reviews ───────────────────────────────────────
vocab_size = 10000
max_length = 200
embed_dim = 64
(X_train, y_train), (X_test, y_test) = keras.datasets.imdb.load_data(
num_words=vocab_size
)
# Padding: alle Sequenzen auf gleiche Länge bringen
X_train = keras.preprocessing.sequence.pad_sequences(X_train, maxlen=max_length)
X_test = keras.preprocessing.sequence.pad_sequences(X_test, maxlen=max_length)
print(f"Trainingsdaten: {X_train.shape}") # (25000, 200)
# ── LSTM-Modell ───────────────────────────────────────────────────────────
model = keras.Sequential([
layers.Embedding(vocab_size, embed_dim, input_length=max_length),
layers.LSTM(64, return_sequences=True), # Gibt alle Hidden States zurück
layers.LSTM(32), # Nur letzten Hidden State
layers.Dropout(0.5),
layers.Dense(1, activation='sigmoid') # Positiv / Negativ
])
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy']
)
model.summary()
history = model.fit(
X_train, y_train,
epochs=5,
batch_size=64,
validation_split=0.1
)
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Accuracy: {test_acc:.2%}") # Typisch: ~87%
5.5 RNN vs. LSTM vs. GRU
| RNN | LSTM | GRU | |
|---|---|---|---|
| Langzeitgedächtnis | ❌ Schlecht | ✅ Gut | ✅ Gut |
| Geschwindigkeit | Schnell | Langsam | Mittel |
| Parameter | Wenig | Viele | Mittel |
| Komplexität | Einfach | Komplex | Einfacher als LSTM |
| Empfehlung | Selten | Standard | Guter Kompromiss |
6. Die Transformer-Architektur
6.1 Motivation: Probleme mit RNNs
RNNs/LSTMs haben zwei wesentliche Schwächen:
- Sequentielle Verarbeitung: Token für Token – keine Parallelisierung
- Begrenztes Gedächtnis: Auch LSTMs haben Probleme bei sehr langen Texten
Der Transformer (Vaswani et al., 2017, „Attention is All You Need") löst beide Probleme.
6.2 Self-Attention – das Herzstück
Self-Attention erlaubt jedem Token, auf alle anderen Tokens in der Sequenz zu achten – gleichzeitig und parallel.
Satz: "Die Bank am Fluss ist schön."
Beim Verarbeiten von "Bank":
→ hohe Aufmerksamkeit auf "Fluss" (Kontext zeigt: Uferbank, nicht Geldinstitut)
→ hohe Aufmerksamkeit auf "schön"
→ niedrige Aufmerksamkeit auf "Die", "am"
Berechnung (vereinfacht):
Für jeden Token werden drei Vektoren berechnet:
Q (Query): "Wonach suche ich?"
K (Key): "Was biete ich an?"
V (Value): "Was ist mein Inhalt?"
Attention(Q, K, V) = softmax(Q · Kᵀ / √dₖ) · V
dₖ = Dimension der Key-Vektoren (Skalierung für numerische Stabilität)
6.3 Multi-Head Attention
Statt einer einzigen Attention-Berechnung verwendet der Transformer mehrere parallele Attention-Köpfe – jeder kann auf andere Beziehungen fokussieren (z.B. grammatische Struktur, semantische Ähnlichkeit):
Eingabe
│
├─ Head 1: achtet auf syntaktische Beziehungen
├─ Head 2: achtet auf semantische Beziehungen
├─ Head 3: achtet auf Korefrenz (welches "er" ist gemeint?)
└─ ...
│
Konkatenation + lineare Projektion
│
Ausgabe
6.4 Vollständige Transformer-Architektur
Encoder (für Textverständnis): Decoder (für Textgenerierung):
Eingabe-Tokens Ausgabe-Tokens (verschoben)
│ │
Positional Encoding Positional Encoding
│ │
┌─────────────┐ ┌─────────────────────┐
│ Multi-Head │ │ Masked Multi-Head │
│ Attention │ │ Attention │
├─────────────┤ ├─────────────────────┤
│ Add & Norm │ │ Cross-Attention │
├─────────────┤ │ (auf Encoder) │
│ Feed-Forward│ ├─────────────────────┤
├─────────────┤ │ Feed-Forward │
│ Add & Norm │ ├─────────────────────┤
└─────────────┘ × N └─────────────────────┘ × N
│ │
Encoder-Output Linear + Softmax
│
Nächstes Token
Positional Encoding: Da Transformer keine Reihenfolge kennen (im Gegensatz zu RNNs), werden Positions-Informationen explizit als Vektoren zum Embedding addiert.
7. BERT und vortrainierte Sprachmodelle
7.1 Was ist BERT?
BERT (Bidirectional Encoder Representations from Transformers, Google 2018) ist ein vortrainierter Transformer, der auf riesigen Textmengen trainiert wurde.
Zwei Trainingsaufgaben:
- Masked Language Model: 15% der Tokens werden maskiert, BERT muss sie vorhersagen
- Next Sentence Prediction: Gehören zwei Sätze zusammen?
Eingabe: "Der [MASK] schläft auf dem Sofa."
BERT: "Der Hund schläft auf dem Sofa." (hohe Konfidenz)
"Der Tisch schläft auf dem Sofa." (niedrige Konfidenz)
7.2 BERT ist bidirektional
Im Gegensatz zu GPT (links→rechts) liest BERT beide Richtungen gleichzeitig:
GPT (unidirektional): Der → Hund → bellt → ?
BERT (bidirektional): ← Der [MASK] bellt → (sieht Kontext links UND rechts)
Das macht BERT besonders gut für Verstehens-Aufgaben (Klassifikation, NER, QA).
7.3 Wichtige vortrainierte Modelle
| Modell | Organisation | Stärke |
|---|---|---|
| BERT | Textverständnis, Klassifikation | |
| RoBERTa | Facebook/Meta | Verbessertes BERT |
| GPT-4 | OpenAI | Textgenerierung |
| T5 | Text-zu-Text alle Aufgaben | |
| LLaMA | Meta | Open-Source LLM |
| deepset/gbert | deepset | BERT auf Deutsch |
7.4 Hugging Face – die NLP-Bibliothek
Hugging Face Transformers ist die zentrale Bibliothek für vortrainierte Modelle:
pip install transformers datasets
from transformers import pipeline
# Sentiment-Analyse (Englisch, ohne eigenes Training!)
classifier = pipeline('sentiment-analysis')
results = classifier([
"This product is absolutely amazing!",
"I'm really disappointed with this purchase.",
"It's okay, nothing special."
])
for r in results:
print(f"{r['label']:10} ({r['score']:.2%})")
# Mehrsprachig: Modell für Deutsch
de_classifier = pipeline(
'sentiment-analysis',
model='oliverguhr/german-sentiment-bert'
)
print(de_classifier("Das Produkt ist fantastisch!"))
8. Praktisches Projekt: Sentiment-Analyse
8.1 Aufgabe
Analyse von Produktbewertungen: Ist die Stimmung positiv, negativ oder neutral?
8.2 Vollständige Pipeline mit TF-IDF + klassischem ML
import pandas as pd
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt
import re
# ── 1. Daten erstellen ────────────────────────────────────────────────────
bewertungen = [
# Positiv (Label 2)
("Absolut begeistert! Top Qualität und schnelle Lieferung.", 2),
("Sehr zufrieden, genau wie beschrieben. Klare Kaufempfehlung!", 2),
("Perfektes Produkt, hält was es verspricht. 5 Sterne.", 2),
("Super schnelle Lieferung, Produkt ist fantastisch.", 2),
("Bin total begeistert, werde es weiterempfehlen.", 2),
("Hervorragende Qualität, jederzeit wieder.", 2),
# Neutral (Label 1)
("Produkt ist okay, nichts Besonderes aber funktioniert.", 1),
("Ganz in Ordnung für den Preis. Nicht schlecht, nicht gut.", 1),
("Erwartet mehr, aber es geht so. Mittelmäßig.", 1),
("Durchschnittlich. Würde es weder empfehlen noch abraten.", 1),
# Negativ (Label 0)
("Totale Enttäuschung! Qualität ist katastrophal.", 0),
("Nie wieder! Das Produkt ist nach einer Woche kaputt.", 0),
("Absolut furchtbar. Geld raus geworfen.", 0),
("Funktioniert überhaupt nicht wie beworben. Rückgabe!", 0),
("Schreckliche Qualität, komplett enttäuscht.", 0),
]
df = pd.DataFrame(bewertungen, columns=['text', 'label'])
label_names = {0: 'Negativ', 1: 'Neutral', 2: 'Positiv'}
X_train, X_test, y_train, y_test = train_test_split(
df['text'], df['label'], test_size=0.25, random_state=42, stratify=df['label']
)
# ── 2. Pipeline aufbauen ──────────────────────────────────────────────────
pipeline = Pipeline([
('tfidf', TfidfVectorizer(
max_features=500,
ngram_range=(1, 2), # Unigramme + Bigramme
sublinear_tf=True # log(TF) statt TF
)),
('clf', LogisticRegression(max_iter=1000, C=1.0))
])
pipeline.fit(X_train, y_train)
# ── 3. Evaluierung ────────────────────────────────────────────────────────
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred,
target_names=[label_names[i] for i in sorted(label_names)]))
# ── 4. Confusion Matrix ───────────────────────────────────────────────────
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Oranges',
xticklabels=label_names.values(),
yticklabels=label_names.values())
plt.title('Confusion Matrix – Sentiment-Analyse')
plt.tight_layout()
plt.show()
# ── 5. Neue Bewertungen klassifizieren ────────────────────────────────────
neue_texte = [
"Ich bin sehr zufrieden mit dem Kauf!",
"Das Gerät ist nach 3 Tagen kaputt gegangen.",
"Naja, geht so. War billiger als gedacht."
]
for text in neue_texte:
pred = pipeline.predict([text])[0]
proba = pipeline.predict_proba([text])[0]
print(f"{label_names[pred]:10} ({proba.max():.0%}) | {text}")
8.3 Wichtige Features anzeigen
# Welche Wörter sind am aussagekräftigsten?
feature_names = pipeline['tfidf'].get_feature_names_out()
coef = pipeline['clf'].coef_
fig, axes = plt.subplots(1, 3, figsize=(14, 5))
klassen = ['Negativ', 'Neutral', 'Positiv']
for i, (ax, klasse) in enumerate(zip(axes, klassen)):
top_idx = coef[i].argsort()[-10:][::-1]
top_feat = feature_names[top_idx]
top_coef = coef[i][top_idx]
ax.barh(top_feat, top_coef, color='#F6A01D')
ax.set_title(f'Top Features: {klasse}')
ax.invert_yaxis()
plt.tight_layout()
plt.show()
9. Praktisches Projekt: Textklassifikation mit BERT
9.1 Fine-Tuning eines deutschen BERT-Modells
from transformers import (
AutoTokenizer,
AutoModelForSequenceClassification,
TrainingArguments,
Trainer
)
from datasets import Dataset
import torch
import numpy as np
# ── 1. Daten vorbereiten ──────────────────────────────────────────────────
texte = [
"Das Produkt ist fantastisch und hält was es verspricht.",
"Absolut enttäuschend, nie wieder!",
"Ganz okay für den Preis.",
"Hervorragende Qualität, sehr empfehlenswert.",
"Katastrophale Verarbeitung, sofort kaputt gegangen.",
"Durchschnittlich, nichts Besonderes.",
]
labels = [2, 0, 1, 2, 0, 1] # 0=Negativ, 1=Neutral, 2=Positiv
dataset = Dataset.from_dict({'text': texte, 'label': labels})
dataset = dataset.train_test_split(test_size=0.33)
# ── 2. Tokenizer laden ────────────────────────────────────────────────────
model_name = 'deepset/gbert-base' # Deutsches BERT
tokenizer = AutoTokenizer.from_pretrained(model_name)
def tokenize(batch):
return tokenizer(batch['text'], truncation=True,
padding='max_length', max_length=128)
dataset = dataset.map(tokenize, batched=True)
# ── 3. Modell laden und anpassen ──────────────────────────────────────────
model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=3 # Negativ, Neutral, Positiv
)
# ── 4. Training ───────────────────────────────────────────────────────────
training_args = TrainingArguments(
output_dir='./bert_sentiment',
num_train_epochs=3,
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
evaluation_strategy='epoch',
save_strategy='epoch',
load_best_model_at_end=True,
logging_steps=10,
)
def compute_metrics(eval_pred):
logits, labels = eval_pred
preds = np.argmax(logits, axis=1)
return {'accuracy': (preds == labels).mean()}
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset['train'],
eval_dataset=dataset['test'],
compute_metrics=compute_metrics,
)
trainer.train()
# ── 5. Vorhersage ─────────────────────────────────────────────────────────
def predict_sentiment(text):
inputs = tokenizer(text, return_tensors='pt',
truncation=True, max_length=128)
with torch.no_grad():
outputs = model(**inputs)
probs = torch.softmax(outputs.logits, dim=1)[0]
label = probs.argmax().item()
names = ['Negativ', 'Neutral', 'Positiv']
return f"{names[label]} ({probs[label]:.1%})"
print(predict_sentiment("Das Produkt ist wirklich toll!"))
print(predict_sentiment("Bin sehr enttäuscht davon."))
10. Große Sprachmodelle und ChatGPT
10.1 Was sind Large Language Models (LLMs)?
LLMs sind Transformer-Modelle mit Milliarden von Parametern, die auf riesigen Textmengen trainiert wurden:
| Modell | Parameter | Training-Daten (ca.) |
|---|---|---|
| GPT-2 (2019) | 1,5 Mrd. | 40 GB Text |
| GPT-3 (2020) | 175 Mrd. | 570 GB Text |
| GPT-4 (2023) | ~1 Billion (geschätzt) | Mehrere TB Text |
| LLaMA 3 (2024) | 8–70 Mrd. | 15+ TB Text |
10.2 Wie funktioniert ChatGPT?
1. Pretraining (GPT-Basis):
Nächstes Token vorhersagen auf riesigen Textmengen
→ Modell lernt Sprache, Fakten, Zusammenhänge
2. Supervised Fine-Tuning (SFT):
Menschliche Demonstrationen von guten Antworten
→ Modell lernt Gesprächsformat
3. RLHF (Reinforcement Learning from Human Feedback):
Menschen bewerten Antworten → Belohnungsmodell
→ Modell wird auf menschliche Präferenzen optimiert
10.3 Prompt Engineering
Die Qualität der Ausgabe hängt stark vom Prompt ab:
# Schlechter Prompt:
"Erkläre ML"
# Besserer Prompt (mit Kontext und Format):
"""
Du bist ein erfahrener Lehrer für Informatik an einer HTL.
Erkläre Machine Learning für 16-jährige Schüler mit Python-Kenntnissen.
Verwende eine einfache Analogie und halte die Erklärung auf 3-4 Sätze.
"""
# Few-Shot Prompting (Beispiele geben):
"""
Klassifiziere die Stimmung als POSITIV, NEGATIV oder NEUTRAL:
Beispiel: "Das war toll!" → POSITIV
Beispiel: "Nie wieder." → NEGATIV
Beispiel: "Geht so." → NEUTRAL
Jetzt: "Bin wirklich beeindruckt!" →
"""
10.4 Anthropic API in Python
import anthropic
client = anthropic.Anthropic(api_key="dein-api-key")
message = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Erkläre den Unterschied zwischen RNN und Transformer."
}
]
)
print(message.content[0].text)
11. Ethische Aspekte in NLP
11.1 Bias in Sprachmodellen
Sprachmodelle lernen aus menschlichen Texten – und übernehmen dabei auch menschliche Vorurteile:
# Klassisches Beispiel für Bias in Word2Vec:
# vec("Arzt") - vec("Mann") + vec("Frau") ≈ vec("Krankenschwester")
# (nicht vec("Ärztin")!)
Ursachen:
- Trainingsdaten spiegeln gesellschaftliche Ungleichheiten wider
- Manche Gruppen sind in Texten unterrepräsentiert
- Historische Texte enthalten veraltete Weltbilder
11.2 Halluzination
LLMs können Aussagen mit hoher Konfidenz machen, die faktisch falsch sind:
Benutzer: "Wann wurde die HTL Pinkafeld gegründet?"
LLM: "Die HTL Pinkafeld wurde 1923 gegründet." (falsch, aber überzeugend formuliert)
Gegenmaßnahmen: Retrieval-Augmented Generation (RAG) – Modell sucht erst in vertrauenswürdigen Quellen, bevor es antwortet.
11.3 Datenschutz
- Trainingsdaten können persönliche Informationen enthalten
- Modelle können im Extremfall Trainingsdaten „memorieren"
- Eingaben in externe APIs (z.B. ChatGPT) sollten keine sensiblen Daten enthalten
11.4 Verantwortungsvoller Einsatz
✅ Quellen kritisch hinterfragen
✅ KI-generierte Inhalte als solche kennzeichnen
✅ Halluzinationen mit vertrauenswürdigen Quellen gegenprüfen
✅ Keine sensiblen Daten in externe Modelle eingeben
✅ Bias-Tests bei kritischen Anwendungen durchführen
12. Zusammenfassung und Ausblick
Lernpfad NLP
Klassisches NLP Modernes NLP
────────────── ────────────
Tokenisierung Word Embeddings (Word2Vec)
Stopwörter │
BoW / TF-IDF RNN / LSTM
│ │
Naive Bayes Transformer
Logistische Regression (Self-Attention)
│
BERT / GPT / T5
(vortrainiert + fine-tuned)
│
ChatGPT / Claude
(RLHF, Instruction Tuning)
Entscheidungshilfe
Aufgabe bekannt, wenig Daten?
→ Vortrainiertes Modell (Hugging Face Pipeline)
Einfache Klassifikation, viele Daten?
→ TF-IDF + Logistische Regression als Baseline
Komplexe Klassifikation?
→ BERT Fine-Tuning
Textgenerierung?
→ GPT-basiertes Modell oder LLM-API
Mustererkennung in Sequenzen?
→ LSTM oder Transformer
Checkliste
✅ Text immer vorverarbeiten (lower, clean, tokenize)
✅ Mit einfachem Baseline beginnen (TF-IDF + LogReg)
✅ Vortrainierte Modelle bevorzugen (Transfer Learning!)
✅ Tokenizer zum Modell passend wählen
✅ Auf Bias und Halluzinationen testen
✅ Ergebnisse mit classification_report evaluieren
Ausblick: Nächste Themen
- MLOps: Modelle versionieren, deployen und überwachen
- Retrieval-Augmented Generation (RAG): LLMs mit eigenem Wissen erweitern
- Multimodale Modelle: Text + Bild (GPT-4o, Gemini)
Anhang: Schnellreferenz NLP
# ── Textvorverarbeitung ───────────────────────────────────────────────────
import re, nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
nltk.download(['punkt', 'stopwords'], quiet=True)
def clean(text, lang='german'):
text = text.lower()
text = re.sub(r'[^a-züäöß\s]', '', text)
tokens = word_tokenize(text, language=lang)
stop = set(stopwords.words(lang))
return [t for t in tokens if t not in stop and len(t) > 2]
# ── TF-IDF + Klassifikation ───────────────────────────────────────────────
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
pipeline = Pipeline([
('tfidf', TfidfVectorizer(max_features=5000, ngram_range=(1,2))),
('clf', LogisticRegression(max_iter=1000))
])
pipeline.fit(X_train, y_train)
pipeline.predict(["neuer Text"])
# ── Hugging Face Pipeline (einfachste Variante) ───────────────────────────
from transformers import pipeline as hf_pipeline
nlp = hf_pipeline('sentiment-analysis',
model='oliverguhr/german-sentiment-bert')
nlp(["Tolles Produkt!", "Sehr enttäuschend."])
# ── IMDB LSTM ─────────────────────────────────────────────────────────────
from tensorflow import keras
from tensorflow.keras import layers
(X_train, y_train), (X_test, y_test) = keras.datasets.imdb.load_data(num_words=10000)
X_train = keras.preprocessing.sequence.pad_sequences(X_train, maxlen=200)
model = keras.Sequential([
layers.Embedding(10000, 64, input_length=200),
layers.LSTM(64),
layers.Dense(1, activation='sigmoid')
])
model.compile('adam', 'binary_crossentropy', ['accuracy'])
HTL Pinkafeld – IF/IT | Data Science & AI | 12. Schulstufe