ML07聚类算法


PCtitle: 聚类算法KMeans—机器学习经典算法
date: 2021-04-15
tags:

  • ML
  • 基础
    categories:
  • dataAnalysis

聚类算法KMeans

无监督学习与聚类算法

在过去,我们学习了决策树,随机森林,逻辑回归,他们虽然有着不同的功能,但却都属于“有监督学习”的一部分,即是说,模型在训练的时候,即需要特征矩阵X,也需要真实标签y。机器学习当中,还有相当一部分算法属于“无监督学习”,无监督的算法在训练的时候只需要特征矩阵X,不需要标签。我们曾经学过的PCA降维算法就是无监督学习中的一种,聚类算法,也是无监督学习的代表算法之一。

聚类算法又叫做“无监督分类”,其目的是将数据划分成有意义或有用的组(或簇)。这种划分可以基于我们的业务需求或建模需求来完成,也可以单纯地帮助我们探索数据的自然结构和分布。比如在商业中,如果我们手头有大量的当前和潜在客户的信息,我们可以使用聚类将客户划分为若干组,以便进一步分析和开展营销活动,最有名的客户价值判断模型RFM,就常常和聚类分析共同使用。再比如,聚类可以用于降维和矢量量化(vectorquantization),可以将高维特征压缩到一列当中,常常用于图像,声音,视频等非结构化数据,可以大幅度压缩数据量。
在这里插入图片描述

聚类 分类
核心 将数据分为多个组,探索每个组之间是否有联系 从已经分组的数据中去学习,把新数据放到已经分好的组中去
学习类型 无监督,无需标签进行训练 有监督,需要标签进行训练
典型算法 K-Means,DBSCAN,层次聚类,光谱聚类 决策树,贝叶斯,逻辑回归
算法输出 聚类结果是不确定的。不一定总是能够反映数据的真实分类,同样的聚类,根据不同的业务需求可能是一个好结果,也可能是一个坏结果 分类结果是确定的,分类的优劣是客观的,不是根据业务或算法需求确定

sklearn中的聚类算法

聚类算法在sklearn中有两种表现形式,一种是类(和我们目前为止学过的分类算法以及数据预处理方法们都一样),需要实例化,训练并使用接口和属性来调用结果。另一种是函数(function),只需要输入特征矩阵和超参数,即可返回聚类的结果和各种指标。

在这里插入图片描述

需要注意的一件重要事情是,该模块中实现的算法可以采用不同类型的矩阵作为输入。 所有方法都接受形状[n_samples,n_features]的标准特征矩阵,这些可以从sklearn.feature_extraction模块中的类中获得。对于亲和力传播,光谱聚类和DBSCAN,还可以输入形状[n_samples,n_samples]的相似性矩阵,我们可以使用sklearn.metrics.pairwise模块中的函数来获取相似性矩阵。

KMeans

1. KMeans是如何工作的

1. 簇和质心

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

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

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

顺序 过程
1 随机抽取K个样本作为最初的质心
2 开始循环
2.1 将每个样本分配到离他们最近的质心,生成K个簇
2.2 对于每个簇,计算所有被分到该簇的样本点的平均值作为新的簇心
3 当质心的位置不再发生变化,迭代停止,聚类完成。

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

簇内误差平方和的定义和解惑

聚类算法聚出的类有什么含义呢?这些类有什么样的性质?我们认为,被分在同一个簇中的数据是有相似性的,而不同簇中的数据是不同的,当聚类完毕之后,我们就要分别去研究每个簇中的样本都有什么样的性质,从而根据业务需求制定不同的商业或者科技策略。这个听上去和我们在上周的评分卡案例中讲解的“分箱”概念有些类似,即我们分箱的目的是希望,一个箱内的人有着相似的信用风险,而不同箱的人的信用风险差异巨大,以此来区别不同信用度的人,因此我们追求“组内差异小,组间差异大”。聚类算法也是同样的目的,我们追求“簇内差异小,簇外差异大”。而这个“差异“,由样本点到其所在簇的质心的距离来衡量。对于一个簇来说,所有样本点到质心的距离之和越小,我们就认为这个簇中的样本越相似,簇内差异就越小。而距离的衡量方法有多种,令 表示簇中的一个样本点, 表示该簇中的质心,n表示每个样本点中的特征数目,i表示组成点 的每个特征,则该样本点到质心的距离可以由以下距离来度量:

在这里插入图片描述

如果我们采用欧几里得距离

在这里插入图片描述

其中,m为一个簇中样本的个数,j是每个样本的编号。这个公式被称为簇内平方和(cluster Sum of Square),又叫做Inertia。而将一个数据集中的所有簇的簇内平方和相加,就得到了整体平方和(Total Cluster Sum of Square),又叫做total inertia。Total Inertia越小,代表着每个簇内样本越相似,聚类的效果就越好。因此KMeans追求的是,求解能够让Inertia最小化的质心。实际上,在质心不断变化不断迭代的过程中,总体平方和是越来越小的。我们可以使用数学来证明,当整体平方和最小的时候,质心就不再发生变化了。如此,K-Means的求解过程,就变成了一个最优化问题。

在这里插入图片描述

KMeans有损失函数吗

记得我们在逻辑回归中曾有这样的结论:损失函数本质是用来衡量模型的拟合效果的,只有有着求解参数需求的算法,才会有损失函数。Kmeans不求解什么参数,它的模型本质也没有在拟合数据,而是在对数据进行一种探索。所以如果你去问大多数数据挖掘工程师,甚至是算法工程师,他们可能会告诉你说,K-Means不存在什么损失函数,Inertia更像是Kmeans的模型评估指标,而非损失函数。

但我们类比过了Kmeans中的Inertia和逻辑回归中的损失函数的功能,我们发现它们确实非常相似。所以,从“求解模型中的某种信息,用于后续模型的使用“这样的功能来看,我们可以认为Inertia是Kmeans中的损失函数,虽然这种说法并不严谨。

对比来看,在决策树中,我们有衡量分类效果的指标准确度accuracy,准确度所对应的损失叫做泛化误差,但我们不能通过最小化泛化误差来求解某个模型中需要的信息,我们只是希望模型的效果上表现出来的泛化误差很小。因此决策树,KNN等算法,是绝对没有损失函数的。

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

距离度量 质心 Interia
欧几里得距离 均值 最小化每个样本点到质心的欧式距离之和
曼哈顿距离 中位数 最小化每个样本点到质心的曼哈顿距离之和
余弦距离 均值 最小化每个样本点到质心的余弦距离之和

而这些组合,都可以由严格的数学证明来推导。在sklearn当中,我们无法选择使用的距离,只能使用欧式距离。因此,我们也无需去担忧这些距离所搭配的质心选择是如何得来的了。

2. KMeans算法的时间复杂度

除了模型本身的效果之外,我们还使用另一种角度来度量算法:算法复杂度。算法的复杂度分为时间复杂度和空间复杂度,时间复杂度是指执行算法所需要的计算工作量,常用大O符号表述;而空间复杂度是指执行这个算法所需要的内存空间。如果一个算法的效果很好,但需要的时间复杂度和空间复杂度都很大,那我们将会权衡算法的效果和所需的计算成本之间,比如我们在降维算法和特征工程那两章中,我们尝试了一个很大的数据集下KNN和随机森林所需的运行时间,以此来表明我们降维的目的和决心。

和KNN一样,KMeans算法是一个计算成本很大的算法。在这里,我们介绍KMeans算法的时间和空间复杂度来加深对KMeans的理解。

KMeans算法的平均复杂度是O(k x n x T),其中k是我们的超参数,所需要输入的簇数,n是整个数据集中的样本量,T是所需要的迭代次数(相对的,KNN的平均复杂度是O(n))。在最坏的情况下,KMeans的复杂度可以写作O(n1),其中n是整个数据集中的样本量,p是特征总数。这个最高复杂度是由D. Arthur和S. Vassilvitskii在2006年发表的论文”k-means方法有多慢?“中提出的。在实践中,比起其他聚类算法,k-means算法已经快了,但它一般找到Inertia的局部最小值。 这就是为什么多次重启它会很有用。

3. Sklearn.cluster.KMeans

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=None, algorithm=’auto’)
3.1 重要参数n_clusters

n_clusters是KMeans中的k,表示着我们告诉模型我们要分几类。**这是KMeans当中唯一一个必填的参数,默认为8类,**但通常我们的聚类结果会是一个小于8的结果。通常,在开始聚类之前,我们并不知道n_clusters究竟是多少,因此我们要对它进行探索。

3.2 先进行一次聚类

当我们拿到一个数据集,如果可能的话,我们希望能够通过绘图先观察一下这个数据集的数据分布,以此来为我们聚类时输入的n_clusters做一个参考。

首先,我们来自己创建一个数据集。这样的数据集是我们自己创建,所以是有标签的。

from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# 自己创建数据集
# 因为我们想画图。所以特征设为了2.人眼能看到的最多为3维
X, y = make_blobs(n_samples=500, n_features=2, centers=4, random_state=1)
print(X.shape)  # (500, 2)
# plt.subplots(1)表示生成一个子图
# fig表示画布,是一个方框
fig, ax1 = plt.subplots(1)
 # 表示每个点的坐标,坐标是[-6.92324165e+00 -1.06695320e+01]。
# X[0, 0]表示-6.923241645991934,X[0,1]表示-1.06695320e+01
ax1.scatter(X[:, 0], X[:, 1]
            , marker='o'  # 点的形状
            , s=8  # 点的大小
            )
plt.show()
# 如果我们想要看见这个点的分布,怎么办?
color = ["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=color[i]
                )
plt.show()
  • 我们有了数据之后,我们来猜测数据集的簇
from sklearn.cluster import KMeans

n_clusters = 3  # 我们先指定为3簇
cluster = KMeans(n_clusters=n_clusters, random_state=0).fit(X)
# 不用predict,可以直接使用cluster.labels来展示分好的簇
y_pred = cluster.labels_
print(y_pred)

  • 当数据量比较大的时候,用predict
# 也可以用以下方法。
pre = cluster.fit_predict(X)
print(pre)
print(pre == y_pred)
cluster_smallsub = KMeans(n_clusters=n_clusters, random_state=0).fit(X[:200]) #从500个样本选择200个来训练
y_pred_ = cluster_smallsub.predict(X)
y_pred == y_pred_  # 部分相等,部分不等
  • 返回质心坐标
centroid = cluster.cluster_centers_
print(centroid)
# [[-7.09306648 -8.10994454]
#  [-1.54234022  4.43517599]
#  [-8.0862351  -3.5179868 ]]
print(centroid.shape)  # (3, 2)
  • 查看总距离平方和
# 查看总距离平方和
inertia = cluster.inertia_
print(inertia) #1903.4503741659241
  • 可视化
# 对聚出来的类进行可视化
color = ["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=color
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值