第11章 K-means(文档聚类分析)

加载数据集

已标记好类别的四个文档数据集:(网络安全,电子学,医学medical,太空)
在这里插入图片描述

import matplotlib.pyplot as plt
import numpy as np

from time import time
from sklearn.datasets import load_files

t = time()
docs = load_files(r'C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data')
print("summary: {0} documents in {1} categories.".format(len(docs.data), len(docs.target_names)))
print("done in {0} seconds".format(time() - t))

summary: 3949 documents in 4 categories.
done in 5.72968864440918 seconds

TF-IDF向量化

这里需要注意 TfidfVectorizer 的几个参数。

max_df=0.4 表示如果一个单词在40%的文档里都出现过,则认为这是一个高频词,对文档聚类没有帮助,在生成词典时就会剔除这个词。

min_df=2 表示,如果一个单词的词频太低,只在两个以下(包含两个)的文档里出现,则也把这个单词从词典里剔除。

max_features 可以进一步过滤词典的大小,它会根据 TF-IDF 权重从高到低进行排序,然后取前面权重高的单词构成词典。

from sklearn.feature_extraction.text import TfidfVectorizer
max_features = 20000

t = time()
vectorizer = TfidfVectorizer(max_df=0.4, 
                             min_df=2, 
                             max_features=max_features, 
                             encoding='latin-1')
X = vectorizer.fit_transform((d for d in docs.data))
print("n_samples: %d, n_features: %d" % X.shape)
print("number of non-zero features in sample [{0}]: {1}".format(
    docs.filenames[0], X[0].getnnz()))
print("done in {0} seconds".format(time() - t))

n_samples: 3949, n_features: 20000

number of non-zero features in sample [C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.electronics\11902-54322]: 56

done in 2.2819015979766846 seconds

从输出可知,我们的一篇文章构成的向量是一个稀疏向量,其大部分元素都为0这也容易理解,我们的词典大小为20000个,而示例文章中不重复的单词却只有56个。

K-means 聚类

sklearn.cluster.KMeans

Parameters:

n_clusters:我们选择的聚类个数为4个。

max_iter=100表示最多进行100次k均值选代。

tol=0.1表示中心点移动距离小于0.1时就认为算法已经收敛,停止迭代。

verbose = 1表示输出送代的过程信息。

n_init=3表示进行3次k均值运算后求平均值,前面介绍过,在算法刚开始送代时,会随机选择聚类中心点,不同的中心点可能导致不同的收敛效果,因此多次运算求平均值的方法可以提供算法的稳定性。

Attributes:

inertia_ : float,Sum of squared distances of samples to their closest cluster center.

cluster_centers_ : array, [n_clusters, n_features],Coordinates of cluster centers.

labels_ : Labels of each point

from sklearn.cluster import KMeans

t = time()
n_clusters = 4
kmean = KMeans(n_clusters=n_clusters, 
               max_iter=100,
               tol=0.01,
               verbose=1,
               n_init=3)
kmean.fit(X);
print("kmean: k={}, cost={}".format(n_clusters, int(kmean.inertia_)))
print("done in {0} seconds".format(time() - t))

Initialization complete
Iteration 0, inertia 7446.126
Iteration 1, inertia 3842.619

Iteration 11, inertia 3822.036
Iteration 12, inertia 3822.010
Converged at iteration 12: center shift 0.000000e+00 within tolerance 4.896692e-07
Initialization complete
Iteration 0, inertia 7589.733
Iteration 1, inertia 3842.690

Iteration 49, inertia 3814.997
Iteration 50, inertia 3814.995
Converged at iteration 50: center shift 0.000000e+00 within tolerance 4.896692e-07
Initialization complete
Iteration 0, inertia 7565.903
Iteration 1, inertia 3852.316

Iteration 26, inertia 3818.030
Iteration 27, inertia 3818.028
Converged at iteration 27: center shift 0.000000e+00 within tolerance 4.896692e-07
kmean: k=4, cost=3814
done in 50.47910118103027 seconds

从输出信息中可以看到,总共进行了3次k均值聚类分析,分别做了12,50,27次迭代后收敛。这样就把3949个文档进行自动分类了。

kmean.labels_ 里保存的就是这些文档的类别信息。

len(kmean.labels_)

3949

如我们所预料,len(kmean.labels_) 的值是3949,还可以通过 kmean.labels_[1000:1010] 查看1000~1010这10个文档的分类情况。

kmean.labels_[1000:1010]

array([0, 0, 0, 1, 2, 1, 2, 0, 1, 1])

与真实所在类别进行对比:发现第9个分错了。实际在sci.space被分在了sci.electronics。

docs.filenames[1000:1010]

array([‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.crypt\10888-15289’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.crypt\11490-15880’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.crypt\11270-15346’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.electronics\12383-53525’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.space\13826-60862’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.electronics\11631-54106’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.space\14235-61437’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.crypt\11508-15928’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.space\13593-60824’,
‘C:\Users\Qiuyi\Desktop\scikit-learn code\code\datasets\clustering\data\sci.electronics\12304-52801’],
dtype=’<U98’)

单词权重分析

argsort():
把一个 numpy 数组进行升序排列,返回的是排序后的索引。[::-1]运算是把升序变为降序。

由于 kmean.cluster_centers_ 是二维数组,因此 kmean.cluster_centers_.argsort()[:, ::-1]语句的含义就是把聚类中心点的不同分量,按照从大到小的顺序进行排序,并且把排序后的元素索引保存在二维数组 order_centroids 里。

vectorizer.get_feature_names() 将得到我们的词典单词,根据索引即可得到每个类别里权重最高的那些单词了。

from __future__ import print_function

order_centroids = kmean.cluster_centers_.argsort()[:, ::-1]

terms = vectorizer.get_feature_names()
for i in range(n_clusters):
    print("Cluster %d:" % i, end='')
    for ind in order_centroids[i, :10]:
        print(' %s' % terms[ind], end='')
    print()

Cluster 0: key clipper chip encryption government will keys escrow we nsa
Cluster 1: my any me by your know some do has so
Cluster 2: space henry nasa toronto pat shuttle zoo moon we orbit
Cluster 3: geb pitt banks gordon shameful dsl n3jxp chastity cadre surrender

Cluster 1的效果不好,因为这几个单词太没特点了,可以是任何类别。
Cluster 0的效果比较高,一看就知道是关于网络安全的,对应的是sci.crypt。
Cluster 2是sci.space,Cluster 3是sci.med。

聚类算法性能评估

分类问题,我们可以直接计算被错误分类的样本数量,这样可以直接算出分类算法的准确率。

聚类问题,由于没有标记,所以不能使用绝对数量的方法进行性能评估。

更典型地,针对k-均值算法,我们可以选择k的数值不等于己标记的类别个数。

“熵”,是信息论中最重要的基础概念。熵表示一个系统的有序程度,而聚类问题的性能评估,就是对比经过聚类算法处理后的数据的有序程度,与人工标记的类别的有序程度之间的差异。

1. Adjust Rand Index(调整兰德指数)

Adjust Rand Index是一种衡量两个序列相似性的算法。

优点:
对任意数量的聚类中心和样本数,随机聚类的ARI都非常接近于0;
取值在[-1,1]之间,负数代表结果不好,越接近于1越好;
可用于聚类算法之间的比较。

缺点:
ARI需要真实标签

2. Homogeneity,Completeness,V-measure

同质性homogeneity:每个群集只包含单个类的成员;
完整性completeness:给定类的所有成员都分配给同一个群集;
V-measure是同质性homogeneity和完整性completeness的调和平均数。

优点:
分数明确:从0到1反应出最差到最优的表现;
解释直观:差的调和平均数可以在同质性和完整性方面做定性的分析;
对簇结构不作假设:可以比较两种聚类算法如k均值算法和谱聚类算法的结果。

缺点:
以前引入的度量在随机标记方面没有规范化,这意味着,根据样本数,集群和先验知识,完全随机标签并不总是产生相同的完整性和均匀性的值,所得调和平均值V-measure也不相同。特别是,随机标记不会产生零分,特别是当簇的数量很大时。
当样本数大于一千,聚类数小于10时,可以安全地忽略该问题。对于较小的样本量或更大数量的集群,使用经过调整的指数(如调整兰德指数)更为安全。

3. Silhouette Coefficient

轮廓系数适用于实际类别信息未知的情况。对于单个样本,设a是与它同类别中其他样本的平均距离,b是与它距离最近不同类别中样本的平均距离,其轮廓系数为:
s = b a m a x ( a , b ) s=\frac{b-a}{max(a,b)} 对于一个样本集合,它的轮廓系数是所有样本轮廓系数的平均值。轮廓系数的取值范围是[-1,1],同类别样本距离越相近不同类别样本距离越远,分数越高。

from sklearn import metrics

labels = docs.target
print("Adjusted Rand-Index: %.3f"
      % metrics.adjusted_rand_score(labels, kmean.labels_))
print("Homogeneity: %0.3f" % metrics.homogeneity_score(labels, kmean.labels_))
print("Completeness: %0.3f" % metrics.completeness_score(labels, kmean.labels_))
print("V-measure: %0.3f" % metrics.v_measure_score(labels, kmean.labels_))
print("Silhouette Coefficient: %0.3f"
      % metrics.silhouette_score(X, kmean.labels_, sample_size=1000))

Adjusted Rand-Index: 0.237
Homogeneity: 0.375
Completeness: 0.554
V-measure: 0.447
Silhouette Coefficient: 0.004

参考:

argsort()

a = np.array([[20, 10, 30, 40], [100, 300, 200, 400], [1, 5, 3, 2]])
a.argsort()[:, ::-1]

#运行结果:
array([[3, 2, 0, 1],
       [3, 1, 2, 0],
       [1, 2, 3, 0]], dtype=int64)

聚类性能评估(Clustering Evaluation and Assessment)——沙沙的兔子

聚类模型评估——howhigh

猜你喜欢

转载自blog.csdn.net/weixin_34275246/article/details/85308850