数据预处理与缩放

之前我们在接触监督学习时了解到,有一些算法(譬如神经网络SVM)对于数据的缩放非常敏感。因此,通常的做法是对数据集进行调节,使得数据表示更适合于这些算法。通常来说,这是对数据特征的一种简单的缩放和移动。

机器学习的理论实际上是起源于概率论与数理统计,接下来,我们来简单提几个相关概念,来帮助大家更好地理解接下来的要说的几种处理方法。

基础概念

中位数——对于一组数字来说,中位数指的是这样的数值x:有一半的数值小于x,另一半的数值大于x。如果数据集的数据个数是偶数,就取中间两个数值的平均数。

四分位数——按照四分之一的数据个数来划分数据集。较小四分位数指的是这样的数值x:有四分之一的数值小于x。较大四分位数指的是这样的数值x:有四分之一的数值大于x。

方差——衡量随机变量或一组数据的离散程度的度量。

异常值——在数据集之中的那些与众不同的数据点。

需要注意的是异常值并不一定是误差值或者错误值。譬如说想要统计某辆公交车上20名乘客的平均年龄,有一种情况就是其中19名乘客是处于20岁到30岁之间,但是有一名乘客的年龄是70岁,那么这名乘客的年龄就是属于与众不同的数据点,是异常值,而不是误差值。并且这个数值会影响最终的统计计算结果——拉高公交车上乘客的平均年龄。

预处理的四种类型

在这里插入图片描述
scikit-learn中一共提供了4种预处理方法,变换效果分别如上图所示,接下来,结合图像,我们来详细说一下这四种变换。
PS:如果大家不喜欢下面的枯燥理论就直接跳过吧,说实话,我觉得只要会使用,知道用于什么地方就够了。

StandardScaler(标准化):确保每个特征的平均值为0,方差为1,使特征值都位于同一量级。但这种缩放不能保证特征任何特定的最大值和最小值。(我曾经在讲解神经网络的时候进行过标准化的人工处理,有源代码,感兴趣的朋友可以去看下,可以帮助大家更好地理解。)

RobustScaler(剔除异常值):RobustScaler也是一种标准化,工作原理与StandardScaler类似,确保每个特征的统计属性都处于同一范围。但是RobustScaler使用的是中位数四分位数,而不是平均值和方差。这样RobustScaler会忽略与其它点有很大不同的数据点(异常值),减少异常值造成的麻烦。

MinMaxScaler(归一化):MinMaxScaler移动数据,使得所有特征都刚好位于0到1之间。对于二维数据集来说,所有的数据都包含在x轴0到1与y轴0到1组成的矩形中。(同样,我在讲解SVM的时候进行过归一化的人工处理,也有相关源代码。)

Normalizer(正则化,有些地方也叫做归一化):Normalizer用到的是一种完全不同的缩放方法。它对每个数据点进行缩放,使得特征向量的欧式长度等于1。通过上面的第四幅小图可以看出:它将一个数据点投射到半径为1的圆上(对于更高维度的情况是球面)。这意味着每个数据点的缩放比例都不相同。如果只有数据的方向(或角度)是重要的,而特征向量的长度无关紧要,那么通常会使用这种归一化。

应用数据转换

接下来我们用数据集胎儿健康分类SVM算法来实际使用下,看看效果。(数据以及相关算法我们都在之前讲解的SVM中详细地讲过了,感兴趣的朋友可以点击超链接去看下,这里就不在赘述了。)

首先是导入数据:

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
import numpy as np
import winreg
###################
real_address = winreg.OpenKey(winreg.HKEY_CURRENT_USER,r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders',)
file_address=winreg.QueryValueEx(real_address, "Desktop")[0]
file_address+='\\'
file_origin=file_address+"\\源数据-分析\\fetal_health.csv"
health=pd.read_csv(file_origin)
#设立桌面绝对路径,读取源数据文件,这样将数据直接下载到桌面上就可以了,省得还要去找
###################

划分训练集和测试集,并进行建模,精度评分:

1
2
3
4
5
6
7
8
9
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn.metrics import accuracy_score
train=health.drop(["fetal_health"],axis=1)
X_train,X_test,y_train,y_test=train_test_split(train,health["fetal_health"],random_state=1)
###考虑到接下来可能需要进行其他的操作,所以定了一个随机种子,保证接下来的train和test是同一组数
svm=svm.SVC(C=1,kernel="rbf",decision_function_shape="ovr")
svm.fit(X_train,y_train)
print("SVM待测模型评分:"+str(accuracy_score(y_test,svm.predict(X_test))))

结果如下所示:

在这里插入图片描述

接下来我们对数据进行缩放再进行建模评分,看看模型精度有什么变化:

1
2
3
4
5
6
7
from sklearn.preprocessing import StandardScaler###标准化
standard=StandardScaler()
standard.fit(X_train)###使用fit方法拟合缩放器,并将其应用于训练数据,其实就和之前学过的算法一样,先用fit去训练数据,适应数据
X_train_scaled=standard.transform(X_train)####对训练数据进行实际缩放,也是类似之前学习的训练过程,在,fit适用数据之后,再用transfrom去同等变换X_train,X_test
X_test_scaled=standard.transform(X_test)###注意测试集相对训练集来说移动必须是一致的,因为变换后数量级是不同的,但是要保证数据的分布形状要完全相同
svm.fit(X_train_scaled,y_train)
print("标准化后SVM模型评分:"+str(accuracy_score(y_test,svm.predict(X_test_scaled))))

在这里插入图片描述

可以看到模型精度较之前有了提升。

整个处理过程与之前算法训练模型类似。首先使用fit方法拟合缩放器(scaler),并将其应用于训练数据。然后为了应用刚刚学习的数据(即对训练数据进行实际缩放),我们使用缩放器的transform方法。最后为了将SVM应用到缩放后的数据上,还需要对测试集进行变换。

需要注意的是,为了让监督模型能够在测试集上运行,对训练集和测试集应用完全相同的变换是很重要的。因为刻度数值可以不一样,但是必须要保证测试集与训练集的数据分布是一样的(可以结合上面的散点图来看一下)

替代方法

通常来说,想要在某个数据集上fit一个模型,然后再将其transform,是一个非常常见的过程。但是可以用比先调fit再调transform更高效的方法来计算。对于这种使用场景,所有具有transform方法的模型也都有一个fit_transform方法,代码如下所示:

1
2
3
4
from sklearn.preprocessing import StandardScaler
standard=StandardScaler()
X_train_scaled=standard.fit(X_train).transform(X_train)###原方法先fit后transform
X_train_scaled=standard.fit_transform(X_train)###结果相同,但计算更加高效

虽然fit_transform不一定对所有模型都更加高效,但在尝试变换训练集时,使用这一方法仍然是很少的。
PS:最主要是看起来上了点档次

小结

对于任何一种类型的数据集或者是一个算法来说,没有绝对正确的预处理算法。预处理方法,数据集与建模算法这三者之间永远都不会存在绑定关系。 譬如我们分别用SVM和神经网络来测试上述四种预处理算法,代码及结果如下所示:

SVM:

1
2
3
4
5
6
7
8
for i in [StandardScaler(),RobustScaler(),MinMaxScaler(),Normalizer()]:
scaled=i
i.fit(X_train)
X_train_scaled=i.transform(X_train)
X_test_scaled=i.transform(X_test)
svm.fit(X_train_scaled,y_train)
score=accuracy_score(y_test,svm.predict(X_test_scaled))
print(str(i)+"处理后得分:"+str(score))

在这里插入图片描述

神经网络(MLP):

1
2
3
4
5
6
7
8
9
10
from sklearn.neural_network import MLPClassifier#多层感知机-MLP/神经网络
mlp=MLPClassifier(solver="lbfgs",random_state=1,max_iter=100000).fit(X_train,y_train)
for i in [StandardScaler(),RobustScaler(),MinMaxScaler(),Normalizer()]:
scaled=i
i.fit(X_train)
X_train_scaled=i.transform(X_train)
X_test_scaled=i.transform(X_test)
mlp.fit(X_train_scaled,y_train)
score=accuracy_score(y_test,mlp.predict(X_test_scaled))
print(str(i)+"处理后得分:"+str(score))

在这里插入图片描述

可以看出,对于同一个数据集应用不同的算法,选择的处理方法是不一样的。对于SVM来说,用标准化处理精度会高一些,而对于神经网络来说,用归一化处理效果会更好。而如果我们对其它数据集进行机器学习的话,那么就会存在其它的选择。所以对于一份没有接触过的数据集来说,如果时间允许的话,可以尝试各种各样的组合,来去搭建精度最高的模型。

最后,虽然数据缩放不涉及任何复杂的数学,但良好的做法仍是使用scikit_learn提供的缩放机制,而不是自己人工实现它们,因为即使在这些简单的计算中也容易犯错。

有很多地方做的不是很好,欢迎网友来提出建议,也希望可以遇到些朋友来一起交流讨论。