数值型数据:归一化#
归一化动机#
通过统计指标与可视化方法对数据做过初步检查之后,通常还要再做一步:把原始特征变换到更合适的尺度上(消除不同维度量纲的影响),以便让模型能够更高效地完成训练。这一步,就叫 归一化(normalization) 过程。直观地讲,归一化的目标是把各个特征压缩到相近的量级。
比如下面这两个特征:
特征
X跨越 154 到 24,917,482 之间。特征
Y跨度不大,只在 5 到 22 之间。
两者一对照,差距悬殊。归一化的活儿,就是把它们一起压到一个相对接近的范围里——比如都落在 0 到 1 之间。
那么,归一化到底换来了什么?
收敛速度更快: 不同特征的尺度差距越大,梯度下降越容易「左撞右撞」地振荡,收敛自然就慢。当然,Adagrad、Adam 这类优化器会随训练自适应地调整学习率,从而缓解这类震荡。
预测质量更好: 特征之间尺度不一,得到的模型往往只能给出比较粗糙的预测。
避开「NaN 陷阱」: 一旦特征值大到超出浮点数能表达的精度范围,系统就只能把它记成
NaN(Not a Number,即「非数字」);更糟的是,模型里只要有一个值变成 NaN,其它值最终也会跟着失真。学到合适的权重: 不做归一化的话,模型会把注意力过度集中在那些取值范围宽的特征上,而忽视取值范围窄的特征——这通常不是我们想要的。
基于以上这些效果,我们推荐这样使用归一化:
凡是彼此量级差距明显的数值型特征(比如年龄与收入),都建议做归一化。
哪怕是单一特征自己跨度就很大(例如
city population),同样也建议归一化。
注意: 训练时对某个特征做了归一化,那么上线推理时也要用完全相同的方式对它做归一化——反之亦然。
MLWM: 关于特征为什么要进行标准化,其主要目的是消除不同特征之间的量纲影响,以便模型能够快速收敛。更多与之相关的内容,可以参见文章 「特征标准化:归一化与标准化的原理及实现」 中的内容。
图示去均值化和最小-最大值标准可视化结果图 当然,还有一点尤其需要说明的是,推理是对新样本进行归一化时的参数一定要与训练集中的保持一致,相关说明可以参见 「手写体识别实例分析:模型改善与泛化实战」 中第4.6.1节数据集标准化中的内容。
归一化对比分析#
我们用两组特征,来看一下不做归一化的实际影响。
先看差距不算悬殊的一组:
特征
A的最小值为 -0.5,最大值为 +0.5。特征
B的最小值为 -5.0,最大值为 +5.0。
A、B 各自的跨度都不大,但 B 的跨度恰好是 A 的 10 倍。这种差异带来三个不良后果:
训练一开始,模型会「以为」
B的重要性是A的 10 倍。训练过程会比应有的时间拉得更长。
最终的模型未必是最优解。
虽说这种差距下「不归一化」的总体损害不大,但我们仍建议把 A、B 一起压到同一量级,比如都缩放到 -1.0 到 +1.0。
再看一组差距夸张的例子:
特征
C的最小值为 -1,最大值为 +1。特征
D的最小值为 +5000,最大值为 +1,000,000,000。
C、D 不归一化的话,模型不仅效果很难令人满意,而且训练过程本身可能根本难以收敛。
在接下来的内容中,将会介绍三种最常用的归一化方法:
线性缩放(linear scaling)
Z-score 缩放
log 缩放
另外还会介绍裁剪(clipping)——严格地说它不算归一化,但在工程上经常和归一化一起出场,把那些特别「不听话」的特征值控幅到更利于建模的范围内。
线性缩放#
线性缩放(linear scaling)——业内更常直接叫「缩放(scaling)」,也即使上面图示中的最大-最小值标准化 ——指的是把浮点特征从其自然范围线性映射到一个标准范围,通常是 0 到 1,或 -1 到 +1。
点击查看数学推导
缩放到 0 到 1(含端点)的标准区间,公式是:
$$ x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}} $$其中各符号含义:
$x'$:缩放之后的值。
$x$:原始值。
$x_{\min}$:该特征在整个数据集里的最小值。
$x_{\max}$:该特征在整个数据集里的最大值。
举个例子:假设 quantity 这一特征的自然范围是 100 到 900,而在某条样本中它的值恰好是 300。那么这一步算出来的归一化值就是:
$x$ = 300,$x_{\min}$ = 100,$x_{\max}$ = 900
x' = (300 - 100) / (900 - 100)
x' = 200 / 800
x' = 0.25线性缩放在3种条件同时成立时,往往是个好选择:
数据的下界与上界短期内不会显著变化。
特征里几乎不存在离群值——或者说即使有,也都在合理范围内。
特征在其取值范围内近似均匀分布。换句话说,画成直方图后,多数柱高差别不大。
举例来说,人类的 age 就是这样一个相对合适的例子:
它的近似下界是 0,上界是 100。
离群值占比很小——全球 100 岁以上的人只占约 0.3%。
不同年龄段虽然样本量略有不均,但只要数据量够大,每个年龄段都会有足够多的样本。
提示: 现实中的特征很少能同时满足上述三条。所以当你不确定时,Z-score 缩放通常是更稳妥的默认选项。
小测验:检验你的理解#
假设模型中有一个名为 net_worth 的特征,表示不同人的净资产。线性缩放对 net_worth 而言算不算合适的归一化方法?请说明理由。
Z-score 缩放#
Z-score(也叫作去均值标准化) 表达的是「某个值距离均值有多少个标准差」。例如,比均值大 2 个标准差的值,对应 Z-score 为 +2.0;比均值小 1.5 个标准差的值,对应 Z-score 为 -1.5。
把一个特征用 Z-score 缩放来表示,等于直接把这个值对应的 Z-score 存进特征向量里。
看下面这张图:
左图:经典的正态分布。
右图:同一个分布用 Z-score 缩放后的形态。

MLWM: 从图中我们可以发现,尽管各个样本点的绝对位置发生了变化,但是样本点之间的相对位置依旧没有变化,因此样本内部存在的模式(pattern)也不会发生变化。
Z-score 缩放也能很好地处理下面这种「形状只是大致像正态分布」的数据:

点击查看数学推导
将 $x$ 归一化到其 Z-score 的公式是:
$$ x' = \frac{x - \mu}{\sigma} $$各符号含义:
$x'$:Z-score。
$x$:原始值,也就是待归一化的那个值。
$\mu$:均值。
$\sigma$:标准差。
举个具体例子。假设:
均值 = 100 、标准差 = 20 、原始值 = 130
那么:
Z-score = (130 - 100) / 20
Z-score = 30 / 20
Z-score = +1.5点击了解正态分布的相关性质
对经典正态分布而言:
至少 68.27% 的数据 Z-score 落在 -1.0 到 +1.0 之间。
至少 95.45% 的数据 Z-score 落在 -2.0 到 +2.0 之间。
至少 99.73% 的数据 Z-score 落在 -3.0 到 +3.0 之间。
至少 99.994% 的数据 Z-score 落在 -4.0 到 +4.0 之间。
从这个范围可以看出:Z-score 小于 -4 或大于 +4 的点虽然极为少见,但它们算不算「离群值」?这其实没有标准答案,要看具体业务对「离群」的定义。值得注意的是,只要样本量足够大,就几乎一定会在数据里出现若干个这样的「稀有」样本——一个含 10 亿样本、服从经典正态分布的特征,外边那段 [-4, +4] 区间里可能塞进了 6 万个样本并不奇怪。
总结一下:Z-score 适合那些符合正态分布、或者近似正态分布的数据。
需要特别留意的是,有些分布看起来在主体范围内很「正态」,但仍存在极端的离群值。上面的 net_worth 特征就是典型:绝大部分样本规规矩矩地落在 3 个标准差以内,但偶尔会冒出几个离均值几百个标准差远的怪物样本。这种情况下,可以把 Z-score 与另一种归一化方法(通常是裁剪)搭配使用。
小测验:检验你的理解#
模型用 height 这一特征训练,记录了一千万名成年女性的身高。Z-score 缩放对 height 而言算不算合适的归一化方法?请说明理由。
log 缩放#
log 缩放计算的是原始值的对数。理论上对数的底可以任取;工程实践里,log 缩放一般选用自然对数(ln)。
点击查看数学推导
用自然对数把 $x$ 归一化,公式是:
$$ x' = \ln(x) $$其中:
$x'$:$x$ 的自然对数。
原始值 = 54.598
于是得:
4.0 = ln(54.598)log 缩放在数据服从幂律分布(power law)时特别好用。用直观的话说,幂律分布大致呈现两个特点:
X较小时,对应的Y值非常高。随着
X增长,Y会急剧下降,到最后大X对应的Y几乎贴近零。
电影评分就是个非常典型的幂律分布。看下面的图就能体会:
少数电影收获了大量用户评分(即「
X小、Y大」)。大多数电影的用户评分则寥寥无几(「
X大、Y小」)。
log 缩放会重塑这种分布形态,让模型学到更稳定的预测规律:

另一个常见的幂律分布例子是图书销量:
大多数出版图书销量极少,往往只有一两百本;
少数中等畅销的图书能卖到几千本;
极少数的「现象级畅销书」能卖出 100 万本以上。
设想一个线性模型,希望找到「图书封面」与「图书销量」之间的关系。如果直接拿原始销量去训练,模型就必须在「卖 100 本」与「卖 100 万本」的图书封面上,硬找一种「强度相差 10000 倍」的对应关系——这显然不合理。但如果先把销量做 log 缩放,问题就会大大缓解:
~4.6 = ln(100)
~13.8 = ln(1,000,000)100 万的对数大约只是 100 的 3 倍。换句话说,让模型去学「畅销书封面是否比小众书封面强大约 3 倍」这种粒度上的差异,就要合理得多。
裁剪#
裁剪(clipping)是用来抑制极端离群值影响的一种工程手法。它的核心想法很朴素:把离群值「盖住(cap)」到一个指定的最大(或最小)值上。这个思路乍看反直觉,但在很多场景下确实有效。
举例说明:一份数据集里有一个名为 roomsPerPerson 的特征,表示每栋住房的「人均房间数」(总房间数除以居住人数)。下图显示,超过 99% 的样本都符合正态分布(均值靠近 1、跨度有限),但仍有少量样本远远偏离主体。

右边这根细长的尾巴会不会把模型带偏?如果我们直接把 roomsPerPerson 的最大值封顶(cap),或者换个说法——裁剪到某个任意值(比如 4.0),会变成什么样?

需要注意的是:把上限设为 4.0,并不意味着模型会忽略所有原本大于 4.0 的样本——这些样本只是被统统「拍扁」成 4.0。4.0 处那个小「鼓包」,正是这一变换留下的痕迹。尽管鼓包有点碍眼,裁剪后的特征整体仍然比原始数据更利于建模。
等等,再想想,把每个离群值一刀切地压到某个固定阈值,真的可以吗?在训练模型时,可以。
同时,裁剪也可以放在其它归一化方法之后再进行。比如:先用 Z-score 缩放,但发现有一小撮离群值的绝对值远大于 3。这时可以:
把大于 3 的 Z-score 截断为 3;
把小于 -3 的 Z-score 截断为 -3。
裁剪确实能有效防止模型被无关紧要的极端数据「牵着走」。但要注意——并非所有离群值都是噪声,有些反而是关键信息,所以裁剪阈值要慎重选取。
归一化技术总结#
提示: 最合适的归一化技术,就是在实践中真正奏效的那个。所以如果你觉得某个新点子对当前特征分布会管用,不妨大胆试一试。
| 归一化技术 | 公式 | 适用场景 |
|---|---|---|
| 线性缩放 | $$ x' = \frac{x - x_{\min}}{x_{\max} - x_{\min}} $$ | 特征在其取值范围内近似均匀——平顶型(Flat-shaped) |
| Z-score 缩放 | $$ x' = \frac{x - \mu}{\sigma} $$ | 特征服从正态分布(峰值贴近均值)——钟型(Bell-shaped) |
| log 缩放 | $$ x' = \log(x) $$ | 分布在尾部一侧严重偏斜——重尾型(Heavy Tail-shaped) |
| 裁剪 | 若 $x > \max$,则 $x' = \max$;若 $x < \min$,则 $x' = \min$ | 特征中存在极端离群值 |
小测验:检验你的知识#
问题 1. 对下面这种分布的特征,哪种归一化技术最合适?

A. Z-score 缩放
B. 线性缩放
C. log 缩放
D. 裁剪
问题 2. 假设要开发一个模型,根据数据中心内部的 temperature(温度)预测其生产力。数据集中几乎所有的 temperature 值都在 15 到 30(摄氏度)之间,但有以下两类例外:
每年都会有 1 到 2 次极端高温,数据中会出现少量 31 到 45 之间的温度。
每 1000 个
temperature样本里,就有 1 个被记成 1000,而不是真实温度。
下列哪种处理,是对 temperature 比较合理的归一化方案?
A. 把 31 到 45 之间的离群值裁剪,但把值为 1000 的离群值直接删除
B. 把所有离群值都裁剪
C. 把所有离群值都删除
D. 把 31 到 45 之间的离群值删除,但把值为 1000 的离群值裁剪
关键术语:
- 裁剪(Clipping)
- NaN 陷阱(NaN trap)
- 归一化(Normalization)
- 缩放(Scaling)
- Z-score 归一化(Z-score normalization)
