Kmeans算法的实现主要分为个部分。
首先在网易公开课上系统学习了吴恩达的《深度学习公开课》,了解了Kmeans背后的算法逻辑,如下图所示:
接下来,我尝试使用代码对Kmeans进行实现。实现过程当中,我将随机样本设置为簇中心,在生成随机数据时,我使用 np.random.randn(n_samples, dimensions) 生成一个随机的正态分布矩阵,通过乘以 cluster_std 来调整数据点在第一个维度上的分布,使得数据点在第一个维度上的分布更加紧密或分散。这样生成的数据点将具有随机性,但同时在第一个维度上会有一定的聚集性,这有助于模拟实际数据中的聚类现象。
总共分为分为了两个文件,一个是Kmeans.py,这个文件实现了生成随机数据,并将数据进行聚类,接着将数据保存在excel当中。另一个是visualize.py,这个文件将聚类后的数据画出来,并按照聚类的label进行标注颜色。
以下是Kmean.py:
import numpy as np
import pandas as pd
import openpyxl
def Kmeans(data_points,k,size):
"""
:param data: 需要进行聚类的所有数据
:param k: 聚类中心的个数
:return: 返回簇的坐标和标签
"""
#随机将样本当中的几个点设置成簇
indices = np.random.choice(data.shape[0],k,replace = False)
#np.random.choice: 用于从给定的序列中随机选择元素,返回一个由所选元素索引组成的数组
#data.shape[0]: 表示data数组的第一维度的大小,即数据点的数量
#k: 表示要生成的簇中心的数量,即K-means中的K
#replace=False: 表示选择是不放回的,即每个索引只被选择一次
#返回被选为簇的点的信息
cluster_centers = data[indices]
for _ in range(size):
# 计算每个数据点到每个簇中心的距离,并分配到最近的簇
#_表示不会在循环当中使用变量
distances = np.linalg.norm(data[:, np.newaxis] - cluster_centers, axis=2)
#np.linalg.norm用于计算两点之间的距离
#axis=2指定沿着最后一个维度,得到每个点与每个簇中心的距离
labels = np.argmin(distances, axis=1)
# np.argmin返回指定轴的最小值的索引
#axis=1指定沿着指定沿着第二个轴(即每个数据点的簇中心距离数组)找到最小值的索引,即每个数据点最近的簇中心的索引
# 更新簇中心为每个簇所有点的均值
new_cluster_centers = np.array([data[labels == i].mean(axis=0) for i in range(k)])
#data[labels == i]: 选择标签为i的数据点
#mean(axis=0): 计算所选数据点的均值,得到第i个簇的新中心
if np.all(cluster_centers == new_cluster_centers):
break
#如果簇不在更新,算法停止运行
cluster_centers = new_cluster_centers
return cluster_centers, labels
def generate_data(size,dimension,centers,cluster_std):
"""
:param size: 表示生成数据的总个数
:param dimension: 表示生成数据的维度
:param centers: 随机生成数据能被大致划分成簇的个数
:param cluster_std: 浮点数,表示簇内数据的标准差
:return: 返回生成的数据
"""
data = np.random.randn(size,dimension)
data[:,0]*=cluster_std
return data
def save_data(data,labels,filename):
#将数据和数据的标签都保存在excel当中
file = pd.DataFrame(data)
file['Cluster'] = labels
with pd.ExcelWriter(filename) as writer:
file.to_excel(writer, index = False)
if __name__ == '__main__':
size = 200
dimension = 2
center = 5
cluster_std = 0.4
data = generate_data(size = size,dimension = dimension,centers=center,cluster_std = cluster_std)
#随机生成二维,五个簇的随机数据
#进行聚类
k = 5
cluster_centers, labels = Kmeans(data,k = k,size = size)
filename = 'Kmeans_random_data.xlsx'
save_data(data,labels,filename=filename)
以下是聚类可视化的实现代码:
import pandas as pd
import matplotlib.pyplot as plt
import openpyxl
if __name__ == '__main__':
data = pd.read_excel('Kmeans_random_data.xlsx',engine='openpyxl')
#检查表格当中的前几行数据是否被正确读入
print(data.head(7))
x = data.iloc[:,0].values
y = data.iloc[:,0].values
#去掉重复标签,留下所有的类别
unique_category = data['Cluster'].unique()
print(unique_category)
#制定一些颜色,方便之后使用
colors = ['r','g','b','c','m','y','k']
for i, category in enumerate(unique_category):
# 选择当前类别的数据
category_data = data[data.iloc[:, 2] == category]
x_category = category_data.iloc[:, 0].values
y_category = category_data.iloc[:, 1].values
# 绘制当前类别的点
plt.scatter(x_category, y_category, color=colors[i], label=str(category))
# 添加图例
plt.legend()
# 添加标题和轴标签
plt.title('Kmeans')
plt.xlabel('x')
plt.ylabel('y')
# 显示图形
plt.show()
在实验过程当中,我调整了cluster_std的数值,也就是正态随机数据的方差。在聚类分析中,数据的分布直接影响聚类的效果。调整数据的方差和范围可以帮助聚类算法更好地识别数据中的模式和结构。例如,如果数据在某个维度上的方差较小,聚类算法可能会将这些点视为一个紧密的群体。下面是调整x轴坐标方差,然后进行聚类的结果:
下面两幅图将x轴的方差设为0.4,也就是将x轴坐标进行缩小
下面两幅图我将x的方差调整为2
下面两幅图,我将x的方差调整为20
可以明显的看到,用正态分步生成随机数据,如果只对某一个轴向(例如x轴)的数据乘以一个较大的标量(通常大于10),或对另一个轴向(y轴)乘以一个缩小的标量(小于1),那么Kmeans算法的聚类结果会呈现为k个长条,长条短边沿x轴方向。这是因为kmeans算法本身在运算的过程当中会不断计算聚类中心与样本点的距离,如果将x轴坐标乘以较大的标量,就相当于在距离计算公式当中给x轴赋予了更大的权重,所以算法倾向于挑选x轴数值上相差较小的点作为一个聚类中心