特征缩放
参考资料
维基百科
知乎
数据标准化/归一化normalization
sklearn
pandas & numpy
numpy中标准差std的神坑
目的
1.提高梯度下降的收敛速度。
- 原始数据的代价函数收敛:
- 特征缩放后:
2. 如果原始数据的特征之间差别很大,不进行特征缩放的话,会导致某些机器学习算法无法正常完成工作。比如很多分类器是通过计算欧氏距离来计算数据与数据之间的距离,如果某个特征的范围比其他特征大的多,可能距离就受到了该特征的支配。
3. batch normalization,批规范化,BN,可以防止梯度爆炸\梯度消失\振荡。
方法
1. rescaling
将数据的特征按比例缩放到[0,1]的区间,公式如下:
2. mean normalization
缩放到[-1, 1]的区间,公式如下:
3. standardization
又称之为:
,将所有数据缩放到平均值为0,方差为1, 如果数据服从正态分布,则现在为标准正态分布,如果数据不服从正态分布,那么缩放后不是标准正态分布,只是服从均值为0,方差为1。数据缩放后在0左右分布,但不一定就在[0,1]范围内,有可能超出:
其中, 为对应特征 的均值, 为对应特征 的标准差。
原始数据最好近似为正态分布,否则缩放的结果不理想。
4.scaling to unit length
都变成了单位向量
直观感受:
# -*- coding: utf-8 -*-
import numpy as np
from functools import reduce
from math import sqrt
data_set = np.array([0.0, 1, 2, 3, 4, 5])
min_data = data_set.min()
max_data = data_set.max()
mu = data_set.mean()
sigma = sqrt(reduce(lambda x, y: x + y, map(lambda z: (z - mu) ** 2, data_set)) / len(data_set))
data_set_rescaling = list(map(lambda x: (x - min_data) / (max_data - min_data), data_set))
data_set_mean_normalization = list(map(lambda x: (x - mu) / (max_data - min_data), data_set))
data_set_standardization = list(map(lambda x: (x - mu) / sigma, data_set))
data_set_scaling_to_unit_length = list(map(lambda x: x/x if x != 0 else 0.0, data_set))
print(data_set)
print(data_set_rescaling)
print(data_set_mean_normalization)
data_set_standardization = [float('%.2f' % data) for data in data_set_standardization]
print(data_set_standardization)
print(data_set_scaling_to_unit_length)
[0. 1. 2. 3. 4. 5.]
[0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
[-0.5, -0.3, -0.1, 0.1, 0.3, 0.5]
[-1.46, -0.88, -0.29, 0.29, 0.88, 1.46]
[0.0, 1.0, 1.0, 1.0, 1.0, 1.0]
scikit learn的API
1. rescaling
使用MinMaxScaler
或者MaxAbsScaler
MinMaxScaler
将数据按照比例缩放到[0,1]
区间。
保留比例关系。
如果给出参数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
其中,min
、max
为feature_range=(min, max)
传入的参数,默认为(0,1)
# coding:utf-8
from sklearn import preprocessing
import numpy as np
x_train = np.array([[1., 3, 5],
[2, 3, 3],
[4, 5, 3], ])
x_test = [[-1., 1., 0.]]
min_max_scaler = preprocessing.MinMaxScaler() # 按照列
x_train_min_max = min_max_scaler.fit_transform(x_train) # 也可以fit(x_train).transform(x_train)
x_test_min_max = min_max_scaler.transform(x_test)
print('最小最大值缩放:\n训练集:\n{}'.format(x_train_min_max))
print('测试集:\n{}'.format(x_test_min_max))
# 即:x * (1\(max-min))- min,也就是,x * scale_ - min_
print(min_max_scaler.scale_) # 数据的缩放比例,即1/(max - min)
print(min_max_scaler.min_) # -min的min
输出:
最小最大值缩放:
训练集:
[[0. 0. 1. ]
[0.33333333 0. 0. ]
[1. 1. 0. ]]
[0.33333333 0.5 0.5 ]
[-0.33333333 -1.5 -1.5 ]
MaxAbsScale
将数据缩放到[-1, 1]
区间。
通过除以最大值的绝对值实现,能够保留原矩阵中的0元素的位置。
可以使用到已经分布在0左右的数据集中。
也可以使用到稀疏矩阵中。
# coding:utf-8
from sklearn import preprocessing
import numpy as np
x_train = np.array([[1., 3, 5],
[2, 3, 3],
[4, 5, 3], ])
x_test = [[-1., 1., 0.]]
max_abs_scaler = preprocessing.MaxAbsScaler().fit(x_train)
x_train_max_abs = max_abs_scaler.transform(x_train)
x_test_max_abs = max_abs_scaler.transform(x_test)
print('最大值绝对值缩放:\n训练集:\n{}\n测试集:\n{}'.format(x_train_max_abs, x_test_max_abs))
输出:
最大值绝对值缩放:
训练集:
[[0.25 0.6 1. ]
[0.5 0.6 0.6 ]
[1. 1. 0.6 ]]
测试集:
[[-0.25 0.2 0. ]]
3. standardization
使用scale
或者兼顾测试集的StandardScaler
(测试集数据用训练集的
、
缩放)
如果数据需要预处理为均值为0,标准差为1的数据,使用该方法。
# coding:utf-8
from sklearn import preprocessing
import numpy as np
x_train = np.array([[1., 3, 5],
[2, 3, 3],
[4, 5, 3], ])
x_test = [[-1., 1., 0.]]
print('训练集均值:{}'.format(x_train.mean(axis=0))) # axis=0, columns, axis=1, row
print('训练集标准差:{}'.format(x_train.std(axis=0)))
# 特征缩放,矩阵的每一个列向量的均值缩放为0,标准差为1
# 即,每一个列向量看成是是一个特征。
# (x - mu) / sigma
print('\n特征缩放:\n缩放为均值为0,标准差为1:')
x_scaled = preprocessing.scale(x_train)
print(x_scaled)
print('\n缩放后的均值:{}'.format(x_scaled.mean(axis=0))) # 均值为0
print('缩放后的标准差:{}'.format(x_scaled.std(axis=0))) # 标准差为1
# 标准缩放类
# 可以让测试集有和训练集一样的特征缩放
print('\n特征缩放:\n缩放为均值为0,标准差为1,且测试集和训练集同缩放变换:')
scaler = preprocessing.StandardScaler(with_mean=False,).fit(x_train)
print('训练集均值:{}'.format(scaler.mean_))
print('训练集标准差:{}'.format(scaler.scale_))
print('\n缩放后的训练集:\n{}'.format(scaler.transform(x_train)))
print('\n缩放后的测试集:\n{}'.format(scaler.transform(x_test)))
输出:
训练集均值:[2.33333333 3.66666667 3.66666667]
训练集标准差:[1.24721913 0.94280904 0.94280904]
特征缩放:
缩放为均值为0,标准差为1:
[[-1.06904497 -0.70710678 1.41421356]
[-0.26726124 -0.70710678 -0.70710678]
[ 1.33630621 1.41421356 -0.70710678]]
缩放后的均值:[-1.48029737e-16 1.48029737e-16 1.48029737e-16]
缩放后的标准差:[1. 1. 1.]
特征缩放:
缩放为均值为0,标准差为1,且测试集和训练集同缩放变换:
训练集均值:[2.33333333 3.66666667 3.66666667]
训练集标准差:[1.24721913 0.94280904 0.94280904]
缩放后的训练集:
[[-1.06904497 -0.70710678 1.41421356]
[-0.26726124 -0.70710678 -0.70710678]
[ 1.33630621 1.41421356 -0.70710678]]
缩放后的测试集:
[[-2.67261242 -2.82842712 -3.8890873 ]]
Process finished with exit code 0
pandas & numpy
原始数据:
# coding:utf-8
import numpy as np
import pandas as pd
data = pd.DataFrame(np.arange(16).reshape(4, 4))
print(data)
'''
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
'''
min max normalization:
# min max normalization
# method one
data_min_max = (data - data.min()) / (data.max() - data.min())
print(data_min_max)
'''
0 1 2 3
0 0.000000 0.000000 0.000000 0.000000
1 0.333333 0.333333 0.333333 0.333333
2 0.666667 0.666667 0.666667 0.666667
3 1.000000 1.000000 1.000000 1.000000
'''
# method two
data_min_max_02 = data.apply(lambda x: (x - np.min(x)) / (np.max(x) - np.min(x)))
print(data_min_max_02)
'''
结果同上
'''
mean normalization:
# mean normalization
# (x - mean)/(max - min)
# method one
data_mean_normalization = (data - data.mean()) / (data.max() - data.min())
print(data_mean_normalization)
'''
0 1 2 3
0 -0.500000 -0.500000 -0.500000 -0.500000
1 -0.166667 -0.166667 -0.166667 -0.166667
2 0.166667 0.166667 0.166667 0.166667
3 0.500000 0.500000 0.500000 0.500000
'''
# method two
data_mean_normalization_02 = data.apply(lambda x: (x - np.mean(x)) / (np.max(x) - np.min(x)))
print(data_mean_normalization_02)
'''
结果同上
'''
standardization:
# z score normalization, standardization
# method one
data_standardization = (data - data.mean()) / data.std()
print(data_standardization)
'''
0 1 2 3
0 -1.161895 -1.161895 -1.161895 -1.161895
1 -0.387298 -0.387298 -0.387298 -0.387298
2 0.387298 0.387298 0.387298 0.387298
3 1.161895 1.161895 1.161895 1.161895
'''
# method two
data_standardization_02 = data.apply(lambda x: (x - np.mean(x)) / np.std(x))
print(data_standardization_02)
'''
0 1 2 3
0 -1.341641 -1.341641 -1.341641 -1.341641
1 -0.447214 -0.447214 -0.447214 -0.447214
2 0.447214 0.447214 0.447214 0.447214
3 1.341641 1.341641 1.341641 1.341641
'''
# fixme 结果不一样
pandas 的mean()和numpy的mean()结果是一样的。
pandas的std()和numpy的std()结果不同。
可能是涉及到了对数据的标准差的计算公式不同。
样本标准差:
其中, 是样本 的样本均值。N为样本数量。
总体标准差:
其中, 为总体数据的均值。N为总体数量。
data_pd = pd.DataFrame([1, 2, 3]) # 样本标准差:1, 总体标准差:0.81
std_pd = data_pd.std()
std_np = data_pd.apply(lambda x: np.std(x))
print(std_pd)
print(std_np)
'''
0 1.0
dtype: float64
0 0.816497
dtype: float64
'''
也就是,pandas的std()
默认将数据当做样本数据计算,采用无偏估计。(N-1)
numpy的std()
默认将数据视为整体数据,而不是抽样的样本,计算的是总体标准差。(N)
ddof=0
总体标准差,ddof=1
样本标准差。- numpy的
std()
的ddof
默认为0。计算无偏估计标准差的参数是np.std(x, ddof=1)
。 - pandas的
std()
的ddof
默认为1。若x.std(ddof=0)
,计算的就是总体标准差。