1502scikit中的k-means

🎯 教学目标与重难点…

【三维目标】

  • 📚 知识目标:掌握 sklearn.cluster.KMeans 的核心参数(n_clustersinitn_initrandom_state);掌握 inertia_ 属性的含义与使用;理解手肘法(Elbow Method)确定最优K值的原理。
  • ⚙️ 能力目标:能够使用 KMeans 对二维数据进行聚类并可视化结果;能够使用手肘法绘制“K值 vs inertia”曲线并确定最佳K值;能够使用 StandardScaler 对数据进行标准化预处理。
  • 💡 素养目标:建立“数据预处理 → 模型训练 → 结果评估”的标准实验流程意识;理解“特征尺度差异”对基于距离的算法的影响。

【重点与难点】

  • 🟢 教学重点KMeans 的代码实现;inertia_ 评估指标的使用;手肘法的原理与代码实现。
  • 🟡 教学难点:理解数据标准化对 k-means 的必要性;理解手肘法中的“肘点”判断(主观性与模糊性)。

📌 一、 课程导入(5 分钟)

回顾旧知:上节课学习了 k-means 的核心概念和手算过程。今天的目标是让计算机替我们完成这些繁琐的迭代计算。

展示“成品”: 课前运行一段 k-means 代码,现场演示:

  1. 生成一组二维数据(散点图)。
  2. 运行 KMeans,K=3,自动聚类。
  3. 将聚类结果可视化(不同颜色标记不同簇 + 质心用特殊符号标出)。

核心疑问

  • 代码只有几行,如何确保结果稳定可靠?
  • K 值应该怎么选?是随便拍脑袋定吗?

本节课路线图

  1. KMeans 基础代码(训练、预测、可视化)
  2. 关键参数详解(n_initrandom_state 的作用)
  3. 手肘法 —— 科学地选择 K 值
  4. 数据标准化 —— 让距离计算更有意义
  • 👨‍🏫 教师活动:在 Jupyter Notebook 中实时演示 KMeans 的完整代码;将 random_state 改为不同值,展示聚类结果的差异。
  • 🧑‍🎓 学生活动:观察聚类结果的可视化图像;思考“为什么同样的数据,不同运行结果会不一样”。

代码演示 + 即时可视化让学生看到“学以致用”的效果。“不同 random_state 导致不同结果”的现象为后续讲解初始化敏感性埋下伏笔。


📖 二、 解决问题过程(一):KMeans 基础代码

1. 最基础的 KMeans 实现

 1import numpy as np
 2import matplotlib.pyplot as plt
 3from sklearn.cluster import KMeans
 4from sklearn.datasets import make_blobs
 5
 6# 生成二维测试数据(方便可视化)
 7X, y_true = make_blobs(n_samples=300, centers=4, 
 8                       cluster_std=0.8, random_state=42)
 9
10# 训练 KMeans 模型(K=4)
11kmeans = KMeans(n_clusters=4, random_state=42)
12kmeans.fit(X)
13
14# 获取聚类结果
15y_pred = kmeans.predict(X)           # 每个样本的簇标签
16centroids = kmeans.cluster_centers_  # 质心坐标
17inertia = kmeans.inertia_            # WCSS 值
18
19print(f"质心坐标:\n{centroids}")
20print(f"WCSS (inertia):{inertia:.2f}")

2. 可视化聚类结果

1plt.figure(figsize=(8, 6))
2plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis', s=50, alpha=0.7)
3plt.scatter(centroids[:, 0], centroids[:, 1], 
4            c='red', marker='X', s=200, linewidths=3, 
5            edgecolors='black', label='质心')
6plt.title('KMeans 聚类结果 (K=4)')
7plt.legend()
8plt.grid(True)
9plt.show()

3. 核心 API 总结

属性/方法 作用
KMeans(n_clusters=K) 创建 KMeans 模型,指定 K 值
.fit(X) 在数据 X 上训练模型
.predict(X) 预测新样本的簇标签
.fit_predict(X) 训练 + 预测(合并调用)
.cluster_centers_ 获取质心坐标(K × 特征数)
.inertia_ 获取 WCSS(簇内平方和)
  • 👨‍🏫 教师活动:逐行运行代码,在每个关键步骤后暂停并解释输出;展示 .cluster_centers_ 与之前手算的“均值”完全对应。
  • 🧑‍🎓 学生活动:在本地复现代码;修改 n_clusters 为 3、5、6,观察图像变化。

将上节课的“质心=均值”概念与 cluster_centers_ 输出对接,让学生看到数学概念在代码中的具体对应,消除“黑盒感”。


💻 三、 解决问题过程(二):关键参数详解

1. 初始化敏感性 —— random_state

问题:k-means 的初始质心是随机选的。不同的初始质心可能导致不同的聚类结果。

演示

 1fig, axes = plt.subplots(1, 3, figsize=(15, 4))
 2for idx, rs in enumerate([0, 42, 99]):
 3    kmeans = KMeans(n_clusters=3, random_state=rs)
 4    kmeans.fit(X)
 5    axes[idx].scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap='viridis', s=30)
 6    axes[idx].scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
 7                      c='red', marker='X', s=200)
 8    axes[idx].set_title(f'random_state={rs}\ninertia={kmeans.inertia_:.1f}')
 9    axes[idx].grid(True)
10plt.tight_layout()
11plt.show()

观察结论:不同 random_state 可能导致不同的质心初始位置,最终得到不同的聚类结果和不同的 inertia 值。

2. 解决方案 —— n_init 参数

  • n_init:指定算法用不同的初始质心运行 多次,返回其中 inertia 最小的结果。
  • 默认值:n_init=10(即运行 10 次取最优)。
1# 运行 50 次取最优
2kmeans = KMeans(n_clusters=3, n_init=50, random_state=42)
3kmeans.fit(X)

3. max_iter —— 最大迭代次数

  • 控制每一轮初始化允许的最大迭代步数。
  • 默认值:max_iter=300
  • 如果数据量很大,可适当增大以保证收敛。

4. 核心参数速查表

参数 默认值 作用 调参建议
n_clusters 8 聚类个数(K值) 使用手肘法确定
init ‘k-means++’ 初始化方法 保持默认(k-means++ 比随机更好)
n_init 10 不同初始值尝试次数 数据大时可增至 20-50
max_iter 300 每轮最大迭代次数 通常够用,可适当增大
random_state None 随机种子 设为固定值以保证结果可复现
  • 👨‍🏫 教师活动:运行三个不同 random_state 的对比代码,展示结果差异;演示 n_init=50 如何找到更优的 inertia。
  • 🧑‍🎓 学生活动:记录三个 random_state 对应的 inertia 值;思考“为什么不同初始质心会导致不同的 inertia”。

通过可视化对比,让学生亲眼看到“随机初始化”带来的问题,再引出 n_init 这个解决方案。这种“问题→解决方案”的教学顺序更能加深记忆。


✍️ 四、 解决问题过程(三):手肘法确定最优 K 值

1. 核心问题:K 值应该选多少?

  • 选太小 → 把不同的群体强行合并(欠拟合)。
  • 选太大 → 把本应为一群的样本打散(过拟合)。

2. 手肘法(Elbow Method)原理

  • 对 K = 1, 2, 3, …, 10,分别训练 KMeans 模型。
  • 记录每个 K 对应的 inertia_(WCSS)。
  • 绘制“K 值 vs inertia”折线图。
  • 观察“肘点”:inertia 下降速度突然变缓的那个 K 值,即为最优 K。

3. 代码实现

 1inertia_list = []
 2K_range = range(1, 11)
 3
 4for k in K_range:
 5    kmeans = KMeans(n_clusters=k, random_state=42)
 6    kmeans.fit(X)
 7    inertia_list.append(kmeans.inertia_)
 8
 9# 绘制手肘图
10plt.figure(figsize=(8, 5))
11plt.plot(K_range, inertia_list, 'bo-', linewidth=2, markersize=8)
12plt.xlabel('K 值(聚类个数)', fontsize=12)
13plt.ylabel('WCSS(簇内平方和)', fontsize=12)
14plt.title('手肘法确定最优 K 值', fontsize=14)
15plt.grid(True, alpha=0.3)
16plt.xticks(K_range)
17plt.show()

4. 分析示例

K 值 inertia 下降幅度
1 8000
2 4500 下降 3500
3 2800 下降 1700
4 1800 下降 1000(肘点)
5 1500 下降 300
6 1300 下降 200

观察:K 从 3→4 时下降幅度仍然较大,从 4→5 起下降明显变缓 → 最优 K=4

  • 👨‍🏫 教师活动:运行手肘法代码,指出图中的“肘点”位置;解释“下降变缓”的物理含义(再增加 K 带来的边际收益递减)。
  • 🧑‍🎓 学生活动:在本地运行手肘法代码;观察自己生成的图中肘点位置是否与真实 K 值一致。

手肘法是实践中最常用的选 K 方法。通过亲手绘制曲线并观察“肘点”,学生获得可复用的工程技能,而非仅停留在理论层面。


📝 五、 课堂小结(5 分钟)

flowchart LR
    root["💻 scikit中的k-means(第二次课)"]

    subgraph C1["📖 核心API"]
        direction TB
        A1["KMeans(n_clusters=K)"]
        A2["fit / predict / fit_predict"]
    end

    subgraph C2["📊 关键属性"]
        direction TB
        B1["cluster_centers_(质心坐标)"]
        B2["inertia_(WCSS值)"]
        B3["labels_(簇标签)"]
    end

    subgraph C3["🔧 关键参数"]
        direction TB
        C1_node["n_init:多次尝试取最优"]
        C2_node["random_state:固定随机种子"]
        C3_node["max_iter:最大迭代次数"]
    end

    subgraph C4["📈 选K方法"]
        direction TB
        D1["手肘法:绘制 K vs inertia"]
        D2["肘点 = 下降变缓处"]
    end

    root --> C1
    root --> C2
    root --> C3
    root --> C4

    style root fill:#e65100,stroke:#bf360c,color:#fff,stroke-width:2px,rx:8px,ry:8px
    style C1 fill:#fff3e0,stroke:#ff9800,stroke-width:1px
    style C2 fill:#e3f2fd,stroke:#1e88e5,stroke-width:1px
    style C3 fill:#e8f5e9,stroke:#43a047,stroke-width:1px
    style C4 fill:#f3e5f5,stroke:#9c27b0,stroke-width:1px

✏️ 随堂检测与互动练习

点击展开:随堂测试题(带解析)

一、 单选题

  1. 在 sklearn 的 KMeans 中,n_init 参数的作用是?
  • A. 指定聚类个数
  • B. 用不同初始质心运行多次,返回最优结果
  • C. 指定最大迭代次数
  • D. 控制算法的并行度
【答案】

【解析】Bn_init 是为了解决 k-means 对初始质心敏感的问题,通过多次尝试选择 inertia 最小的结果。

  1. 手肘法中,“肘点”指的是?
  • A. inertia 值最小的点
  • B. inertia 下降速度突然变缓的点
  • C. inertia 值最大的点
  • D. inertia 曲线上的任意点
【答案】

【解析】B。肘点是曲线从“快速下降”转为“缓慢下降”的转折点,代表增加 K 值的边际收益开始递减。

二、 代码填空题

题目:现有数据 X,请补充代码完成 KMeans 聚类(K=3),并输出质心坐标和 WCSS 值。

1from sklearn.cluster import KMeans
2
3kmeans = KMeans(n_clusters=____, random_state=42)
4kmeans.____(X)
5centroids = kmeans._______________
6wcss = kmeans.________
7print(centroids, wcss)
【答案】
  1. 3
  2. fit
  3. cluster_centers_
  4. inertia_

三、 简答题

题目:如果某数据的特征包括“年龄”(2060岁)和“年收入”(5万100万),直接使用欧氏距离进行 k-means 聚类可能存在什么问题?应该如何解决?

【答案】

年收入的数值范围远大于年龄(5万100万 vs 2060),导致距离计算中年收入占据主导地位,年龄特征几乎不起作用。解决方案:使用 StandardScaler 对数据进行标准化,使所有特征在同一尺度下比较。

📮 六、 课后作业与拓展

📮 课后作业…
  1. 基础作业:使用 make_blobs 生成 500 个样本、5 个簇的数据,训练 KMeans 模型并可视化聚类结果。
  2. 手肘法作业:在 K=1K=15 范围内绘制手肘图,标注出你认为的最优 K 值。
  3. n_init 探究作业:固定 n_init=1,使用不同的 random_state(0-9)训练 10 个模型,记录每个模型的 inertia,观察最大与最小 inertia 的差异。再设置 n_init=50 重复实验,观察差异是否缩小。
  4. 数据标准化作业:构造一个包含两个特征(一个范围 0-1,一个范围 0-10000)的二维数据集,分别对原始数据和标准化后的数据进行 k-means 聚类,对比聚类结果的差异。
  5. 预习作业:阅读 15.3 节“其他聚类方法”,初步了解层次聚类和 DBSCAN 的基本思想。

📋 七、 板书设计

🛠️ 板书设计…
 1💻 scikit中的k-means – 第二次课
 2
 3一、 核心代码模板
 4    from sklearn.cluster import KMeans
 5    model = KMeans(n_clusters=K, random_state=42)
 6    model.fit(X)
 7    labels = model.labels_
 8    centroids = model.cluster_centers_
 9    wcss = model.inertia_
10
11二、 关键参数
12    n_clusters  → K值(最重要)
13    n_init      → 多次尝试取最优(默认10)
14    random_state → 固定随机种子(保证可复现)
15    max_iter    → 最大迭代次数(默认300)
16
17三、 选K方法:手肘法
18    ① 对不同K值训练模型,记录inertia
19    ② 绘制 K vs inertia 曲线
20    ③ 找“肘点”(下降变缓的位置)
21
22四、 数据标准化
23    原因:特征尺度差异影响距离计算
24    方法:from sklearn.preprocessing import StandardScaler

本课用到的单词

英文术语 发音(美式) 中文释义 专业语境解释
Inertia /ɪˈnɜːr.ʃə/ 惯性(WCSS) KMeans 中的目标函数值,即簇内平方和,越小聚类越紧凑。
Elbow Method /ˈel.boʊ ˈmeθ.əd/ 手肘法 通过绘制 K 值与 inertia 曲线寻找肘点来确定最优 K 的方法。
Initialization /ɪˌnɪʃ.əl.aɪˈzeɪ.ʃən/ 初始化 算法开始前给参数赋予初始值的过程,在 KMeans 中指初始质心选择。
Random State /ˈræn.dəm steɪt/ 随机种子 控制随机数生成的参数,固定后每次运行结果一致。
Standardization /ˌstæn.dɚ.daɪˈzeɪ.ʃən/ 标准化 将特征调整为均值为0、方差为1的分布,消除尺度影响。