Comparação entre FeatureHasher e DictVectorizer

Beginner

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

Introdução

Neste laboratório, exploraremos a vetorização de texto, que é o processo de representar dados de entrada não numéricos (como dicionários ou documentos de texto) como vetores de números reais. Compararemos dois métodos, FeatureHasher e DictVectorizer, utilizando ambos para vetorizar documentos de texto pré-processados (tokenizados) com a ajuda de uma função Python personalizada.

Dicas da Máquina Virtual

Após o arranque da máquina virtual, clique no canto superior esquerdo para mudar para a aba Notebook para aceder ao Jupyter Notebook para a prática.

Às vezes, pode ser necessário esperar alguns segundos para o Jupyter Notebook terminar de carregar. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.

Se tiver problemas durante o aprendizado, não hesite em contactar o Labby. Forneça feedback após a sessão e resolveremos prontamente o problema para si.

Carregar Dados

Vamos carregar dados do conjunto de dados 20newsgroups, que compreende cerca de 18.000 mensagens de grupos de notícias sobre 20 tópicos divididos em dois subconjuntos: um para treino e outro para teste. Por simplicidade e para reduzir o custo computacional, selecionamos um subconjunto de 7 tópicos e usamos apenas o conjunto de treino.

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("Carregando dados de treino do 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)} documentos - {data_size_mb:.3f}MB")

Definir funções de pré-processamento

Um token pode ser uma palavra, parte de uma palavra ou qualquer coisa compreendida entre espaços ou símbolos numa string. Aqui, definimos uma função que extrai os tokens usando uma expressão regular (regex) simples que corresponde a caracteres de palavra Unicode. Isto inclui a maioria dos caracteres que podem fazer parte de uma palavra em qualquer língua, bem como números e o sublinhado:

import re

def tokenize(doc):
    """Extrair tokens de doc.

    Isto utiliza uma regex simples que corresponde a caracteres de palavra para dividir as strings em tokens. Para uma abordagem mais fundamentada, consulte CountVectorizer ou TfidfVectorizer.
    """
    return (tok.lower() for tok in re.findall(r"\w+", doc))

Definimos uma função adicional que conta a frequência de ocorrência de cada token num documento dado. Retorna um dicionário de frequências a ser usado pelos vectorizadores.

from collections import defaultdict

def token_freqs(doc):
    """Extrair um dicionário que mapeia tokens de doc às suas ocorrências."""

    freq = defaultdict(int)
    for tok in tokenize(doc):
        freq[tok] += 1
    return freq

DictVectorizer

Vamos avaliar o DictVectorizer, que é um método que recebe dicionários como entrada.

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"feito em {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} termos únicos")

FeatureHasher

Vamos avaliar o FeatureHasher, que é um método que constrói um vetor de comprimento pré-definido aplicando uma função hash aos recursos (por exemplo, tokens), depois usando os valores hash diretamente como índices de recursos e atualizando o vetor resultante nesses índices.

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"feito em {duration:.3f} s")
print(f"Encontrados {len(np.unique(X.nonzero()[1]))} tokens únicos")

Comparação com vetorizadores de texto de propósito específico

Vamos comparar os métodos anteriores com o CountVectorizer e o HashingVectorizer.

from sklearn.feature_extraction.text import CountVectorizer, HashingVectorizer, TfidfVectorizer

t0 = time()
vectorizer = CountVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"feito em {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} termos únicos")

t0 = time()
vectorizer = HashingVectorizer(n_features=2**18)
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"feito em {duration:.3f} s")

t0 = time()
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"feito em {duration:.3f} s")
print(f"Encontrados {len(vectorizer.get_feature_names_out())} termos únicos")

Plotar os resultados

Vamos plotar a velocidade dos métodos acima para vetorização.

import matplotlib.pyplot as plt
import numpy as np

dict_count_vectorizers = {
    "vectorizer": [
        "DictVectorizer\nem dicionários de frequência",
        "FeatureHasher\nem dicionários de frequência",
        "FeatureHasher\nem tokens brutos",
        "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("velocidade (MB/s)")

Resumo

Neste laboratório, exploramos a vetorização de texto comparando dois métodos, FeatureHasher e DictVectorizer, e quatro vetorizadores de texto de propósito específico, CountVectorizer, HashingVectorizer e TfidfVectorizer. Realizamos benchmarks dos métodos para vetorização e plotamos os resultados. Concluímos que o HashingVectorizer apresenta melhor desempenho que o CountVectorizer, embora à custa da inversão da transformação devido a colisões de hash. Além disso, o DictVectorizer e o FeatureHasher têm melhor desempenho do que seus equivalentes em vetorizadores de texto em documentos tokenizados manualmente, pois a etapa de tokenização interna dos primeiros compila uma expressão regular uma vez e, em seguida, a reutiliza para todos os documentos.