91scikit-learn 机器学习入门实战--K-Means 聚类算法应用

K-Means 聚类算法应用

K-Means 聚类

监督学习被用于解决分类和回归问题,而无监督学习主要是用于解决聚类问题。聚类,顾名思义就是将具有相似属性或特征的数据聚合在一起。聚类算法有很多,最简单和最常用的就算是 K-Means 算法了。

image.png

K-Means 算法在应用时,相对来上面的例子要复杂一些。现在,假设有如下图所示的一组二维数据。接下来,我们就一步一步演示 K-Means 的聚类过程。
首先,正如我们前面介绍无监督学习时所说,无监督学习面对的数据都是没有标签的。如果我们把下方示例数据的颜色看作是标签,那么只有一种颜色。
image.png

第一步,确定要聚为几类?也就是 K 值。假设,这里我们想将样本聚为 3 类。当然,你也完全可以将其聚为 2 类或 4 类,不要受到视觉上的误导。
这里,我们以 3 类为例。当确定聚为 3 类之后,我们在特征空间上,随机初始化 3 个类别中心。这里的 3 也就对应着 K 值的大小。
image.png

第二步,我们依据这三个随机初始化的中心,将现有样本按照与最近簇中心点之间的距离进行归类。图中绿线将全部样本划分为三个类别。(中间小三角形是参考线,可以忽略。)
image.png

这样,我们的样本被划为为三个区域。现在,我们就要用到上面提到的均值来重新求解 3 个区域对应的新的样本中心。
image.png

如上图所示,假设我们求解的新样本中心为三个绿点所示的位置。然后,又重新回到上一步,根据这三个中心重新划分样本,再求解中心。
image.png

依次迭代,直到样本中心变化非常小时终止。最终,就可以将全部样本聚类为三类。我们通过下面的动图来演示完整的 K-Means 的聚类过程。
https://doc.shiyanlou.com/courses/uid214893-20190523-1558591867573
让我们看一下 K-Means 如何在我们之前看过的简单数据集上运行。为了表示这是无监督的,我们将不绘制群集的颜色:

import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs  # 导入聚类数据生成器
%matplotlib inline

plt.style.use('seaborn')  # 样式美化

# 生成 300 个可分成 4 类的数据
X, y = make_blobs(n_samples=300, centers=4,
                  random_state=0, cluster_std=0.60)
plt.scatter(X[:, 0], X[:, 1], s=50)

用肉眼看,挑选四个集群相对容易。但是,如果要对数据的不同分段执行详尽搜索,则搜索空间的点数将是指数级的,我们看看 K-Means 的聚类效果如何。接下来,导入 KMeans 估计器进行聚类。scikit-learn 中的聚类算法都包含在 sklearn.cluster 方法下,这里 KMeans 估计器最重要的参数就是 n_clusters,即我们选择聚类的类别数 k 值,完整的参数可参考 官方文档

# 导入 KMeans 估计器
from sklearn.cluster import KMeans
est = KMeans(n_clusters=4)  # 选择聚为 4 类
est.fit(X)
y_kmeans = est.predict(X)  # 预测类别,输出为含0、1、2、3数字的数组

# 为预测结果上色并可视化
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
centers = est.cluster_centers_  # 找出中心
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.5)  # 绘制中心点

该算法以与我们肉眼所见非常相似的方式识别四个点的聚类。
K均值算法:期望最大化
K-Means 是使用期望最大化方法得出结果的算法。期望最大化可解释成两步,其工作原理如下:
1.猜测一些簇中心点。
2.重复直至收敛。

期望步骤(E-step):将点分配至离其最近的簇中心点。
最大化步骤(M-step):将簇中心点设置为所有点坐标的平均值。

from sklearn.metrics import pairwise_distances_argmin  # 最小距离函数
import numpy as np


def find_clusters(X, n_clusters, rseed=2):
    # 1.随机选择簇中心点
    rng = np.random.RandomState(rseed)
    i = rng.permutation(X.shape[0])[:n_clusters]
    centers = X[i]
    while True:
        # 2a.基于最近的中心指定标签
        labels = pairwise_distances_argmin(X, centers)
        # 2b.根据点的平均值找到新的中心
        new_centers = np.array([X[labels == i].mean(0)
                                for i in range(n_clusters)])
        # 2c.确认收敛
        if np.all(centers == new_centers):
            break
        centers = new_centers
    return centers, labels


centers, labels = find_clusters(X, 4)
plt.scatter(X[:, 0], X[:, 1], c=labels,
            s=50, cmap='viridis')  # 绘制聚类结果

上面是我们用期望最大化方法聚类出的结果,可以看到和我们直接用 K-Means 聚类是一样的。

K-Means在手写数字数据集中的应用

为了更加接近实际案例应用,让我们再次看一下手写数字数据集。在这里,我们将使用 K-Means 对 64 个维度的数据进行聚类,然后查看簇中心,观察其聚类效果。

from sklearn.datasets import load_digits  # 导入数据集
digits = load_digits()  # 加载数据集

est = KMeans(n_clusters=10)  # 选择聚为 10 类
clusters = est.fit_predict(digits.data)  # 返回每个数据对应的标签
est.cluster_centers_.shape  # 输出簇中心参数

我们看到 64 个维度的 10 个群集。让我们可视化这些簇中心,看看它们代表什么:

fig = plt.figure(figsize=(8, 3))  # 创建画布,设置画布参数

for i in range(10):
    ax = fig.add_subplot(2, 5, 1 + i, xticks=[], yticks=[])  # 创建子图
    ax.imshow(est.cluster_centers_[i].reshape((8, 8)), cmap=plt.cm.binary)

我们看到,即使没有标签,K-Means 仍能找到 10 个簇的中心数字显示(数字 8 显示得不太明显)。聚类后的数据的标签的顺序按照类别排列了,我们先将其恢复到原来数据的顺序,再看看 K-Means 的准确性如何:

# scipy.stats.mode 寻找数组或者矩阵每行/每列中最常出现成员以及出现的次数
from scipy.stats import mode
from sklearn.metrics import accuracy_score  # 导入评估模块

labels = np.zeros_like(clusters)  # 变成全 0 数组

# 置换聚类后的数据标签顺序
for i in range(10):
    mask = (clusters == i)
    labels[mask] = mode(digits.target[mask])[0]

# 评估 K-Means 的准确性
accuracy_score(digits.target, labels)

可以看到准确性将近 80%,这个结果还是很不错的。

K-Means 用于色彩压缩

聚类的还有一种有趣的应用是用于彩色图像压缩。假设有一张具有数百万种颜色的图像(彩色图像中,每个像素的大小为 3 字节(RGB),可以表示的颜色总数为256×256×256),但其实大多数图像中的很大一部分色彩通常是不会被眼睛注意到的,而且图像中的很多像素都拥有类似或者相同的颜色。
通过数据集模块可以访问 scikit-learn 的许多图像。比如 load_sample_image 模块包含大量内置的图像,这里我们加载其中一张名为 'china' 的图片:

from sklearn.datasets import load_sample_image  # 导入图像数据集

china = load_sample_image("china.jpg")
plt.imshow(china)  # 图像显示
plt.grid(False)  # 不显示网格线

china.shape

该图像存储在一个三维数组 (height, width, RGB) 中,以 0~255 的整数表示红 / 蓝 / 绿信息。我们将像素值缩小到 0 到 1 之间,然后将数组重塑为典型的 scikit-learn 输入 (n_samples,n_features):

X = (china / 255.0).reshape(-1, 3)  # 指定新数组列为 3
print(X.shape)

我们现在使用 K-means 聚类,将 1600 万种颜色(256^3)缩减到 64 种颜色。这个原理是在数据中找到 Ncolor 个簇,并创建一个新图像,其中用最接近的簇的颜色替换真实的输入颜色,即一个簇(类)只有一种颜色。因为这里数据量较大,所以使用更加复杂的 MiniBatchKMeans 估计器。

# 导入 MiniBatchKMeans 估计器
from sklearn.cluster import MiniBatchKMeans

# 缩减到的颜色数量
n_colors = 64

X = (china / 255.0).reshape(-1, 3)  # 重塑输入

model = MiniBatchKMeans(n_colors)
labels = model.fit_predict(X)  # 返回每个数据对应的标签
colors = model.cluster_centers_  # 得到簇中心颜色
new_image = colors[labels].reshape(china.shape)  # 新图像生成
new_image = (255 * new_image).astype(np.uint8)  # 像素值恢复

# 绘制新图像
with plt.style.context('seaborn-white'):  # 绘图风格
    plt.figure()
    plt.imshow(china)
    plt.title('input: 16 million colors')

    plt.figure()
    plt.imshow(new_image)
    plt.title('{0} colors'.format(n_colors))

我们比较上面两个图像输出,将颜色从 256 ^ 3种缩减到了 64 种,但最后成像差别不大,颜色数量的大幅减少可以提高模型运行速度,你还可以将上面代码中 n_colors 改为 8、16 或者 32,看看最后新图像效果。

K 值选择

前面我们举的例子的类别都是事先知道的,所以我们模型训练时的 K 值都是可知的,但面对分类不明确甚至基本完全不知其分布规律的数据,我们在进行聚类的 K 值要怎么选择呢?
有一种评估方法,叫 轮廓系数。轮廓系数综合了聚类后的两项因素:内聚度和分离度。内聚度就指一个样本在簇内的不相似度,而分离度就指一个样本在簇间的不相似度。
在 scikit-learn 中,同样也提供了直接计算轮廓系数的方法。我们使用实验一开始生成的可分成 4 类的简单数据集,绘制出轮廓系数与聚类 K 值变化的折线图。

from sklearn.metrics import silhouette_score  # 导入轮廓系数计算模块

index2 = []  # 横坐标
silhouette = []  # 轮廓系数列表

# 生成 300 个可分成 4 类的数据
X, y = make_blobs(n_samples=300, centers=4,
                  random_state=0, cluster_std=0.60)

# K 从 2 ~ 10 聚类
for i in range(8):
    est = KMeans(n_clusters=i + 2)
    index2.append(i + 2)
    silhouette.append(silhouette_score(X, est.fit_predict(X)))

print(silhouette)  # 输出不同聚类下的轮廓系数s

# 绘制折线图
plt.plot(index2, silhouette, "-o")

轮廓系数越接近于 1,代表聚类的效果越好。我们可以很清楚的看出,K=4 对应的轮廓系数最大,也更接近于 1 。

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

推荐阅读更多精彩内容