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

K-Means 聚类算法应用

K-Means 聚类

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

91scikit-learn 机器学习入门实战--K-Means 聚类算法应用_第1张图片
image.png

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

第一步,确定要聚为几类?也就是 K 值。假设,这里我们想将样本聚为 3 类。当然,你也完全可以将其聚为 2 类或 4 类,不要受到视觉上的误导。
这里,我们以 3 类为例。当确定聚为 3 类之后,我们在特征空间上,随机初始化 3 个类别中心。这里的 3 也就对应着 K 值的大小。
91scikit-learn 机器学习入门实战--K-Means 聚类算法应用_第3张图片
image.png

第二步,我们依据这三个随机初始化的中心,将现有样本按照与最近簇中心点之间的距离进行归类。图中绿线将全部样本划分为三个类别。(中间小三角形是参考线,可以忽略。)
91scikit-learn 机器学习入门实战--K-Means 聚类算法应用_第4张图片
image.png

这样,我们的样本被划为为三个区域。现在,我们就要用到上面提到的均值来重新求解 3 个区域对应的新的样本中心。
91scikit-learn 机器学习入门实战--K-Means 聚类算法应用_第5张图片
image.png

如上图所示,假设我们求解的新样本中心为三个绿点所示的位置。然后,又重新回到上一步,根据这三个中心重新划分样本,再求解中心。
91scikit-learn 机器学习入门实战--K-Means 聚类算法应用_第6张图片
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 。

你可能感兴趣的:(91scikit-learn 机器学习入门实战--K-Means 聚类算法应用)