无监督学习——K均值聚类(上)

近几年在机器学习领域里面,聚类是比较热门的一个词汇。它是将数据集划分成组的任务,这些组叫做。其目标是划分数据,使得一个簇内的数据点非常相似且簇内的数据点非常不同。与分类算法相似,聚类算法为每个数据点分配(或预测)一个数字,表示这个点属于哪个簇。但是,与分类算法不同的是,聚类属于无监督学习,也就是说事先并不知道数据集的标签或者说特征值分类,而分类算法是监督学习,意味着已经提前知道了数据点的所属类别。接下来,我重点介绍下聚类里面比较常用的算法——k均值聚类

K均值聚类的原理讲解

1.算法介绍

k均值聚类是最简单也最常用的聚类算法之一。它试图找到代表数据特定区域的簇中心。算法交替执行以下两个步骤:将每个数据点分配给最近的簇中心,然后将簇中心设置为所分配的所有数据点的平均值。如果簇的分配不再发生变化,那么算法结束。

2.代码讲解

用scikit-learn应用K均值相当简单。下面,我们将其应用于模拟数据make_blobs,将Kmeans实例化,并设置我们要找的簇的个数。然后对数据调用fit方法。代码如下:

1
2
3
4
5
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
X,y=make_blobs(random_state=1)###生成模拟的二维数据
kmeans=KMeans(n_clusters=3)###如果不指定n_clusters,它的默认值是8
kmeans.fit(X)

PS:其中make_blobs的函数功能是生成各向同性的高斯斑点以进行聚类,感兴趣的同学可以自行百度研究下,这里就不在进行赘述了。

(1) kmeans.labels_

在算法运行期间,Kmeans为X中的每个训练数据点分配一个簇标签。我们可以在kmeans.labels_中找到这些标签,实际上kmeans.labels_就是算法结果(与kmeans.predict(X)的意义一样)

在这里插入图片描述

如上图所示,聚类算法与分类算法有些相似,每个元素都有一个分配的标签。我们与数据集的原分类对比一下:

在这里插入图片描述

两相对比可以看出,标签并不是真实的,因此标签本身并没有什么意义。算法给予你的唯一信息就是所有标签相同的数据点都是相似的。所以,我们不应该为其中一组的标签是0,另一组的标签是1赋予任何意义。

(2) kmeans.cluster_centers_

之前提到的簇中心数据被保存在kmeans.cluster_centers_属性中:

在这里插入图片描述

(3) 可视化

接下来我们将数据分类结果可视化:

1
2
3
import mglearn
mglearn.discrete_scatter(X[:,0],X[:,1],kmeans.labels_,markers="o")
mglearn.discrete_scatter(kmeans.cluster_centers_[:,0],kmeans.cluster_centers_[:,1],[0,1,2],markers="*",markeredgewidth=2)

在这里插入图片描述

从上面的图像可以看出,数据被3个簇中心很好的分成了三个部分。

实际上,我们也可以使用更多或更少的簇中心:

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt
fig,axes=plt.subplots(1,2,figsize=(10,5))
kmeans=KMeans(n_clusters=2)###使用两个簇
kmeans.fit(X)
mglearn.discrete_scatter(X[:,0],X[:,1],kmeans.labels_,ax=axes[0])
kmeans=KMeans(n_clusters=5)###使用五个簇
kmeans.fit(X)
mglearn.discrete_scatter(X[:,0],X[:,1],kmeans.labels_,ax=axes[1])

在这里插入图片描述

K均值聚类的实际应用

1.数据来源

水质测试数据:https://www.kaggle.com/adityakadiwal/water-potability

在这里插入图片描述

该数据集共包含3277个数据点,并通过水质硬度,酸碱度等9个特征值,将水分成了两类。(0和1指代水的类别)

接下来,为了演示无监督学习,我们将其当作不知分类结果的数据集,并用K均值算法进行聚类。

2.数据处理

老规矩,还是先读取数据,代码如下:

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import winreg
###################
real_address = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
file_address=winreg.QueryValueEx(real_address, "Desktop")[0]
file_address+='\\'
file_origin=file_address+"\\源数据-分析\\water_potability.csv"###https://www.kaggle.com/adityakadiwal/water-potability
water=pd.read_csv(file_origin)
#设立桌面绝对路径,读取源数据文件,这样将数据直接下载到桌面上就可以了,省得还要去找
###################

结果如下所示:

在这里插入图片描述

可以看到这份数据中有很多空值,为了保证数据的完整性,我们将包含空值的行全部删掉(也可以用平均数来代替):

1
2
3
water=water.dropna()####去掉包含空值的行
water_test=water.drop("Potability",axis=1)#####去掉分类结果
y=water["Potability"]

3.应用K均值聚类并与原结果进行比较

我们对上面已经处理好的数据集应用K均值聚类算法:

1
2
3
kmeans=KMeans(n_clusters=2)###对应源数据中的两个分类结果
kmeans.fit(water_test)
y_pred=kmeans.predict(water_test)

处理好之后,我们将聚类结果与源数据的分类进行差值计算。如果分类结果相同,则差值等于0,因此,通过统计差值中有多少个数值为0,来对聚类结果进行测评:

1
2
3
4
import numpy as np
y_original=np.array(y)
a=y_pred-y_original###将结果进行比较,计算两者之间的差值
len(a[a==0])/len(a)###如果分类结果相同,则等于0,所以统计差值中有多少个数值为0

结果如下所示,无监督学习的K均值聚类精度为52.8%

在这里插入图片描述

K均值的精度

我们从上面的结果可以看出来,实际应用中K均值的精度并没有很高。一是因为与监督学习中的分类器相比,K均值聚类属于无监督学习,也就是说事先并不知道分类结果,没有办法进行模型训练。二是因为即使你知道给定数据中簇的“正确”个数,k均值可能也不是总能找到它们。每个簇仅由其中心定义,这意味着每个簇都是凸形。因此,K均值只能找到相对简单的形状。K均值还假设所有簇在某种程度上具有相同的“直径”,它总是将簇之间的边界刚好画在簇中心的中间位置。

举个栗子:

1
2
3
4
5
6
7
from sklearn.datasets import make_moons
X,y=make_moons(n_samples=200,noise=0.05,random_state=0)
kmeans=KMeans(n_clusters=2)###使用两个簇
kmeans.fit(X)
y_pred=kmeans.predict(X)###与labels_相同,为新数据点分配簇标签
plt.scatter(X[:,0],X[:,1],c=y_pred,cmap=mglearn.cm2,s=60)
plt.scatter(kmeans.cluster_centers_[:,0],kmeans.cluster_centers_[:,1],marker="*",c=[mglearn.cm2(0),mglearn.cm2(1)],s=100,linewidth=2)

在这里插入图片描述

对于上面的数据集,很明显,我们是希望聚类算法能够发现两个半月形,但是在这里利用K均值算法是不可能做到这一点的。

总结

本篇主要讲了关于K均值聚类的一些基础的东西,包括代码原理,实际应用以及应用中的一些局限性。限于篇幅原因,下一篇我再说一些有关K均值聚类稍微复杂一点的内容。

有很多地方做的不是很好,欢迎网友来提出建议,也希望可以遇到些朋友来一起交流讨论。