FeatureHasher 와 DictVectorizer 비교

Beginner

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

소개

이 실습에서는 숫자가 아닌 입력 데이터 (예: 사전이나 텍스트 문서) 를 실수 벡터로 표현하는 텍스트 벡터화 과정을 살펴봅니다. 사용자 정의 파이썬 함수를 사용하여 사전처리 (토큰화) 된 텍스트 문서를 벡터화하는 두 가지 방법인 FeatureHasherDictVectorizer를 비교해 보겠습니다.

VM 팁

VM 시작이 완료되면 왼쪽 상단 모서리를 클릭하여 Notebook 탭으로 전환하여 연습용 Jupyter Notebook에 접근합니다.

때때로 Jupyter Notebook 이 완전히 로드되기까지 몇 초 정도 기다려야 할 수 있습니다. Jupyter Notebook 의 제한으로 인해 작업의 유효성 검사는 자동화될 수 없습니다.

학습 중 문제가 발생하면 Labby 에 문의하십시오. 세션 후 피드백을 제공하면 문제를 신속하게 해결해 드리겠습니다.

데이터 로드

20newsgroups_dataset에서 데이터를 로드합니다. 이 데이터는 20 개 주제에 대한 약 18,000 개의 뉴스그룹 게시물로 구성되며, 두 개의 하위 집합 (하나는 학습용, 하나는 테스트용) 으로 나뉩니다. 간결성과 계산 비용 절감을 위해 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("20 뉴스그룹 학습 데이터 로드 중")
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)} 문서 - {data_size_mb:.3f}MB")

전처리 함수 정의

토큰은 단어, 단어 일부 또는 문자열에서 공백이나 기호 사이에 포함된 모든 것을 의미할 수 있습니다. 여기서는 유니코드 단어 문자를 일치시키는 간단한 정규 표현식 (regex) 을 사용하여 토큰을 추출하는 함수를 정의합니다. 이에는 모든 언어에서 단어의 일부가 될 수 있는 대부분의 문자와 숫자, 밑줄이 포함됩니다.

import re

def tokenize(doc):
    """doc 에서 토큰을 추출합니다.

    문자열을 토큰으로 분할하기 위해 단어 문자를 일치시키는 간단한 정규 표현식을 사용합니다.
    더 체계적인 접근 방식은 CountVectorizer 또는 TfidfVectorizer 를 참조하십시오.
    """
    return (tok.lower() for tok in re.findall(r"\w+", doc))

주어진 문서에서 각 토큰의 발생 빈도를 계산하는 추가 함수를 정의합니다. 벡터화기에 사용할 빈도 사전을 반환합니다.

from collections import defaultdict

def token_freqs(doc):
    """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

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\non freq dicts",
        "FeatureHasher\non freq dicts",
        "FeatureHasher\non raw tokens",
        "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)")

요약

이 실험에서는 두 가지 방법인 FeatureHasherDictVectorizer 그리고 네 가지 특수 목적 텍스트 벡터화기인 CountVectorizer, HashingVectorizer, TfidfVectorizer를 비교하여 텍스트 벡터화를 탐색했습니다. 벡터화 방법들을 벤치마킹하고 결과를 플롯했습니다. 결론적으로 HashingVectorizer는 해시 충돌로 인한 변환의 역변환 불가능성에도 불구하고 CountVectorizer보다 성능이 더 좋았습니다. 또한, DictVectorizerFeatureHasher는 내부 토큰화 단계가 문서 전체에 대해 정규 표현식을 한 번 컴파일하고 재사용하기 때문에 수동으로 토큰화된 문서에서 동등한 텍스트 벡터화기보다 성능이 더 좋았습니다.