机器学习Chapter3-(聚类分析)Python实现K-Means算法

K-Means算法

给定样本集 D=x1,x2,...,xm ,假定聚类的簇划分 C=C1,C2,...,Ck k-means算法的目标是最小化平均距离

E=i=1kxCi||xμi||22
其中 μi=1|Ci|xCix 是簇 Ci 的均值向量。上式刻画了簇类样本围绕簇均值向量的紧密程度,越小代表样本距簇均值中心越靠近。

K-Means算法采用贪心策略,通过迭代优化来近似求解。原理是:
假定一组向量做为所有簇的簇均值向量,然后根据假设的簇均值向量给出了数据集D的一个簇划分,再根据这个簇划分来计算真实的簇均值向量(这是一个迭代过程)。

算法步骤:

这里写图片描述

算法示意图如下:

这里写图片描述


算法特点

优点

  • 简单并且很有限的聚类方法
  • 算法收敛的很快
  • 相对高效和高扩展性。算法复杂度为 O(tkn)
    • t为迭代次数 k为中心点个数 n为样本个数

缺点

  • 需要指定聚类中心个数k
    • 需要先验和主观知识
  • 可能会陷入局部最优(实际过程中,可以选择多个不同初始化点)
  • 对噪点比较敏感

不一样的初始点,聚类结果可能不同,如下图:

这里写图片描述



Python实现K-means算法

代码:所有代码见我的github

数据集

这里为了省事,直接使用sklearn包下的make_blobs函数生成数据,该函数的原理是在给定的中心点周围产生采样若干数据,采样函数是正态分布(可指定正态函数标准差)。

产生数据代码如下:

    from sklearn.datasets.samples_generator import make_blobs

    def create_data(centers, num=100, std=0.7):
        '''
            生成用于聚类的数据集
        :param centers: 聚类的中心点组成的数组。如果中心点是二维的,则产生的每个样本都是二维的。
        :param num: 总的样本数
        :param std: 生成数据簇中样本的标准差
        :return: 用于聚类的数据集。是一个元组,第一个元素为样本集,第二个元素为样本集的真实簇分类标记
        '''
        X, labels_true = make_blobs(n_samples=num, centers=centers, cluster_std=std)
        return X, labels_true
  • 随机产生的数据如下:

    mark

  • 使用matplotlib绘制出来

            def plot_data(*data):
                '''
                绘制用于聚类的数据集
    
                :param data: 可变参数。它是一个元组。元组元素依次为:第一个元素为样本集,第二个元素为样本集的真实簇分类标记
                :return: None
                '''
                X, labels_true = data
                labels = np.unique(labels_true) # np.unique保留array中不同的数值,即保留所有labels
                fig = plt.figure()
                ax = fig.add_subplot(1,1,1)
                colors = 'rgbyckm' # 每个簇的样本标记不同的颜色
                for i, label in enumerate(labels):
                    position = labels_true == label # 选取出不同label的所有点的index
                    ax.scatter(X[position, 0], X[position, 1], label="cluster %d"%label,
                    color=colors[i%len(colors)], s=1) # 绘制数据 设置size=1
    
                ax.legend(loc="best", framealpha=0.5)
                ax.set_xlabel("X[0]")
                ax.set_ylabel("Y[1]")
                ax.set_title("data")
                plt.show()
    
            if __name__=='__main__':
                centers=[[1,1],[5,6],[1,10],[10,20]] # 用于产生聚类的中心点
                X,labels_true=create_data(centers,400,0.5) # 产生用于聚类的数据集

    结果如下:

    这里写图片描述

Python下手写K-means算法

这里贴出了k-means算法部分:

    def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
        m = np.shape(dataSet)[0]
        clusterAssment = np.mat(np.zeros((m, 2)))  # 用于分配数据点
        centroids = createCent(dataSet, k)  # 随机生成k个均值向量
        clusterChanged = True
        while clusterChanged:
            clusterChanged = False
            for i in range(m):  #为每个数据点分配到最近的均值向量上
                minDist = np.inf
                minIndex = -1
                for j in range(k):
                    distJI = distMeas(centroids[j, :], dataSet[i, :])
                    if distJI < minDist:  
                        minDist = distJI  
                        minIndex = j
                if clusterAssment[i, 0] != minIndex:
                    clusterChanged = True
                clusterAssment[i, :] = int(minIndex), minDist ** 2
            for cent in range(k):  # recalculate centroids
                ptsInClust = dataSet[np.nonzero(clusterAssment[:, 0].A == cent)[0]]  # get all the point in this cluster
                centroids[cent, :] = np.mean(ptsInClust, axis=0)  # assign centroid to mean
        return centroids, clusterAssment

程序结果图如下:

这里写图片描述

方块点的是聚类后的均值向量,聚类的效果看起来还可以~


使用scikit-learn包下Kmeans类

K-means函数原型

KMeans是scikit-learn提供的 k 均值聚类算法模型,定义如下:

        def __init__(self, n_clusters=8, init='k-means++', n_init=10,
                 max_iter=300, tol=1e-4, precompute_distances='auto',
                 verbose=0, random_state=None, copy_x=True,
                 n_jobs=1, algorithm='auto'):

参数介绍

参数 description
n_clusters int, optional, default: 8
指定分类簇的中心数量.
max_iter int, default: 300
单轮k均值算法,最大迭代次数
n_init int, default: 10
指定算法运行次数,每一次都会选择一组不同的初始化均值向量,算法最后会选择最佳的分类簇作为最终结果
init {‘k-means++’, ‘random’ or an ndarray}
指定初始化均值向量的策略
- k-means++: 该初始化策略选择的初始化均值向量相互之间都距离较远,它的效果较好。
- random:从数据集中随机选择K个样本作为初始化均值向量
- 或者提供一个形状(n_clusters, n_features)的数据作为初始化均值向量

k均值算法总能够收敛,但是其收敛程度情况依赖于初始化的均值。有可能收敛到局部极小值。因此通常都是用多组初始化均值来计算若干次,选用最好的结果。K-means++在一定程度解决这个问题
algorithm “auto”, “full” or “elkan”, default=”auto”
使用的算法。
precompute_distances {‘auto’, True, False}
指定是否提前计算好样本之间的距离.(提前算,需要更多的内存,但是速度快)
auto : if n_samples * n_clusters > 12 million,就不提钱计算了
True:提前计算
False:不提前计算
tol float, default: 1e-4
指定判断算法收敛的阈值
n_jobs int
指定并行运算的CPU数量
random_state integer or numpy.RandomState, optional
指定RandomState实例
verbose int, default 0
是否打印日志
copy_x boolean, default True
True:不会改变数据值
False:改变原始数据,用于节省内存,返回时返回原始数据,可能会有精度损失

属性介绍

属性 description
cluster_centers_ array, [n_clusters, n_features]
分类簇的均值向量
labels_ 每个样本簇的簇标记
inertia_ float
所有样本距样本簇中心的距离和

方法介绍

方法 description
fit(self, X, y=None) 训练模型
predict(self, X) 预测样本所属的簇
fit_predict(self, X, y=None) 训练模型并预测样本所属簇
score(self, X, y=None) Opposite of the value of X on the K-means objective

测试K-Means簇数量影响

K-means算法中聚类簇数量的选择是非常重要的,下面我们测试不同的聚类簇下各个指标的变化。

    def test_Kmeans_nclusters(*data):
        '''
        测试 KMeans 的聚类结果随 n_clusters 参数的影响

        :param data: 可变参数。它是一个元组。元组元素依次为:第一个元素为样本集,第二个元素为样本集的真实簇分类标记
        :return: None
        '''
        X,labels_true=data
        nums=range(1,50)
        ARIs=[]
        Distances=[]
        for num in nums:
            clst=cluster.KMeans(n_clusters=num)
            predicted_labels=clst.fit_predict(X)
            ARIs.append(adjusted_rand_score(labels_true,predicted_labels))
            Distances.append(clst.inertia_)

        ## 绘图
        fig=plt.figure()
        ax=fig.add_subplot(1,2,1)
        ax.plot(nums,ARIs,marker="+")
        ax.set_xlabel("n_clusters")
        ax.set_ylabel("ARI")
        ax=fig.add_subplot(1,2,2)
        ax.plot(nums,Distances,marker='o')
        ax.set_xlabel("n_clusters")
        ax.set_ylabel("inertia_")
        fig.suptitle("KMeans")
        plt.show()

输出:

mark

聚类簇数量选择4时,ARI(=RIE[RI]max(RI)E[RI])值最高。ARI值越高说明聚类效果越好

同时注意到,聚类簇数量从1到4这个过程,样本到簇中心的距离和迅速下降,而从4以后,基本不变了。

测试K-means算法运行次数和选择中心均值向量策略的影响

测试k-means++random两个初始策略,测试1到50次下算法运行结果。

    def test_Kmeans_n_init(*data):
        '''
        测试 KMeans 的聚类结果随 n_init 和 init  参数的影响

        :param data: 可变参数。它是一个元组。元组元素依次为:第一个元素为样本集,第二个元素为样本集的真实簇分类标记
        :return: None
        '''
        X,labels_true=data
        nums=range(1,50)
        ARIs_k=[]
        Distances_k=[]
        ARIs_r=[]
        Distances_r=[]
        for num in nums:
                clst=cluster.KMeans(n_init=num,init='k-means++')  # 使用k-mean++策略
                predicted_labels=clst.fit_predict(X)
                ARIs_k.append(adjusted_rand_score(labels_true,predicted_labels))
                Distances_k.append(clst.inertia_)

                clst=cluster.KMeans(n_init=num,init='random')   # 使用random策略
                predicted_labels=clst.fit_predict(X)
                ARIs_r.append(adjusted_rand_score(labels_true,predicted_labels))
                Distances_r.append(clst.inertia_)

        ## 绘图
        fig=plt.figure()
        ax=fig.add_subplot(1,2,1)
        ax.plot(nums,ARIs_k,marker="+",label="k-means++")
        ax.plot(nums,ARIs_r,marker="+",label="random")
        ax.set_xlabel("n_init")
        ax.set_ylabel("ARI")
        ax.set_ylim(0,1)
        ax.legend(loc='best')
        ax=fig.add_subplot(1,2,2)
        ax.plot(nums,Distances_k,marker='o',label="k-means++")
        ax.plot(nums,Distances_r,marker='o',label="random")
        ax.set_xlabel("n_init")
        ax.set_ylabel("inertia_")
        ax.legend(loc='best')

        fig.suptitle("KMeans")
        plt.show()

输出如下:

mark



参考资料

MOOC-数据挖掘:理论与算法

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值