手写机器学习算法系列——K-Means聚类算法(一)

代码仓库(数字空间项目,GN可上)
不想看的话,我也将代码上传到本博客中。

1.聚类算法简介

在数据科学和机器学习领域,聚类(Clustering)算法是一种无监督学习方法,它将相似的对象分到同一个组,而不同的对象则被分到不同的组。这种算法的主要目标是根据数据的特征进行分组,以此找出数据的内在结构。聚类算法的一个核心特点就是它并不需要预先知道数据的类别,而是通过算法自动进行分组。

在实际应用中,我们常见的聚类算法有K-meansDBSCAN谱聚类、**AGNES(层次聚类)**等。每种算法都有其适用的场景,选择哪种算法主要取决于我们对数据的理解和目标。

在接下来的博客中,我们将深入探讨这些算法的工作原理,以及如何在实际问题中应用它们。本文,只涉及到划分聚类算法,本人使用C++实现了K-Means、K-Medoids、Spectral Clustering(谱聚类)和Mean-Shift算法。代码已经开源。

2. 划分聚类算法

聚类算法是典型的无监督学习,没有标签结果,即我们实际上并不知道准确的分类结果。聚类算法的评价指标往往非常主观,简单来说,聚类问题图解如下所示。
在这里插入图片描述

2.1 K-means算法

K-Means算法很早就出现了,核心思想:将数据集划分为K个集群,则每个集群内的数据点尽可能靠近(即内部差异小),而不同集群的数据点尽可能不同(即集群间差异大)。它需要确定各类中心点,然后依据每个点到中心点之间的L2距离确定属于哪一类,然后用每个类簇的平均点更新中心点。

2.1.1 算法核心描述

假定,数据点 x ⃗ = ( x 1 , x 2 , . . . , x n ) T \vec x=(x_1,x_2,...,x_n)^T x =(x1,x2,...,xn)T,数据集 { x ⃗ ( i ) ∣ i = 1 , . . . , m } \{\vec x^{(i)}|i=1,...,m\} {x (i)i=1,...,m},聚类簇的类别数 K K K,要求算法输出一个向量 y ⃗ = { k 1 , k 2 , . . . , k n ∣ k i = 0 , . . . , K − 1 } {\vec y=\{k_1,k_2,...,k_n|k_i = 0,...,K-1}\} y ={k1,k2,...,knki=0,...,K1}来表示聚类结果。

K-Means算法可以描述为以下三个步骤:
第一个初始化中心点(K个):
r a n d o m    i n i t i a l i z a t i o n    :    C P ⃗ j ,     j = 1 , 2 , . . . K (1) random\ \ initialization\ \ :\ \ \vec {CP}_j,\ \ \ j=1,2,...K\tag{1} random  initialization  :  CP j,   j=1,2,...K(1)
根据中心点对每个数据进行类别划分:
C l a s s ( x ⃗ ( i ) ) = a r g m i n j ∣ ∣ x ⃗ ( i ) − C P ⃗ j ∣ ∣ (2) Class(\vec x^{(i)}) = argmin_j||\vec x^{(i)} - \vec {CP}_j||\tag{2} Class(x (i))=argminj∣∣x (i)CP j∣∣(2)
用簇中的平均位置更新中心点:
C P ⃗ j = ∑ i = 1 m I { C l a s s ( x ⃗ ( i ) ) = j } x ⃗ ( i ) ∑ i = 1 m I { C l a s s ( x ⃗ ( i ) ) = j } , j = 1 , 2 , . . . K (3) \vec {CP}_{j}=\frac{\sum_{i=1}^mI\{{Class(\vec x^{(i)})=j\}\vec x^{(i)}}}{\sum_{i=1}^mI\{{Class(\vec x^{(i)})=j\}}},j=1,2,...K\tag{3} CP j=i=1mI{Class(x (i))=j}i=1mI{Class(x (i))=j}x (i),j=1,2,...K(3)
循环执行(2)和(3)直到中心点不再发生变化,程序即可退出。

2.1.2 代码结果展示

这里的动图是循环的,代码执行的最后结果是均点不再发生变化。
在这里插入图片描述
在这里插入图片描述

2.1.3 优缺点

优点:算法简单,实现简单,直观,对于大规模数据集来说很实用(因为很简单)。
缺点:需要提前给出类簇数目K,随机的起始点对于聚类结果影响很大。离群点噪声对于该算法的影响将会是致命的。

3. 代码分享

from lib import clustering,dataGenerator # 这两个模块是自己用C++写的
import numpy as np
import matplotlib.pyplot as plt
if __name__ == '__main__':
    y = dataGenerator.clusteringData(1000, 3, 2, -10, 10, 2)

    clustering.init(y, 3, 10)
    yy = np.array(y)
    centerPoint = list([yy[0,:], yy[2,:], yy[-1,:]])

    plt.figure(figsize=(8,8))
    for i in range(10):
        plt.cla()
        centerPoint = clustering.singleTrain(centerPoint)
        print(centerPoint)
        label = clustering.getLabel(centerPoint)

        # print(label)
        indices1 = [i for i, x in enumerate(label) if x == 1]
        indices2 = [i for i, x in enumerate(label) if x == 0]
        indices3 = [i for i, x in enumerate(label) if x == 2]

        lable = np.array(centerPoint)
        plt.scatter(yy[indices1,0], yy[indices1,1], color = 'blue')
        plt.scatter(yy[indices2,0], yy[indices2,1], color = 'pink')
        plt.scatter(yy[indices3,0], yy[indices3,1], color = 'black')
        plt.scatter(lable[:,0], lable[:,1], color='red')
        plt.savefig('./image/' + str(i) + '.png')
        plt.pause(1)
    plt.show()

4.总结与思考

K-means算法呢,上个世纪中叶就有了,很简单。但是该算法仍有一些局限性:
1、需要预设聚类数量:K-means算法需要用户预先设定K值(即聚类的数量),这在实际应用中可能是一个挑战,因为最优的聚类数量通常是未知的。
2、对初始质心敏感:K-means算法的结果可能受到初始质心选择的影响。不同的初始质心可能会导致不同的聚类结果。
3、对异常值敏感:K-means算法对异常值和噪声很敏感,这些值可能会影响聚类质心的计算,从而影响最终的聚类结果。
4、假设聚类为凸形:K-means算法假设每个聚类的形状都是凸的,这在某些情况下可能不成立。

上述聚类数量K的确定,有一个“肘部法则”的方法。有一个一个簇内平方和的指标WCSS,一种用于评估聚类效果的指标,也可以用于确定聚类算法中的聚类数目K值。

具体做法是,对于每一个K值,我们都计算对应的WCSS值,然后画出K值与WCSS值的关系图。在这个图中,WCSS值会随着K值的增大而减小,但是当K值到达一个点之后,WCSS值的减小速率会明显降低,这个点就像人的肘部一样,因此被称为“肘点”。我们通常会选择这个“肘点”对应的K值作为最终的聚类数量。画个图解释如下所示。
在这里插入图片描述

  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值