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.