一:聚类的目的
使同一类对象近可能的大,不同对象之间的相似度尽可能的小
二:聚类的分类:
1 : 基于划分
K-Means ,K-Medoids,CLarans
2: 基于层次
BIRCH , CURE ,CHAMELEON
3: 基于密度
DBscan ,Optics , DenClue
推荐网站可视化:
https://www.naftaliharris.com/blog/visualizing-k-means-clustering/
比较好玩,形象化!!!
三:K -mans: 基于划分的聚类,无监督学习算法(硬聚类)
适用数据集:球状分布的数据,没有标签
K-Means works best in datasets that have with clusters that are roughly equally-sized and shaped roughly regularly.(每个类数量大致相等,形状规则)
数据集X:
基本思想:
The k-means algorithm captures the insight that each point in a cluster should be near to the center of that cluster. (所有样本点到中心点的距离最小)
策略:采用欧式距离平方和最小 squared Euclidean distance
The k-means algorithm divides a set of N samples X into K disjoint clusters C(数据集n个样本,划分成C个族,每个族中心为uj)
(简单理解: 每个样本点到它的所分类中心的距离之和)
李航<<统计学习方法>>:
X = {x1, x2,x3 .....xn} , xi 是m维特征,无标签数据集
K均值聚类的目标是将n个数据集划分到k个不同的簇(cu) cluster, 或者叫类中k < n 。
定义样本之间距离欧式距离:
损失函数:
优化问题: C* = arg min W(C)
算法流程:
(1)随机选取K个样本点,作为初始化聚类 中心。
(2)数据集中每个样本点测量其到每个聚类中心的距离,并把它划分到最近的中心。
(3)重新计算,每个类中所有点的均值作为这个类的聚类中心。
(4)迭代(2)~(3)步直至新的中心与原中心相等或小于指定阈值,算法结束。
注意一下几个问题?
1:初始化K个聚类中心(centroids) 的选择?
-
随机选择空间中任意k个位置作为距离中心
-
随机从数据集 中选择k个聚类中心
-
最远中心(1:数据集随机选择一个点作为聚类中心。然后在剩下数据中选择距离第一个聚类中心最远的点,作为第二个聚类中心, 循环以上,直到选择k个聚类中心)
初始聚类中心: 不同的方法对收敛速度,聚类结果都有很大影响
k-means++ 最好的初始化聚类中心(基本思想就是:初始的聚类中心之间的相互距离要尽可能的远)
2: 距离
-
欧式距离
-
余弦相似度(文本数据)
3:k值选择
事先不知道数据集能划分多少聚类,K是试探性 的,然后比较损失函数选出合适的k值
4: 优缺点
优点:
原理比较简单,实现也是很容易,收敛速度快
主要需要调参的参数仅仅是k
缺点:
-
K值的选取不好把握
-
对于不是凸的数据集比较难收敛(改进:基于密度的聚类算法更加适合,比如DESCAN算法)
-
如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。
-
采用迭代方法,得到的结果只是局部最优。
-
对噪音和异常点比较的敏感(改进1:离群点检测的LOF算法,通过去除离群点后再聚类,可以减少离群点和孤立点对于聚类效果的影响;改进2:改成求点的中位数,这种聚类方式即K-Mediods聚类(K中值))
-
初始聚类中心的选择(改进1:k-means++; 改进2:二分K-means)
四:代码实现:
一:初聚类中心初始化最远
聚类中心的选择,第一个中心: centrod1,数据集中随机选择一个
第二个中心: centrod2,为数据集中到第centrod1最远的点
第三个中心 :centrod3 ,为数据集中到 centrod1,centrod2 距离之和最大的点
'''
第k个中心:centrodk ,数据集中到(centrod1,centrod2,...centrodk-1)距离之和最大的点
import numpy as np
import matplotlib.pyplot as plt
import random
from sklearn import datasets
#两点之间的距离欧式距离
def distance(point1,point2):
"""
e1: 数据1 向量
e2: 数据2
return : distance(point2,point2)
"""
e1 = np.array(point1)
e2 = np.array(point2)
return np.sqrt(np.sum((point1-point2)**2))
#聚类中心
def meanCenter(class_one):
"""
class_one: 一个聚类的所有样本点
return : 这个聚类所有样本点的均值
"""
arr = np.array(class_one)
mean = arr.mean(axis=0)
return mean
#最远中心法,用于初始化聚类中心
def farCenter(centrods,data_set):
"""
centrods: k个聚类中心数组
data_set: 整个数据集
return: 一个样本点: 离已经有的聚类中心距离求和,最远的点
"""
m = np.shape(data_set)[1] #特征维度
temp_point =np.zeros([m]) # 临时保存样本点数据
max_dist = 0
for point in data_set:
dist =0
for i in range(len(centrods)):
dist += np.sqrt(distance(centrods[i],point)) #到已经有的聚类中心求和,开平方为了简化计算
if dist >max_dist:
max_dist = dist
temp_point =point
return temp_point
data_set = datasets.load_iris().data
k= 3 # 聚类中心的个数,人为设置
# 随机选项一个点,初始化聚类中心
rand_point = random.choice(data_set)
clusterCenter_K =np.array([rand_point])
class_arr = [[]] #保存每个聚类的最近样本点 例:cla[1][...] 第1 个类中的样本点
# 初始化聚类中心,数据集中选择距离最大的k个点
for i in range(k-1):
far_point = farCenter(clusterCenter_K,data_set)
clusterCenter_K =np.concatenate([clusterCenter_K,np.array([far_point])])
class_arr.append([])
#可视化初始化数据
col = ['HotPink', 'Aqua', 'Chartreuse', 'yellow', 'LightSalmon']
plt.scatter(data_set[:,0],data_set[:,1],label='origin data',c='g')
plt.legend(loc = 'upper left')
for i in range(k):
plt.scatter(clusterCenter_K[i][0],clusterCenter_K[i][1],linewidth=10, color=col[i])
plt.show()
#迭代
epochs = 50
for epoch in range(epochs):
#1:更新样本点所属类,离聚类中心最近,则划分到这个聚类中
for point in data_set:
ki = 0
mid_dist =distance(point,clusterCenter_K[ki]) # 找出离聚类中心最近的聚类
for j in range(1,len(clusterCenter_K)):
dist =distance(point,clusterCenter_K[j])
if dist <mid_dist:
mid_dist = dist
ki = j
class_arr[ki].append(point) #将样本点划入 最近Ki类中
#2更新聚类中心
for i in range(len(clusterCenter_K)):
if epochs-1 == epoch:# 最后一次不更新
break
clusterCenter_K[i]= meanCenter(class_arr[i])
class_arr[i] = []
## 可视化展示
col = ['HotPink', 'Aqua', 'red', 'yellow', 'LightSalmon']
for i in range(k):
plt.scatter(clusterCenter_K[i][0], clusterCenter_K[i][1], linewidth=10, color=col[i])
plt.scatter([e[0] for e in class_arr[i]], [e[1] for e in class_arr[i]], color=col[i])
plt.show()
二: k-means ++
k-means++算法选择初始seeds的基本思想就是:初始的聚类中心之间的相互距离要尽可能的远。
算法步骤:
(1)从输入的数据点集合中随机选择一个点作为第一个聚类中心
(2)对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x)
(3)选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大
(4)重复2和3直到k个聚类中心被选出来
(5) 与K-means一样,聚类中心更新,和在划分类。
从上面的算法描述上可以看到,算法的关键是第3步,如何将D(x)反映到点被选择的概率上,一种算法如下:
-
数据集中随机选择一个样本点,作为种子点
-
对于数据集中每个样本点,我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。
-
再取一个概率随机值P, 属于[0,1],用权重的方式来取计算下一个“种子点”。这个算法的实现是,total =Sum(D(x))*P ,然后用total -= D(x),直到其<=0,此时的点就是下一个“种子点”。
import math
import random
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
def distance(point1,point2)->float:
"""
point1: 样本点1
point2: 样本点2
return:distance(point1,point2) 欧式距离
"""
dist = 0.0
for e1,e2 in zip(point1,point2):
dist += (e1-e2)**2
return math.sqrt(dist)
def closest_distance(point,centroids):
"""
计算样本点到所有聚类中心最短距离
"""
min_dist = math.inf #初始设置成无穷大
for _, centroid in enumerate(centroids):
dist = distance(centroid,point)
if dist < min_dist:
min_dist = dist
return min_dist
#初始化聚类中心
def k_Centers(data_set:list ,k : int)->list:
"""
从数据集 中返回k个对象作为质心
"""
cluster_centers = []
cluster_centers.append(random.choice(data_set)) # 第一个聚类中心在数据集中随机选择一个
d =[0 for i in range(len(data_set))] # 初始化为0
for _ in range(1,k):
total = 0
for i ,point in enumerate(data_set):
d[i] = closest_distance(point,cluster_centers)#样本点到所有聚类中心最近的点
total +=d[i]
total *=random.random() # total * [0,1]的概率
for i ,di in enumerate(d):
total -= di
if total > 0:
continue
cluster_centers.append(data_set[i])
break
return cluster_centers
#更新聚类中心
def meanCenter_updata(subsetData:list):
"""
subsetData: 一个聚类中所有的样本
return : 这个聚类中所有样本的平均值
"""
data = np.array(subsetData)
mean = data.mean(axis=0)
return mean
data_set = datasets.load_iris().data # 加载数据
epochs = 50
class_arr = [[]] #保存每个类的样本数据
k= 3 # 聚类个数
cluster_centers = k_Centers(data_set,k) #初始化聚类中心 initial cluster center
for _ in range(len(cluster_centers)):
class_arr.append([])
#绘制初始聚类中心分布图
col = ['HotPink', 'red', 'Chartreuse', 'yellow', 'LightSalmon']
for i in range(len(cluster_centers)):
plt.scatter(cluster_centers[i][0],cluster_centers[i][1],linewidth=10, color=col[i])
plt.scatter(data_set[:,0],data_set[:,1],c="g",marker='o',label='origin data')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend(loc=2)
plt.show()
for epoch in range(epochs):
for point in data_set: #1将数据划分到最近聚类中心
index = 0
min_dist = distance(point,cluster_centers[0])
for i in range(1,len(cluster_centers)):
dist = distance(point,cluster_centers[i])
if dist < min_dist:
min_dist =dist
index = i
class_arr[index].append(point)
for j in range(len(cluster_centers)): #2:更新聚类中心
if epoch == epochs -1 :
break
cluster_centers[j] = meanCenter_updata(class_arr[j])
class_arr[j] = []
#结果可视化
for i in range(k):
plt.scatter(cluster_centers[i][0], cluster_centers[i][1], linewidth=10, color=col[i])
plt.scatter([e[0] for e in class_arr[i]], [e[1] for e in class_arr[i]], color=col[i])
plt.show()
3: scikit-learn
官网手册:https://scikit-learn.org/stable/modules/clustering.html#k-means
使用:
导包 : from sklearn.cluster import KMeans
KMeans()
参数: paramters
-
n_clusters = 8 default
-
max_iter: 300 最大迭代数
-
n_init : 10 用不同质心初始化运算比较次数,选择一个损失函数最小的 inertia
-
init:始化方法k-means++ , random , ndarray向量(n_cluster,n_features)自定义
-
precopute_distance: 预计算距离,计算速度快,但占内存。auto ,True ,False
-
tol: float default 1e-4
-
n_jobs : -1 cpu计算 +1 ,不进行并行运算
-
copy_x: True default 当我们precomputing distances时,将数据中心化会得到更准确的结果。如果把此参数值设为True,则原始数据不会被改变。如果是False,则会直接在原始数据
属性: attributes
-
cluster_centers_:
-
Labels_ : 每个点的分类
-
inertia_
方法: methods
-
fit()
-
fit_predictt()
-
fit_transform()
-
get_params()
-
predict()
-
score()
-
set_params()
-
transform()
iris数据集k-means聚类
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn import datasets
from sklearn.metrics import confusion_matrix
iris = datasets.load_iris()
data = iris.data
#绘制数据分布图
plt.scatter(data[:,0],data[:,1],c="red",marker='o',label='see')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend(loc=2)
plt.show()
estimator = KMeans(n_clusters=3,max_iter=400,n_init=11,init="k-means++") # 构造聚类器
estimator.fit(data)
label_pred = estimator.labels_ # 获取聚类标签 每个点的分类
cluster_center = estimator.cluster_centers_ # 获取聚类中心
color =['r','g','b']
i = 0
for e in cluster_center:
plt.scatter(e[0],e[1],linewidth=10,c=color[i])
i+=1
# 绘制聚类结果
X0 = data[label_pred ==0]
X1 = data[label_pred ==1]
X2 = data[label_pred ==2]
plt.scatter(X0[:,0],X0[:,1],c='r',marker='o',label='label0')
plt.scatter(X1[:,0],X1[:,1],c='g',marker='+',label='label1')
plt.scatter(X2[:,0],X2[:,1],c='b',marker='*',label='label2')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend(loc=2)
print('聚类结果,混淆矩阵')
print(confusion_matrix(iris.target,label_pred))
#print(estimator.predict(data))
print(estimator.score(data))
print(estimator.get_params())
#print(estimator.fit_transform(data))
Reference:
[1]:https://towardsdatascience.com/the-5-clustering-algorithms-data-scientists-need-to-know-a36d136ef68
[2]:https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Ftowardsdatascience.com%2Fthe-5-clustering-algorithms-data-scientists-need-to-know-a36d136ef68
[3] https://blog.csdn.net/github_39261590/article/details/76910689
[4] https://blog.csdn.net/loadstar_kun/article/details/39450615
[5] https://blog.csdn.net/itplus/article/details/10088429