模式识别实验报告
学号:
姓名:
日期:
实验 4 K 均值聚类算法
1 实验目的
1.理解掌握 K-means 聚类算法的基本原理;
2.学会用 python 实现 K-means 算法
2 实验环境
Windows 10,Python 3.7.4,PyCharm 2019.2.3
3 实验原理
3.1 聚类简介
俗话说:“物以类聚,人以群分”,在自然科学和社会科学中,存在着大量的分类问题。
所谓类,通俗地说,就是指相似元素的集合。
而对于分类问题,我们通常不会提供 x 与 y 这样的映射关系,对于这种用机器自动找出
其中规律并进行分类的问题,我们称为聚类。
聚类在实际的应用中亦是非常广泛的,如:市场细分(Market segmentation)、社交
圈分析(social network analysis)、集群计算(organize computing clusters)、天体
数据分析(astronomical data analysis)
聚类与分类最大的区别在于,聚类过程为无监督过程,即待处理数据对象没有任何先验
知识,而分类过程为有监督过程,即存在有先验知识的训练数据集。
3.2 K-means 算法简介
K-Means 算法是典型的基于距离的聚类算法,其中 k 代表类簇个数,means 代表类簇内
数据对象的均值(这种均值是一种对类簇中心的描述),因此,K-Means 算法又称为 k-均值
算法。K-Means 算法是一种基于划分的聚类算法,以距离作为数据对象间相似性度量的标准,
即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。数据对象
间距离的计算有很多种,k-means 算法通常采用欧氏距离来计算数据对象间的距离。该算法
认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
3.3 K-means 工作流程
首先,随机确定 k 个初始点作为质心。然后将数据集中的每个点分配到一个簇中,具体
来讲,为每个点找距其最近的质心,并将其分配给该质心所对应的簇。这一步完成之后,每
一个簇的质心更新为该簇所有点的平均值。
上述过程的伪代码表示如下:
创建 k 个点作为起始质心(经常是随机选择)
当任意一点的簇分配结果发生改变时
对数据集中的每一个数据点
对每一个质心
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每一个簇,计算簇中所有点的均值并将均值作为质心
4 实验内容
4.1 数据集
在样本空间内设定 4 个固定的点(1,1)(1,2)(2,2)(2,1),如图红色的“▲”,在每一个
点的附近都随机生成 50 个绿色的点“+”,对所有的绿色的“+”进行聚类分析。
图 1 初始样本图
4.2 实验步骤
(1)在四个中心点附近产生一堆数据
real_center = [(1, 1), (1, 2), (2, 2), (2, 1)]
point_number = 50
points_x = []
points_y = []
for center in real_center:
offset_x, offset_y = np.random.randn(point_number) * 0.3, np.random.randn(point_number)
* 0.25
x_val, y_val = center[0] + offset_x, center[1] + offset_y
points_x.append(x_val)
points_y.append(y_val)
points_x = np.concatenate(points_x) # 将二维数组拼接成一个 list
points_y = np.concatenate(points_y)
# 绘制点图
plt.scatter(points_x, points_y, color='green', marker='+')
# 绘制中心点
center_x, center_y = zip(*real_center)
plt.scatter(center_x, center_y, color='red', marker='^')
plt.xlim(0, 3)
plt.ylim(0, 3)
plt.show()
(2)随机选择 K 个点
K = 4
p_list = np.stack([points_x, points_y], axis=1)
index = np.random.choice(len(p_list), size=K)
centeroid = p_list[index]
# 以下是画图部分
for p in centeroid:
plt.scatter(p[0], p[1], marker='^')
plt.xlim(0, 3)
plt.ylim(0, 3)
plt.show()
图 2 随机选取的 K 个质心
(3)遍历所有点 P,将 P 放入最近的聚类中心的集合中
points_set = {key: [] for key in range(K)}
for p in p_list:
nearest_index = np.argmin(np.sum((centeroid - p) ** 2, axis=1) ** 0.5)
points_set[nearest_index].append(p)
# 以下是画图部分
for k_index, p_set in points_set.items():
p_xs = [p[0] for p in p_set]
p_ys = [p[1] for p in p_set]
plt.scatter(p_xs, p_ys, color='C{}'.format(k_index))
for ix, p in enumerate(centeroid):
plt.scatter(p[0], p[1], color='C{}'.format(ix), marker='^', edgecolor='black', s=128)
plt.xlim(0, 3)
plt.ylim(0, 3)
plt.show()
图 3 将 P 放入最近的聚类中心的集合
(4)遍历每一个点集,计算新的聚类中心
for k_index, p_set in points_set.items():
p_xs = [p[0] for p in p_set]
p_ys = [p[1] for p in p_set]
centeroid[k_index, 0] = sum(p_xs) / len(p_set)
centeroid[k_index, 1] = sum(p_ys) / len(p_set)
(5)重复进行以上步骤
4.3 实验结果
循环执行了 10 次,绘制出了每聚类完一次的样本分类情况和质心的位置。
四个质心的坐标为:
以上已经介绍了 KMeans 方法的具体流程,但是我们还面临一个问题,如何确定 K 值—
—在以上的演示中,由于数据是我们自己生成的,所以我们很容易就确定了 K 值,但是真实
的环境下,我们往往不能立马清楚 K 值的大小。一种比较通用的解决方法是计算每个点到自
己的聚类中心的平均距离,虽然说,K 值越大,理论上这个平均距离会越小。但是当我们画
出平均距离随 K 值的变化曲线后,会发现其中存在一个肘点——在这个肘点前,平均距离随
K 值变大迅速下降,而在这个肘点后,平均距离的下降将变得缓慢。现在我们使用 sklearn
库中的 KMeans 方法来跑一下聚类过程,然后将到聚类中心的平均值变化作图。
5 实验小结
通过这次实验我对 K-means 有了进一步的更加直观的理解,但是在代码编写中存在冗
余,有待改进。对于聚类算法来说 K-means 算法原理简单;计算复杂度是 O(NKt),N 为数据
对象的数目,K 是聚类中心的数目,t 是迭代的次数;对于大数据集的处理,K-means 算法
具有可伸缩性和高效性。
但也存在缺点:需要预先设定 K 值,K 值得设定和真实的数据样本未必是吻合的;求解
的结果是局部最优而非全局最优(当数据簇的分布差异较大时表现的不好);容易受到噪声
点的影响;样本最后只能被分到单一的类别中。