6-1特征缩放——标准化与归一化
📌 一、 课程导入(5 分钟)
情景模拟:食堂打菜“数据歧视”
假设学校食堂要预测一个学生的“饭量”(目标Y)。我们收集了两个特征:
- 特征A:体重(kg) —— 数值范围 45 ~ 120
- 特征B:吃饭时长(分钟) —— 数值范围 5 ~ 30
如果直接拿这两个数去算“距离”(比如 KNN 找饭量相似的人),体重这一列的数字(45120)明显比吃饭时长(530)大得多。
- 计算距离时,$(体重差)^2$ 动辄几百,$(时长差)^2$ 顶多几百。
- 结果:模型会认为“体重”比“吃饭时长”重要几百倍! 这就叫 “大数吃小数”。
引出问题:但我们都知道,饭量大小可能跟吃饭时长关系更大,体重重的人不一定饭量大(比如在减肥)。模型搞“数字歧视”了,怎么办?
解决方案:我们需要把这两列数据“拉到一个平等的起跑线”上,这就是 特征缩放(Feature Scaling)。
- 👨🏫 教师活动:用 PPT 展示体重和时长的数据表格,提问:“如果让你根据这两个数找人,你会觉得哪个特征影响更大?”引发学生争论。
- 🧑🎓 学生活动:学生凭直觉回答,有的说体重,有的说时长。老师顺势指出模型的“死板”——只看数字大小,从而引出缩放的必要性。
使用职高学生最熟悉的“食堂”和“体重”场景,将抽象的“量纲不一致”转化为直观的数字大小比拼,瞬间点燃好奇心,降低学习门槛。
📖 二、 解决问题过程(一):标准化(Standardization)—— 让数据“正态归零”
1. 核心思想: 把数据变成 “均值为 0,标准差为 1” 的标准正态分布形态。
2. 数学公式(看一眼就行,代码自动算):
\[ z = \frac{x - \text{均值}}{\text{标准差}} \]3. 通俗理解(体检标准分): 假设全班数学平均分 70,标准差 10。你考了 80 分,标准分就是 \( (80-70)/10 = 1.0 \),代表你高于平均线 1 个标准差。标准化就是算你的“全班排名偏差值”。
4. 代码实现(万金油模板):
5. 适用场景(必考重点):
- 当数据本身 有异常值(离群点) 时,标准化比归一化更稳(因为用了标准差)。
- 大多数算法都适用:线性回归、逻辑回归、SVM、PCA、神经网络(只要算法涉及计算距离或梯度下降,标准化往往都有帮助)。
- 👨🏫 教师活动:
- 板书写下公式,直接告诉学生“均值和标准差用代码算,我们只关注代码怎么写”。
- 在 Jupyter 中逐行运行代码,并打印出
mean_和scale_属性,让学生看到内部存储的均值和标准差。
- 🧑🎓 学生活动:
- 跟着老师在笔记本上敲下
StandardScaler的代码。 - 观察输出:为什么两列数据现在都变成了小数点附近?为什么第一列原来是 60、80、100,现在变成了 -1、0、1?
- 跟着老师在笔记本上敲下
公式只做“眼熟”要求,重点放在 API 调用和运行结果观察上。通过“均值归零”的直观现象,让学生理解标准化的本质是“中心化+压缩”。
💻 三、 解决问题过程(二):归一化(MinMaxScaler)—— 让数据“挤到 0 和 1 之间”
1. 核心思想: 把数据按比例压缩,强制放到 [0, 1] 这个区间里。
2. 数学公式(看一眼就行):
\[ x_{norm} = \frac{x - \text{最小值}}{\text{最大值} - \text{最小值}} \]3. 通俗理解(按分数排百分比): 你考了 80 分,全班最低 0 分,最高 100 分,你的归一化分数就是 \( (80-0)/(100-0) = 0.8 \)。代表你处在全班 80% 的位置。
4. 代码实现(对比学习):
5. 适用场景(必考重点):
- 必须用在:K-最近邻(KNN)、K-Means 聚类(因为严重依赖欧氏距离)。
- 当数据没有明显异常值时使用。
- 注意:如果数据里有极大异常值(比如马云乱入月收入),归一化会把其他所有正常数据压成一条线(失去区分度),这时候用标准化更好!
- 👨🏫 教师活动:
- 重点提问:“如果数据里有 1 个 10000,其他都是 10 左右,用归一化会怎样?”(让学生思考异常值的危害)。
- 展示运行结果:同样两组数,标准化后两列的数值分布形态(0附近波动)和归一化后(0~1)有何不同。
- 🧑🎓 学生活动:
- 学生分组讨论:“身高和年龄”应该用标准化还是归一化?(答案:都行,但KNN必须归一化)。
- 学生动手修改数组,加入一个极大值 999,再运行归一化看效果。
通过对比代码输出,直观看到同样的数据在不同的缩放方法下变成了什么样子。强调“KNN 必须用 MinMaxScaler”这个硬核结论,对接后续课程。
✍️ 四、 解决问题过程(三):课堂动手练习——“拯救被歧视的食堂数据”
📝 五、 课堂小结(5 分钟)
flowchart LR
root["⚙️ 6.1 特征缩放(预处理基石)"]
subgraph C1["❓ 为什么要缩放"]
direction TB
A1["避免大数吃小数"]
A2["让所有特征平等参与计算"]
end
subgraph C2["📐 标准化 StandardScaler"]
direction TB
B1["公式:(x-均值)/标准差"]
B2["结果:均值=0,标准差=1"]
B3["适用:有异常值 / 回归/SVM"]
end
subgraph C3["📏 归一化 MinMaxScaler"]
direction TB
C1_node["公式:(x-最小值)/(最大-最小)"]
C2_node["结果:强制缩放到 [0, 1]"]
C3_node["适用:**KNN / K-Means 必用**"]
end
root --> C1
root --> C2
root --> C3
style root fill:#4b6cb7,stroke:#253b6e,color:#fff,stroke-width:2px,rx:8px,ry:8px
style C1 fill:#e3f2fd,stroke:#2196f3,stroke-width:1px
style C2 fill:#fff3e0,stroke:#ff9800,stroke-width:1px
style C3 fill:#e8f5e9,stroke:#4caf50,stroke-width:1px
✏️ 随堂检测与互动练习
📮 六、 课后作业与拓展
📋 七、 板书设计
本课用到的单词
| 单词 | 发音 | 专业英语解释(中文) |
|---|---|---|
| Standardization | /ˌstændədaɪˈzeɪʃən/ | 标准化。将特征分布调整为均值为0,方差为1的过程。 |
| Normalization | /ˌnɔːrmələˈzeɪʃən/ | 归一化。将单个样本缩放到单位范数或将特征缩放到[min, max]区间(本课特指Min-Max缩放)。 |
| Scale | /skeɪl/ | 尺度/缩放。指特征数值的量级。 |
| Fit Transform | /fɪt trænsˈfɔːrm/ | 拟合转换。先计算数据统计量(fit),再应用转换(transform)的组合操作。 |
| Feature Scaling | /ˈfiːtʃər ˈskeɪlɪŋ/ | 特征缩放。本课所学内容的统称,是数据预处理的核心步骤。 |
补充理解 📐 标准差(Standard Deviation)
一、定义
标准差就是“每个数据偏离平均值的平均距离”。它衡量一组数据到底有多“散”。
- A班:身高全是
[160, 160, 160]→ 完全没波动,标准差 = 0。 - B班:身高
[150, 160, 170]→ 参差不齐,标准差 > 0。
二、核心公式
总体标准差用希腊字母 σ(西格玛)表示:
\[ \sigma = \sqrt{\frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2} \]用数据 [1, 2, 3] 手算一遍:
- 求平均数(μ):\( (1+2+3)/3 = 2 \)
- 每个数减平均数,再平方(目的是去掉负号,放大离群值):
- \( (1-2)^2 = 1 \)
- \( (2-2)^2 = 0 \)
- \( (3-2)^2 = 1 \)
- 求这些平方的平均数(这叫方差):\( (1+0+1)/3 \approx 0.666 \)
- 开根号(把单位还原回去):\( \sqrt{0.666} \approx 0.816 \)
结论:这组数据的标准差是 0.816,说明平均每个数离平均值 2 有 0.816 的距离。
三、⚠️ 代码里的“大坑”:NumPy 和 Pandas 默认不一样
| 库 | 默认公式 | 分母 | 适用场景 |
|---|---|---|---|
NumPy (np.std()) |
总体标准差 | N | 纯数学计算,与 StandardScaler 一致 |
Pandas (.std()) |
样本标准差 | N-1 | 数据分析实战,用样本估计整体 |
代码验证(直接跑,结果不一样):
“机器学习预处理中,如果用手动算的标准差去验证 StandardScaler,请用
np.std(),不要用df.std()!否则数字对不上会怀疑人生。”
原因:sklearn.preprocessing.StandardScaler 内部使用的是总体标准差(除以 N),所以:
- ✅ 验证时用
np.std(data)→ 和 StandardScaler 结果一致。 - ❌ 验证时用
df.std()→ 和 StandardScaler 结果不一致,因为 Pandas 默认除以 N-1。
如果要用 Pandas 算,显式指定分母: