数值特征处理:标准化、归一化与非线性变换

数值特征,说白了就是那些能直接拿尺子量、拿秤称的数据——年龄、收入、温度、点击次数。这类特征在机器学习里最常见,但也是最容易出问题的。我见过太多新手直接把原始数值扔进模型,结果模型要么收敛慢得像蜗牛,要么干脆学偏了。

为什么会这样?因为不同特征的量纲和分布差异太大了。比如年龄范围是0-100,收入可能是0-100万,这两个特征放在一起,模型会天然认为收入更重要——仅仅因为它的数值更大。这显然不合理。

所以,数值特征处理的核心目标就两个:统一尺度和改善分布。下面我把常用的方法挨个讲清楚。

标准化(Z-score)

标准化是最常用的方法。它的公式很简单:

x' = (x - μ) / σ

其中μ是均值,σ是标准差。处理后,数据变成均值为0、标准差为1的分布。

我个人习惯在以下场景优先用标准化:

  • 数据本身近似正态分布——标准化后效果最好
  • 模型需要计算距离或梯度——比如SVM、逻辑回归、神经网络
  • 特征中有明显的异常值——标准化对异常值没那么敏感
小技巧:标准化不会改变数据的分布形状。如果你的数据原本是偏态的,标准化后依然是偏态的。它只是平移和缩放,不是魔法。

归一化(Min-Max)

归一化把数据压缩到[0, 1]区间:

x' = (x - min) / (max - min)

这个方法我一般在以下情况用:

  • 数据有明确的上下界——比如像素值0-255
  • 模型对数值范围敏感——比如KNN、神经网络
  • 需要保留原始数据的相对关系
注意:归一化对异常值极其敏感。我曾经在一个用户行为数据集上做归一化,结果有个用户的点击量是正常值的100倍,导致其他所有用户的特征都被压缩到了0.01以下。模型直接废了。

RobustScaler

RobustScaler是我处理异常值时的首选。它使用中位数和四分位距:

x' = (x - median) / IQR

其中IQR是上四分位数与下四分位数的差值。

它的优势很明显:

  • 不受异常值影响——中位数和IQR对极端值不敏感
  • 保留数据的相对排序
  • 适合有大量噪声的数据

我记得有一次做金融风控模型,用户的收入特征里有几个亿万富翁,用Min-Max归一化后,99%的用户收入都被压到了0.01以下。换成RobustScaler后,问题立刻解决了。

非线性变换

有时候,标准化和归一化都不够用。比如数据严重偏态——长尾分布、指数分布,这时候就需要非线性变换。

对数变换(Log)

对数变换是最简单的非线性变换:

x' = log(x + 1)

加1是为了处理x=0的情况。它能把右偏的数据拉近正态分布。

我常用的场景:

  • 收入、房价、点击量——这些数据通常呈长尾分布
  • 比例数据——比如转化率、点击率
  • 数据跨度很大——比如从1到100万

Box-Cox变换

Box-Cox是对数变换的推广,它有一个参数λ可以自动优化:

x' = (x^λ - 1) / λ,  λ ≠ 0
x' = log(x),          λ = 0

它的好处是:

  • 自动寻找最佳变换参数
  • 能处理多种偏态
  • 有统计理论基础
限制:Box-Cox要求数据全部为正数。如果数据有负数或零,需要先做平移。

Yeo-Johnson变换

Yeo-Johnson是Box-Cox的改进版,它不需要数据为正:

对于 x ≥ 0:  x' = ((x+1)^λ - 1) / λ,  λ ≠ 0
            x' = log(x+1),            λ = 0
对于 x < 0:  x' = -(((-x+1)^(2-λ) - 1) / (2-λ)),  λ ≠ 2
            x' = -log(-x+1),                       λ = 2

说实话,公式看着复杂,但实际用起来很简单——scikit-learn里一行代码搞定。我一般在数据包含负数时用Yeo-Johnson,比如差值、变化量这类特征。

方法对比

方法 适用场景 对异常值敏感度 输出范围 数据要求
标准化(Z-score) 近似正态分布、梯度模型 中等 无固定范围 无
归一化(Min-Max) 有界数据、KNN 高 [0, 1] 无
RobustScaler 含异常值的数据 低 无固定范围 无
对数变换 右偏分布、长尾数据 低 无固定范围 数据 > 0
Box-Cox 自动优化变换参数 低 无固定范围 数据 > 0
Yeo-Johnson 含负数的数据 低 无固定范围 无

知识体系图

下面这张图总结了数值特征处理的整体脉络:

数值特征处理方法体系 数值特征处理 线性变换 非线性变换 标准化 Z-score 归一化 Min-Max RobustScaler 对数变换 Log Box-Cox Yeo-Johnson 选择建议 • 数据近似正态分布 → 标准化(Z-score) • 数据有界且无异常值 → 归一化(Min-Max) • 数据含异常值 → RobustScaler • 数据严重偏态 → 对数变换 / Box-Cox / Yeo-Johnson

代码示例

下面用scikit-learn演示几种方法的用法:

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

# 模拟数据:包含异常值
data = np.array([[100], [200], [300], [400], [5000]]).astype(float)

# 标准化
scaler_std = StandardScaler()
data_std = scaler_std.fit_transform(data)
print("标准化:", data_std.flatten())

# 归一化
scaler_mm = MinMaxScaler()
data_mm = scaler_mm.fit_transform(data)
print("归一化:", data_mm.flatten())

# RobustScaler
scaler_rob = RobustScaler()
data_rob = scaler_rob.fit_transform(data)
print("RobustScaler:", data_rob.flatten())

# Box-Cox变换(要求数据为正)
pt_boxcox = PowerTransformer(method='box-cox')
data_bc = pt_boxcox.fit_transform(data)
print("Box-Cox:", data_bc.flatten())

# Yeo-Johnson变换(支持负数)
pt_yj = PowerTransformer(method='yeo-johnson')
data_yj = pt_yj.fit_transform(data)
print("Yeo-Johnson:", data_yj.flatten())
关键点:注意看归一化那行——5000这个异常值把其他所有数据都压到了0.06以下。而RobustScaler和Box-Cox就好得多。这就是为什么我总说:先看数据,再选方法。

避坑指南

最后分享几个我踩过的坑:

  • 先拆分再变换——一定要先用训练集拟合scaler,再用训练集的参数去变换测试集。我曾经见过有人对整个数据集做标准化再拆分,结果造成数据泄露,模型评估虚高。
  • 树模型不需要特征缩放——决策树、随机森林、XGBoost这类模型对尺度不敏感,标准化反而可能降低可解释性。
  • 不要盲目用Box-Cox——它虽然能自动优化参数,但有时候优化出来的λ会让数据变得难以解释。我一般先试试对数变换,效果不好再上Box-Cox。
  • 注意保存scaler参数——模型上线后,新数据需要和训练数据用同样的方式变换。不保存scaler参数,上线就是灾难。

嗯,数值特征处理就这些。方法不多,但每个都有它的脾气。你想想看,选对了方法,模型效果能提升一大截;选错了,调参调到头秃也救不回来。所以,多花点时间在特征处理上,绝对值得。