使用scikit-learn中的管道扩展数据:StandardScaler与RobustScaler

时间:2018-03-13 18:01:39

标签: python machine-learning scikit-learn logistic-regression

我想使用GridSearchCV来确定具有L1正则化的逻辑回归中的最优正则化参数“C”。我还想扩展/标准化我的输入功能。

在执行数据泄漏的交叉验证结果之前,使用单个转换缩放整个训练数据集:在交叉验证中,训练数据集被分为k个折叠,每个折叠被视为验证数据集,而其他人是训练折叠。但是,如果在整个训练数据集的交叉验证之前完成标准化,则每个折叠(包括验证折叠)将使用从整个训练数据集计算的参数(例如,平均值和标准偏差)进行缩放,所以在某种程度上,训练折叠总是“知道一些关于验证折叠”。

因此,缩放数据的正确方法是分别计算和应用每个交叉验证折叠的缩放(即,在内部训练折叠上,在每次迭代中保持验证折叠)。 在scikit-learn中,可以使用管道来完成。

我实现了一个测试用例,以查看两种方法之间的差异(“不正确的缩放”与“使用管道进行适当的缩放”),并且在使用StandardScaler时,无论方法如何,得到的回归系数都是相同的,我发现令人惊讶。但是,使用RobustScaler时,结果系数不同。

为什么“流水线”缩放会对RobustScaler产生影响,但对于StandardScaler却没有?

谢谢!

这是我的测试用例:

import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.datasets import load_breast_cancer

# Choose between the two scalers:
# scaler = RobustScaler()
scaler = StandardScaler()  

C_values = [0.001, 0.01, 0.05, 0.1, 1., 100.]

cancer = load_breast_cancer()
X, y = cancer.data, cancer.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

###########################################
# Version A: Proper scaling with pipeline #
###########################################

param_grid = {'logisticregression__C': C_values}

logReg = LogisticRegression(fit_intercept=True, 
                            penalty='l1', 
                            solver='liblinear', 
                            tol=0.0001, 
                            max_iter=1000, 
                            random_state=0)

# Create a pipeline that scales, then runs logistic regression
pipeline = make_pipeline(scaler, logReg)

vA = GridSearchCV(pipeline, param_grid=param_grid,
                     scoring='roc_auc', cv=10, refit=True)
vA.fit(X_train, y_train)

# Get coefficients
coefA = vA.best_estimator_.named_steps['logisticregression'].coef_

###############################
# Version B: Improper scaling #     
###############################

param_grid = {'C': C_values}

X_train_scaled = scaler.fit_transform(X_train)

vB = GridSearchCV(logReg, param_grid=param_grid,
                     scoring='roc_auc', cv=10, refit=True)
vB.fit(X_train_scaled, y_train)

# Get coefficients
coefB = vB.best_estimator_.coef_


# Compare coefficients
# (Assertion will pass for StandardScaler, but 
# fail for RobustScaler)
assert np.array_equal(coefA, coefB)

1 个答案:

答案 0 :(得分:0)

首先,这里只是一个共同发生,因为你选择的random_statecv,StandardScaler不会改变coef_的值。如果您将cv=10更改为cv = 3或4并删除random_state,则还会为StandardScaler获取不同的coef_值。

现在解释一下:

你看,第一种方法中要观察的线是:

vA.fit(X_train, y_train)

现在vA是一个gridsearch,它会通过将X_train,y_train分成更多的训练和测试并找到最佳参数然后拟合整个X_train, y_train来进行交叉验证。这意味着管道将适合整个数据。因此,使用StandardScaler或RobustScaler并不重要。

现在在方法2中你正在做:

X_train_scaled = scaler.fit_transform(X_train)

因此,您在两种方法中使用缩放器上的相同数据。这两种方法中的缩放器将适合完全相同的数据,并且可以学习完全相同的scale_mean_或其他属性。

因此,请检查完全相同的LogisticRegression是否适合。

在你的方法1中执行此操作:

>> print(vA.best_params_)
#Output: {'logisticregression__C': 1.0}

这方法2:

>> print(vB.best_params_)
#Output: {'C': 1}   for StandardScaler
#Output: {'C': 0.1}   for RobustScaler

所以你看,coef_的差异是由于LogReg中C的差异造成的。 grid_search在StandardScaler中找到的C在两种方法中都相同(等于1.0),但不适用于RobustScaler。

因此,GridSearchCV中发生的内部拆分会传递给RobustScaler,后者会对数据进行不同的缩放,因此找到不同的C值。