特征哈希器与字典向量化器对比

Beginner

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

简介

在本实验中,我们将探索文本向量化,即将非数值输入数据(如字典或文本文档)表示为实数向量的过程。我们将通过使用两种方法对借助自定义 Python 函数进行预处理(分词)的文本文档进行向量化,来比较 FeatureHasherDictVectorizer 这两种方法。

虚拟机使用提示

虚拟机启动完成后,点击左上角切换到 笔记本 标签页,以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会立即为你解决问题。

加载数据

我们将从 20newsgroups_dataset 加载数据,该数据集包含约 18000 篇关于 20 个主题的新闻组帖子,分为两个子集:一个用于训练,一个用于测试。为了简单起见并降低计算成本,我们选择 7 个主题的子集并仅使用训练集。

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("Loading 20 newsgroups training data")
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")

定义预处理函数

一个词元可以是一个单词、单词的一部分,或者是字符串中两个空格或符号之间的任何内容。在这里,我们定义一个函数,该函数使用一个简单的正则表达式(regex)来提取词元,该正则表达式匹配 Unicode 单词字符。这包括任何语言中可能作为单词一部分的大多数字符,以及数字和下划线:

import re

def tokenize(doc):
    """从文档中提取词元。

    这使用一个简单的正则表达式来匹配单词字符,从而将字符串拆分为词元。
    若要采用更有原则的方法,请参阅 CountVectorizer 或 TfidfVectorizer。
    """
    return (tok.lower() for tok in re.findall(r"\w+", doc))

我们定义另一个函数,该函数统计给定文档中每个词元的(出现频率)。它返回一个频率字典,供向量化器使用。

from collections import defaultdict

def token_freqs(doc):
    """提取一个字典,将文档中的词元映射到它们的出现次数。"""

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

DictVectorizer

我们将对 DictVectorizer 进行基准测试,它是一种接收字典作为输入的方法。

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"done in {duration:.3f} s")
print(f"Found {len(vectorizer.get_feature_names_out())} unique terms")

特征哈希器

我们将对“特征哈希器(FeatureHasher)”进行基准测试,它是一种通过对特征(例如词元)应用哈希函数来构建预定义长度向量的方法,然后直接将哈希值用作特征索引,并在这些索引处更新结果向量。

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"done in {duration:.3f} s")
print(f"Found {len(np.unique(X.nonzero()[1]))} unique tokens")

与专用文本向量化器的比较

我们将把之前的方法与 CountVectorizerHashingVectorizer 进行比较。

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

t0 = time()
vectorizer = CountVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"done in {duration:.3f} s")
print(f"Found {len(vectorizer.get_feature_names_out())} unique terms")

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

t0 = time()
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(raw_data)
duration = time() - t0
print(f"done in {duration:.3f} s")
print(f"Found {len(vectorizer.get_feature_names_out())} unique terms")

绘制结果

我们将绘制上述向量化方法的速度。

import matplotlib.pyplot as plt

dict_count_vectorizers = {
    "vectorizer": [
        "DictVectorizer\n非频率字典",
        "特征哈希器\n非频率字典",
        "特征哈希器\n非原始词元",
        "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("速度 (MB/s)")

总结

在本实验中,我们通过比较两种方法(“特征哈希器(FeatureHasher)”和“字典向量化器(DictVectorizer)”)以及四种专用文本向量化器(“计数向量化器(CountVectorizer)”、“哈希向量化器(HashingVectorizer)”和“词频 - 逆文档频率向量化器(TfidfVectorizer)”)来探索文本向量化。我们对这些向量化方法进行了基准测试并绘制了结果。我们得出结论,“哈希向量化器(HashingVectorizer)”的性能优于“计数向量化器(CountVectorizer)”,但其代价是由于哈希冲突导致转换的不可逆性。此外,在手动分词的文档上,“字典向量化器(DictVectorizer)”和“特征哈希器(FeatureHasher)”比它们对应的文本向量化器表现更好,因为前一种向量化器的内部分词步骤会编译一次正则表达式,然后在所有文档中重复使用它。