参考自:
K-均值是最普及的聚类算法,算法接受一个未标记的数据集,然后将数据集聚类成不同的组。
K-均值是一个迭代算法,假设我们想要将数据聚类成n个组,其方法为:
这个过程中分两个主要步骤,第一个就是第二步,将训练集中的样本点根据其与聚类中心的距离,分配到距离最近的聚类中心处,接着第二个就是第三步,更新类中心,做法是计算每个类的所有样本的平均值,然后将这个平均值作为新的类中心值,接着继续这两个步骤,直到达到终止条件,一般是指达到设定好的迭代次数。
当然在这个过程中可能遇到有聚类中心是没有分配数据点给它的,通常的一个做法是删除这种聚类中心,或者是重新选择聚类中心,保证聚类中心数还是初始设定的K个。
K-均值最小化问题,就是最小化所有的数据点与其所关联的聚类中心之间的距离之和,因此K-均值的代价函数(又称为畸变函数)为:
所以我们的优化目标是找出是的代价函数最小的 和c(1),c(2),…,c(m)和μ1,μ2,…,μm :
在运行K-均值算法之前,首先需要随机初始化所有的聚类中心点,做法如下:
K-均值的一个问题在于,它有可能会停留在一个局部最小值处,而这取决于初始化的情况。
为了解决这个问题,通常需要多次运行K-均值算法,每一次都重新进行随机初始化,最后再比较多次运行K-均值的结果,选择代价函数最小的结果。这种方法在K较小(2-10)的时候还是可行的,但是如果K较大,这种做法可能不会有明显地改善。
代码参考自K-Means Clustering。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2016/10/21 16:35
@Author : cai
实现 K-Means 聚类算法
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
import os
# 寻址最近的中心点
def find_closest_centroids(X, centroids):
m = X.shape[0]
k = centroids.shape[0]
idx = np.zeros(m)
for i in range(m):
min_dist = 1000000
for j in range(k):
# 计算每个训练样本和中心点的距离
dist = np.sum((X[i, :] - centroids[j, :]) ** 2)
if dist < min_dist:
# 记录当前最短距离和其中心的索引值
min_dist = dist
idx[i] = j
return idx
# 计算聚类中心
def compute_centroids(X, idx, k):
m, n = X.shape
centroids = np.zeros((k, n))
for i in range(k):
indices = np.where(idx == i)
# 计算下一个聚类中心,这里简单的将该类中心的所有数值求平均值作为新的类中心
centroids[i, :] = (np.sum(X[indices, :], axis=1) / len(indices[0])).ravel()
return centroids
# 初始化聚类中心
def init_centroids(X, k):
m, n = X.shape
centroids = np.zeros((k, n))
# 随机初始化 k 个 [0,m]的整数
idx = np.random.randint(0, m, k)
for i in range(k):
centroids[i, :] = X[idx[i], :]
return centroids
# 实现 kmeans 算法
def run_k_means(X, initial_centroids, max_iters):
m, n = X.shape
# 聚类中心的数目
k = initial_centroids.shape[0]
idx = np.zeros(m)
centroids = initial_centroids
for i in range(max_iters):
idx = find_closest_centroids(X, centroids)
centroids = compute_centroids(X, idx, k)
return idx, centroids
dataPath = os.path.join('data', 'ex7data2.mat')
data = loadmat(dataPath)
X = data['X']
initial_centroids = init_centroids(X, 3)
# print(initial_centroids)
# idx = find_closest_centroids(X, initial_centroids)
# print(idx)
# print(compute_centroids(X, idx, 3))
idx, centroids = run_k_means(X, initial_centroids, 10)
# 可视化聚类结果
cluster1 = X[np.where(idx == 0)[0], :]
cluster2 = X[np.where(idx == 1)[0], :]
cluster3 = X[np.where(idx == 2)[0], :]
fig, ax = plt.subplots(figsize=(12, 8))
ax.scatter(cluster1[:, 0], cluster1[:, 1], s=30, color='r', label='Cluster 1')
ax.scatter(cluster2[:, 0], cluster2[:, 1], s=30, color='g', label='Cluster 2')
ax.scatter(cluster3[:, 0], cluster3[:, 1], s=30, color='b', label='Cluster 3')
ax.legend()
plt.show()
# 载入一张测试图片,进行测试
imageDataPath = os.path.join('data', 'bird_small.mat')
image = loadmat(imageDataPath)
# print(image)
A = image['A']
print(A.shape)
# 对图片进行归一化
A = A / 255.
# 重新调整数组的尺寸
X = np.reshape(A, (A.shape[0] * A.shape[1], A.shape[2]))
# 随机初始化聚类中心
initial_centroids = init_centroids(X, 16)
# 运行聚类算法
idx, centroids = run_k_means(X, initial_centroids, 10)
# 得到最后一次的最近中心点
idx = find_closest_centroids(X, centroids)
# map each pixel to the centroid value
X_recovered = centroids[idx.astype(int), :]
# reshape to the original dimensions
X_recovered = np.reshape(X_recovered, (A.shape[0], A.shape[1], A.shape[2]))
# plt.imshow(X_recovered)
# plt.show()
完整代码例子和数据可以查看Kmeans练习代码。