机器学习系列(九)——数据归一化及Sklearn中的Scaler
数据归一化
在之前的knn算法中,我们使用的是原始数据计算距离,但在一些情况下这样会导致一些问题,继续用肿瘤分类的例子:
/ | 肿瘤大小(厘米) | 发现时间(天) |
---|---|---|
样本1 | 1 | 200=0.55年 |
样本2 | 5 | 100=0.27年 |
该例子中,样本1和样本二的距离被时间主导,尽管肿瘤大小差5倍之多,但在欧拉距离中和天数相比造成的差距还是微不足道,这是不符合实际的。如果用年来衡量时间,又会发现距离被肿瘤大小主导。因此,如果不进行相关处理的话,直接计算的距离很可能是有偏差的,不能非常好的同时反映样本中每一个特征的重要程度。于是一般都要对数据进行归一化处理。
所谓数据归一化处理就是将所有数据都映射到同一尺度。
最值归一化
最常用的一种数据归一化方法叫最值归一化,最值归一化(normalization)把所有数据映射到0-1之间:
import numpy as np
import matplotlib.pyplot as plt
x=np.random.randint(0,100,size=100)
(x-np.min(x))/(np.max(x)-np.min(x))
最值归一化适用于分布有明显边界的情况,即特征的取值范围是在一定区间内的,比如考试分数在0-100分之间;RGB图像像素点取值在0-255之间。但同时该方法也有很大的缺点,就是受极端数据值(outlier)影响比较大,比如工资就不是一个有明显边界的特征,绝大部分人月薪0-3w,而有些人收入极其高,月薪100w甚至更高,这样往0-1之间映射会有很大误差。相应的一个改进的归一化方法是均值方差归一化。
均值方差归一化
均值方差归一化是把所有数据归一到均值为0方差为1的分布中:
这样归一化之后数据并不一定在0-1之间,但是所有数据归一化后均值为0,方差为1。这种归一化方法适用于数据分布没有明显的边界的情况,即有可能存在极端数据值的情况。当然其实在有明显边界时该方法表现也很好,所以建议使用此归一化方法。
x2=np.random.randint(0,100,(50,2))
x2=np.array(x2,dtype=float)
x2[:,0] = (x2[:,0]-np.mean(x2[:,0]))/np.std(x2[:,0])
x2[:,1] = (x2[:,1]-np.mean(x2[:,1]))/np.std(x2[:,1])
测试数剧集归一化
训练数剧集使用均值方差归一化处理后进行训练,但这就有一个问题,测试数剧集如何归一化呢?要注意的是,测试数剧集使用均值方差归一化时要使用训练集的均值和方差。原因是测试数据是模拟真实环境的,对数据的归一化也是算法的一部分,真实环境并不一定能得到所有测试。因此需要保存训练数剧集得到的均值和方差。
scikit-learn中专门对数据进行归一化处理的类叫做Scaler。
scikit-learn中的StandardScaler
skearn中封装的有数据预处理模块可以对数据进行归一化处理。
from sklearn import datasets
iris = datasets.load_iris()
x=iris.data
y=iris.target
'''归一化之前先划分训练集和数剧集'''
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(iris.data,iris.target,random_state=666,test_size = 0.2)
standardscaler.mean_#四个特征相应均值(由用户传入变量计算出来的变量)
standardscaler.std_#version19后被弃用
standardscaler.scale_#相当于标准差,用于描述数据分布离散范围
x_train_standard=standardscaler.transform(x_train)#返回归一化后的矩阵,x_train本身没变
'''此时只需要传入测试集即可利用训练集的保存信息进行测试集的归一化'''
x_test_standard = standardscaler.transform(x_test)
'''接下来使用归一化的数据进行knn分类'''
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier(n_neighbors=3)
knn_clf.fit(x_train_standard,y_train)
knn_clf.score(x_test_standard,y_test)
out:1
当然也可以自己模拟sklearn中的功能,实现均值方差标准化,在play_Ml模块中新建preprocessing.py文件:
'''preprocessing.py'''
import numpy as np
class StandardScaler:
def __init__(self):
self.mean_ = None
self.scale_ = None
def fit(self,x):
'''根据训练数剧集x获得数据均值和方差'''
assert x.ndim == 2,"must be 2"
self.mean_ = np.array([np.mean(x[:,i]) for i in range(x.shape[1])])
self.scale_ = np.array([np.std(x[:,i]) for i in range(x.shape[1])])
return self
def transform(self,x):
'''将x根据standardScaler进行均值方差归一化处理'''
assert x.ndim == 2,"must be 2"
assert self.mean_ is not None and self.scale_ is not None,\
"must fit before tranform!"
assert x.shape[1] == len(self.mean_),"feature number if x must be equal to mean_ and std_"
resX = np.empty(shape=x.shape,dtype=float)
for col in range(x.shape[1]):
resX[:,col] = (x[:,col] - self.mean_[col])/self.scale_[col]
return resX
调用模块中函数模拟sklearn中的功能:
1
测试归一化后的准确率:
2