简介
在本实验中,我们将对 20 个新闻组数据集使用谱共聚类(Spectral Co-clustering)算法对文档进行双聚类。该数据集包含 20 类文档,我们将排除“comp.os.ms-windows.misc”类别,因为其中包含无数据的帖子。经 TF-IDF 矢量化后的帖子形成一个词频矩阵,然后使用迪隆(Dhillon)的谱共聚类算法对其进行双聚类。得到的文档 - 词双聚类表示在那些文档子集中更常使用的词的子集。我们还将使用 MiniBatchKMeans 对文档进行聚类以作比较。
虚拟机使用提示
虚拟机启动完成后,点击左上角切换到“笔记本”(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(),它继承自 TfidfVectorizer(),以构建一个使用我们之前定义的 number_normalizer() 函数的分词器。
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)
寻找最佳双聚类
我们将通过计算双聚类的归一化割并选择排名前五的来找到最佳双聚类。
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]
)
## 单词
不在聚类中的文档 = cocluster.row_labels_!= cluster
不在聚类中的文档 = np.where(不在聚类中的文档)[0]
单词列 = X[:, cluster_words]
单词得分 = np.array(
单词列[cluster_docs, :].sum(axis=0)
- 单词列[不在聚类中的文档, :].sum(axis=0)
)
单词得分 = 单词得分.ravel()
重要单词 = 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(重要单词)))
总结
在本实验中,我们学习了如何在二十个新闻组数据集上使用谱共聚类算法进行双聚类。我们还学习了如何使用 MiniBatchKMeans 对数据进行聚类以作比较。最后,我们通过计算归一化割并选择排名前五的来找到最佳双聚类。