3.3 聚类任务 3. Scikit-learn 实践应用领域:3.3 聚类任务详解 3.3.1 聚类任务概述 聚类 (Clustering) 是一种无监督学习 (Unsupervised Learning) 技术,旨在将数据集中的样本划分为若干个“簇”(cluster),使得同一簇内的样本彼此相似,而不同簇的样本彼此相异。在没有预先标记的类别信息的情况下,聚类算法通过数据自身的内在性质,发现数据分布的规律,并将相似的数据点归为一类。 聚类任务的核心目标: 簇内相似性 (Intra-cluster similarity): 同一簇内的样本尽可能相似。 簇间差异性 (Inter-cluster dissimilarity): 不同簇的样本尽可能相异。
3. Scikit-learn 实践应用领域:3.3 聚类任务详解
3.3.1 聚类任务概述
聚类 (Clustering) 是一种无监督学习 (Unsupervised Learning) 技术,旨在将数据集中的样本划分为若干个“簇”(cluster),使得同一簇内的样本彼此相似,而不同簇的样本彼此相异。在没有预先标记的类别信息的情况下,聚类算法通过数据自身的内在性质,发现数据分布的规律,并将相似的数据点归为一类。
聚类任务的核心目标:
簇内相似性 (Intra-cluster similarity): 同一簇内的样本尽可能相似。
簇间差异性 (Inter-cluster dissimilarity): 不同簇的样本尽可能相异。
聚类任务的应用场景广泛,例如:
客户细分 (Customer Segmentation): 将客户根据购买行为、 demographics 等信息划分为不同的群体,以便进行精准营销和个性化服务。
图像分割 (Image Segmentation): 将图像中的像素根据颜色、纹理等特征划分为不同的区域,用于目标识别、图像编辑等。
文档聚类 (Document Clustering): 将文档根据主题内容划分为不同的类别,用于信息检索、主题发现等。
异常检测 (Anomaly Detection): 将偏离正常数据模式的数据点识别为异常值,用于欺诈检测、设备故障预警等。
生物信息学 (Bioinformatics): 基因表达数据聚类、蛋白质结构聚类等,用于发现生物学规律。
社交网络分析 (Social Network Analysis): 社区发现,将社交网络中的用户划分为不同的社群。
Scikit-learn 在聚类任务中的优势:
Scikit-learn 提供了丰富的聚类算法实现,并且接口统一、易于使用。其优势包括:
多种聚类算法: 涵盖了常用的 K-Means、层次聚类、DBSCAN、谱聚类、高斯混合模型等多种算法,满足不同场景的需求。
简洁的 API 设计: 使用 fit(), predict(), fit_predict() 等统一的接口,方便模型训练和预测。
完善的评估指标: 提供了轮廓系数、Calinski-Harabasz 指数、Davies-Bouldin 指数等多种聚类评估指标,帮助评估聚类效果。
与其他 Scikit-learn 模块的良好集成: 可以方便地与其他模块(如数据预处理、降维、模型选择等)结合使用,构建完整的机器学习流程。
3.3.2 常用聚类算法及代码实践 (Scikit-learn)
以下将详细介绍 Scikit-learn 中几种常用的聚类算法,并提供代码示例和详细解释。我们将使用 sklearn.cluster 模块中的聚类算法,并结合 matplotlib 和 seaborn 进行可视化展示。
3.3.2.1 K-Means 聚类
算法原理:
K-Means 算法是一种经典的基于距离的聚类算法。它的目标是将数据集划分为 k 个簇,使得每个数据点都属于距离其最近的均值(簇中心)对应的簇。算法步骤如下:
初始化簇中心: 随机选择 k 个数据点作为初始簇中心。
分配簇: 对于每个数据点,计算其与 k 个簇中心的距离,将其分配到距离最近的簇。
更新簇中心: 对于每个簇,计算所有属于该簇的数据点的均值,将均值作为新的簇中心。
迭代: 重复步骤 2 和 3,直到簇中心不再发生明显变化或达到最大迭代次数。
代码实践 (Python + Scikit-learn):
import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sklearn.datasets import make_blobs # 1. 生成模拟数据 n_samples = 300 n_clusters = 3 random_state = 42 X, y = make_blobs(n_samples=n_samples, centers=n_clusters, random_state=random_state) # 2. 创建 KMeans 聚类器 kmeans = KMeans(n_clusters=n_clusters, init='k-means++', max_iter=300, n_init=10, random_state=random_state) # 3. 训练模型并进行聚类预测 y_kmeans = kmeans.fit_predict(X) # 4. 可视化聚类结果 plt.figure(figsize=(8, 6)) plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis') # 5. 绘制簇中心 centers = kmeans.cluster_centers_ 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()
代码详解:
导入库: 导入 numpy 用于数值计算,matplotlib.pyplot 用于绘图,KMeans 类从 sklearn.cluster 导入,make_blobs 用于生成模拟聚类数据。
生成模拟数据 make_blobs:
n_samples=300: 生成 300 个数据点。
centers=n_clusters=3: 生成 3 个簇。
random_state=42: 设置随机种子,保证结果可复现。
make_blobs 函数返回 X (特征数据) 和 y (真实簇标签,这里仅用于生成数据,K-Means 是无监督学习,实际应用中没有真实标签)。
创建 KMeans 聚类器 KMeans(...):
n_clusters=n_clusters: 指定簇的数量为 3。
init='k-means++': 初始化簇中心的方法。'k-means++' 是一种更智能的初始化方法,可以加速收敛并提高聚类效果,比随机初始化 'random' 更优。
max_iter=300: 最大迭代次数,防止算法运行时间过长。
n_init=10: K-Means 算法会多次运行(这里是 10 次)并选择 inertia (簇内平方和) 最小的结果,以避免陷入局部最优解。
random_state=random_state: 设置随机种子,保证结果可复现。
训练模型并预测 kmeans.fit_predict(X):
fit_predict(X) 方法:先使用数据 X 训练 KMeans 模型 (fit),然后直接对 X 进行聚类预测 (predict),返回每个数据点所属的簇标签 y_kmeans。可视化聚类结果 plt.scatter(...):
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis'): 绘制散点图。
X[:, 0], X[:, 1]: 分别取特征矩阵 X 的第一列和第二列作为 x 和 y 坐标。
c=y_kmeans: 使用聚类标签 y_kmeans 作为颜色,不同簇的点显示不同颜色。
s=50: 点的大小。
cmap='viridis': 颜色映射方案。
绘制簇中心 plt.scatter(centers[:, 0], centers[:, 1], ...):
centers = kmeans.cluster_centers_: 获取 KMeans 模型训练后得到的簇中心坐标。
plt.scatter(...): 绘制簇中心点。
c='red': 簇中心颜色设置为红色。
s=200: 簇中心点的大小。
alpha=0.75: 透明度。
marker='x': 簇中心点的形状设置为叉号。
K-Means 参数详解:
n_clusters: 最重要的参数! 指定要聚类的簇的数量 k。需要根据实际问题和数据特点进行选择,通常需要尝试不同的 k 值并结合评估指标来确定最佳 k 值。
init: 簇中心初始化方法。
'k-means++' (默认): 智能初始化方法,通常效果更好。
'random': 随机初始化。
ndarray: 可以手动指定初始簇中心,形状为 (n_clusters, n_features)。
max_iter: 单次 K-Means 运行的最大迭代次数。
n_init: K-Means 算法运行的次数,最终选择 inertia 最小的结果。
random_state: 随机种子,用于控制随机过程,保证结果可复现。
algorithm: K-Means 算法的实现方式,可选 'auto', 'full', 'elkan'。通常 'auto' 会自动选择最佳算法。
K-Means 优点:
算法简单,易于理解和实现。
计算速度快,对于大规模数据集也相对有效率。
常用且有效。
K-Means 缺点:
需要预先指定簇的数量 k。 k 值的选择对聚类结果影响很大,但实际应用中 k 值往往难以确定。
对初始簇中心敏感。 不同的初始簇中心可能导致不同的聚类结果,容易陷入局部最优解。 n_init 参数可以缓解这个问题。
对异常值敏感。 异常值会影响簇中心的计算,导致聚类结果偏差。
只适用于球形簇结构。 对于非球形、复杂形状的簇结构,K-Means 效果较差。
假设各簇数据密度和方差相似。
3.3.2.2 层次聚类 (Hierarchical Clustering)
算法原理:
层次聚类是一种树状结构的聚类方法,它通过在不同层次对数据集进行划分,形成树状的聚类结构(树状图 Dendrogram)。层次聚类分为两种类型:
凝聚型层次聚类 (Agglomerative Hierarchical Clustering): 自底向上,初始时每个数据点作为一个簇,然后逐步合并最相似的簇,直到满足停止条件(例如簇的数量达到预设值或达到某个相似度阈值)。
分裂型层次聚类 (Divisive Hierarchical Clustering): 自顶向下,初始时所有数据点属于同一个簇,然后逐步分裂簇,直到满足停止条件。
Scikit-learn 中主要实现了凝聚型层次聚类,通过 AgglomerativeClustering 类实现。
代码实践 (Python + Scikit-learn):
import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import AgglomerativeClustering from sklearn.datasets import make_blobs # 1. 生成模拟数据 (同 K-Means 示例) n_samples = 300 n_clusters = 3 random_state = 42 X, y = make_blobs(n_samples=n_samples, centers=n_clusters, random_state=random_state) # 2. 创建 AgglomerativeClustering 聚类器 hierarchical = AgglomerativeClustering(n_clusters=n_clusters, linkage='ward') # 3. 训练模型并进行聚类预测 y_hierarchical = hierarchical.fit_predict(X) # 4. 可视化聚类结果 (同 K-Means 示例) plt.figure(figsize=(8, 6)) plt.scatter(X[:, 0], X[:, 1], c=y_hierarchical, s=50, cmap='viridis') plt.title('Hierarchical Clustering (Agglomerative)') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.show()
代码详解:
代码结构与 K-Means 示例基本相同,主要区别在于聚类器的创建和算法名称。
创建 AgglomerativeClustering 聚类器 AgglomerativeClustering(...):
n_clusters=n_clusters: 指定最终簇的数量为 3。
linkage='ward': 连接准则 (Linkage Criteria)。 用于衡量簇之间距离的方法。
'ward' (默认): Ward 方差最小化,倾向于产生大小相似的簇。
'average': 平均连接,簇间距离定义为两个簇中所有点对之间距离的平均值。
'complete' (最大连接): 完全连接,簇间距离定义为两个簇中点对之间距离的最大值。
'single' (单连接): 单连接,簇间距离定义为两个簇中点对之间距离的最小值,容易产生链状簇。
层次聚类参数详解:
n_clusters: 重要参数! 指定最终簇的数量。
linkage: 连接准则。 决定了簇之间距离的计算方法,影响聚类结果。
affinity: 距离度量方式,默认为 'euclidean' (欧氏距离)。 可选 'euclidean', 'l1', 'l2', 'manhattan', 'cosine', 'precomputed' 等。 当 linkage='ward' 时,affinity 必须为 'euclidean'。
distance_threshold: 距离阈值。 可以替代 n_clusters 使用,当簇之间的距离超过阈值时停止合并,形成簇。 如果设置了 distance_threshold,则 n_clusters 必须为 None。
层次聚类优点:
无需预先指定簇的数量。 可以通过树状图 (Dendrogram) 可视化聚类过程,并根据树状图选择合适的簇的数量。
可以发现簇之间的层次关系。 树状结构可以清晰地展示簇的合并过程和簇之间的嵌套关系。
对簇的形状没有太多假设。 比 K-Means 更适用于非球形簇结构。
层次聚类缺点:
计算复杂度较高。 时间复杂度通常为 O(N^3) 或 O(N^2 log N),对于大规模数据集效率较低。
对噪声和异常值敏感。
结果可能受连接准则影响较大。 需要根据数据特点选择合适的连接准则。
3.3.2.3 DBSCAN 聚类 (Density-Based Spatial Clustering of Applications with Noise)
算法原理:
DBSCAN 是一种基于密度的聚类算法,它将簇定义为密度相连的点的最大集合。DBSCAN 可以发现任意形状的簇,并且能够识别噪声点 (outliers)。
核心概念:
核心点 (Core Point): 如果一个点在其半径 ε (eps) 范围内包含至少 min_samples 个点(包括自身),则该点称为核心点。
边界点 (Border Point): 如果一个点不是核心点,但它在某个核心点的 ε 邻域内,则该点称为边界点。
噪声点 (Noise Point): 既不是核心点也不是边界点的点,被认为是噪声点。
密度直达 (Directly Density-Reachable): 如果点 p 在核心点 q 的 ε 邻域内,则称 p 从 q 密度直达。
密度可达 (Density-Reachable): 对于点 p 和点 q,如果存在一个点链 p1, p2, ..., pn,其中 p1=q, pn=p,且 pi+1 从 pi 密度直达,则称 p 从 q 密度可达。
密度相连 (Density-Connected): 对于点 p 和点 q,如果存在核心点 o,使得 p 和 q 都从 o 密度可达,则称 p 和 q 密度相连。
算法步骤:
标记所有点为未访问。
遍历每个未访问的点 p:
如果 p 已被访问,则跳过。
标记 p 为已访问。
找出 p 的 ε 邻域内的所有点 Neighbors(p)。
如果 Neighbors(p) 的数量少于 min_samples,则标记 p 为噪声点。
如果 Neighbors(p) 的数量大于等于 min_samples,则标记 p 为核心点,并创建一个新的簇 C,将 p 添加到 C。
递归地访问 Neighbors(p) 中的每个未访问的点 p':
标记 p' 为已访问。
找出 p' 的 ε 邻域内的所有点 Neighbors(p')。
如果 Neighbors(p') 的数量大于等于 min_samples,则将 Neighbors(p') 中的点添加到 Neighbors(p)。
如果 p' 不是噪声点 (即 p' 是核心点或边界点),则将 p' 添加到簇 C。
重复步骤 2,直到所有点都被访问。
代码实践 (Python + Scikit-learn):
import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import DBSCAN from sklearn.datasets import make_moons # 1. 生成非球形模拟数据 (月牙形) n_samples = 300 random_state = 42 X, y = make_moons(n_samples=n_samples, noise=0.05, random_state=random_state) # 2. 创建 DBSCAN 聚类器 dbscan = DBSCAN(eps=0.3, min_samples=5) # 3. 训练模型并进行聚类预测 y_dbscan = dbscan.fit_predict(X) # 4. 可视化聚类结果 plt.figure(figsize=(8, 6)) plt.scatter(X[:, 0], X[:, 1], c=y_dbscan, s=50, cmap='viridis') plt.title('DBSCAN Clustering') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.show() # 5. 统计噪声点数量 n_noise = list(y_dbscan).count(-1) # DBSCAN 将噪声点标记为 -1 print(f"Number of noise points: {n_noise}")
代码详解:
导入库: 与之前示例类似,导入必要的库。 make_moons 用于生成月牙形数据,适合展示 DBSCAN 的优势。
生成非球形模拟数据 make_moons:
make_moons(n_samples=n_samples, noise=0.05, random_state=random_state): 生成月牙形数据, noise 参数控制噪声水平。创建 DBSCAN 聚类器 DBSCAN(...):
eps=0.3: 邻域半径 ε (epsilon)。 定义了点的邻域大小。 需要根据数据密度进行调整,ε 值过大可能将多个簇合并,ε 值过小可能将簇分裂或将簇内点标记为噪声。
min_samples=5: 最小样本数 min_samples。 定义了核心点的条件。 min_samples 值越大,簇的密度要求越高,噪声点会增多。
训练模型并预测 dbscan.fit_predict(X): 与之前类似。 DBSCAN 将噪声点标记为 -1。
可视化聚类结果 plt.scatter(...): 与之前类似。
统计噪声点数量: 统计 y_dbscan 中值为 -1 的点的数量,即噪声点数量。
DBSCAN 参数详解:
eps: 重要参数! 邻域半径 ε。 需要根据数据密度调整。 可以尝试不同的 eps 值,并观察聚类结果和噪声点数量的变化。
min_samples: 重要参数! 最小样本数。 也需要根据数据密度调整。 min_samples 值越大,簇的密度要求越高。
metric: 距离度量方式,默认为 'euclidean'。 可选 'euclidean', 'manhattan', 'chebyshev', 'minkowski', 'cosine', 'precomputed' 等。
algorithm: 近邻搜索算法,可选 'auto', 'ball_tree', 'kd_tree', 'brute'。 通常 'auto' 会自动选择最佳算法。
leaf_size: 当 algorithm 为 'ball_tree' 或 'kd_tree' 时,控制树的叶子节点大小,影响搜索效率。
DBSCAN 优点:
无需预先指定簇的数量。 DBSCAN 可以自动发现簇的数量。
可以发现任意形状的簇。 适用于非球形簇结构。
能够识别噪声点。 对噪声数据不敏感。
聚类结果鲁棒性较好。 对参数选择相对不敏感。
DBSCAN 缺点:
对参数 eps 和 min_samples 敏感。 参数选择对聚类结果影响较大,需要根据数据特点进行调整。
对密度不均匀的数据集聚类效果较差。 当簇的密度差异很大时,DBSCAN 可能无法有效聚类。
高维数据中,维度灾难会导致距离计算困难,影响聚类效果。
3.3.3 聚类评估指标 (Scikit-learn)
由于聚类是无监督学习,没有真实的标签信息,因此评估聚类效果相对困难。 常用的聚类评估指标分为两类:
内部评估指标 (Intrinsic Evaluation Metrics): 仅使用聚类结果自身的信息进行评估,例如簇内紧凑度、簇间分离度等。
外部评估指标 (Extrinsic Evaluation Metrics): 需要使用真实的标签信息进行评估,例如当聚类结果需要与已知的类别信息进行比较时。 在实际应用中,真实标签往往是未知的,因此内部评估指标更为常用。
Scikit-learn 中常用的聚类评估指标 (sklearn.metrics 模块):
3.3.3.1 轮廓系数 (Silhouette Coefficient)
原理:
轮廓系数综合考虑了簇的凝聚度和分离度。 对于每个样本点 i,计算:
a(i): 样本 i 到同簇其他样本的平均距离 (簇内不相似度)。
b(i): 样本 i 到其他簇的所有样本的平均距离的最小值 (簇间不相似度)。
轮廓系数 s(i) 定义为:
s(i) = (b(i) - a(i)) / max(a(i), b(i))
轮廓系数的取值范围为 [-1, 1]:
s(i) 接近 1: 样本 i 聚类效果好,簇内相似且簇间分离。
s(i) 接近 0: 样本 i 位于簇的边界上,聚类效果一般。
s(i) 接近 -1: 样本 i 可能被误分到错误的簇中,聚类效果差。
代码实践 (Python + Scikit-learn):
import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score from sklearn.datasets import make_blobs # 1. 生成模拟数据 (同 K-Means 示例) n_samples = 300 n_clusters = 3 random_state = 42 X, y = make_blobs(n_samples=n_samples, centers=n_clusters, random_state=random_state) # 2. 创建 KMeans 聚类器并训练 kmeans = KMeans(n_clusters=n_clusters, random_state=random_state) y_kmeans = kmeans.fit_predict(X) # 3. 计算轮廓系数 silhouette_avg = silhouette_score(X, y_kmeans) print(f"Silhouette Score for K-Means: {silhouette_avg:.4f}") # --- 尝试不同簇数量 --- silhouette_scores = [] for n_clusters in range(2, 6): # 尝试簇数量 2 到 5 kmeans = KMeans(n_clusters=n_clusters, random_state=random_state) y_kmeans = kmeans.fit_predict(X) silhouette_avg = silhouette_score(X, y_kmeans) silhouette_scores.append(silhouette_avg) print(f"Silhouette Score for {n_clusters} clusters: {silhouette_avg:.4f}") # 4. 绘制轮廓系数随簇数量变化的曲线 plt.figure(figsize=(8, 6)) plt.plot(range(2, 6), silhouette_scores, marker='o') plt.xlabel('Number of Clusters') plt.ylabel('Silhouette Score') plt.title('Silhouette Score vs. Number of Clusters') plt.xticks(range(2, 6)) plt.grid(True) plt.show()
代码详解:
导入库: 导入 silhouette_score 函数从 sklearn.metrics 模块。
生成模拟数据、创建 KMeans 聚类器、训练模型: 与之前示例相同。
计算轮廓系数 silhouette_score(X, y_kmeans):
silhouette_score(X, y_kmeans) 函数计算数据集 X 基于聚类标签 y_kmeans 的平均轮廓系数。尝试不同簇数量并计算轮廓系数: 循环尝试不同的簇数量,计算对应的轮廓系数,并将结果存储在 silhouette_scores 列表中。
绘制轮廓系数曲线: 绘制轮廓系数随簇数量变化的折线图,可以帮助选择最佳的簇数量。 通常轮廓系数越高,聚类效果越好。