先有鸡or先有蛋?浅谈数据拆分与特征缩放的顺序问题

数据挖掘环节中重要的两个环节,数据拆分与特征缩放,关于他们两个的先后顺序问题,我将在下文进行阐述

前些天在 100-Days-Of-ML-Code 上回答了一个关于数据拆分与特征缩放的顺序先后的一个issue,感觉挺有争议性的,故单独拎出来做下笔记说明。我的观点是:机器学习工程中,应该先进行数据划分,再进行特征缩放。出于严谨性,本篇文章是从机器学习-数据挖掘方面进行数据拆分与特征缩放的顺序问题阐述,同时也欢迎大家一起讨论这个问题。 

问题阐述

关于数据拆分与特征缩放的顺序先后问题,一般会在工程中遇到,具体表现为:

先数据拆分再特征缩放

from sklearn.preprocessing import StandardScaler,MinMaxScaler
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.1)
sc = StandardScaler()
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)

先数据缩放再数据拆分 

from sklearn.preprocessing import StandardScaler,MinMaxScaler
sc = StandardScaler()
X_transform = sc.fit_transform(X)
X_train,X_test,y_train,y_test = train_test_split(X_transform,y,test_size=0.1)

论点阐述

首先先来看下我们常用的两种 sklearn 上的特征缩放:StandardScaler()MinMaxScaler()

从图中可以看出StandardScalar涉及到了均值μ标准差σ,而MinMaxScaler则涉及到了最大值max最小值min。这些参数的取值都得考虑到全局样本的,什么意思呢?我们来看下两者的输出结果:

先数据拆分再特征缩放先数据缩放再数据拆分 

可以很明显看出,两种不同的操作顺序输出的数据是完全不同的,也就是说样本的分布是完全不同的(很重要!后面阐述要用到),那这种差异性在现实工程中会有什么影响?要解答这个问题,首先我们首先需要了解fit_transform()方法,fit_transform()你可以理解为fit()方法和transform()方法的pipeline,进行特征缩放时我们的顺序是

  1. 先fit获得相应的参数值(可以理解为获得特征缩放规则)
  2. 再用transform进行转换

fit_transform方法就是先执行fit()方法再执行transform()方法,所以每执行一次就会采用新的特征缩放规则,我们可以将训练集的特征缩放规则应用到测试集上,可以将测试集的特征缩放规则应用到训练集上(不过一般很少这么做),但是通过全部数据集(训练集+测试集)fit到的的特征缩放规则是没有模型训练意义的。

这里我们举一个例子:假设农业部要求我们用LR模型来对花类型进行分类,我们经过学习得到了一个LR模型,模型上线后,现在需要对新的花数据进行预测分类(此时我们可以把旧花数据看做训练集新花数据看做测试集):

  • 按照先数据拆分再特征缩放的做法是:先将旧花数据fit出特征缩放规则,接着将其transform到新花数据上,接着对应用旧花数据特征缩放规则的新花数据进行预测分类;
  • 按照先数据缩放再数据拆分的做法是:将新旧花数据合并为一个总数据集,接着对总数据集进行fit_transform操作,最后再把新花数据切分出来进行预测分类;

重点!!!

这时候问题来了,“我们经过学习得到了一个LR模型”,请问我们学习的数据是什么?旧花数据 OR 新旧花合并数据?答案肯定是旧花数据啊,更为详细地讲,是应用旧花数据特征缩放规则的旧花数据,这时候第二种做法的问题就出来了,我们这个LR模型是根据应用旧花数据特征缩放规则的旧花数据分布学习到的这条分类线

而此时你却将这条分类线去应用在应用新旧花数据特征缩放规则的新花数据上,根据上方我们得到的论点“两种不同的操作顺序输出的样本的分布是完全不同”,两种完全不同的分布,你用根据其中一种分布学习得到分类线对另一种分布来说是完全没有使用意义的,因为两者根本可以说是根据不同的数据学习而来的,所以有些时候第二种做法效果可能会很好也可能会很糟糕,这就像你拿牛数据学习的LR模型去预测花的分类一样。而机器学习的前身就是统计学,而统计学的一个样本基本原则就是样本同质性(homogenetic)。 

总结

>>> from sklearn.datasets import load_iris
>>> from sklearn.model_selection import train_test_split
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
>>> quantile_transformer = preprocessing.QuantileTransformer(random_state=0)
>>> X_train_trans = quantile_transformer.fit_transform(X_train)
>>> X_test_trans = quantile_transformer.transform(X_test)
>>> np.percentile(X_train[:, 0], [0, 25, 50, 75, 100]) 
array([ 4.3,  5.1,  5.8,  6.5,  7.9])

这里我贴的是sklearn的一段官方demo代码,可以看出sklearn的演示代码也是遵从先数据拆分再特征缩放的顺序进行的操作,先fit到X_train的特征缩放规则,再将其应用在X_test上,这也从一个小方面验证了我的观点吧(虽然我也不喜欢不严谨的举例论证方法)。所以综上所述,我的观点是在进行数据挖掘方面的工作时,面对特征缩放环节,应该先进行数据拆分再进行特征缩放。


才学疏浅,欢迎评论指导

欢迎前往我的个人小站:www.wengjj.ink

数据矿工学习
数据矿工学习

数据世界的矿工,关注机器学习的相关学习,从零开始记录入门学习笔记,帮助大家更好地步入AI世界

入门特征缩放数据划分
76
MarketAxess・量化研究院
Hi wengJJ, This is a popular top under discussion. Indeed separating the data first and then do normalization is more reasonable. But sometimes we indeed use all sample to fit Imputer, especially for some embedding task. In this case we can not only include more scenario in training, but also inject some noise in training to avoid over-fitting. What do you think? Thanks, Xiang
Thank you very much for your message. I don't know much about the embedding task, so I emphasized at the beginning of the article that "this article expounds the sequence of data splitting and feature scaling from the aspect of machine learning-data mining". Therefore, I can only say from the Imputer of sklearn that the Impter of sklearn is aimed at "Missing Data", which data does not exist in the beginning. This step is to create data, unlike the feature scaling step which is to modify the original data. The data created can be Shared by all, so it is possible to use the entire sample. The Imputer step is in the process of data cleaning. In the traditional data mining process, the process of data cleaning is before the feature scaling, so it does not conflict with my conclusion.
MarketAxess・量化研究院
回复wengJJ
That makes sense! And for embedding I am referring to something like word2vec, where we usually include all data to train the LabelEncoder so that we can consider all the words for embedding. But yes it doesn't conflict with your theory.
pipeline在机器学习领域到底指什么?刚进入这个行业,但是之前在书本上学习时从没见这个词的使用。大佬出来解释下,谢谢。
我们一般称pipeline为流水线或管道,你可以理解为将一系列步骤放到一起,运行时这些步骤就会按你设定的流程顺序运行的一种链式结构。
噢~理解成一条已经固定的流水线就行了。非常有帮助,大指拇。