查看全课大纲(第 13 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
特征幅度缩放
约 6 分钟
小象实战讲义 · 特征工程精讲与实践
在机器学习建模中,不同特征的数值范围可能差异巨大。例如,年龄在0-100之间,而收入可能高达数万。这种量级差异会影响许多模型(如逻辑回归、神经网络)的收敛速度与性能。本节我们将学习特征幅度缩放的核心方法,通过代码实战掌握如何将特征缩放到统一尺度,并理解训练集与测试集在特征工程中的正确处理流程。
💡 核心导读
- 为什么需要幅度缩放:消除特征间量纲差异,加速模型收敛,提升模型性能。
- 三种核心缩放方法:标准化(Z-Score Scaling)、归一化(Min-Max Scaling)、鲁棒缩放(Robust Scaling)。
- Scikit-learn 标准流程:
fit(在训练集学习统计量)→transform(应用变换)。 - 工业实践关键:必须用训练集学到的规则去变换测试集,防止数据泄漏。
幅度缩放的意义与场景
特征幅度缩放(Feature Scaling)是特征预处理的关键步骤。其核心目标是调整特征的数值范围,使其落入一个相对统一的区间。这并非改变数据的分布形态,而是对数值进行线性变换。
主要应用场景:
- 基于距离的模型:如K近邻(KNN)、支持向量机(SVM)。特征值范围过大时,数值大的特征会主导距离计算。
- 梯度下降优化的模型:如线性回归、逻辑回归、神经网络。特征尺度统一能大幅加快梯度下降的收敛速度。
- 正则化模型:如岭回归(Ridge)、Lasso。正则化项对系数施加惩罚,若特征尺度不一,系数的尺度也无法公平比较。
相反,树模型(如决策树、随机森林、XGBoost)基于特征阈值进行分裂,对特征幅度不敏感,通常不需要进行缩放。
核心缩放方法原理
1. 标准化 (Standardization / Z-Score Scaling)
标准化将数据线性变换为均值为0、标准差为1;它不改变分布形态——仅当原数据本身近似正态分布时,变换后的结果才近似标准正态分布。 其中, 是特征的均值, 是特征的标准差。
- 优点:多数模型(如逻辑回归、SVM、KNN)的默认选择;变换后各特征尺度可比。
- 注意:基于均值与标准差计算,仍受异常值影响(敏感性中等——标准差经平方运算会被极端值显著拉大);数据含显著异常值时应改用鲁棒缩放。
- 结果范围:理论上无界,但大部分值落在[-3, 3]区间。
2. 归一化 (Normalization / Min-Max Scaling)
归一化将数据线性变换到[0, 1]的固定区间。 其中, 和 分别是特征的最小值和最大值。
- 优点:严格限定输出范围,解释性强。
- 缺点:对异常值非常敏感。一个极端大的异常值会将其他正常数据压缩到很小的区间。
3. 鲁棒缩放 (Robust Scaling)
鲁棒缩放使用中位数和四分位距(IQR)进行缩放,对异常值不敏感。 其中, 是中位数,(75%分位数 - 25%分位数)。
- 优点:抗异常值能力强,适用于包含离群点的数据。
- 结果范围:无界。
代码实战:Scikit-learn 缩放流程
我们将在一个完整的机器学习数据流中演示三种缩放方法。关键原则:所有变换规则(均值、标准差、最小最大值等)必须仅从训练集学习,然后同时应用于训练集和测试集。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# 1. 构造一个可复现的微型泰坦尼克数据集样例(自包含,硬编码)
# 包含Pclass, Sex, Age, Fare, SibSp, Survived字段
data = pd.DataFrame({
'Pclass': [3, 1, 3, 1, 3, 2, 3, 3],
'Sex': ['male', 'female', 'female', 'female', 'male', 'male', 'female', 'male'],
'Age': [22.0, 38.0, 26.0, 35.0, 35.0, 54.0, 2.0, 27.0],
'Fare': [7.2500, 71.2833, 7.9250, 53.1000, 8.0500, 51.8625, 21.0750, 11.1333],
'SibSp': [1, 1, 0, 1, 0, 0, 3, 0],
'Survived': [0, 1, 1, 1, 0, 0, 1, 0]
})
print("原始数据集:")
print(data)
print("\n" + "="*50 + "\n")
# 2. 划分训练集和测试集(模拟真实项目流程)
# 使用Survived作为标签y,其他作为特征X
X = data.drop(columns=['Survived'])
y = data['Survived']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
print("训练集形状:", X_train.shape)
print("测试集形状:", X_test.shape)
print("\n训练集特征数据(用于学习变换规则):")
print(X_train)
print("\n测试集特征数据(将应用学到的规则):")
print(X_test)原始数据集:
Pclass Sex Age Fare SibSp Survived
0 3 male 22.0 7.2500 1 0
1 1 female 38.0 71.2833 1 1
2 3 female 26.0 7.9250 0 1
3 1 female 35.0 53.1000 1 1
4 3 male 35.0 8.0500 0 0
5 2 male 54.0 51.8625 0 0
6 3 female 2.0 21.0750 3 1
7 3 male 27.0 11.1333 0 0
==================================================
训练集形状: (6, 5)
测试集形状: (2, 5)
训练集特征数据(用于学习变换规则):
Pclass Sex Age Fare SibSp
0 3 male 22.0 7.2500 1
7 3 male 27.0 11.1333 0
2 3 female 26.0 7.9250 0
4 3 male 35.0 8.0500 0
3 1 female 35.0 53.1000 1
6 3 female 2.0 21.0750 3
测试集特征数据(将应用学到的规则):
Pclass Sex Age Fare SibSp
1 1 female 38.0 71.2833 1
5 2 male 54.0 51.8625 0方法一:标准化 (Z-Score Scaling)
# 重新导入确保自包含(实际写作时可省略,这里为演示)
from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np
# 重新准备与 train_test_split(random_state=42) 完全一致的训练集和测试集(自包含原则)
X_train = pd.DataFrame({
'Pclass': [3, 3, 3, 3, 1, 3],
'Sex': ['male', 'male', 'female', 'male', 'female', 'female'],
'Age': [22.0, 27.0, 26.0, 35.0, 35.0, 2.0],
'Fare': [7.2500, 11.1333, 7.9250, 8.0500, 53.1000, 21.0750],
'SibSp': [1, 0, 0, 0, 1, 3]
})
X_test = pd.DataFrame({
'Pclass': [1, 2],
'Sex': ['female', 'male'],
'Age': [38.0, 54.0],
'Fare': [71.2833, 51.8625],
'SibSp': [1, 0]
})
# 1. 初始化标准化缩放器
zscore_scaler = StandardScaler()
# 2. 在训练集上拟合:计算训练集Fare特征的均值(mu)和标准差(sigma)
zscore_scaler.fit(X_train[['Fare']])
# 3. 查看学习到的参数
print("【标准化】学习到的参数:")
print(" 均值 (mu):", zscore_scaler.mean_[0])
print(" 标准差 (sigma):", zscore_scaler.scale_[0])
# 4. 对训练集进行变换
X_train_zscore = X_train.copy()
X_train_zscore['Fare_zscore'] = zscore_scaler.transform(X_train[['Fare']])
print("\n训练集标准化后的结果:")
print(X_train_zscore[['Fare', 'Fare_zscore']])
# 5. 验证标准化结果
mean_after = X_train_zscore['Fare_zscore'].mean()
std_after = X_train_zscore['Fare_zscore'].std()
print(f"\n验证:标准化后Fare_zscore的均值: {mean_after:.10f}")
print(f"验证:标准化后Fare_zscore的标准差: {std_after:.6f}")
# 6. 使用训练集学到的scaler对测试集进行变换
X_test_zscore = X_test.copy()
X_test_zscore['Fare_zscore'] = zscore_scaler.transform(X_test[['Fare']])
print("\n测试集标准化后的结果(使用训练集的mu和sigma):")
print(X_test_zscore[['Fare', 'Fare_zscore']])【标准化】学习到的参数:
均值 (mu): 18.088883333333335
标准差 (sigma): 16.353595004403637
训练集标准化后的结果:
Fare Fare_zscore
0 7.2500 -0.662783
1 11.1333 -0.425324
2 7.9250 -0.621508
3 8.0500 -0.613864
4 53.1000 2.140882
5 21.0750 0.182597
验证:标准化后Fare_zscore的均值: -0.0000000000
验证:标准化后Fare_zscore的标准差: 1.095445
测试集标准化后的结果(使用训练集的mu和sigma):
Fare Fare_zscore
0 71.2833 3.252766
1 51.8625 2.065211💡 口径说明:
StandardScaler按总体标准差(ddof=0)标准化,因此缩放后数据的总体标准差为 1.0;验证打印出的 1.095445 来自pandas的.std()(默认样本标准差,ddof=1)——对 6 个样本即 。这是自由度口径差异,不是计算错误。
方法二:归一化 (Min-Max Scaling)
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
# 重新准备与上一块一致的数据(自包含)
X_train = pd.DataFrame({'Fare': [7.2500, 11.1333, 7.9250, 8.0500, 53.1000, 21.0750]})
X_test = pd.DataFrame({'Fare': [71.2833, 51.8625]})
# 1. 初始化归一化缩放器
minmax_scaler = MinMaxScaler()
# 2. 在训练集上拟合:计算训练集Fare特征的最小值(min)和最大值(max)
minmax_scaler.fit(X_train[['Fare']])
# 3. 查看学习到的参数
print("【归一化】学习到的参数:")
print(" 最小值 (min):", minmax_scaler.data_min_[0])
print(" 最大值 (max):", minmax_scaler.data_max_[0])
# 4. 对训练集进行变换
X_train_minmax = X_train.copy()
X_train_minmax['Fare_minmax'] = minmax_scaler.transform(X_train[['Fare']])
print("\n训练集归一化后的结果:")
print(X_train_minmax[['Fare', 'Fare_minmax']])
# 5. 验证归一化结果
min_after = X_train_minmax['Fare_minmax'].min()
max_after = X_train_minmax['Fare_minmax'].max()
print(f"\n验证:归一化后Fare_minmax的最小值: {min_after}")
print(f"验证:归一化后Fare_minmax的最大值: {max_after}")
# 6. 对测试集进行变换
X_test_minmax = X_test.copy()
X_test_minmax['Fare_minmax'] = minmax_scaler.transform(X_test[['Fare']])
print("\n测试集归一化后的结果(使用训练集的min和max):")
print(X_test_minmax[['Fare', 'Fare_minmax']])【归一化】学习到的参数:
最小值 (min): 7.25
最大值 (max): 53.1
训练集归一化后的结果:
Fare Fare_minmax
0 7.2500 0.000000
1 11.1333 0.084696
2 7.9250 0.014722
3 8.0500 0.017448
4 53.1000 1.000000
5 21.0750 0.301527
验证:归一化后Fare_minmax的最小值: 0.0
验证:归一化后Fare_minmax的最大值: 1.0
测试集归一化后的结果(使用训练集的min和max):
Fare Fare_minmax
0 71.2833 1.396582
1 51.8625 0.973010注意一个细节:测试集的 71.2833 超出了训练集的最大值 53.1,因此它被变换到了 1.3966——落在了 [0, 1] 区间之外。这正是「用训练集规则变换测试集」的真实写照:未来的数据完全可能超出历史范围,缩放器只保证训练集落在目标区间,测试值的外推是正常现象,而不是错误。
方法三:鲁棒缩放 (Robust Scaling)
from sklearn.preprocessing import RobustScaler
import pandas as pd
import numpy as np
# 重新准备与上一块一致的数据(自包含)
X_train = pd.DataFrame({'Fare': [7.2500, 11.1333, 7.9250, 8.0500, 53.1000, 21.0750]})
X_test = pd.DataFrame({'Fare': [71.2833, 51.8625]})
# 1. 初始化鲁棒缩放器
robust_scaler = RobustScaler()
# 2. 在训练集上拟合:计算训练集Fare特征的中位数和IQR
robust_scaler.fit(X_train[['Fare']])
# 3. 查看学习到的参数
print("【鲁棒缩放】学习到的参数:")
print(" 中位数 (median):", robust_scaler.center_[0])
print(" 四分位距 (IQR):", robust_scaler.scale_[0])
# 4. 对训练集进行变换
X_train_robust = X_train.copy()
X_train_robust['Fare_robust'] = robust_scaler.transform(X_train[['Fare']])
print("\n训练集鲁棒缩放后的结果:")
print(X_train_robust[['Fare', 'Fare_robust']])
# 5. 对测试集进行变换
X_test_robust = X_test.copy()
X_test_robust['Fare_robust'] = robust_scaler.transform(X_test[['Fare']])
print("\n测试集鲁棒缩放后的结果(使用训练集的中位数和IQR):")
print(X_test_robust[['Fare', 'Fare_robust']])【鲁棒缩放】学习到的参数:
中位数 (median): 9.591650000000001
四分位距 (IQR): 10.633325
训练集鲁棒缩放后的结果:
Fare Fare_robust
0 7.2500 -0.220218
1 11.1333 0.144983
2 7.9250 -0.156738
3 8.0500 -0.144983
4 53.1000 4.091698
5 21.0750 1.079940
测试集鲁棒缩放后的结果(使用训练集的中位数和IQR):
Fare Fare_robust
0 71.2833 5.801727
1 51.8625 3.975318方法对比与选择指南
| 方法 | 公式 | 输出范围 | 对异常值敏感性 | 适用场景 |
|---|---|---|---|---|
| 标准化 | 无界(通常≈[-3,3]) | 中等(受均值、标准差影响) | 特征近似正态分布;大多数模型默认选择 | |
| 归一化 | [0, 1](固定) | 高(min/max直接受异常值影响) | 需要严格限定范围;图像像素值处理 | |
| 鲁棒缩放 | 无界 | 低(基于中位数和IQR) | 数据包含显著异常值;金融、生物医学数据 |
工业实践建议:
- 默认尝试标准化:适用于大多数场景,尤其是后续使用线性模型、神经网络时。
- 数据有异常值时用鲁棒缩放:能保证变换后的数据更稳定。
- 谨慎使用归一化:除非你确信数据没有异常值,或者业务要求特征必须在[0,1]区间。
- 树模型通常不需要缩放:但进行缩放也不会损害性能,有时为了一致性也会统一处理。
📝 动手练一练
- 概念辨析:假设一个数据集中“年龄”特征的范围是[0, 100],“年薪”特征的范围是[20000, 200000]。如果直接使用原始数据训练一个逻辑回归模型,可能会遇到什么问题?为什么?
👉 点击查看参考答案
问题:模型训练过程(梯度下降)可能收敛缓慢或不稳定,且特征重要性的解读会被尺度差异扭曲。
原因:逻辑回归使用梯度下降优化损失函数。特征尺度差异大会导致损失函数的等高线呈椭圆形,梯度下降路径会剧烈震荡,需要更多迭代步数才能收敛。此外,许多正则化方法(如L1/L2)平等地惩罚所有系数,尺度大的特征对应的系数自然会变小,但这并非因为该特征不重要,而是尺度差异导致的,从而影响特征重要性的解读。
- 代码实践:上面鲁棒缩放的例子中,我们计算了训练集
Fare特征的中位数和IQR。请根据打印出的参数(中位数≈9.59,IQR≈10.63),手动计算测试集中第一个样本Fare=71.2833的鲁棒缩放结果,验证是否与代码输出一致。
👉 点击查看参考答案
手动计算: 公式:
代入测试集第一个样本值:
代码输出结果为5.801727,两者一致。同时可以看到,这个测试样本远超训练集的取值范围(训练集Fare最大仅53.1),因此鲁棒缩放后的值也远大于训练集中任何一个变换后的值——变换规则只对「学到的数据」负责,未来数据超出历史范围时输出也会相应外推。
本章小结
本节我们深入学习了特征工程中的基础但至关重要的技术——特征幅度缩放。我们理解了不同缩放方法(标准化、归一化、鲁棒缩放)的数学原理、适用场景及其在Scikit-learn中的标准实现流程。最重要的是,我们掌握了工业实践中必须先在训练集上拟合(fit)变换器,再将同一规则应用于测试集(transform) 的核心原则,这是防止数据泄漏、保证模型评估可靠性的关键。
📋 行动清单
- 在你的下一个机器学习项目中,对连续型特征有意识地进行幅度缩放,并观察其对模型训练速度的影响。
- 使用Scikit-learn的
StandardScaler、MinMaxScaler、RobustScaler分别对同一数据集进行变换,比较变换后数据的分布与范围。 - 牢记并实践“
fit仅用于训练集,transform用于训练集和测试集”的工作流,养成编写可复现特征工程管道的习惯。
—— 小象教研组