【机器学习17】聚类K-MEANS和DBSCAN算法详解


手动反爬虫: 原博地址

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

如若转载,请标明出处,谢谢!

前言

前面已经介绍了决策树模型集成学习模型等内容,无论使用它两用来做回归还是分类任务都是有一个预定对比的y值(也就是标签),如果数据中没有这个标签了,只是一群离散的值,该怎么处理呢?这就涉及到本次梳理的内容了,也就是聚类问题(属于无监督,没有预定对比的条件了),最终目的要是把相似的东西分到一组,主要的难点在于如何对创建的模型进行评估和参数调整,因为没有y了,之前的那些评估的方法也自然就不适用了,本次梳理将详细地介绍相关的知识并进行代码辅助理解。
在这里插入图片描述

主要介绍两种聚类算法:K-MEANS和DBSCAN算法

一、K-MEANS算法

1.基本流程

基础的概念:物以类聚、人以群分,就是将数据按照一定的流程分成k组,那么具体的流程如何呢?为了方便理解,先进行图示,然后进行举例说明

图解示例如下:

在这里插入图片描述

具体流程举例说明:(这里假定k=2,分为两组)

  • (a)首先输入k的值,即我们希望将数据集经过聚类得到k个分组;
  • (b)从数据集中随机选择k个数据点作为初始大哥(做大哥的依据就是:质心,向量各维取平均);
  • (c)对集合中每一个小弟,计算与每一个大哥的距离(欧氏距离或者余弦相似度,需要先标准化),离哪个大哥距离近,就跟定哪个大哥。
  • (d)这时每一个大哥手下都聚集了一票小弟,这时候召开会议,每一群选出新的大哥(新大哥会有选举标准)。
  • (e)如果新大哥和老大哥之间的距离小于某一个设置的阈值(表示重新计算的质心的位置变化不大,趋于稳定,或者说收敛),可以认为我们进行的聚类已经达到期望的结果,算法终止。
  • (f)如果新大哥和老大哥距离变化很大,需要迭代3~5步骤。

2.优缺点

优点可以从流程中看出:简单、快速、适合一些常规的数据集

缺点就是k的值是人工设定,如何能取到最优的值很难确定;
聚类的复杂度是和样本数量成线性关系(样本数量越多,计算的也就越多);
最后一点就是 很难发现任意形状的簇

为了解释最后一个缺点,这里不进行代码可视化的演示,选择使用一个在线的K-MEANS可视化的网页进行展示,只需要动动鼠标点击即可网页打开如下:(这里点击中间的Randomly,进入数据挑选,然后选择Gaussian Mixture数据)
在这里插入图片描述
就会进入设定界面,restart是重置按钮,add centroids就是第二步中的质心,如果希望分成几份,就点击这个按钮几次,很明显这个数据是有三组,就可以点击三次。整个可视化的操作过程如下:(注意留意第二次的操作,初始点的选择会导致最后的聚类结果发生错误,也就是K-MEANS对于初始质心的选择不好会导致整个模型垮掉)
在这里插入图片描述
然后使用其他的数据说明一下最后的一个缺点,就是我们觉得应该可以聚类成几种,但是K-MEANS却不懂我们的心,就拿笑脸数据为例,看上去应该是聚类为4簇,但是当选择4个质心值,系统返回的并不是我们想要的结果,类似这样的数据还有很多很多,网页上提供的还有其他的数据,可以没事玩一玩
在这里插入图片描述

二、DBSCAN算法

根据上面可视化展示的操作,可以发现K-MEANS虽然很简单,特别容易上手,但是也存在着不少的缺点,因此就有改进的算法或者更加优异的算法,这里的DBSCAN算法就是一种。

1.可视化流程

该算法的全称:Density-Based Spatial Clustering of Applications with Noise (只要知道很牛B就行了),暂不介绍其中的一些基础定义和参数,先在DBSCAN可视化的网页中玩一玩游戏看看这个算法是怎么工作的,还是以Gaussian Mixture数据集进行聚类,可视化的操作如下:
在这里插入图片描述
可以发现在演示中左下方有两个参数分别是epsilon和minPoints,而下面的左右拖动条分别就代表着这两个参数,往左调整参数值变小,往右调整,参数变大。整个过程就像是领土扩张一样,一步一步的进行下去(也可以理解为传销,一个一个的发展,然后形成一个阵地,然后这个阵地开拓完了之后进入下一个地区进行活动,直至最后没有发展的成员了),而在扩张的过程中,调大epsilon参数可以明显感觉到扩张的速度提升了,而增大minPoints值,并没有很明显的感觉速度加快的意思,反而还速度减缓了。

以上就是玩游戏时候的留意的一些事项,为了方便理解这里直接看图说话(图源来自百度百科)
在这里插入图片描述
首先有个指定的epsilon(圆的半径)和minPoints(区域内包含的最少的点数),比如上图中给定的半径是r(统一的大小就不用管长度的了),那么最小的点数设置为3,根据DBSCAN算法就会开始搜索,假定由A点开始的,画个半径为r的圆后,加上自身的这个点共四个,也就满足了在规定的领域内点的数量不小于minPoints的值,因此就可以将这四个归为一类

然后就开始以另外三个点为核心进行半径r画圆,如果区域内不少于3个点,就继续归为一类(都是用红色的圆圈起来),直到最后不满足,比如传递到B和C两点时候,仍然还是继续画圆,可见该圆内只有两个点(黄色圆中包含的点),那这两个点就是属于这一类别中的非核心点(对比传销更好理解一些,也就是ta没有发展下限的能力了,那ta就属于这个群体中最边缘的人员了,不可能占据核心的位置),最后就是N点,蓝色的圆圈内除了自己没有任何点,这就没有办法归到任意一类了,说明这个点本身就有问题的,没有人愿意和ta玩,就算传销也看不上ta,所以这点就属于异常值,这里称作为噪声点

2.术语解释

以上就是关于DBSCAN算法流程介绍了,那接下来就是枯燥的基础知识的理论和相关的参数概念性的解释了,其中核心的一些术语汇总一下:

  • 核心对象:若某个点的密度达到算法设定的阈值则其为核心点(上面红色的点)
  • ϵ-邻域的距离阈值: 设定的半径r
  • 直接密度可达:某点p在点q的 r 邻域内,且q是核心点则p-q直接密度可达(就是两个点都在同一个圆内)
  • 密度可达:对于样本集合D,给定一串样本点p_{1},p_{2}….p_{n},p= p_{1},q= p_{n},假如对象p_{i}从p_{i-1}直接密度可达,那么对象q从对象p密度可达。(说人话就是C左侧的一个红点可以与B右侧的一个红点属于同类)
  • 边界点:属于某一个类的非核心点,不能画圆了(上面的BC点)
  • 噪声点(离群点):不属于任何一个类簇的点,从任何一个核心点出发都是密度不可达的(说人话就是没有人和ta玩,图中的N点)

3.优缺点

优点缺点
改变两个参数即可参数精准选择还是比较困难(主要由于参数对结果的影响非常大)
不需要指定簇的个数,可以发现任意形状的簇高维数据有些困难(但是可以做降维)
擅长找到离群点,进行检测任务Sklearn中的效率不高

在这里插入图片描述

三、K-MEANS常用操作

可以参考一下官方关于这部分的文档参数的讲解:sklearn.cluster.KMeans,官方给的示例较为简单,如下:
在这里插入图片描述
上面的示例方便理解这个K-MEANS的运行,接下来进行深度的解析,首先进行数据的创建,就不再是简单的几个点数据了,而是指定几个质心,让数据围绕这个这几个质心进行分布,这样就方便进行聚类的展示

import warnings
warnings.filterwarnings('ignore')
import numpy as np
np.random.seed(42)
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt

#导入创建聚类数据的模块
from sklearn.datasets import make_blobs
#指定质心坐标
blob_centers = np.array(
    [[0.2,2.3],
     [-1.5,2.3],
     [-2.8,1.8],
     [-2.8,2.8],
     [-2.8,1.3]])
     
#指定数据在质心周围浮动的范围
blob_std =np.array([0.4,0.3,0.1,0.1,0.1]) 
#按照要求共生成2000个点
X,y = make_blobs(n_samples=2000,centers=blob_centers,
                     cluster_std = blob_std,random_state=7)
#绘制聚类数据图
def plot_clusters(X, y=None):
    plt.scatter(X[:, 0], X[:, 1],s = 4)
    plt.xlabel("$x_1$", fontsize=14)
    plt.ylabel("$x_2$", fontsize=14, rotation=0)
plt.figure(figsize=(8, 4))
plot_clusters(X)
plt.show()

输出结果为:(make_blobs模块的使用可以参考官网,用于生成聚类的数据)
在这里插入图片描述
接下来就是要使用K-MEANS算法进行数据的聚类操作了,由下面的输出可以看到kmeans.fit_predict(X)kmeans.labels_ 得到的预测结果是一样的,可以通过kmeans.cluster_centers_方法查看聚类后质心的坐标,最后的预测也和之前的其他模型使用一样,传入数据后kmeans.predict一下就可以了
在这里插入图片描述
还有一个有用的方法,由于K-MEANS算法是基于距离实现的聚类,那么能不能根据我给出的预测点,直接求解出该点到各个质心之间的距离呢?kmeans.transform(X)就是完成这个工作的(Transform X to a cluster-distance space.),可以对比一下每行中最小值的索引与上面预测的结果之间的对应关系
在这里插入图片描述

四、聚类结果展示

这里不再使用之前的可视化网页进行展示,而是在jupyter notebook中使用代码进行聚类结果的展示,展示质心和决策边界代码如下:

#显示数据分布的函数
def plot_data(X):
    plt.plot(X[:, 0], X[:, 1], 'k.', markersize=2)

#绘制质心显示的韩式
def plot_centroids(centroids, weights=None, circle_color='w', cross_color='k'):
	#这一步就是根据权重调整质心
    if weights is not None:
        centroids = centroids[weights > weights.max() / 10]
    #两个散点图就是绘制成了圆圈里面有个×的样子
    plt.scatter(centroids[:, 0], centroids[:, 1],
                marker='o', s=30, linewidths=8,
                color=circle_color, zorder=10, alpha=0.9)
    plt.scatter(centroids[:, 0], centroids[:, 1],
                marker='x', s=50, linewidths=50,
                color=cross_color, zorder=11, alpha=1)
                
#绘制决策边界显示,这个在之前绘制决策树的决策边界的时候原理一样
def plot_decision_boundaries(clusterer, X, resolution=1000, show_centroids=True,
                             show_xlabels=True, show_ylabels=True):
    mins = X.min(axis=0) - 0.1
    maxs = X.max(axis=0) + 0.1
    xx, yy = np.meshgrid(np.linspace(mins[0], maxs[0], resolution),
                         np.linspace(mins[1], maxs[1], resolution))
    Z = clusterer.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    plt.contourf(Z, extent=(mins[0], maxs[0], mins[1], maxs[1]),
                cmap="Pastel2")
    plt.contour(Z, extent=(mins[0], maxs[0], mins[1], maxs[1]),
                linewidths=1, colors='k')
    #在棋盘上绘制所有的数据            
    plot_data(X)
    #控制是否绘制质心
    if show_centroids:
        plot_centroids(clusterer.cluster_centers_)
	#控制是否显示x,y轴标签
    if show_xlabels:
        plt.xlabel("$x_1$", fontsize=14)
    else:
        plt.tick_params(labelbottom='off')
    if show_ylabels:
        plt.ylabel("$x_2$", fontsize=14, rotation=0)
    else:
        plt.tick_params(labelleft='off')

#设置画布后调用函数绘制图形即可
plt.figure(figsize=(8, 4))
plot_decision_boundaries(kmeans, X)
plt.show()

输出的结果为:(至此就完成了质心和决策边界的绘制)
在这里插入图片描述

五、K-MEANS算法流程

通过代码看一下这个算法到底是怎么工作的,首先需要了解一下实例化时候Kmeans中的参数,如下,

  • n_clusters:默认是8,这里设置的聚类数据是5个,所以之后会设置为5,;
  • init:是初始化位置,可以自己指定也可以随机分配;
  • n_init:值会选择10次中效果最好的那个值(由于Kmeans每次运行结果可能都不一样,可以回顾一下最开始玩的那个可视化网页的游戏,第二次指定的初始值最后的聚类是很令人失望的),因此再创建Kmeans实例的时候就已经是默认在10次选择中找到最佳的质心,相当于把Kmeans跑了10次;
  • max_iter:迭代次数,更新完质心之后,会判断是否符合要求,如果不符合就需要进行迭代,这里就是设置最大的迭代次数
    在这里插入图片描述

因此要实现K-MEANS算法流程,那么就要控制一下 max_iter 参数,才能将每一步的流程展现出来,由于之前已经封装了绘图函数,接下来只要创建几个Kmeans实例运行之后就可以绘图了,代码如下

#init如果自己指定的话就显得有点麻烦,这里直接使用随机初始化,n_init这里跑一次就行,不需要跑10次了,就是要看它每一步的流程,所以设置为1,
#然后就是迭代次数了,每次都递增+1,这样就可以显示出具体的流程
kmeans_iter1 = KMeans(n_clusters = 5,init = 'random',n_init = 1,max_iter=1,random_state=1)
kmeans_iter2 = KMeans(n_clusters = 5,init = 'random',n_init = 1,max_iter=2,random_state=1)
kmeans_iter3 = KMeans(n_clusters = 5,init = 'random',n_init = 1,max_iter=3,random_state=1)
kmeans_iter4 = KMeans(n_clusters = 5,init = 'random',n_init = 1,max_iter=4,random_state=1)

kmeans_iter1.fit(X)
kmeans_iter2.fit(X)
kmeans_iter3.fit(X)
kmeans_iter4.fit(X)

#接下来就是分别绘制子图了,后面也可以使用for循环遍历,这里直接就代码复制改了数值
plt.figure(figsize=(12,8))
plt.subplot(421)
plot_data(X)
plot_centroids(kmeans_iter1.cluster_centers_, circle_color='r', cross_color='k')
plt.title('Update cluster_centers')

plt.subplot(422)
plot_decision_boundaries(kmeans_iter1, X,show_xlabels=False, show_ylabels=False)
plt.title('Label')

plt.subplot(423)
plot_decision_boundaries(kmeans_iter1, X,show_xlabels=False, show_ylabels=False)
plot_centroids(kmeans_iter2.cluster_centers_,)

plt.subplot(424)
plot_decision_boundaries(kmeans_iter2, X,show_xlabels=False, show_ylabels=False)

plt.subplot(425)
plot_decision_boundaries(kmeans_iter2, X,show_xlabels=False, show_ylabels=False)
plot_centroids(kmeans_iter3.cluster_centers_,)

plt.subplot(426)
plot_decision_boundaries(kmeans_iter3, X,show_xlabels=False, show_ylabels=False)

plt.subplot(427)
plot_decision_boundaries(kmeans_iter4, X,show_xlabels=False, show_ylabels=False)
plot_centroids(kmeans_iter4.cluster_centers_,)

plt.subplot(428)
plot_decision_boundaries(kmeans_iter4, X,show_xlabels=False, show_ylabels=False)

plt.show()

输出结果为:(左侧就是每一步更新质心的过程,右侧就是为更新聚类边界的过程,可以发现第4迭代后,质心就没有在发生变化了,导致更新边界的图形与更新质心的图形一致了)
在这里插入图片描述

六、不稳定结果

由之前玩可视化网页游戏可知,随机初始化的质心会导致聚类结果发生偏差,也就是每次聚类的结果会不确定,那么如何在代码中实现这种不确定性的结果呢,就需要用到对照试验,分别创建两个Kmeans实例,看看结果如何

#封装个对比实验函数
def plot_clusterer_comparison(c1,c2,X):
    c1.fit(X)
    c2.fit(X)
    
    plt.figure(figsize=(12,4))
    plt.subplot(121)
    plot_decision_boundaries(c1,X)
    plt.subplot(122)
    plot_decision_boundaries(c2,X)

采用不同的随机种子进行Kmeans实例化,然后进行对照,可以发现虽然两个种子状态相差为1,但是有时候聚类分界线结果是一致的(肉眼观看的),有的时候却相差甚远,这也就在代码中证明了采用K-MEANS算法聚类的结果是有不稳定性的,因此在之后的实际操作中关于n_init的值,这里就不能设置为1,可以选择默认值,让模型从10次随机值中选取最佳的初始值。那么问题就来了,如何确定最佳的初始值?评估的标准???
在这里插入图片描述

七、评估方法

既然有不稳定的结果,想要让其稳定,我们就要采取措施迫使其乖乖投降,好好听我们的话,于是就有了评估方法的出现,可以通过之前的n_init参数的介绍时,里面提到了inertia,这个指标就是用来评估每次选取的随机质心的好坏,其代表的是 每个样本与对应质心的距离
在这里插入图片描述
看一下具体关于这个参数的介绍,在实例化进行模型训练之后,会有inertia_属性代表着就是每个样本与对应质心的距离平方和的累加值
在这里插入图片描述
就拿最初实现Kmeans的实例来说,其中是没有设置n_init这个参数的,因此就默认实现了最佳距离的寻找,之前介绍了可以直接获取某点与各质心之间的距离,因为每个质心的距离都存在,只要找到最小的那个就可以了,这里可以利用实例化自动生成的标签labels_进行判定某点其对应的质心,然后索引获得数据之后平方和累加就获得了最终的结果了,其实现的基本原理如下
在这里插入图片描述
那么也可以看一下刚刚进行对比实验的聚类模型的效果如何,果然c1的聚类结果最差其对应的inertia值也就是最高的,而c3和c4肉眼可见是一致的,但是体现在数值量化上两者还是有细微的差别,同时也把进行默认情况下聚类获得的inertia值进行对比,为了突出今后在进行聚类的时候n_init参数一定要设置的大一些,默认也可以,但是不要设置为1。
在这里插入图片描述
最后这里再介绍一下老外的思维,跟我们反着来的,认为是负的更容易理解一些,所以还有一个方法进行评估,就是实例化模型后直接使用kmeans.score(X)即可得出模型的inertia的值,只不过是负的
在这里插入图片描述

八、寻找最“合适”K值

在K-MEANS算法中,最重要的就是这个k值的选定了,给定的数值就决定了最后聚类的簇数,根据上面讲到的评估方法是否就可以选择最小inertia值对应的k值呢?答案是否定的,显然只要是增加k的值,质心的个数就会增加,那么聚类的簇就会很多,每簇数据点到质心的距离也就会很小,得到的最后inertia值也就会越小,但是聚类的效果不一定是越来越好的

有一个巧妙的方式,可以借鉴一下,就是绘制一系列的不同k值的图像,根据图像选择拐点处附近的数值作为k值的参考,代码如下:

#直接采用1-9创建9个聚类实例
kmeans_per_k = [KMeans(n_clusters = k).fit(X) for k in range(1,10)]
#分别获得其中inertia值
inertias = [model.inertia_ for model in kmeans_per_k]

#绘制inertia值随着k值变化的图形
plt.figure(figsize=(8,4))
plt.plot(range(1,10),inertias,'bo-')
plt.show()

输出的结果为:(可以发现在绘制的图像中存在k=2和k=4时图像的下降率发生剧烈变化,两者都可以考虑作为适合的k值进行考虑,然后对照一下之前生成的聚类数据,最初设置的就是5个质心,因此这里应该选择的就是k=4附近的点作为合适的值,只能说是附近,不能认为这个拐点就是最合适的值,所以这个最 “合适” 要加引号)
在这里插入图片描述
以上就是通过绘制inertia值随着k值变化的曲线根据拐点进行合适的k值选择,最终拐点的值只能作为参考,接下来介绍另外一种寻找k值的参考方式

九、轮廓系数

首先提供两个值及对应的概念,如下

  • a i a_{i} ai: 计算样本 i i i到同簇其他样本的平均距离 a i a_{i} ai a i a_{i} ai 越小,说明样本 i i i越应该被聚类到该簇。将 a i a_{i} ai称为样本i的簇内不相似度。
  • b i b_{i} bi: 计算样本 i i i到其他某簇 C j C_{j} Cj的所有样本的平均距离 b i j b_{ij} bij,称为样本 i i i与簇 C j C_{j} Cj的不相似度。定义为样本 i i i的簇间不相似度: b i = m i n ( b i 1 , b i 2 , . . . , b i k ) b_{i} =min (b_{i1}, b_{i2}, ..., b_{ik}) bi=min(bi1,bi2,...,bik)

列举出这两行文字,光从字面意思上理解的话,如果要想聚类的效果好的话, a i a_{i} ai越小, b i b_{i} bi越大。

那么问题就来了,既然这两个量之间是动态比较的,然后才决定最后聚类的好坏,那么就有一个谁占主导地位的时候,到底 a i a_{i} ai小到什么程度, b i b_{i} bi大到什么程度,才有话语权呢?最终也就需要有一个公式来衡量这量谁说的算,故有了 s ( i ) s(i) s(i),公式如下:
s ( i ) = b ( i ) − a ( i ) m a x ( a ( i ) , b ( i ) )           ⇒           s ( i ) = { 1 − a ( i ) b ( i ) , a ( i ) < b ( i ) 0 , a ( i ) = b ( i ) a ( i ) b ( i ) − 1 , a ( i ) > b ( i ) s(i)=\frac{b(i)-a(i)}{max(a(i),b(i))} \space \space \space \space \space \space \space\space \space \Rightarrow \space \space \space \space \space \space \space \space \space s(i)=\left\{ \begin{aligned} 1-\frac{a(i)}{b(i)} & , & a(i) <b(i)\\ 0 &, &a(i) =b(i) \\ \frac{a(i)}{b(i)} -1& , & a(i) >b(i) \end{aligned} \right. s(i)=max(a(i),b(i))b(i)a(i)                  s(i)=1b(i)a(i)0b(i)a(i)1,,,a(i)<b(i)a(i)=b(i)a(i)>b(i)结论: (可以假设 b i ⇒ 0 b_{i} \Rightarrow 0 bi0 a i ⇒ 0 a_{i} \Rightarrow 0 ai0进行理解)

  • s ( i ) s(i) s(i)接近1,则说明样本 i i i聚类合理;

  • s ( i ) s(i) s(i)接近-1,则说明样本 i i i更应该分类到另外的簇;

  • s ( i ) s(i) s(i) 近似为0,则说明样本 i i i在两个簇的边界上。

可以参考官网中关于silhouette_score参数的讲解,在代码中实现中看看效果,代码如下:

#首先直接导入这个模块,评价默认的kmeans模型
from sklearn.metrics import silhouette_score
silhouette_score(X,kmeans.labels_)
#输出的得分为:0.655517642572828

#之前训练的不同k值的实例这里直接拿过来用
silhouette_scores = [silhouette_score(X,model.labels_) for model in kmeans_per_k]

到这一步注意一个细节,如果直接执行的话,系统会提示报错如下,
在这里插入图片描述
这里就需要结合官方文档进行理解了,上面链接网页打开后第一段中就有介绍,说是这种评估的方式不适用于只用一个簇的聚类时,截图如下
在这里插入图片描述
所以选取不同k值的实例的时候应该从k=2开始取,也就对应数据从1开始切片,过滤掉第一个0索引对应的k=1的实例,代码如下

silhouette_scores = [silhouette_score(X,model.labels_) for model in kmeans_per_k[1:]]
silhouette_scores
#k取值1-9,现在又去掉k=1的情况,最后就只用8个结果了
#[0.5966442557582528,0.5723900247411775,0.688531617595759,
#0.655517642572828,0.601878677912387,0.6071325093726307,
#0.561411737095725,0.5661946395774896]

#绘制图表
plt.figure(figsize=(8,4))
plt.plot(range(2,10),silhouette_scores,'bo-')
plt.show()

输出结果为:(根据上面的分析 s ( i ) s(i) s(i)越接近1,聚类效果越好,对应的k值也是4)
在这里插入图片描述
既然两次评估的结果都是k=4,那么我们就绘制一下看看k=4时候究竟有多好,代码和出入如下,可以看出实际上我们是想聚类成5簇的,这里将左下角的两堆数据合并为1簇了,如果你接受这种结果也可以认为这两种方式评估聚类结果k=4是靠谱的,我这里认为是这两种方式的评估结果只能作为一个参考,至于最合适的K值需要自己根据需求在得到的这个“最合适”的k值附近进行取值
在这里插入图片描述

十、K-MEANS存在的问题

那么接下来增加一下数据的难度,也就意味着这里我可以进行作弊开挂,疯狂输出,重新生成一份用于聚类的数据

X1, y1 = make_blobs(n_samples=1000, centers=((4, -4), (0, 0)), random_state=42)
X1 = X1.dot(np.array([[0.374, 0.95], [0.732, 0.598]]))
X2, y2 = make_blobs(n_samples=250, centers=1, random_state=42)
X2 = X2 + [6, -8]
X = np.r_[X1, X2]
y = np.r_[y1, y2]

plot_data(X)

输出的结果为:(这里的具体数据,希望使用实例化的Kmeans可以聚类为3簇)
在这里插入图片描述
接下来就要开挂了,之前设置质心的时候是10次中选择效果最好的那个,这里我直接肉眼可见的选择三个质心,然后和系统自己只能选定的质心进行对比实验,看看能不能都分辨出,不用多说自然我开挂选择的三个质心一定会好于系统智能的选择,那么关键的就来了,期待着系统可不可以完成聚类3簇的目标,代码如下

#开挂代码,直接将肉眼所见的三个质心大致位置填在这个初始化参数中
kmeans_good = KMeans(n_clusters=3,init=np.array([[-1.5,2.5],[0.5,0],[4,0]]),n_init=1,random_state=42)
#接下来就让系统从10个里面慢慢找
kmeans_bad = KMeans(n_clusters=3,random_state=42)
kmeans_good.fit(X)
kmeans_bad.fit(X)

#最后就是分别绘制两者的聚类效果图
plt.figure(figsize = (10,4))
plt.subplot(121)
plot_decision_boundaries(kmeans_good,X)
plt.title('Good - inertia = {}'.format(kmeans_good.inertia_))

plt.subplot(122)
plot_decision_boundaries(kmeans_bad,X)
plt.title('Bad - inertia = {}'.format(kmeans_bad.inertia_))

输出的结果为:(期待得到了回答,事实证明玩游戏要想取的好成绩还是得开挂,而且这里也将inertia值进行输出,可以发现系统选择的聚类方案inertia值还低于我们开挂的方案,但是系统并没有取的我们这么好的成绩,再次证明了前面我认为的inertia值和轮廓系数两种评估方法取的最终结果只能用来做参考)
在这里插入图片描述
那么还有没有可能默认的10次中选择一次最好的质心作为输入,这个10次太少了,才导致系统没有聚类好呢?那么就实践一下,将n_init的值分别设定为100和1000再次运行,代码和结果如下,事实证明K-MEANS算法确实存在这个问题
在这里插入图片描述
故K-MEANS算法的评估的确是一项很难的工作,目前介绍的两种方式只能用作与参考,最终的聚类结果还是要根据自己的业务设置合理的参数,情况允许的条件下,开个挂玩玩游戏也是可以的

十一、DBSCAN算法实践

之前在可视化的网页中实现了DBSCAN算法聚类,那么接下来就在代码中实现一下,首先生成要处理的数据(两个月牙)

from sklearn.datasets import make_moons
X, y = make_moons(n_samples=1000, noise=0.05, random_state=42)
plt.plot(X[:,0],X[:,1],'b.')

输出的结果为:
在这里插入图片描述
开始进行对比实验,最开始这个半径的值不能给的太大,否则会影响判断,可以先从小的给,然后再增大

from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps = 0.05,min_samples=5)
dbscan.fit(X)

dbscan2 = DBSCAN(eps = 0.2,min_samples=5)
dbscan2.fit(X)

#查看前10个的标签,注意-1在dbscan中是离群点的意思
dbscan.labels_[:10]
#输出:array([ 0,  2, -1, -1,  1,  0,  0,  0,  2,  5], dtype=int64)

#查看核心样本应的索引,可以用来获取核心对象
dbscan.core_sample_indices_[:10]
#输出:array([ 0,  4,  5,  6,  7,  8, 10, 11, 12, 13], dtype=int64)

#还可以看一下,该算法运行后聚类了几簇,需要去掉-1,这里还是代表离群的那些数据
np.unique(dbscan.labels_)
#输出:array([-1,  0,  1,  2,  3,  4,  5,  6], dtype=int64)

#封装绘制函数,都和之前绘制图形的原理类似
def plot_dbscan(dbscan, X, size, show_xlabels=True, show_ylabels=True):
    core_mask = np.zeros_like(dbscan.labels_, dtype=bool)
    core_mask[dbscan.core_sample_indices_] = True #核心对象标记
    anomalies_mask = dbscan.labels_ == -1 #离群数据
    non_core_mask = ~(core_mask | anomalies_mask) #不是核心数据的

    cores = dbscan.components_  
    anomalies = X[anomalies_mask]
    non_cores = X[non_core_mask]
    
    plt.scatter(cores[:, 0], cores[:, 1],
                c=dbscan.labels_[core_mask], marker='o', s=size, cmap="Paired")
    plt.scatter(cores[:, 0], cores[:, 1], marker='*', s=20, c=dbscan.labels_[core_mask])
    plt.scatter(anomalies[:, 0], anomalies[:, 1],
                c="r", marker="x", s=100)
    plt.scatter(non_cores[:, 0], non_cores[:, 1], c=dbscan.labels_[non_core_mask], marker=".")
    if show_xlabels:
        plt.xlabel("$x_1$", fontsize=14)
    else:
        plt.tick_params(labelbottom='off')
    if show_ylabels:
        plt.ylabel("$x_2$", fontsize=14, rotation=0)
    else:
        plt.tick_params(labelleft='off')
    plt.title("eps={:.2f}, min_samples={}".format(dbscan.eps, dbscan.min_samples), fontsize=14)

#调用函数绘图
plt.figure(figsize=(9, 3.2))
plt.subplot(121)
plot_dbscan(dbscan, X, size=100)

plt.subplot(122)
plot_dbscan(dbscan2, X, size=600, show_ylabels=False)

plt.show()

输出结果为:(可以发现的是DBSCAN算法对半径的要求很严格,也正如最开始玩的网页游戏直观感受到的一样,调大半径,模型执行的速度较快,聚类的簇范围也变得较大)
在这里插入图片描述
有兴趣的话也可以调整一下其他的参数试一下,这里就不再进行测试了,还有就是如何判断“合适”的半径值,也是可以使用之前介绍的轮廓系数的,至此关于K-MEANS和DBSCAN算法讲解就到此结束了

总结

本文介绍了两种聚类的算法,为了方便认知,选择先从可视化展示的游戏开始,然后再结合图例进行讲解,最后辅助代码实践。最后建议可以使用分类完成的任务尽量使用分类,不到迫不得已再使用聚类算法完成,因为你要努力的自圆其说,聚类中的k值和半径的选择还是比较困难,调参时候要先把自己说服,整清楚,根据自己的业务需求来评估模型的效果。

  • 12
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lys_828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值