声明:本文基于在校课程及吴恩达ML教程,代码参考自多份博客(已在参考链接中表明),如需转载请标明出处。
源代码、实验数据、实验指导书: https://pan.baidu.com/s/1w7y03FAsk1Y6bVNmaYMj6w 提取码: jykt
一、算法描述
K-均值是最普及的聚类算法。
在非监督学习中,数据没有附带任何标签。也就是说,在非监督学习中,将一系列无标签的训练数据,输入到一个算法中,然后让这个算法根据给定的数据 找出这个数据的内在结构。下图上的数据看起来可以分成两个分开的点集(称为簇,cluster),一个能够找到这些点集的算法,就被称为聚类算法(clustering algorithm)。
聚类就是对大量未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小。
K-均值是一个聚类算法,算法接受一个未标记的数据集 以及 想要获得的组数K,然后算法将数据聚类成 K 个不同的组。
二、算法流程
K-均值是一个迭代算法,假设我们想要将数据聚类成 K 个组,其方法为:
- 取得 K 个初始中心点。
选择 K 个初始中心点(可以是指定的,也可以是随机的点),它们被称为聚类中心(cluster centroids)
- 把每个点划分进相应的簇。
对于数据集中的每一个数据,计算它到 K 个中心点的距离,将其划分进与它距离最近的中心点的簇
- 重新计算中心点。
计算每一个组的平均值,将平均值作为该组新的中心点
- 迭代至收敛。
重复步骤 2-3 直至中心点不再变化 或 达到最大迭代次数。
三、K-Means 算法 python 实现
import numpy as np
class KMeans():
def __init__(self, X, K, centroids = None):
self.X = X
self.K = K
self.n_samples, self.dim = X.shape
self.centers = []
# 步骤1:取得k个初始中心点
if centroids != None:
self.centroids = np.array(centroids, float)
else :
seeds = np.random.permutation(self.n_samples)[:K]
self.centroids = X[seeds]
def euclDistance(self, vector1, vector2):
return np.sqrt(sum(np.power(vector2 - vector1, 2)))
def cluster(self, max_iter):
clusterRes = np.zeros(self.n_samples)
self.centers.append(self.centroids.copy())
for i in range(max_iter):
clusterChanged = False
# 步骤2:把每个点划分进相应的簇
for i in range(self.n_samples):
minDist = float('inf')
minIndex = 0
for j in range(self.K):
dist = self.euclDistance(self.centroids[j, :], self.X[i, :])
if dist < minDist:
minDist = dist
minIndex = j
if clusterRes[i] != minIndex:
clusterChanged = True
clusterRes[i] = minIndex
# 步骤4:收敛,中心点不再变化
if not clusterChanged:
break;
# 步骤3:重新计算中心点
for j in range(self.K):
pointsInCluster = self.X[np.nonzero(clusterRes == j)[0]]
self.centroids[j, :] = np.mean(pointsInCluster, axis=0) # 对各列求均值,返回 1*n 矩阵
self.centers.append(self.centroids.copy())
return clusterRes
四、吴恩达-机器学习 K-Means 作业实践
- 对 ex7data2.m 中的数据进行聚类(K=3)
- 用 Kmeans 来进行图片压缩。(在一个简单的24位颜色表示图像。每个像素被表示为三个 8 8 8 位无符号整数(从 0 0 0到 255 255 255),指定了红、绿和蓝色的强度值。这种编码通常被称为RGB编码。)给定的图像包含数千种颜色,把颜色的数量减少到 16 16 16 种颜色。把原始图片的每个像素看作一个数据样本,然后利用 K-means 算法去找分组最好的16种颜色。
assignment_1
import scipy.io as scio
import numpy as np
import matplotlib.pyplot as plt
from KMeans import KMeans
# 读取图片数据
data = scio.loadmat('ex7data2.mat')
X = data['X']
STEP = 10
if __name__ == '__main__':
plt.figure(0)
plt.title("raw data")
plt.scatter(X[:, 0], X[:, 1], c='r', marker='o')
cls = KMeans(X, 3, [[3, 3], [6, 2], [8, 5]])
clusterRes = cls.cluster(STEP)
ActualIterNum = np.shape(cls.centers)[0]-1
plt.figure(1)
color = ['r', 'g', 'b']
col = [color[int(i)] for i in clusterRes]
plt.scatter(X[:, 0], X[:, 1], c='', marker='o', facecolors='none', edgecolors = col)
for i in range(3):
plt.plot(np.array(cls.centers)[:, i, 0], np.array(cls.centers)[:, i, 1], 'k', linewidth=1)
plt.scatter(np.array(cls.centers)[:,i,0], np.array(cls.centers)[:,i,1], color='k', marker='x', s = 50)
plt.title("initial point: [[3, 3], [6, 2], [8, 5]] | Iteration number %d" %(ActualIterNum))
cls = KMeans(X, 3)
clusterRes = cls.cluster(STEP)
ActualIterNum = np.shape(cls.centers)[0]-1
plt.figure(2)
color = ['r', 'g', 'b']
col = [color[int(i)] for i in clusterRes]
plt.scatter(X[:, 0], X[:, 1], c='', marker='o', facecolors='none', edgecolors=col)
for i in range(3):
plt.plot(np.array(cls.centers)[:, i, 0], np.array(cls.centers)[:, i, 1], 'k', linewidth=1)
plt.scatter(np.array(cls.centers)[:, i, 0], np.array(cls.centers)[:, i, 1], color='k', marker='x', s=50)
plt.title("Random initial point | Iteration number %d" %(ActualIterNum))
plt.show()
-
参数设置
聚类组数:K = 3
给定初始点的情况:[[3, 3], [6, 2], [8, 5]]
最大迭代次数:STEP = 10 -
原始数据图
-
给定初始点 [[3, 3], [6, 2], [8, 5]] 最大迭代 10 次的聚类结果图
迭代 6 6 6 次后,第 7 7 7 次聚类结果与第 6 6 6 次结果完全相同,中心点不会再变化,故停止迭代,所以上图仅有 7 7 7 个 x 点(包含 1 1 1 个初始点,和 6 6 6 次迭代得到的中心点)。 -
随机取初始聚类中心点,收敛时的结果图
assignment_2
import scipy.io as scio
import skimage
from skimage import io
import numpy as np
import matplotlib.pyplot as plt
from KMeans import KMeans
# 读取图片数据
data = scio.loadmat('bird_small.mat')
A = data['A'] # A.shape (128, 128, 3)
# A = skimage.io.imread('bird_small.png')
STEP = 1000
if __name__ == '__main__':
plt.figure(0)
plt.title("Original Image")
plt.imshow(A)
plt.axis('off')
X = A.reshape(-1, 3)
X = X/255 # 防止最后得到的中心点是小数,压缩后的像素值都是小数,无法展示
K = 16
cls = KMeans(X, K)
clusterRes = cls.cluster(STEP)
ActualIterNum = np.shape(cls.centers)[0]-1 # 实际迭代次数
centroids = cls.centroids # 聚类中心
compressedImage = np.zeros(X.shape)
for i in range(len(centroids)):
compressedImage[i == clusterRes] = centroids[i]
compressedImage = compressedImage.reshape(A.shape)
plt.figure(1)
plt.title("Compressed with %d colors | Iteration number %d" %(K, ActualIterNum))
plt.imshow(compressedImage)
plt.axis('off')
plt.show()
-
参数设置
聚类组数:K = 16
给定初始点的情况:随机初始点
最大迭代次数:STEP = 1000 -
原始图像
-
使用 KMeans 压缩并重构的图像
-
原始图像与使用 KMeans 压缩成 16 16 16 种颜色的重构图像并排对比