目录
9.1聚类任务
在"无监督学习"中研究最多、应用最广的是"聚类" .聚类试图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个"簇" .聚类过程仅能自动形成簇结构,簇所对应的概念语义需由使用者来把握和命名。聚类的结果可用包含m个元素的簇标记向量 λ=(λ1;λ2;... ;λm) 表示.
9.2性能度量
聚类性能度量亦称聚类"有效性指标"聚类性能度量大致有两类. 一类是将聚类结果与某个"参考模型" 进行比较,称为"外部指标" ; 另一类是直接考察聚类结果而不利用任何参考模型,称为"内部指标" 。
聚类性能度量外部指标有如下:
Jaccard 系数(计算聚类结果与参考模型之间的交集与并集的比值) :
FM 指数(计算聚类结果与参考模型之间的精确度和召回率的调和平均数) :
Rand 指数(计算聚类结果与参考模型之间的一致决策和不一致决策的比值)
上述性能度量的结果值均在 [0 1] 区间,值越大越好.
聚类性能度量内部指标有如下:
**DB 指数 (**通过衡量各个簇的紧密度和分离度来评估聚类的效果):
Du nn 指数(通过比较簇内的紧密度和簇间的分离度来评估聚类的质量):
DBI 的值越小越好,而 DI 则相反,值越大越好.
9.3距离计算
对函数 dist(. ,.),若它是一个"距离度量" (distance measure) ,则需满足一 些基本性质:
非负性: dist(Xi , Xj) >= 0 ;
同一性: dist(Xi , Xj)=0当且仅当 Xi = Xj ;
对称性: dist(Xi , Xj)) = dist(xj , Xi) ;
直递性: dist(Xi , Xj) <= dist(Xi , Xk) + dist(Xk,X j*)*
最常用的是 "闵可夫斯基距离": ,p>=1
p=2 时,闵可夫斯基距离即欧氏距离:
p=1 时,闵可夫斯基距离即曼哈顿距离:
我们常将属性划分为"连续属性" 和"离散属性" ,前者在定义域上有无穷多个可能的取值,后者在定义域上是有限个取值.
对无序属性可采用 VDM,表示在属性 u上取值为a 的样本数, 表示在第i个样本簇
中在属性u 上取值为a 的样本数 ,k 为样本簇数,则属性u 上两个离散值a,b之间的 VDM 距离为:
当样本空间中不同属性的重要 性不同时 ,可使用" 加权距 离", 以加权闵 可夫 斯基距离为 例:
9.4圆形聚类
算法先对原型进行初始化,然后对原型进行迭代更新求解.采用不同的原型表示、不同的求解方式,将产生不同的算法.
9.4.1k均值算法
给定样本集 "k 均值" 算法针对聚类所得簇划分 最小化平方误差:
步骤如下:
-
选择K值:确定要分成的簇的数量K。
-
初始化中心:随机选择K个点作为初始簇中心(质心)。
-
分配簇:将每个数据点分配到距离其最近的簇中心所属的簇。
-
更新中心:重新计算每个簇的质心,质心是簇中所有点的均值。
-
重复迭代:重复步骤3和步骤4,直到簇中心不再发生显著变化,或者达到预设的迭代次数。
-
停止 :算法结束时,簇的划分稳定,所有点被划分到其最终的簇中。
下面是关于k均值算法的实验过程及分析结果:
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
# 生成样本数据
X, y = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)
# 执行K均值算法
kmeans = KMeans(n_clusters=4)
kmeans.fit(X)
# 获取簇中心和每个点的簇标签
centers = kmeans.cluster_centers_
labels = kmeans.labels_
# 可视化结果
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, cmap='viridis')
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X')
plt.title('K-Means Clustering')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()
分析:
1.数据生成:生成了300个样本数据,划分为4个簇,每个簇的标准差为0.60。
2.K均值算法:对这些数据执行了K均值聚类,指定了4个簇。
3.可视化:结果图展示了数据点按簇进行着色,簇中心以红色"X"标记。
通过运行代码,可以直观地看到数据点如何被聚类到4个不同的簇中,以及每个簇的中心位置。这有助于验证K均值算法在实际数据上的效果。
实验结果:
9.4.2学习向量量化法(LVQ)
LVQ 假设数据样本带有类别标记,学习过程利用样本的这些监督信息来辅助聚类。
步骤如下:
-
初始化:从训练数据中随机选择若干样本点作为初始的原型向量(或质心),这些原型向量的数量通常等于类别数。
-
分配类别:为每个原型向量分配一个类别标签,通常是其所属的训练样本的类别。
-
训练过程:对每个训练样本:
- 更新原型 :
- 如果样本点的真实类别与最近原型的类别相同,则将原型向量向样本点移动(调整原型向量的位置)。
- 如果样本点的真实类别与最近原型的类别不同,则将原型向量远离样本点移动。
- 选择最近原型:找到距离样本点最近的原型向量,并确定其类别标签。
- 计算距离:计算样本点到所有原型向量的距离。
- 更新原型 :
-
重复训练:重复步骤3,直到原型向量的位置稳定,或者达到预设的训练次数。
-
分类 :使用训练好的原型向量对新样本进行分类:计算新样本与所有原型向量的距离,并将样本分配给距离最近的原型的类别
学习向量量化的实验过程及分析结果如下:
python
import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
class LVQ:
def __init__(self, n_prototypes):
self.n_prototypes = n_prototypes
def fit(self, X, y, n_iter=100, lr=0.01):
self.classes = np.unique(y)
self.prototypes = np.zeros((self.n_prototypes, X.shape[1]))
self.labels = np.zeros(self.n_prototypes, dtype=int)
# Initialize prototypes randomly
for i in range(self.n_prototypes):
idx = np.random.choice(len(X))
self.prototypes[i] = X[idx]
self.labels[i] = y[idx]
for _ in range(n_iter):
for i, x in enumerate(X):
dists = np.linalg.norm(x - self.prototypes, axis=1)
closest_idx = np.argmin(dists)
if y[i] == self.labels[closest_idx]:
self.prototypes[closest_idx] += lr * (x - self.prototypes[closest_idx])
else:
self.prototypes[closest_idx] -= lr * (x - self.prototypes[closest_idx])
def predict(self, X):
dists = np.linalg.norm(X[:, np.newaxis] - self.prototypes, axis=2)
closest_idx = np.argmin(dists, axis=1)
return self.labels[closest_idx]
# Load Iris dataset
data = load_iris()
X = data.data
y = data.target
# Standardize the features
scaler = StandardScaler()
X = scaler.fit_transform(X)
# Split into training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# Initialize and train LVQ model
model = LVQ(n_prototypes=10)
model.fit(X_train, y_train)
# Predict and evaluate
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(f'准确率: {accuracy:.2f}')
分析:
这个实现没有依赖于 sklearn_lvq
库,而是手动实现了 LVQ 算法。使用了 numpy
和 scikit-learn
进行数据处理和评估。
- 初始化 LVQ 类 :定义
LVQ
类及其方法。 - 训练模型:初始化原型,更新原型的位置。
- 预测和评估:通过计算距离并预测类别,评估模型性能。
实验结果:
9.4.3高斯混合聚类
高斯混合聚类采用概率模型来表达聚类原型。对n维样本空间X中的随机向量 x,若x服从高斯分布,其概率密度函数为:
步骤如下:
-
初始化参数
-
期望步骤(E-Step)
-
最大化步骤(M-Step)
-
收敛性检查
-
结果解释
下面是关于高斯混合聚类算法的实验过程及分析结果:
python
import numpy as np
from sklearn.mixture import GaussianMixture
import matplotlib.pyplot as plt
# 生成示例数据
np.random.seed(0)
n_samples = 300
C = np.array([[0.7, -0.7], [0.7, 0.7]])
X = np.dot(np.random.randn(n_samples, 2), C)
# 应用高斯混合模型
gmm = GaussianMixture(n_components=2, random_state=0)
gmm.fit(X)
labels = gmm.predict(X)
# 结果可视化
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, s=40, cmap='viridis')
plt.title('GMM Clustering')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
# 绘制高斯分布
ax = plt.gca()
x = np.linspace(X[:, 0].min(), X[:, 0].max(), 100)
y = np.linspace(X[:, 1].min(), X[:, 1].max(), 100)
X_grid, Y_grid = np.meshgrid(x, y)
Z = np.exp(gmm.score_samples(np.c_[X_grid.ravel(), Y_grid.ravel()]))
Z = Z.reshape(X_grid.shape)
contour = ax.contour(X_grid, Y_grid, Z, levels=5, cmap='Greys', alpha=0.5)
plt.colorbar(contour)
plt.show()
# 输出分析结果
print("GMM Converged:", gmm.converged_)
print("Number of Iterations:", gmm.n_iter_)
print("Means of the clusters:\n", gmm.means_)
print("Covariances of the clusters:\n", gmm.covariances_)
分析:
- 聚类可视化:散点图显示了数据点根据模型预测的簇标签进行着色。等高线图显示了高斯分布的轮廓。
- GMM 收敛状态 :通过
gmm.converged_
判断模型是否收敛。 - 迭代次数 :
gmm.n_iter_
表示模型收敛的迭代次数。 - 均值和协方差 :
gmm.means_
和gmm.covariances_
提供了每个簇的均值和协方差矩阵。
这个代码和结果分析展示了高斯混合模型在二维数据上的应用。
实验结果:
9.5密度聚类
密度聚类算法从样本密度的角度来考察样本之间的可连接性,并基于可连接样本不断扩展聚类簇以获得最终的聚类结果.DBSCAN 是一种著名的密度聚类算法,它基于一组"邻域"参数来刻画样本分布的紧密程度.
DBSCAN算法步骤如下:
-
选择参数 :设置两个参数,
eps
(邻域的半径)和min_samples
(一个簇的最小样本数)。 -
遍历数据点 :对于数据集中的每一个点,计算其
eps
邻域内的点数。 -
密度检验:
- 如果邻域内点数大于或等于
min_samples
,将该点标记为核心点,并形成一个新簇。 - 否则,标记为噪声点(如果它在任何核心点的邻域内)。
- 如果邻域内点数大于或等于
-
扩展簇:
- 从核心点开始,递归地将所有密度可达的点加入到同一簇中。
- 对于新加入的点,再检查其邻域,重复此过程直到所有密度可达的点被处理完。
-
重复:对数据集中尚未标记的点重复步骤2至4,直到所有点被处理完。
-
处理噪声:所有未被分配到任何簇的点被标记为噪声。
DBSCAN的优点是能够发现任意形状的簇,并能自动识别噪声点。
下面是关于DBSCAN算法的实验代码及分析结果:
python
import numpy as np
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
# 生成示例数据
X, _ = make_moons(n_samples=300, noise=0.1, random_state=42)
# 应用 DBSCAN 算法
dbscan = DBSCAN(eps=0.3, min_samples=5)
labels = dbscan.fit_predict(X)
# 结果可视化
plt.figure(figsize=(8, 6))
unique_labels = np.unique(labels)
colors = plt.cm.get_cmap('viridis', len(unique_labels))
for label in unique_labels:
mask = (labels == label)
plt.scatter(X[mask, 0], X[mask, 1], c=[colors(label)], label=f'Cluster {label}' if label != -1 else 'Noise', s=50)
plt.title('DBSCAN Clustering')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
# 输出分析结果
print("Cluster labels:", np.unique(labels))
print("Number of clusters found:", len(set(labels)) - (1 if -1 in labels else 0))
print("Number of noise points:", list(labels).count(-1))
分析结果:
-
聚类可视化:
散点图展示了通过 DBSCAN 聚类得到的结果。不同的颜色表示不同的簇,噪声点(未分配到任何簇的点)通常会被标记为单独的颜色。
-
簇标签:
np.unique(labels)
列出了所有簇标签。标签-1
表示噪声点。 -
簇数量:
len(set(labels)) - (1 if -1 in labels else 0)
计算了簇的数量。-1
被排除在外,因为它代表噪声点,不属于任何簇。 -
噪声点数量:
list(labels).count(-1)
计算了噪声点的数量。
实验结果:
9.6层次聚类
层次聚类试图 不同层次对数据集进行划分,从而形成树聚类结构 ,数据集的划分采用"自 底向上 "的聚合策略,也可采 "自顶向下" 分拆策略.
AGNES 是一种采用自底 向上聚合策略的层次聚类算法.它先将数据集中的每个样本看作初始聚类簇,然后在算法运行的每一步中找出离最近的两个聚类簇进行合并,该过程不断重复,直至达到预设的聚类簇个数.
步骤如下:
-
初始化:将每个数据点视为一个单独的簇。
-
计算距离:计算所有簇之间的距离。初始时,距离是数据点之间的距离。
-
合并簇:
- 找到距离最小的两个簇,将它们合并成一个新的簇。
- 更新簇间的距离。具体方法取决于距离度量,例如单链接、全链接或平均链接。
-
重复:重复步骤2和3,直到所有点被合并成一个簇或达到预定的簇数量。
-
停止条件:算法结束时,得到一个树状图(dendrogram),表示不同簇的合并过程。
下面是AGNES算法的实验代码和分析结果:
python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_moons
import scipy.cluster.hierarchy as sch
# 生成示例数据
X, _ = make_moons(n_samples=300, noise=0.1, random_state=42)
# 应用 AGNES 算法(AgglomerativeClustering)
agnes = AgglomerativeClustering(n_clusters=2, linkage='ward')
labels = agnes.fit_predict(X)
# 结果可视化
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=50, edgecolor='k')
plt.title('Agglomerative Clustering (AGNES)')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.show()
# 生成树状图(dendrogram)
plt.figure(figsize=(10, 7))
dendrogram = sch.dendrogram(sch.linkage(X, method='ward'))
plt.title('Dendrogram for AGNES')
plt.xlabel('Sample index')
plt.ylabel('Distance')
plt.show()
# 输出分析结果
print("Cluster labels:", np.unique(labels))
print("Number of clusters found:", len(set(labels)))
分析结果:
-
聚类可视化:
散点图展示了通过 AGNES 聚类得到的结果。不同的颜色表示不同的簇,便于观察数据的分组情况。
-
树状图:
树状图展示了数据点合并的过程。每次合并操作都会在树状图上形成一个连接线,线的高度表示合并时的距离。通过观察树状图,可以确定不同簇的形成过程和距离。
-
簇标签:
np.unique(labels)
列出了所有簇的标签。与散点图中的颜色对应。 -
簇数量:
len(set(labels))
计算了最终得到的簇的数量。这与在 AGNES 中设定的n_clusters
参数一致。
这段代码展示了如何使用 AGNES 进行层次聚类,并通过可视化结果和树状图来分析聚类效果
实验结果: