沈阳企业自助建站/百度大数据搜索引擎
本文整理自<数据预处理:标准化和归一化><特征归一化特性及其数学原理推导>
- 1. 主要方法及应用场景
- 2. 特征归一化的使用范围
- 3. 为什么需要对数值类型的特征做归一化?
- 4. 代码实现
- 5. 应用实例——基于股票数据
- 6. 数学原理推导
1. 主要方法及应用场景
对数值类型的特征做归一化可以将所有的特征都统一到一个大致相同的数值区间内。最常用的方法主要有以下两种。
(1)线性函数归一化(Min-Max Scaling),又称极大极小归一化(Min-Max Normalization),或简称归一化。它对原始数据进行线性变换,使结果映射到[0, 1]的范围,实现对原始数据的等比缩放。归一化公式如下

其中,X为原始数据,Xmax、Xmin分别为数据最大值和最小值。
(2)零均值归一化(Z-Score Normalization),又称零均值标准化(Z-Score Standardization),或简称标准化。它会将原始数据映射到均值为0、标准差为1的分布上。具体来说,假设原始特征的均值为μ、标准差为σ,那么归一化公式定义为:

因为极大极小归一化的方法是,根据极值,将所有内部值压缩到[0,1]区间;而零均值标准化做的是,根据所有数值,将分布转为标准正态分布,均值为0,方差为1,取值[-1,1]。在实际使用过程中深有体会,当用极大极小归一化时,如果不是已经知道极值(比如0~255),一旦有新的数据(比如Haar特征)加入,根据固定的极值(当前数据最大最小值)去压缩,就可能产生超过1或者小于0的特征值,但是在处理上会方便许多;采用零均值标准化的话,由于可能取到负值,而且新进来的数据依然可能越界,其实都不好使……
那么,应该如何取舍归一化和标准化呢?
- 如果对输出结果有要求,或者数据比较稳定,用归一化。
- 如果存在一些异常值和噪音,数据符合正态分布,用标准化。
- 如果涉及距离度量计算相似性(比如KNN、Kmeans聚类)、或者PCA,用标准化。(这点我也疑惑,相似性计算是指什么?)
前面两个都好理解,数据稳定,用极值方便;有异常或噪音,用标准化保证整体数值分布。我们来仔细分析一下第三点,首先推导零均值标准化原理(及后续推导内容放在数学原理推导小节内)。
除了极大极小归一化、零均值归一化,还有一些非线性的归一化,比如log10、log2、tan等。归一化的好处真的非常之大,是机器学习入门必备的概念。
2. 特征归一化的使用范围
《百面机器学习》上说
在实际应用中,通过梯度下降法求解的模型通常是需要归一化的,包括线性回归、逻辑回锅、支持向量机、神经网络等模型。但对于决策树模型则并不适用,以C4.5决策树为例,决策树在进行节点分裂时主要依据数据集D关于特征x的信息增益比,而信息增益比跟特征是否进行归一化是无关的,因为归一化并不会改变样本在特征x上的信息增益。
但其实,看一下C4.5的信息增益比公式,样本集合D,类别数N,某类样本子集Ci,Di中属于第n类的子集D(in),特征取值数K,||表示取元素个数,某特征X,其信息增益比为:

信息增益比相当于帮决策树进行了归一化。而且其实,决策树使用了归一化,也不会有太大的问题。
另一种补充观点:
那么是否任何情况都有必要进行标准化归一化呢?答案是否定的。我们所熟知的决策树、随机森林等概率模型,就不需要。因为它们并不关心变量的值,而是关心变量的分布和变量之间的条件概率。
3. 为什么需要对数值类型的特征做归一化?
通过一个形象的随机梯度下降例子来解释:
假设有两种数值型特征,x1的取值范围为[0, 10],x2的取值范围为[0, 3],于是可以构造一个目标函数符合图1.1(a)中的等值图。在学习速率相同的情况下,x1的更新速度会大于x2,需要较多的迭代才能找到最优解。如果将x1和x2归一化到相同的数值区间后,优化目标的等值图会变成图1.1(b)中的圆形,x1和x2的更新速度变得更为一致,容易更快地通过梯度下降找到最优解。

用人话来讲就是,归一化使得特征的变化更容易用统一标准去衡量了。
这个例子其实来源于斯坦福机器学习公开课的一节,课题是我们想要去预测房价,假设存在这样一个函数:

这个函数里的y是预测房价,x1是房间大小(1~100平方米),x2是房间数量(1~4间)。我们都知道房间越多、房间越大,房子越值钱,但不知道具体“50平方米2间房”和“60平方米1间房”哪个更值钱。所以现在有一批[房间大小, 房间数量, 房价]的训练数据,我们用这批数据去求得使得大部分数据都符合这个公式的参数θ1、θ2,不就得到房价预测模型了吗?以后输入房间大小、房间数量,代入公式,就得到了预测房价。那么“大部分都符合”这个标准用数学量化出来就是“损失函数最小”。命题转变为了求解使得损失函数最小的参数θ1、θ2。
损失函数很容易定义为预测房价和真实房价差值的平方(为了取正数):

损失函数值越小,说明预测房价和真实房价的差距越小,预测越精准。
可以想象的是,当没有归一化的时候,x1=50,x2=2,y(real)=100,我们可以取1*50+25*2 - 100,也可以取0.2*50+45*2 -100,他们的值都是0,然而θ1的取值范围波动会小一些,θ2的取值范围波动巨大,如果画出等值线,就会是θ1进行缓慢地变化θ2剧烈变化的椭圆;当归一化之后,x1=0.5,x2=0.5,y(real)=1,那么取值1*0.5+1*0.5-1,或者0*0.5+2*0.5-1,会发现参数变化更加均匀一些了,等值线(等高线)也就越趋近于圆。
所以,用原始的量纲不同、取值范围差异较大的两个特征,去进行梯度下降寻找最优解,未归一化的话这个圆会变得很尖,归一化了圆就会更圆,但程序不知道啊,它会通过梯度下降去迭代,未归一化的数据更容易产生“之”字型走法来迭代寻找最优解,归一化的数据更容易走直线快速找到最优解。
但这只是讲了归一化的好处——提升模型收敛速度,并没有讲不归一化会发生什么。不归一化的最大坏处在于“数值小的特征变化失去意义”,举个例子(我以前就干过这样的事……):
我们已知平面上有很多点(x, y),现在取一个点(x0, y0)=(0,0),找出和它最近的2个点。x的取值范围是-1~1,y的取值范围是-1000~1000
1. (0.1,100)
2. (0.2,300)
3. (0.2,100)
4. (0.3,300)
我们会发现,当计算距离的时候,y的取值很大程度地影响了距离的值,y稍微一变化,距离就变化很大。x重要吗?根本不重要!完全和已知在纵轴上,y0=0,寻找最近的2个点的命题几乎一样:
y1=100
y2=300
y3=100
y4=300
最近的仍然是y1、y3,哪怕你有一个(x5,y5)=(1,300),x变化再剧烈,最近邻也轮不到你。效果就是,x这个特征似乎消失了。
所以,归一化在维数非常多的时候,可以防止某一维或某几维对数据影响过大。
4. 代码实现
参考<Python数据预处理><Python求均值,方差,标准差>。另外,标准化会涉及到标准差,不知道是默认总体方差还是样本方差,留待以后。已确认不存在ddof参数。样本方差和总体方差概念参见<补充附录(9)数据处理相关:样本方差和总体方差>
- (1)标准化(Z-score standardization)
按照公式(X-mean)/std 计算,对其每个属性按列(缺省按列,可以通过axis=1改为按行)分别进行。得到的结果是,对于每个属性/每列来说所有数据都聚集在0附近,方差为1。实现时,有两种不同的方式:
- sklearn.preprocessing.scale(X,axis=1)函数,可以直接将给定数据进行标准化。可设置axis参数
- 使用sklearn.preprocessing.StandardScaler类,使用该类的好处在于可以保存训练集中的参数(均值、方差),继而直接使用其对象转换测试集数据。似乎不存在axis参数,也就不能改为按行
from sklearn import preprocessing
import numpy as npX = np.array([[ 1., -1., 2.],[ 2., 0., 0.],[0., 1., -1.]])# sklearn.preprocessing.scale(X,axis)函数
X_scaled1 = preprocessing.scale(X) #缺省按每行相应位置之间,即按每列求 mean,std,(X-mean)/std
print(X_scaled1)
print('-----------------')
X_scaled2 = preprocessing.scale(X,axis=1) #改为按每列相应位置之间,即按每行求
print(X_scaled2)# sklearn.preprocessing.StandardScaler函数
print('-----------------')
scaler = preprocessing.StandardScaler().fit(X)
X_scaled3=scaler.transform(X)
print(scaler.mean_)
print(scaler.var_)
#print(scaler.std_) # 这句报错,说不存在std_属性,挺奇怪的
print(X_scaled3)
print('-----------------')
#可以直接使用训练集对测试集数据进行转换
test_data=[[-1.,1.,0.]]
scaler.transform(test_data)# 输出
[[ 0. -1.22474487 1.33630621][ 1.22474487 0. -0.26726124][-1.22474487 1.22474487 -1.06904497]]
-----------------
[[ 0.26726124 -1.33630621 1.06904497][ 1.41421356 -0.70710678 -0.70710678][ 0. 1.22474487 -1.22474487]]
-----------------
[1. 0. 0.33333333] #可以很明显看到,三列,所以算出来三个值
[0.66666667 0.66666667 1.55555556] [[ 0. -1.22474487 1.33630621][ 1.22474487 0. -0.26726124][-1.22474487 1.22474487 -1.06904497]]
-----------------
array([[-2.44948974, 1.22474487, -0.26726124]])
- (2)归一化(Min-Max Scaling)
作用:对于方差非常小的属性可以增强其稳定性;维持稀疏矩阵中为0的条目。类似StandardScaler类,似乎不能没有axis设置参数
- preprocessing.MinMaxScaler类实现
>>> X_train = np.array([[ 1., -1., 2.],[ 2., 0., 0.],[0., 1., -1.]])
>>> min_max_scaler = preprocessing.MinMaxScaler()
>>> X_train_minmax = min_max_scaler.fit_transform(X_train)
>>> print(X_train_minmax)
[[0.5 0. 1. ][1. 0.5 0.33333333][0. 1. 0. ]]>>> #缩放因子等属性
>>> min_max_scaler.scale_
array([ 0.5, 0.5,0.33])
>>> min_max_scaler.min_
array([0.,0.5,0.33])>>> #将相同的缩放应用到测试集数据中
>>> X_test = np.array([[ -3.,-1.,4.]])
>>> X_test_minmax = min_max_scaler.transform(X_test)
>>> X_test_minmax
array([[-1.5, 0., 1.66666667]])
当然,在构造类对象的时候也可以直接指定最大最小值的范围:feature_range=(min, max),此时应用的公式变为:
X_std=(X-X.min(axis=0))/(X.max(axis=0)-X.min(axis=0))
X_scaled=X_std/(max-min)+min
注意,这两行自定义公式为何有效,熟悉以下几行代码就明白了X_train.min(axis=0) = [ 0. -1. -1.]
X_train.min 也是存在的
补充:关于StandardScaler和MinMaxScaler的使用,观察上面例子可以看到,一是不存在axis参数设置。二是具体语句使用上可以是fit_transform+transform或者fit+transform+transform,相当于前者拆成后者。无论用哪种,一开始变换的时候,必须要使用fit,如下所示
# 第一种
>>> X_train = np.array([[ 1., -1., 2.],[ 2., 0., 0.],[0., 1., -1.]])
>>> X_test = np.array([[ -3.,-1.,4.]])
>>> min_max_scaler = preprocessing.MinMaxScaler()
#用于train数据时,必须有fit
>>> result_train = min_max_scaler.fit_transform(X_train)
#而将标准用于test数据时,直接transform即可,后续的test也是
>>> result_test = min_max_scaler.transform(X_test)# 第二种
>>> X_train = np.array([[ 1., -1., 2.],[ 2., 0., 0.],[0., 1., -1.]])
>>> X_test = np.array([[ -3.,-1.,4.]])
>>> min_max_scaler = preprocessing.MinMaxScaler.fit() # .fit()放到下面一行也是可以的,很灵活
>>> result_train = min_max_scaler.transform(X_train)
>>> result_test = min_max_scaler.transform(X_test)
以上我已经实际验证过了。StandardScaler同样适用。不理解建立者为什么要搞得这么灵活,fit_transform=fit+transform,限定一种写法不就行了吗,太冗余了
5. 应用实例——基于股票数据
- (1)标准化(standardization)
公式一般为:(X-mean)/std,其中mean是平均值,std是方差。
从公式我们可以看出,标准化操作(standardization)是将数据按其属性(按列)减去平均值,然后再除以方差。这个过程从几何上理解就是,先将坐标轴零轴平移到均值这条线上,然后再进行一个缩放,涉及到的就是平移和缩放两个动作。这样处理以后的结果就是,对于每个属性(每列)来说,所有数据都聚集在0附近,方差为1。计算时对每个属性/每列分别进行。(我不理解这个缩放从哪里来的?除非std是根据输入X变化的数据,不然这个公式只是线性函数,大家同比例变化,何来缩放直说?可以自己操作一下函数看看研究一下)
下面我们通过实操加深印象,并真正理解标准化的实际意义。使用sklearn中的preprocessing中的scale()函数,可以直接将给定数据进行标准化。
首先我们导入需要的库,和需要处理的数据。我们随便导入一支股票,000002万科a。(数据源来自tushare pro版,感谢挖地兔)。

接着,我们选取该股票最后的100个交易日的数据,选取价格和成交量两个特征作为演示。很显然,这两个特征量纲不一样,数值相差很大,需要对他们进行一个数据预处理。先看一下原始数据:

对原始数据进行标准化,如下:

看到变化了吗,虽然各个点的相对位置看上去还是没变,但是坐标轴变了。均值是0,方差为1。
还有一种标准化的方法是使用sklearn.preprocessing.StandardScaler类,主要是fit()和transform(),使用该类的好处在于可以保存训练集中的参数(均值、方差)直接使用其对象转换测试集数据。
- (2)归一化(scale,缩放到一个指定范围,一般是0到1之间)
公式一般为:(X-min)/(max-min),其中min和max分别是该属性的最小值和最大值。
归一化其实是一种不太标准的翻译而已,真正要理解它,只需要知道它是一种缩放就行。归一化操作的过程,首先是把某个属性(按列)的最大值和最小值之间的距离看成是单位1,然后再看x和最小值的距离占总距离的比例。所以它总是一个处于0到1之间的百分数。
使用sklearn中的preprocessing中的MinMaxScaler类,可以实现归一化,具体方法如下:

各个点的相对位置还是没变,但是取值范围变成了[0,1]。
6. 数学原理推导
- 正态分布的期望从定义开始推导
正态分布也叫高斯分布,期望有很多种推导方式,比如极坐标这种很秀的推导方法,我们只记最简单的Γ函数推导就好了。
(1)推导E(x)到合适的地方
已知一般正态分布:

其概率密度函数为:

根据期望的定义有:

换元积分,令:

则E(x)可以改写为:

左式为奇函数在对称区间的积分,积分为0,而右边是偶函数在对称区间的积分,积分为2倍半区间积分,于是:

(2)推导Γ函数的特性
注意到含参变量的以无穷乘积函数定义的反常积分Γ函数定义为:

用分部积分法(其公式不再继续往下证明)有:

那么,令:

得到Γ函数的递推公式及其推论:

(3)利用Γ函数的特性
所以,对E(X)再次换元:

有:

正态分布的方差从定义开始推导
(1)推导D(x)到合适的地方

同样地,进行换元,令:

利用分部积分法,有:

注意这里分部积分法的使用,是拆开了前面的平方,组合后面的。而且没有用网上所谓的“注意到”:

虽然和分部积分法原理一样,但是除非非常熟练,看到这个积分就能想到这个导数,否则以我的视角根本不可能去“注意到”。还是分部积分法靠谱一些。
(2)利用Γ函数特性

第二种推导方案是利用:

来进行推导。
第三种推导方案是,不使用奇函数特性,使用洛必达法则推导;不使用Γ函数特性,使用双重积分+极坐标推导。
第四种推导方案是,不使用分部积分法,使用一种新奇的换元方法+Γ函数特性:

这里不再赘述。
为什么零均值标准化能使得均值为0方差为1
即需要证明,若X~N(μ,σ),则Z=(X-μ/σ)~N(0,1)
我们知道,分布函数F(x)、概率密度f(x)存在这样的关系:

那么:

所以,Z=(X-μ/σ)~N(0,1)
为什么零均值标准化适合于距离度量、PCA
PCA原理(需要自己研读一下)不再介绍,我们知道PCA的核心就是方差与协方差。原始特征样本协方差为(N-1是无偏估计,撇表示区分不是指导数):

那么,使用零均值标准化:


而使用极大极小归一化:


极大极小归一化使得协方差产生了倍数值缩放,无法消除量纲的影响;而零均值标准化对方差进行了归一化,使得量纲不会对协方差的计算产生任何影响。
所以,如果需要每个特征值都对整体归一化产生一定影响的话(和分布相关的话),选择零均值归一化。