机器学习-K-Means算法

1. 基本原理

1)K-means
  • K-means算法是根据距离划分组无监督聚类算法,对于给定的样本集,按照样本间的距离大小,将样本划分为K个簇,使得簇内的点尽量紧密相连,而簇间的点距离尽量大
2)一些概念
  • KMeans 算法将一组 N 个样本的特征矩阵 X 划分为 K 个无交集的簇,直观上来看是簇是一组一组聚集在一起的数据,在一个簇中的数据就认为是同一类。簇就是聚类的结果表现
  • 簇中所有数据的均值通常被称为这个簇的“质心”(centroids)
  • 簇的个数 K 是一个超参数,需要我们人为输入来确定。KMeans 的核心任务就是根据我们设定好的 K,找出 K 个最优的质心,并将离这些质心最近的数据分别分配到这些质心代表的簇中去
3)算法
  • 创建 k 个点作为初始质心(通常是随机选择)
  • 计算每个对象与各个质心之间的距离,把每个对象分配给距离它最近的 质心。质心 以及分配给它们的对象就代表一个聚类。
  • 一旦全部对象都被分配了,每个聚类的质心会根据聚类中现有的对象被重新计算。
  • 这个过程将不断重复直到满足某个终止条件。终止条件可以是以下任何一个:
    • 没有(或最小数目)对象被重新分配给不同的聚类。
    • 没有(或最小数目)质心 再发生变化。
    • 簇内平方和局部最小
4)簇内平方和的定义
  • 对于一个簇来说,所有样本点到质心的距离之和越小,我们就认为这个簇中的样本越相似,簇内差异就越小。而距离的衡量方法有多种,令:

    • x 表示簇中的一个样本点;
    • μ 表示该簇中的质心;
    • n 表示每个样本点中的特征数目;
    • i 表示组成点 x 的每个特征编号;
  • 一些距离表示

    • 欧几里得距离

      \[d(x,\mu )=\sqrt{\sum_{i=1}^{n}(x_i-\mu)^2 } \]

    • 曼哈顿距离

      \[d(x,\mu )=\sum_{i=1}^{n}(|x_i-\mu |) \]

    • 余弦距离

      \[cos\theta =\frac{\sum_{i=1}^{n}(x_i*\mu)}{\sqrt{ {\textstyle \sum_{1}^{n}(x_i)^2} }*\sqrt{ {\textstyle \sum_{1}^{n}\mu ^2} } } \]

  • 基于欧氏距离的簇内平方和(Cluster Sum of Square)

    \[CSS =\sum_{j=0}^{m}\sum_{i=1}^{n}(x_i-\mu _j)^2 \]

    • m 为一个簇中样本的个数
    • j 是每个样本的编号
  • 整体平方和(Total Cluster Sum of Square)又称 total inertia

    \[Total\space Inertia =\sum_{l=1}^{k}CSS_l \]

    • Total Inertia 越小,代表着每个簇内样本越相似,聚类的效果就越好
    • 在质心不断变化不断迭代的过程中,总体平方和是越来越小的。当整体平方和最小的时候,质心就不再发生变化了

2. sklearn实现

1)参数
class sklearn.cluster.KMeans (
    n_clusters=8, # 簇的个数,默认为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=None,
	algorithm='auto'
)
  • init & n_init & random_state:初始质心选择

    • init
      • 初始化质心的方法,默认"k-means++"
      • 输入"k-means++":一种为 K 均值聚类选择初始聚类中心的聪明的办法,以加速收敛
      • 如果输入了 n 维数组,数组的形状应该是(n_clusters,n_features)并给出初始质心
    • n_init
      • 使用不同的质心随机初始化的种子来运行 k-means 算法的次数,默认10。最终结果会是基于 Inertia 来计算的
      • n_init 次连续运行后的最佳输出
  • max_iter & tol:让迭代停下来

    • max_iter:整数,默认300. 单次运行kmeans允许的最大迭代次数
    • tol:浮点数,默认1e-4. 两次迭代间Interia下降的量,如果小于tol所设定的值,迭代就会停下
2)属性
  • cluster.labels_
  • KMeans 因为并不需要建立模型或者预测结果,因此我们只需要 fit 就能够得到聚类结果了
  • KMeans 也有接口 predict 和 fit_predict:
    • predict 表示学习数据 X 并对 X 的类进行预测(对 分类器. fit() 之后,再预测)
    • fit_predict 不需要 分类器. fit() 之后都可以预测
    • 对于全数据而言,分类器. fit(). predict 的结果 = 分类器. fit_predict(X) = cluster. labels_
  • 当我们数据量非常大的时候,为了提高模型学习效率,我们可以使用部分数据来帮助我们确认质心剩下的数据的聚类结果,使用 predict 来调用
  • cluster.cluster_centers_
    • 查看质心
  • cluster.inertia_
    • 查看总距离平方和

3. 聚类算法的模型评估指标--轮廓系数

1)聚类模型难以评估
  • 聚类模型的结果不是某种标签输出,并且聚类的结果是不确定的,其优劣业务需求或者算法需求来决定,并且没有永远的正确答案
  • 我们就可以通过衡量簇内差异来衡量聚类的效果。Inertia 是用距离来衡量簇内差异的指标,因此,可以使用 Inertia 来作为聚类的衡量指标,Inertia 越小模型越好。但是这个指标的缺点和极限太大
    • 它不是有界的。我们只知道,Inertia 是越小越好,是 0 最好,但我们不知道,一个较小的Inertia究竟有没有达到模型的极限,能否继续提高
    • 它的计算太容易受到特征数目的影响,数据维度很大的时候,Inertia 的计算量会陷入维度诅咒之中,计算量会爆炸,不适合用来一次次评估模型
    • 它会受到超参数 K 的影响,在我们之前的尝试中其实我们已经发现,随着 K 越大,Inertia 注定会越来越小,但这并不代表模型的效果越来越好了
    • Inertia 作为评估指标,会让聚类算法在一些细长簇,环形簇,或者不规则形状的流形时表现不佳
2)轮廓系数
  • 能够衡量

    • 样本与其自身所在的簇中的其他样本的相似度 a,等于样本与同一簇中所有其他点之间的平均距离
    • 样本与其他簇中的样本的相似度 b,等于样本与下一个最近的簇中的所有点之间的平均距离
  • 单个样本的轮廓系数计算为

    \[s=\frac{b-a}{max(a,b)} \]

    解析为

    \[\begin{array}{l} s= \left\{\begin{matrix} 1-a/b, \space\space \space if\space a \lt b \\ 0, \space\space \space \space \space \space \space \space \space \space \space \space \space if\space a = b \\ b/a-1, \space\space \space if\space a \gt b \end{matrix}\right. \end{array} \]

  • 轮廓系数范围是 (-1,1)

    • 轮廓系数越接近 1:样本与自己所在的簇中的样本很相似,并且与其他簇中的样本不相似。
    • 轮廓系数为 0 时:两个簇中的样本相似度一致,两个簇本应该是一个簇
    • 轮廓系数为时:样本点与簇外的样本更相似
3)基于轮廓系数来选择 n_clusters

​ 通常会绘制轮廓系数分布图聚类后的数据分布图来选择我们的最佳 n_clusters

#导包
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import pandas as pd

#自己创建数据集
X, y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1)

score=[] # 存储 所有样本的轮廓系数均值

#将k值从2变化到20
for i in range(2,20):
    cluster= KMeans(n_clusters=i, random_state=0).fit(X)
    score.append(silhouette_score(X,cluster.labels_))  #计算所有样本的轮廓系数均值
                 
plt.plot(range(2,20),score)
#给k最大的位置加虚线   #idxmax()[] 取最大索引  因为这边从k=2开始 所以+2
plt.axvline(pd.DataFrame(score).idxmax()[0]+2,ls=':')

# 根据所得到的图,选择轮廓系数均值最高的 k 值,即 n_clusters

posted @ 2023-06-05 21:52  爻一  阅读(23)  评论(0编辑  收藏  举报