春江暮客

春江暮客的个人学习分享网站

预处理方法怎么选:MinMaxScaler、StandardScaler、RobustScaler 与 Normalizer 区别

2021-12-26 技术
预处理方法怎么选:MinMaxScaler、StandardScaler、RobustScaler 与 Normalizer 区别

机器学习里,预处理通常不是“顺手做一下”的小步骤,而是会直接影响模型表现的重要环节。尤其当不同特征的量纲差异很大,或者数据里存在明显离群值时,不同的缩放方式可能会让模型效果差很多。

sklearn 里围绕这个问题最常见的几个名字是:MinMaxScalerStandardScalerRobustScalerNormalizer。它们看起来都像“归一化”,但实际处理目标并不一样。

这篇文章先把概念分清,再用一组模拟数据看看不同方法处理前后的变化,最后给出更实用的选择建议。

定义

先把几个容易混淆的词分开:

  • scale:广义上指“改变数值尺度”
  • standardize:通常指减去均值、再除以标准差,让数据变成均值接近 0、标准差接近 1
  • normalize:在很多文章里会和前两者混着说,但在 sklearn 里,Normalizer 更具体,指把每一行样本缩放到单位范数

也就是说,StandardScalerMinMaxScalerRobustScaler 通常都是按列处理特征,而 Normalizer 更多是按行处理样本。

开始前先记住一个判断顺序

如果你只想先快速选一个方法,可以先按下面这个顺序判断:

  1. 特征量纲差很多,但离群值不严重,优先看 StandardScaler
  2. 希望把值压到固定区间,比如 [0, 1],优先看 MinMaxScaler
  3. 数据里离群值明显,优先看 RobustScaler
  4. 如果你更关心样本向量方向,而不是单个特征绝对大小,比如文本向量或余弦相似度场景,再看 Normalizer

原因

为什么这些预处理会影响模型表现?

因为不少算法对特征尺度非常敏感。如果一个特征的数值范围在几千,另一个特征只在 0 到 1 之间,模型很可能会过度依赖前者。

常见受影响比较明显的算法包括:

  • 线性回归
  • KNN
  • NN
  • SVM
  • PCA
  • LDA

尤其是基于距离、梯度或特征方差的算法,通常更依赖合适的预处理。

生成测试数据

这里用 np.random 生成一组模拟数据,分别包含 beta 分布、指数分布、正态分布,以及均值和方差更大的正态分布,用来观察不同缩放方法的效果。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 设置种子,保证数据可重现
np.random.seed(1024)
data_beta = np.random.beta(1, 2, 1000)
data_exp = np.random.exponential(scale=2, size=1000)
data_nor = np.random.normal(loc=1, scale=2, size=1000)
data_bignor = np.random.normal(loc=2, scale=5, size=1000)

# 生成dataframe
df = pd.DataFrame({
    "beta": data_beta,
    "exp": data_exp,
    "bignor": data_bignor,
    "nor": data_nor,
})
df.head()

先看原始分布:

sns.displot(df.melt(), x="value", hue="variable", kind="kde")
plt.savefig("origin.png", dpi=200)

origin

比较不同处理后数据变化情况

下面分别使用 MinMaxScalerRobustScalerStandardScaler 进行处理并比较。这里先不把 Normalizer 混在一起,因为它处理的是“每一行样本”,逻辑和前面三个不一样。

MinMaxScaler

MinMaxScaler 会把每一列特征线性压缩到固定区间,默认是 [0, 1]

from sklearn.preprocessing import MinMaxScaler

df_minmax = pd.DataFrame(MinMaxScaler().fit(df).transform(df), columns=df.columns)
sns.displot(df_minmax.melt(), x="value", hue="variable", kind="kde")
plt.savefig("minmaxscaler.png", dpi=200)
plt.close()

MinMaxScaler 转换后数据变成这样:

beta    exp     bignor  nor
0       0.402556        0.077887        0.623988        0.662302
1       0.344735        0.058066        0.671804        0.635879
2       0.050261        0.587947        0.573984        0.249901
3       0.113638        0.568196        0.767447        0.483282
4       0.394540        0.190253        0.661321        0.662763

分布为:

minmaxscaler

它的优点是范围固定、直观,但如果数据里有极端离群值,其他大多数样本会被一起压缩得很厉害。

RobustScaler

RobustScaler 使用中位数和四分位距来缩放,因此对离群值更稳。

from sklearn.preprocessing import RobustScaler

df_rsca = pd.DataFrame(RobustScaler().fit(df).transform(df), columns=df.columns)
sns.displot(df_rsca.melt(), x="value", hue="variable", kind="kde")
plt.savefig("robustscaler.png", dpi=200)
plt.close()

RobustScaler 转换后数据变成这样:

beta    exp     bignor  nor
0       0.284395        -0.141315       0.551292        0.923234
1       0.126973        -0.278467       0.779068        0.790394
2       -0.674763       3.388036        0.313090        -1.150118
3       -0.502211       3.251365        1.234674        0.023211
4       0.262573        0.636202        0.729129        0.925553

分布为:

robustscaler

如果你的数据明显偏态,或者已经知道存在一批极端值,RobustScaler 往往会比 StandardScaler 更稳一些。

StandardScaler

StandardScaler 是最常见的标准化方式:均值变成接近 0,标准差变成接近 1。

from sklearn.preprocessing import StandardScaler

df_std = pd.DataFrame(StandardScaler().fit(df).transform(df), columns=df.columns)
sns.displot(df_std.melt(), x="value", hue="variable", kind="kde")
plt.savefig("standardscaler.png", dpi=200)
plt.close()

StandardScaler 转换后数据变成这样:

beta    exp     bignor  nor
0       0.252859        -0.460413       0.711804        1.207845
1       0.012554        -0.600899       1.008364        1.030065
2       -1.211305       3.154733        0.401670        -1.566938
3       -0.947902       3.014740        1.601554        0.003337
4       0.219547        0.336005        0.943345        1.210949

分布为:

standardscaler

如果你没有特别强的理由选别的,StandardScaler 往往是很多连续型数值特征的默认起点。

Normalizer 和前面三个有什么不同

Normalizer 很容易和前面三个混淆,但它的目标不一样。前面三个通常都是按列处理特征,而 Normalizer 是按行处理每个样本,把每个样本缩放到单位范数。

一个简单例子:

from sklearn.preprocessing import Normalizer

sample = pd.DataFrame({
    "x1": [3, 1],
    "x2": [4, 2]
})

Normalizer().fit_transform(sample)
# array([[0.6       , 0.8       ],
#        [0.4472136 , 0.89442719]])

这类处理常见于:

  1. 文本向量
  2. 稀疏特征
  3. 更关注向量方向而不是绝对大小的相似度计算

所以它和 StandardScalerMinMaxScaler 并不是简单替代关系。

实际中该怎么选

如果只想要一个简单实用的选择建议,可以记住下面这张表:

方法 主要作用 更适合的情况
MinMaxScaler 压到固定区间 神经网络输入、需要固定边界时
StandardScaler 变成均值 0、方差 1 大多数连续数值特征的默认选择
RobustScaler 对离群值更稳 偏态分布或异常值明显的数据
Normalizer 让每个样本单位范数化 文本向量、余弦相似度等场景

真正落地时,不要只看名字,而要先问:

  1. 我是在按特征缩放,还是按样本缩放?
  2. 数据里有没有明显离群值?
  3. 下游模型到底是更看重距离、梯度,还是向量方向?

总结

本文比较了 MinMaxScalerStandardScalerRobustScalerNormalizer 这几种常见预处理方法,并解释了它们在处理目标上的差异。

如果你处理的是普通连续数值特征,StandardScaler 往往是最常见的起点;如果离群值明显,就优先看 RobustScaler;如果必须压缩到固定区间,就考虑 MinMaxScaler;如果你关心的是每个样本向量的方向而不是绝对大小,再考虑 Normalizer

友情链接

其它