Classification de documents texte

Machine LearningMachine LearningBeginner
Pratiquer maintenant

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

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Ce laboratoire montre comment utiliser scikit-learn pour classifier des documents texte en différentes catégories. Nous utiliserons l'ensemble de données 20 newsgroups, qui contient environ 18 000 messages de newsgroup sur 20 sujets. Nous utiliserons une approche du sac de mots et une matrice creuse de termes de document pondérée par Tf-idf pour encoder les caractéristiques. Le laboratoire montrera également divers classifieurs qui peuvent gérer efficacement les matrices creuses.

Conseils sur la machine virtuelle

Une fois le démarrage de la machine virtuelle terminé, cliquez dans le coin supérieur gauche pour basculer vers l'onglet Notebook pour accéder à Jupyter Notebook pour la pratique.

Parfois, vous devrez peut-être attendre quelques secondes pour que Jupyter Notebook ait fini de charger. La validation des opérations ne peut pas être automatisée en raison des limitations de Jupyter Notebook.

Si vous rencontrez des problèmes pendant l'apprentissage, n'hésitez pas à demander à Labby. Donnez des commentaires après la session, et nous réglerons rapidement le problème pour vous.

Chargement et vectorisation de l'ensemble de données texte 20 Newsgroups

Nous définissons une fonction pour charger les données à partir de l'ensemble de données 20newsgroups_dataset, qui comprend environ 18 000 messages de newsgroup sur 20 sujets divisés en deux sous-ensembles : l'un pour l'entraînement et l'autre pour le test. Nous allons charger et vectoriser l'ensemble de données sans élimination des métadonnées.

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=()):
    """Charge et vectorise l'ensemble de données 20 newsgroups."""
    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,
    )

    ## L'ordre des étiquettes dans `target_names` peut être différent de `categories`
    target_names = data_train.target_names

    ## Diviser la cible en un ensemble d'entraînement et un ensemble de test
    y_train, y_test = data_train.target, data_test.target

    ## Extraction des caractéristiques à partir des données d'entraînement en utilisant un vectoriseur creux
    vectorizer = TfidfVectorizer(
        sublinear_tf=True, max_df=0.5, min_df=5, stop_words="english"
    )
    X_train = vectorizer.fit_transform(data_train.data)

    ## Extraction des caractéristiques à partir des données de test en utilisant le même vectoriseur
    X_test = vectorizer.transform(data_test.data)

    feature_names = vectorizer.get_feature_names_out()

    if verbose:
        print(f"{len(data_train.data)} documents")
        print(f"{len(data_test.data)} documents")
        print(f"{len(target_names)} catégories")
        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 d'un classifieur de documents du type sac de mots

Nous allons maintenant entraîner un classifieur deux fois, une fois sur les échantillons de texte y compris les métadonnées et une fois après avoir éliminé les métadonnées. Nous allons analyser les erreurs de classification sur un ensemble de test en utilisant une matrice de confusion et examiner les coefficients qui définissent la fonction de classification des modèles entraînés.

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"Matrice de confusion pour {clf.__class__.__name__}\n sur les documents originaux"
)

def plot_feature_effects():
    ## coefficients apprises pondérées par la fréquence d'apparition
    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]

    ## tracer les effets des caractéristiques
    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 mots clés par classe:")
    print(top)

    return ax

_ = plot_feature_effects().set_title("Effet moyen des caractéristiques sur les données originales")

Modèle avec élimination des métadonnées

Nous allons maintenant utiliser l'option remove du chargeur d'ensemble de données 20 newsgroups dans scikit-learn pour entraîner un classifieur de texte qui ne dépend pas trop des métadonnées pour prendre ses décisions. Nous allons également analyser les erreurs de classification sur un ensemble de test en utilisant une matrice de confusion et examiner les coefficients qui définissent la fonction de classification des modèles entraînés.

(
    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"Matrice de confusion pour {clf.__class__.__name__}\n sur les documents filtrés"
)

_ = plot_feature_effects().set_title("Effets moyens des caractéristiques sur les documents filtrés")

Évaluation comparative de classifieurs

Nous allons maintenant entraîner et tester les ensembles de données avec huit modèles de classification différents et obtenir les résultats de performance de chaque modèle. L'objectif de cette étude est de mettre en évidence les compromis entre calcul et précision pour différents types de classifieurs dans le cadre d'un problème de classification de texte multi-classe.

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), "Régression logistique"),
    (RidgeClassifier(alpha=1.0, solver="sparse_cg"), "Classifieur Ridge"),
    (KNeighborsClassifier(n_neighbors=100), "kNN"),
    (RandomForestClassifier(), "Forêt aléatoire"),
    ## L2 penalty Linear SVC
    (LinearSVC(C=0.1, dual=False, max_iter=1000), "SVC linéaire"),
    ## L2 penalty Linear SGD
    (
        SGDClassifier(
            loss="log_loss", alpha=1e-4, n_iter_no_change=3, early_stopping=True
        ),
        "SGD avec log-loss",
    ),
    ## NearestCentroid (aka Rocchio classifier)
    (NearestCentroid(), "Centroïde le plus proche"),
    ## Sparse naive Bayes classifier
    (ComplementNB(alpha=0.1), "Naive Bayes complémentaire"),
):
    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="Compromis entre score et temps d'entraînement",
    yscale="log",
    xlabel="précision sur le test",
    ylabel="temps d'entraînement (s)",
)
fig, ax2 = plt.subplots(figsize=(10, 8))
ax2.scatter(score, test_time, s=60)
ax2.set(
    title="Compromis entre score et temps de test",
    yscale="log",
    xlabel="précision sur le test",
    ylabel="temps de test (s)",
)

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

Sommaire

Ce laboratoire a démontré comment utiliser scikit-learn pour classifier des documents texte en différentes catégories. Nous avons chargé l'ensemble de données 20 newsgroups et utilisé une approche du sac de mots et une matrice creuse document-terme pondérée par Tf-idf pour encoder les caractéristiques. Nous avons entraîné un classifieur deux fois, une fois sur les échantillons de texte y compris les métadonnées et une fois après avoir éliminé les métadonnées. Nous avons analysé les erreurs de classification sur un ensemble de test en utilisant une matrice de confusion et examiné les coefficients qui définissent la fonction de classification des modèles entraînés. Nous avons également entraîné et testé les ensembles de données avec huit modèles de classification différents et obtenu les résultats de performance de chaque modèle. L'objectif de cette étude était de mettre en évidence les compromis entre calcul et précision pour différents types de classifieurs dans le cadre d'un problème de classification de texte multi-classe.