《机器学习》之 Kmeans聚类原理及代码

Kmeans原理浅析及代码实现

Posted by 予以初始 on July 27, 2020

1 介绍

聚类算法是机器学习中经典的无监督学习算法,聚类算法有多种:Kmeans、Kmedians、Mean-shift、DBSCAN、层次聚类、EM等。 本文只介绍Kmeans原理及代码,之后会陆续更新其他聚类算法的文章。

2 原理

Kmeans聚类原理比较简单,在一些简单的聚类任务中也能达到不错的效果。

算法步骤: 1 随机初始化几个聚类质心点,聚类中心的个数需自己估计; 2 计算每个数据点到每个质心的距离,跟哪个聚类的质心更近,就分类到该聚类; 3 计算每个聚类中样本向量的均值,作为该聚类新的质心; 4 重复上述步骤2,3,迭代更新每个聚类的质心,直到质心的位置不再发生变化。

其中,样本与质心之间距离的计算有多种方式,一般有:1范式、2范式、无穷范式。2范式 (欧氏距离) 最为常用。了解算法的迭代步骤之后,其优缺点显而易见:

优点: 1 只有一个超参数(簇的个数),调参比较容易; 2 原理简单,只涉及到数据点和质心点之间距离的计算,速度较快。 缺点: 1 需要预先定义聚类质心的个数,最佳的值难以确定; 2 随机的质心初始化,可能会导致不同的聚类结果; 3 使用样本均值来定义新的质心,所以对异常值比较敏感。

聚类的质心应该如何选取?(面试高频问题) 随机选取质心有很大的不确定性,应该选择距离尽可能远的质心,因为太近可能造成一类的样本点被强行分成两类。

3 代码实现

理解算法的最好方式就是用代码实现它。Kmeans类代码如下:

import numpy as np
import random

class Kmeans(object):
    def __init__(self, k,  data):
        self.k = k          #簇的个数
        self.data = data    #矩阵样本
        
    #欧氏距离计算(二范式)
    def getDistance(self, n1, n2):
        return np.linalg.norm(n1-n2, ord=2)

    #簇心初始化
    def center_init(self):
        idx = random.sample(range(len(self.data)), k=self.k)    #随机抽取k个样本下标
        self.centers = self.data[idx]                           #选取k个样本作为簇中心

    def fit(self):
        self.center_init()  #簇心初始化
        clusterDistance = np.zeros((len(self.data), 2)) #记录每个样本距离最近的簇心下标,与对应的距离

        flag = True  #控制迭代的标志,控制聚类迭代次数
        while(flag): #迭代更新簇心,直至收敛
            flag = False
            #遍历每个样本
            for i in range(len(self.data)):
                minIdx = -1             #距离最小簇心下标
                minDistance = 10**5     #最小距离
                for j in range(self.k): #遍历每个簇,计算与该样本的距离
                    dist = self.getDistance(self.data[i], self.centers[j])  # 样本i到簇j的距离
                    if dist<minDistance:
                        minDistance = dist
                        minIdx = j
                if(clusterDistance[i][0]!=minIdx):  #如果样本i所属的簇不是minIdx,说明样本i的簇类别变了,
                    flag = True                     #此时应继续迭代,更新簇心
                #记录样本i与簇的最小距离dist,及对应簇的下标j
                clusterDistance[i][0] = minIdx
                clusterDistance[i][1] = minDistance

            #样本的簇划分好之后,用样本均值更新簇心
            for i in range(self.k):
                x = self.data[clusterDistance[:, 0]==i]   #取出属于簇i的所有样本
                self.centers[i] = np.mean(x, axis=0)      #取样本均值作为新的簇心

实例测试:

数据集点我

import matplotlib.pyplot as plt
#二维样本点,方便可视化
data = np.loadtxt('E:\\PycharmProjects\\机器学习算法\\data\\testSet.txt')

model = Kmeans(k=4, data=data)	#定义模型
model.fit()				  	 	#训练
centers = model.centers   		#聚类中心

#聚类结果可视化
plt.scatter(data[:, 0], data[:, 1], c='b', s=10)        #样本点(蓝色)
plt.scatter(centers[:, 0], centers[:, 1], c='r', s=30)  #聚类中心(红色)
plt.show()

聚类结果如下: 在这里插入图片描述 此时簇心的个数 k=4,聚类效果很好。也可尝试其他个数,比如 k=5 时,聚类结果如下: 在这里插入图片描述Kmeans只是聚类算法的入门算法,后续会陆续更新聚类其他算法的详细介绍。 觉得有收获就点个赞吧~万分感谢