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)
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            │
          └─────────────────────────────────┘

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:

  1. Sequentielle Verarbeitung: Token für Token – keine Parallelisierung
  2. 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:

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 Google Textverständnis, Klassifikation
RoBERTa Facebook/Meta Verbessertes BERT
GPT-4 OpenAI Textgenerierung
T5 Google 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:

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

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


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

Vorheriges ThemaDeep Learning Nächstes ThemaMLOps