K_mean、k_mean++聚类简单原理和Python代码实现

本文介绍了K-Means聚类算法的基本原理,包括选择k个质心、距离计算和迭代更新过程。重点讲解了k++算法的改进,解决随机初始化导致的问题,以及提供了Python代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

​ K_mean聚类可以算是聚类算法里面比较有代表性的算法之一了。其通俗易懂的原理和较好的效果让其被人们所认可接受。本文将简单的介绍一下原理和实现过程+代码的实现,并不适合想看严谨的介绍的读者。

原理

K_mean

基本理解

​ 要说到k_mean 算法。当燃从其名字输入。这里的分成两个。一个是k,另一个是mean。

​ ①k:选定有k个簇(类)。

​ ②mean:指某一个簇中的均值(质心)。

算法流程

​ ①对训练数据进行标准化,去异常值。

​ ②给定一个k值,也就是选定要聚成几类,然后在训练数据中随机选取k个训练数据,将其作为k个均值点(质心)。

​ ③对训练数据中每一个数据都与每一个均值点计算距离。每一个数据与某一个均值点最近,就认定其属于该均值点的簇。

​ ④对得到的每一个簇,计算簇中的所有点的均值作为新的均值点(质心)。

​ ⑤训练③、④步,直到均值点收敛不再改变。

两个小问题

​ ①问题1:对于一些数据,我们不知道该聚成几类。也就是初始值k不知道如何确定。可以选择最小化损失函数来解决。也就是:
L = ∑ j = 1 m ∣ ∣ x j − μ ∣ ∣ 2 L=\sum\limits_{j=1}^m{||x_j-\mu||_2} L=j=1m∣∣xjμ2
​ 上面的式子代表的就是某一个簇中的损失。其中,下面的2表示L2范数。我们就是要选定不同的k,来让这个损失函数最小便可。

​ ②问题2:实际上,k_mean的随机初始化值的方式如果初始化的两个值位置很相近,会导致模型收敛的速度变慢。也会影响最终的模型聚类结果。其中,一个解决方案是k_mean++,它是k_mean的改进算法。就是为了解决这个问题的。

k_mean++

算法流程

​ ①对训练数据进行标san准化,去异常值。

​ ②给定一个k值,也就是选定要聚成几类,然后在训练数据中随机选取1个训练数据作为一个质心

​ ③计算每一个点到已有的簇的距离,然后选取最小距离。最后将所有最小距离除以所有最小距离的总和,转化为概率,以该概率在原始数据中继续抽取1个质心。反复迭代③,直到抽取到k个质心。

​ ④对训练数据中每一个数据都与每一个均值点计算距离。每一个数据与某一个均值点最近,就认定其属于该均值点的簇。

​ ⑤对得到的每一个簇,计算簇中的所有点的均值作为新的均值点(质心)。

​ ⑥训练③、④步,直到均值点收敛不再改变。

代码实现

k_mean

​ 训练过程图如下所示。可以看到质心是一点点往各自的簇中心移动的。ps:代码使用欧氏距离
在这里插入图片描述

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import imageio
plt.ion()
images=[]
class K_Mean():
    def __init__(self,x):
        self.x=x
        self.mean = np.mean(x, axis=0)
        self.std = np.std(x, axis=0)
        self.k_data=0
    def fit(self,k):#k是簇
        self.x=(self.x-self.mean)/self.std#标准化
        #随机选取k个值(质心)
        index=np.random.choice(self.x.shape[0],k,replace=False)
        k_data=self.x[index]
        self.k_data=k_data.copy()#为了将数据保存到模型中,预测的时候好调用。
        distances=np.empty(shape=(self.x.shape[0],k))#对每一个k(质心)的值计算他和其他点的距离,结果按列保存到distances中
        while True:#作循环,直到模型收敛
            for j,each_k in enumerate(k_data):
                #对每一个数据减去均值点然后求平方和,实际上就是欧氏距离
                result=self.x-each_k
                distance=np.linalg.norm(result,ord=2,axis=1).reshape(-1)
                distances[:,j]=distance#将结果保存到distances中
            #计算数据点和哪个簇近
            a=np.argmin(distances,axis=1)
            #对每一个簇重新计算均值点并更新
            for each in range(k):
                mean=np.mean(self.x[a==each],axis=0)
                k_data[each,:]=mean
            plot_figure(self.x, a,self.k_data)#绘图函数,与模型无关
            if (self.k_data==k_data).all():#判定模型是否收敛,收敛则打破循环
                #以下都是绘图和保存相关,与模型无关
                imageio.mimsave("result.gif", images, duration=0.3)
                plt.ioff()
                plt.show()
                #结束循环
                break
            else:
                self.k_data=k_data.copy()
            #绘图
            plt.pause(0.5)
    def predict(self,x): #预测函数,与fit函数差不多同理
        x=(x-self.mean)/self.std #标准化
        result=np.ones(shape=(x.shape[0],1),dtype=int) #生成用于储存结果的数组
        for index,i in enumerate(x):
            L2=np.linalg.norm(self.k_data-i,ord=2,axis=1)
            classify=np.argmin(L2)
            result[index,:]=classify
        return result
def plot_figure(x,y,k_data):
    '''绘图函数,与模型无关'''
    map_color={0:"r",1:"g",2:"b"}
    color_initial=[map_color[i] for i in y.squeeze()]
    plt.cla()
    plt.scatter(x[:,0],x[:,1],c=color_initial)
    plt.scatter(k_data[:, 0], k_data[:, 1], c="y", marker="*")
    plt.savefig("1.png")
    images.append(imageio.v3.imread("1.png"))
    plt.show()
def main():
    #生成第一类数据g
    x1=stats.norm.rvs(0,2,(100,2))
    #生成第二类数据r
    x2=stats.norm.rvs(6,2,(100,2))
    #生成第三类数据b
    x3=stats.norm.rvs(12,2,(100,2))
    #合并数据
    x=np.concatenate([x1,x2,x3],axis=0)
    #初始化并训练模型参数
    model=K_Mean(x)
    model.fit(3)
    #预测结果
    result=model.predict(x2)
if __name__ == '__main__':
    main()

k_mean++

​ 可以看到k_mean++算法明显比k普通的能够更快收敛一些:
在这里插入图片描述

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import imageio
plt.ion()
images=[]
class K_Mean():
    def __init__(self,x):
        self.x=x
        self.mean = np.mean(x, axis=0)
        self.std = np.std(x, axis=0)
        self.k_data=0
    def fit(self,k):#k是簇
        self.x=(self.x-self.mean)/self.std#标准化
        #随机选取1个中心点(质心)
        k_data= self.x[np.random.choice(self.x.shape[0], 1, replace=False)]
        #dis用于储存距离
        dis = np.empty(shape=(self.x.shape[0]), dtype=int)
        # 循环选取其余的k均值点:
        for j in range(1,k):
            #循环每一个样本点,计算它们与最短均值点的距离并保存
            for i in range(self.x.shape[0]):
                result=k_data-self.x[i,:]
                L2=np.linalg.norm(result,ord=2,axis=1)
                dis[i]=np.min(L2)
            #计算概率
            p=dis/dis.sum()
            #依据概率选取均值点
            new_k_data=self.x[np.random.choice(self.x.shape[0],1,p=p)]
            k_data=np.insert(k_data,j,new_k_data,axis=0)

        self.k_data=k_data.copy()#为了将数据保存到模型中,预测的时候好调用。
        distances=np.empty(shape=(self.x.shape[0],k))#对每一个k(质心)的值计算他和其他点的距离,结果按列保存到distances中
        while True:#作循环,直到模型收敛
            for index,each_k in enumerate(k_data):
                #每一个样本与均值都做差然后求平方和,实际上就是欧氏距离
                result=self.x-each_k
                distance=np.linalg.norm(result,ord=2,axis=1).reshape(-1)
                distances[:,index]=distance#将结果保存到distances中
            #计算数据点和哪个簇近
            a=np.argmin(distances,axis=1)
            #对每一个簇重新计算均值点并更新
            for each in range(k):
                mean=np.mean(self.x[a==each],axis=0)
                k_data[each,:]=mean
            plot_figure(self.x, a,self.k_data)#绘图函数,与模型无关
            if (self.k_data==k_data).all():#判定模型是否收敛,收敛则打破循环
                #以下都是绘图和保存相关,与模型无关
                imageio.mimsave("result1.gif", images, duration=0.3)
                plt.ioff()
                plt.show()
                #结束循环
                break
            else:
                self.k_data=k_data.copy()
            #绘图
            plt.pause(0.5)
    def predict(self,x): #预测函数,与fit函数差不多同理
        x=(x-self.mean)/self.std #标准化
        result=np.ones(shape=(x.shape[0],1),dtype=int) #生成用于储存结果的数组
        for index,i in enumerate(x):
            L2=np.linalg.norm(self.k_data-i,ord=2,axis=1)
            classify=np.argmin(L2)
            result[index,:]=classify
        return result
def plot_figure(x,y,k_data):
    '''绘图函数,与模型无关'''
    map_color={0:"r",1:"g",2:"b"}
    color_initial=[map_color[i] for i in y.squeeze()]
    plt.cla()
    plt.scatter(x[:,0],x[:,1],c=color_initial)
    plt.scatter(k_data[:, 0], k_data[:, 1], c="y", marker="*")
    plt.savefig("1.png")
    images.append(imageio.v3.imread("1.png"))
    plt.show()
def main():
    #生成第一类数据g
    x1=stats.norm.rvs(0,2,(100,2))
    #生成第二类数据r
    x2=stats.norm.rvs(6,2,(100,2))
    #生成第三类数据b
    x3=stats.norm.rvs(12,2,(100,2))
    #合并数据
    x=np.concatenate([x1,x2,x3],axis=0)
    #初始化并训练模型参数
    model=K_Mean(x)
    model.fit(3)
    #预测结果
    result=model.predict(x2)
if __name__ == '__main__':
    main()

结束语

​ 以上就是k_mean算法的简单介绍和代码实现啦。过程并不严谨,如有错误,还请指正,阿里嘎多。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值