聚类算法:kmeans

Kmeans算法的原理

    K-means聚类属于原型聚类(基于原型的聚类,prototype-based clustering)。原型聚类算法假设聚类结构能够通过一组原型进行刻画,在现实聚类任务中极为常用。通常情况下,原型聚类算法对原型进行初始化,然后对原型进行迭代更新求解。

k-means算法以k为参数,把n个对象分成k个簇,使簇内具有较高的相似度,而簇间的相似度较低。

k-means算法的处理过程如下:

      首先,随机地选择k个对象,每个对象初始地代表了一个簇的平均值或中心,即选择K个初始质心;对剩余的每个对象,根据其与各簇中心的距离,将它赋给最近的簇;然后重新计算每个簇的平均值。 这个过程不断重复,直到准则函数收敛,直到质心不发生明显的变化。通常,采用平方误差准则,误差的平方和SSE作为全局的目标函数,即最小化每个点到最近质心的欧几里得距离的平方和。此时,簇的质心就是该簇内所有数据点的平均值。

K-Means 算法的详细过程

选择K个点作为初始质心  
repeat  
    将每个点指派到最近的质心,形成K个簇  
    重新计算每个簇的质心  
until 簇不发生变化或达到最大迭代次数  

时间复杂度:O(tKmn),其中,t为迭代次数,K为簇的数目,m为记录数,n为维数
空间复杂度:O((m+K)n),其中,K为簇的数目,m为记录数,n为维数

[kmeans这么做的原因,数学推导参考K-Means聚类]

从上图中,我们可以看到,A, B, C, D, E 是五个在图中点,而灰色的点是我们的种子点,也就是我们用来找点群的点。有两个种子点,所以K=2。
然后,K-Means的算法如下:
①随机在图中取K(这里K=2)个种子点。
②然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。(我们可以看到A,B属于上面的种子点,C,D,E属于下面中部的种子点)
③接下来,我们要移动种子点到属于他的“点群”的中心。(见图上的第三步)
④然后重复第2)和第3)步,直到,种子点没有移动(我们可以看到图中的第四步上面的种子点聚合了A,B,C,下面的种子点聚合了D,E)。
聚类的效果如下图,折线是历次循环时3个簇的质心的更新轨迹,黑点是初始质心:

我们查看基本K均值算法实现步骤及上面的聚类效果可以发现,该聚类算法将所有数据点都进行了指派,不识别噪音点。另外选择适当的初试质心是基本K均值过程的关键。

k均值的优缺点及分类

优点:1,简单,易于理解和实现;2,时间复杂度低

缺点:

1)kmeans要手工输入类数目,对初始值的设置很敏感;所以有了k-means++、intelligent k-means、genetic k-means;
2)k-means对噪声和离群值非常敏感,所以有了k-medoids和k-medians;
3)k-means只用于numerical类型数据,不适用于categorical类型数据,所以k-modes;
4)k-means不能解决非凸(non-convex)数据,所以有了kernel k-means。
5)k-means主要发现圆形或者球形簇,不能识别非球形的簇。
3、k-means与DBSCAN的区别
k-means聚类算法的初始点选择不稳定,是随机选取的,这就引起聚类结果的不稳定。k-means属于动态聚类,往往聚出来的类有点圆形或者椭圆形。kmeans对于圆形区域聚类效果较好,dbscan基于密度,对于集中区域效果较好。对于不规则形状,kmeans完全无法用,dbscan可以起到很好的效果。

k值如何确定?

kmenas算法首先需要选择K个初始质心,其中K是用户指定的参数(簇的个数)。很多情况下,我们并不知道数据的分布情况,实际上聚类就是我们发现数据分布的一种手段。如何有效的确定K值,这里大致提供几种方法:
1 与层次聚类结合[2]
        经常会产生较好的聚类结果的一个有趣策略是,首先采用层次凝聚算法决定结果粗的数目,并找到一个初始聚类,然后用迭代重定位来改进该聚类。
2 稳定性方法[3]
       稳定性方法对一个数据集进行2次重采样产生2个数据子集,再用相同的聚类算法对2个数据子集进行聚类,产生2个具有k个聚类的聚类结果,计算2个聚类结果的相似度的分布情况。2个聚类结果具有高的相似度说明k个聚类反映了稳定的聚类结构,其相似度可以用来估计聚类个数。采用次方法试探多个k,找到合适的k值。
3 系统演化方法[3]
        系统演化方法将一个数据集视为伪热力学系统,当数据集被划分为K个聚类时称系统处于状态K。系统由初始状态K=1出发,经过分裂过程和合并过程,系统将演化到它的稳定平衡状态Ki,所对应的聚类结构决定了最优类数Ki。系统演化方法能提供关于所有聚类之间的相对边界距离或可分程度,适用于明显分离的聚类结构和轻微重叠的聚类结构。
4 使用canopy算法进行初始划分[4]
         基于CanopyMethod的聚类算法将聚类过程分为两个阶段
        Stage1、聚类最耗费计算的地方是计算对象相似性的时候,CanopyMethod在第一阶段选择简单、计算代价较低的方法计算对象相似性,将相似的对象放在一个子集中,这个子集被叫做Canopy,通过一系列计算得到若干Canopy,Canopy之间可以是重叠的,但不会存在某个对象不属于任何Canopy的情况,可以把这一阶段看做数据预处理;
         Stage2、在各个Canopy内使用传统的聚类方法(如K-means),不属于同一Canopy 的对象之间不进行相似性计算。
从这个方法起码可以看出两点好处:首先,Canopy 不要太大且Canopy 之间重叠的不要太多的话会大大减少后续需要计算相似性的对象的个数;其次,类似于K-means这样的聚类方法是需要人为指出K的值的,通过Stage1得到的Canopy 个数完全可以作为这个K值,一定程度上减少了选择K的盲目性。
        其他方法如贝叶斯信息准则方法(BIC)可参看文献[5]。

5 ml学习课程中的方法:What is the right value of K?

 

Note:there actually isn't a great way of answering this or doing this automatically and by far the most common way of choosing the number of clusters, is still choosing it manually by looking at visualizations or by looking at the output of the clustering algorithm or something else.Choosing the value of K


Note:

1. Elbow: distortion goes down rapidly until K equals 3, really goes down very slowly after that.So let's pick K equals 3.
2. 这种方法不是对所有聚类案例有效:but just as often, you end up with a plot that looks like the one on the right and is not clear where the ready location of the elbow is. It  makes it harder to choose a number of clusters using this method.

3. Too many clusters will still overfit the data, as it will find "structure" that is purely ficticious and not truly present in the data.

Note:

1. if that later, downstream purpose,such as market segmentation.then often, a better way to determine the number of clusters, is to see how well different numbers of clusters serve that later downstream purpose.

2. 衣服大小越细分,成本越高;这就取决于市场。总结:

the better way to think about how to choose the number of clusters is to ask, for what purpose are you running K-means?And then to think, what is the number of clusters K that serves that, you know, whatever later purpose that you actually run the K-means for.

还可以定义一个指标「例如簇平均直径」,通过二分搜索来确定最佳k。

[(stackoverflow)k-means聚类k值的确定 : How do I determine k when using k-means clustering?]

[用parametric bootstrap寻找K-means最佳K值]

[Machine Learning - XIII. Clustering聚类 (Week 8)]

初始质心的选取

kmeans的一个缺点是最开始的种子点的选择的好坏会影响到聚类结果,选择适当的初始质心是基本kmeans算法的关键步骤。

1 常见的方法是随机的选取初始质心,但是这样簇的质量常常很差。处理选取初始质心问题的一种常用技术是:多次运行,每次使用一组不同的随机初始质心,然后选取具有最小SSE(误差的平方和)的簇集。这种策略简单,但是效果可能不好,这取决于数据集和寻找的簇的个数。

2 kmeans++

基本原则是使得各个种子点之间的距离尽可能的大,但是又得排除噪声的影响。基本思路:

1、从输入的数据点集合(要求有k个聚类)中随机选择一个点作为第一个聚类中心
2、对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x)
3、选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大(即从不同权重的N个元素中随机选择一个的权重随机选择算法)
4、重复2和3直到k个聚类中心被选出来
5、利用这k个初始的聚类中心来运行标准的k-means算法

[python代码:K-means算法的改进:K-means++]

[python和matlab代码:浅入浅出:从Kmeans到Kmeans++]

3 取一个样本,并使用层次聚类技术对它聚类。从层次聚类中提取K个簇,并用这些簇的质心作为初始质心。该方法通常很有效,但仅对下列情况有效:(1)样本相对较小,例如数百到数千(层次聚类开销较大);(2)K相对于样本大小较小
4 第三种选择初始质心的方法,随机地选择第一个点,或取所有点的质心作为第一个点。然后,对于每个后继初始质心,选择离已经选取过的初始质心最远的点。使用这种方法,确保了选择的初始质心不仅是随机的,而且是散开的。但是,这种方法可能选中离群点。此外,求离当前初始质心集最远的点开销也非常大。为了克服这个问题,通常该方法用于点样本。由于离群点很少(多了就不是离群点了),它们多半不会在随机样本中出现。计算量也大幅减少。

5 第四种方法就是上面提到的canopy算法。

其它问题

距离的度量

         常用的距离度量方法包括:欧几里得距离和余弦相似度。两者都是评定个体间差异的大小的。欧几里得距离度量会受指标不同单位刻度的影响,所以一般需要先进行标准化,同时距离越大,个体间差异越大;空间向量余弦夹角的相似度度量不会受指标刻度的影响,余弦值落于区间[-1,1],值越大,差异越小。但是针对具体应用,什么情况下使用欧氏距离,什么情况下使用余弦相似度?
         从几何意义上来说,n维向量空间的一条线段作为底边和原点组成的三角形,其顶角大小是不确定的。也就是说对于两条空间向量,即使两点距离一定,他们的夹角余弦值也可以随意变化。感性的认识,当两用户评分趋势一致时,但是评分值差距很大,余弦相似度倾向给出更优解。举个极端的例子,两用户只对两件商品评分,向量分别为(3,3)和(5,5),这两位用户的认知其实是一样的,但是欧式距离给出的解显然没有余弦值合理。
质心的计算
        对于距离度量不管是采用欧式距离还是采用余弦相似度,簇的质心都是其均值,即向量各维取平均即可。
算法停止条件
        一般是目标函数达到最优或者达到最大的迭代次数即可终止。对于不同的距离度量,目标函数往往不同。当采用欧式距离时,目标函数一般为最小化对象到其簇质心的距离的平方和;当采用余弦相似度时,目标函数一般为最大化对象到其簇质心的余弦相似度和。
空聚类的处理
          如果所有的点在指派步骤都未分配到某个簇,就会得到空簇。如果这种情况发生,则需要某种策略来选择一个替补质心,否则的话,平方误差将会偏大。一种方法是选择一个距离当前任何质心最远的点。这将消除当前对总平方误差影响最大的点。另一种方法是从具有最大SSE的簇中选择一个替补的质心,这将分裂簇并降低聚类的总SSE。如果有多个空簇,则该过程重复多次。另外,编程实现时,要注意空簇可能导致的程序bug。

皮皮blog

代码实现

python直接实现

import random
import numpy as np

def k_means(dataset, k, iter):
    # init kernel
    cmean = [dataset[i] for i in random.sample(range(len(dataset)), k)]

    cs = [[] for _ in range(k)]
    for _ in range(iter):
        # cluster points
        cs = [[] for _ in range(k)]

        for i in dataset:
            min_edist = 2 ** 32 - 1
            ci = 0
            for ide, j in enumerate(cmean):
                # Euclid distance
                edist = np.linalg.norm(np.asarray(i) - np.asarray(j))

                if edist < min_edist:
                    min_edist = edist
                    ci = ide
            cs[ci].append(i)

        cmean = []
        for i in cs:
            x = [ii[0] for ii in i]
            y = [ii[1] for ii in i]
            cmean.append((sum(x) / len(x), sum(y) / len(y)))
    return cs

from matplotlib import pyplot as plt

# test data1
dataset = []
fileIn = open('a.txt')
for line in fileIn.readlines():
    lineArr = line.strip().split('\t')
    dataset.append([float(lineArr[0]), float(lineArr[1])])

# test data2
# data = """
# 1,0.697,0.46,2,0.774,0.376,3,0.634,0.264,4,0.608,0.318,5,0.556,0.215,
# 6,0.403,0.237,7,0.481,0.149,8,0.437,0.211,9,0.666,0.091,10,0.243,0.267,
# 11,0.245,0.057,12,0.343,0.099,13,0.639,0.161,14,0.657,0.198,15,0.36,0.37,
# 16,0.593,0.042,17,0.719,0.103,18,0.359,0.188,19,0.339,0.241,20,0.282,0.257,
# 21,0.748,0.232,22,0.714,0.346,23,0.483,0.312,24,0.478,0.437,25,0.525,0.369,
# 26,0.751,0.489,27,0.532,0.472,28,0.473,0.376,29,0.725,0.445,30,0.446,0.459
# """
# a = data.split(',')
# dataset = [[float(a[i]), float(a[i + 1])] for i in range(1, len(a) - 1, 3)]

print(dataset)

C = k_means(dataset, 3, 500)

colValue = ['r', 'y', 'g', 'b', 'c', 'k', 'm']
for i in range(len(C)):
    coo_X = []  # x坐标列表
    coo_Y = []  # y坐标列表
    for j in range(len(C[i])):
        coo_X.append(C[i][j][0])
        coo_Y.append(C[i][j][1])
    plt.scatter(coo_X, coo_Y, marker='x', color=colValue[i % len(colValue)], label=i)
plt.show()

a.txt

1.658985    4.285136
-3.453687  3.424321
4.838138   -1.151539
-5.379713  -3.362104
0.972564   2.924086
-3.567919  1.531611
0.450614   -3.302219
-3.487105  -1.724432
2.668759   1.594842
-3.156485  3.191137
3.165506   -3.999838
-2.786837  -3.099354
4.208187   2.984927
-2.123337  2.943366
0.704199   -0.479481
-0.392370  -3.963704
2.831667   1.574018
-0.790153  3.343144
2.943496   -3.357075
-3.195883  -2.283926
2.336445   2.875106
-1.786345  2.554248
2.190101   -1.906020
-3.403367  -2.778288
1.778124   3.880832
-1.688346  2.230267
2.592976   -2.054368
-4.007257  -3.207066
2.257734   3.387564
-2.679011  0.785119
0.939512   -4.023563
-3.674424  -2.261084
2.046259   2.735279
-3.189470  1.780269
4.372646   -0.822248
-2.579316  -3.497576
1.889034   5.190400
-0.798747  2.185588
2.836520   -2.658556
-3.837877  -3.253815
2.096701   3.886007
-2.709034  2.923887
3.367037   -3.184789
-2.121479  -4.232586
2.329546   3.179764
-3.284816  3.273099
3.091414   -3.815232
-3.762093  -2.432191
3.542056   2.778832
-1.736822  4.241041
2.127073   -2.983680
-4.323818  -3.938116
3.792121   5.135768
-4.786473  3.358547
2.624081   -3.260715
-4.009299  -2.978115
2.493525   1.963710
-2.513661  2.642162
1.864375   -3.176309
-3.171184  -3.572452
2.894220   2.489128
-2.562539  2.884438
3.491078   -3.947487
-2.565729  -2.012114
3.332948   3.983102
-1.616805  3.573188
2.280615   -2.559444
-2.651229  -3.103198
2.321395   3.154987
-1.685703  2.939697
3.031012   -3.620252
-4.599622  -2.185829
4.196223   1.126677
-2.133863  3.093686
4.668892   -2.562705
-2.793241  -2.149706
2.884105   3.043438
-2.967647  2.848696
4.479332   -1.764772
-4.905566  -2.911070

结果:

sklearn实现

[Scikit-learn:聚类clustering​​​​​​​]

from: 皮皮blog

ref: [机器学习算法与Python实践之(五)k均值聚类(k-means)]

[k-means算法详解及python代码]*

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值