Python sklearn模板实现k均值聚类算法

聚类操作得有数据才行,这里我们先用 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;">

  1. 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;">

  1. 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 所示:

k均值聚类的簇边界和簇中心

图 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 均值聚类算法,只需要十几行代码就够了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容