散记知识点
——“聚类:经典的无监督学习方法”
1.基本概念和方法
(1) 聚类的基本概念
- 聚类是一个把数据对象划分成多个组或簇的过程,使得簇内的对象具有很高的相似性,但与其他簇中的对象很不相似。
(2) 聚类的基本方法
基于距离的划分方法
给定一个 个对象的集合,划分方法构建数据的 个划分。大部分的划分方法是基于距离的,首先给定要构建的分区数 构建一个初始划分。通过不断迭代,使得同一簇中的对象尽可能接近,不同簇中的对象尽可能远离。最终达到稳定状态。典型的这类方法有: -均值和 -中心点的启发式算法,能够逼近局部最优。
基于层次的方法
层次方法分为凝聚和分裂。凝聚:自底向上,开始将每个对象作为单独的一个组,然后逐渐合并相近的组,直到所有的组合并为一个组(或者满足某个终止条件)。分裂:自顶向下,开始将所有对象置于一个簇中,每次相继迭代,划分更小的簇,最终每个对象单独在一个簇中。
基于密度的方法
考虑“邻域”中的密度(对象或数据点的数目),超过某个阈值,就继续增长给定的簇。基于密度的聚类方法,可以用来过滤噪声和离群点,能够发现任意形状的簇。
基于网格的方法
把对象空间化为有限个单元形成一个网格结构。聚类操作在网格上进行。处理速度快,因为其处理时间独立于数据对象的个数,仅依赖于量化空间每一维的单元数。
总结:
2. -均值(k-means)聚类算法
(1) 算法的主要思想和原理
算法的目标
假设数据集 包含 个欧式空间中的对象。算法将 个对象分配到 个簇 中,使得对于 。用一个目标函数评估划分的质量,使得簇内对象相似,簇间对象相异。
评价(目标)函数
-means取簇 中对象的均值为中心点 代表该簇,对象 与其簇的代表 之差为 度量,其中 表示两个点之间的欧式距离。算法划分 个簇的质量可用数据集中所有对象与所属簇中心的距离之和度量定义为:
算法策略
要想求出全局最优解,需要穷举出所有可能的划分,比较目标函数。即使在2维空间内,穷举法也是个N-P难问题。如果簇数为 维度为 ,则穷举法对 个对象求出最佳的 个划分的时间复杂度为 。这个开销是很大的,为了克服这个问题,通常采用贪心算法的策略。
(2) 算法的主要过程
- 首先,在 中随机选择 (预定义 的大小)个对象,每个对象代表一个簇的初始均值或中心。对剩下的每个对象,根据其与各个簇中心的欧式距离,将它分配到最相似的簇。
- 然后,算法迭代地改善簇内变差(所有对象和形心 得误差平方和)。对每个簇,它使用上次迭代分配到该簇的对象,计算新的均值。
- 接着,使用更新后的均值作为新的簇中心,重新分配所有对象。迭代继续,直到分配稳定,即本轮形成的簇与前一轮形成的簇相同。
(3) 算法的评价
- -means不能保证能够收敛于全局最优解,结果可能受初始簇中心的选择有关。实践中,通常以不同的初始簇中心,多次运行算法,以期许达到好的结果。
- 此外,算法的复杂度为 ,其中 为对象数, 为簇数, 为迭代次数。远远低于穷举法,因此对于处理大数据集,该算法相对可伸缩和有效的。
- -means要求事先给定簇数 值,这一点很盲目,算是一个缺点。但是可以采取其他技术确定较好的 值。
- -means不适合发现非凸形状的簇或者大小差别很大的簇,此外, -means对噪声和离群点敏感,这些值会大大影响均值的计算。
2. -中心点聚类算法
(1) 算法的主要思想和原理
- 针对
-means对噪声和离群点敏感的问题,
-中心选择实际的对象作为簇的代表。每个簇选择一个代表对象
,和
同样采用误差平方和作为评价度量:
- 围绕中心点划分(Partition Around Medoids, PAM)算法是k-中心聚类的一种流行实现方法。采用贪心迭代的思想:初始随机选择 个代表,考虑簇中一个非代表对象替换代表对象是否能够提高聚类质量。找到使得聚类质量最好的非代表对象替换代表对象。
(2) 算法的主要过程
- 初始随机选择k个数据点 作为代表对象,将每个非代表对象分配到离其最近代表对象簇中,计算误差平方和。
- 依次考虑每个非代表对象 是否是当前代表对象的好的替代。例如:用 替代 ,则重新分配非代表对象。并计算代价函数(新的误差平方和与之前的差)。代价函数为负则替代。
- 当聚类不发生改变(代价函数稳定或簇中心不在变化),聚类停止。
(3) 算法的评价
- 与k-means相比,k-中心对噪声和离群点更加鲁棒,中心点不容易受离群点影响。
- k-中心算法每次迭代的复杂度为 ,当n和k较大时,计算开销变得很大,远高于k-means。
- 与k-means相同,k-中心也要事先指定具体的簇数k。
- k-中心在小型数据集上运行良好,但是不能很好地用于大型数据集。为了处理大数据集,可以使用CLARA(Clustering LaRge Applications, 大型应用聚类)的基于抽样的方法。然后使用PAM法计算最佳中心点。
基于层次划分的聚类算法
一种类似于生物的门科种属分类的层次划分聚类。
(1) 算法的原理的思想(以凝聚法为例)
- 首先把每个数据对象看成一个个小簇,然后小簇之间根据某种度量(如距离最近)合成更大的簇。更大的簇最终会形成一个簇(层次结构的根)。
- 典型的结构如下:
- 可以采取某层横截的方法,生成相应数量的簇。例如,上图经过绿色线的横截,生成了3个簇。
(2) 算法采取的距离度量
基于层次的聚类算法一个核心的问题是度量两个簇之间的距离,目前有4个广泛使用的簇间距离度量方法。
假设两个对象 和 之间的距离为 , 是簇 的均值, 为簇 中对象的数目。则两个簇 和 之间的距离度量有以下几种:
- 最小距离:
- 最大距离:
- 均值距离:
- 平均距离:
几点说明:
- 当算法使用最小距离度量时,又被称为最近邻聚类算法。此外,如果当最近的两个簇之间的距离超过用户给定的阈值时,聚类过程就会终止,则称其为单连接算法。使用最小距离度量的凝聚层次聚类算法也被称为最小生成树算法。
- 当算法使用最大距离度量时,又被称为最远邻聚类算法。如果,当最近的两个簇之间的最大距离超过用户给定的阈值时,聚类过程便终止,则称为全连接算法。
- 以上最小和最大距离度量代表了簇间距离度量的两个极端。它们趋向对噪声和离群点敏感。使用均值距离或平均距离是对最小和最大的折中,并且可以克服离群点敏感问题。其中,均值距离计算最简单,平均距离则能处理分类数据。
DBSCAN:基于密度的聚类方法
- 基于划分(k-means和k-中心)和层次的聚类方法倾向于发现球状簇,很难发现任意形状的簇。如下图所示:
对于上图的情形,发现高密度的簇会更有意义。 - DBSCAN(Density-Based Spatial Clustering of Applications with Noise, 具有噪声应用的基于密度的空间聚类)法试图寻找核心对象及其邻域稠密对象形成稠密区域作为簇。
1. DBSCAN算法基本原理和思想
(1) 基本概念
- 密度和邻域:对象 的密度可用靠近 的对象数度量。指定参数 作为每个对象的邻域半径。对象 的 -邻域是以 为中心, 为半径的空间。
- 稠密区域:指定参数 作为稠密区域的阈值:如果一个对象的 -邻域至少包含 个对象,则该对象是核心对象。核心对象是稠密区域的支柱,一系列核心对象及其邻域共同形成了稠密区域即簇。
- 聚类任务:给定一个数据对象集D,识别关于参数 和 的所有核心对象。寻找由一定数量的核心对象以及邻域构成的不同的簇。
(2) 密度可达和密度相连
- 直接密度可达:对于两个核心对象 ,如果 在 的 -邻域内,则称 是从 (关于 和 )直接密度可达的(注意,密度可达不是对称的。)。
- 密度可达:对于两个核心对象 ,如果存在一个对象链 ,使得 ,且对于 , 是从 (依次)直接密度可达的。则称 是从 (关于 和 )密度可达的。
- 密度相连:对于两个核心对象 ,如果存在一个对象 使得 和 都是从 (关于 和 )密度可达的,则称 是(关于 和 )密度相连的。不像密度可达,密度相连是等价关系。
- 下图描述了三种关系:
(3) 簇的发现
- 通过使用密度相连的闭集来发现连通的稠密区域作为簇。每个闭集即是一个基于密度的簇。对于簇 :任意两个对象 , 和 是密度相连的;同时,对 ,不存在 ,使得 和 是密度相连的。
2. DBSCAN算法主要过程
- 初始,标记给定数据集D中所有对象为“unvisited”。随机选择一个未访问的对象 ,标记为“visited”。
- 检查 的 -邻域是否至少包含 个对象。若是,则 为核心对象,为 创建一个新的簇 ;否则标记 为噪声点。
- 若 为核心对象,把 的 -邻域的所有对象都放到候选集合 中。然后,迭代地把 中不属于其他簇的对象添加到 中。
- 在迭代的过程中,对于 中标记为“unvisited”的对象 ,DBSCAN把它标记为”visited”,并检查它的 -邻域是否至少包含 个对象。如果是,则把 的 -邻域的所有对象都放到候选集合 中。
- DBSCAN继续添加对象到 中,直到 不能扩展,也即 为空,此时第一个簇 完全生成。
- 接下来继续寻找下一个簇,同样在剩下的对象中随机选择一个未被访问的对象。重复以上过程。直到所有对象都被访问。
3. DBSCAN算法复杂度分析
- 如果使用空间索引,则DBSCAN的计算复杂度为 ,其中 是数据库对象数。不使用空间索引,其复杂度为 。
- 如果用户定义的参数 和 设置恰当,则该算法可以有效地发现任意形状的簇。
聚类评估
(1) 评估聚类趋势
- 如何确定一个数据集是否适合聚类,也就是当给定一个数据集对象,我们要检测其是否呈区域密集簇分布。毕竟均匀分布聚类的意义不是很大。这就是所谓的评估聚类趋势。
- 霍普金斯统计量是一种空间统计量,检验空间分布的变量的随机性。给定数据集 ,可以看作一个随机变量 的一个样本,我们要确定 在多大程度上不同于数据空间中的均匀分布。
计算霍普金斯统计量
- (1) 均匀地从数据集D中抽取n个点
。对于每个点找出其在
中的最近邻。令
表示点
与它在D中最近邻之间的距离:
- (2) 均匀地从D中抽取n个点
,对于每个点
,找出其在
中的最近邻,并令
为
与它在
中的最近邻之间的距离:
- (3) 计算霍普金斯统计量
:
(2) 确定簇数
- 在聚类分析中,如果能够事先确定数据集的簇数是很关键的。一方面,方便服务于k-means和k-中心这类事先需要于设定簇数k的聚类算法;另一方面,合适的簇数可以控制适当的聚类分析粒度。可以看作在聚类分析的可压缩性与准确性之间寻找好的平衡点。
- 一种简单的经验方法:对于n个点的数据集,设置簇数p大约为 。在期望情况下,每个簇大约有 个点。
肘方法
- 基于观察:初始增加簇数有助于降低每个簇的簇内方差之和。这是由于更多的簇可以捕获更细的数据对象簇,簇中对象之间更为相似。
- 但是,随着簇逐渐的增加,降低簇内方差和的边际效应可能会下降,因为把一个凝聚的簇分裂成两个只引起簇内方差和稍微降低。
- 因此,一种选择正确簇数的启发式方法是:使用簇内方差和关于簇数的曲线拐点。
- 具体的做法:给定k>0,可以使用事先使用k-means等方法对数据集聚类,并计算簇内方差 和k。然后绘制 关于k的曲线。曲线的第一个(或最显著的)拐点则可能表示合理的簇数。
还可以通过交叉验证的方法确定数据集中正确的簇数。
(3) 评估聚类质量
- 评判聚类结果是否良好或者满足预期以及比较不同方法产生的聚类效果,则需要对聚类质量进行评估。根据是否有基准可用,将评估方法分为外在方法和内在方法。这里的基准是一种理想的聚类,通常由专家构建。
- 外在方法将聚类的结果和基准比较;内在方法则通过考虑簇的分离情况来评估聚类的好坏。基准可以看作一种“簇标号”形式的监督。因此,外在方法又称监督方法,内在方法为无监督方法。
BCubed精度和召回率(外在方法)
- BCubed根据基准,对给定数据集上聚类中的每个对象估计精度和召回率。
- 一个对象的精度指示同一簇中有多少个其他对象与该对象同属一个类别。
- 一个对象的召回率反映有多少同一类别的对象被分配在相同的簇中。
设
是对象的集合,
是
中的一个聚类。设
是基准确定的
的类别,
是
中的簇标号。对于两个对象
和
在聚类
中的关系的正确性由下式给出:
- BCubed精度定义为:
- BCubed召回率定义为:
轮廓系数(内在方法)
- 当没有数据集的基准可用时,一般采取基于对象之间相似性度量的内在方法。
对于含有 个对象的数据集 ,假设能被划分为 个簇 。对于 中的每个对象 :
- 计算
与
所属簇的其他对象之间的平均距离
:
- 类似地,计算
与不是其所属簇中其他对象之间的最小平均距离
:
- 对象
的轮廓系数定义为:
- 轮廓系数的值在-1和1之间。 反映 所属簇的紧凑性。该值越小,簇越紧凑。 反映 与其他簇的分离程度,该值越大, 与其他簇越分离。
- 因此,当对象的轮廓系数接近1时,包含它的簇时紧凑的,并且它远离其他簇。
算法的简单实现
——“挑几个算法偶尔码码”
python简单实现 聚类
(1) 实现过程
- 生成数据集:采用二维高斯随机产生300个数据点(为简单起见默认3个簇大小都为100)。
- 初始聚类过程:初始随机挑选3个数据点作为3个簇的中心,采用贪心的策略:先计算第1个簇中心与所有数据点的欧氏距离,根据距离从到大排序选择前100个数据点生成第一个簇,同时在原始数据集中删除这些点。然后,计算第2的簇中心与剩余数据点的大小,排序取前100个距离最小的点生成第二个簇。最后剩余的数据点即是第3个簇。
- 迭代聚类:根据生成的三个数据簇,计算每个数据点的均值作为新的簇中心,根据新的簇中心迭代生成新的3个簇。迭代终止条件为簇中心的位置不再发生改变。
(2) 主要代码
# -*- coding: utf-8 -*-
__author__ = "Yunfan Yang"
import numpy as np
import matplotlib.pyplot as plt
import random
def generate_data():
"""生成数据"""
# 产生3个2维高斯分布
mean0 = [0,1] # 第1个二维高斯均值、协方差矩阵
cov0 = [[0.8,0],[0,0.6]]
np.random.seed(0) # 设置随机种子为0
norm0 = np.random.multivariate_normal(mean0,cov0, 100)
mean1 = [3,5] # 第2个二维高斯均值、协方差矩阵
cov1 = [[0.6,0],[0,0.8]]
np.random.seed(1) # 设置随机种子为1
norm1 = np.random.multivariate_normal(mean1,cov1, 100)
mean2 = [4,1] # 第3个二维高斯均值、协方差矩阵
cov2 = [[0.6,0],[0,0.6]]
np.random.seed(2) # 设置随机种子为2
norm2 = np.random.multivariate_normal(mean2,cov2, 100)
norm_data = list(norm0) + list(norm1) + list(norm2)
DataSet = []
for i in range(len(norm_data)):
temp_tuple = (round(norm_data[i][0], 4), round(norm_data[i][1],4))
DataSet.append(temp_tuple)
return DataSet
def dist(A, B):
"""计算两点之间的欧几里得距离"""
Ojld = np.sqrt(np.square(A[0]-B[0]) + np.square(A[1]-B[1]))
Ojld = round(Ojld, 2)
return Ojld
def clustering(DataSet):
"""进行聚类"""
k_means = [(0, 0), (0, 0), (0, 0)]
# random.seed(2)
tk = random.sample(DataSet, 3) # 从数据集中随机抽取三个点作为初始簇中心
k_new_means = tk[:]
while k_new_means != k_means: # 判断终止条件:簇中心不再发生改变
CopyDataSet = {} # 这里将数据集转化为字典形式,便于后续的删除数据点操作,批量删除列表元素太麻烦了。。
for i in range(len(DataSet)):
CopyDataSet[i] = DataSet[i]
k=0
Clusters = {}
while(k < 3):
distance_list = {}
for key,value in CopyDataSet.items():
temp_dist = dist(k_new_means[k], value) # 计算第一个簇中心与数据集中所有点的距离
distance_list[key]=temp_dist
sort_distance1 = sorted(distance_list.items(),reverse=False,key=lambda x:x[1]) # 按照距离从小到大排列
sub_cluster = [] # 创建子簇
for i in range(100): # 贪心法取前100个距离返回下标作为第一个簇的数据点下标
index = sort_distance1[i][0]
temp_tuple = CopyDataSet[index]
sub_cluster.append(temp_tuple)
CopyDataSet.pop(index) # 从数据集的副本中剔除已形成簇的数据点
Clusters[k] = sub_cluster
k+=1
k_means = k_new_means[:]
for k, cluster in Clusters.items(): # 计算新一轮的簇中心
x_sum = 0
y_sum = 0
for data_dot in cluster:
x_sum += data_dot[0]
y_sum += data_dot[1]
k_mean = (round((x_sum / 100),4), round(y_sum / 100,4))
k_new_means[k] = k_mean
return Clusters
def data_visual(DataSet,Clusters):
"""绘制原始数据点和簇"""
x = []
y = []
for value in DataSet:
x.append(value[0])
y.append(value[1])
# plt.style.use('ggplot')
fig1 = plt.figure()
ax1 = fig1.add_subplot(111)
# for spine in ['left', 'bottom']:
# ax1.spines[spine].set_position('zero') # 将坐标轴移动至原点
ax1.spines['right'].set_visible(False) # 右边框不可见
ax1.spines['top'].set_color('none') # 上边框不可见
ax1.plot(x,y, 'o', c='blue', alpha=0.5, markersize=5)
ax1.axis('equal')
ax1.grid(True, color='black', linewidth=0.2, linestyle='-.')
color_list = ['r','g','b']
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
for k,cluster in Clusters.items(): # 绘出每个簇的数据点
x_k = []
y_k = []
for data_dot in cluster:
x_k.append(data_dot[0])
y_k.append(data_dot[1])
ax2.plot(x_k,y_k,'o',c=color_list[k], alpha=0.7,markersize=5)
ax2.spines['right'].set_visible(False) # 右边框不可见
ax2.spines['top'].set_color('none') # 上边框不可见
ax2.axis('equal')
ax2.grid(True, color='black', linewidth=0.2, linestyle='-.')
plt.show()
if __name__ == "__main__":
DataSet = generate_data() # 产生3个二维随机高斯分布的原始数据集
Clusters = clustering(DataSet) # 对原始数据集进行聚类
data_visual(DataSet,Clusters) # 绘图
(3) 运行结果
R语言简单实现层次聚类
(1) 问题简述
- 基于意大利6个城市之间的距离进行简单的层次聚类:
- 上图左边为距离矩阵,根据此距离矩阵在R语言中简单实现层次聚类
(2) R代码
#### 输入原始距离数据
x<- c(0, 662, 877, 225, 412, 996,
662, 0, 295, 468, 268, 400,
877, 295, 0, 754, 564, 138,
255, 468, 754, 0, 219, 869,
412, 268, 564, 219, 0, 669,
996, 400, 138, 869, 669, 0)
y<-x/(max(x)-min(x)) # 数据归一化
names<-c("BA","FI","MI","NA","RM","TO")
r<-matrix(y, nrow=6, ncol=6, dimnames=list(names, names)) # 将数据转化为矩阵
#### 聚类分析
#### 函数dist()将普通矩阵转化为聚类分析用的距离结构
d<-dist(r); hc<-hclust(d); dend<-as.dendrogram(hc)
#用四种不同的距离方法生成系统聚类
hc1<-hclust(d, "single"); hc2<-hclust(d, "complete") #最短距离法和最长距离法
hc3<-hclust(d, "median"); hc4<-hclust(d, "average") #中间距离法和平均距离法
#绘制出以上四种方法的树形结构图,并以2x2的形式画在一张图上.图像距离边界的距离
opar <- par(mfrow = c(2, 2))
#hang是表明谱系图中各类所在的位置 当hang取负值时,谱系图中的类从底部画起 生成谱系图
plot(hc1,hang=-1); plot(hc2,hang=-1)
plot(hc3,hang=-1); plot(hc4,hang=-1)
par(opar)
(3) 基于四种距离度量的运行结果