スペクトラル共クラスタリングアルゴリズムを用いた文書の二部クラスタリング

Beginner

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

はじめに

この実験では、20 つのニュースグループのデータセットに対してスペクトラル共クラスタリングアルゴリズムを使用して、文書を二部クラスタリングします。このデータセットには 20 種類の文書があり、データが含まれていない投稿が含まれているため、「comp.os.ms-windows.misc」カテゴリを除外します。TF-IDF ベクトル化された投稿は単語頻度行列を形成し、その後、Dhillon のスペクトラル共クラスタリングアルゴリズムを使用して二部クラスタリングされます。得られた文書 - 単語の二部クラスタは、それらの文書のサブセットでより頻繁に使用される単語のサブセットを示します。比較のために、MiniBatchKMeans を使用して文書をクラスタリングすることも行います。

VM のヒント

VM の起動が完了したら、左上隅をクリックしてノートブックタブに切り替え、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):
    """Map all numeric tokens to a placeholder.

    For many applications, tokens that begin with a number are not directly
    useful, but the fact that such a token exists can be relevant.  By applying
    this form of dimensionality reduction, some methods may perform better.
    """
    return ("#NUMBER" if token[0].isdigit() else token for token in tokens)

数値正規化ベクトル化器の定義

先ほど定義した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("bicluster {} : {} documents, {} words".format(idx, n_rows, n_cols))
    print("categories   : {}".format(cat_string))
    print("words        : {}\n".format(", ".join(important_words)))

まとめ

この実験では、20 つのニュースグループデータセットに対して、スペクトラル共クラスタリングアルゴリズムを使って二部クラスタリングを行う方法を学びました。また、比較のために MiniBatchKMeans を使ってデータをクラスタリングする方法も学びました。最後に、正規化カットを計算し、上位 5 つを選択することで、最適な二部クラスタを見つけました。