一、聚类任务
聚类试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇”(cluster).通过这样的划分,每个簇可能对应于一些潜在的概念。
聚类既能作为一个单独过程,用于找寻数据内在的分布结构,也可作为分类等其他学习任务的前驱过程。
二、性能度量
好的聚类结果:“簇内相似度”(intra-cluster similarity)高且“簇间相似度”(inter-cluster similarity)低.
聚类性能度量大致有两类. 一类是将聚类结果与某个“参考模型”(reference model)进行比较,称为“外部指标”(external index);另一类是直接考察聚类结果而不利用任何参考模型,称为“内部指标”(internalindex).
外部指标:
对数据集,假定通过聚类给出的簇划分为
,参考模型给出的簇划分为
.相应地,令
与
分别表示与C和C*对应的簇标记向量.我们将样本两两配对考虑,定义
- Jaccard系数(Jaccard Coefficient,简称JC):
- FM指数(Fowlkes and Mallows Index,简称FMI):
- Rand指数(Rand Index,简称RI):
上述性能度量的结果值均在 [0 1] 区间,值越大越好。
内部指标:
考虑聚类结果的簇划分,定义
(用于计算两个样本之间的距离;
代表簇C的中心点
)
- DB指数(Davies-Bouldin Index,简称DBI)
- Dunn指数(Dunn Index,简称DI)
DBI的值越小越好,而DI 则相反,值越大越好.
三、距离计算
给定样本与
- “闵可夫斯基距离”(Minkowski distance)
- p=2时,欧氏距离(Euclidean distance)
- p=1时,曼哈顿距离(Manhattan distance)
“连续属性”(continuous attribute)在定义域上有无穷多个可能的取值
“离散属性”(categorical attribute)在定义域上是有限个取值
- “有序属性”(ordinal attribute)能直接在属性值上计算距离(闵可夫斯基距离可用于有序属性)
- “无序属性”(non-ordinalattribute)不能直接在属性值上计算距离
无序属性:
令表示在属性u上取值为a的样本数,
表示在第i个样本簇中在属性u上取值为a的样本数,k为样本簇数
VDM (Value Difference Metric)距离:
闵可夫斯基距离和VDM结合处理混合属性.个有序属性、
个无序属性,不失一般性,令有序属性排列在无序属性之前,则
" 加权距离"(以加权闵可夫斯基距离为例):
四、原型聚类
原型聚类:先对原型进行初始化,然后对原型进行迭代更新求解.采用不同的原型表示、不同的求解方式,将产生不同的算法.
k均值算法:
给定样本集,“k 均值”(k-means)算法针对聚类所得簇划分
最小化平方误差
其中是簇C的均值向量
算法流程:
利用K-均值聚类算法对未标注数据分组:
import math
from numpy import *
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
print(curLine)
fltLine = list(map(float, curLine))
dataMat.append(fltLine)
return dataMat
'''
distEclud(vecA, vecB)函数计算两个向量的欧式距离
公式:sqrt((x1-x2)^2+(y1-y2)^2)
'''
def distEclud(vecA, vecB):
return math.sqrt(sum(power(vecA - vecB, 2)))
'''
randCent()函数为给定数据集构建一个包含k个随机质心的集合。
随机质心必须要在整个数据集的边界之内,这可以通过找到数据集每一维的最小值和最大值来完成。
然后生成0到1.0之间的随机数并通过取值范围和最小值,以便确保随机点在数据的边界之内。
'''
def randCent(dataSet, k):
n = shape(dataSet)[1]
centroids = mat(zeros((k, n))) # 创建存储k个质心的矩阵
for j in range(n): # 在边界范围内,随机生成k个质心
minJ = min(dataSet[:, j]) # 边界的最小值
rangeJ = float(max(dataSet[:, j]) - minJ) # 边界范围
centroids[:, j] = mat(minJ + rangeJ * random.rand(k, 1))
return centroids
'''
K-Means算法:
两个必选参数:
dataSet:该参数为给定的数据集,
k:该参数为簇的数目,
两个可选的参数:
distEclud:计算两个向量组之间的距离,
randCent:创建初始质心;
该算法会创建k个质心,然后将每个点分配到最近的质心,再重新计算质心。
这个过程会重复多次,知道数据点的簇分配结果不再改变为止。
'''
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
# 确定数据集中数据点的总行数,shape()函数:查看矩阵或者数组的维数。
m = shape(dataSet)[0]
# 创建一个矩阵来存放每个点的簇分配结果,包含两列:一列是记录簇索引值,第二列是存储误差。
# 误差是指当前点到簇质心的距离,后面将使用该误差来评价聚类的效果。
clusterAssment = mat(zeros((m, 2)))
# 通过功能函数randCent()随机获取K个质心,K由用户决定
centroids = createCent(dataSet, k)
# 标志变量:是否需要迭代的标志。
# 初始为True,表示需要迭代,迭代的顺序为:计算质心-分配点到距离质心最短的簇中-重新计算
# 直到所有数据点的簇分配结果不再改变为止
clusterChanged = True
# while循环:一直迭代循环计算:计算质心-分配-重新计算
while clusterChanged:
clusterChanged = False
# 计算每个点到所有质心的距离,将每个点分配到距离其最近的质心所在的簇中
for i in range(m):
minDist = inf;
minIndex = -1 # 质心的索引
# for循环:寻找最近的质心
for j in range(k):
distJI = distMeas(centroids[j, :], dataSet[i, :]) # 计算数据点到每个质心的距离
if distJI < minDist: # 如果该距离比最小的距离还小,则更新minDist(最小距离)和最小质心的index(索引)
minDist = distJI;
minIndex = j
# 如果任一点的簇分配结果发生改变,则更新标志的值,表示点的分配结果还未完成,需要继续迭代计算。
if clusterAssment[i, 0] != minIndex: clusterChanged = True
clusterAssment[i, :] = minIndex, minDist ** 2
print("--------------------------------------------------")
print(centroids) # 打印所有质心
# 遍历所有质心,并更新质心的取值。
# 实现逻辑:首先通过数据过滤来获得给定簇的所有点。然后计算所有点的均值,该均值即为新的质心的坐标。
# axis=0表示沿矩阵的列方向进行均值计算;最后返回所有的类质心与点分配结果
for cent in range(k):
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]] # 获取该簇中所有的点
centroids[cent, :] = mean(ptsInClust, axis=0) # 将所有点的横、纵坐标的平均值赋给质心,质心的位置改变,mean()函数就是求平均值
# 返回每个类的质心,以及该类所包含的所有的数据点
return centroids, clusterAssment
#画图
def show(dataSet, k, centroids, clusterAssment):
from matplotlib import pyplot as plt
numSamples, dim = dataSet.shape
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
for i in range(numSamples):
markIndex = int(clusterAssment[i, 0])
plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])
mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb']
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 7, color='orange')
plt.show()
def main():
dataMat = mat(loadDataSet('testSet.txt'))
# 指定获取四个质心
myCentroids, clustAssing = kMeans(dataMat, 4)
# myCentroids, clustAssing = biKmeans(dataMat, 4)
print("--------------------------------------------------")
print("最终的质心列表:")
print(myCentroids)
print("--------------------------------------------------")
show(dataMat, 4, myCentroids, clustAssing)
if __name__ == '__main__':
main()
运行结果:
学习向量量化:
“学习向量量化”(Learning Vector Quantization,简称LVQ)试图找到一组原型向量来刻画聚类结构,LVQ假设数据样本带有类别标记,学习过程利用样本的这些监督信息来辅助聚类.
LVQ算法描述:
高斯混合聚类:
高斯混合(Mixture-of-Gaussian)聚类采用概率模型来表达聚类原型
高斯分布概率密度函数:
其中,为n维均值向量,
为
的协方差矩阵,因此高斯分布完全由这两个参数确定,就可以写成
。
高斯混合分布:
若训练集由上述过程生成,令随机变量
表示生成样本
,的高斯混合成分,其取值未知.显然,
的先验概率
对应于
根据贝叶斯定理,
的后验分布对应于
高斯混合聚类算法:
五、密度聚类
密度聚类亦称“基于密度的聚类”(density-based clustering),假设聚类结构能通过样本分布的紧密程度确定.通常情形下,密度聚类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终的聚类结果.
DBSCAN基于一组“邻域”(neigh-borhood)参数(, MinPts)来刻画样本分布的紧密程度.给定数据集
,定义下面这几个概念:
-邻域:对
,其
-邻域包含样本集D中与
的距离不大于
的样本,即
;
- 核心对象(core object):若
的
-邻域至少包含MinPts个样本,即
,则
是一个核心对象;
- 密度直达(directly density-reachable):若
位于
的
-邻域中,且
是核心对象,则称
由
密度直达
- 密度可达(density-reachable):对
和
,若存在样本序列
,其中
,且
由
密度直达,那么称
由
密度可达
- 密度相连(density-connected):对
和
,若存在
使得
和
均由
密度可达,那么
和
密度相连
DBSCAN算法:
六、层次聚类
层次聚类(hierarchical clustering)试图在不同层次对数据集进行划分,从而形成树形的聚类结构.数据集的划分可采用“自底向上”的聚合策略,也可采用“自顶向下”的分拆策略.
AGNES是一种采用自底向上聚合策略的层次聚类算法.它先将数据集中的每个样本看作一个初始聚类簇,然后在算法运行的每一步中找出距离最近的两个聚类簇进行合并,该过程不断重复,直至达到预设的聚类簇个数.
给定聚类簇与
:
以上三种算法,AGNES 算法被相应地称为“单链接”(single-linkage)、“全链接”(complete-linkage)或“均链接”(average-linkage)算法.
AGNES算法: