【机器学习】KMeans

KMeans

KMeans聚类算法也称k均值聚类算法,是集简单和经典于一身的基于距离的聚类算法。它采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为类簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。

KMeans聚类算法是一种迭代求解的聚类分析算法,其步骤是随机选取K个对象作为初始的聚类中心,然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。每分配一个样本,聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。

聚类算法在sklearn模块中有两种表现形式,一种是类,需要实例化,训练并使用接口和属性来调用结果。另一种是函数(function),只需要输入特征矩阵和超参数,即可返回聚类的结果和各种指标。

在这里插入图片描述

簇与质心

KMeans算法将一组N个样本的特征矩阵x划分为K个无交集的簇,直观上来看是簇是一组一组聚集在一起的数据,在一个簇中的数据就认为是同一类。簇就是聚类的结果表现。

簇中所有数据的均值 μ i {\mu_{i}} μi通常被称为这个簇的"质心”(centroids)。在一个二维平面中,一簇数据点的质心的横坐标就是这一簇数据点的横坐标的均值,质心的纵坐标就是这一簇数据点的纵坐标的均值。同理可推广至高维空间。

在KMeans算法中,簇的个数K是一个超参数,需要我们人为输入来确定。KMeans的核心任务就是根据我们设定好的K,找出K个最优的质心,并将离这些质心最近的数据分别分配到这些质心代表的簇中去。具体过程如下表所示:

在这里插入图片描述

那什么情况下,质心的位置会不再变化呢?当我们找到一个质心,在每次迭代中被分配到这个质心上的样本都是一致的,即每次新生成的筷都是一致的,所有的样本点都不会再从一个簇转移到另一个簇,质心就不会变化了。

簇内误差平方和

对于一个簇来说,所有样本点到质心的距离之和越小,我们就认为这个簇中的样本越相似,簇内差异就越小。而距离的衡量方法有多种,令 x x x表示簇中的一个样本点, μ {\mu} μ表示该簇中的质心, n n n表示每个样本点中的特征数目, i i i表示组成点 x x x的每个特征,则该样本点到质心的距离可以由以下距离来度量:
欧 几 里 得 距 离 : d ( x , μ ) = ∑ i = 1 n ( x i − μ i ) 2 曼 哈 顿 距 离 : d ( x , μ ) = ∑ i = 1 n ( ∣ x i − μ i ∣ ) 余 弦 距 离 : cos ⁡ θ = ∑ i = 1 n ( x i ∗ μ i ) ∑ i = 1 n ( x i ) 2 ⋅ ∑ i = 1 n ( μ i ) 2 欧几里得距离:d(x,\mu) = \sqrt{\sum^{n}_{i=1}(x_{i}-\mu_{i})^{2}} \\曼哈顿距离:d(x,\mu) = \sum^{n}_{i=1}(\vert{x_{i}-\mu_{i}}\vert) \\余弦距离:\cos\theta = \frac{\sum^{n}_{i=1}(x_{i}*\mu_{i})}{\sqrt{\sum^{n}_{i=1}(x_{i})^{2}\cdot}\sqrt{\sum^{n}_{i=1}(\mu_{i})^{2}}} d(x,μ)=i=1n(xiμi)2 d(x,μ)=i=1n(xiμi)cosθ=i=1n(xi)2 i=1n(μi)2 i=1n(xiμi)
如采用欧几里得距离,则一个簇中所有样本点到质心的距离的平方和为:
C l u s t e r   S u m   o f   S q u a r e ( C S S ) = ∑ j = 0 m ∑ i = 1 n ( x i − μ i ) 2 T o t a l   C l u s t e r   S u m   o f   S q u a r e = ∑ l = 1 k C S S l Cluster\space Sum \space of \space Square (CSS)=\sum^{m}_{j=0}\sum^{n}_{i=1}(x_{i}-\mu_{i})^{2} \\ Total\space Cluster\space Sum \space of \space Square=\sum^{k}_{l=1}CSS_{l} Cluster Sum of Square(CSS)=j=0mi=1n(xiμi)2Total Cluster Sum of Square=l=1kCSSl
其中, m m m为一个簇中样本的个数, j j j是每个样本的编号。这个公式被称为簇内平方和(cluster Sum of Square),又叫做Inertia。而将一个数据集中的所有簇的簇内平方和相加,就得到了整体平方和(Total Cluster Sum of Square),又叫做total inertia。Total lnertia越小,代表着每个簇内样本越相似,聚类的效果就越好。因此KMeans追求的是,求解能够让Inertia最小化的质心。实际上,在质心不断变化不断迭代的过程中,总体平方和是越来越小的。我们可以使用数学来证明,当整体平方和最小的时候,质心就不再发生变化了。如此,KMeans的求解过程,就变成了一个最优化问题。

Inertia是基于欧几里得距离的计算公式得来的。实际上,我们也可以使用其他距离,每个距离都有自己对应的Inertia。在过去的经验中,我们总结出不同距离所对应的质心选择方法和Inertia,在KMeans中,只要使用了正确的质心和距离组合.无论使用什么样的距离.都可以达到不错的聚类效果:

在这里插入图片描述

核心class

class sklearn.cluster.KMeans(n_clusters=8, init='k-means++', n_init=10, max_iter=300, tol=0.0001, precompute_distances='auto', verbose=0, random_state=None, copy_x=True, n_jobs=1, algorithm='auto')

其中,n_clusters是KMeans中的k,表示模型需要分几个类别。

简单代码实现

import matplotlib.pyplot as plt
from sklearn.datasets import  make_blobs
# 创建数据集
X, y = make_blobs(n_samples=500, n_features=2, centers=4, 
				  random_state=1
				  )

# 数据可视化
colors = ['red', 'pink', 'orange', 'gray']
fig, ax1 = plt.subplots(1)
for i in range(4):
    ax1.scatter(X[y == i, 0], X[y == i, 1]
                , marker='o' # 点的形状
                , s=8 # 点的大小
                , c=colors[i]
                )
plt.show()

在这里插入图片描述

上图是数据的真实分布情况,接下来使用KMeans对数据进行聚类!

# 将数据聚类成三类
from sklearn.cluster import KMeans

n_clusters = 3
cluster = KMeans(n_clusters=n_clusters, random_state=1).fit(X)

# labels_属性是用来查看聚类完成后的类别,每个样本对应一个类
y_pred = cluster.labels_
print(y_pred)

# KMeans因为并不需要建立模型或者预测结果,因此只需要fit就能得到聚类结果
# 然而KMeans也有predict和fit_predict接口,表示学习数据X并对X的类进行预测,
# 结果与label_一致
pre = cluster.fit_predict(X)

# cluster_centers_属性用来查看质心
centroid = cluster.cluster_centers_
print(centroid)

# inertia_属性是用来查看总距离平方和的
inertia = cluster.inertia_
print(inertia)

# 聚类数据可视化
colors = ['red', 'pink', 'orange', 'gray']
fig, ax1 = plt.subplots(1)
for i in range(n_clusters):
    ax1.scatter(X[y_pred == i, 0], X[y_pred == i, 1]
                , marker='o' # 点的形状
                , s=8 # 点的大小
                , c=colors[i]
                )
ax1.scatter(centroid[:, 0], centroid[:, 1]
            , marker="x"
            , s=15
            , c="black"
            )
plt.show()

在这里插入图片描述

然而在实际中,我们也许并不清楚将数据分成多少个类才合适,就如上述代码所示(选择将真实的4类数据分成3类),当我们不确定将数据分成多少类时,就只能尝试多种情况。为此,为找到合适的分类参数值,我们需要对模型建立评估指标。

模型评估指标

KMeans的目标是确保"簇内差异小,簇外差异大",因此,我们可以通过衡量簇内差异来衡量聚类的效果。而Inertia虽然是用距离来衡量簇内差异的指标,但是这个指标的缺点和极限太大,所以并不适合,其具体缺点在于:

  1. 它不是有界的,Inertia是越小越好,是0最好,但我们不知道,一个较小的Inertia究竟有没有达到模型的极限,能否继续提高。
  2. 它的计算太容易受到特征数目的影响,数据维度很大的时候,Inertia的计算量会陷入维度诅咒之中,计算量会爆炸,不适合用来一次次评估模型。
  3. Inertia对数据的分布有假设,它假设数据满足凸分布(即数据在二维平面图像上看起来是一个凸函数的样子),并且它假设数据是各向同性的 (isotropic),即是说数据的属性在不同方向上代表着相同的含义。但是现实中的数据往往不是这样。所以使用Inertia作为评估指标,会让聚类算法在一些细长簇,环形簇,或者不规则形状的流形时表现不佳。

在这里插入图片描述

轮廓系数

在样本的真实标签未知的情况下,聚类是完全依赖于评价簇内的稠密程度(簇内差异小)和簇间的离散程度(簇外差异大)来评估聚类的效果,其中轮廓系数是最常用的聚类算法的评价指标。它是对每个样本来定义的,它能够同时衡量:

  1. 样本与其自身所在的簇中的其他样本的相似度 a a a,等于样本与同一簇中所有其他点之间的平均距离
  2. 样本与其他簇中的样本的相似度 b b b,等于样本与下一个最近的簇中得所有点之间的平均距离

根据聚类的要求簇内差异小,簇外差异大",希望 b b b永远大于 a a a,并且大得越多越好。于是单个样本的轮廓系数计算为:
s = b − a m a x ( a , b ) s = \frac{b-a}{max(a,b)} s=max(a,b)ba
很容易理解轮廓系数范围是(-1,1),其中值越接近1表示样本与自己所在的簇中的样本很相似,并且与其他簇中的样本不相似,当样本点与簇外的样本更相似的时候,轮廓系数就为负。当轮廓系数为0时,则代表两个簇中的样本相似度一致,两个簇本应该是一个簇。

如果一个簇中的大多数样本具有比较高的轮廓系数,则簇会有较高的总轮廓系数,整个数据集的平均轮廓系数也越高,则聚类是合适的。如果许多样本点具有低轮廓系数甚至负值,则聚类是不合适的,聚类的超参数K可能设定得太大或者太小。

在sklearn中,我们使用模块metrics中的类silhouette_score来计算轮廓系数,它返回的是一个数据集中,所有样本的轮廓系数的均值。但我们还有同在metrics模块中的silhouette_sample_,它的参数与轮廓系数一致,但返回的是数据集中每个样本自己的轮廓系数。

from sklearn.metrics import silhouette_score
from sklearn.metrics import  silhouette_samples

print(silhouette_score(X, y_pred))
print(silhouette_samples(X, y_pred))

基于轮廓系数选择n_clusters

import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.metrics import  silhouette_samples
# 创建数据集
X, y = make_blobs(n_samples=500, n_features=2, centers=4, random_state=1)

for n_clusters in [2, 3, 4, 5, 6]:
    n_clusters = n_clusters

    # 创建一个画布,画布上共有一行两列两个图
    fig, (ax1, ax2) = plt.subplots(1, 2)

    # 画布尺寸
    fig.set_size_inches(18, 7)

    # 第一个图是轮廓系数图像,是由各个簇的轮廓系数组成的横向条形图
    # 轮廓系数的取值是在[-1, 1]之间,但是横坐标太长不利于可视化
    ax1.set_xlim([-0.1, 1])
    ax1.set_ylim([0, X.shape[0] + (n_clusters + 1) * 10])

    # 模型建立
    cluster = KMeans(n_clusters=n_clusters, random_state=1).fit(X)
    cluster_labels = cluster.labels_

    # 调用轮廓系数分数
    silhouette_avg = silhouette_score(X, cluster_labels)
    print("For n_clusters =", n_clusters, "The average silhouette_score is :", silhouette_avg)

    sample_silhouette_values = silhouette_samples(X, cluster_labels)

    # 设定y轴上的初始取值
    y_lower = 10

    for i in range(n_clusters):
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i
        color = cm.nipy_spectral(float(i)/n_clusters)

        ax1.fill_betweenx(np.arange(y_lower, y_upper)
                         , ith_cluster_silhouette_values
                         , facecolor=color
                         , alpha=0.7
                         )

        ax1.set_title("The silhouette plot for the various cluster")
        ax1.set_xlabel("The silhouette coefficient values")
        ax1.set_ylabel("Cluster label")
        ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
        ax1.set_yticks([])
        ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
        y_lower = y_upper

    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)

    ax2.scatter(X[:, 0], X[:, 1]
                , marker='o'
                , s=8
                , c=colors
                )
    center = cluster.cluster_centers_
    ax2.scatter(center[:, 0], center[:, 1]
                , marker='x'
                , c='red'
                , alpha=1
                , s=200
                )
    ax2.set_title("The visualization of the clustered data")
    ax2.set_xlabel("Feature space of the 1st feature")
    ax2.set_ylabel("Feature space of the 2nd feature")

    plt.suptitle(("Silhouette analysis for KMeans clustering on sample data"),
                    fontsize=14,
                    fontweight='bold'
                 )

    plt.show()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

从图中分析可知,当分类为4时效果最佳!

猜你喜欢

转载自blog.csdn.net/zzy_NIC/article/details/120923451