对K-MEANS算法个人理解

注:参考他人的博客。
https://www.cnblogs.com/yixuan-xu/p/6272208.html
https://www.cnblogs.com/xiaxianfei/p/5385081.html
https://blog.csdn.net/u013181595/article/details/80362292
** 一、类属**
K-MEANS属于的是无监督机器学习的算法,其实现原理简单,在数学建模,或者是在项目研究可谓是会经常老熟人或者是老熟人的变种之一,它属于模式聚类,会改变样本点的分布形式。
** 二、核心思想**
使同一聚类中的对象相似度较高,而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得来进行计算。算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近它们的对象归类。通过重复迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
三、算法流程
(1)第一步是初始化随机选择质心,为待聚类的点寻找聚类中心;
(2)第二步是计算每个点到聚类中心的距离,将每个点聚类到离该点最近的聚类中去;
(3)第三步是计算每个聚类中所有点的坐标平均值,并将这个平均值作为新的聚类中心,更新;
反复执行(2)、(3),直到聚类中心不再进行大范围波动或者聚类次数达到要求为止,及收敛误差、和迭代次数限制。
四、个人评价
属于最底层的聚类算法之一、很经典,但是优缺点兼备!
超参数难以事先确定,收敛或许靠碰运气。
首先提两个概念:
时间复杂度:执行算法所需要的计算工作量
空间复杂度:执行这个算法所需要的内存空间
一个好的算法,应该是二者均满足使用者要求。
底层的K-means聚类算法的时间复杂度与样本数N近于线性关系(一次方),对于大体量数据集有较高的处理效率,并且具有可伸缩性,其无参数化与无监督性K-means的簇类数等超参数有时较难确定,且对异常值较为敏感,可考虑采用ISODATA、Kernel K-means算法进行改进优化。
其中优化的角度无非是三个方面,一是对(质心距离)的计算、二是归类的原则、三就是深层次的算法思想。
例子1:ISODATA的全称是迭代自组织数据分析法,当属于某个类别的样本数过少时把这个类别去除,当属于某个类别的样本数过多、分散程度较大时把这个类别分为两个子类别(也即是对质心参数设定有),
例2,Kernel K-means:传统K-means采用欧式距离进行样本间的相似度度量,显然并不是所有的数据集都适用于这种度量方式。参照支持向量机中核函数的思想,将所有样本映射到另外一个特征空间中再进行聚类,就有可能改善聚类效果。注意:还有K-L散度、马氏距离等。
例3,K-means++:原始K-means算法最开始随机选取数据集中K个点作为聚类中心,而K-means++按照如下的思想选取K个聚类中心:假设已经选取了n个初始聚类中心(0<n<K),则在选取第n+1个聚类中心时:距离当前n个聚类中心越远的点会有更高的概率被选为第n+1个聚类中心。在选取第一个聚类中心(n=1)时同样通过随机的方法。可以说这也符合我们的直觉:聚类中心当然是互相离得越远越好。这个改进虽然直观简单,但是却非常得有效。

五、代码与效果图
**利用numpy、matplotlib**库绘制出来的图
利用此图为numpy、matplotlib库、绘制

‘@author XFXZ_EDITED’
import time
from numpy import *
import matplotlib.pyplot as plt

#导入数据,后续所有的过程 均以函数封装了
def loadDataSet(fileName):
    dataSets = []
    data = []
    f = open(fileName)
    for line in f.readlines():
        curLine = line.strip().split('\t')
        x = float(curLine[0])
        y = float(curLine[1])
        dataSets.append([x,y])
    return mat(dataSets)
#计算距离
def distEclud(vecA, vecB):
    return np.linalg.norm(vecA, vecB)
#随机生成k个点作为初始质心
def randCent(dataSet, k):
    n = shape(dataSet)[1] #n是列数
    centroids = mat(zeros((k, n)))
    for j in range(n):
        minJ = min(dataSet[:, j]) #找到第j列最小值
        rangeJ = float(max(dataSet[:, j]) - minJ) #求第j列最大值与最小值的差
        centroids[:, j] = minJ + rangeJ * random.rand(k, 1) #生成k行1列的在(0, 1)之间的随机数矩阵
    return centroids
#K均值聚类算法实现
def KMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = shape(dataSet)[0] #数据集的行
    clusterAssment = mat(zeros((m, 2)))
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m): #遍历数据集中的每一行数据
            minDist = 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, :] = minIndex, minDist**2 #记录最小距离质心下标,最小距离的平方
        print(centroids)
        for cent in range(k): #更新质心位置
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] #获得距离同一个质心最近的所有点的下标,即同一簇的坐标
            centroids[cent,:] = mean(ptsInClust, axis=0) #求同一簇的坐标平均值,axis=0表示按列求均值
    return centroids, clusterAssment
#绘制散点图
def showCluster(dataSet, k, clusterAssment, centroids):
    fig = plt.figure()
    plt.title("K-means")
    ax = fig.add_subplot(111)
    data = []
    for cent in range(k): #提取出每个簇的数据
        ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]] #获得属于cent簇的数据
        data.append(ptsInClust)
    for cent, c, marker in zip( range(k), ['r', 'g', 'b', 'y'], ['^', 'o', '*', 's'] ): #画出数据点散点图
        ax.scatter(array(data[cent][:, 0]), array(data[cent][:, 1]), s=80, c=c, marker=marker)
    ax.scatter(array(centroids[:, 0]), array(centroids[:, 1]), s=1000, c='black', marker='+', alpha=1) #画出质心点
    ax.set_xlabel('X Label')
    ax.set_ylabel('Y Label')
    plt.show()
def main():
    dat = mat(loadDataSet('Test_data.txt'))  # 读入数据
    center, clust = KMeans(dat, 4)
    randCent(dat,4)
    showCluster(dat, 4, clust, center)

if __name__ == "__main__":   #非被导入模块下运行主程序
    times=time.time()
    main()
    timee=time.time()
print(str(timee-times)+'s')   #计算聚类所用时间

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值