kmeans及python图解实现

  在无监督学习中,训练样本的标记信息是未知的,网络是通过对无标记样本的学习来揭示数据的内在性质和规律。在无监督学习中,应用最多的就是聚类。
  简单的理解聚类:聚类就是把数据划分为不同的组,组内的数据具有相似的属性和特征,组间的数据具有高度不相关的属性和特征。即把相似的东西分为一组。
那么,组内相似越大,组间差别越大,那么聚类的效果就会很好。

难点:如何评估(不知到分类结果到底怎么样),如何调参(分成多少类合适)
现在,就其中最简单的算法,kmeans进行学习。
基本概念:
簇:相似的堆的个数,用k值表示
质心:均值,即对数据各个维度取平均
距离的度量:常用欧式距离
优化目标: min i = 1 K x C i d i s t ( c i , x ) 2 \min \sum\limits_{i = 1}^K {\sum\limits_{x \in {C_i}} {dist{{\left( {{c_i},x} \right)}^2}} }
具体例子:
在这里插入图片描述

  • 图a:我们想要生成2堆数据,即簇为2,K=2。
  • 图b:首先,我们先随机产生两个点,对应图中红×和蓝×。
  • 图c:分别计算图中各点到红×和蓝×之间的距离,如果离红×比较近,那么这个点被归为红色点,反之,则归为蓝色点。
  • 图d:经过步骤3中,分好了2个数据堆,如图d,分别计算红色堆和蓝色堆的质心,即在两个维度上的均值。对应图d中的红叉和蓝×
  • 图e:同步骤3
  • 图f:继续计算当前质心,更新。继续遍历所有数据,直到满足停止条件。
    下图是kmean算法步骤,注意看停止条件。

在这里插入图片描述
这里停止条件的意思是,直到新质心的位置相比旧质心的位置不再改变,或者差值在一个很小的数内,就可以停止了。或者也可以把迭代次数作为停止条件。

  • 优点:简单,适合常规的数据集
  • 缺点:k值难以确定,复杂度比较高因为存在遍历,很难发现任意形状的簇。

代码实现:

testset 是一个80x2 的txt文件。总共80个点,每个点有2个特征。
代码实现参考b站:k-means 实战 / 非监督问题(文刀出品)自己对代码进行了细致的分析与注释,感谢大佬。

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
##假设k取4
data = pd.read_table('C:/Users/red/Desktop/a1/testSet.txt', header=None, names=['x', 'y'])
##没有表头,read_table去Tab
x = data['x']
y = data['y']
plt.subplot(2,1,1)
plt.scatter(x, y)

def distance(data, centers):
    #     data: 80x2, centers: kx2
    dist = np.zeros((data.shape[0], centers.shape[0])) ## 出来的是80*4,即每个点相对4个质心的距离
    for i in range(len(data)):
        for j in range(len(centers)):
            dist[i, j] = np.sqrt(np.sum((data.iloc[i, :] - centers[j]) ** 2)) ##共80个样本,每个样本的在两个特征维度上,
                                                                            # 分别对k个质心求距离然后求和,类似矩阵乘法,
                                                                            # 所以距离矩阵为80x4

    return dist

def near_center(data, centers): ##得到每个点离哪个质心最近,返回对应质心的label
    dist = distance(data, centers)
    near_cen = np.argmin(dist, 1)  ##得到的dist行为80个点,列为每个点到4个质心的距离。然后取最小距离,得到对应质心的label。
    return near_cen

def kmeans(data, k):
    # step 1: init. centers
    centers = np.random.choice(np.arange(-5, 5, 0.1), (k, 2)) ##随机产生质心
    print(centers)

    for _ in range(10): #做10次迭代
        # step 2: 点归属
        near_cen = near_center(data, centers)
        # step 3:簇重心更新
        for ci in range(k): ##每次点划分完之后,安照步骤,需要重新寻找各个簇的质心,即求平均
            centers[ci] = data[near_cen == ci].mean()
    return centers, near_cen

centers, near_cen = kmeans(data, 4)
print(near_cen)
plt.subplot(2,1,2)
plt.scatter(x, y, c=near_cen)
plt.scatter(centers[:, 0], centers[:, 1], marker='*', s=500, c='r')
plt.show()

对代码中distance函数及质心的更新解释如下:
在这里插入图片描述
假设:

  1. testset数据集为80x2,相当于二维平面上的80个点,每个点的坐标为(x,y)。分簇数为4,就得到图中前两个矩阵a和b,a中每个点(总共80个)都要对b中初始随机生成的4个点(质心)依次求距离,如上图,画出了2次的。所以,得到的距离矩阵c为80x4的,行为80个点,列为每个点到4个质心的距离。
  2. 由1可得,在near_center()函数里,是对每一行,求距离的最小值。返回每个点对应的最近质心的label。
  3. 在更新质心时,是对已经分好簇的点,分别求平均,来更新质心。 循环终止条件是迭代次数。
    最终,得到结果如下图所示:
    在这里插入图片描述
    代码还有很多改进的地方,如替换for循环为矩阵计算,这样运算速度会快,不过以上代码理解kmeans的思想是足够了。
    另外,可以通过python内置的sklearn库实现好的kmeans算法,对鸢尾花数据集进行聚类分析。
    代码如下:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris

iris = load_iris()
X = iris.data[:, :]
#绘制数据分布图
plt.subplot(2,1,1)
plt.scatter(X[:, 0], X[:, 1], c = "red", marker='o', label='see')
plt.legend(loc=2)
estimator = KMeans(n_clusters=3)#构造聚类器
estimator.fit(X)#聚类
label_pred = estimator.labels_ #获取聚类标签

#绘制k-means结果
x0 = X[label_pred == 0]
x1 = X[label_pred == 1]
x2 = X[label_pred == 2]
# x3 = X[label_pred == 3]
plt.subplot(2,1,2)
plt.scatter(x0[:, 0], x0[:, 1], c = "red", marker='o', label='label0')
plt.scatter(x1[:, 0], x1[:, 1], c = "green", marker='*', label='label1')
plt.scatter(x2[:, 0], x2[:, 1], c = "blue", marker='+', label='label2')
# plt.scatter(x3[:, 0], x3[:, 1], c = "blue", marker='x', label='label3')
plt.legend(loc=2)
plt.show()

在这里插入图片描述
另外,刚开始随机初始点的选择,因此最终求得的簇的划分与随机选取的“簇中心”有关,这是因为kmeans算法收敛到了局部最小值,而非全局最小值。既然对初值很敏感,可以做多次蒙特卡洛仿真求平均
参考:利用python内置K-Means聚类算法实现鸢尾花数据的聚类
k-means 实战 / 非监督问题(文刀出品)

发布了61 篇原创文章 · 获赞 17 · 访问量 2880

猜你喜欢

转载自blog.csdn.net/qq_35027690/article/details/103922286