k-中心点聚类算法(k-medoids)详解(Python实现)
medoid 在英文中的意思为“中心点”,所以 k-medoids 算法又叫 k-中心点聚类算法。
与 k-means 有所不同的是,k-medoids 算法不采用簇中对象的平均值作为参照点,而是选用簇中位置最中心的对象,即中心点作为参照点。
然后开始迭代过程:对于每一次迭代,将随机选择的一个非中心点替代原始中心点中的一个,重新计算聚类结果。如果聚类效果有所提高,保留此次替换,否则恢复原中心点。当替换对聚类效果不再有所提高时,迭代停止。
用代价函数来衡量聚类结果的质量,该函数用来度量对象与中心点之间的平均相异度,具体定义如下:
其中,p 是空间中的点,即为给定对象,oi 代表簇 Ci 的中心点,E 则表示数据集中所有对象的离差平方和。
在进行新一轮中心点替换后,以 newCi,i=1,2,…,k 表示新中心集划分的簇,以 oldCi,i=1,2,…,k 代表原来的簇,它们的聚类评价函数分别为 Enew,Eold,则:
由 Enew,Eold 定义中心替换的代价函数为:Cost=Enew-Eold。
聚类所要达到的目标是使得簇内各个对象之间的差异尽可能小,因此,如果要判定一个非代表对象 orandom 是否是对当前中心点 oi 的更优替换点,对于每一个非中心点对象 p,每当重新分配时,平方-误差:
产生的差别会对代价函数产生影响,替换所产生的总代价是所有非中心点对象的代价之和。如果总代价的值小于零,即实际平方-误差减小,表明经过替换后,簇内对象之间的差异变得更小了,此时,可用 orandom 替换 oi 作为新的中心点对象。反之,如果替换所产生的总代价一直大于或等于零,则未能产生一个有效的替换,此时算法收敛。
k-medoids 聚类算法的具体步骤为:
k-medoids 聚类初始中心的选择仍可采用最大距离法、最大最小距离法和 Huffman 树。
【实例】利用 k-medoids 算法对随机数据点进行聚类。

图 4 k-medoids聚类效果
与 k-means 有所不同的是,k-medoids 算法不采用簇中对象的平均值作为参照点,而是选用簇中位置最中心的对象,即中心点作为参照点。
k-medoids算法的基本思想
对于给定聚类数目 k,首先随机选择 k 个代表对象作为初始聚类中心,计算各剩余对象与代表对象的距离并将其分配给最近的一个簇,产生相应的聚类结果。然后开始迭代过程:对于每一次迭代,将随机选择的一个非中心点替代原始中心点中的一个,重新计算聚类结果。如果聚类效果有所提高,保留此次替换,否则恢复原中心点。当替换对聚类效果不再有所提高时,迭代停止。
用代价函数来衡量聚类结果的质量,该函数用来度量对象与中心点之间的平均相异度,具体定义如下:

其中,p 是空间中的点,即为给定对象,oi 代表簇 Ci 的中心点,E 则表示数据集中所有对象的离差平方和。
在进行新一轮中心点替换后,以 newCi,i=1,2,…,k 表示新中心集划分的簇,以 oldCi,i=1,2,…,k 代表原来的簇,它们的聚类评价函数分别为 Enew,Eold,则:

由 Enew,Eold 定义中心替换的代价函数为:Cost=Enew-Eold。
聚类所要达到的目标是使得簇内各个对象之间的差异尽可能小,因此,如果要判定一个非代表对象 orandom 是否是对当前中心点 oi 的更优替换点,对于每一个非中心点对象 p,每当重新分配时,平方-误差:

产生的差别会对代价函数产生影响,替换所产生的总代价是所有非中心点对象的代价之和。如果总代价的值小于零,即实际平方-误差减小,表明经过替换后,簇内对象之间的差异变得更小了,此时,可用 orandom 替换 oi 作为新的中心点对象。反之,如果替换所产生的总代价一直大于或等于零,则未能产生一个有效的替换,此时算法收敛。
k-medoids聚类算法步骤
k-medoids 聚类算法的基本思想为:- 输入:期望聚类数目 k,包含 n 个数据对象的数据集;
- 输出:k 个簇,使得所有点与其最近中心点的相异度总和最小。
k-medoids 聚类算法的具体步骤为:
- 在 n 个数据对象中随机选择 k 个点,作为初始中心集。
- 计算每个非代表对象到各中心点的距离,将其分配给离其最近的簇中。
- 对于每个非中心对象,依次执行以下过程:用当前点替换其中一个中心点,并计算替换所产生的代价函数,若为负,则替换,否则不替换且还原中心点。
- 得到一个最终的较优 k 个中心点集合,根据最小距离原则重新将所有对象划分到离其最近的簇中。
k-medoids 聚类初始中心的选择仍可采用最大距离法、最大最小距离法和 Huffman 树。
k-medoids算法的实现
上面对 k-medoids 算法的基本思想及步骤进行了介绍,下面通过一个实例来演示 kmedoids 算法的应用。【实例】利用 k-medoids 算法对随机数据点进行聚类。
from sklearn.datasets import make_blobs from matplotlib import pyplot import numpy as np import random class KMediod(): """ 实现简单的 k-medoid 算法 """ def __init__(self, n_points, k_num_center): self.n_points = n_points self.k_num_center = k_num_center self.data = None def get_test_data(self): """ 产生测试数据,n_samples 表示多少个点,n_features 表示几维,centers 得到的 data 是 n 个点的各自坐标;target 是每个坐标的分类 target 长度为 n,范围为 0-3,主要是画图颜色区别 :return: none """ self.data, target = make_blobs(n_samples=self.n_points, n_features=2, centers=self.n_points) np.put(self.data, [self.n_points, 0], 500, mode='clip') np.put(self.data, [self.n_points, 1], 500, mode='clip') pyplot.scatter(self.data[:, 0], self.data[:, 1], c=target) # 画图 pyplot.show() def ou_distance(self, x, y): # 定义欧氏距离的计算 return np.sqrt(sum(np.square(x - y))) def run_k_center(self, func = ou_distance): """ 选定好距离公式开始训练 """ print('初始化', self.k_num_center, '个中心点') indexes = list(range(len(self.data))) random.shuffle(indexes) # 随机选择质心 init_centroids_index = indexes[:self.k_num_center] # 初始中心点 centroids = self.data[init_centroids_index, :] # 初始中心点 # 确定种类编号 levels = list(range(self.k_num_center)) print('开始迭代') sample_target = [] if_stop = False while(not if_stop): if_stop = True classify_points = [[centroid] for centroid in centroids] sample_target = [] # 遍历数据 for sample in self.data: # 计算距离,由距离该数据最近的中心,确定该点所属类别 distances = [func(sample, centroid) for centroid in centroids] cur_level = np.argmin(distances) sample_target.append(cur_level) # 统计,方便迭代完成后重新计算中间点 classify_points[cur_level].append(sample) # 重新划分质心 for i in range(self.k_num_center): # 几类中分别寻找一个最优点 distances = [func(p_1, centroids[i]) for point_1 in classify_points[i]] # 首先计算出现在中心点和其他所有点的距离总和 now_distances = sum(distances) for point in classify_points[i]: distances = [func(p_1, point) for point_1 in classify_points[i]] # 计算出该聚簇中各个点与其他所有点距离的总和,若是有小于当前中心点的距离总和的,中心点去掉 new_distance = sum(distances) if new_distance < now_distances: now_distances = new_distance centroids[i] = point # 换成该点 if_stop = False print('结束') return sample_target def run(self): """ 先获得数据,由传入参数得到杂乱的 n 个点,然后由这 n 个点,分为 m 个类 """ self.get_test_data() predict = self.run_k_center(self.ou_distance) pyplot.scatter(self.data[:, 0], self.data[:, 1], c=predict) pyplot.show() test_one = KMediod(n_points=1000, k_num_center=3) test_one.run()运行程序,得到聚类效果如下图所示:

图 4 k-medoids聚类效果