实验目的
了解无监督任务范式概念,掌握聚类思想,掌握k-means算法基本原理和实现方法。
实验要求
编程实现k均值聚类算法,对如下数据进行聚类。对于k均值算法,随机从样本中选出k个点作为初始聚类中心,并设置迭代次数为100。依次将聚类数设置为k=1,2,3,...,10,计算相应聚类结果的簇内平方误差指标。
绘制不同k值时聚类结果图,用不同颜色表示不同的类。绘制loss值随k值增加的变化曲线图。
实验环境
Python, numpy, matplotlib
实验代码
import numpy as np
from matplotlib import pyplot as plt
import matplotlib
matplotlib.use('TKAgg')
# 导入数据
train_dataset = np.genfromtxt("experiment_10_training_set.csv", delimiter=",", skip_header=1)
rowOfTrainDataset = train_dataset.shape[0]
columnOfTrainDataset = train_dataset.shape[1]
# 设置训练轮数和最终类数
epochs = 100
T = 10
# 画图颜色列表
colors = [
'red', 'blue', 'green', 'purple', 'orange',
'brown', 'pink', 'gray', 'olive', 'cyan'
]
# 画曲线数据
x_line = np.arange(1, T+1)
y_line = np.zeros(T)
# k从1到10
for k in range(1, T+1):
# 初始化均值向量
u = np.zeros((k, 2))
# 随机选取k个向量
ini = np.random.choice(np.arange(0, rowOfTrainDataset), k, replace=False)
# 最终每个类的向量下标
result = [[] for _ in range(k)]
# 初始化损失
loss = 0
# 设置均值向量
for i in range(k):
u[i, :] = train_dataset[ini[i], :]
# 循环轮数
for epoch in range(epochs):
# 每一轮结果
res = [[] for _ in range(k)]
# 遍历数据
for i in range(rowOfTrainDataset):
# 初始化距离和类别
dis = np.inf
label = -1
# 计算所有类的距离
for j in range(k):
dis_temp = np.sqrt(np.sum((train_dataset[i, :] - u[j, :]) ** 2))
if dis_temp < dis:
dis = dis_temp
label = j
# 将结果存储
res[label].append(i)
# 所有均值向量是否改变
flag = 0
# 更新均值向量
for i in range(k):
he = np.zeros((1, columnOfTrainDataset))
for j in res[i]:
he += train_dataset[j, :]
he /= len(res[i])
# 均值向量是否改变
if ~np.allclose(u[i, :], he):
u[i, :] = he
flag = 1
# 如果均值向量都未改变
if flag == 0:
result = res
# 初始化损失指标
l = 0
# 计算损失指标
for i in range(k):
for j in res[i]:
l += np.sum((train_dataset[j, :] - u[i, :]) ** 2)
loss = l
break
# 判断是否是最后一轮
if epoch == epochs - 1:
result = res
# 初始化损失指标
l = 0
# 计算损失指标
for i in range(k):
for j in res[i]:
l += np.sum((train_dataset[j, :] - u[i, :]) ** 2)
loss = l
y_line[k-1] = loss
# 打印结果
print(f"k={k} -> loss={loss: .4f}")
# 画图
plt.figure(k)
# 画每一个类的点
for i in range(k):
x_point = train_dataset[result[i], 0]
y_point = train_dataset[result[i], 1]
plt.scatter(x_point, y_point, color=colors[i], alpha=0.7, edgecolor='white', s=10)
plt.title(f"k={k} result")
plt.xlabel("x", fontsize=12)
plt.ylabel("y", fontsize=12)
plt.grid(alpha=0.3, linestyle=':')
plt.tight_layout()
# 画损失曲线
plt.figure(11)
plt.plot(x_line, y_line, color='b', linewidth=1.5, label='Loss')
plt.title("Loss value", fontsize=14)
plt.xlabel("k", fontsize=12)
plt.ylabel("loss value", fontsize=12)
plt.legend(loc='upper right', frameon=True)
plt.grid(alpha=0.3, linestyle=':')
plt.tight_layout()
plt.show()
结果分析
聚类结果图
k=1 |
k=2 |
k=3 |
k=4 |
k=5 |
k=6 |
k=7 |
k=8 |
k=9 |
k=10 |
loss随k值增加的变化曲线图