聚类
在机器学习的"无监督学习"(unsupervised learning)领域中,训练样本的标记信息是未知的。我们的目标是通过对无标记训练样本的学习,来揭示数据的内在性质及规律,为进一步的数据分析提供基础。而在这些任务中,研究最多、应用最广的当属聚类(Clustering)。
python
# 基础库导入(建议放在博客最开头)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs, make_moons
import warnings
warnings.filterwarnings('ignore') # 忽略一些版本警告
1. 聚类任务概述
聚类试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个"簇"(cluster)。通过这样的划分,每个簇可能对应于一些潜在的概念(类别)。
需要说明的是,这些概念对聚类算法而言事先是未知的,聚类过程仅能自动形成簇结构,而簇所对应的概念语义需由使用者来把握和命名。例如,将西瓜数据集聚类后可能得到"浅色瓜"、"深色瓜"等簇。
1.1 形式化定义
假定样本集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}2, \dots, \boldsymbol{x}m\}D={x1,x2,...,xm} 包含 mmm 个无标记样本,每个样本 xi=(xi1;xi2;... ;xin)\boldsymbol{x}i = (x{i1}; x{i2}; \dots; x{in})xi=(xi1;xi2;...;xin) 是一个 nnn 维特征向量。
聚类算法将样本集 DDD 划分为 kkk 个不相交的簇 {Cl∣l=1,2,...,k}\{C_l \mid l = 1, 2, \dots, k\}{Cl∣l=1,2,...,k},其中 Cl′∩Cl=∅C_{l'} \cap C_{l} = \varnothingCl′∩Cl=∅ 且 D=⋃l=1kClD = \bigcup_{l=1}^k C_lD=⋃l=1kCl。
相应地,我们用 λj∈{1,2,...,k}\lambda_j \in \{1, 2, \dots, k\}λj∈{1,2,...,k} 表示样本 xj\boldsymbol{x}_jxj 的"簇标记"(cluster label),即 xj∈Cλj\boldsymbol{x}j \in C{\lambda_j}xj∈Cλj。于是,聚类的结果可用包含 mmm 个元素的簇标记向量 λ=(λ1;λ2;... ;λm)\boldsymbol{\lambda} = (\lambda_1; \lambda_2; \dots; \lambda_m)λ=(λ1;λ2;...;λm) 表示。
python
# --- 代码示例:生成聚类数据 ---
# 生成模拟数据:400个样本,4个中心,标准差为0.60
X, y_true = make_blobs(n_samples=400, centers=4, random_state=42, cluster_std=0.60)
# 可视化数据(不显示标签,模拟聚类任务的输入)
plt.figure(figsize=(8, 5))
plt.scatter(X[:, 0], X[:, 1], s=50, c='gray', alpha=0.6, edgecolors='w')
plt.title("Unlabeled Data / 无标记样本集 D")
plt.xlabel("Feature 1 ($x_1$)")
plt.ylabel("Feature 2 ($x_2$)")
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
2. 性能度量
聚类性能度量亦称聚类"有效性指标"(validity index)。与监督学习中的性能度量作用相似,我们需通过某种性能度量来评估聚类结果的好坏;另一方面,若明确了最终将要使用的性能度量,则可直接将其作为聚类过程的优化目标。
直观上看,我们希望"物以类聚",即同一簇的样本尽可能彼此相似,不同簇的样本尽可能不同。换言之,聚类结果的"簇内相似度"(intra-cluster similarity)高且"簇间相似度"(inter-cluster similarity)低。
聚类性能度量大致有两类:
- 外部指标(External Index):将聚类结果与某个"参考模型"(reference model)进行比较。
- 内部指标(Internal Index):直接考察聚类结果而不利用任何参考模型。
2.1 外部指标
对数据集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm},假定通过聚类给出的簇划分为 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck},参考模型给出的簇划分为 C∗={C1∗,C2∗,...,Cs∗}\mathcal{C}^* = \{C^*_1, C^*_2, \dots, C^*_s\}C∗={C1∗,C2∗,...,Cs∗}。相应地,令 λ\boldsymbol{\lambda}λ 与 λ∗\boldsymbol{\lambda}^*λ∗ 分别表示 C\mathcal{C}C 和 C∗\mathcal{C}^*C∗ 对应的簇标记向量。
我们将样本两两配对考虑,定义:
a=∣SS∣,SS={(xi,xj)∣λi=λj,λi∗=λj∗,i<j} a = |SS|, \quad SS = \{(\boldsymbol{x}_i, \boldsymbol{x}_j) \mid \lambda_i = \lambda_j, \lambda^*_i = \lambda^*_j, i < j\} a=∣SS∣,SS={(xi,xj)∣λi=λj,λi∗=λj∗,i<j}
b=∣SD∣,SD={(xi,xj)∣λi=λj,λi∗≠λj∗,i<j} b = |SD|, \quad SD = \{(\boldsymbol{x}_i, \boldsymbol{x}_j) \mid \lambda_i = \lambda_j, \lambda^*_i \neq \lambda^*_j, i < j\} b=∣SD∣,SD={(xi,xj)∣λi=λj,λi∗=λj∗,i<j}
c=∣DS∣,DS={(xi,xj)∣λi≠λj,λi∗=λj∗,i<j} c = |DS|, \quad DS = \{(\boldsymbol{x}_i, \boldsymbol{x}_j) \mid \lambda_i \neq \lambda_j, \lambda^*_i = \lambda^*_j, i < j\} c=∣DS∣,DS={(xi,xj)∣λi=λj,λi∗=λj∗,i<j}
d=∣DD∣,DD={(xi,xj)∣λi≠λj,λi∗≠λj∗,i<j} d = |DD|, \quad DD = \{(\boldsymbol{x}_i, \boldsymbol{x}_j) \mid \lambda_i \neq \lambda_j, \lambda^*_i \neq \lambda^*_j, i < j\} d=∣DD∣,DD={(xi,xj)∣λi=λj,λi∗=λj∗,i<j}
其中:
- 集合 SSSSSS 包含了在 C\mathcal{C}C 中隶属于相同簇且在 C∗\mathcal{C}^*C∗ 中也隶属于相同簇的样本对。
- 集合 SDSDSD 包含了在 C\mathcal{C}C 中隶属于相同簇但在 C∗\mathcal{C}^*C∗ 中隶属于不同簇的样本对。
- 由于每个样本对 (xi,xj) (i<j)(\boldsymbol{x}_i, \boldsymbol{x}_j) \ (i < j)(xi,xj) (i<j) 仅能出现在一个集合中,因此 a+b+c+d=m(m−1)/2a+b+c+d = m(m-1)/2a+b+c+d=m(m−1)/2 成立。
基于上述定义,导出常用的外部指标:
-
Jaccard 系数 (Jaccard Coefficient, JC)
JC=aa+b+c \text{JC} = \frac{a}{a+b+c} JC=a+b+ca -
FM 指数 (Fowlkes and Mallows Index, FMI)
FMI=aa+b⋅aa+c \text{FMI} = \sqrt{\frac{a}{a+b} \cdot \frac{a}{a+c}} FMI=a+ba⋅a+ca -
Rand 指数 (Rand Index, RI)
RI=2(a+d)m(m−1) \text{RI} = \frac{2(a+d)}{m(m-1)} RI=m(m−1)2(a+d)
显然,上述性能度量的结果值均在 [0,1][0, 1][0,1] 区间,值越大越好。
2.2 内部指标
考虑聚类结果的簇划分 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck},定义:
-
簇内平均距离 :
avg(C)=2∣C∣(∣C∣−1)∑1⩽i<j⩽∣C∣dist(xi,xj) \text{avg}(C) = \frac{2}{|C|(|C|-1)} \sum_{1 \leqslant i < j \leqslant |C|} \text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) avg(C)=∣C∣(∣C∣−1)21⩽i<j⩽∣C∣∑dist(xi,xj) -
簇内最大距离(簇直径) :
diam(C)=max1⩽i<j⩽∣C∣dist(xi,xj) \text{diam}(C) = \max_{1 \leqslant i < j \leqslant |C|} \text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) diam(C)=1⩽i<j⩽∣C∣maxdist(xi,xj) -
簇间最近样本距离 :
dmin(Ci,Cj)=minxi∈Ci,xj∈Cjdist(xi,xj) d_{\text{min}}(C_i, C_j) = \min_{\boldsymbol{x}_i \in C_i, \boldsymbol{x}_j \in C_j} \text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) dmin(Ci,Cj)=xi∈Ci,xj∈Cjmindist(xi,xj) -
簇中心点距离 :
dcen(Ci,Cj)=dist(μi,μj) d_{\text{cen}}(C_i, C_j) = \text{dist}(\boldsymbol{\mu}_i, \boldsymbol{\mu}_j) dcen(Ci,Cj)=dist(μi,μj)其中 μ\boldsymbol{\mu}μ 代表簇 CCC 的中心点 μ=1∣C∣∑1⩽i⩽∣C∣xi\boldsymbol{\mu} = \frac{1}{|C|} \sum_{1 \leqslant i \leqslant |C|} \boldsymbol{x}_iμ=∣C∣1∑1⩽i⩽∣C∣xi。
基于上述定义,导出常用的内部指标:
-
DB 指数 (Davies-Bouldin Index, DBI)
DBI=1k∑i=1kmaxj≠i(avg(Ci)+avg(Cj)dcen(μi,μj)) \text{DBI} = \frac{1}{k} \sum_{i=1}^k \max_{j \neq i} \left( \frac{\text{avg}(C_i) + \text{avg}(C_j)}{d_{\text{cen}}(\boldsymbol{\mu}_i, \boldsymbol{\mu}_j)} \right) DBI=k1i=1∑kj=imax(dcen(μi,μj)avg(Ci)+avg(Cj)) -
Dunn 指数 (Dunn Index, DI)
DI=min1⩽i⩽k{minj≠i(dmin(Ci,Cj)max1⩽l⩽kdiam(Cl))} \text{DI} = \min_{1 \leqslant i \leqslant k} \left\{ \min_{j \neq i} \left( \frac{d_{\text{min}}(C_i, C_j)}{\max_{1 \leqslant l \leqslant k} \text{diam}(C_l)} \right) \right\} DI=1⩽i⩽kmin{j=imin(max1⩽l⩽kdiam(Cl)dmin(Ci,Cj))}
显然,DBI 的值越小越好,而 DI 则相反,值越大越好。
python
# --- 代码示例:计算聚类性能指标 ---
from sklearn.cluster import KMeans
from sklearn.metrics import adjusted_rand_score, davies_bouldin_score
# 先执行一个简单的 K-Means 聚类以获得预测标签
kmeans = KMeans(n_clusters=4, random_state=42)
y_pred = kmeans.fit_predict(X)
# 1. 外部指标:Adjusted Rand Index (ARI)
# ARI 是 Rand Index 的改进版,取值范围 [-1, 1],1 表示完美匹配
ari_score = adjusted_rand_score(y_true, y_pred)
# 2. 内部指标:Davies-Bouldin Index (DBI)
# 不需要真实标签,值越小越好
dbi_score = davies_bouldin_score(X, y_pred)
print(f"聚类结果评估:")
print(f"外部指标 (ARI, 越接近1越好): {ari_score:.4f}")
print(f"内部指标 (DBI, 越小越好): {dbi_score:.4f}")
3. 距离计算
对函数 dist(⋅,⋅)\text{dist}(\cdot, \cdot)dist(⋅,⋅),若它是一个"距离度量"(distance measure),则需满足以下基本性质:
- 非负性 :dist(xi,xj)⩾0\text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) \geqslant 0dist(xi,xj)⩾0
- 同一性 :dist(xi,xj)=0\text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) = 0dist(xi,xj)=0 当且仅当 xi=xj\boldsymbol{x}_i = \boldsymbol{x}_jxi=xj
- 对称性 :dist(xi,xj)=dist(xj,xi)\text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) = \text{dist}(\boldsymbol{x}_j, \boldsymbol{x}_i)dist(xi,xj)=dist(xj,xi)
- 直递性 (亦称三角不等式):dist(xi,xj)⩽dist(xi,xk)+dist(xk,xj)\text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) \leqslant \text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_k) + \text{dist}(\boldsymbol{x}_k, \boldsymbol{x}_j)dist(xi,xj)⩽dist(xi,xk)+dist(xk,xj)
3.1 闵可夫斯基距离
给定样本 xi=(xi1;xi2;... ;xin)\boldsymbol{x}i = (x{i1}; x_{i2}; \dots; x_{in})xi=(xi1;xi2;...;xin) 与 xj=(xj1;xj2;... ;xjn)\boldsymbol{x}j = (x{j1}; x_{j2}; \dots; x_{jn})xj=(xj1;xj2;...;xjn),最常用的是闵可夫斯基距离 (Minkowski distance):
distmk(xi,xj)=(∑u=1n∣xiu−xju∣p)1p \text{dist}{\text{mk}}(\boldsymbol{x}i, \boldsymbol{x}j) = \left( \sum{u=1}^n |x{iu} - x{ju}|^p \right)^{\frac{1}{p}} distmk(xi,xj)=(u=1∑n∣xiu−xju∣p)p1
对 p⩾1p \geqslant 1p⩾1,该式显然满足距离度量的基本性质。
-
欧氏距离 (Euclidean distance) :当 p=2p=2p=2 时,即
disted(xi,xj)=∣∣xi−xj∣∣2=∑u=1n∣xiu−xju∣2 \text{dist}_{\text{ed}}(\boldsymbol{x}_i, \boldsymbol{x}_j) = ||\boldsymbol{x}i - \boldsymbol{x}j||2 = \sqrt{ \sum{u=1}^n |x{iu} - x{ju}|^2 } disted(xi,xj)=∣∣xi−xj∣∣2=u=1∑n∣xiu−xju∣2 -
曼哈顿距离 (Manhattan distance) :当 p=1p=1p=1 时,亦称"街区距离",即
distman(xi,xj)=∣∣xi−xj∣∣1=∑u=1n∣xiu−xju∣ \text{dist}_{\text{man}}(\boldsymbol{x}_i, \boldsymbol{x}_j) = ||\boldsymbol{x}i - \boldsymbol{x}j||1 = \sum{u=1}^n |x{iu} - x{ju}| distman(xi,xj)=∣∣xi−xj∣∣1=u=1∑n∣xiu−xju∣ -
切比雪夫距离 :当 p→∞p \to \inftyp→∞ 时得到。
python
# --- 代码示例:闵可夫斯基距离计算 ---
def minkowski_distance(x1, x2, p):
"""
计算两个向量之间的闵可夫斯基距离
"""
return np.sum(np.abs(x1 - x2) ** p) ** (1/p)
# 定义两个样本向量
vec_a = np.array([1, 2, 3])
vec_b = np.array([4, 6, 8])
# 计算不同 p 值下的距离
dist_manhattan = minkowski_distance(vec_a, vec_b, p=1) # 曼哈顿距离
dist_euclidean = minkowski_distance(vec_a, vec_b, p=2) # 欧氏距离
print(f"向量 A: {vec_a}, 向量 B: {vec_b}")
print(f"曼哈顿距离 (p=1): {dist_manhattan}")
print(f"欧氏距离 (p=2): {dist_euclidean:.4f}")
3.2 属性处理与 VDM
属性可分为"连续属性"(定义域有无穷多个取值)和"离散属性"(定义域有有限个取值)。
- 若离散属性定义了"序"(如 {1, 2, 3}),称为有序属性,可直接计算距离。
- 若离散属性无序(如 {飞机, 火车, 轮船}),称为无序属性,不能直接计算距离。
对无序属性,可采用 VDM (Value Difference Metric) 。令 mu,am_{u,a}mu,a 表示在属性 uuu 上取值为 aaa 的样本数,mu,a,im_{u,a,i}mu,a,i 表示在第 iii 个样本簇中在属性 uuu 上取值为 aaa 的样本数,kkk 为样本簇数,则属性 uuu 上两个离散值 aaa 与 bbb 之间的 VDM 距离为:
VDMp(a,b)=∑i=1k∣mu,a,imu,a−mu,b,imu,b∣p \text{VDM}p(a, b) = \sum{i=1}^k \left| \frac{m_{u,a,i}}{m_{u,a}} - \frac{m_{u,b,i}}{m_{u,b}} \right|^p VDMp(a,b)=i=1∑k mu,amu,a,i−mu,bmu,b,i p
3.3 混合属性距离与加权距离
将闵可夫斯基距离和 VDM 结合即可处理混合属性。假定有 ncn_cnc 个有序属性,n−ncn - n_cn−nc 个无序属性,令有序属性排列在无序属性之前,则:
MinkovDMp(xi,xj)=(∑u=1nc∣xiu−xju∣p+∑u=nc+1nVDMp(xiu,xju))1p \text{MinkovDM}p(\boldsymbol{x}i, \boldsymbol{x}j) = \left( \sum{u=1}^{n_c} |x{iu} - x{ju}|^p + \sum_{u=n_c+1}^n \text{VDM}p(x{iu}, x_{ju}) \right)^{\frac{1}{p}} MinkovDMp(xi,xj)=(u=1∑nc∣xiu−xju∣p+u=nc+1∑nVDMp(xiu,xju))p1
当样本空间中不同属性的重要性不同时,可使用加权距离 (weighted distance)。以加权闵可夫斯基距离为例:
distwmk(xi,xj)=(w1⋅∣xi1−xj1∣p+⋯+wn⋅∣xin−xjn∣p)1p \text{dist}{\text{wmk}}(\boldsymbol{x}i, \boldsymbol{x}j) = \left( w_1 \cdot |x{i1} - x{j1}|^p + \dots + w_n \cdot |x{in} - x_{jn}|^p \right)^{\frac{1}{p}} distwmk(xi,xj)=(w1⋅∣xi1−xj1∣p+⋯+wn⋅∣xin−xjn∣p)p1
其中权重 wi⩾0 (i=1,2,...,n)w_i \geqslant 0 \ (i=1, 2, \dots, n)wi⩾0 (i=1,2,...,n) 表征不同属性的重要性,通常 ∑i=1nwi=1\sum_{i=1}^n w_i = 1∑i=1nwi=1。
4. 原型聚类
原型聚类亦称"基于原型的聚类"(prototype-based clustering),此类算法假设聚类结构能通过一组原型刻画。通常情况下,算法先对原型进行初始化,然后对原型进行迭代更新求解。
4.1 k 均值算法 (k-means)
给定样本集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm},"kkk 均值"算法针对聚类所得簇划分 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck} 最小化平方误差:
E=∑i=1k∑x∈Ci∣∣x−μi∣∣22 E = \sum_{i=1}^k \sum_{\boldsymbol{x} \in C_i} ||\boldsymbol{x} - \boldsymbol{\mu}_i||_2^2 E=i=1∑kx∈Ci∑∣∣x−μi∣∣22
其中 μi=1∣Ci∣∑x∈Cix\boldsymbol{\mu}i = \frac{1}{|C_i|} \sum{\boldsymbol{x} \in C_i} \boldsymbol{x}μi=∣Ci∣1∑x∈Cix 是簇 CiC_iCi 的均值向量。直观来看,上式刻画了簇内样本围绕簇均值向量的紧密程度,EEE 值越小则簇内样本相似度越高。
算法流程:
由于最小化 EEE 是一个 NP 难问题,k 均值算法采用贪心策略,通过迭代优化来近似求解:
- 初始化 :从 DDD 中随机选择 kkk 个样本作为初始均值向量 {μ1,μ2,...,μk}\{\boldsymbol{\mu}_1, \boldsymbol{\mu}_2, \dots, \boldsymbol{\mu}_k\}{μ1,μ2,...,μk}。
- 簇分配 :计算样本 xj\boldsymbol{x}_jxj 与各均值向量 μi\boldsymbol{\mu}iμi 的距离 dji=∣∣xj−μi∣∣2d{ji} = ||\boldsymbol{x}_j - \boldsymbol{\mu}_i||2dji=∣∣xj−μi∣∣2。根据距离最近的均值向量确定 xj\boldsymbol{x}jxj 的簇标记:λj=arg mini∈{1,2,...,k}dji\lambda_j = \operatorname*{arg\,min}{i \in \{1, 2, \dots, k\}} d{ji}λj=argmini∈{1,2,...,k}dji,将样本划入相应的簇。
- 均值更新 :计算新均值向量 μi′=1∣Ci∣∑x∈Cix\boldsymbol{\mu}'i = \frac{1}{|C_i|} \sum{\boldsymbol{x} \in C_i} \boldsymbol{x}μi′=∣Ci∣1∑x∈Cix。
- 迭代 :若 μi′≠μi\boldsymbol{\mu}'_i \neq \boldsymbol{\mu}_iμi′=μi,则将当前均值向量更新为 μi′\boldsymbol{\mu}'_iμi′;否则保持不变。
- 终止:重复步骤 2-4,直到当前均值向量未更新或达到最大迭代轮数。
python
# --- 代码示例:K-Means 聚类可视化 ---
# 使用之前生成的数据 X
kmeans = KMeans(n_clusters=4, init='k-means++', random_state=42)
y_kmeans = kmeans.fit_predict(X)
centers = kmeans.cluster_centers_
plt.figure(figsize=(8, 5))
# 绘制不同簇的样本
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis', alpha=0.6)
# 绘制簇中心(原型)
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, marker='X', label='Centroids ($\mu$)')
plt.title("K-Means Clustering Result")
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
4.2 学习向量量化 (LVQ)
学习向量量化 (Learning Vector Quantization, LVQ) 也是试图找到一组原型向量来刻画聚类结构,但与一般聚类算法不同的是,LVQ 假设数据样本带有类别标记,学习过程利用样本的这些监督信息来辅助聚类。
给定样本集 D={(x1,y1),(x2,y2),...,(xm,ym)}D = \{(\boldsymbol{x}_1, y_1), (\boldsymbol{x}_2, y_2), \dots, (\boldsymbol{x}_m, y_m)\}D={(x1,y1),(x2,y2),...,(xm,ym)},每个样本 xj\boldsymbol{x}_jxj 是 nnn 维特征向量,yj∈Yy_j \in \mathcal{Y}yj∈Y 是类别标记。LVQ 的目标是学得一组 nnn 维原型向量 {p1,p2,...,pq}\{\boldsymbol{p}_1, \boldsymbol{p}_2, \dots, \boldsymbol{p}_q\}{p1,p2,...,pq},每个原型向量代表一个聚类簇,簇标记 ti∈Yt_i \in \mathcal{Y}ti∈Y。
算法关键步骤(原型向量更新):
在每一轮迭代中,算法随机选取一个有标记训练样本 (xj,yj)(\boldsymbol{x}j, y_j)(xj,yj),找出与其距离最近的原型向量 pi∗\boldsymbol{p}{i^*}pi∗。
根据两者的类别标记是否一致来对原型向量进行更新:
-
若类别一致 (yj=ti∗y_j = t_{i^*}yj=ti∗),则令 pi∗\boldsymbol{p}_{i^*}pi∗ 向 xj\boldsymbol{x}jxj 的方向靠拢:
p′=pi∗+η⋅(xj−pi∗) \boldsymbol{p}' = \boldsymbol{p}{i^*} + \eta \cdot (\boldsymbol{x}j - \boldsymbol{p}{i^*}) p′=pi∗+η⋅(xj−pi∗)此时 p′\boldsymbol{p}'p′ 与 xj\boldsymbol{x}_jxj 之间的距离为:
∣∣p′−xj∣∣2=(1−η)⋅∣∣pi∗−xj∣∣2 ||\boldsymbol{p}' - \boldsymbol{x}_j||2 = (1 - \eta) \cdot ||\boldsymbol{p}{i^*} - \boldsymbol{x}_j||_2 ∣∣p′−xj∣∣2=(1−η)⋅∣∣pi∗−xj∣∣2令学习率 η∈(0,1)\eta \in (0, 1)η∈(0,1),则更新后的原型向量更接近 xj\boldsymbol{x}_jxj。
-
若类别不一致 (yj≠ti∗y_j \neq t_{i^*}yj=ti∗),则令 pi∗\boldsymbol{p}_{i^*}pi∗ 远离 xj\boldsymbol{x}jxj:
p′=pi∗−η⋅(xj−pi∗) \boldsymbol{p}' = \boldsymbol{p}{i^*} - \eta \cdot (\boldsymbol{x}j - \boldsymbol{p}{i^*}) p′=pi∗−η⋅(xj−pi∗)此时更新后的原型向量与 xj\boldsymbol{x}jxj 之间的距离将增大为 (1+η)⋅∣∣pi∗−xj∣∣2(1 + \eta) \cdot ||\boldsymbol{p}{i^*} - \boldsymbol{x}_j||_2(1+η)⋅∣∣pi∗−xj∣∣2。
几何解释(Voronoi 剖分):
在学得一组原型向量 {p1,p2,...,pq}\{\boldsymbol{p}_1, \boldsymbol{p}_2, \dots, \boldsymbol{p}_q\}{p1,p2,...,pq} 后,对于任意样本 x\boldsymbol{x}x,它将被划入与其距离最近的原型向量所代表的簇中。这实际上定义了与每个原型向量相关的一个区域 RiR_iRi:
Ri={x∈X∣∣∣x−pi∣∣2⩽∣∣x−pi′∣∣2,i′≠i} R_i = \{ \boldsymbol{x} \in \mathcal{X} \mid ||\boldsymbol{x} - \boldsymbol{p}_i||2 \leqslant ||\boldsymbol{x} - \boldsymbol{p}{i'}||_2, i' \neq i \} Ri={x∈X∣∣∣x−pi∣∣2⩽∣∣x−pi′∣∣2,i′=i}
由此形成了对样本空间 X\mathcal{X}X 的簇划分 {R1,R2,...,Rq}\{R_1, R_2, \dots, R_q\}{R1,R2,...,Rq},该划分通常称为"Voronoi 剖分" (Voronoi tessellation)。
4.3 高斯混合聚类
与 kkk 均值、LVQ 用原型向量来刻画聚类结构不同,高斯混合 (Mixture-of-Gaussian) 聚类采用概率模型来表达聚类原型。
4.3.1 多元高斯分布回顾
我们先回顾一下多元高斯分布的定义。对 nnn 维样本空间 X\mathcal{X}X 中的随机向量 x\boldsymbol{x}x,若 x\boldsymbol{x}x 服从高斯分布,其概率密度函数为:
p(x)=1(2π)n2∣Σ∣12e−12(x−μ)TΣ−1(x−μ) p(\boldsymbol{x}) = \frac{1}{(2\pi)^{\frac{n}{2}} |\boldsymbol{\Sigma}|^{\frac{1}{2}}} e^{-\frac{1}{2}(\boldsymbol{x} - \boldsymbol{\mu})^\text{T} \boldsymbol{\Sigma}^{-1} (\boldsymbol{x} - \boldsymbol{\mu})} p(x)=(2π)2n∣Σ∣211e−21(x−μ)TΣ−1(x−μ)
其中 μ\boldsymbol{\mu}μ 是 nnn 维均值向量,Σ\boldsymbol{\Sigma}Σ 是 n×nn \times nn×n 的协方差矩阵。由上式可以看出,高斯分布完全由均值向量 μ\boldsymbol{\mu}μ 和协方差矩阵 Σ\boldsymbol{\Sigma}Σ 这两个参数确定。为了明确显示高斯分布与相应参数的依赖关系,我们将概率密度函数记为 p(x∣μ,Σ)p(\boldsymbol{x} \mid \boldsymbol{\mu}, \boldsymbol{\Sigma})p(x∣μ,Σ)。
4.3.2 高斯混合分布
我们可以定义高斯混合分布:
pM(x)=∑i=1kαi⋅p(x∣μi,Σi) p_{\mathcal{M}}(\boldsymbol{x}) = \sum_{i=1}^k \alpha_i \cdot p(\boldsymbol{x} \mid \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) pM(x)=i=1∑kαi⋅p(x∣μi,Σi)
该分布共由 kkk 个混合成分组成,每个混合成分对应一个高斯分布。其中 μi\boldsymbol{\mu}_iμi 与 Σi\boldsymbol{\Sigma}iΣi 是第 iii 个高斯混合成分的参数,而 αi>0\alpha_i > 0αi>0 为相应的"混合系数" (mixture coefficient),且满足 ∑i=1kαi=1\sum{i=1}^k \alpha_i = 1∑i=1kαi=1。
4.3.3 聚类过程与后验概率
假设样本的生成过程由高斯混合分布给出:首先,根据 α1,α2,...,αk\alpha_1, \alpha_2, \dots, \alpha_kα1,α2,...,αk 定义的先验分布选择高斯混合成分,其中 αi\alpha_iαi 为选择第 iii 个混合成分的概率;然后,根据被选择的混合成分的概率密度函数进行采样,从而生成相应的样本。
若训练集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm} 由上述过程生成,令随机变量 zj∈{1,2,...,k}z_j \in \{1, 2, \dots, k\}zj∈{1,2,...,k} 表示生成样本 xj\boldsymbol{x}_jxj 的高斯混合成分,其取值未知。显然,zjz_jzj 的先验概率 P(zj=i)P(z_j = i)P(zj=i) 对应于 αi(i=1,2,...,k)\alpha_i (i=1, 2, \dots, k)αi(i=1,2,...,k)。根据贝叶斯定理,zjz_jzj 的后验分布对应于:
pM(zj=i∣xj)=P(zj=i)⋅p(xj∣zj=i)pM(xj)=αi⋅p(xj∣μi,Σi)∑l=1kαl⋅p(xj∣μl,Σl) \begin{aligned} p_{\mathcal{M}}(z_j = i \mid \boldsymbol{x}_j) &= \frac{P(z_j = i) \cdot p(\boldsymbol{x}j \mid z_j = i)}{p{\mathcal{M}}(\boldsymbol{x}_j)} \\ &= \frac{\alpha_i \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_i, \boldsymbol{\Sigma}i)}{\sum{l=1}^k \alpha_l \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_l, \boldsymbol{\Sigma}_l)} \end{aligned} pM(zj=i∣xj)=pM(xj)P(zj=i)⋅p(xj∣zj=i)=∑l=1kαl⋅p(xj∣μl,Σl)αi⋅p(xj∣μi,Σi)
换言之,pM(zj=i∣xj)p_{\mathcal{M}}(z_j = i \mid \boldsymbol{x}_j)pM(zj=i∣xj) 给出了样本 xj\boldsymbol{x}jxj 由第 iii 个高斯混合成分生成的后验概率。为方便叙述,将其简记为 γji (i=1,2,...,k)\gamma{ji} \ (i=1, 2, \dots, k)γji (i=1,2,...,k)。
当高斯混合分布已知时,高斯混合聚类将把样本集 DDD 划分为 kkk 个簇 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck},每个样本 xj\boldsymbol{x}_jxj 的簇标记 λj\lambda_jλj 如下确定:
λj=arg maxi∈{1,2,...,k}γji \lambda_j = \operatorname*{arg\,max}{i \in \{1, 2, \dots, k\}} \gamma{ji} λj=i∈{1,2,...,k}argmaxγji
由此可见,从原型聚类的角度来看,高斯混合聚类是采用概率模型(高斯分布)对原型进行刻画,簇划分则由原型对应后验概率确定。
4.3.4 参数求解 (EM 算法)
对于模型参数 {(αi,μi,Σi)∣1⩽i⩽k}\{(\alpha_i, \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) \mid 1 \leqslant i \leqslant k\}{(αi,μi,Σi)∣1⩽i⩽k} 如何求解呢?显然,给定样本集 DDD,可采用极大似然估计,即最大化(对数)似然:
LL(D)=ln(∏j=1mpM(xj))=∑j=1mln(∑i=1kαi⋅p(xj∣μi,Σi)) \begin{aligned} LL(D) &= \ln \left( \prod_{j=1}^m p_{\mathcal{M}}(\boldsymbol{x}j) \right) \\ &= \sum{j=1}^m \ln \left( \sum_{i=1}^k \alpha_i \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) \right) \end{aligned} LL(D)=ln(j=1∏mpM(xj))=j=1∑mln(i=1∑kαi⋅p(xj∣μi,Σi))
常采用 EM 算法 进行迭代优化求解。下面做一个简单的推导。
若参数 {(αi,μi,Σi)∣1⩽i⩽k}\{(\alpha_i, \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) \mid 1 \leqslant i \leqslant k\}{(αi,μi,Σi)∣1⩽i⩽k} 能使 LL(D)LL(D)LL(D) 最大化,则由 ∂LL(D)∂μi=0\frac{\partial LL(D)}{\partial \boldsymbol{\mu}_i} = 0∂μi∂LL(D)=0 有:
∑j=1mαi⋅p(xj∣μi,Σi)∑l=1kαl⋅p(xj∣μl,Σl)(xj−μi)=0 \sum_{j=1}^m \frac{\alpha_i \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_i, \boldsymbol{\Sigma}i)}{\sum{l=1}^k \alpha_l \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_l, \boldsymbol{\Sigma}_l)} (\boldsymbol{x}_j - \boldsymbol{\mu}_i) = 0 j=1∑m∑l=1kαl⋅p(xj∣μl,Σl)αi⋅p(xj∣μi,Σi)(xj−μi)=0
由后验概率公式以及 γji=pM(zj=i∣xj)\gamma_{ji} = p_{\mathcal{M}}(z_j = i \mid \boldsymbol{x}_j)γji=pM(zj=i∣xj),有:
μi=∑j=1mγjixj∑j=1mγji \boldsymbol{\mu}i = \frac{\sum{j=1}^m \gamma_{ji} \boldsymbol{x}j}{\sum{j=1}^m \gamma_{ji}} μi=∑j=1mγji∑j=1mγjixj
即各混合成分的均值可通过样本加权平均来估计,样本权重是每个样本属于该成分的后验概率。
类似的,由 ∂LL(D)∂Σi=0\frac{\partial LL(D)}{\partial \boldsymbol{\Sigma}_i} = 0∂Σi∂LL(D)=0 可得:
Σi=∑j=1mγji(xj−μi)(xj−μi)T∑j=1mγji \boldsymbol{\Sigma}i = \frac{\sum{j=1}^m \gamma_{ji} (\boldsymbol{x}_j - \boldsymbol{\mu}_i)(\boldsymbol{x}j - \boldsymbol{\mu}i)^\text{T}}{\sum{j=1}^m \gamma{ji}} Σi=∑j=1mγji∑j=1mγji(xj−μi)(xj−μi)T
对于混合系数 αi\alpha_iαi,除了要最大化 LL(D)LL(D)LL(D),还需满足 αi⩾0,∑i=1kαi=1\alpha_i \geqslant 0, \sum_{i=1}^k \alpha_i = 1αi⩾0,∑i=1kαi=1。考虑 LL(D)LL(D)LL(D) 的拉格朗日形式:
LL(D)+λ(∑i=1kαi−1) LL(D) + \lambda \left( \sum_{i=1}^k \alpha_i - 1 \right) LL(D)+λ(i=1∑kαi−1)
其中 λ\lambdaλ 为拉格朗日乘子。由上式对 αi\alpha_iαi 的导数为 0,有:
∑j=1mp(xj∣μi,Σi)∑l=1kαl⋅p(xj∣μl,Σl)+λ=0 \sum_{j=1}^m \frac{p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_i, \boldsymbol{\Sigma}i)}{\sum{l=1}^k \alpha_l \cdot p(\boldsymbol{x}_j \mid \boldsymbol{\mu}_l, \boldsymbol{\Sigma}_l)} + \lambda = 0 j=1∑m∑l=1kαl⋅p(xj∣μl,Σl)p(xj∣μi,Σi)+λ=0
两边同乘以 αi\alpha_iαi,对所有样本求和可知 λ=−m\lambda = -mλ=−m,有:
αi=1m∑j=1mγji \alpha_i = \frac{1}{m} \sum_{j=1}^m \gamma_{ji} αi=m1j=1∑mγji
即每个高斯成分的混合系数由样本属于该成分的平均后验概率确定。
算法流程:
由上述推导即可获得高斯混合模型的 EM 算法:在每步迭代中,先根据当前参数来计算每个样本属于每个高斯成分的后验概率 γji\gamma_{ji}γji (E步 ),再根据公式更新模型参数 {(αi,μi,Σi)∣1⩽i⩽k}\{(\alpha_i, \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) \mid 1 \leqslant i \leqslant k\}{(αi,μi,Σi)∣1⩽i⩽k} (M步)。
输入 :样本集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm};高斯混合成分个数 kkk。
过程:
- 初始化高斯混合分布的模型参数 {(αi,μi,Σi)∣1⩽i⩽k}\{(\alpha_i, \boldsymbol{\mu}_i, \boldsymbol{\Sigma}_i) \mid 1 \leqslant i \leqslant k\}{(αi,μi,Σi)∣1⩽i⩽k}
- repeat
- for j=1,2,...,mj = 1, 2, \dots, mj=1,2,...,m do
- 根据公式计算 xj\boldsymbol{x}jxj 由各混合成分生成的后验概率 γji\gamma{ji}γji
- end for
- for i=1,2,...,ki = 1, 2, \dots, ki=1,2,...,k do
- 计算新均值向量:μi′=∑j=1mγjixj∑j=1mγji\boldsymbol{\mu}'i = \frac{\sum{j=1}^m \gamma_{ji}\boldsymbol{x}j}{\sum{j=1}^m \gamma_{ji}}μi′=∑j=1mγji∑j=1mγjixj
- 计算新协方差矩阵:Σi′=∑j=1mγji(xj−μi′)(xj−μi′)T∑j=1mγji\boldsymbol{\Sigma}'i = \frac{\sum{j=1}^m \gamma_{ji}(\boldsymbol{x}_j - \boldsymbol{\mu}'_i)(\boldsymbol{x}j - \boldsymbol{\mu}'i)^\text{T}}{\sum{j=1}^m \gamma{ji}}Σi′=∑j=1mγji∑j=1mγji(xj−μi′)(xj−μi′)T
- 计算新混合系数:αi′=∑j=1mγjim\alpha'i = \frac{\sum{j=1}^m \gamma_{ji}}{m}αi′=m∑j=1mγji
- end for
- 将模型参数更新为 {(αi′,μi′,Σi′)∣1⩽i⩽k}\{(\alpha'_i, \boldsymbol{\mu}'_i, \boldsymbol{\Sigma}'_i) \mid 1 \leqslant i \leqslant k\}{(αi′,μi′,Σi′)∣1⩽i⩽k}
- until 满足停止条件(例如达到最大迭代轮数或似然函数增长很少)
- Ci=∅ (1⩽i⩽k)C_i = \varnothing \ (1 \leqslant i \leqslant k)Ci=∅ (1⩽i⩽k)
- for j=1,2,...,mj = 1, 2, \dots, mj=1,2,...,m do
- 确定 xj\boldsymbol{x}jxj 的簇标记 λj=arg maxi∈{1,2,...,k}γji\lambda_j = \operatorname*{arg\,max}{i \in \{1, 2, \dots, k\}} \gamma_{ji}λj=argmaxi∈{1,2,...,k}γji
- 将 xj\boldsymbol{x}jxj 划入相应的簇:Cλj=Cλj∪{xj}C{\lambda_j} = C_{\lambda_j} \cup \{\boldsymbol{x}_j\}Cλj=Cλj∪{xj}
- end for
输出 :簇划分 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck}
python
# --- 代码示例:高斯混合模型 (GMM) ---
from sklearn.mixture import GaussianMixture
from matplotlib.patches import Ellipse
def draw_ellipse(position, covariance, ax=None, **kwargs):
"""绘制高斯分布的概率椭圆"""
ax = ax or plt.gca()
if covariance.shape == (2, 2):
U, s, Vt = np.linalg.svd(covariance)
angle = np.degrees(np.arctan2(U[1, 0], U[0, 0]))
width, height = 2 * np.sqrt(s)
else:
angle = 0
width, height = 2 * np.sqrt(covariance)
for nsig in range(1, 4):
ax.add_patch(Ellipse(position, nsig * width, nsig * height,
angle, **kwargs))
# 训练 GMM 模型
gmm = GaussianMixture(n_components=4, random_state=42)
gmm.fit(X)
labels = gmm.predict(X)
plt.figure(figsize=(8, 5))
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis', zorder=2, alpha=0.6)
plt.title("Gaussian Mixture Clustering (with Probability Ellipses)")
# 绘制高斯分布的轮廓
w_factor = 0.2 / gmm.weights_.max()
for pos, covar, w in zip(gmm.means_, gmm.covariances_, gmm.weights_):
draw_ellipse(pos, covar, alpha=w * w_factor, color='red')
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()
5. 密度聚类
密度聚类亦称"基于密度的聚类" (density-based clustering),此类算法假设聚类结构能通过样本分布的紧密程度确定。通常情况下,密度聚类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终的聚类结果。
DBSCAN 是一种著名的密度聚类算法,它基于一组"邻域" (neighborhood) 参数 (ϵ,MinPts)(\epsilon, MinPts)(ϵ,MinPts) 来刻画样本分布的紧密程度。
5.1 基本概念
给定数据集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm},定义下面这几个概念:
- ϵ\epsilonϵ-邻域 :对 xj∈D\boldsymbol{x}_j \in Dxj∈D,其 ϵ\epsilonϵ-邻域包含样本集 DDD 中与 xj\boldsymbol{x}jxj 的距离不大于 ϵ\epsilonϵ 的样本,即 Nϵ(xj)={xi∈D∣dist(xi,xj)⩽ϵ}N{\epsilon}(\boldsymbol{x}_j) = \{ \boldsymbol{x}_i \in D \mid \text{dist}(\boldsymbol{x}_i, \boldsymbol{x}_j) \leqslant \epsilon \}Nϵ(xj)={xi∈D∣dist(xi,xj)⩽ϵ};
- 核心对象 (core object) :若 xj\boldsymbol{x}jxj 的 ϵ\epsilonϵ-邻域至少包含 MinPtsMinPtsMinPts 个样本,即 ∣Nϵ(xj)∣⩾MinPts|N{\epsilon}(\boldsymbol{x}_j)| \geqslant MinPts∣Nϵ(xj)∣⩾MinPts,则 xj\boldsymbol{x}_jxj 是一个核心对象;
- 密度直达 (directly density-reachable) :若 xj\boldsymbol{x}_jxj 位于 xi\boldsymbol{x}_ixi 的 ϵ\epsilonϵ-邻域中,且 xi\boldsymbol{x}_ixi 是核心对象,则称 xj\boldsymbol{x}_jxj 由 xi\boldsymbol{x}_ixi 密度直达;
- 密度可达 (density-reachable) :对 xi\boldsymbol{x}_ixi 与 xj\boldsymbol{x}_jxj,若存在样本序列 p1,p2,...,pn\boldsymbol{p}_1, \boldsymbol{p}_2, \dots, \boldsymbol{p}_np1,p2,...,pn,其中 p1=xi,pn=xj\boldsymbol{p}_1 = \boldsymbol{x}_i, \boldsymbol{p}_n = \boldsymbol{x}jp1=xi,pn=xj 且 pi+1\boldsymbol{p}{i+1}pi+1 由 pi\boldsymbol{p}_ipi 密度直达,则称 xj\boldsymbol{x}_jxj 由 xi\boldsymbol{x}_ixi 密度可达;
- 密度相连 (density-connected) :对 xi\boldsymbol{x}_ixi 与 xj\boldsymbol{x}_jxj,若存在 xk\boldsymbol{x}_kxk 使得 xi\boldsymbol{x}_ixi 与 xj\boldsymbol{x}_jxj 均由 xk\boldsymbol{x}_kxk 密度可达,则称 xi\boldsymbol{x}_ixi 与 xj\boldsymbol{x}_jxj 密度相连。
5.2 簇的定义
基于这些概念,DBSCAN 将"簇"定义为:由密度可达关系导出的最大的密度相连样本集合。形式化地,给定邻域参数 (ϵ,MinPts)(\epsilon, MinPts)(ϵ,MinPts),簇 C⊆DC \subseteq DC⊆D 是满足以下性质的非空样本子集:
- 连接性 (connectivity) :
xi∈C,xj∈C⇒xi 与 xj 密度相连 \boldsymbol{x}_i \in C, \boldsymbol{x}_j \in C \Rightarrow \boldsymbol{x}_i \text{ 与 } \boldsymbol{x}_j \text{ 密度相连} xi∈C,xj∈C⇒xi 与 xj 密度相连 - 最大性 (maximality) :
xi∈C,xj 由 xi 密度可达 ⇒xj∈C \boldsymbol{x}_i \in C, \boldsymbol{x}_j \text{ 由 } \boldsymbol{x}_i \text{ 密度可达 } \Rightarrow \boldsymbol{x}_j \in C xi∈C,xj 由 xi 密度可达 ⇒xj∈C
5.3 DBSCAN 算法流程
实际上,若 x\boldsymbol{x}x 为核心对象,由 x\boldsymbol{x}x 密度可达的所有样本组成的集合记为 X={x′∈D∣x′ 由 x 密度可达}X = \{\boldsymbol{x}' \in D \mid \boldsymbol{x}' \text{ 由 } \boldsymbol{x} \text{ 密度可达}\}X={x′∈D∣x′ 由 x 密度可达},则不难证明 XXX 即为满足连接性与最大性的簇。
于是,DBSCAN 算法先任选数据集中的一个核心对象为"种子" (seed),再由此出发确定相应的聚类簇。
输入 :样本集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm};邻域参数 (ϵ,MinPts)(\epsilon, MinPts)(ϵ,MinPts)。
过程:
- 初始化核心对象集合:Ω=∅\Omega = \varnothingΩ=∅
- for j=1,2,...,mj = 1, 2, \dots, mj=1,2,...,m do
- 确定样本 xj\boldsymbol{x}jxj 的 ϵ\epsilonϵ-邻域 Nϵ(xj)N{\epsilon}(\boldsymbol{x}_j)Nϵ(xj)
- if ∣Nϵ(xj)∣⩾MinPts|N_{\epsilon}(\boldsymbol{x}_j)| \geqslant MinPts∣Nϵ(xj)∣⩾MinPts then
- 将样本 xj\boldsymbol{x}_jxj 加入核心对象集合:Ω=Ω∪{xj}\Omega = \Omega \cup \{\boldsymbol{x}_j\}Ω=Ω∪{xj}
- end if
- end for
- 初始化聚类簇数:k=0k = 0k=0
- 初始化未访问样本集合:Γ=D\Gamma = DΓ=D
- while Ω≠∅\Omega \neq \varnothingΩ=∅ do
- 记录当前未访问样本集合:Γold=Γ\Gamma_{\text{old}} = \GammaΓold=Γ
- 随机选取一个核心对象 o∈Ω\boldsymbol{o} \in \Omegao∈Ω,初始化队列 Q=⟨o⟩Q = \langle \boldsymbol{o} \rangleQ=⟨o⟩
- Γ=Γ∖{o}\Gamma = \Gamma \setminus \{\boldsymbol{o}\}Γ=Γ∖{o}
- while Q≠∅Q \neq \varnothingQ=∅ do
- 取出队列 QQQ 中的首个样本 q\boldsymbol{q}q
- if ∣Nϵ(q)∣⩾MinPts|N_{\epsilon}(\boldsymbol{q})| \geqslant MinPts∣Nϵ(q)∣⩾MinPts then
- 令 Δ=Nϵ(q)∩Γ\Delta = N_{\epsilon}(\boldsymbol{q}) \cap \GammaΔ=Nϵ(q)∩Γ
- 将 Δ\DeltaΔ 中的样本加入队列 QQQ
- Γ=Γ∖Δ\Gamma = \Gamma \setminus \DeltaΓ=Γ∖Δ
- end if
- end while
- k=k+1k = k + 1k=k+1,生成聚类簇 Ck=Γold∖ΓC_k = \Gamma_{\text{old}} \setminus \GammaCk=Γold∖Γ
- Ω=Ω∖Ck\Omega = \Omega \setminus C_kΩ=Ω∖Ck
- end while
输出 :簇划分 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck}
python
# --- 代码示例:DBSCAN vs K-Means ---
from sklearn.cluster import DBSCAN
# 生成非凸形状的数据(月亮形状)
X_moons, _ = make_moons(n_samples=300, noise=0.05, random_state=42)
# 1. 使用 K-Means (效果通常不佳)
km_moons = KMeans(n_clusters=2, random_state=42).fit_predict(X_moons)
# 2. 使用 DBSCAN (基于密度)
# eps: 邻域半径, min_samples: MinPts
dbscan = DBSCAN(eps=0.3, min_samples=5).fit_predict(X_moons)
# 绘图对比
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
axes[0].scatter(X_moons[:, 0], X_moons[:, 1], c=km_moons, cmap='viridis', s=50)
axes[0].set_title("K-Means on Moons (Failed)")
axes[1].scatter(X_moons[:, 0], X_moons[:, 1], c=dbscan, cmap='viridis', s=50)
axes[1].set_title("DBSCAN on Moons (Success)")
plt.show()
6. 层次聚类
层次聚类 (hierarchical clustering) 试图在不同层次对数据集进行划分,从而形成树形的聚类结构。数据集的划分可采用"自底向上"的聚合策略,也可采用"自顶向下"的分拆策略。
AGNES (AGglomerative NESting) 是一种采用自底向上聚合策略的层次聚类算法。它先将数据集中的每个样本看作一个初始聚类簇,然后在算法运行的每一步中找出距离最近的两个聚类簇进行合并,该过程不断重复,直至达到预设的聚类簇个数。
6.1 簇间距离计算
这里的关键是如何计算聚类簇之间的距离。实际上,每个簇是一个样本集合,因此,只需采用关于集合的某种距离即可。例如,给定聚类簇 CiC_iCi 与 CjC_jCj,可通过下面的式子来计算距离:
-
最小距离 (Single Linkage) :
dmin(Ci,Cj)=minx∈Ci,z∈Cjdist(x,z) d_{\min}(C_i, C_j) = \min_{\boldsymbol{x} \in C_i, \boldsymbol{z} \in C_j} \text{dist}(\boldsymbol{x}, \boldsymbol{z}) dmin(Ci,Cj)=x∈Ci,z∈Cjmindist(x,z) -
最大距离 (Complete Linkage) :
dmax(Ci,Cj)=maxx∈Ci,z∈Cjdist(x,z) d_{\max}(C_i, C_j) = \max_{\boldsymbol{x} \in C_i, \boldsymbol{z} \in C_j} \text{dist}(\boldsymbol{x}, \boldsymbol{z}) dmax(Ci,Cj)=x∈Ci,z∈Cjmaxdist(x,z) -
平均距离 (Average Linkage) :
davg(Ci,Cj)=1∣Ci∣∣Cj∣∑x∈Ci∑z∈Cjdist(x,z) d_{\text{avg}}(C_i, C_j) = \frac{1}{|C_i||C_j|} \sum_{\boldsymbol{x} \in C_i} \sum_{\boldsymbol{z} \in C_j} \text{dist}(\boldsymbol{x}, \boldsymbol{z}) davg(Ci,Cj)=∣Ci∣∣Cj∣1x∈Ci∑z∈Cj∑dist(x,z)
显然,最小距离由两个簇的最近样本决定,最大距离由两个簇的最远样本决定,而平均距离则由两个簇的所有样本共同决定。当聚类簇距离由 dmin,dmaxd_{\min}, d_{\max}dmin,dmax 或 davgd_{\text{avg}}davg 计算时,AGNES 算法被相应地称为"单链接"、"全链接"或"均链接"算法。
6.2 AGNES 算法流程
输入 :样本集 D={x1,x2,...,xm}D = \{\boldsymbol{x}_1, \boldsymbol{x}_2, \dots, \boldsymbol{x}_m\}D={x1,x2,...,xm};聚类簇距离度量函数 ddd;聚类簇数 kkk。
过程:
- for j=1,2,...,mj = 1, 2, \dots, mj=1,2,...,m do
- Cj={xj}C_j = \{\boldsymbol{x}_j\}Cj={xj}
- end for
- for i=1,2,...,mi = 1, 2, \dots, mi=1,2,...,m do
- for j=1,2,...,mj = 1, 2, \dots, mj=1,2,...,m do
- M(i,j)=d(Ci,Cj)M(i, j) = d(C_i, C_j)M(i,j)=d(Ci,Cj)
- M(j,i)=M(i,j)M(j, i) = M(i, j)M(j,i)=M(i,j)
- end for
- end for
- 设置当前聚类簇个数:q=mq = mq=m
- while q>kq > kq>k do
- 找出距离最近的两个聚类簇 Ci∗C_{i^*}Ci∗ 和 Cj∗C_{j^*}Cj∗
- 合并 Ci∗C_{i^*}Ci∗ 和 Cj∗C_{j^*}Cj∗:Ci∗=Ci∗∪Cj∗C_{i^*} = C_{i^*} \cup C_{j^*}Ci∗=Ci∗∪Cj∗
- for j=j∗+1,j∗+2,...,qj = j^* + 1, j^* + 2, \dots, qj=j∗+1,j∗+2,...,q do
- 将聚类簇 CjC_jCj 重编号为 Cj−1C_{j-1}Cj−1
- end for
- 删除距离矩阵 MMM 的第 j∗j^*j∗ 行与第 j∗j^*j∗ 列
- for j=1,2,...,q−1j = 1, 2, \dots, q-1j=1,2,...,q−1 do
- M(i∗,j)=d(Ci∗,Cj)M(i^*, j) = d(C_{i^*}, C_j)M(i∗,j)=d(Ci∗,Cj)
- M(j,i∗)=M(i∗,j)M(j, i^*) = M(i^*, j)M(j,i∗)=M(i∗,j)
- end for
- q=q−1q = q - 1q=q−1
- end while
输出 :簇划分 C={C1,C2,...,Ck}\mathcal{C} = \{C_1, C_2, \dots, C_k\}C={C1,C2,...,Ck}
python
# --- 代码示例:层次聚类与树状图 ---
from scipy.cluster.hierarchy import dendrogram, linkage
# 为了展示清晰,只随机选取50个样本进行层次聚类
np.random.seed(42)
indices = np.random.choice(len(X), 50, replace=False)
X_subset = X[indices]
# 使用 Ward 方法计算链接矩阵 (Linkage Matrix)
# Ward 方法试图最小化簇内的方差
linked = linkage(X_subset, 'ward')
plt.figure(figsize=(10, 6))
# 绘制树状图
dendrogram(linked,
orientation='top',
distance_sort='descending',
show_leaf_counts=True)
plt.title("Hierarchical Clustering Dendrogram (AGNES)")
plt.xlabel("Sample Index")
plt.ylabel("Distance (Ward)")
plt.show()