⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议。
Skip to content

第14章 聚类方法

习题14.1

  试写出分裂聚类算法,自上而下地对数据进行聚类,并给出其算法复杂度。

解答:

解答思路:

  1. 给出一般聚类方法概述
  2. 给出分裂聚类的定义
  3. 给出分裂聚类预先确定的要素
  4. 写出分裂聚类算法,并计算复杂度
  5. 自编程实现分裂聚类算法

解答步骤:

第1步:一般聚类方法概述

  根据书中第14章的聚类方法介绍:

  聚类是针对给定的样本,依据它们的特征的相似度或距离,将其归并到若干个“类”或“簇”的数据分析问题。一个类是给定样本集合的一个子集。直观上,相似的样本聚集在相同的类,不相似的样本分散在不同的类。常用的聚类算法包括层次聚类和k均值聚类。层次聚类又分为聚合(自下而上)和分裂(自上而下)两种方法。

第2步:分裂聚类的定义

  根据书中第14章的分裂聚类介绍:

  分裂法开始将所有样本分到一个类,之后将已有类中相距最远的样本分到两个新的类,重复此操作直到满足停止条件,得到层次化的类别。

第3步:分裂聚类预先确定的要素

  分裂聚类的具体过程如下:对于给定的样本集合,开始将所有样本分到一个类,然后按照一定分裂方法,例如类中距离最大,将最满足规则条件的样本分到两个新的类;如此反复进行,每次减少剩余未分类的样本,直到满足停止条件,如所有样本都分到了对应的类中。

  由此可知,分裂聚类需要预先确定下面三个要素:
  (1)选择要分裂的类方法;
  (2)分裂类的方法;
  (3)停止条件。

  根据这些要素的不同组合,就可以构成不同的分裂聚类方法。选择要分裂的类的方法可以是选择最大直径的类、样本最多的类、最大均值的类、最大SSE的类。分裂类的方法可以是使用贪心算法选择新类中心、K-means算法、Ward方法。停止条件可以是每个类包含一个样本、类的个数达到阈值。

第4步:写出分裂聚类算法,并计算复杂度

分裂聚类算法
输入:n个样本组成的样本集合及设定的样本类别数k
输出:满足设定的样本类别数的样本集合的一个层次化聚类。
(1)构造1个类,该类包含全部样本。
(2)计算n个样本两两之间的欧氏距离{dij}
(3)分裂类中距离最大的两个样本,将其分到两个类,并设置为各自的类中心。
(4)计算未分裂的样本与目前各个类中心的距离,分裂类中距离最大的样本作为新的类中心,构造一个新类。
(5)如果类的个数满足设定的样本类别数k,则继续执行第(6)步,否则回到第(4)步继续分裂样本。
(6)根据当前的类中心,按照最近邻的原则对整个数据集进行分类。

计算复杂度:

  1. 时间复杂度为O(kn2m):整个算法计算过程中需要计算两两样本之间的复杂度,设m是样本的维度,总体样本为n个,则时间复杂度为O(n2m);设定的样本类别数为k,则需要重复k次,故时间复杂度为O(kn2m)
  2. 空间复杂度为O(n2):需要一个n×n的矩阵保存两两样本间的距离。

第4步:自编程实现分裂聚类算法

python
import numpy as np


class DivisiveClustering:
    def __init__(self, num_class):
        # 聚类类别个数
        self.num_class = num_class
        # 聚类数据集
        self.cluster_data = []
        if num_class > 1:
            self.cluster_data = [[] for _ in range(num_class)]

    def fit(self, data):
        """
        :param data: 数据集
        """
        num_sample = data.shape[0]

        if self.num_class == 1:
            # 如果只设定了一类,将所有数据放入到该类中
            for d in data:
                self.cluster_data.append(d)
        else:
            # (1) 构造1个类,该类包含全部样本
            # 初始化类中心
            class_center = []

            # (2) 计算n个样本两两之间的欧氏距离
            distance = np.zeros((num_sample, num_sample))
            for i in range(num_sample):
                for j in range(i + 1, num_sample):
                    distance[j, i] = distance[i, j] = np.linalg.norm(data[i, :] - data[j, :], 
                                                                     ord=2)

            # (3) 分裂距离最大的两个样本,并设置为各自的类中心
            index = np.where(np.max(distance) == distance)
            class_1 = index[1][0]
            class_2 = index[1][1]
            # 记录已经分裂完成的样本
            finished_data = [class_1, class_2]
            class_center.append(data[class_1, :])
            class_center.append(data[class_2, :])

            num_class_temp = 2
            # (5) 判断类的个数是否满足设定的样本类别数
            while num_class_temp != self.num_class:
                # (4.1) 计算未分裂的样本与目前各个类中心的距离
                data2class_distance = np.zeros((num_sample, 1))
                for i in range(num_sample):
                    # 计算样本到各类中心的距离总和
                    data2class_sum = 0
                    for j, center_data in enumerate(class_center):
                        if i not in finished_data:
                            data2class_sum += np.linalg.norm(data[i, :] - center_data)
                    data2class_distance[i] = data2class_sum

                # (4.2) 分裂类间距离最大的样本作为新的类中心,构造一个新类
                class_new_index = np.argmax(data2class_distance)
                num_class_temp += 1
                finished_data.append(class_new_index)
                # 添加到类中心集合中
                class_center.append(data[class_new_index, :])

            # 根据当前的类中心,按照最近邻的原则对整个数据集进行分类
            for i in range(num_sample):
                data2class_distance = []
                for j, center_data in enumerate(class_center):
                    # 计算每个样本到类中心的距离
                    data2class_distance.append(np.linalg.norm(data[i, :] - center_data))

                # 将样本划分到最近的中心的类中
                label = np.argmin(data2class_distance)
                self.cluster_data[label].append(data[i, :])
python
# 使用书中例14.2的样本数据集
dataset = np.array([[0, 2],
                    [0, 0],
                    [1, 0],
                    [5, 0],
                    [5, 2]])

num_class = 2
divi_cluster = DivisiveClustering(num_class=num_class)
divi_cluster.fit(dataset)
print("分类数:", num_class)
for i in range(num_class):
    print(f"class_{i}:", divi_cluster.cluster_data[i])
分类数: 2
class_0: [array([0, 0]), array([1, 0]), array([5, 0])]
class_1: [array([0, 2]), array([5, 2])]

习题14.2

  证明类或簇的四个定义中,第一个定义可推出其他三个定义。

解答:

解答思路:

  1. 给出定义14.5
  2. 给出定义14.6,并由定义14.5推导定义14.6
  3. 给出定义14.7,并由定义14.5推导定义14.7
  4. 给出定义14.8,并由定义14.5推导定义14.8

解答步骤:

第1步:定义14.5

  根据书中第14.1.2节的定义14.5:

  用G表示类或簇,用xi,xj表示类中的样本,NG表示G中样本的个数,用dij表示样本xi与样本xj之间的距离。

定义14.5T为给定的正数,若集合G中任意两个样本xi,xj,有

dijT

则称G为一个类或簇。

第2步:由定义14.5推导定义14.6

证明:

  根据书中第14.1.2节的定义14.6:

定义14.6T为给定的正数,若对集合G中任意样本xi,一定存在G中另一个样本xj,使得

dijT

则称G为一个类或簇。

  由定义14.5可知,T大于等于集合G中两两样本间的距离最大值:

DG=maxxi,xjGdijT

  则集合G中任取两个样本xi,xj,满足

dijDGT

  定义14.6得证。

第3步:由定义14.5推导定义14.7

证明:

  根据书中第14.1.2节的定义14.7:

定义14.7T为给定的正数,若对集合G中任意一个样本xiG中的另一个样本$ x_j$满足

1nG1xjGdijT

其中nG为G中样本的个数,则称G为一个类或簇。

  将集合G中除样本xi,其余nG1个样本与xi的距离dij求和,有

xjGdij(nG1)T

  所以

1nG1xjGdijT

  定义14.7得证。

第4步:由定义14.5推导定义14.8

证明:

  根据书中第14.1.2节的定义14.8:

定义14.8TV为给定的两个正数,如果集合G中任意两个样本xi,xj的距离dij满足

1nG(nG1)xiGxjGdijTdijV

则称G为一个类或簇。

  根据定义14.5可知:dijT,则不妨令V=T,对于G中任意两个样本xi,xj,则有dijV,dijT,结合定义14.7,则有

xiGxjGdijnG(nG1)T

  所以

1nG(nG1)xiGxjGdijTdijV

  定义14.8得证。

习题14.3

  证明式(14.21)成立,即k均值的可能解的个数是指数级的。

解答:

解答思路:

  1. 给出书中公式(14.21)
  2. 给出可区分球到不可区分盒子的分配策略,得到指数的生成函数
  3. 利用指数生成函数进行二项式展开,推导并证明公式

解答步骤:

第1步:书中公式(14.21)

  根据书中第14.3.2节的描述:

  k均值聚类就是求解最优化问题:

C=argminCW(C)=argminCl=1kC(i)=lxix¯l2

  相似的样本被聚到同类中,损失函数值最小,这个目标函数的最优化能达到聚类的目的。但是,这是一个组合优化问题,n个样本分到k类,所有可能分法的数目是:

(14.21)S(n,k)=1k!l=0k(1)kl(kl)ln

这个数字是指数级的。

第2步:给出可区分球到不可区分盒子的分配策略

  以下示例来自于《应用组合数学(第2版)》第216页5.5.3节:

  S(n,k)的定义是把n个可区分球分配到k个可区分盒子里,其每一个盒子不为空时的方法数量。 首先,考虑把n个可区分球放入标有标签1,2,,k的可区分盒子里,且每一个盒子都不为空的方法数量T(n,k)的问题,注意

T(n,k)=k!S(n,k)

确定把n个可区分球放入k个不为空的不可区分盒子的分配,然后再标记(排序)这些盒子,接下来计算T(n,k)。假设球i放入到盒子C(i)中,可以通过给出序列C(1)C(2)C(n)编码把球放入可区分盒子里的分配,这是k集合{1,2,,k}的一个n排列,且k集合中的每一个标签j至少使用一次,因此,T(n,k)是这一排列的数量,且对于固定的kT(n,k)的指数生成函数由下式给出:

H(x)=(x+x22!+x33!+)k=(ex1)k

其中T(n,k)H(x)的展开式中xnn!的系数。

  根据《应用组合数学(第2版)》第213页指数生成函数定义:

  记P(n,k)n集合的k排序的数量,固定n,则P(n,k)的普通生成函数由下式给出:

G(x)=P(n,0)x0+P(n,1)x1+P(n,2)x2++P(n,n)xn=k=0(nk)!n!xk

结合从n集合中取k个元素的数量C(n,k),则有下面的表达式:

C(n,0)x0+C(n,1)x1+C(n,2)x2++C(n,n)xn

通过二项式展开可以化简为(1+x)n,则有:

P(n,r)=C(n,r)P(r,r)=C(n,r)r!

根据上式可重写为

P(n,0)x00!+P(n,1)x11!+P(n,2)x22!++P(n,n)xnn!=(1+x)n

其中P(n,k)(1+x)n的展开式中xkk!的系数。
  如果(ak)是任意序列,则一序列的指数生成函数是

H(x)=a0x00!+a1x11!+a2x22!++akxkk!=kakxkk!

说明:

  已知,S(n,k)是将n个样本分到k类中的数量,和上述描述的一致,则可考虑这个k类是不可区分的,即分类是有重复的,得到如下式子:

T(n,k)=k!S(n,k)

  将第i个球放入第C(i)个盒子中,由于k个盒子是不可区分的,故需要进行k的指数,所以该指数生成函数是

H(x)=(x+x22!+x33!+)k=(ex1)k

其中T(n,k)H(x)的展开式中xnn!的系数

第3步:进行二项式展开推导公式

  《应用组合数学(第2版)》第216页5.5.3节:

根据二项式展开(定理2.7),有

H(x)=i=0k(ki)(1)ie(ki)x

(ki)x取代x,得到

H(x)=i=0k(ki)(1)in=01n!(ki)nxn=n=0xnn!i=0k(1)i(ki)(ki)n

由于T(n,k)H(x)的展开式中xnn!的系数,可得

T(n,k)=i=0k(1)i(ki)(ki)n

结合T(n,k)=k!S(n,k),可得

S(n,k)=1k!i=0k(1)i(ki)(ki)n

  将上述公式的ki替换成l,即l=ki,可得

S(n,k)=1k!l=0k(1)kl(kl)ln

习题14.4

  比较k均值聚类与高斯混合模型加EM算法的异同。

解答:

解答思路:

  1. 给出k均值聚类算法
  2. 给出高斯混合模型参数估计的EM算法
  3. 写出两者的相同点
  4. 写出两者的不同点

解答步骤:

第1步:k均值聚类算法

  根据书中第14.3.3节的k均值聚类算法:

  k均值聚类的算法是一个迭代的过程,每次迭代包括两个步骤,首先选择k个类的中心,将样本逐个指派到与其最近的中心的类中,得到一个聚类结果;然后更新每个类的样本的均值,作为类的新的中心;重复以上步骤,直到收敛为止。具体过程如下:   首先,对于给定的中心值(m1,m2,,mk),求一个划分C,使得目标函数极小化:

minCl=1kC(i)=lximl2

就是说在类中心确定的情况下,将每个样本分到一个类中,使样本和其所属类的中心之间的距离总和最小。求解结果,将每个样本指派到与其最近的中心ml的类Gl中。
  然后,对给定的划分C,再求各个类的中心(m1,m2,,mk),使得目标函数极小化:

minm1,...mkl=1kC(i)=lximl2

就是说在划分确定的情况下,使样本和其所属类的中心之间的距离总和最小。求解结果,对于每个包含nl个样本的类Gl,更新其均值ml

ml=1nlC(i)=lxi,l=1,,k

  重复以上两个步骤,直到划分不再改变,得到聚类结果。

第2步:高斯混合模型参数估计的EM算法

  根据书中第9章的定义9.2的高斯混合模型:

定义9.2(高斯混合模型) 高斯混合模型是指具有如下形式的概率分布模型:

P(y|θ)=k=1Kαkϕ(y|θk)

其中,αk是系数,αk0k=1Kαk=1ϕ(y|θk)是高斯分布密度,θk=(uk,σk2)

ϕ(y|θk)=12πσkexp((yuk)22σk2)

  根据书中第9.3.2节的高斯混合模型参数估计的EM算法:

假设观测数据y1,y2,,yN由高斯混合模型生成,

P(y|θ)=k=1Kαkϕ(y|θk)

其中,θ=(α1,α2,,αK;θ1,θ2,,θK)。 1.明确隐变量,写出完全数据的对数似然函数
可定义隐变量γjk如下:

γjk={1,j个观测来自第k个分模型0,否则j=1,2,,N;k=1,2,,K

γjk是0-1随机变量。
完全数据的对数似然函数为

logP(y,γ|θ)=k=1K{nklogαk+j=1Nγjk[log(12π)logσk12σk2(yjuk)2]}

2.EM算法的E步:依据当前模型参数,计算分模型k对观测数据yj的响应度

γ^jk=αkγ(yj|θk)k=1Kαkγ(yj|θk),j=1,2,,N;k=1,2,,K

3.EM算法的M步:计算新一轮迭代的模型参数

u^k=j=1Nγ^jkyjj=1Nγ^jk,k=1,2,,Kσ^2=j=1Nγ^jk(yjuk)2j=1Nγ^jkk=1,2,,Kα^k=j=1Nγ^jkN,k=1,2,,K

重复以上计算,直至对数似然函数值不再有明显的变化为止。

第3步:两者的相同点

  1. 两者求解的步骤相似:k均值聚类算法和高斯混合模型的EM算法的求解方式都是迭代求解,k均值聚类算法的求解中的E步是将每个样本分配到距离最近的类中心;M步是根据分配后的样本更新各类的均值
  2. 两者都需要提前指定k值,并且分类都受初始的k值影响
  3. 两者都有可能收敛到局部最优解

第4步:两者的不同点

  1. 高斯混合模型的EM算法可以看作k均值聚类算法的软扩展(Soft extension):k均值聚类算法中每一个样本属于k个类别中的某一个类别是确定性的,高斯混合模型的EM算法中每一个样本属于某一类是由k个高斯分布分模型按照某一概率分布表示的;
  2. 待估计的参数不同,换言之,即损失函数不同。k均值聚类算法中需要更新的参数是各类的均值μ,高斯混合模型的EM算法中需要更新的参数是先验α、均值μ、方差σ

参考文献

【1】Fred S. Roberts, Barry Tesman, 冯速(译).应用组合数学(原书第2版)[M]. 2007(1):216