机器学习-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 次连续运行后的最佳输出
- 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