结合 PCA、特征缩放和交叉验证,而不会泄露训练测试数据

机器算法验证 机器学习 主成分分析 交叉验证 scikit-学习 数据泄露
2022-03-23 12:28:26

用于交叉验证的sci-kit 学习文档说明了以下关于使用特征缩放和交叉验证的内容:

正如在训练中保留的数据上测试预测器很重要一样,预处理(如标准化、特征选择等)和类似的数据转换同样应该从训练集中学习并应用于保留的数据以进行预测

我理解这背后的原因是为了防止交叉验证期间训练集和测试集之间的信息泄漏,这可能导致对模型性能的乐观估计。

我想知道,如果我想在训练回归模型之前使用主成分分析来减少特征集的大小,并且 PCA 需要特征缩放才能有效,我如何将特征缩放链接到 PCA 以交叉验证回归,而不在交叉验证中的训练测试拆分之间引入数据泄漏?

4个回答

您需要将特征缩放、pca 和回归模型视为牢不可破的操作链(就好像它是单个模型一样),其中应用了交叉验证。自己编写代码非常棘手,但在sklearnvia中相当容易Pipeline管道对象是数据上的一系列运算符,被视为(并充当)看似单一的模型,在库中确认fit和范式。predict

为了不使用 scikit 管道的可能读者的利益:

  • 训练子集进行中心化和缩放不仅会产生中心化和缩放的训练数据,还会产生描述偏移量和缩放因子的向量。在预测新案例时,将此偏移量和比例应用于新案例,然后将得到的居中和缩放数据传递给主成分预测
  • 进而应用通过拟合训练数据确定的旋转。
  • 以此类推,直到达到最终预测。

对于任何可能偶然发现这个问题的人,我有一个使用 scikit-learn 的Pipeline的解决方案,正如接受的答案中所推荐的那样。下面是我用来解决我的问题的代码,链接在一起StandardScalerPCARidge回归到交叉验证的网格搜索中:

from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler

pipe = Pipeline([("scale", StandardScaler()),
                 ("reduce_dims", PCA()),
                 ("ridge", Ridge())
                ])


param_grid = dict(reduce_dims__n_components = [0.5, 0.75, 0.95],
                  ridge__alpha = np.logspace(-5, 5, 10),
                  ridge__fit_intercept = [True, False],
                 )

grid = GridSearchCV(pipe, param_grid=param_grid, cv=10)
grid.fit(X, y)

我遇到了一些管道问题(例如,如果我想应用我自己的自定义函数,这是一个真正的危险)所以这里是我使用的:

X_train, X_test, y_train, y_test = train_test_split(X, Y, stratify=Y, random_state=seed, test_size=0.2)
sc = StandardScaler().fit(X_train)
X_train = sc.transform(X_train)
X_test = sc.transform(X_test)
pca = PCA().fit(X_train)
X_train = pca.transform(X_train)
X_test = pca.transform(X_test)
eclf = SVC()
parameters_grid = {
                'C': (0.1, 1, 10)
                   }
grid_search = GridSearchCV(eclf, parameters_grid, cv=cv, refit='auc', return_train_score=True)
grid_search.fit(X_train, y_train)
best_model = eclf.set_params(**grid_search.best_params_).fit(X_train, y_train)
test_auc_score = roc_auc_score(y_test, best_model.predict(X_test))

我意识到它有点长,但很清楚你在做什么。