Textklassifizierung

Machine LearningMachine LearningBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Lab wird gezeigt, wie man scikit-learn verwendet, um Textdokumente in verschiedene Kategorien zu klassifizieren. Wir werden den 20 Newsgroups-Datensatz verwenden, der ungefähr 18.000 Newsgroup-Beiträge zu 20 Themen enthält. Wir werden einen Bag-of-Words-Ansatz und eine Tf-idf-Gewichtung der Dokument-Term-Sparse-Matrix verwenden, um die Merkmale zu kodieren. Das Lab wird auch verschiedene Klassifizierer demonstrieren, die sparse Matrizen effizient verarbeiten können.

VM-Tipps

Nachdem die VM gestartet ist, klicken Sie in der oberen linken Ecke, um zur Registerkarte Notebook zu wechseln und Jupyter Notebook für die Übung zu nutzen.

Manchmal müssen Sie einige Sekunden warten, bis Jupyter Notebook vollständig geladen ist. Die Validierung von Vorgängen kann aufgrund der Einschränkungen in Jupyter Notebook nicht automatisiert werden.

Wenn Sie bei der Lernphase Probleme haben, können Sie Labby gerne fragen. Geben Sie nach der Sitzung Feedback, und wir werden das Problem für Sie prompt beheben.

Laden und Vektorisieren des 20 Newsgroups Text-Datensatzes

Wir definieren eine Funktion, um Daten aus dem 20newsgroups-Datensatz zu laden, der ungefähr 18.000 Newsgroup-Beiträge zu 20 Themen enthält, die in zwei Teilmengen unterteilt sind: eine für das Training und die andere für das Testen. Wir werden den Datensatz laden und vektorisieren, ohne die Metadaten zu entfernen.

from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer

categories = [
    "alt.atheism",
    "talk.religion.misc",
    "comp.graphics",
    "sci.space",
]

def load_dataset(verbose=False, remove=()):
    """Laden und Vektorisieren des 20 Newsgroups-Datensatzes."""
    data_train = fetch_20newsgroups(
        subset="train",
        categories=categories,
        shuffle=True,
        random_state=42,
        remove=remove,
    )

    data_test = fetch_20newsgroups(
        subset="test",
        categories=categories,
        shuffle=True,
        random_state=42,
        remove=remove,
    )

    ## Die Reihenfolge der Labels in `target_names` kann von `categories` unterschiedlich sein
    target_names = data_train.target_names

    ## Teilen Sie das Ziel in einen Trainingssatz und einen Testsatz auf
    y_train, y_test = data_train.target, data_test.target

    ## Extrahieren von Merkmalen aus den Trainingsdaten mit einem sparsen Vektorizer
    vectorizer = TfidfVectorizer(
        sublinear_tf=True, max_df=0.5, min_df=5, stop_words="english"
    )
    X_train = vectorizer.fit_transform(data_train.data)

    ## Extrahieren von Merkmalen aus den Testdaten mit dem gleichen Vektorizer
    X_test = vectorizer.transform(data_test.data)

    feature_names = vectorizer.get_feature_names_out()

    if verbose:
        print(f"{len(data_train.data)} Dokumente")
        print(f"{len(data_test.data)} Dokumente")
        print(f"{len(target_names)} Kategorien")
        print(f"n_samples: {X_train.shape[0]}, n_features: {X_train.shape[1]}")
        print(f"n_samples: {X_test.shape[0]}, n_features: {X_test.shape[1]}")

    return X_train, X_test, y_train, y_test, feature_names, target_names

X_train, X_test, y_train, y_test, feature_names, target_names = load_dataset(verbose=True)

Analyse eines Bag-of-Words-Dokumentklassifizierers

Wir werden nun einen Klassifizierer zweimal trainieren, einmal auf den Textdaten einschließlich der Metadaten und einmal nach Entfernung der Metadaten. Wir werden die Klassifizierungsfehler auf einem Testset mithilfe einer Konfusionsmatrix analysieren und die Koeffizienten untersuchen, die die Klassifizierungsfunktion der trainierten Modelle definieren.

from sklearn.linear_model import RidgeClassifier
from sklearn.metrics import ConfusionMatrixDisplay
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

clf = RidgeClassifier(tol=1e-2, solver="sparse_cg")
clf.fit(X_train, y_train)
pred = clf.predict(X_test)

fig, ax = plt.subplots(figsize=(10, 5))
ConfusionMatrixDisplay.from_predictions(y_test, pred, ax=ax)
ax.xaxis.set_ticklabels(target_names)
ax.yaxis.set_ticklabels(target_names)
_ = ax.set_title(
    f"Konfusionsmatrix für {clf.__class__.__name__}\nauf den ursprünglichen Dokumenten"
)

def plot_feature_effects():
    ## gelernt Koeffizienten, gewichtet mit Häufigkeit des Auftretens
    average_feature_effects = clf.coef_ * np.asarray(X_train.mean(axis=0)).ravel()

    for i, label in enumerate(target_names):
        top5 = np.argsort(average_feature_effects[i])[-5:][::-1]
        if i == 0:
            top = pd.DataFrame(feature_names[top5], columns=[label])
            top_indices = top5
        else:
            top[label] = feature_names[top5]
            top_indices = np.concatenate((top_indices, top5), axis=None)
    top_indices = np.unique(top_indices)
    predictive_words = feature_names[top_indices]

    ## Plotten der Feature-Effekte
    bar_size = 0.25
    padding = 0.75
    y_locs = np.arange(len(top_indices)) * (4 * bar_size + padding)

    fig, ax = plt.subplots(figsize=(10, 8))
    for i, label in enumerate(target_names):
        ax.barh(
            y_locs + (i - 2) * bar_size,
            average_feature_effects[i, top_indices],
            height=bar_size,
            label=label,
        )
    ax.set(
        yticks=y_locs,
        yticklabels=predictive_words,
        ylim=[
            0 - 4 * bar_size,
            len(top_indices) * (4 * bar_size + padding) - 4 * bar_size,
        ],
    )
    ax.legend(loc="lower right")

    print("top 5 Schlüsselwörter pro Klasse:")
    print(top)

    return ax

_ = plot_feature_effects().set_title("Durchschnittlicher Feature-Effekt auf die ursprünglichen Daten")

Modell mit Entfernung der Metadaten

Wir werden nun die remove-Option des 20 Newsgroups-Datensatz-Ladeprozedurs in scikit-learn verwenden, um einen Textklassifizierer zu trainieren, der bei seinen Entscheidungen nicht zu stark auf Metadaten angewiesen ist. Wir werden auch die Klassifizierungsfehler auf einem Testset mithilfe einer Konfusionsmatrix analysieren und die Koeffizienten untersuchen, die die Klassifizierungsfunktion der trainierten Modelle definieren.

(
    X_train,
    X_test,
    y_train,
    y_test,
    feature_names,
    target_names,
) = load_dataset(remove=("headers", "footers", "quotes"))

clf = RidgeClassifier(tol=1e-2, solver="sparse_cg")
clf.fit(X_train, y_train)
pred = clf.predict(X_test)

fig, ax = plt.subplots(figsize=(10, 5))
ConfusionMatrixDisplay.from_predictions(y_test, pred, ax=ax)
ax.xaxis.set_ticklabels(target_names)
ax.yaxis.set_ticklabels(target_names)
_ = ax.set_title(
    f"Konfusionsmatrix für {clf.__class__.__name__}\nauf gefilterten Dokumenten"
)

_ = plot_feature_effects().set_title("Durchschnittliche Feature-Effekte auf gefilterte Dokumente")

Benchmarking von Klassifizierern

Wir werden nun die Datensätze mit acht verschiedenen Klassifikationsmodellen trainieren und testen und die Leistungsergebnisse für jedes Modell erhalten. Ziel dieser Studie ist es, die Rechenleistung/Genauigkeit-Abstimmung verschiedener Typen von Klassifizierern für ein solches mehrklassenes Textklassifizierungsproblem zu verdeutlichen.

from sklearn.utils.extmath import density
from sklearn import metrics
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.linear_model import SGDClassifier
from sklearn.naive_bayes import ComplementNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neighbors import NearestCentroid
from sklearn.ensemble import RandomForestClassifier

results = []
for clf, name in (
    (LogisticRegression(C=5, max_iter=1000), "Logistische Regression"),
    (RidgeClassifier(alpha=1.0, solver="sparse_cg"), "Ridge-Klassifizierer"),
    (KNeighborsClassifier(n_neighbors=100), "kNN"),
    (RandomForestClassifier(), "Random Forest"),
    ## L2 Strafmaß Linear SVC
    (LinearSVC(C=0.1, dual=False, max_iter=1000), "Linear SVC"),
    ## L2 Strafmaß Linear SGD
    (
        SGDClassifier(
            loss="log_loss", alpha=1e-4, n_iter_no_change=3, early_stopping=True
        ),
        "log-loss SGD",
    ),
    ## NearestCentroid (aka Rocchio-Klassifizierer)
    (NearestCentroid(), "NearestCentroid"),
    ## Sparse naive Bayes-Klassifizierer
    (ComplementNB(alpha=0.1), "Complement naive Bayes"),
):
    print("=" * 80)
    print(name)
    results.append(benchmark(clf, name))

indices = np.arange(len(results))

results = [[x[i] for x in results] for i in range(4)]

clf_names, score, training_time, test_time = results
training_time = np.array(training_time)
test_time = np.array(test_time)

fig, ax1 = plt.subplots(figsize=(10, 8))
ax1.scatter(score, training_time, s=60)
ax1.set(
    title="Score-Trainingszeit-Abstimmung",
    yscale="log",
    xlabel="Testgenauigkeit",
    ylabel="Trainingszeit (s)",
)
fig, ax2 = plt.subplots(figsize=(10, 8))
ax2.scatter(score, test_time, s=60)
ax2.set(
    title="Score-Testzeit-Abstimmung",
    yscale="log",
    xlabel="Testgenauigkeit",
    ylabel="Testzeit (s)",
)

for i, txt in enumerate(clf_names):
    ax1.annotate(txt, (score[i], training_time[i]))
    ax2.annotate(txt, (score[i], test_time[i]))

Zusammenfassung

In diesem Lab wurde gezeigt, wie man scikit-learn verwendet, um Textdokumente in verschiedene Kategorien zu klassifizieren. Wir haben den 20 Newsgroups-Datensatz geladen und einen Bag-of-Words-Ansatz sowie eine Tf-idf-Gewichtung der Dokument-Term-Sparse-Matrix verwendet, um die Merkmale zu kodieren. Wir haben einen Klassifizierer zweimal trainiert, einmal auf den Textdaten einschließlich der Metadaten und einmal nach Entfernung der Metadaten. Wir haben die Klassifizierungsfehler auf einem Testset mithilfe einer Konfusionsmatrix analysiert und die Koeffizienten untersucht, die die Klassifizierungsfunktion der trainierten Modelle definieren. Wir haben auch die Datensätze mit acht verschiedenen Klassifikationsmodellen trainiert und getestet und die Leistungsergebnisse für jedes Modell erhalten. Ziel dieser Studie war es, die Rechenleistung/Genauigkeit-Abstimmung verschiedener Typen von Klassifizierern für ein solches mehrklassenes Textklassifizierungsproblem zu verdeutlichen.