Introduction
Dans ce laboratoire, nous allons explorer la vectorisation de texte, qui est le processus de représenter des données d'entrée non numériques (telles que des dictionnaires ou des documents texte) sous forme de vecteurs de nombres réels. Nous allons comparer deux méthodes, FeatureHasher et DictVectorizer, en utilisant les deux méthodes pour vectoriser des documents texte qui sont prétraités (tokenisés) à l'aide d'une fonction Python personnalisée.
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 Carnet de notes pour accéder au carnet Jupyter pour pratiquer.
Parfois, vous devrez peut-être attendre quelques secondes pour que le carnet Jupyter ait fini de charger. La validation des opérations ne peut pas être automatisée en raison des limitations du carnet Jupyter.
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ésoudrons rapidement le problème pour vous.
Charger les données
Nous allons charger les données à partir de 20newsgroups_dataset, qui comprend environ 18000 messages de newsgroups sur 20 sujets divisés en deux sous-ensembles : l'un pour l'entraînement et l'autre pour le test. Pour simplifier et réduire le coût de calcul, nous sélectionnons un sous-ensemble de 7 sujets et n'utilisons que l'ensemble d'entraînement.
from sklearn.datasets import fetch_20newsgroups
categories = [
"alt.atheism",
"comp.graphics",
"comp.sys.ibm.pc.hardware",
"misc.forsale",
"rec.autos",
"sci.space",
"talk.religion.misc",
]
print("Chargement des données d'entraînement des 20 newsgroups")
raw_data, _ = fetch_20newsgroups(subset="train", categories=categories, return_X_y=True)
data_size_mb = sum(len(s.encode("utf-8")) for s in raw_data) / 1e6
print(f"{len(raw_data)} documents - {data_size_mb:.3f}MB")
Définir les fonctions de prétraitement
Un jeton peut être un mot, une partie de mot ou tout ce qui se trouve entre espaces ou symboles dans une chaîne. Ici, nous définissons une fonction qui extrait les jetons à l'aide d'une expression régulière simple (regex) qui correspond aux caractères de mot Unicode. Cela inclut la plupart des caractères qui peuvent faire partie d'un mot dans n'importe quelle langue, ainsi que les nombres et le tiret bas :
import re
def tokenize(doc):
"""Extraire les jetons à partir de doc.
Cela utilise une regex simple qui correspond aux caractères de mot pour diviser les chaînes
en jetons. Pour une approche plus rigoureuse, voir CountVectorizer ou
TfidfVectorizer.
"""
return (tok.lower() for tok in re.findall(r"\w+", doc))
Nous définissons une fonction supplémentaire qui compte l'occurrence (fréquence) de chaque jeton dans un document donné. Elle renvoie un dictionnaire de fréquences à utiliser par les vectoriseurs.
from collections import defaultdict
def token_freqs(doc):
"""Extraire un dictionnaire qui associe les jetons de doc à leur nombre d'occurrences."""
freq = defaultdict(int)
for tok in tokenize(doc):
freq[tok] += 1
return freq
DictVectorizer
Nous allons évaluer DictVectorizer, qui est une méthode qui reçoit des dictionnaires en entrée.
from sklearn.feature_extraction import DictVectorizer
from time import time
t0 = time()
vectorizer = DictVectorizer()
vectorizer.fit_transform(token_freqs(d) for d in raw_data)
duration = time() - t0
print(f"terminé en {duration:.3f} s")
print(f"Trouvé {len(vectorizer.get_feature_names_out())} termes uniques")
FeatureHasher
Nous allons évaluer FeatureHasher, qui est une méthode qui construit un vecteur de longueur prédéfinie en appliquant une fonction de hachage aux caractéristiques (par exemple, les jetons), puis en utilisant directement les valeurs de hachage comme indices de caractéristiques et en mettant à jour le vecteur résultant à ces indices.
from sklearn.feature_extraction import FeatureHasher
import numpy as np
t0 = time()
hasher = FeatureHasher(n_features=2**18)
X = hasher.transform(token_freqs(d) for d in raw_data)
duration = time() - t0
print(f"terminé en {duration:.3f} s")
print(f"Trouvé {len(np.unique(X.nonzero()[1]))} jetons uniques")
Comparaison avec des vectoriseurs de texte à usage spécifique
Nous allons comparer les méthodes précédentes avec CountVectorizer et HashingVectorizer.
from sklearn.feature_extraction.text import CountVectorizer, HashingVectorizer, TfidfVectorizer
t0 = time()
vectorizer = CountVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"terminé en {duration:.3f} s")
print(f"Trouvé {len(vectorizer.get_feature_names_out())} termes uniques")
t0 = time()
vectorizer = HashingVectorizer(n_features=2**18)
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"terminé en {duration:.3f} s")
t0 = time()
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"terminé en {duration:.3f} s")
print(f"Trouvé {len(vectorizer.get_feature_names_out())} termes uniques")
Tracer les résultats
Nous allons tracer la vitesse des méthodes ci-dessus pour la vectorisation.
import matplotlib.pyplot as plt
dict_count_vectorizers = {
"vectorizer": [
"DictVectorizer\nsur des dictionnaires non fréquences",
"FeatureHasher\nsur des dictionnaires non fréquences",
"FeatureHasher\nsur des jetons bruts",
"CountVectorizer",
"HashingVectorizer",
"TfidfVectorizer"
],
"speed": [
2.4, 4.4, 7.2, 5.1, 11.7, 2.9
]
}
fig, ax = plt.subplots(figsize=(12, 6))
y_pos = np.arange(len(dict_count_vectorizers["vectorizer"]))
ax.barh(y_pos, dict_count_vectorizers["speed"], align="center")
ax.set_yticks(y_pos)
ax.set_yticklabels(dict_count_vectorizers["vectorizer"])
ax.invert_yaxis()
_ = ax.set_xlabel("vitesse (MB/s)")
Sommaire
Dans ce laboratoire, nous avons exploré la vectorisation de texte en comparant deux méthodes, FeatureHasher et DictVectorizer, et quatre vectoriseurs de texte à usage spécifique, CountVectorizer, HashingVectorizer et TfidfVectorizer. Nous avons évalué les méthodes pour la vectorisation et tracé les résultats. Nous avons conclu que HashingVectorizer est plus performant que CountVectorizer au détriment de l'inversibilité de la transformation en raison des collisions de hachage. De plus, DictVectorizer et FeatureHasher sont plus performants que leurs vectoriseurs de texte équivalents sur des documents tokenisés manuellement, car l'étape de tokenisation interne des premiers vectoriseurs compile une expression régulière une fois et la réutilise ensuite pour tous les documents.