聚类与分类的区别
分类:类别是已知的,通过对已知类别的数据进行训练和学习,找到这些不同类的特征,再对未知类别的数据进行分类。属于监督学习。
聚类:事先不知道数据会分为几类,通过聚类分析将数据聚合成几个群体。聚类不需要对数据进行训练和学习。属于无监督学习。
关于监督学习和无监督学习,这里给一个简单的介绍:一般而言,是否有监督,就看输入数据是否有标签,输入数据有标签,则为有监督学习,否则为无监督学习。
K-means 聚类原理简述
聚类算法有很多种,K-Means 是聚类算法中的最常用的一种,算法最大的特点是简单,好理解,运算速度快,但是只能应用于连续型的数据,并且一定要在聚类前需要手工指定要分成几类。
K-Means 聚类算法的大致思想就是“物以类聚,人以群分”:
1、首先输入 k 的值,即我们指定希望通过聚类得到 k 个分组;
2、从数据集中随机选取 k 个数据点作为初始大佬(质心);
对集合中每一个小弟,计算与每一个大佬的距离,离哪个大佬距离近,就跟定哪个大佬;
3、这时每一个大佬手下都聚集了一票小弟,这时候召开选举大会,每一群选出新的大佬(即通过算法选出新的质心);
4、如果新大佬和老大佬之间的距离小于某一个设置的阈值(表示重新计算的质心的位置变化不大,趋于稳定,或者说收敛),可以认为我们进行的聚类已经达到期望的结果,算法终止;
5、如果新大佬和老大佬距离变化很大,需要迭代3~5步骤。
上述内容引用自知乎大佬【打工人小飞】的文章5 分钟带你弄懂 K-means 聚类
大佬通过一个生动形象的例子清晰的解释了K-means算法的大致思路,这里我通过这个例子写了一个伪K-means算法供大家参考
import random
from math import sqrt
class K_means:
def __init__(self,K:int,dataset,iter:int):
"""K为聚类数,dataset为数据集,iter为迭代次数"""
self.K=K
self.dataset=dataset
self.verbose=iter
def cal_distance(self,point1:list,point2:list)->float:
"""计算两点距离"""
sumOfSquare = (point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2
return sqrt(sumOfSquare)
def average_points(self,points_arr:list)->tuple:
"""计算均值点"""
x_ = sum(point[0] for point in points_arr) / len(points_arr)
y_ = sum(point[1] for point in points_arr) / len(points_arr)
return (x_, y_)
def train(self):
# 随机生成K个中心点的种子
# seeds=[0,1]
seeds=random.sample(range(0,len(self.dataset)),self.K)
# 将随机选择的K个点作为初始中心点
init_centre = []
for i, seed in enumerate(seeds):
init_centre.append(self.dataset[seed])
print('初始中心点_train', init_centre)
# 将初始中心点外的点作为'小弟'
bubs = [self.dataset[i] for i in range(len(self.dataset)) if i not in seeds]
print('bubs_train', bubs)
self.iteration(init_centre=init_centre,init_bubs=bubs)
def iteration(self,init_centre,init_bubs):
"""init_boss=[(),(),()···()] """
centre=init_centre
bubs=init_bubs
for verb in range(1,self.verbose+1):
cluster = [[] for i in range(self.K)]
bubs_distance = [] # 每个小弟到其他中心点的距离,如3个中心点3个小弟则[[a1,a2,a3],[b1,b2,b3],[c1,c2,c3]]
# print('centre_%d'%verb,centre)
# print('分配1_%d' %verb,bubs)
for bub in bubs:
bub_to_centre = [] # 单个小弟到其他中心点的距离[dist1,dist2,···,distK]
for cen in centre:
bub_to_centre_dist = self.cal_distance(bub, cen) # 两点间的距离
bub_to_centre.append(bub_to_centre_dist)
bubs_distance.append(bub_to_centre)
# print('每个小弟到其他中心点的距离', bubs_distance)
rearrange = []
# 将距小弟最近的中心点的小标记作一个列表
for bub_dist in bubs_distance:
close_boss = bub_dist.index(min(bub_dist))
# print(close_boss)
rearrange.append(close_boss)
# 更新最新中心点
new_centre=list(set(self.dataset)-set(bubs))
# 将最新中心点分配到各个组/簇
for i, bos in enumerate(new_centre):
cluster[i].append(bos)
# 根据列表重新分配组/簇
for bub, i in zip(bubs, rearrange):
cluster[i].append(bub)
# print('cluster1_%d'%verb,cluster)
# 重新更新小弟集
tmp_bubs=[]
# 如果一个簇仅有一个元素那么这个元素就是一个大哥/中心点,有多个元素的簇将取其均值点作为新的大哥/中心点
# 之前的大哥将被下方至小弟集中
for clus in cluster:
# print('clus',clus[0])
if len(clus) != 1:
# print('clus',clus)
for cl in clus:
tmp_bubs.append(cl)
bubs=tmp_bubs
# print('分配2_%d'%verb,bubs)
print('第%d轮簇'%verb,cluster)
boss=[]
# 计算新的中心点放入cluster
for clus in cluster:
new_boss = self.average_points(clus)
# print(new_boss)
boss.append(new_boss)
# print('boss',boss)
centre=boss
if __name__ == '__main__':
Dataset = [[0, 0], [1, 2], [3, 1], [8, 8], [9, 10], [10, 7]]
Dataset = [tuple(point) for point in Dataset]
K_mean=K_means(2,Dataset,3)
K_mean.train()
# 初始中心点_train [(9, 10), (10, 7)]
# bubs_train [(0, 0), (1, 2), (3, 1), (8, 8)]
# 第1轮簇 [[(9, 10), (8, 8)], [(10, 7), (0, 0), (1, 2), (3, 1)]]
# 第2轮簇 [[(9, 10), (8, 8), (10, 7)], [(0, 0), (1, 2), (3, 1)]]
# 第3轮簇 [[(9, 10), (8, 8), (10, 7)], [(0, 0), (1, 2), (3, 1)]]