第25课:数据标准化与归一化
学习目标
完成本课学习后,你将能够:
- 理解为什么需要对数据进行缩放(标准化与归一化)
- 区分标准化(Z-score Scaling)和归一化(Min-Max Scaling)的原理与适用场景
- 使用Python和scikit-learn对数据进行标准化和归一化处理
- 在实际机器学习流程中正确应用数据缩放技术,避免数据泄露等常见错误
核心概念
为什么需要数据缩放?
想象一下,你有一个数据集,其中包含两个特征:年龄(范围:20-60岁)和收入(范围:30,000-200,000元)。许多机器学习算法(特别是基于距离的算法,如K近邻、支持向量机,以及梯度下降算法,如线性回归、逻辑回归、神经网络)会受到特征尺度的影响。
如果特征的尺度差异很大,算法可能会:
- 误认为尺度较大的特征更重要
- 导致梯度下降收敛缓慢或不稳定
- 影响基于距离计算的准确性
数据缩放的目的就是将不同特征转换到同一尺度上,使模型能够更公平地学习所有特征。
标准化(Z-score Scaling)
标准化将数据转换为均值为0、标准差为1的分布。计算公式为: $$ z = \frac{x - \mu}{\sigma} $$ 其中:
- $x$ 是原始值
- $\mu$ 是特征的均值
- $\sigma$ 是特征的标准差
适用场景:当数据近似正态分布,或算法假设数据以原点为中心(如SVM、逻辑回归)时。
归一化(Min-Max Scaling)
归一化将数据缩放到一个固定的范围,通常是[0, 1]。计算公式为: $$ x_{norm} = \frac{x - x_{min}}{x_{max} - x_{min}} $$
适用场景:当需要将数据限制在特定范围内(如图像像素值[0, 255]→[0, 1]),或算法对输入尺度敏感且要求正值时(如神经网络)。
代码示例
让我们通过一个完整的示例来演示这两种缩放方法:
# 导入必要的库
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
# 1. 准备数据
# 使用经典的鸢尾花数据集
iris = load_iris()
X = iris.data
y = iris.target
# 特征名称
feature_names = iris.feature_names
print("原始数据统计:")
print(pd.DataFrame(X, columns=feature_names).describe())
# 划分训练集和测试集(重要:先划分,再缩放!)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
print(f"\n训练集大小: {X_train.shape[0]} 样本")
print(f"测试集大小: {X_test.shape[0]} 样本")
# 2. 标准化(Z-score Scaling)
print("\n" + "="*50)
print("标准化处理(Z-score Scaling)")
# 创建标准化器并拟合训练数据
standard_scaler = StandardScaler()
X_train_standardized = standard_scaler.fit_transform(X_train)
X_test_standardized = standard_scaler.transform(X_test) # 使用训练集的均值和标准差
# 验证标准化结果
print("标准化后训练集的均值(应接近0):", X_train_standardized.mean(axis=0))
print("标准化后训练集的标准差(应接近1):", X_train_standardized.std(axis=0))
# 3. 归一化(Min-Max Scaling)
print("\n" + "="*50)
print("归一化处理(Min-Max Scaling)")
# 创建归一化器并拟合训练数据
minmax_scaler = MinMaxScaler()
X_train_normalized = minmax_scaler.fit_transform(X_train)
X_test_normalized = minmax_scaler.transform(X_test) # 使用训练集的最小值和最大值
# 验证归一化结果
print("归一化后训练集的最小值(应为0):", X_train_normalized.min(axis=0))
print("归一化后训练集的最大值(应为1):", X_train_normalized.max(axis=0))
# 4. 可视化比较
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# 原始数据
axes[0].boxplot(X_train)
axes[0].set_title('原始数据')
axes[0].set_xticklabels(feature_names, rotation=45)
# 标准化数据
axes[1].boxplot(X_train_standardized)
axes[1].set_title('标准化数据 (Z-score)')
axes[1].set_xticklabels(feature_names, rotation=45)
axes[1].axhline(y=0, color='r', linestyle='--', alpha=0.5)
# 归一化数据
axes[2].boxplot(X_train_normalized)
axes[2].set_title('归一化数据 (Min-Max)')
axes[2].set_xticklabels(feature_names, rotation=45)
axes[2].axhline(y=0.5, color='r', linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()
# 5. 实际应用示例
print("\n" + "="*50)
print("实际应用示例:使用缩放后的数据训练模型")
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# 使用原始数据训练
model_raw = LogisticRegression(max_iter=1000)
model_raw.fit(X_train, y_train)
acc_raw = accuracy_score(y_test, model_raw.predict(X_test))
# 使用标准化数据训练
model_std = LogisticRegression(max_iter=1000)
model_std.fit(X_train_standardized, y_train)
acc_std = accuracy_score(y_test, model_std.predict(X_test_standardized))
# 使用归一化数据训练
model_norm = LogisticRegression(max_iter=1000)
model_norm.fit(X_train_normalized, y_train)
acc_norm = accuracy_score(y_test, model_norm.predict(X_test_normalized))
print(f"原始数据准确率: {acc_raw:.4f}")
print(f"标准化数据准确率: {acc_std:.4f}")
print(f"归一化数据准确率: {acc_norm:.4f}")
实践练习
练习1:基础标准化
要求:给定数据集 data = np.array([[100, 0.5], [200, 0.8], [300, 0.3], [400, 0.9]]),对数据进行标准化处理,并输出标准化后的均值和标准差。
预期输出:
标准化后均值: [0. 0.]
标准化后标准差: [1. 1.]
练习2:比较不同缩放方法
要求:使用sklearn的 load_wine 数据集,分别用标准化和归一化处理后,比较两种缩放方法下数据分布的差异。绘制第一个特征('alcohol')在原始、标准化、归一化后的直方图。
预期输出:三个并排的直方图,展示不同缩放方法对数据分布的影响。
练习3:完整机器学习流程
要求:完成以下任务:
- 加载sklearn的
fetch_california_housing数据集 - 划分训练集和测试集(70%/30%)
- 对特征进行标准化处理
- 训练一个线性回归模型
- 评估模型性能(使用MSE和R²分数)
- 尝试不使用缩放重复上述过程,比较结果
预期输出:
- 使用缩放和不使用缩放的模型性能对比
- 对缩放如何影响线性回归模型的解释
常见错误
1. 数据泄露:在划分前缩放整个数据集
错误做法:
# 错误!在划分前缩放整个数据集
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # 使用了所有数据的信息
X_train, X_test = train_test_split(X_scaled, test_size=0.3)
正确做法:
# 正确!先划分,再缩放
X_train, X_test = train_test_split(X, test_size=0.3)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 使用训练集的统计量
2. 忘记对测试集进行相同的变换
错误做法:
# 训练集缩放了,测试集没缩放
X_train_scaled = scaler.fit_transform(X_train)
# 直接使用原始测试集
predictions = model.predict(X_test) # 尺度不一致!
3. 对所有特征盲目使用相同的缩放方法
有些特征可能已经是同一尺度(如经过独热编码的分类变量),不需要缩放。对分类变量进行标准化或归一化可能没有意义。
4. 过度依赖自动缩放
某些算法(如基于树的算法:决策树、随机森林、梯度提升)对特征的尺度不敏感,通常不需要缩放。
小结
关键要点回顾:
- 为什么缩放:不同特征的尺度差异会影响许多机器学习算法的性能
- 标准化 vs 归一化:
- 标准化(Z-score):均值为0,标准差为1,适用于正态分布数据
- 归一化(Min-Max):缩放到固定范围[0, 1],适用于需要正值的算法
- 正确流程:
- 先划分训练集和测试集
- 在训练集上拟合缩放器(计算均值/标准差或最小值/最大值)
- 使用相同的统计量变换训练集和测试集
- 适用场景:
- 需要缩放的算法:SVM、KNN、线性回归、逻辑回归、神经网络
- 通常不需要缩放的算法:决策树、随机森林
下一步学习预告:下一课我们将学习主成分分析(PCA)降维,这是另一种重要的数据预处理技术,可以在保留重要信息的同时减少特征数量。