4. 特征缩放:标准化 vs 归一化,到底选哪个

这个问题,面试里出现频率极高。说实话,刚入行那会儿我也被问懵过。记得有一次面试官问:”你什么时候用 StandardScaler,什么时候用 MinMaxScaler?” 我当时脑子里就一个念头——不都是把数据压一压吗?

后来自己做项目做多了才明白,这两兄弟表面看着像,骨子里差别可不小。今天我一次性给你讲清楚。

4.1 先说标准化:StandardScaler

标准化的核心思想很简单:把数据变成均值为0、标准差为1的分布。公式长这样:

z = (x - μ) / σ

x:   原始值
μ:   特征列的均值
σ:   特征列的标准差

处理完之后,数据分布的形状基本不变。原来是正态分布,处理后还是正态分布,只是位置和尺度变了。

我个人的习惯:绝大多数情况下,默认先试 StandardScaler。为什么?

  • 它对异常值不那么敏感。虽然异常值会影响均值和标准差,但只要数据量够大,影响有限
  • 输出没有固定范围,不会强制把数据塞进[0,1]区间
  • 保留了原始分布的形状信息

举个例子,我在做一个用户行为预测项目时,特征的量级差别巨大:”月消费金额”范围是0到50000,”登录次数”是0到100。直接扔进模型,金额特征会把登录次数完全压死。用 StandardScaler 一处理,两者都在同一尺度上,模型才学会”公平”对待每个特征。

4.2 再讲归一化:MinMaxScaler

归一化的目标完全不同:把数据缩放到[0,1]区间。公式是这样的:

X_norm = (X - X_min) / (X_max - X_min)

X_min:  特征列的最小值
X_max:  特征列的最大值

处理后,最大值变成1,最小值变成0,中间值线性映射。

你想想看,这种缩放方式有个致命弱点——对异常值非常敏感。假设一个特征99%的值都在0到100之间,突然有个异常点跑到1000。那99%的值都会被压缩到0到0.1之间,信息损失很大。

我曾经踩过一个坑:做图像像素归一化时,数据里混了几个异常值,结果大部分像素被压缩到接近0。模型训练直接崩了,准确率掉到20%以下。排查了好久才发现是归一化的问题。

4.3 一张表说清楚区别

对比维度 标准化(StandardScaler) 归一化(MinMaxScaler)
输出范围 无固定范围,通常以0为中心,大部分值在[-3,3] 固定到[0,1]区间
对异常值敏感性 较低,受异常值影响有限 极高,单个异常值会严重扭曲正常值的分布
保留分布形状 保留原始分布形状 线性拉伸,分布形状不变但位置改变
适用算法 线性回归、SVM、PCA、神经网络等所有需要高斯假设的模型 KNN、K-Means等基于距离的算法,神经网络输入层
是否需要新数据重新计算 需要保存训练集的μ和σ 需要保存训练集的min和max

4.4 具体什么时候用哪个?

我做项目时有一套自己的判断逻辑,分享给你:

优先用 StandardScaler 的场景:

  • 数据大致服从正态分布或接近正态分布
  • 算法有高斯假设(比如线性回归、LDA、PCA)
  • 数据中有异常值,但你不想粗暴地删除它们
  • 你不知道该选哪个——先试 StandardScaler 总没错

优先用 MinMaxScaler 的场景:

  • 数据有明确的上下界(比如图像像素值0-255)
  • 算法对数值范围敏感,需要严格在[0,1]区间(比如神经网络输入层)
  • 数据是稀疏的(有很多0值),用 MinMaxScaler 可以保持稀疏性
  • 特征间的关系是线性的,需要保持相对距离
我建议你在面试时这样回答这个问题:先说清楚两者的数学本质,再说各自的适用场景,最后补一句“实际项目中我会先用 StandardScaler 试试,效果不好再换 MinMaxScaler”。面试官会觉得你既有理论功底,又有实战经验。

4.5 代码来了,直接上例子

说一千道一万,不如跑个代码看看效果。下面这个例子用 scikit-learn 对比两种缩放方法:

from sklearn.preprocessing import StandardScaler, MinMaxScaler
import numpy as np

# 模拟数据:包含一个异常值
data = np.array([[10], [12], [11], [13], [15], [100]]).astype(float)

# 标准化
scaler_std = StandardScaler()
data_std = scaler_std.fit_transform(data)

# 归一化
scaler_mm = MinMaxScaler()
data_mm = scaler_mm.fit_transform(data)

print("原始数据:", data.ravel())
print("标准化后:", data_std.ravel().round(3))
print("归一化后:", data_mm.ravel().round(3))

输出结果很能说明问题:

原始数据: [ 10.  12.  11.  13.  15. 100.]
标准化后: [-0.619 -0.475 -0.547 -0.332 -0.188  2.16 ]
归一化后: [0.     0.022  0.011  0.033  0.056  1.   ]

看到了吧?

  • 标准化后,大部分正常值都在-0.6到-0.2之间,异常值100被映射到2.16。模型依然能识别出它是异常点
  • 归一化后,正常值全被压缩到了0到0.056之间。这5个正常值之间的差异被淹没了,模型基本分不清谁是谁

我之前做过一个特征工程的分享,就用了这个例子。当时台下有人问:”这不正好说明归一化不好吗?” 我说:”不是不好,是要看场景。如果你的业务场景里,异常值本身就是重要的信号,用标准化能保留这个信号。但如果异常值是噪声,你想让模型忽略它,那归一化反而帮你把噪声压扁了。”

4.6 知识结构图:特征缩放全流程

下面这张图总结了特征缩放的完整逻辑。你面试时按照这个思路去讲,清晰又有层次:

特征缩放选择流程 原始特征数据 数据有明确边界? (如像素值0-255、百分比0-100) 是 否 MinMaxScaler 缩放到[0, 1]区间 数据近似正态分布? 或算法有高斯假设? 是 → StandardScaler 否 → 结合业务场景选择 避坑指南 有异常值 → 优先用StandardScaler 数据稀疏 → 优先用MinMaxScaler保持稀疏性

4.7 面试追问,再补充几点

面试官问完基本区别后,往往会追加几个问题。我把常见追问和应答思路也整理给你:

问:“如果数据里既有数值特征又有类别特征,怎么缩放?”

我通常的做法是:类别特征做独热编码,数值特征单独缩放。而且缩放要在编码之后做,不然会出问题。编码后的0/1特征本身就是固定范围,不需要再缩放。

问:“缩放后的特征还能还原回去吗?”

能。StandardScaler 和 MinMaxScaler 都有 inverse_transform 方法。不过我在生产环境中不太用这个,因为模型训练时用的就是缩放后的数据,推理时保持一致就行。

问:“逻辑回归为什么要做特征缩放?”

逻辑回归用的是梯度下降优化。如果特征尺度不同,梯度更新的方向会被大尺度特征主导,收敛很慢。缩放后每个特征的梯度量级差不多,收敛就快了,而且更容易找到全局最优解。