为什么要归一化(Normalization)
问题引出:为什么要Normalization?
举例:
有这么几个人:
1:35岁收入10000
2:25岁收入10000
3:26岁收入10500
这几个人一般我们主观会认为2和3较为相似(年龄和收入差不多) 但是,如果没有进行数据的归一化,机器用距离计算相似度时: 会认为1和2是相似的,因为他们的年龄距离是10,收入距离为0,而 1与3的年龄距离是9,但工资距离确是500.所以,这就是为什么要进行数据的归一化。
**个人理解:**这就是相当于一种权重的分配,每一个特征(年龄,收入…)数值的浮动对结果的影响是不一样的, 而归一化就是使各种特征对结果影响的程度变得平等的过程, 即特征小变化引起结果突变的,给他一个较大的权重(来显得这个特征的重要性)。而特征数值跳动很大对结果影响却微乎其微的,我们就给他一个小的权值,来削弱它的重要性。也不能说它不重要,本质上权重就是量纲单位。其实就是做计算之前要统一单位罢了。说白了就是计算机只认数据。
好处、目的:
没归一化的梯度下降的效果 --------------------- 归一化处理后的梯度下降的效果
举个例子:
假如我们客户数据信息,有两个维度,一个是用户的年龄,一个是用户的月收入,目标变量是快乐程度。
name | age | salary | happy |
---|---|---|---|
路博通 | 36 | 7000 | 100 |
马老师 | 42 | 20000 | 180 |
赵老师 | 22 | 30000 | 164 |
…… | …… | …… | …… |
通过年龄和月收入来预测开心程度,可以写出线性回归公式:
y
=
θ
1
x
1
+
θ
2
x
2
+
b
y = \theta_1x_1 + \theta_2x_2 + b
y=θ1x1+θ2x2+b
我们把 x1 看成是年龄,x2 看成是收入, y 对应着快乐程度。机器学习就是在知道 X,y的情况下解方程组调整出最优解的过程。
根据公式我们也可以发现 y 是两部分贡献之和,按常理来说,一开始并不知道两个部分谁更重要的情况下,可以想象为两部分对 y 的贡献是一样的,
即
θ
1
x
1
=
θ
2
x
2
,
如
果
x
2
≪
x
1
,
那
么
最
终
θ
2
≫
θ
1
即\theta_1x_1 = \theta_2x_2 ,如果 x_2 \ll x_1 ,那么最终 \theta_2 \gg \theta_1
即θ1x1=θ2x2,如果x2≪x1,那么最终θ2≫θ1
这就是为什么这个图是椭圆的原因。
回到梯度下降中,在一开始初始化权重参数是根据标准正态分布随机生成的,也就是说一开始的权重参数都是差不多的大小。
根
据
梯
度
公
式
g
j
=
(
h
θ
(
x
)
−
y
)
x
j
,
因
为
x
2
≪
x
1
,
所
以
g
2
≪
g
1
根据梯度公式 g_j= (h_{\theta}(x) - y)x_j,因为 x_2 \ll x_1,所以g_2 \ll g_1
根据梯度公式gj=(hθ(x)−y)xj,因为x2≪x1,所以g2≪g1
所 以 , 由 更 新 梯 度 : θ j n + 1 = θ j n − η ∗ g j 可 知 , 每 次 更 新 θ 2 的 幅 度 会 ≪ 远 小 于 θ 1 更 新 的 幅 度 所以,由更新梯度:\theta_j^{n+1} = \theta_j^n - \eta * g_j 可知,每次更新\theta_2的幅度会\ll远小于\theta_1更新的幅度 所以,由更新梯度:θjn+1=θjn−η∗gj可知,每次更新θ2的幅度会≪远小于θ1更新的幅度
这 就 意 味 着 : 最 后 θ 1 需 要 比 θ 2 更 少 的 迭 代 次 数 就 可 以 收 敛 这就意味着:最后 \theta_1 需要比 \theta_2 更少的迭代次数就可以收敛 这就意味着:最后θ1需要比θ2更少的迭代次数就可以收敛
而 我 们 要 最 终 求 得 最 优 解 , 就 必 须 每 个 维 度 θ 都 收 敛 才 可 以 , 所 以 会 出 现 而我们要最终求得最优解,就必须每个维度 \theta 都收敛才可以,所以会出现 而我们要最终求得最优解,就必须每个维度θ都收敛才可以,所以会出现
θ 1 等 待 θ 2 收 敛 的 情 况 。 对 应 图 就 可 以 理 解 为 什 么 是 先 顺 着 θ 1 的 坐 标 轴 往 下 走 再 往 右 走 的 原 因 了 \theta_1等待\theta_2收敛的情况。对应图就可以理解为什么是先顺着 \theta_1的坐标轴往下走再往右走的原因了 θ1等待θ2收敛的情况。对应图就可以理解为什么是先顺着θ1的坐标轴往下走再往右走的原因了
没有归一化就像先富带动后富,先富的这批人等待其他的人富裕起来;但是,更好途经是实现共同富裕(Normalization),最后每个人都不能落下, 优化的步伐是一致的。
归一化:就是数量级的统一,无量纲化 。
常见的归一化方法:
1、最大值最小值归一化
也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0 - 1]之间。转换函数如下:
X
∗
=
X
−
X
_
m
i
n
X
_
m
a
x
−
X
_
m
i
n
X^* = \frac{X - X\_min}{X\_max -X\_min}
X∗=X_max−X_minX−X_min
使用最大值最小值归一化(min-max标准化)的时候,优点是一定可以把数值归一到 0 ~ 1 之间,缺点是如果有一个离群值(比如马云的财富),正如我们举的例子一样,会使得一个数值为 1,其它数值都几乎为 0,所以受离群值的影响比较大
使用scikit-learn函数:
import numpy as np
from sklearn.preprocessing import MinMaxScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
display(x)
min_max_scaler = MinMaxScaler()
x_ = min_max_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_)
归一化之前的数据:
array([[ 6, 254],
[ 8, 187],
[ 8, 138],
[ 6, 289],
[ 1, 285],
[ 3, 111],
[ 3, 234],
[ 8, 142],
[ 4, 173],
[ 6, 195]])
归一化之后的数据:
array([[0.71428571, 0.80337079],
[1. , 0.42696629],
[1. , 0.15168539],
[0.71428571, 1. ],
[0. , 0.97752809],
[0.28571429, 0. ],
[0.28571429, 0.69101124],
[1. , 0.1741573 ],
[0.42857143, 0.34831461],
[0.71428571, 0.47191011]])
离群值的影响:
#把x中的一个数据改的非常大
x[0][1] = 123456789
print(x)
print("最大最小归一化后:")
np.set_printoptions(suppress=True)#不显示科学计数
min_max_scaler = MinMaxScaler()
x_ = min_max_scaler.fit_transform(x)
print(x_)
[[ 4 123456789]
[ 7 115]
[ 9 209]
[ 2 175]
[ 5 259]
[ 8 163]
[ 9 177]
[ 1 157]
[ 4 241]
[ 5 244]]
最大最小归一化后:
[[0.375 1. ]
[0.75 0. ]
[1. 0.00000076]
[0.125 0.00000049]
[0.5 0.00000117]
[0.875 0.00000039]
[1. 0.0000005 ]
[0. 0.00000034]
[0.375 0.00000102]
[0.5 0.00000104]]
2、0-均值标准化(Z-score标准化)
经过处理的数据符合标准正态分布,即均值为0,标准差为1,转化函数为:
X
∗
=
X
−
μ
σ
X^* = \frac{X - \mu}{\sigma}
X∗=σX−μ
其中 μ 为所有样本数据的均值,σ 为所有样本数据的标准差。
μ
=
1
n
∑
i
=
1
n
x
i
,
σ
=
1
n
∑
i
=
1
n
(
x
i
−
μ
)
2
\mu = \frac{1}{n}\sum\limits_{i = 1}^nx_i , \sigma = \sqrt{\frac{1}{n}\sum\limits_{i = 1}^n(x_i - \mu)^2}
μ=n1i=1∑nxi,σ=n1i=1∑n(xi−μ)2
相对于最大值最小值归一化来说,因为标准归一化除以了标准差,而标准差的计算会考虑到所有样本数据,所以受到离群值的影响会小一些,这就是除以方差的好处!但是,0-均值标准化不一定会把数据缩放到 0 ~ 1 之间了。既然是0均值,也就意味着,有正有负!
scikit-learn函数:
# preprocessing 预处理,数据
from sklearn.preprocessing import StandardScaler
standard = StandardScaler()
# 第一步,进行训练
standard.fit(X)
# 第二步,转换
X_norm2 = standard.transform(X)
# standard.fit_transform(X) # 一步转换
X_norm2
array([[ 0.94884747, 1.18993453],
[ 1.50699304, 0.01524523],
[-0.16744367, 1.39768441],
[-1.84188039, -1.82915904],
[-0.72558924, -0.11531847],
[ 0.94884747, 0.18152194],
[-0.72558924, -1.51235006],
[-0.72558924, 0.93187921],
[-0.16744367, -0.28620331],
[ 0.94884747, 0.02676556]])
那为什么要减去均值呢?
其实做均值归一化还有一个特殊的好处(对比最大值最小值归一化,全部是正数0~1),我们来看一下梯度下降的式子,
你
就
会
发
现
α
是
正
数
,
不
管
A
是
正
还
是
负
(
A
就
是
y
^
−
y
=
h
θ
(
x
)
−
y
)
,
你就会发现 \alpha是正数,不管 A 是正还是负( A 就是 \hat{y} - y = h_{\theta}(x) - y),
你就会发现α是正数,不管A是正还是负(A就是y^−y=hθ(x)−y),
对 于 所 有 的 维 度 X , 比 如 这 里 的 x 1 和 x 2 来 说 , α 乘 上 A 都 是 一 样 的 符 号 , 那 么 每 对于所有的维度 X,比如这里的 x_1和x_2 来说,\alpha乘上 A 都是一样的符号,那么每 对于所有的维度X,比如这里的x1和x2来说,α乘上A都是一样的符号,那么每
次 迭 代 的 时 候 w 1 t + 1 和 w 2 t + 1 的 更 新 幅 度 符 号 也 必 然 是 一 样 的 , 这 样 就 会 像 下 图 次迭代的时候 w_1^{t+1}和 w_2^{t+1}的更新幅度符号也必然是一样的,这样就会像下图 次迭代的时候w1t+1和w2t+1的更新幅度符号也必然是一样的,这样就会像下图
有 右 侧 所 示 : 要 想 从 w t 更 新 到 w ∗ 就 必 然 要 么 w 1 和 w 2 同 时 变 大 再 同 时 变 小 , 或 有右侧所示:要想从 w_t更新到 w^* 就必然要么 w_1和 w_2 同时变大再同时变小,或 有右侧所示:要想从wt更新到w∗就必然要么w1和w2同时变大再同时变小,或
者 就 w 1 和 w 2 同 时 变 小 再 同 时 变 大 。 不 能 如 图 上 所 示 蓝 色 的 最 优 解 路 径 , 即 w 1 变 小 的 同 时 w 2 变 大 ! 者就 w_1和 w_2 同时变小再同时变大。不能如图上所示蓝色的最优解路径,即 w_1 变小的同时 w_2 变大! 者就w1和w2同时变小再同时变大。不能如图上所示蓝色的最优解路径,即w1变小的同时w2变大!
那我们如何才能做到让 w_1变小的时候 w_2 变大呢?归其根本还是数据集 X 矩阵(经过min-max归一化)中的数据均为正数。所以如果我们可以让 x_1 和 x_2它们符号不同,比如有正有负,其实就可以在做梯度下降的时候有更多的可能性去让更新尽可能沿着最优解路径去走。
结论:0-均值标准化处理数据之后,属性有正有负,可以让梯度下降沿着最优路径进行~
注意:
我们在做特征工程的时候,很多时候如果对训练集的数据进行了预处理,比如这里讲的归一化,那么未来对测试集的时候,和模型上线来新的数据的时候,都要进行相同的数据预处理流程,而且所使用的均值和方差是来自当时训练集的均值和方差!
因为我们人工智能要干的事情就是从训练集数据中找规律,然后利用找到的规律去预测新产生的数据。这也就是说假设训练集和测试集以及未来新来的数据是属于同分布的!从代码上面来说如何去使用训练集的均值和方差呢?就需要把 scaler 对象持久化, 回头模型上线的时候再加载进来去对新来的数据进行处理。
import joblib
joblib.dump(standard_scaler,'scale') # 持久化
standard_scaler = joblib.load('scale') # 加载
standard_scaler.transform(x) # 使用
假设训练集和测试集以及未来新来的数据是属于同分布的!从代码上面来说如何去使用训练集的均值和方差呢?就需要把 scaler 对象持久化, 回头模型上线的时候再加载进来去对新来的数据进行处理。
import joblib
joblib.dump(standard_scaler,'scale') # 持久化
standard_scaler = joblib.load('scale') # 加载
standard_scaler.transform(x) # 使用
注:学习笔记,如果有侵权请告知