스펙트럴 공동 클러스터링을 이용한 문서 이분 클러스터링

Beginner

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

소개

이 실습에서는 20 개 뉴스그룹 데이터셋에서 스펙트럴 공동 군집화 알고리즘을 사용하여 문서를 이분류 (bicluster) 합니다. 이 데이터셋은 20 개의 문서 카테고리를 가지고 있으며, "comp.os.ms-windows.misc" 카테고리는 데이터가 없는 게시물을 포함하고 있기 때문에 제외합니다. TF-IDF 벡터화된 게시물은 단어 빈도 행렬을 구성하며, 이 행렬은 Dhillon 의 스펙트럴 공동 군집화 알고리즘을 사용하여 이분류됩니다. 결과적인 문서 - 단어 이분류는 해당 문서 하위 집합에서 더 자주 사용되는 단어의 하위 집합을 나타냅니다. 비교를 위해 MiniBatchKMeans 를 사용하여 문서를 클러스터링할 것입니다.

VM 팁

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

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

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

라이브러리 가져오기

이 실습에 필요한 라이브러리를 가져옵니다.

from collections import defaultdict
import operator
from time import time
import numpy as np
from sklearn.cluster import SpectralCoclustering
from sklearn.cluster import MiniBatchKMeans
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.cluster import v_measure_score

숫자 정규화 함수 정의

모든 숫자 토큰을 플레이스홀더로 매핑하는 함수 number_normalizer()를 정의합니다. 이는 차원 축소에 사용됩니다.

def number_normalizer(tokens):
    """모든 숫자 토큰을 플레이스홀더로 매핑합니다.

    많은 응용 프로그램에서 숫자로 시작하는 토큰은 직접적으로 유용하지 않지만, 그러한 토큰이 존재한다는 사실은 관련성이 있을 수 있습니다. 이러한 형태의 차원 축소를 적용하면 일부 방법이 더 나은 성능을 보일 수 있습니다.
    """
    return ("#NUMBER" if token[0].isdigit() else token for token in tokens)

NumberNormalizingVectorizer 정의

이전에 정의한 number_normalizer() 함수를 사용하는 토크나이저를 구축하기 위해 TfidfVectorizer()를 상속하는 NumberNormalizingVectorizer() 클래스를 정의합니다.

class NumberNormalizingVectorizer(TfidfVectorizer):
    def build_tokenizer(self):
        tokenize = super().build_tokenizer()
        return lambda doc: list(number_normalizer(tokenize(doc)))

데이터 로드 및 준비

20 개 뉴스그룹 데이터셋을 로드하고 "comp.os.ms-windows.misc" 카테고리를 제외합니다. 또한 벡터라이저를 정의합니다.

categories = [
    "alt.atheism",
    "comp.graphics",
    "comp.sys.ibm.pc.hardware",
    "comp.sys.mac.hardware",
    "comp.windows.x",
    "misc.forsale",
    "rec.autos",
    "rec.motorcycles",
    "rec.sport.baseball",
    "rec.sport.hockey",
    "sci.crypt",
    "sci.electronics",
    "sci.med",
    "sci.space",
    "soc.religion.christian",
    "talk.politics.guns",
    "talk.politics.mideast",
    "talk.politics.misc",
    "talk.religion.misc",
]
newsgroups = fetch_20newsgroups(categories=categories)
y_true = newsgroups.target

vectorizer = NumberNormalizingVectorizer(stop_words="english", min_df=5)

데이터 벡터화

이전에 정의한 벡터라이저를 사용하여 데이터를 벡터화합니다.

X = vectorizer.fit_transform(newsgroups.data)

스펙트럴 공동 클러스터링 알고리즘을 이용한 이분 클러스터링

스펙트럴 공동 클러스터링 알고리즘을 사용하여 이분 클러스터링을 수행합니다. 공동 클러스터 객체를 정의하고 데이터에 맞춥니다.

cocluster = SpectralCoclustering(
    n_clusters=len(categories), svd_method="arpack", random_state=0
)
cocluster.fit(X)
y_cocluster = cocluster.row_labels_

MiniBatchKMeans 를 이용한 클러스터링

MiniBatchKMeans 를 사용하여 데이터를 클러스터링합니다.

kmeans = MiniBatchKMeans(
    n_clusters=len(categories), batch_size=20000, random_state=0, n_init=3
)
y_kmeans = kmeans.fit_predict(X)

최적의 이분 클러스터 찾기

정규화된 절단 값을 계산하여 최적의 이분 클러스터를 찾고, 상위 5 개를 선택합니다.

feature_names = vectorizer.get_feature_names_out()
document_names = list(newsgroups.target_names[i] for i in newsgroups.target)

def bicluster_ncut(i):
    rows, cols = cocluster.get_indices(i)
    if not (np.any(rows) and np.any(cols)):
        import sys
        return sys.float_info.max
    row_complement = np.nonzero(np.logical_not(cocluster.rows_[i]))[0]
    col_complement = np.nonzero(np.logical_not(cocluster.columns_[i]))[0]
    weight = X[rows][:, cols].sum()
    cut = X[row_complement][:, cols].sum() + X[rows][:, col_complement].sum()
    return cut / weight

bicluster_ncuts = list(bicluster_ncut(i) for i in range(len(newsgroups.target_names)))
best_idx = np.argsort(bicluster_ncuts)[:5]

결과 출력

8 단계에서 찾은 최적의 이분 클러스터 결과를 출력합니다.

for idx, cluster in enumerate(best_idx):
    n_rows, n_cols = cocluster.get_shape(cluster)
    cluster_docs, cluster_words = cocluster.get_indices(cluster)
    if not len(cluster_docs) or not len(cluster_words):
        continue

    ## 카테고리
    counter = defaultdict(int)
    for i in cluster_docs:
        counter[document_names[i]] += 1
    cat_string = ", ".join(
        "{:.0f}% {}".format(float(c) / n_rows * 100, name)
        for name, c in most_common(counter)[:3]
    )

    ## 단어
    out_of_cluster_docs = cocluster.row_labels_ != cluster
    out_of_cluster_docs = np.where(out_of_cluster_docs)[0]
    word_col = X[:, cluster_words]
    word_scores = np.array(
        word_col[cluster_docs, :].sum(axis=0)
        - word_col[out_of_cluster_docs, :].sum(axis=0)
    )
    word_scores = word_scores.ravel()
    important_words = list(
        feature_names[cluster_words[i]] for i in word_scores.argsort()[:-11:-1]
    )

    print("이분 클러스터 {} : {} 문서, {} 단어".format(idx, n_rows, n_cols))
    print("카테고리   : {}".format(cat_string))
    print("단어        : {}\n".format(", ".join(important_words)))

요약

이 실습에서는 20 개 뉴스그룹 데이터셋에 스펙트럴 공동 클러스터링 알고리즘을 사용하여 이분 클러스터링을 수행하는 방법을 배웠습니다. 또한, 비교를 위해 MiniBatchKMeans 를 사용하여 데이터를 클러스터링하는 방법도 학습했습니다. 마지막으로, 정규화된 절단 값을 계산하여 최적의 이분 클러스터를 찾고 상위 5 개를 선택했습니다.