Clasificación de documentos de texto

Machine LearningMachine LearningBeginner
Practicar Ahora

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

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Esta práctica muestra cómo utilizar scikit-learn para clasificar documentos de texto en diferentes categorías. Utilizaremos el conjunto de datos 20 newsgroups, que contiene alrededor de 18.000 publicaciones de grupos de noticias sobre 20 temas. Utilizaremos un enfoque de bolsa de palabras y una matriz dispersa de términos-documento con pesos Tf-idf para codificar las características. La práctica también demostrará varios clasificadores que pueden manejar eficientemente matrices dispersas.

Consejos sobre la VM

Una vez finalizada la inicialización de la VM, haga clic en la esquina superior izquierda para cambiar a la pestaña Notebook y acceder a Jupyter Notebook para practicar.

A veces, es posible que tenga que esperar unos segundos a que Jupyter Notebook termine de cargar. La validación de las operaciones no se puede automatizar debido a las limitaciones de Jupyter Notebook.

Si tiene problemas durante el aprendizaje, no dude en preguntar a Labby. Deje sus comentarios después de la sesión y lo resolveremos rápidamente para usted.

Carga y vectorización del conjunto de datos de texto 20 Newsgroups

Definimos una función para cargar datos del conjunto de datos 20newsgroups_dataset, que comprende alrededor de 18.000 publicaciones de grupos de noticias sobre 20 temas divididas en dos subconjuntos: uno para entrenamiento y el otro para prueba. Cargaremos y vectorizaremos el conjunto de datos sin eliminar los metadatos.

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=()):
    """Carga y vectoriza el conjunto de datos 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,
    )

    ## El orden de las etiquetas en `target_names` puede ser diferente de `categories`
    target_names = data_train.target_names

    ## Divide la variable objetivo en un conjunto de entrenamiento y un conjunto de prueba
    y_train, y_test = data_train.target, data_test.target

    ## Extracción de características a partir de los datos de entrenamiento utilizando un vectorizador disperso
    vectorizer = TfidfVectorizer(
        sublinear_tf=True, max_df=0.5, min_df=5, stop_words="english"
    )
    X_train = vectorizer.fit_transform(data_train.data)

    ## Extracción de características a partir de los datos de prueba utilizando el mismo vectorizador
    X_test = vectorizer.transform(data_test.data)

    feature_names = vectorizer.get_feature_names_out()

    if verbose:
        print(f"{len(data_train.data)} documentos")
        print(f"{len(data_test.data)} documentos")
        print(f"{len(target_names)} categorías")
        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)

Análisis de un clasificador de documentos de bolsa de palabras

Ahora entrenaremos un clasificador dos veces, una vez en las muestras de texto que incluyen metadatos y una vez después de eliminar los metadatos. Analizaremos los errores de clasificación en un conjunto de prueba utilizando una matriz de confusión y examinaremos los coeficientes que definen la función de clasificación de los modelos entrenados.

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"Matriz de confusión para {clf.__class__.__name__}\n en los documentos originales"
)

def plot_feature_effects():
    ## coeficientes aprendidos ponderados por la frecuencia de aparición
    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]

    ## graficar los efectos de las características
    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 palabras clave por clase:")
    print(top)

    return ax

_ = plot_feature_effects().set_title("Efecto promedio de la característica en los datos originales")

Modelo con eliminación de metadatos

Ahora usaremos la opción remove del cargador de conjuntos de datos 20 newsgroups en scikit-learn para entrenar un clasificador de texto que no dependa demasiado de los metadatos para tomar sus decisiones. También analizaremos los errores de clasificación en un conjunto de prueba utilizando una matriz de confusión y examinaremos los coeficientes que definen la función de clasificación de los modelos entrenados.

(
    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"Matriz de confusión para {clf.__class__.__name__}\n en documentos filtrados"
)

_ = plot_feature_effects().set_title("Efectos promedio de las características en documentos filtrados")

Evaluación de clasificadores

Ahora entrenaremos y probaremos los conjuntos de datos con ocho diferentes modelos de clasificación y obtendremos los resultados de rendimiento para cada modelo. El objetivo de este estudio es resaltar los trade-offs entre cálculo y precisión de diferentes tipos de clasificadores para un problema de clasificación de texto multi-clase.

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), "Regresión Logística"),
    (RidgeClassifier(alpha=1.0, solver="sparse_cg"), "Clasificador Ridge"),
    (KNeighborsClassifier(n_neighbors=100), "kNN"),
    (RandomForestClassifier(), "Bosque Aleatorio"),
    ## L2 penalty Linear SVC
    (LinearSVC(C=0.1, dual=False, max_iter=1000), "SVC Lineal"),
    ## L2 penalty Linear SGD
    (
        SGDClassifier(
            loss="log_loss", alpha=1e-4, n_iter_no_change=3, early_stopping=True
        ),
        "SGD con log-loss",
    ),
    ## NearestCentroid (aka Rocchio classifier)
    (NearestCentroid(), "Centroide más cercano"),
    ## Sparse naive Bayes classifier
    (ComplementNB(alpha=0.1), "Naive Bayes Complementario"),
):
    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="Trade-off entre puntuación y tiempo de entrenamiento",
    yscale="log",
    xlabel="precisión de prueba",
    ylabel="tiempo de entrenamiento (s)",
)
fig, ax2 = plt.subplots(figsize=(10, 8))
ax2.scatter(score, test_time, s=60)
ax2.set(
    title="Trade-off entre puntuación y tiempo de prueba",
    yscale="log",
    xlabel="precisión de prueba",
    ylabel="tiempo de prueba (s)",
)

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

Resumen

Esta práctica mostró cómo utilizar scikit-learn para clasificar documentos de texto en diferentes categorías. Cargamos el conjunto de datos 20 newsgroups y utilizamos un enfoque de bolsa de palabras y una matriz dispersa de términos-documento ponderada por Tf-idf para codificar las características. Entrenamos un clasificador dos veces, una vez en las muestras de texto que incluyen metadatos y una vez después de eliminar los metadatos. Analizamos los errores de clasificación en un conjunto de prueba utilizando una matriz de confusión y examinamos los coeficientes que definen la función de clasificación de los modelos entrenados. También entrenamos y probamos los conjuntos de datos con ocho diferentes modelos de clasificación y obtuvimos los resultados de rendimiento para cada modelo. El objetivo de este estudio fue resaltar los trade-offs entre cálculo y precisión de diferentes tipos de clasificadores para un problema de clasificación de texto multi-clase.