聚类操作得有数据才行,这里我们先用 sklearn 的数据生成工具 make_blobs( ) 来合成所需的数据。make_blobs( ) 方法常被用来生成聚类算法的测试数据,简单来说,make_blobs( ) 会根据用户指定的样本数量、特征数量、簇中心数量、生成数据的波动范围等来生成数据,这些数据可用于测试聚类算法的效果。
make_blobs( ) 方法的原型如下:
<pre class="python sh_python snippet-formatted sh_sourceCode" style="margin: 0px; display: block; padding: 0px; font-size: 14px; line-height: 1.6em; color: rgb(102, 102, 102); white-space: pre-wrap; overflow-wrap: break-word; background: none; border: none; border-radius: 0px;">
- sklearn.datasets.make_blobs(n_samples=100, n_features=2,centers=None, cluster_std=1.0, center_box=(-10.0, 10.0), shuffle=True, random_state=None)
</pre>
主要参数 n_samples 是待生成的样本总数,默认值为 100;n_features 是每个样本的特征数量,默认值为 2;centers 表示要生成的样本中心(类别)数,或是确定的中心点数量,默认值为 3;cluster_std 是每个类别的方差。
该方法有两个返回值。X 返回维度为 [n_samples, n_features] 的特征数据;y 返回维度为 [n_samples] 的标签数据。从返回的数据可以看出,make_blobs( ) 同样可用于监督学习的分类算法中,因为它也提供了标签(分类)信息。
对这个方法有了基本的认知之后,下面我们就“牛刀小试”,生成一些数据并将它们可视化输出,看看它们长成什么模样。
我们先导入必要的包或方法,包括绘图的 Matplotlib 和生成数据的 make_blobs。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [1]:
import matplotlib.pyplot as plt
导入生成数据的方法
from sklearn.datasets import make_blobs</pre>
然后生成数据,方法如下:
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [2]:
生成合成数据
blobs = make_blobs(n_samples = 200, random_state =1, centers = 4)</pre>
需要注意的是,make_blobs( ) 方法返回两个数据,实际上这两个数据会被打包成一个匿名的元组。
如果我们用一个变量来接收它,那么这个变量就是一个包含两个元素的元组,上面的 blobs 就是这样的。所以,如果我们想提取特征数据,必须按照提取元组元素的方式来完成。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [3]:
X_blobs = blobs[0] #提取特征数据</pre>
在 In [2] 处,由于我们并没有设置 make_blobs( ) 的特征数 n_features,参考该方法的原型可知,n_features 的默认值为 2,即合成的数据是二维的。二维数据是很容易被绘制出来的。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [4]:
plt.scatter(X_blobs[:, 0], X_blobs[:, 1])
plt.show()</pre>
运行结果如图 1 所示,可以看到所有簇都被渲染成了同一种颜色,辨识度不是很高。
图 1:生成数据的聚类图
事实上,make_blobs( ) 还返回了标签信息,对于聚类算法而言,它基本没有用。但在绘制图形时,标签信息可用于区分不同簇。例如,如果我们把上述代码稍微修改一下,用不同的标签信息来标识不同簇,就会发现这些簇的颜色泾渭分明,清晰可辨。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [5]:
plt.scatter(X_blobs[:, 0], X_blobs[:, 1], c = blobs[1])
plt.show()</pre>
上述代码中,散点图绘制方法 scatter( ) 中的参数 c 表示颜色(color)。blobs[1] 表示的是标签信息 y。
程序运行结果如图 2 所示:
图 2:用标签信息染色的聚类图
有了数据,就可用利用 sklearn 的标准流程实施聚类分析了。首先导入相应的聚类模型。
https://docs.qq.com/pdf/DR1doYmNBYUZ3RVNX
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [6]:
(1)导入KMeans工具包
from sklearn.cluster import KMeans</pre>
需要说明的是,聚类模型非常之多,而 k 均值聚类仅仅是其中的一种。所以我们是通过 sklearn.cluster 导入 KMeans 工具包的。接下来,我们要构建 KMeans 模型对象。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [7]:
(2)构建 KMeans 模型对象,设定 k=4
kmeans = KMeans(n_clusters = 4)</pre>
为了更好地使用这个模型,下面我们来说明一下实现 KMeans 模型的方法原型,如下所示:
<pre class="python sh_python snippet-formatted sh_sourceCode" style="margin: 0px; display: block; padding: 0px; font-size: 14px; line-height: 1.6em; color: rgb(102, 102, 102); white-space: pre-wrap; overflow-wrap: break-word; background: none; border: none; border-radius: 0px;">
- sklearn.cluster.KMeans(n_clusters=8, init= 'k-means++', n_init=10, max_iter=300, ..., random_state=None, ...)
</pre>
KMeans 中常用的参数解释如下。
- n_clusters:指定簇中心的个数。这个是超参数,人为设定。
- init:簇中心初始化方法。任意指定的参数为 random,默认值为 k-means++,它是一种特殊的初始化方法,可在很大程度上提高算法的收敛速度。
- n_init:初始化的次数。模型会多次初始化不同的簇中心得到不同的结果,并择优选定。
- max_iter:得到最终簇中心的迭代次数。
- random_state:相当于随机种子。在开始运行时,k 均值聚类需要从众多数据中随机挑选 k 个点作为簇中心,random_state 就是为挑选 k 个簇中心而准备的随机种子。
前面提及的训练集和测试集的分割方法— train_test_split( ) 也涉及随机种子的设置。与之类似的是,如果我们将随机种子指定为某个值,实际上就是固化了随机数的生成,这样做的好处在于,便于进行性能评估和调参。如果不设置,sklearn 会以时间作为随机种子,因为时间在每时每刻都是不同的,所以基本上能保证初始簇中心也是不同的。
当 KMeans 模型生成以后,就可以训练模型了。如前所述,sklearn 中的所有模型训练都称为拟合(fit)。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [8]:
(3)训练模型(拟合,懒惰算法)
kmeans.fit(X_blobs)</pre>
下面,我们就可以用可视化的方式绘制出每个簇的边界(势力范围)及簇中心了。为了便于说明,我们把下面的代码进行编号。
<pre class="info-box" style="margin: 6px auto; display: block; padding: 10px; font-size: 14px; line-height: 1.6em; color: rgb(68, 68, 68); white-space: pre-wrap; overflow-wrap: break-word; background: none rgb(248, 248, 248); border: 1px solid rgb(225, 225, 225); border-radius: 4px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">In [9]:
01 #(4)绘制可视化图
02 import numpy as np
03 x_min, x_max = X_blobs[:, 0].min() - 0.5, X_blobs[:, 0].max() + 0.5
04 y_min, y_max = X_blobs[:, 1].min() - 0.5, X_blobs[:, 1].max() + 0.5
05 #(5)生成网格点矩阵
06 xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02), np.arange(y_min, y_max, 0.02))
07 Z = kmeans.predict(np.c_[xx.ravel(), yy.ravel()])
08 Z = Z.reshape(xx.shape)
09 plt.figure(1)
10 plt.clf()
11 plt.imshow(Z, interpolation = 'hermite', extent = (xx.min(), xx.max(), yy.min(), yy.max()), cmap = plt.cm.winter, aspect = 'auto', origin = 'lower')
12 plt.plot(X_blobs[:, 0], X_blobs[:, 1], 'w.', markersize = 5)
13 #用红色的x表示簇中心
14 centroids = kmeans.cluster_center_
15 plt.scatter(centroids[:, 0], centroids[:, 1], marker = "x", s = 150, linewidths = 3, color = 'r', zorder = 10)
16 plt.xlim(x_min, x_max)
17 plt.ylim(y_min, y max)
18 plt.xticks()
19 plt.yticks()
20 plt.show()</pre>
程序执行结果如图 3 所示:
图 3:k 均值聚类的簇边界和簇中心
上述代码的核心功能就是实现聚类,特别之处是画出了每个簇的“势力范围”(用不同的颜色标识),也就是说,落到这个所谓的“势力范围”内的点,就属于这个簇。如何画出这个“势力范围”呢?
首先,找到数据集的最大值、最小值,并稍稍扩展这个极值边界,即比最小值还要小 0.5,比最大值还要大 0.5(代码 03~04 行)。
然后利用 NumPy 中的 meshgrid( ) 方法返回一个网格矩阵,它就像一张密集的网,覆盖整个坐标轴(代码第 06 行)。然后,我们把这个密集的网格坐标点当作一个测试集,把这些坐标点一一拿去预测,看它们分别属于哪个簇(代码第 07 行)。
而后,根据每个网格点所属的簇不同,渲染不同的颜色,如果这样的网格点分布足够密集,那么不同簇的“势力范围”就生动地展现出来了(代码第 11 行)。
https://docs.qq.com/pdf/DR1doYmNBYUZ3RVNX
然后,我们就利用常规方法画出数据的散点图(代码第 12 行),接着根据拟合得到各个簇中心的坐标(代码第 14 行)。如前所述,簇中心坐标属于 sklearn 拟合而出的重要参数,它有自己专门的命名规则,即常规英文单词后面加一个下画线,如 cluster_centers_。然后我们用标记“×”将簇中心标识出来即可(代码第 15 行)。
以上就是利用 sklearn 实施k均值聚类的流程。在上述范例中,为了辅助说明,我们添加了很多解释性的代码。而实际上,如果我们有了预处理好的数据,并熟悉 sklearn 框架的用法,实现一个 k 均值聚类算法,只需要十几行代码就够了。