提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
DAY 17 训练
- 聚类算法
-
- 聚类评估指标介绍
-
- 1. 轮廓系数 (Silhouette Score)
- 2. CH 指数 (Calinski-Harabasz Index)
- 3. DB 指数 (Davies-Bouldin Index)
- 1. KMeans 聚类
-
- 算法原理
- 确定簇数的方法:肘部法
- KMeans 算法的优缺点
-
- 优点
- 缺点
- 2.DBSCAN
- 3.层次聚类
聚类算法
聚类算法是一种无监督学习方法,通过将相似的数据样本自动分组到不同的簇(cluster)中,使得同一簇内的样本相似度高而不同簇间的样本差异大。常见的聚类算法包括K-means(基于距离划分)、DBSCAN(基于密度划分)和层次聚类(基于树状结构划分),广泛应用于客户分群、异常检测、图像分割等领域。
聚类评估指标介绍
以下是三种常用的聚类效果评估指标,分别用于衡量聚类的质量和簇的分离与紧凑程度:
1. 轮廓系数 (Silhouette Score)
- 定义:轮廓系数衡量每个样本与其所属簇的紧密程度以及与最近其他簇的分离程度。
- 取值范围:[-1, 1]
- 轮廓系数越接近 1,表示样本与其所属簇内其他样本很近,与其他簇很远,聚类效果越好。
- 轮廓系数越接近 -1,表示样本与其所属簇内样本较远,与其他簇较近,聚类效果越差(可能被错误分类)。
- 轮廓系数接近 0,表示样本在簇边界附近,聚类效果无明显好坏。
- 使用建议:选择轮廓系数最高的
k
值作为最佳簇数量。
2. CH 指数 (Calinski-Harabasz Index)
- 定义:CH 指数是簇间分散度与簇内分散度之比,用于评估簇的分离度和紧凑度。
- 取值范围:[0, +∞)
- CH 指数越大,表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
- 没有固定的上限,值越大越好。
- 使用建议:选择 CH 指数最高的
k
值作为最佳簇数量。
3. DB 指数 (Davies-Bouldin Index)
- 定义:DB 指数衡量簇间距离与簇内分散度的比值,用于评估簇的分离度和紧凑度。
- 取值范围:[0, +∞)
- DB 指数越小,表示簇间分离度越高,簇内紧凑度越高,聚类效果越好。
- 没有固定的上限,值越小越好。
- 使用建议:选择 DB 指数最低的
k
值作为最佳簇数量。
1. KMeans 聚类
算法原理
KMeans 是一种基于距离的聚类算法,需要预先指定聚类个数,即 k
。其核心步骤如下:
- 随机选择
k
个样本点作为初始质心(簇中心)。 - 计算每个样本点到各个质心的距离,将样本点分配到距离最近的质心所在的簇。
- 更新每个簇的质心为该簇内所有样本点的均值。
- 重复步骤 2 和 3,直到质心不再变化或达到最大迭代次数为止。
确定簇数的方法:肘部法
- 肘部法(Elbow Method) 是一种常用的确定
k
值的方法。 - 原理:通过计算不同
k
值下的簇内平方和(Within-Cluster Sum of Squares, WCSS),绘制k
与 WCSS 的关系图。 - 选择标准:在图中找到“肘部”点,即 WCSS 下降速率明显减缓的
k
值,通常认为是最佳簇数。这是因为增加k
值带来的收益(WCSS 减少)在该点后变得不显著。
KMeans 算法的优缺点
优点
- 简单高效:算法实现简单,计算速度快,适合处理大规模数据集。
- 适用性强:对球形或紧凑的簇效果较好,适用于特征空间中簇分布较为均匀的数据。
- 易于解释:聚类结果直观,簇中心具有明确的物理意义。
缺点
- 需预先指定
k
值:对簇数量k
的选择敏感,不合适的k
会导致聚类效果较差。 - 对初始质心敏感:初始质心的随机选择可能导致结果不稳定或陷入局部最优(可通过 KMeans++ 初始化方法缓解)。
- 对噪声和异常值敏感:异常值可能会显著影响质心的位置,导致聚类结果失真。
- 不适合非球形簇:对非线性可分或形状复杂的簇效果较差,无法处理簇密度不均的情况。
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns
scaler = StandardScaler() #聚类前需要标准化
X_scaled = scaler.fit_transform(X)
k_range = range(2, 11)
inertia_values = [] # 存储每个k值对应的inertia值
silhouette_scores = [] # 存储每个k值对应的轮廓系数
ch_scores = [] # 存储每个k值对应的Calinski-Harabasz指数
db_scores = [] # 存储每个k值对应的Davies-Bouldin指数
for k in k_range: # 遍历k值
kmeans = KMeans(n_clusters=k, random_state=42) # 创建KMeans对象
kmeans.fit(X) # 训练模型
inertia_values.append(kmeans.inertia_) # 惯性(肘部法则)
silhouette = silhouette_score(X_scaled, kmeans.labels_)
silhouette_scores.append(silhouette) # 轮廓系数
ch = calinski_harabasz_score(X_scaled, kmeans.labels_)
ch_scores.append(ch) # Calinski-Harabasz指数
db = davies_bouldin_score(X_scaled, kmeans.labels_)
db_scores.append(db) # Davies-Bouldin指数
print(f"k={k}, 惯性: {kmeans.inertia_:.2f}, 轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")
plt.figure(figsize=(15, 10)) # 设置画布大小
# 绘制惯性曲线
plt.subplot(2, 2, 1) # 绘制轮廓系数曲线
plt.plot(k_range, inertia_values, marker='o', label='Inertia') # 绘制惯性曲线
plt.title("手肘法确定最优k值,惯性越小越好")
plt.xlabel("k")
plt.ylabel("Inertia")
plt.grid(True) # 显示网格
# 绘制轮廓系数曲线
plt.subplot(2, 2, 2) # 绘制轮廓系数曲线
plt.plot(k_range, silhouette_scores, marker='o', label='Silhouette Score') # 绘制轮廓系数曲线
plt.title("轮廓系数法确定最优k值,轮廓系数越大越好")
plt.xlabel("k")
plt.ylabel("Silhouette Score")
plt.grid(True) # 显示网格
# 绘制Calinski-Harabasz指数曲线
plt.subplot(2, 2, 3) # 绘制Calinski-Harabasz指数曲线
plt.plot(k_range, ch_scores, marker='o', label='Calinski-Harabasz Index') # 绘制Calinski-Harabasz指数曲线
plt.title("Calinski-Harabasz指数法确定最优k值,CH指数越大越好")
plt.xlabel("k")
plt.ylabel("Calinski-Harabasz Index")
plt.grid(True)
# 绘制Davies-Bouldin指数曲线
plt.subplot(2, 2, 4)
plt.plot(k_range, db_scores, marker='o', label='Davies-Bouldin Index') # 绘制Davies-Bouldin指数曲线
plt.title("Davies-Bouldin指数法确定最优k值,DB指数越小越好")
plt.xlabel("k")
plt.ylabel("Davies-Bouldin Index")
plt.grid(True)
# 提示用户选择 k 值
selected_k = 6
# 使用选择的 k 值进行 KMeans 聚类
kmeans = KMeans(n_clusters=selected_k, random_state=42)
kmeans_labels = kmeans.fit_predict(X_scaled)
X['KMeans_Cluster'] = kmeans_labels
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# KMeans 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=kmeans_labels, palette='viridis')
plt.title(f'KMeans Clustering with k={selected_k} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()
# 打印 KMeans 聚类标签的前几行
print(f"KMeans Cluster labels (k={selected_k}) added to X:")
print(X[['KMeans_Cluster']].value_counts())
- 先对数据标准化
- 对每个k值进行训练评估并用图像可视化
- 使用最佳k值训练,打印结果
2.DBSCAN
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,它通过寻找高密度区域来识别簇,并将稀疏分布的点标记为噪声。其核心思想是基于两个参数:eps
(邻域半径)和min_samples
(最小样本数)。算法从一个未访问的点开始,查找其eps
范围内的邻域点,如果邻域内的点数大于或等于min_samples
,则形成一个簇,并递归地扩展该簇;否则,该点被标记为噪声。通过这种方式,DBSCAN能够发现任意形状的簇,并有效处理噪声点。
这段代码实现了DBSCAN聚类的参数优化和结果可视化。它通过遍历不同的eps
和min_samples
组合,对数据进行聚类,并计算轮廓系数、Calinski-Harabasz指数和Davies-Bouldin指数等评估指标来选择最优参数。最终,使用选定的参数进行聚类,并通过PCA降维可视化聚类结果,同时统计簇的分布情况。
from sklearn.cluster import DBSCAN
eps_range = np.arange(0.3, 0.8, 0.1) # 测试 eps 从 0.3 到 0.7
min_samples_range = range(3, 8) # 测试 min_samples 从 3 到 7
results = []
for eps in eps_range:
for min_samples in min_samples_range:
dbscan = DBSCAN(eps=eps, min_samples=min_samples)
dbscan_labels = dbscan.fit_predict(X_scaled)
# 计算簇的数量(排除噪声点 -1)
n_clusters = len(np.unique(dbscan_labels)) - (1 if -1 in dbscan_labels else 0)
# 计算噪声点数量
n_noise = list(dbscan_labels).count(-1)
# 只有当簇数量大于 1 且有有效簇时才计算评估指标
if n_clusters > 1:
# 排除噪声点后计算评估指标
mask = dbscan_labels != -1
if mask.sum() > 0: # 确保有非噪声点
silhouette = silhouette_score(X_scaled[mask], dbscan_labels[mask])
ch = calinski_harabasz_score(X_scaled[mask], dbscan_labels[mask])
db = davies_bouldin_score(X_scaled[mask], dbscan_labels[mask])
results.append({
'eps': eps,
'min_samples': min_samples,
'n_clusters': n_clusters,
'n_noise': n_noise,
'silhouette': silhouette,
'ch_score': ch,
'db_score': db
})
print(f"eps={eps:.1f}, min_samples={min_samples}, 簇数: {n_clusters}, 噪声点: {n_noise}, "
f"轮廓系数: {silhouette:.3f}, CH 指数: {ch:.2f}, DB 指数: {db:.3f}")
else:
print(f"eps={eps:.1f}, min_samples={min_samples}, 簇数: {n_clusters}, 噪声点: {n_noise}, 无法计算评估指标")
# 将结果转为 DataFrame 以便可视化和选择参数
results_df = pd.DataFrame(results)
# 绘制评估指标图,增加点论文中的工作量
plt.figure(figsize=(15, 10))
# 轮廓系数图
plt.subplot(2, 2, 1)
for min_samples in min_samples_range:
subset = results_df[results_df['min_samples'] == min_samples] #
plt.plot(subset['eps'], subset['silhouette'], marker='o', label=f'min_samples={min_samples}')
plt.title('轮廓系数确定最优参数(越大越好)')
plt.xlabel('eps')
plt.ylabel('轮廓系数')
plt.legend()
plt.grid(True)
# CH 指数图
plt.subplot(2, 2, 2)
for min_samples in min_samples_range:
subset = results_df[results_df['min_samples'] == min_samples]
plt.plot(subset['eps'], subset['ch_score'], marker='o', label=f'min_samples={min_samples}')
plt.title('Calinski-Harabasz 指数确定最优参数(越大越好)')
plt.xlabel('eps')
plt.ylabel('CH 指数')
plt.legend()
plt.grid(True)
# DB 指数图
plt.subplot(2, 2, 3)
for min_samples in min_samples_range:
subset = results_df[results_df['min_samples'] == min_samples]
plt.plot(subset['eps'], subset['db_score'], marker='o', label=f'min_samples={min_samples}')
plt.title('Davies-Bouldin 指数确定最优参数(越小越好)')
plt.xlabel('eps')
plt.ylabel('DB 指数')
plt.legend()
plt.grid(True)
# 簇数量图
plt.subplot(2, 2, 4)
for min_samples in min_samples_range:
subset = results_df[results_df['min_samples'] == min_samples]
plt.plot(subset['eps'], subset['n_clusters'], marker='o', label=f'min_samples={min_samples}')
plt.title('簇数量变化')
plt.xlabel('eps')
plt.ylabel('簇数量')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# 选择 eps 和 min_samples 值(根据图表选择最佳参数)
selected_eps = 0.6 # 根据图表调整
selected_min_samples = 6 # 根据图表调整
# 使用选择的参数进行 DBSCAN 聚类
dbscan = DBSCAN(eps=selected_eps, min_samples=selected_min_samples)
dbscan_labels = dbscan.fit_predict(X_scaled)
X['DBSCAN_Cluster'] = dbscan_labels
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# DBSCAN 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=dbscan_labels, palette='viridis')
plt.title(f'DBSCAN Clustering with eps={selected_eps}, min_samples={selected_min_samples} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()
# 打印 DBSCAN 聚类标签的分布
print(f"DBSCAN Cluster labels (eps={selected_eps}, min_samples={selected_min_samples}) added to X:")
print(X[['DBSCAN_Cluster']].value_counts())
本实验中效果差
3.层次聚类
Agglomerative Clustering 是一种自底向上的层次聚类方法,初始时每个样本是一个簇,然后逐步合并最相似的簇,直到达到指定的簇数量或满足停止条件。由于它需要指定簇数量(类似于 KMeans),我将通过测试不同的簇数量 n_clusters 来评估聚类效果,并使用轮廓系数(Silhouette Score)、CH 指数(Calinski-Harabasz Index)和 DB 指数(Davies-Bouldin Index)作为评估指标。
import numpy as np
import pandas as pd
from sklearn.cluster import AgglomerativeClustering
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score, calinski_harabasz_score, davies_bouldin_score
import matplotlib.pyplot as plt
import seaborn as sns
sacler = StandardScaler()
X_scaled = scaler.fit_transform(X)
n_clusters_range = range(2,11)
silhouette_scores = []
ch_scores = []
db_scores = []
for n_clusters in n_clusters_range:
agglo = AgglomerativeClustering(n_clusters=n_clusters,linkage='ward')
agglo_labels = agglo.fit_predict(X_scaled)
silhouette = silhouette_score(X_scaled,agglo_labels)
ch = calinski_harabasz_score(X_scaled,agglo_labels)
db = davies_bouldin_score(X_scaled,agglo_labels)
silhouette_scores.append(silhouette)
ch_scores.append(ch)
db_scores.append(db)
print(f"For n_clusters={n_clusters}, silhouette score={silhouette:.4f}, CH score={ch:.4f}, DB score={db:.4f}")
plt.figure(figsize=(15, 5))
# 轮廓系数图
plt.subplot(1, 3, 1)
plt.plot(n_clusters_range, silhouette_scores, marker='o')
plt.title('轮廓系数确定最优簇数(越大越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('轮廓系数')
plt.grid(True)
# CH 指数图
plt.subplot(1, 3, 2)
plt.plot(n_clusters_range, ch_scores, marker='o')
plt.title('Calinski-Harabasz 指数确定最优簇数(越大越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('CH 指数')
plt.grid(True)
# DB 指数图
plt.subplot(1, 3, 3)
plt.plot(n_clusters_range, db_scores, marker='o')
plt.title('Davies-Bouldin 指数确定最优簇数(越小越好)')
plt.xlabel('簇数量 (n_clusters)')
plt.ylabel('DB 指数')
plt.grid(True)
plt.tight_layout()
plt.show()
# 提示用户选择 n_clusters 值(这里可以根据图表选择最佳簇数)
selected_n_clusters = 10 # 示例值,根据图表调整
# 使用选择的簇数进行 Agglomerative Clustering 聚类
agglo = AgglomerativeClustering(n_clusters=selected_n_clusters, linkage='ward')
agglo_labels = agglo.fit_predict(X_scaled)
X['Agglo_Cluster'] = agglo_labels
# 使用 PCA 降维到 2D 进行可视化
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# Agglomerative Clustering 聚类结果可视化
plt.figure(figsize=(6, 5))
sns.scatterplot(x=X_pca[:, 0], y=X_pca[:, 1], hue=agglo_labels, palette='viridis')
plt.title(f'Agglomerative Clustering with n_clusters={selected_n_clusters} (PCA Visualization)')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.show()
# 打印 Agglomerative Clustering 聚类标签的分布
print(f"Agglomerative Cluster labels (n_clusters={selected_n_clusters}) added to X:")
print(X[['Agglo_Cluster']].value_counts())