K-means聚类原理实现 优缺点 轮廓系数 调优Canopy算法

  Kmeans聚类是我最早接触的,也是最简单的机器学习聚类算法了。它是一种无监督学习算法。

原理:

  还是先简单过一下Kmeans聚类的过程吧。不像模像样地罗列个一二三四,直接口述一下过程就好:比如我们要对二维坐标下一堆散列的点进行Kmeans聚类,我们要提前指定K的值,然后随机选择k个点作为k个聚类中心。此时我们计算其他所有的点和这k个点的距离,这些点离哪个聚类中心近就被归为哪一类;当所有点都被归好类后,我们重新计算每个类的中心。然后再重复上面的操作(根据中心归类,计算新中心,根据中心归类,计算新中心……)当聚类中心不再发生变化时(或者是一些其他停止条件)我们的聚类就结束了。

Kmeans优缺点:

优点:Kmeans的优点就在于它原理简单,好实现,并且聚类效果很不错。

缺点:

1.它需要人为指定K值,有时我们并不知道该把数据归为几类合适。

2.Kmeans聚类对聚类中心的初始化是随机的,但初始点位置对聚类效率和效果的影响是不小的。

3.Kmeans聚类采用的是欧氏距离计算,对数据集的要求比较高,并且如果有噪声点、异常点,将很容易造成中心点的偏移,对数据很敏感。

Kmeans实现

下面是我自己简单实现的对随机生成的二维坐标平面内的点进行Kmeans聚类代码,和上次的密度聚类类似,将数据点分为3类并进行聚类效果展示。

from matplotlib import pyplot as plt
plt.figure()
import random
def distance(x1,y1,x2,y2): #计算两点间距离的函数 为了减少运算消耗没有加开根号的步骤
    return (x1-x2)**2+(y1-y2)**2
dot_list=[] #用于存放所有点的列表
def find_center(c): #找到第c类的中心位置的函数
    x_sum = 0
    y_sum = 0
    num = 1 #防止出现被除数为0的情况
    for dot in dot_list:
        if dot._class==c:
            x_sum+=dot.x
            y_sum+=dot.y
            num+=1
    x_center = x_sum/num
    y_center = y_sum/num
    return x_center,y_center
    
class Dot():#定义一个点类
    def __init__(self,x,y):
        self.x = x
        self.y = y
        self._class = None
for i in range(500):    
    x = random.randint(1,200)
    y = random.randint(1,200)
    dot_list.append(Dot(x,y))
    plt.plot(x,y,'d',color = 'blue')
x1=random.randint(1,200) #随机生成一开始的3个中心
y1=random.randint(1,200)
x2=random.randint(1,200)
y2=random.randint(1,200)
x3=random.randint(1,200)
y3=random.randint(1,200)
last_x1 = 0 #给last_x1初始化值
last_y1 = 0
while ((last_x1!=x1)|(last_y1!=y1)): #当第一类一直再更新,只要有一个在变,则一直循环迭代
    last_x1 = x1
    last_y1 = y1
    for dot in dot_list:
        distance1=distance(dot.x,dot.y,x1,y1)
        distance2=distance(dot.x,dot.y,x2,y2)
        distance3=distance(dot.x,dot.y,x3,y3)
        if distance1<min(distance2,distance3):
            dot._class = 1
        elif distance2<min(distance1,distance3):
            dot._class = 2
        else:
            dot._class = 3
        x1,y1 = find_center(1)
        x2,y2 = find_center(2)
        x3,y3 = find_center(3)   
#画出最终的三个中心  对应结果中三个红色的点
plt.plot(x1,y1,'o',color = 'red')
plt.plot(x2,y2,'o',color = 'red')
plt.plot(x3,y3,'o',color = 'red')
#给不同类别定义不同的样式和颜色
for dot in dot_list:
    if dot._class==1:
        plt.plot(dot.x,dot.y,'x',color = 'blue')
    elif dot._class==2:
        plt.plot(dot.x,dot.y,'d',color = 'green')
    else:
        plt.plot(dot.x,dot.y,'^',color = 'black')
plt.show()

运行结果:

聚类效果评估:

  我们一般用轮廓系数来衡量聚类的效果。轮廓系数的主要思想就是希望聚类的结果类内距离尽可能小(一个类里更加凝聚),类间距离尽可能大(不同类间更加分离)。每个样本点对应的轮廓系数S(i)的公式为:

 其中,a(i)是样本点i到同类别其他点的平均距离,b(i)是样本点i到与它最近的类别中所有点的平均距离。

  将所有样本点的轮廓系数S(i)求平均,就会得到总轮廓系数S。

  在最理想的情况下,分子无限接近于b,分母为b,轮廓系数值为1,聚类效果最好;再最坏的情况下,分子接近于-a,分母为a,轮廓系数为-1,聚类效果最差。(其实,如果轮廓系数为-1了,等于根本没有聚类,完全混沌,无法想象)

Kmeans调优:

关于Kmeans有一些优化点可以尝试。

1.因为Kmeans中是使用的欧氏距离来计算距离的,所以在使用Kmeans前一定要对数据点的度量做统一、归一化等处理。

2.Kmeans++

  Kmeans++主要是为了解决Kmeans初始化聚类中心太过随意的问题。因为假如一开始初始化了几个相距非常近的聚类中心点,那么会耗费大量的迭代次数和计算资源;合理地初始化聚类中心,将达到事半功倍的效果。Kmeans++的核心思想就是初始化聚类中心,使得彼此两两相距最远。基本实现思路就是每选择一个新的聚类中心时,都要和现有的聚类中心进行距离计算比较,尽量选出较远的那个(其实这里是将距离转化为概率,距离越大,选择为新中心点的概率就越大)。

3.Canopy算法

  Canopy算法是为了解决Kmeans需要人为指定K值的问题。因为有时如果数据点应该是分成三类为佳,我们却将K值指定为4,那么分类效果就不会很好。当然,如果我们必须要将数据分成4类,那把K人为指定为4是没问题的,但如果只是为了将数据“自适应”地聚类,就可以考虑使用Canopy算法来帮我们找到合适的K值。

  其实Canopy属于一种‘粗’聚类算法,即使用一种简单、快捷的距离计算方法将数据集分为若干可重叠的子集canopy,Canopy算法步骤如下:

1.将原始样本集看成集合D,根据先验知识或交叉验证调参设定初始距离阈值T1、T2,且T1>T2。

2.从集合D中随机选取一个样本P作为第一个canopy的质心,并将P从集合中删除。

3.从集合D中随机选取一个样本Q,计算Q到所有质心的距离,考察其中最小的距离d:

3.1.如果d≤T1,则给Q一个弱标记,表示Q属于该canopy,并将Q加入其中;

3.2.如果d≤T2,则给Q一个强标记,表示Q属于该canopy,且和质心非常接近,所以将该canopy的质心设为所有强标记样本的中心位置,并将Q从集合D中删除;

3.3.如果d>T1,则Q形成一个新的聚簇,并将Q从集合D中删除。

4.重复第3步直到集合D中元素个数为零。

  最终,我们通过Canopy算法进行粗聚类得到k个质心,再使用K-means算法进行聚类。

小结:

  这篇文章简单整理了Kmeans聚类的原理,实现以及优缺点,也包括聚类效果评估以及Canopy调优等方面。

猜你喜欢

转载自blog.csdn.net/weixin_44492824/article/details/125225497