Fork me on GitHub

【机器学习】--Kmeans从初识到应用

一.前述

Kmeans算法一般在数据分析前期使用,选取适当的k,将数据分类后,然后分类研究不同聚类下数据的特点。

Kmeans算法是一种无监督的算法。 

常用于分组,比如用户偏好。

 

二.概念及原理

Kmeans原理:

      1 随机选取k个中心点

      2 遍历所有数据,将每个数据划分到最近的中心点中

      3 计算每个聚类的平均值,并作为新的中心点

      4 重复2-3,直到这k个中线点不再变化(收敛了),或执行了足够多的迭代。

样本点之间的相似度距离计算:

1.欧氏距离相似度(常用!!!

 

2.Jaccard相似度(用于比较样本之间的相似性,系数值越大表示值越高

 

3.余弦相似度

 

余弦相似度值在[-1,1]之间,值越趋近于1代表越相近,越趋近于-1,代表越相反,接近于0,代表两个向量正交。

常用计算文本相似性!!!将两个文本根据他们词,建立两个向量,计算这两个向量的余弦值,就可以知道两个文本在统计学方法中他们的相似度情况。

4.Pearson相似度

5.相对熵(K-L距离)

 总结:

           对于高维空间点之间的度量,用欧氏距离。

           对于集合度量Jaccard相似度。

     对于自然语言处理用余弦相似度。

     对于函数度量用Pearson相似度

 

目标函数:

考虑欧几里得距离的数据,使用误差平方和(Sum of the Squared Error,SSE)作为聚类的目标函数,两次运行K均值产生的两个不同的簇集,我们更喜欢SSE最小的那个。

k表示k个聚类中心,ci表示第几个中心,dist表示的是欧几里得距离。
这里有一个问题就是为什么,我们更新质心是让所有的点的平均值,这里就是SSE所决定的。

通俗来说,每个点计算 到所在分配的中心店的距离,然后加和。

随着k的增长损失函数,逐渐递减。肘部法就是计算不同的K,然后计算SSE,求最大的两组即可。

选择一开始下降速度快,后来下降速度慢的。

肘部法图解:

聚类评价方法:

 

常见的聚类评测指标有纯度和 F 值,其中 F 值更为常用。

F 值的更普适的应用是信息检索的结果,其计算包括了两个指标:召回率(Recall Rate)和准确率(Precision Rate)。

  • 召回率的定义为:检索出的相关文档数和文档库中所有的相关文档数的比率,衡量的是检索系统的查全率;
  • 准确率的定义为:检索出相关文档数与检索出的文档总数的比率,衡量的是检索系统的查准率;F 值为两者的调和平均值。

以上评估方法都是基于原始数据事先知道一定的标签,但这种似乎并不太常用,所以常用的评估模型的指标是轮廓系数,适用于在一开始不知道标签的情况下。

具体如下:

轮廓系数它结合内聚度和分离度两种因素。可以用来在相同原始数据的基础上用来评价不同算法、或者算法不同运行方式对聚类结果所产生的影响。

方法:

                  1,计算样本i到同簇其他样本的平均距离ai。ai 越小,说明样本i越应该被聚类到该簇。将ai 称为样本i的簇内不相似度

                            簇C中所有样本的a i 均值称为簇C的簇不相似度。

                  2,计算样本i到其他某簇Cj 的所有样本的平均距离bij,称为样本i与簇Cj 的不相似度。定义为样本i的簇间不相似度:bi =min{bi1, bi2, ..., bik}

                            bi越大,说明样本i越不属于其他簇。

                  3,根据样本i的簇内不相似度a i 和簇间不相似度b i ,定义样本i的轮廓系数

                 4,判断:

                            si接近1,则说明样本i聚类合理;

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

                            若si 近似为0,则说明样本i在两个簇的边界上。

结果为负数则可以直接放弃了!!!说明分类的很不好。

二分Kmeans原理:

为了得到k个簇,将所有点的集合分裂成两个簇,从这些簇中选取一个继续分裂,如此下去,直到产生k个簇。

比如要分成5个组,第一次分裂产生2个组,然后从这2个组中选一个目标函数产生的误差比较大的,分裂这个组产生2个,这样加上开始那1个就有3个组了,然后再从这3个组里选一个分裂,产生4个组,重复此过程,产生5个组。这算是一中基本求精的思想。二分k均值不太受初始化的困扰,因为它执行了多次二分试验并选取具有最小误差的试验结果,还因为每步只有两个质心。

 

Kmeans++原理:

k-means++算法选择初始seeds的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。

  1. 从输入的数据点集合中随机选择一个点作为第一个聚类中心
  2. 对于数据集中的每一个点x,计算它与聚类中心(指已选择的聚类中心)的距离D(x),然后对于每一个点/总和得出一个概率,则第二个点依据概率进行选择。第三个点的选择是其他所有的点到前两个点的距离然后加和,再计算每一个点/总和的概率,再选择一个中心店。以此类推。。。。。
  3. 选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点被选取作为聚类中心的概率较大(概率化选择)

利用Canopy计算初始类簇中心点。

不需要迭代,比较快

步骤:

1.首先定义两个距离T1和T2,T1>T2.从初始的点的集合S中随机移除一个点P,然后对于还在S中的每个点I,计算该点I与点P的距离

2.如果距离小于T1,则将点I加入到点P所代表的Canopy中如果距离小于T2,则将点I从集合S中移除,并将点I加入到点P所代表的Canopy中。

3.迭代完一次之后,重新从集合S中随机选择一个点作为新的点P,然后重复执行以上步骤。

图示:

总结:与中心的距离大于T1时,这些点就不会被归入到中心所在的这个canopy类中。然当距离小于T1大于T2时,这些点会被归入到该中心所在的canopy中,但是它们并不会从D中被移除,也就是说,它们将会参与到下一轮的聚类过程中,成为新的canopy类的中心或者成员。亦即,两个Canopy类中有些成员是重叠的。而当距离小于T2的时候,这些点就会被归入到该中心的canopy类中,而且会从D中被移除,也就是不会参加下一次的聚类过程了。

 三、代码

代码一:Kmeans实现

# !/usr/bin/python
# -*- coding:utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as ds
import matplotlib.colors
from sklearn.cluster import KMeans


def expand(a, b):
    d = (b - a) * 0.1
    return a-d, b+d


if __name__ == "__main__":
    N = 400#400个数据点
    centers = 4#4个中心店
    data, y = ds.make_blobs(N, n_features=2, centers=centers, random_state=2)#聚类测试点 创建高沙分布的点
    data2, y2 = ds.make_blobs(N, n_features=2, centers=centers, cluster_std=(1, 2.5, 0.5, 2), random_state=2)#每个类别的标准差不一样,u是正态分布,胖瘦
    data3 = np.vstack((data[y == 0][:], data[y == 1][:50], data[y == 2][:20], data[y == 3][:5]))#垂直放 每个类别生成一百个数据 每个类别拿的数据不一样。目的是每一个簇里面的密度不一样,看看聚类效果。
    y3 = np.array([0] * 100 + [1] * 50 + [2] * 20 + [3] * 5)#自己构建label

    cls = KMeans(n_clusters=4, init='k-means++')
    y_hat = cls.fit_predict(data)
    y2_hat = cls.fit_predict(data2)
    y3_hat = cls.fit_predict(data3)

    m = np.array(((1, 1), (1, 3)))#矩阵点乘 ,相当于旋转
    data_r = data.dot(m)
    y_r_hat = cls.fit_predict(data_r)

    matplotlib.rcParams['font.sans-serif'] = [u'SimHei']
    matplotlib.rcParams['axes.unicode_minus'] = False
    cm = matplotlib.colors.ListedColormap(list('rgbm'))

    plt.figure(figsize=(9, 10), facecolor='w')
    plt.subplot(421)
    plt.title(u'原始数据')
    plt.scatter(data[:, 0], data[:, 1], c=y, s=30, cmap=cm, edgecolors='none')#坐标轴
    x1_min, x2_min = np.min(data, axis=0)
    x1_max, x2_max = np.max(data, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(422)
    plt.title(u'KMeans++聚类')
    plt.scatter(data[:, 0], data[:, 1], c=y_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(423)
    plt.title(u'旋转后数据')
    plt.scatter(data_r[:, 0], data_r[:, 1], c=y, s=30, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data_r, axis=0)
    x1_max, x2_max = np.max(data_r, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(424)
    plt.title(u'旋转后KMeans++聚类')
    plt.scatter(data_r[:, 0], data_r[:, 1], c=y_r_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(425)
    plt.title(u'方差不相等数据')
    plt.scatter(data2[:, 0], data2[:, 1], c=y2, s=30, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data2, axis=0)
    x1_max, x2_max = np.max(data2, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(426)
    plt.title(u'方差不相等KMeans++聚类')
    plt.scatter(data2[:, 0], data2[:, 1], c=y2_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(427)
    plt.title(u'数量不相等数据')
    plt.scatter(data3[:, 0], data3[:, 1], s=30, c=y3, cmap=cm, edgecolors='none')
    x1_min, x2_min = np.min(data3, axis=0)
    x1_max, x2_max = np.max(data3, axis=0)
    x1_min, x1_max = expand(x1_min, x1_max)
    x2_min, x2_max = expand(x2_min, x2_max)
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.subplot(428)
    plt.title(u'数量不相等KMeans++聚类')
    plt.scatter(data3[:, 0], data3[:, 1], c=y3_hat, s=30, cmap=cm, edgecolors='none')
    plt.xlim((x1_min, x1_max))
    plt.ylim((x2_min, x2_max))
    plt.grid(True)

    plt.tight_layout(2, rect=(0, 0, 1, 0.97))
    plt.suptitle(u'数据分布对KMeans聚类的影响', fontsize=18)
    # https://github.com/matplotlib/matplotlib/issues/829
    # plt.subplots_adjust(top=0.92)
    # plt.show()
    plt.savefig('cluster_kmeans')

 

  结果:

代码二:评估指标

# !/usr/bin/python
# -*- coding:utf-8 -*-

from sklearn import metrics#评估


if __name__ == "__main__":
    y = [0, 0, 0, 1, 1, 1]
    y_hat = [0, 0, 1, 1, 2, 2]
    h = metrics.homogeneity_score(y, y_hat)
    c = metrics.completeness_score(y, y_hat)
    print(u'同一性(Homogeneity):', h)
    print(u'完整性(Completeness):', c)
    v2 = 2 * c * h / (c + h)
    v = metrics.v_measure_score(y, y_hat)
    print(u'V-Measure:', v2, v)

    y = [0, 0, 0, 1, 1, 1]
    y_hat = [0, 0, 1, 3, 3, 3]
    h = metrics.homogeneity_score(y, y_hat)
    c = metrics.completeness_score(y, y_hat)
    v = metrics.v_measure_score(y, y_hat)
    print(u'同一性(Homogeneity):', h)
    print(u'完整性(Completeness):', c)
    print(u'V-Measure:', v)

    # 允许不同值
    y = [0, 0, 0, 1, 1, 1]
    y_hat = [1, 1, 1, 0, 0, 0]
    h = metrics.homogeneity_score(y, y_hat)
    c = metrics.completeness_score(y, y_hat)
    v = metrics.v_measure_score(y, y_hat)
    print(u'同一性(Homogeneity):', h)
    print(u'完整性(Completeness):', c)
    print(u'V-Measure:', v)

    y = [0, 0, 1, 1]
    y_hat = [0, 1, 0, 1]
    ari = metrics.adjusted_rand_score(y, y_hat)
    print(ari)

    y = [0, 0, 0, 1, 1, 1]
    y_hat = [0, 0, 1, 1, 2, 2]
    ari = metrics.adjusted_rand_score(y, y_hat)
    print(ari)

 

均一性,一个簇中只包含一个类别样本,Precision
完整性,同类别样本被归到同一个簇中,Recall

V-Measure:

 

 

posted @ 2018-01-18 01:53  L先生AI课堂  阅读(537)  评论(0编辑  收藏  举报