Fortgeschrittene NLP-Techniken

Wir interagieren eigentlich jeden Tag mit NLP – die Wortvorschläge beim Tippen auf der Tastatur Ihres Handys, die Spam-E-Mails, die von Ihrer E-Mail-App automatisch gefiltert werden, und die fremdsprachigen Websites, die Ihnen Übersetzungssoftware verständlich macht. All dies sind Anwendungen der natürlichen Sprachverarbeitung (Natural Language Processing, NLP).

Bevor ChatGPT auftauchte, hatte sich NLP bereits über Jahrzehnte hinweg entwickelt, aber die technische Hürde war hoch. Man musste eine lange Liste von Konzepten wie Tokenisierung, Part-of-Speech-Tagging, syntaktische Analyse und semantische Rollenetikettierung verstehen und zudem Merkmale manuell entwerfen, damit Maschinen Sprache ein wenig „verstehen“ konnten.

Heutzutage machen große Sprachmodelle all dies einfach. Wenn man jedoch die technische Evolution von NLP versteht, kann man gründlicher nachvollziehen, warum große Modellen dazu in der Lage sind und wo ihre Grenzen liegen.

Dieses Modul führt Sie von Wortvektoren bis hin zu den heutigen großen Sprachmodellen und baut ein vollständiges NLP-Wissenssystem auf.。

Lernpfad: Wortvektoren → vortrainierte Modelle → die drei Hauptparadigmen von BERT/GPT/T5 → Downstream-Aufgaben (Klassifizierung, NER, Übersetzung, Zusammenfassung). Jeder Schritt enthält entsprechende Codebeispiele, die Ihnen helfen, die Theorie in die Praxis umzusetzen.


Die Evolution vortrainierter Sprachmodelle

Die Entwicklung von NLP lässt sich klar in mehrere Phasen unterteilen, von denen jede wegweisende technologische Durchbrüche aufweist.

Word2Vec: Die Revolution der Worteinbettungen

Vor 2013 war die Art und Weise, wie Computer Text verarbeiteten, sehr primitiv.

Meistens wurde „One-Hot-Encoding“ verwendet: Jedes Wort entspricht einem extrem langen Vektor, bei dem nur eine Position den Wert 1 und der Rest den Wert 0 hat. Wenn das Vokabular beispielsweise 100.000 Wörter umfasst, ist jedes Wort ein 100.000-dimensionaler Vektor.

Das Problem bei diesem Ansatz liegt auf der Hand: Die Vektoren enthalten keinerlei semantische Informationen. Der Abstand zwischen „Katze“ und „Hund“ ist derselbe wie der Abstand zwischen „Katze“ und „Tisch“.

Die Kernidee von Word2Vec lautet:Die Bedeutung eines Wortes wird durch die Wörter definiert, die es umgeben.。

Beispiel

# ============================================
# Demonstration des Word2Vec-Grundkonzepts
# Verwenden Sie die gensim-Bibliothek, um ein einfaches Wortvektormodell zu trainieren
# ============================================

# Installieren Sie zuerst gensim: pip install gensim

from gensim.models import Word2Vec
import numpy as np

# Trainingsdaten vorbereiten: ein paar einfache Sätze
sentences = [
    ["I", "like", "eat", "apple"],
    ["I", "like", "eat", "banana"],
    ["Katze", "mögen", "essen", "Fisch"],
    ["Hund", "mögen", "essen", "Fleisch"],
    ["Apfel", "ist", "eine Art von", "Frucht"],
    ["Banane", "ist", "eine Art von", "Frucht"],
    ["Katze", "ist", "eine Art von", "Tier"],
    ["Hund", "ist", "eine Art von", "Tier"],
    ["BEISPIEL", "ist", "a", Programmier, Website],
    [Lernen, Programmieren, Go, "BEISPIEL"],
]

# Word2Vec-Modell trainieren
# vector_size: Dimension der Wortvektoren
# window: Kontextfenstergröße (betrachte einige Wörter davor und danach)
# min_count: Wörter ignorieren, deren Häufigkeit unter diesem Wert liegt
# workers: Anzahl der Threads für das parallele Training
model = Word2Vec(
    sentences=sentences,
    vector_size=50,  # jedes Wort wird durch einen 50-dimensionalen Vektor repräsentiert
    window=3,       # betrachte 3 Wörter davor und danach
    min_count=1,    # behalte alle Wörter
    workers=4,
    epochs=100      # trainiere für 100 Epochen
)

# Wortvektor abrufen
apple_vector = model.wv[apple]
print(fWortvektor für 'apple' (erste 10 Dimensionen): {apple_vector[:10]})
print(fWortvektor-Dimension: {len(apple_vector)})

# Ähnlichkeit zwischen Wörtern berechnen
similarity = model.wv.similarity(apple, banana)
print(fÄhnlichkeit zwischen 'apple' und 'banana': {similarity:.4f})

similarity = model.wv.similarity(apple, cat)
print(fÄhnlichkeit zwischen 'apple' und 'cat': {similarity:.4f})

# ähnlichste Wörter finden
print("\nÄhnlichste Wörter zu 'cat':)
for word, score in model.wv.most_similar(cat, topn=3):
    print(f"  {word}: {score:.4f}")

print("\nÄhnlichste Wörter zu 'EXAMPLE':)
for word, score in model.wv.most_similar("EXAMPLE", topn=3):
    print(f"  {word}: {score:.4f}")

# klassische Wortvektor-Arithmetik: König - Mann + Frau ≈ Königin
# in unserem kleinen Korpus versuchen: Frucht - Apfel + Fisch ≈ ?
if apple in model.wv and fish in model.wv and fruit in model.wv:
    result = model.wv.most_similar(positive=[fruit, fish], negative=[apple], topn=3)
    print("\n'fruit' - 'apple' + 'fish' ≈)
    for word, score in result:
        print(f"  {word}: {score:.4f}")

Der Erfolg von Word2Vec beweist eine Sache:Semantik kann mithilfe von Vektorräumen dargestellt werden.。

Aber es hat eine Einschränkung: Jedes Wort hat nur einen festen Vektor, unabhängig vom Kontext. Zum Beispiel hat "打" in "打电话" (einen Anruf tätigen) und "打游戏" (Spiele spielen) unterschiedliche Bedeutungen, aber Word2Vec liefert denselben Vektor.

ELMo: Kontextuelle Wort-Einbettungen

ELMo (Embeddings from Language Models), eingeführt im Jahr 2018, löste dieses Problem.

Die Idee von ELMo ist:Es weist nicht vorab jedem Wort einen festen Vektor zu, sondern betrachtet den gesamten Satz und generiert dann einen Vektor für dieses Wort.。

Dasselbe Zeichen „打“ ist ein Vektor in „I打电话“ und ein anderer Vektor in „I打游戏“.

ELMo verwendet bidirektionale LSTM (Long Short-Term Memory-Netzwerke), um den Kontext zu modellieren, was die erste groß angelegte Nutzung des Paradigmas "Vortraining + Feinabstimmung" war.

GPT-1: Unidirektionales Vortraining

Ebenfalls im Jahr 2018 veröffentlichte OpenAI GPT-1 (Generative Pre-training Transformer).

Seine Merkmale sind:

1. Verwendung eines Transformer-Decoders anstelle eines LSTM.

2. Unidirektional: betrachtet nur die vorhergehenden Wörter, sagt das nächste Wort voraus.

3. Generativ: kann Text weiterschreiben.

GPT-1 demonstrierte das große Potenzial von Transformer bei NLP-Aufgaben.

BERT: Bidirektionales Vortraining

Ende 2018 veröffentlichte Google BERT (Bidirectional Encoder Representations from Transformers), was das NLP-Feld komplett veränderte.

Die Kerninnovation von BERT ist:

Bidirektional: Betrachtet sowohl den vorhergehenden als auch den nachfolgenden Kontext gleichzeitig.

2. MLM (Masked Language Model): Maskiert zufällig einige Wörter und lässt das Modell diese vorhersagen.

3. NSP (Next Sentence Prediction): Bestimmt, ob zwei Sätze aufeinanderfolgen.

BERT erzielte zu dieser Zeit die besten Ergebnisse bei 11 NLP-Aufgaben und markierte den Eintritt der NLP in die "Ära der vortrainierten Modelle".

Der Sprung von GPT-3 zu ChatGPT

Im Jahr 2020 wurde GPT-3 mit einer Parameteranzahl von 175 Milliarden veröffentlicht.

Man hat festgestellt, dass, wenn das Modell groß genug und die Daten reichlich genug sind, ein "Emergenz"-Phänomen auftritt – das Modell erlangt plötzlich Fähigkeiten, die kleinen Modellen fehlen, wie Few-Shot-Learning und komplexes Schlussfolgern.

Ende 2022 wurde ChatGPT veröffentlicht, das RLHF (Reinforcement Learning from Human Feedback) nutzt, um die Ausgaben des Modells besser an menschliche Präferenzen anzupassen, und KI wurde wahrhaftig mainstream.

Fassen wir diese Evolutionsgeschichte mit einer Tabelle zusammen:

JahrmodelKernideeHistorischer Status
2013Word2VecNutze die Wörter um ein Wort herum, um seine Bedeutung zu definieren.Die Wortvektor-Revolution: voneinander abgeleitete Repräsentation von Semantik.
2018ELMoKontextabhängige WortvektorenErste Implementierung der Vektordarstellung von Polysemie.
2018GPT-1Transformer-Decoder, unidirektionales Vortraining.Demonstriert das Potenzial von Transformern.
2018BERTTransformer-Encoder, bidirektionales VortrainingNLP tritt in die Ära der vortrainierten Modelle ein
2020GPT-3175 Milliarden Parameter, emergente FähigkeitenDemonstriert die unendlichen Möglichkeiten großer Modelle
2022ChatGPTRLHF + KonversationsfähigkeitenKI erreicht wahrhaftig die Massen

BERT im Detail

BERT ist ein Meilenstein in der NLP-Geschichte und verdient unser eingehendes Verständnis.

MLM-Aufgabe (Masked Language Model)

BERTs Kern-Vortrainingsaufgabe ist MLM: Ersetzen von zufällig 15 % der Wörter in einem Satz durch [MASK], und das Modell vorhersagen lassen, wie das ursprüngliche Wort lautete.

Zum Beispiel könnte der Satz: „I like eating apples“ zu Folgendem werden: „I [MASK] eating apples“, und das Modell muss vorhersagen, dass [MASK] „like“ ist.

Warum das Ganze? Weil es das Modell zwingt, gleichzeitig Informationen aus dem umliegenden Kontext zu verwenden, nicht nur von links oder rechts.

In der Praxis werden diese 15 % der Wörter nicht alle durch [MASK] ersetzt, sondern wie folgt:

1. Zu 80 % der Zeit durch [MASK] ersetzt

2. Zu 10 % der Zeit durch ein zufälliges Wort ersetzt

3. Zu 10 % der Zeit wird das Originalwort unverändert beibehalten

Dies geschieht, um das Modell robuster zu machen – es kann sich nicht sicher sein, ob ein Wort ersetzt wurde, also muss es den Kontext wirklich verstehen.

NSP-Aufgabe (Next Sentence Prediction)

BERTs zweite Vortrainingsaufgabe ist NSP: Gegeben zwei Sätze A und B, bestimmen, ob B der wahre nächste Satz nach A ist.

Positives Beispiel: A = „I like eating apples“, B = „Apples are a fruit“

Negatives Beispiel: A = „I like eating apples“, B = „The weather is really nice today“

Diese Aufgabe hilft dem Modell, Beziehungen zwischen Sätzen zu verstehen, was sehr hilfreich für Aufgaben wie Frage-Antwort-Systeme und Natural Language Inference ist.

Anwendungsfälle für BERT

BERT ist eine „Encoder“-Architektur, die gut darin ist, Sprache zu verstehen, und geeignet für:

Textklassifikation, Sentiment-Analyse, Named Entity Recognition, Frage-Antwort-Systeme, Natural Language Inference usw.

Es ist nicht sehr gut darin, Text zu generieren, was die Stärke von GPT ist.

Standardprozess für das Fine-Tuning von BERT

Lassen Sie uns die Hugging Face Transformers-Bibliothek verwenden, um BERT für die Textklassifikation in der Praxis anzupassen:

Beispiel

# ============================================
# Fine-Tuning von BERT zur Textklassifikation mit Hugging Face
# Aufgabe: Bestimmen, ob ein Satz ein positives oder negatives Sentiment hat
# ============================================

# Zuerst Abhängigkeiten installieren:
# pip install transformers datasets torch scikit-learn

import torch
from transformers import (
    BertTokenizer,
    BertForSequenceClassification,
    TrainingArguments,
    Trainer,
)
from datasets import Dataset, load_metric
import numpy as np
from sklearn.model_selection import train_test_split

# ============================================
# 1. Daten vorbereiten
# ============================================

# Konstruieren eines einfachen Sentiment-Klassifikationsdatensatzes
texts = [
    „Dieses Produkt ist wirklich gut zu benutzen, ich mag es sehr“,
    „Die Qualität ist zu schlecht, sehr enttäuschend“,
    „Das EXAMPLE-Tutorial ist klar geschrieben und leicht zu erlernen“,
    Die Logistik ist sehr langsam und die Verpackung ist auch beschädigt,
    Die Kamera-Leistung dieses Handys ist ausgezeichnet,
    Schlechte Service-Haltung, komme nicht wieder,
    Der Inhalt des Buches ist wunderbar, zum Kauf empfohlen,
    Der Film ist sehr langweilig, bin beim Anschauen eingeschlafen,
    Das Essen in diesem Restaurant ist sehr lecker,
    Das Spiel hat zu viele Bugs, schlechte Erfahrung,
    Das NLP-Tutorial von EXAMPLE ist sehr hilfreich,
    Die Benutzeroberfläche dieser Software ist gut gestaltet,
    Die Haltung des Kuriers ist sehr freundlich,
    Die Konfektionsgröße ist ungenau und die Farbe ist auch falsch,
    Die Hotelumgebung ist sehr ruhig, habe gut geschlafen,
    Der Kundenservice antwortet sehr langsam, das Problem wurde nicht gelöst,
]

labels = [
    1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0
]  # 1 = positiv, 0 = negativ

# Trainings- und Validierungsdatensätze aufteilen
train_texts, val_texts, train_labels, val_labels = train_test_split(
    texts, labels, test_size=0.25, random_state=42
)

# Dataset-Objekt erstellen
train_dataset = Dataset.from_dict({"text": train_texts, "label": train_labels})
val_dataset = Dataset.from_dict({"text": val_texts, "label": val_labels})

# ============================================
# 2. Tokenizer und Modell laden
# ============================================

# Chinesisches BERT-Modell verwenden
model_name = "bert-base-chinese"

tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(
    model_name,
    num_labels=2  # Binäre Klassifikationsaufgabe
)

# ============================================
# 3. Datenvorverarbeitung: Text in Modelleingabe umwandeln
# ============================================

def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        padding="max_length",
        truncation=True,
        max_length=64
    )

# Vorverarbeitung anwenden
tokenized_train = train_dataset.map(tokenize_function, batched=True)
tokenized_val = val_dataset.map(tokenize_function, batched=True)

# ============================================
# 4. Evaluierungsmetriken definieren
# ============================================

metric = load_metric("accuracy")

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return metric.compute(predictions=predictions, references=labels)

# ============================================
# 5. Trainingsparameter konfigurieren
# ============================================

training_args = TrainingArguments(
    output_dir="./example-bert-sentiment",  # Ausgabeverzeichnis
    num_train_epochs=3,                    # Anzahl der Trainingsepochen
    per_device_train_batch_size=4,         # Trainings-Batch-Größe
    per_device_eval_batch_size=4,          # Evaluierungs-Batch-Größe
    warmup_steps=5,                        # Warmup-Schritte
    weight_decay=0.01,                     # Weight Decay
    logging_dir="./logs",                  # Log-Verzeichnis
    logging_steps=10,
    evaluation_strategy="epoch",           # Einmal pro Epoche evaluieren
    save_strategy="epoch",
    learning_rate=2e-5,
    load_best_model_at_end=True,
)

# ============================================
# 6. Trainer erstellen und Training starten
# ============================================

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_train,
    eval_dataset=tokenized_val,
    compute_metrics=compute_metrics,
)

# Training starten (auskommentiert, da dies viel Zeit und eine GPU erfordert)
# print("Start training...")
# trainer.train()

# ============================================
# 7. Use the trained model for prediction
# ============================================

# Here we directly use the original model to demonstrate the prediction process
# In actual use, you should use the model saved after training with trainer

def predict_sentiment(text, model, tokenizer):
    """Predict sentence sentiment"""
    inputs = tokenizer(
        text,
        return_tensors="pt",
        padding=True,
        truncation=True,
        max_length=64
    )

    model.eval()
    with torch.no_grad():
        outputs = model(**inputs)
        logits = outputs.logits
        probabilities = torch.softmax(logits, dim=-1)
        prediction = torch.argmax(probabilities, dim=-1).item()

    label_map = {0: "Negative", 1: "Positive"}
    confidence = probabilities[0][prediction].item()

    return {
        "text": text,
        "sentiment": label_map[prediction],
        "confidence": confidence,
        "negative_prob": probabilities[0][0].item(),
        "positive_prob": probabilities[0][1].item(),
    }

# Test a few sentences
test_texts = [
    "EXAMPLE tutorial is really great!",
    "This product's quality is too poor",
    "The weather is really nice today",
    "I don't really like this movie",
]

print("Test model prediction (using pretrained bert-base-chinese):")
print("-" * 50)
for text in test_texts:
    # Note: directly using the original model here, no fine-tuning, results are for demonstration only
    result = predict_sentiment(text, model, tokenizer)
    print(f"Text: {result['text']}")
    print(fSentiment: {result['sentiment']})
    print(fConfidence: {result['confidence']:.4f})
    print(fPositive probability: {result['positive_prob']:.4f})
    print(fNegative probability: {result['negative_prob']:.4f})
    print("-" * 50)

Hinweis: Der obige Code demonstriert die vollständige Pipeline, aber das eigentliche Fine-Tuning von BERT erfordert eine GPU und viel Zeit. In Produktionsumgebungen können Sie auch in Betracht ziehen, leichtere Modelle (wie DistilBERT) zu verwenden oder direkt die API eines großen Modells zu nutzen.


Tiefgehender Einblick in die GPT-Serie

GPT (Generative Pre-trained Transformer) schlug einen anderen Weg als BERT ein.

Autoregressives Sprachmodell

GPT ist „autoregressiv“: Es generiert Wort für Wort und verwendet bei jedem Schritt alle zuvor generierten Wörter, um das nächste Wort vorherzusagen.

Um zum Beispiel „Ich esse gerne Äpfel“ zu generieren, sieht der Prozess wie folgt aus:

1. Eingabe „Ich“, Vorhersage des nächsten Wortes „esse“

2. Eingabe „Ich esse“, Vorhersage des nächsten Wortes „gerne“

3. Eingabe „Ich esse gerne“, Vorhersage des nächsten Wortes „Äpfel“

Dieser Ansatz eignet sich von Natur aus hervorragend für die Texterstellung.

Decoder-Only-Architektur

GPT verwendet nur den Transformer-Decoder, während BERT nur den Encoder nutzt.

Das Charakteristikum des Decoders ist die „Masked Self-Attention“ – jede Position kann nur Positionen zu ihrer Linken sehen, nicht zu ihrer Rechten.

Das macht Sinn, da die Texterstellung von links nach rechts verläuft; man kann nicht spicken und Wörter vorab sehen, die noch gar nicht generiert wurden.

Die Entdeckung von Skalierungsgesetzen (Scaling Laws)

Die wichtigste Entdeckung der GPT-Serie ist das Skalierungsgesetz:Die Modellleistung weist eine Potenzgesetz-Beziehung zu Modellgröße, Datenvolumen und Rechenaufwand auf.。

Einfach ausgedrückt: Je größer das Modell, desto mehr Daten und je länger das Training, desto besser die Ergebnisse. Und diese Verbesserung verläuft vorhersehbar ohne ein offensichtliches Plateau.

Aus diesem Grund wird die GPT-Serie immer „größer“: GPT-1 (117M) → GPT-2 (1.5B) → GPT-3 (175B).


T5 und das Seq2Seq-Paradigma

Es gibt noch ein drittes Paradigma: die Encoder-Decoder-Struktur, repräsentiert durch T5.

Einheitliches Text-zu-Text-Framework

Der Kernaspekt von T5 (Text-to-Text Transfer Transformer) ist:Alle NLP-Aufgaben in ein „Text-zu-Text“-Format zu vereinheitlichen.。

Zum Beispiel:

Textklassifizierung: Eingabe „Classification: This movie is great“ → Ausgabe „Positive“

Übersetzung: Eingabe „Translate English to Chinese: Hello world“ → Ausgabe „Hello World“

Zusammenfassung: Eingabe „Summary: This is an article about...“ → Ausgabe „This article mainly discusses...“

Der Vorteil dieses einheitlichen Frameworks besteht darin, dass dasselbe Modell verschiedene Aufgaben bewältigen kann, ohne dass für jede Aufgabe die Modellstruktur geändert werden muss.

Wie der Encoder-Decoder funktioniert

Der Encoder ist dafür verantwortlich, den Eingabetext zu verstehen, und der Decoder ist für die Generierung des Ausgabetexts zuständig.

Nehmen wir die Übersetzung als Beispiel:

1. Der Encoder liest „Hello world“ und generiert eine „verstandene“ Repräsentation.

2. Basierend auf dieser Repräsentation generiert der Decoder Wort für Wort „Hello World“.

Der Decoder kann sich auf verschiedene Positionen des Encoders konzentrieren („attend“) – zum Beispiel achtet er beim Generieren von Hello stärker auf „Hello“ und beim Generieren von „world“ stärker auf „world“.


Vergleich der drei Hauptparadigmen: BERT vs. GPT vs. T5

Vergleichen wir diese drei Architekturen in einer Tabelle:

MerkmalBERTGPTT5
ArchitekturNur Encoder (Encoder-only)Nur Decoder (Decoder-only)Encoder-Decoder
AttentionBidirektional (sieht sowohl linken als auch rechten Kontext)Unidirektional (sieht nur den linken Kontext)Encoder bidirektional, Decoder unidirektional
VortrainingsaufgabeMLM (Masked Language Modeling)Autoregressives SprachmodellierenSpan Corruption (Span-Beschädigung)
Hervorragend geeignet fürVerständnisaufgaben: Klassifizierung, NER, QAGenerierungsaufgaben: Fortsetzung, DialogTransformationsaufgaben: Übersetzung, Zusammenfassung, Umschreibung
Repräsentative ModelleBERT, RoBERTa, ALBERTGPT-1/2/3/4, ChatGPTT5, BART, mT5
Typische AnwendungenStimmungsanalyse, Spam-FilterungSchreibassistent, ChatbotMaschinelle Übersetzung, Dokumentenzusammenfassung

Heutige große Sprachmodelle verwenden überwiegend eine Decoder-Only-Architektur (den GPT-Weg), da sie bei der Generierung am besten abschneidet und durch Skalierungsgesetze kontinuierlich verbessert werden kann. Die anderen beiden Architekturen haben jedoch für spezifische Aufgaben weiterhin ihre Vorteile.


Text-Klassifizierung

Die Text-Klassifizierung ist eine der häufigsten NLP-Aufgaben und die beste Einstiegsaufgabe für Anfänger.

Multiklassen-Klassifizierung in der Praxis

Multi-Klassen-Klassifizierung bedeutet, dass einem Text ein Label zugewiesen wird und das Label mehrere Optionen hat.

Zum Beispiel: Nachrichtenklassifizierung (Technologie, Sport, Unterhaltung, Finanzen), Klassifizierung von Kundenservice-Tickets, Klassifizierung von Produktbewertungen usw.

Beispiel

# ============================================
# Text-Klassifizierung in der Praxis: Vergleich mehrerer Ansätze
# ============================================

from typing import List, Dict, Any

# ============================================
# Ansatz 1: Traditionelles maschinelles Lernen + TF-IDF
# Geeignet für kleine Datensätze, starke Interpretierbarkeit
# ============================================

def traditional_text_classification_demo():
    """Traditionelle Methoden zur Text-Klassifizierung verwenden"""
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.pipeline import Pipeline

    # Trainingsdaten
    texts = [
        "Ich habe heute eine Aktie gekauft, sie ist stark gestiegen",
        "Regelmäßige Fondsinvestitionen sind eine gute Möglichkeit zur Vermögensverwaltung",
        "Der Stürmer dieses Teams hat ein Tor erzielt",
        "Die NBA-Playoffs sind wirklich spannend",
        "Die Kamera dieses Handys macht tolle Fotos",
        "Die Programmiertutorials von EXAMPLE sind sehr verständlich",
        "Die Handlung dieses Films ist sehr berührend",
        "Die Songs auf diesem Album sind sehr schön",
    ]

    labels = ["Finanzen", "Finanzen", "Sport", "Sport", "Technologie", "Technologie", "Unterhaltung", "Unterhaltung"]

    # Pipeline erstellen: TF-IDF + Naive Bayes
    pipeline = Pipeline([
        ("tfidf", TfidfVectorizer()),  # Text in TF-IDF-Merkmale umwandeln
        ("classifier", MultinomialNB()),  # Naive-Bayes-Klassifikator
    ])

    # Trainieren
    pipeline.fit(texts, labels)

    # Test
    test_texts = [
        "Die Aktie ist gefallen, ich bin schlecht gelaunt",
        "Basketballspiel Last-Second-Buzzer-Beater",
        "Der neu veröffentlichte Laptop hat eine starke Leistung",
        "Das neue NLP-Tutorial von EXAMPLE",
    ]

    predictions = pipeline.predict(test_texts)
    probabilities = pipeline.predict_proba(test_texts)

    print("Option 1: Traditionelles maschinelles Lernen + TF-IDF")
    print("-" * 50)
    for text, pred, probs in zip(test_texts, predictions, probabilities):
        print(f"Text: {text}")
        print(f"Vorhersage: {pred}")
        print(f"Klassenwahrscheinlichkeiten: {dict(zip(pipeline.classes_, probs))}")
        print()

# ============================================
# Option 2: Hugging Face Pipeline verwenden
# Geeignet für schnelles Prototyping, kein Training erforderlich
# ============================================

def huggingface_pipeline_demo():
    """Die vortrainierte Pipeline von Hugging Face verwenden"""
    print("Option 2: Hugging Face Pipeline")
    print("-" * 50)

    try:
        from transformers import pipeline

        # Sentiment-Analyse-Pipeline verwenden (Englisch)
        classifier = pipeline("sentiment-analysis")

        test_texts = [
            "Ich liebe die Tutorials von EXAMPLE!",
            "Dieser Film ist schrecklich.",
        ]

        results = classifier(test_texts)

        for text, result in zip(test_texts, results):
            print(f"Text: {text}")
            print(f"Sentiment: {result['label']}")
            print(f"Konfidenz: {result['score']:.4f}")
            print()

    except Exception as e:
        print(f"Zuerst müssen Transformers installiert werden: pip install transformers")
        print(f"Fehler: {e}")

# ============================================
# Option 3: Großes Modell API verwenden (empfohlen)
# Geeignet für Produktionsumgebung, beste Leistung
# ============================================

def llm_based_classification_demo():
    """Großes Modell zur Text-Klassifizierung verwenden (Simulation)"""
    print("Option 3: Klassifizierung mit großem Modell (Demonstrationskonzept)")
    print("-" * 50)

    # Bei tatsächlicher Verwendung APIs wie OpenAI/Anthropic aufrufen
    # Hier das Prompt-Design für die Klassifizierung demonstrieren

    def classify_with_llm(text: str, categories: List[str]) -> Dict[str, Any]:
        """Simulierte Funktion zur Klassifizierung mit einem LLM"""
        # Echtes Szenario: LLM-API aufrufen
        # prompt = f"""Bitte klassifizieren Sie den folgenden Text und geben Sie nur den Kategorienamen zurück.
        # Text: {text}
        # Optionale Kategorien: {', '.join(categories)}"""

        # Hier einen einfachen Schlüsselabgleich zur Simulation durchführen
        category_map = {
            "Finanzen": ["Aktie", "Fonds", "Vermögensverwaltung", "Investition"],
            "Sport": ["Basketball", "Fußball", "Wettkampf", "Tor"],
            "Technologie": ["Handy", "Computer", "Programmierung", "Tutorial"],
            "Unterhaltung": ["Film", "Musik", "Album", "Song"],
        }

        for category, keywords in category_map.items():
            if any(keyword in text for keyword in keywords):
                return {
                    "category": category,
                    "Konfidenz": 0.9,
                    "reasoning": f"Enthält Schlüsselwörter: {', '.join([k for k in keywords if k in text])}"
                }

        return {"category": "Andere", "Konfidenz": 0.5}

    test_texts = [
        "Die Aktie ist gestiegen, ich bin sehr glücklich",
        "Die Fußball-Weltmeisterschaft hat begonnen",
        "Das neue Programmiertutorial von EXAMPLE",
    ]

    categories = ["Finanzen", "Sport", "Technologie", "Unterhaltung", "Andere"]

    for text in test_texts:
        ergebnis = classify_with_llm(text, kategorien)
        print(f"Text: {text}")
        print(f"Kategorie: {result['category']}")
        print(f"Konfidenz: {result['confidence']:.4f}")
        if "reasoning" in result:
            print(f"Grund: {result['reasoning']}")
        print()

# ============================================
# Demo ausführen
# ============================================

if __name__ == "__main__":
    traditional_text_classification_demo()
    print("=" * 50)
    llm_based_classification_demo()

Few-Shot-Klassifizierungsmethoden

Few-Shot Learning ist eine neue Fähigkeit großer Modelle – man gibt einfach ein paar Beispiele vor, und das Modell kann lernen zu klassifizieren.

Zum Beispiel:

Text: Dieses Produkt funktioniert gut → Label: Positiv

Text: Die Qualität ist zu schlecht → Label: Negativ

Text: [dein neuer Text] → Label: ?

Nachdem das große Modell diese beiden Beispiele gesehen hat, kann es die Aufgabe verstehen und sinnvolle Klassifizierungen für neuen Text vornehmen.

Auswahl: Großes Modell vs. kleines Modell

Wann welchen Ansatz verwenden? Hier ist ein Entscheidungsleitfaden:

SzenarioEmpfohlene VorgehensweiseBegründung
Kleine Datenmenge (< 1000 Einträge)Few-Shot-Learning mit großem ModellKleine Modelle können es nicht lernen; große Modelle verallgemeinern gut
Große Datenmenge (> 10000 Einträge)Kleines Modell fine-tunen (z. B. BERT)Geringere Kosten, schnellere Inferenz
Schnelle Validierung erforderlichGroßes Modell APIKein Training erforderlich, sofort nutzbar
Produktionsumgebung, hoher DurchsatzKleines Modell fine-tunen + DistillationSchnelle Inferenz, kontrollierbare Kosten
Kategorien ändern sich häufigPrompt-Engineering für großes ModellKein Neutraining erforderlich, einfach den Prompt ändern

Erkennung benannter Entitäten (NER)

Named Entity Recognition (NER) ist die Aufgabe, spezifische Entitäten aus Text zu identifizieren, wie Personennamen, Ortsnamen und Organisationsnamen.

Sequenz-Labeling-Framework

NER wird meistens als „Sequenz-Labeling“-Aufgabe modelliert: jedem Wort wird ein Label zugewiesen, das angibt, ob es Teil einer Entität ist.

Zum Beispiel der Satz: "Zhang SanIn Google 工do", die Labels könnten sein:

Zhang San → B-PER (Beginn Personenname)

in → O (keine Entität)

Google → B-ORG (Beginn Organisation)

Arbeit → O

BIO-Annotationsformat

Das gängigste Annotationsformat ist BIO:

B-XXX: Beginn der Entität

I-XXX: Innerhalb der Entität

O: Keine Entität

Zum Beispiel: „Wang Xiaoming arbeitet bei Microsoft in Peking“, gelabelt als:

Wang → B-PER

Xiao → I-PER

Ming → I-PER

bei → O

Bei → B-LOC

Jing → I-LOC

's → O

Wei → B-ORG

Ruan → I-ORG

Gong → I-ORG

Si → I-ORG

an → O

Klasse → O

Praktische Anwendungen

Verwenden wir Hugging Face für NER:

Beispiel

# ============================================
# Praktisches NER: Hugging Face für Named Entity Recognition nutzen
# ============================================

def ner_with_huggingface():
    """Ein vortrainiertes NER-Modell verwenden"""
    print(NER in Aktion: Hugging Face)
    print("-" * 50)

    try:
        from transformers import pipeline

        # NER-Pipeline laden
        # Englisches NER
        ner_pipeline = pipeline(
            "ner",
            model="dbmdz/bert-large-cased-finetuned-conll03-english",
            grouped_entities=True  # Mehrere Token derselben Entität zusammenführen
        )

        test_text = "John Smith works at Google in New York City. He loves EXAMPLE tutorials."

        results = ner_pipeline(test_text)

        print(f"Text: {test_text}")
        print("Erkannte Entitäten:")
        for entity in results:
            print(f" - {entity['word']}: {entity['entity_group']} (Konfidenz: {entity['score']:.4f})")
        print()

    except Exception as e:
        print(f"Zuerst müssen Transformers installiert werden: pip install transformers")
        print(f"Fehler: {e}")

# ============================================
# Chinesisches NER: mit Regex + Schlüsselwörtern (einfaches Szenario)
# ============================================

def simple_chinese_ner():
    """Einfaches chinesisches NER: mit Regex und Wörterbuch"""
    import re

    print("Einfaches chinesisches NER: Regex + Wörterbuch")
    print("-" * 50)

    # Entitäten-Wörterbuch (wäre in realen Szenarien größer)
    person_names = ["Zhang San", "Li Si", "Wang Xiaoming", "Liu Dehua"]
    organizations = ["Google", "Microsoft", "Alibaba", "Tencent", "EXAMPLE"]
    locations = ["Peking", "Shanghai", "Shenzhen", "New York", "London"]

    # Regex-Muster
    patterns = {
        "PERSON": "|".join(re.escape(name) for name in person_names),
        "ORG": "|".join(re.escape(org) for org in organizations),
        "LOC": "|".join(re.escape(loc) for loc in locations),
        "PHONE": r"1[3-9]\d{9}",  # Telefonnummer
        "EMAIL": r"\w+@\w+\.\w+",  # E-Mail
    }

    def extract_entities(text: str):
        """Entitäten aus Text extrahieren"""
        entities = []

        for entity_type, pattern in patterns.items():
            for match in re.finditer(pattern, text):
                entities.append({
                    "text": match.group(),
                    "type": entity_type,
                    "start": match.start(),
                    "end": match.end(),
                })

        # Nach Position sortieren
        entities.sort(key=lambda x: x["start"])
        return entities

    # Test
    test_texts = [
        "Zhang San arbeitet bei Google, Telefonnummer ist 13800138000",
        "Wang Xiaoming reist geschäftlich zu Microsoft nach Peking",
        "Wenn Sie Fragen haben, kontaktieren Sie [email protected]",
        "EXAMPLE ist eine sehr gute Lernwebsite",
    ]

    for text in test_texts:
        entities = extract_entities(text)
        print(f"Text: {text}")
        if entities:
            print("Erkannte Entitäten:")
            for ent in entities:
                print(f"  - {ent['text']}: {ent['type']}")
        else:
            print("Keine Entitäten erkannt")
        print()

# ============================================
# Großes Modell für NER
# ============================================

def ner_with_llm():
    """Demonstration der Idee der Verwendung eines großen Modells für NER"""
    print("Großes Modell für NER (Demonstrationsidee)")
    print("-" * 50)

    def extract_entities_with_llm(text: str):
        """Verwendung von LLM für NER (Simulation)"""
        # Reales Szenario: Prompt konstruieren, um LLM aufzurufen
        # prompt = f"""Bitte extrahieren Sie benannte Entitäten aus dem folgenden Text.
        # Geben Sie nur das JSON-Format zurück, einschließlich der Entitätstypen: PERSON (Personenname), ORG (Organisation), LOC (Ort).
        # Text: {text}"""

        # Hier machen wir eine einfache Simulation
        entities = []

        # Einfacher Schlüsselwortabgleich für die Simulation
        if "Zhang San" in text:
            entities.append({"text": "Zhang San", "type": "PERSON"})
        if "Google" in text:
            entities.append({"text": "Google", "type": "ORG"})
        if "Beijing" in text:
            entities.append({"text": "Beijing", "type": "LOC"})
        if "EXAMPLE" in text:
            entities.append({"text": "EXAMPLE", "type": "ORG"})

        return entities

    test_text = "Zhang San arbeitet bei Google in Peking, und er nutzt oft EXAMPLE zum Lernen."
    entities = extract_entities_with_llm(test_text)

    print(f"Text: {test_text}")
    print("Erkannte Entitäten:")
    for ent in entities:
        print(f"  - {ent['text']}: {ent['type']}")

# ============================================
# Demo ausführen
# ============================================

if __name__ == "__main__":
    simple_chinese_ner()
    print("=" * 50)
    ner_with_llm()

Maschinelle Übersetzung

Maschinelle Übersetzung ist eine der klassischsten Anwendungen von NLP und eine der am frühesten kommerzialisierten Anwendungen.

Prinzipien der neuronalen maschinellen Übersetzung

Die frühe maschinelle Übersetzung war regelbasiert, dann statistisch und heute ist sie vollständig neuronal.

Neuronale maschinelle Übersetzung (NMT) verwendet typischerweise eine Encoder-Decoder-Architektur:

1. Der Encoder liest den Quellsprachen-Satz und generiert eine semantische Repräsentation

2. Der Decoder generiert den Zielsprachen-Satz basierend auf der semantischen Repräsentation

Der Aufmerksamkeitsmechanismus ermöglicht es dem Decoder, sich bei der Generierung jedes Wortes auf verschiedene Positionen des Quellsprachen-Satzes zu „konzentrieren“ – dies ist der Schlüssel zur Verbesserung der Übersetzungsqualität.

Evaluierungsmetrik: BLEU-Score

Wie misst man die Übersetzungsqualität? BLEU (Bilingual Evaluation Understudy) ist die am häufigsten verwendete Metrik.

Die Kernidee von BLEU ist:Je höher die n-Gramm-Überschneidung zwischen dem maschinellen Übersetzungsergebnis und dem von Menschen übersetzten Ergebnis, desto besser ist die Qualität.。

Der BLEU-Score reicht von 0 bis 1, je höher desto besser. Im Allgemeinen:

Unter 0.1: im Wesentlichen unverständlich

0.1-0.3: kann die allgemeine Bedeutung verstehen

0.3-0.5: gute Übersetzungsqualität

Über 0.5: nahe an menschlicher Übersetzung

Beispiel

# ============================================
# Demo zur Berechnung des BLEU-Scores
# ============================================

def bleu_score_demo():
    """Berechnung des BLEU-Scores demonstrieren"""
    print("BLEU-Score-Demo")
    print("-" * 50)

    # Hinweis: In der Praxis wird empfohlen, die Bibliothek sacrebleu zu verwenden
    # Hier machen wir eine konzeptionelle Demonstration

    from collections import Counter
    import math

    def compute_ngrams(tokens, n):
        """n-Gramm berechnen"""
        return [tuple(tokens[i:i+n]) for i in range(len(tokens)-n+1)]

    def simple_bleu(reference: str, candidate: str, max_n: int = 4):
        """Einfache BLEU-Berechnung (konzeptionelle Demonstration)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        # Strafen für Kürze berechnen
        ref_len = len(ref_tokens)
        cand_len = len(cand_tokens)

        if cand_len == 0:
            return 0.0

        if cand_len <= ref_len:
            bp = math.exp(1 - ref_len / cand_len)
        else:
            bp = 1.0

        # Berechne die Präzision für jedes n-gram
        precisions = []
        for n in range(1, max_n + 1):
            ref_ngrams = Counter(compute_ngrams(ref_tokens, n))
            cand_ngrams = Counter(compute_ngrams(cand_tokens, n))

            if not cand_ngrams:
                precisions.append(0.0)
                continue

            # Zähle die Anzahl der Treffer
            matches = 0
            for ngram, count in cand_ngrams.items():
                matches += min(count, ref_ngrams.get(ngram, 0))

            total = sum(cand_ngrams.values())
            precisions.append(matches / total if total > 0 else 0.0)

        # Geometrisches Mittel
        if all(p == 0 for p in precisions):
            geo_mean = 0.0
        else:
            # Vermiede log(0)
            log_sum = sum(math.log(p + 1e-10) for p in precisions) / max_n
            geo_mean = math.exp(log_sum)

        bleu = bp * geo_mean
        return bleu

    # Test
    reference = "the cat sat on the mat"
    candidates = [
        "the cat sat on the mat",  # Exakter Treffer
        "the cat was on the mat",  # Ein Wort anders
        "the cat sat on a mat",    # Anderer Artikel
        "a cat sat on the mat",
        "the dog sat on the mat",  # Ein Wort falsch
        "mat the on sat cat the",  # Wortreihenfolge völlig durcheinander
        "hello world",             # Völlig irrelevant
    ]

    print(fReferenzübersetzung: {reference})
    print()
    for candidate in candidates:
        bleu = simple_bleu(reference, candidate)
        print(fKandidatenübersetzung: {candidate})
        print(fBLEU-Wert: {bleu:.4f})
        print()

    # Chinesisches Beispiel
    print(Chinesisches Übersetzungsbeispiel:)
    print("-" * 30)
    ref = I like to eat apples
    cand1 = I like to eat apples
    cand2 = I love to eat apples
    cand3 = Apples I like to eat

    print(fReferenz: {ref})
    print(fKandidat 1: {cand1}, BLEU: {simple_bleu(ref, cand1):.4f})
    print(fKandidat 2: {cand2}, BLEU: {simple_bleu(ref, cand2):.4f})
    print(fKandidat 3: {cand3}, BLEU: {simple_bleu(ref, cand3):.4f})


if __name__ == "__main__":
    bleu_score_demo()

Übersetzungsqualität im Zeitalter der LLMs

Nach dem Aufkommen großer Modelle hat die Qualität der maschinellen Übersetzung ein neues Niveau erreicht.

Traditionelle Übersetzungsmodelle werden mit bilingualen parallelen Daten trainiert, während große Modelle mit riesigen Textmengen trainiert werden und ein tieferes Verständnis der Sprache besitzen.

Vorteile großer Modelle bei der Übersetzung:

1. Besseres Kontextverständnis: können mit Mehrdeutigkeiten und Polysemie umgehen

2. Stilsteuerung: Ton und Stil der Übersetzung können vorgegeben werden

3. Terminologiekonsistenz: es kann ein Glossar bereitgestellt werden, um die konsistente Übersetzung von Eigennamen sicherzustellen

4. Mehrsprachige Fähigkeit: ein einzelnes Modell kann zwischen Dutzenden oder Hunderten von Sprachen übersetzen


Stimmungsanalyse

Die Stimmungsanalyse bestimmt die emotionale Tendenz eines Textes – positiv, negativ oder neutral.

Feingranulare Stimmungsanalyse

Einfache Stimmungsanalyse ist eine binäre Klassifikation (positiv/negativ); feingranularer kann sein:

1. Stimmungswertung: 1-5 Sterne, 1 ist am negativsten, 5 ist am positivsten

2. Stimmungsklassifikation: Ärger, Freude, Traurigkeit, Überraschung usw.

3. Aspektbasierte Stimmungsanalyse: bewertet nicht nur die Gesamtstimmung, sondern analysiert auch die Stimmung gegenüber spezifischen Aspekten.

Zum Beispiel: "Das Essen in diesem Restaurant ist köstlich, aber der Service ist viel zu langsam."

Gesamtstimmung: neutral bis positiv

Aspektebene-Stimmung:

Essen → Positiv

Service → Negativ

Mehrsprachige Stimmungsmodelle

Aktuelle Stimmungsmodelle können mehrere Sprachen verarbeiten, ohne dass ein separates Training für jede Sprache erforderlich ist.

Gängige mehrsprachige Modelle:

Große Modelle wie XLM-RoBERTa, mBERT (multilinguales BERT), Llama und Claude.


Textzusammenfassung

Textzusammenfassung ist der Prozess des Kürzens von langem Text bei gleichzeitiger Beibehaltung der Kerninformationen.

Extraktive vs. abstrakte Zusammenfassung

Es gibt zwei Hauptansätze für Zusammenfassungen:

Extraktive Zusammenfassung: Wählt die wichtigsten Sätze aus dem Originaltext aus und fügt sie zusammen.

Vorteile: Informationen sind genau und werden nicht frei erfunden

Nachteile: nicht flüssig genug, kann redundante Informationen enthalten

Abstrakte Zusammenfassung: Lässt das Modell den Originaltext „verstehen“ und schreibt dann eine Zusammenfassung in eigenen Worten.

Vorteile: flüssig, prägnant und kann das Original zusammenfassen

Nachteile: kann „halluzinieren“ und Informationen erfinden, die im Originaltext nicht vorhanden sind

Im Zeitalter großer Modelle ist die abstrakte Zusammenfassung zum Mainstream geworden, aber bei ihrer Verwendung muss auf das Problem der Halluzinationen geachtet werden.

ROUGE-Evaluierungsmetrik

Wie wird die Qualität von Zusammenfassungen evaluiert? ROUGE ist die am häufigsten verwendete Metrik.

ROUGE hat mehrere Varianten:

ROUGE-1: Abgleich basierend auf Unigrammen (einzelnen Wörtern)

ROUGE-2: Abgleich basierend auf Bigrammen (zwei Wörtern)

ROUGE-L: basierend auf der längsten gemeinsamen Teilsequenz (Longest Common Subsequence)

Ähnlich wie BLEU misst ROUGE ebenfalls die Überschneidung zwischen maschinell erzeugten Zusammenfassungen und menschlichen Referenzzusammenfassungen.

Beispiel

# ============================================
# Textzusammenfassung: ROUGE-Metriken + einfache extractive Zusammenfassung
# ============================================

def rouge_demo():
    """Demo für ROUGE-Metriken"""
    print("ROUGE metrics demo")
    print("-" * 50)

    from collections import Counter

    def compute_rouge_1(reference: str, candidate: str) -> float:
        """Berechnet ROUGE-1 (Unigramm-F1)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        if not ref_tokens or not cand_tokens:
            return 0.0

        ref_counts = Counter(ref_tokens)
        cand_counts = Counter(cand_tokens)

        # Anzahl der Übereinstimmungen berechnen
        matches = 0
        for token, count in cand_counts.items():
            matches += min(count, ref_counts.get(token, 0))

        precision = matches / len(cand_tokens)
        recall = matches / len(ref_tokens)

        if precision + recall == 0:
            return 0.0

        f1 = 2 * precision * recall / (precision + recall)
        return f1

    def compute_lcs(a: list, b: list) -> int:
        """Berechnet die Länge der längsten gemeinsamen Teilsequenz"""
        m, n = len(a), len(b)
        dp = [[0] * (n + 1) for _ in range(m + 1)]

        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if a[i-1] == b[j-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

        return dp[m][n]

    def compute_rouge_l(reference: str, candidate: str) -> float:
        """Berechnet ROUGE-L (F1 der längsten gemeinsamen Teilsequenz)"""
        ref_tokens = reference.split()
        cand_tokens = candidate.split()

        if not ref_tokens or not cand_tokens:
            return 0.0

        lcs_len = compute_lcs(ref_tokens, cand_tokens)

        precision = lcs_len / len(cand_tokens)
        recall = lcs_len / len(ref_tokens)

        if precision + recall == 0:
            return 0.0

        f1 = 2 * precision * recall / (precision + recall)
        return f1

    # Test
    reference = "Natural language processing is an important branch of artificial intelligence, studying the interaction between computers and human language."
    candidates = [
        "Natural language processing is an important branch of artificial intelligence, studying the interaction between computers and human language.",  # Genaue Übereinstimmung
        "Natural language processing studies the interaction between computers and human language, and is an important branch of artificial intelligence.",  # Reihenfolge geändert
        "Natural language processing is a branch of artificial intelligence, studying the interaction between computers and language.",  # Gekürzte Version
        "EXAMPLE is a programming learning website.",  # Irrelevant
    ]

    print(f"Reference summary: {reference}")
    print()
    for candidate in candidates:
        rouge1 = compute_rouge_1(reference, candidate)
        rougel = compute_rouge_l(reference, candidate)
        print(f"Candidate summary: {candidate}")
        print(f"ROUGE-1: {rouge1:.4f}")
        print(f"ROUGE-L: {rougel:.4f}")
        print()


def simple_extractive_summarization():
    Einfache extractive Zusammenfassung: Auswählen wichtiger Sätze basierend auf der Wortfrequenz
    print(Simple extractive summarization demo)
    print("-" * 50)

    import re
    from collections import Counter

    def split_sentences(text: str) -> list:
        Einfache chinesische Satzsegmentierung
        # Sätze anhand von Punkten, Ausrufezeichen und Fragezeichen aufteilen
        sentences = re.split(r'[。!?]', text)
        # Leere Strings herausfiltern
        sentences = [s.strip() for s in sentences if s.strip()]
        return sentences

    def get_word_frequency(text: str) -> Counter:
        """Wortfrequenz berechnen (einfaches Demo: simuliert mit zeichenbasierten n-Grammen)"""
        # In realen Szenarien sollte ein Wortsegmentierungstool wie jieba verwendet werden
        # Hier verwenden wir einfach Unigramme
        chars = [c for c in text if c.strip()]
        return Counter(chars)

    def sentence_score(sentence: str, word_freq: Counter) -> float:
        """Satzwerte berechnen"""
        if not sentence:
            return 0.0

        # Einfacher Score: Summe der Wortfrequenzen im Satz / Satzlänge
        total_score = 0.0
        for char in sentence:
            total_score += word_freq.get(char, 0)

        # Normalisierung
        return total_score / (len(sentence) + 1)

    def extractive_summary(text: str, num_sentences: int = 2) -> str:
        """Extraktive Zusammenfassung"""
        sentences = split_sentences(text)

        if len(sentences) <= num_sentences:
            return text

        # Wortfrequenz berechnen
        word_freq = get_word_frequency(text)

        # Den Score für jeden Satz berechnen
        scores = [(i, sentence_score(s, word_freq)) for i, s in enumerate(sentences)]

        # Nach Score sortieren und die besten auswählen
        scores.sort(key=lambda x: x[1], reverse=True)
        top_indices = [i for i, score in scores[:num_sentences]]
        top_indices.sort()  # Ursprüngliche Reihenfolge beibehalten

        # Ergebnisse zusammenfügen
        summary = "。".join(sentences[i] for i in top_indices) + "。"
        return summary

    # Testtext
    test_text = """
Natural language processing is an important branch of artificial intelligence. It studies how to make computers understand and generate human language.
This technology has many applications, including machine translation, sentiment analysis, question-answering systems, and more.
In recent years, the emergence of large language models has brought major breakthroughs in natural language processing.
The EXAMPLE website provides many excellent programming tutorials to help developers learn new skills.
"""


    print("Originaltext:")
    print(test_text.strip())
    print()

    summary = extractive_summary(test_text, num_sentences=2)
    print("Extraktive Zusammenfassung:")
    print(summary)


if __name__ == "__main__":
    rouge_demo()
    print("=" * 50)
    simple_extractive_summarization()
Weitere Erweiterungen