手写算法-python代码实现Kmeans

手写算法-python代码实现Kmeans

原理解析

今天,我们来讲一下Kmeans,一种无监督聚类算法,也是最为经典的基于划分的聚类方法,它的思想是:对于给定的样本集,按照样本之间的距离大小,将样本集划分为K个簇。让簇内的点尽量紧密的连在一起,而让簇间的距离尽量的大。

实现流程如下:
1、先确定数据集聚类个数k;
2、在数据集中随机选取k个数据,作为初始质心;
3、计算数据集中每个样本到每个质心的距离,把样本划分到
距离最小的质心所属的类别;
4、根据聚类结果,重新计算质心,当本次计算的质心与上一次质心完全一样(或者收敛)时,停止迭代;
否则更新质心,继续执行步骤2、3、4。

流程比较简单,这里要说的是注意几个问题:
1、步骤1确定数据集聚类个数k,很显然,当数据集D是高维数据时,没办法很快确定合适的k;
2、步骤2随机选取数据作为初始质心,可以想象,当质心之间距离很近的时候,将需要迭代很多次,会影响收敛速度,甚至不能收敛,只能得到局部最小值;
3、步骤3中计算距离,我们应该想到,距离受到量纲的影响,在使用时,需要考虑一下标准化;
然后就是基于所有样本,因此会对异常数据比较敏感;
4、当数据集很大时,计算的时间复杂度很高。

可见优化的点很多(优化部分我们明天再写,今天只实现最基础的Kmeans)。

代码实现

基于上面描述的过程,我们来写python实现代码:

import numpy as np
from sklearn.datasets import make_blobs
from matplotlib import pyplot as plt

#无监督算法,学习过程就是训练质心的位置,进行聚类
class Kmeans:
    def __init__(self,k):
        self.k = k
    
    def calc_distance(self,x1,x2):
        diff = x1 - x2
        distances = np.sqrt(np.square(diff).sum(axis=1))
        return distances
    
    def fit(self,x):
        self.x = x
        m,n = self.x.shape
        #随机选定k个数据作为初始质心,不重复选取
        self.original_ = np.random.choice(m,self.k,replace=False)
        #默认类别是从0到k-1
        self.original_center = x[self.original_]
        while True:
            #初始化一个字典,以类别作为key,赋值一个空数组
            dict_y = {}
            for j in range(self.k):
                dict_y[j] = np.empty((0,n))
            for i in range(m):
                distances =self.calc_distance(x[i],self.original_center)
                #把第i个数据分配到距离最近的质心,存放在字典中
                label = np.argsort(distances)[0]
                dict_y[label] = np.r_[dict_y[label],x[i].reshape(1,-1)]
            centers = np.empty((0,n))
            #对每个类别的样本重新求质心
            for i in range(self.k):
                center = np.mean(dict_y[i],axis=0).reshape(1,-1)
                centers = np.r_[centers,center]
            #与上一次迭代的质心比较,如果没有发生变化,则停止迭代(也可考虑收敛时停止)
            result = np.all(centers == self.original_center)
            if result == True:
                break
            else:
                #继续更新质心
                self.original_center = centers

    def predict(self,x):
        y_preds = []
        m,n = x.shape
        for i in range(m):
            distances =self.calc_distance(x[i],self.original_center)
            y_pred = np.argsort(distances)[0]
            y_preds.append(y_pred)
        return y_preds

实例演示

下面我们来生成一些数据集,来看看我们写的Kmeans聚类效果:

#下面是我今天找到的一个很有代表性的数据集,哈哈,为什么这么说,后面会用到
x,y = make_blobs(centers=5,random_state=20,cluster_std=1)
plt.scatter(x[:,0],x[:,1])
plt.show()

在这里插入图片描述

从图像上看,我们适合把这些数据分成5个类别,跑一下代码看一下结果:

model = Kmeans(k=5)
model.fit(x)
y_preds = model.predict(x)
plt.scatter(x[:,0],x[:,1],c=y_preds)
plt.show()

在这里插入图片描述
,聚类得挺完美的,等一下,有人跑出不一样的结果:

在这里插入图片描述
在这里插入图片描述
哈哈,要的就是这个效果(大家多重复执行一下上面这段代码,就会发现结果各种各样),为什么呢?
答案就在我们刚才说的几个问题上,就是步骤2的问题,
具体怎么优化我们明天写。

sklearn对比

上面的问题,sklearn中的Kmeans算法,会不会也出现呢?
我们来验证一下:

from sklearn.cluster import KMeans
model_1 = KMeans(n_clusters=5)
y_pred_1 = model_1.fit_predict(x)
plt.scatter(x[:,0],x[:,1],c=y_pred_1)
plt.show()

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以发现,不管执行多少次,聚类效果都很稳定,都很好。(原因解析,请看:链接: 手写算法-python代码实现Kmeans++以及优化

总结

K-Means是个简单实用的聚类算法,现在对K-Means的优缺点做一个总结。

K-Means的主要优点有:
1、原理比较简单,实现也是很容易,收敛速度快。
2、聚类效果较优。
3、算法的可解释度比较强。
4、主要需要调参的参数仅仅是簇数k。

K-Means的主要缺点有:
1、K值的选取不好把握
2、对于不是凸的数据集比较难收敛
3、如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。
4、采用迭代方法,得到的结果只是局部最优。
5、对噪音和异常点比较的敏感。

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值