山东大学计算机科学与技术学院机器学习实验六
实验题目:K_means
实验学时:4
实验目的:
In this exercise, you will use K-means to compress an image by reducing the number of colors it contains. To begin, download data6.zip and unpack its contents into your Matlab/Octave working directory. Photo credit: The bird photo used in this exercise belongs to Frank Wouters and is used with his permission.
实验步骤与内容:
- 首先读取图片到一个numpy数组中
A = np.array(Image.open('ex8Data/bird_small.tiff'))
A的形状是(128,128,3),分别是长宽高和(R,G,B)颜色
- 然后显示这张图片
3. # Display the original image
4. plt.figure()
5. plt.imshow(A.astype(np.uint8))
6. plt.title('original')
7. plt.show()
如图所示:
- 随机取16个点作为16个聚簇的中心
4. m, n, color = A.shape
5. k = 16
6. centers = np.zeros((k, 3))
7. for i in range(k):
8. a = random.randint(1, n - 1)
9. b = random.randint(1, m - 1)
10. centers[i, :] = A[a, b, :]
- 设置迭代的次数,和每一个像素点属于的label
12. max_iter = 100
13. label = np.zeros((m, n))
14. loss = np.zeros(max_iter)
15.
16. flag = 0
17. iteration = 0
进行K-means算法,迭代到最大次数或者说两次迭代之间的损失小于一个数的时候就停止迭代
for i in tqdm(range(max_iter))::这是外层循环,控制K-means算法的迭代次数,max_iter 是最大迭代次数。tqdm 是一个用于在循环中显示进度条的库。
内层三重循环:
for x in range(m)::遍历图像的行。
for y in range(n)::遍历图像的列。
for j in range(k)::遍历聚类中心。
计算每个像素到各个聚类中心的距离,并将其分配给距离最近的聚类中心(label[x, y] 记录该像素的聚类标签)。
更新损失值 loss[i],这是每次迭代中所有像素点与其最近聚类中心的距离之和。
如果当前迭代与上一次迭代的损失值差异小于某个阈值(abs(loss[i - 1] - loss[i]) < 1),则认为算法已经收敛,提前结束迭代。
更新聚类中心:
计算每个聚类中的像素点数量和各个维度上的和。
更新聚类中心为各个维度上的平均值。
如果提前结束迭代,则跳出外层循环。
18. for i in tqdm(range(max_iter)):
19. for x in range(m):
20. for y in range(n):
21. min_dis = 1e10
22. for j in range(k):
23. dis = A[x, y, :] - centers[j, :]
24. dis = np.dot(dis, dis)
25. if dis < min_dis:
26. min_dis = dis
27. label[x, y] = j
28. loss[i] += min_dis
29. if i > 0 and abs(loss[i - 1] - loss[i]) < 1:
30. iteration = i
31. flag = 1
32. break
33. if flag:
34. break
35. if flag:
36. break
37.
38. cluster_size = np.zeros(k)
39. new_center = np.zeros((k, 3))
40.
41. for x in range(m):
42. for y in range(n):
43. lb = int(label[x, y])
44. cluster_size[lb] += 1
45. new_center[lb, :] += A[x, y, :]
46.
47. for j in range(k):
48. if cluster_size[j] != 0:
49. centers[j, :] = new_center[j, :] / cluster_size[j]
6 . 把每一个点的像素的颜色变为它所属于的类的颜色
52. for x in range(m):
53. for y in range(n):
54. lb = int(label[x, y])
55. A[x, y, :] = centers[lb, :]
56. 画出进行完算法之后的图像以及损失
结论分析与体会:
在这个实验中,我深入了解了K-means聚类算法,并在图像数据上进行了实际应用。以下是我的结论和个人体会:
首先,通过观察损失函数随迭代次数的变化,我发现在一定迭代次数后,损失值趋于稳定,这表明算法在给定数据集上逐渐收敛。这对我理解算法的性能和收敛性提供了关键的见解。
在观察聚类效果时,我发现K-means算法成功地将图像分成了预期的簇,并且每个簇中的像素点具有相似的特征。通过可视化每个簇的中心和样本,我更清晰地了解了聚类的效果,这有助于评估算法在图像数据上的表现。
我还尝试了不同的初始聚类中心,并注意到算法对初始条件的敏感性。不同的初始中心可能导致不同的聚类结果,这使我认识到在实际应用中,选择合适的初始中心对于算法的性能至关重要。
通过尝试不同的聚类中心数量,我得以比较不同情况下的聚类效果。这有助于我理解K-means在不同问题中的适用性,并找到最佳的聚类中心数量,以达到最佳效果。
在思考算法的应用和局限性时,我认识到K-means算法在图像聚类以外可能也具有广泛的应用。然而,我也意识到算法对初始条件敏感,且对异常值敏感,这是在某些情况下需要谨慎考虑的局限性。
总的来说,通过这个实验,我不仅深入了解了K-means聚类算法的原理和实现细节,还学到了如何调整参数、优化算法以及处理不同情况。这为我今后在实际项目中应用聚类算法提供了有价值的经验教训。
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from tqdm import tqdm
import random
# Read the image
A = np.array(Image.open("ex8Data/IMG_202311089677_248x248.jpg"))
print(A.shape)
# Display the original image
plt.figure()
plt.imshow(A.astype(np.uint8))
plt.title('original')
plt.show()
# Your further processing code here
m, n, color = A.shape
k = 3
centers = np.zeros((k, 3))
for i in range(k):
a = random.randint(1, n - 1)
b = random.randint(1, m - 1)
centers[i, :] = A[a, b, :]
max_iter = 100
label = np.zeros((m, n))
loss = np.zeros(max_iter)
flag = 0
iteration = 100
for i in tqdm(range(max_iter)):
for x in range(m):
for y in range(n):
min_dis = 1e10
for j in range(k):
dis = A[x, y, :] - centers[j, :]
dis = np.dot(dis, dis)
if dis < min_dis:
min_dis = dis
label[x, y] = j
loss[i] += min_dis
if i > 0 and abs(loss[i - 1] - loss[i]) < 1:
iteration = i
flag = 1
break
if flag:
break
if flag:
break
cluster_size = np.zeros(k)
new_center = np.zeros((k, 3))
for x in range(m):
for y in range(n):
lb = int(label[x, y])
cluster_size[lb] += 1
new_center[lb, :] += A[x, y, :]
for j in range(k):
if cluster_size[j] != 0:
centers[j, :] = new_center[j, :] / cluster_size[j]
for x in range(m):
for y in range(n):
lb = int(label[x, y])
A[x, y, :] = centers[lb, :]
# Display clustered image
plt.figure()
plt.imshow(A.astype(np.uint8))
plt.title('cluster into 16 RGB means')
plt.show()
# Plot loss function
plt.figure()
plt.plot(np.arange(0, iteration), loss[0:iteration], linewidth=2)
plt.xlabel('iteration')
plt.ylabel('loss')
plt.title('K-means loss function')
plt.show()