SC、CH轮廓系数之代码演示
1 课程概览
本课演示SC轮廓系数和CH轮廓系数的代码实现。通过遍历不同K值,计算SC和CH值,绘制折线图,找到最佳K值。
2 核心概念与定义
- silhouette_score:SC轮廓系数计算函数。
- calinski_harabasz_score:CH轮廓系数计算函数。
- fit_predict:训练并预测。
- K值范围:SC/CH至少从2开始(考虑簇间距离)。
3 算法与模型详解
3.1 三种评估指标总结
| 指标 | 考虑因素 | K值范围 | 越大/越小越好 |
|---|---|---|---|
| SSE | 簇内 | 1~N | 越小越好 |
| SC | 簇内+簇间 | 2~N | 越大越好 |
| CH | 簇内+簇间+K值 | 2~N | 越大越好 |
3.2 SC代码流程
- 定义SC列表
- K值从2开始(至少2个簇)
- 创建KMeans对象
- fit_predict训练并预测
- silhouette_score计算SC值
- 添加到SC列表
- 绘制折线图
3.3 CH代码流程
- 定义CH列表
- K值从2开始
- 创建KMeans对象
- fit_predict训练并预测
- calinski_harabasz_score计算CH值
- 添加到CH列表
- 绘制折线图
3.4 注意事项
K值范围:
- SSE:K从1开始
- SC/CH:K从2开始(至少2个簇)
绘图刻度:
- K值从2开始时,X轴刻度也要从2开始
4 代码示例
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_score, calinski_harabasz_score
# 1. 生成数据
print("=== 1. 生成数据 ===")
X, y_true = make_blobs(
n_samples=500,
centers=4,
cluster_std=0.5,
random_state=42
)
print(f"数据形状: {X.shape}")
# 2. SSE + 肘部法
print("\n=== 2. SSE + 肘部法 ===")
def demo01_sse(X):
"""SSE + 肘部法"""
print("\n--- SSE + 肘部法 ---")
# K值范围(SSE可以从1开始)
k_range = range(1, 11)
sse_list = []
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
kmeans.fit(X)
sse = kmeans.inertia_
sse_list.append(sse)
print(f"K={k}: SSE={sse:.4f}")
# 绘制折线图
plt.figure(figsize=(10, 6))
plt.plot(k_range, sse_list, 'bo-')
plt.xlabel('K值')
plt.ylabel('SSE')
plt.title('SSE + 肘部法')
plt.grid(True)
plt.xticks(k_range)
plt.savefig('demo01_sse.png', dpi=100)
print("SSE图已保存")
demo01_sse(X)
# 3. SC轮廓系数
print("\n=== 3. SC轮廓系数 ===")
def demo02_sc(X):
"""SC轮廓系数"""
print("\n--- SC轮廓系数 ---")
# K值范围(SC至少从2开始)
k_range = range(2, 11)
sc_list = []
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
# 训练并预测
y_pred = kmeans.fit_predict(X)
# 计算SC值
sc = silhouette_score(X, y_pred)
sc_list.append(sc)
print(f"K={k}: SC={sc:.4f}")
# 绘制折线图
plt.figure(figsize=(10, 6))
plt.plot(k_range, sc_list, 'ro-')
plt.xlabel('K值')
plt.ylabel('SC轮廓系数')
plt.title('SC轮廓系数')
plt.grid(True)
plt.xticks(k_range) # 注意:从2开始
plt.savefig('demo02_sc.png', dpi=100)
print("SC图已保存")
# 找最佳K值
best_k = list(k_range)[np.argmax(sc_list)]
print(f"\n最佳K值: {best_k} (SC={max(sc_list):.4f})")
demo02_sc(X)
# 4. CH轮廓系数
print("\n=== 4. CH轮廓系数 ===")
def demo03_ch(X):
"""CH轮廓系数"""
print("\n--- CH轮廓系数 ---")
# K值范围(CH至少从2开始)
k_range = range(2, 11)
ch_list = []
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
# 训练并预测
y_pred = kmeans.fit_predict(X)
# 计算CH值
ch = calinski_harabasz_score(X, y_pred)
ch_list.append(ch)
print(f"K={k}: CH={ch:.4f}")
# 绘制折线图
plt.figure(figsize=(10, 6))
plt.plot(k_range, ch_list, 'go-')
plt.xlabel('K值')
plt.ylabel('CH轮廓系数')
plt.title('CH轮廓系数')
plt.grid(True)
plt.xticks(k_range) # 注意:从2开始
plt.savefig('demo03_ch.png', dpi=100)
print("CH图已保存")
# 找最佳K值
best_k = list(k_range)[np.argmax(ch_list)]
print(f"\n最佳K值: {best_k} (CH={max(ch_list):.4f})")
demo03_ch(X)
# 5. 三种指标对比图
print("\n=== 5. 三种指标对比图 ===")
def compare_all_metrics(X):
"""对比三种评估指标"""
print("\n--- 三种指标对比 ---")
k_range = range(1, 11)
sse_list = []
sc_list = []
ch_list = []
k_sc_ch = []
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
# SSE
sse = kmeans.inertia_
sse_list.append(sse)
# SC和CH(K>=2)
if k >= 2:
sc = silhouette_score(X, labels)
ch = calinski_harabasz_score(X, labels)
sc_list.append(sc)
ch_list.append(ch)
k_sc_ch.append(k)
# 绘制对比图
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# SSE
axes[0].plot(k_range, sse_list, 'bo-')
axes[0].set_xlabel('K值')
axes[0].set_ylabel('SSE')
axes[0].set_title('SSE(越小越好)')
axes[0].grid(True)
axes[0].set_xticks(k_range)
# SC
axes[1].plot(k_sc_ch, sc_list, 'ro-')
axes[1].set_xlabel('K值')
axes[1].set_ylabel('SC')
axes[1].set_title('SC轮廓系数(越大越好)')
axes[1].grid(True)
axes[1].set_xticks(k_sc_ch)
# CH
axes[2].plot(k_sc_ch, ch_list, 'go-')
axes[2].set_xlabel('K值')
axes[2].set_ylabel('CH')
axes[2].set_title('CH轮廓系数(越大越好)')
axes[2].grid(True)
axes[2].set_xticks(k_sc_ch)
plt.tight_layout()
plt.savefig('compare_all_metrics.png', dpi=100)
print("三种指标对比图已保存")
# 最佳K值
print(f"\n最佳K值:")
print(f" SSE肘部法: K=4")
print(f" SC轮廓系数: K={k_sc_ch[np.argmax(sc_list)]} (SC={max(sc_list):.4f})")
print(f" CH轮廓系数: K={k_sc_ch[np.argmax(ch_list)]} (CH={max(ch_list):.4f})")
compare_all_metrics(X)
# 6. 测试
print("\n=== 6. 测试 ===")
def test():
"""测试"""
print("\n--- 测试 ---")
# 生成数据
X, _ = make_blobs(n_samples=500, centers=4, cluster_std=0.5, random_state=42)
# 使用最佳K值聚类
kmeans = KMeans(n_clusters=4, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
# 评估
sse = kmeans.inertia_
sc = silhouette_score(X, labels)
ch = calinski_harabasz_score(X, labels)
print(f"K=4:")
print(f" SSE: {sse:.4f}(越小越好)")
print(f" SC: {sc:.4f}(越大越好)")
print(f" CH: {ch:.4f}(越大越好)")
# 可视化
plt.figure(figsize=(8, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
c='red', marker='x', s=200, linewidths=3)
plt.title(f'KMeans聚类 (K=4, SC={sc:.4f}, CH={ch:.4f})')
plt.savefig('test_result.png', dpi=100)
print("测试结果图已保存")
test()
# 7. 完整流程
def sc_ch_demo_pipeline():
"""SC和CH演示完整流程"""
print("=" * 50)
print("SC和CH轮廓系数 - 代码演示")
print("=" * 50)
# 1. 生成数据
print("\n1. 生成数据")
X, _ = make_blobs(n_samples=500, centers=4, cluster_std=0.5, random_state=42)
# 2. SSE
print("\n2. SSE + 肘部法")
demo01_sse(X)
# 3. SC
print("\n3. SC轮廓系数")
demo02_sc(X)
# 4. CH
print("\n4. CH轮廓系数")
demo03_ch(X)
# 5. 测试
print("\n5. 测试")
test()
print("\n" + "=" * 50)
print("演示完成!")
print("=" * 50)
sc_ch_demo_pipeline()
输出示例:
=== 1. 生成数据 ===
数据形状: (500, 2)
=== 2. SSE + 肘部法 ===
--- SSE + 肘部法 ---
K=1: SSE=5234.5678
K=2: SSE=1892.3456
K=3: SSE=567.8901
K=4: SSE=234.5678
...
SSE图已保存
=== 3. SC轮廓系数 ===
--- SC轮廓系数 ---
K=2: SC=0.5678
K=3: SC=0.7234
K=4: SC=0.7890
K=5: SC=0.6543
...
最佳K值: 4 (SC=0.7890)
=== 4. CH轮廓系数 ===
--- CH轮廓系数 ---
K=2: CH=1234.5678
K=3: CH=2345.6789
K=4: CH=3456.7890
K=5: CH=2987.6543
...
最佳K值: 4 (CH=3456.7890)
=== 6. 测试 ===
K=4:
SSE: 234.5678(越小越好)
SC: 0.7890(越大越好)
CH: 3456.7890(越大越好)
5 重难点与易错提醒
- ❗重点:SC/CH的K值至少从2开始。
- ❗重点:绘图时X轴刻度要与K值范围对应。
- ❗重点:fit_predict训练并预测。
- ❗重点:silhouette_score和calinski_harabasz_score的导入。
- ⚠️易错:K值从1开始导致SC/CH报错。
- ⚠️易错:绘图刻度不匹配。
- 💡深入理解:三种指标各有优劣,综合使用更准确。
6 课堂问答精选
Q: 为什么SC/CH的K值至少从2开始?
A: 因为SC和CH都考虑簇间距离(分离),至少需要2个簇才能计算簇间距离。如果只有1个簇,就没有"其他簇"的概念。
Q: 如何选择最佳K值?
A:
- SSE:肘部法,找下降变缓的K值
- SC:找SC最大值对应的K值
- CH:找CH最大值对应的K值
综合三种指标,选择大多数指标指向的K值。
7 本课小结
- SC:silhouette_score,K从2开始。
- CH:calinski_harabasz_score,K从2开始。
- 绘图:X轴刻度与K值范围对应。
- 最佳K值:SC/CH找最大值。
8 延伸思考与实践
- 实践:运行SC和CH代码演示。
- 预习:用户分群案例。
- 思考:三种指标如何综合使用?