Scikit-learn和聚类

一些scikit learn 的知识点

作者: David Sheehan 数据科学家

对于数据科学家来说生成一个片段(或者叫簇)算是家常便饭. 那从哪里开始生成聚类?当然是根据定义啊. 聚类被划分到监督学习里, 给予某种共同的特征将没有标签的数据划分到对应的组里. Scikit-Learn 就是一个很好的工具, 因此我们将尽可能不谈代码. 取而代之, 我们用GIF图片, 这个教程将描述非常通用的技术.

技术

聚类算法大致可以分为两类, 这取决于类的数目是否具体精确确定. 正如我们将会发现的那样, 有时区别会有点不清楚, 因为一些算法会使用类数目作为参数. 但是在我们做任何事情之前, 我们必须在我们python模块中加载必要的模块. 我们也需要建立数据集来说明和比较不谈技术. 我们希望每个方法的意义都变明显.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import silhouette_score
from sklearn import cluster, datasets, mixture
from sklearn.neighbors import kneighbors_graph
np.random.seed(844)
clust1 = np.random.normal(5, 2, (1000,2)) ## 生成二维数据, (5,5)为中心,以下类推
clust2 = np.random.normal(15, 3, (1000,2))
clust3 = np.random.multivariate_normal([17,3], [[1,0],[0,1]], 1000)
clust4 = np.random.multivariate_normal([2,16], [[1,0],[0,1]], 1000)
dataset1 = np.concatenate((clust1, clust2, clust3, clust4))
# we take the first array as the second array has the cluster labels
dataset2 = datasets.make_circles(n_samples=1000, factor=.5, noise=.05)[0]
# plot clustering output on the two datasets
def cluster_plots(set1, set2, colours1 = 'gray', colours2 = 'gray',
title1 = 'Dataset 1', title2 = 'Dataset 2'):
fig,(ax1,ax2) = plt.subplots(1, 2)
fig.set_size_inches(6, 3)
ax1.set_title(title1,fontsize=14)
ax1.set_xlim(min(set1[:,0]), max(set1[:,0]))
ax1.set_ylim(min(set1[:,1]), max(set1[:,1]))
ax1.scatter(set1[:, 0], set1[:, 1],s=8,lw=0,c= colours1)
ax2.set_title(title2,fontsize=14)
ax2.set_xlim(min(set2[:,0]), max(set2[:,0]))
ax2.set_ylim(min(set2[:,1]), max(set2[:,1]))
ax2.scatter(set2[:, 0], set2[:, 1],s=8,lw=0,c=colours2)
fig.tight_layout()
plt.show()
cluster_plots(dataset1, dataset2)

png

K-means 聚类

基于绝对没有经验依据, K-means大概是机器学习中最流行的聚类算法了. 算法本身是简单的: 从预先指定团簇中心的数目开始, 然后每个点呗初始分配到最近的中心. 下一步, 对于每个簇, 将中心更新为每个簇的中质心. 然后再将点重新分配到最近的中心. 重复这个过程, 直到中心的位置不在变化. 下面的GIF简要说明了算法.

K-means算法的变化包括 k-medoids 和 k-medians, 其中质心会分别更新到当前簇的medoid和中位数. 注意, 在k-modoids, 簇的中心必须和数据集的成员对应. K-means系列算法对于团册的起始位置是敏感的, 因为每个方法收敛到局部最优, 随着特征纬度的增加局部最优点也会更多. 对K-means里的问题在下面的GIF进行了说明.

在scikit中K-means聚类对传统方法提供了一些扩展. 为了防止算法返回次优聚类, 这个K-means包含了n_innit和method参数. 前者只是使用n个不同的初始化重新允许该算法并且返回最佳的输出(基于簇内的距离平方和). 将后者设置为 kmeans++(默认), 初始中心呗巧妙地选择(比随机的好). 这具有降低运行时间(减少达到收敛的步骤)的额外好处.

1
2
3
4
5
6
7
8
# 实现 k-means 聚类
kmeans_dataset1 = cluster.KMeans(n_clusters=4, max_iter=300,
init='k-means++',n_init=10).fit_predict(dataset1)
kmeans_dataset2 = cluster.KMeans(n_clusters=2, max_iter=300,
init='k-means++',n_init=10).fit_predict(dataset2)
print 'Dataset1'
print ["Cluster_"+ str(i) +":"+ str(sum(kmeans_dataset1==i)) for i in range(4)]
cluster_plots(dataset1, dataset2, kmeans_dataset1, kmeans_dataset2)
Dataset1
['Cluster_0:1008', 'Cluster_1:1022', 'Cluster_2:1018', 'Cluster_3:952']

png

K-means 对Dataset1的效果很好, 但是在Dataset2上却惨败. 事实上, 这两个数据集说明了 k-means的强项和弱点. 该算法寻求兵识别球状簇(基本上是球形的). 如果这个假设不成立, 那么模型输出可能很糟糕. 不止于此, k-means也可能对大小和密度不同的团簇失效.
1
2
3
4
5
6
7
8
9
kmeans_dataset1 = cluster.KMeans(n_clusters=4, max_iter=300,
init='k-means++',n_init=10).fit_predict(np.vstack([dataset1[:2080,:],
dataset1[3000:3080,:]]))
kmeans_dataset2 = cluster.KMeans(n_clusters=4, max_iter=300,
init='k-means++',n_init=10).fit_predict(np.vstack([dataset1[-2080:,],
dataset1[:80,]]))
cluster_plots(np.vstack([dataset1[:2080,],dataset1[3000:3080,]]),
np.vstack([dataset1[-2080:,],dataset1[:80,]]),
kmeans_dataset1, kmeans_dataset2,title1='', title2='')

png

对其所有缺点, k-means(以及相关的算法)的持久普及源于起多功能性. 它的平均复杂度是 O(knT), 其中k, n 和 T 分别代表簇的数目, 样本数和迭代次数. 因此, 它呗认为是最快的聚类算法之一. 并且在大数据的世界里, 这很重要. 如果你老板想要基于商业紧邻的10个客户聚类, 那么你可能会使用聚类.

最大期望 (EM)

这个技术是将一般期望最大化(EM)算法应用于聚任务(如下面gif所示). 它在概念上和视觉上是和Kmeans相似的. K-meas在观察点和其质心之间寻求最小化距离, EM估计一些潜在的变量(尤其是多项式分布的均值和协方差矩阵(叫做高斯混合模型(GMM))), 以便使观测数据对数似然. 和K-means类似, 算法通过迭代提供性能(即降低对数似然). 然而, 再次和K-means一样, 不能保证算法已经在全局最优值而不是局部最优值(在更高纬度上更要关注这个问题).

和k-means相反, 观察结果并没有明确地分配给团簇而是给出每个分布的概率. 如果底层分布呗正确识别, 那么算法执行的很好. 实践中尤其是对大型数据集 底层的分布往往无法被检索到, 因此EM聚类可能不太适合这样的任务.

1
2
3
4
# implementing Expecation Maximistation (specifically Guassian Mixture Models)
em_dataset1 = mixture.GaussianMixture(n_components=4, covariance_type='full').fit(dataset1)
em_dataset2 = mixture.GaussianMixture(n_components=2, covariance_type='full').fit(dataset2)
cluster_plots(dataset1, dataset2, em_dataset1.predict(dataset1), em_dataset2.predict(dataset2))

png

不出意外, EM聚类对于第一个数据集完美预测 因为它是基于正态分布的. 相反, 用GMM对Dataset2 无法正确预测, 因此这也是为什么EM在这个案例中失败的原因.

分层聚类 (Hierarchical Clustering)

1
2
人艰不拆,生活不易