Sklearn RFE、管道和交叉验证

Sklearn RFE, pipeline and cross validation

我正在尝试弄清楚如何使用 RFE 解决回归问题,并且我正在阅读一些教程。

我找到了一个关于如何使用 RFECV 自动 select 理想数量特征的例子,它是这样的:

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold
from sklearn.feature_selection import RFECV


rfecv = RFECV(estimator=RandomForestClassifier(random_state=101), step=1, cv=StratifiedKFold(10), scoring='accuracy')
rfecv.fit(X, target)
print(np.where(rfecv.support_ == False)[0])

我觉得这很简单。

然而,我正在检查如何使用 RFE 对象做同样的事情,但为了包括交叉验证,我只找到了涉及使用管道的解决方案,例如:

X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# create pipeline
rfe = RFE(estimator=DecisionTreeRegressor(), n_features_to_select=5)
model = DecisionTreeRegressor()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# evaluate model
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(pipeline, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1, error_score='raise')

# report performance
print(f'MAE: {mean(n_scores):.3f}')

我不确定这里到底发生了什么。 pipeline用于排队RFE算法和第二个DecisionTreeRegressor(模型)。 如果我没记错的话,这个想法是对于交叉验证中的每次迭代,执行 RFE,所需数量的最佳特征是 selected,然后第二个模型是 运行只使用那些功能。 但是 how/when RFE 是否将有关哪些功能已 selected 的信息传递给 DecisionTreeRegressor?它甚至发生了吗,或者代码是否缺少这一部分?

嗯,首先,让我们指出 RFECV 和 RFE 在您的脚本中做两个独立的工作:前者选择最佳数量的特征,而后者选择最重要的五个特征(或者, 5 个特征的最佳组合,考虑到它们对 DecisionTreeRegressor 的重要性)。

回到你的问题:“RFE是什么时候将选择了哪些特征的信息传递给决策树的?”值得注意的是,RFE 并没有明确告诉 Decision Tree 选择了哪些特征。简单地说,它以一个矩阵作为输入(训练集),并根据 n_features_to_select=N 参数将其转换为 N 列的矩阵。 该矩阵(即转换后的训练集)与目标变量一起作为输入传递给决策树,目标变量 return 是一个可用于预测未见实例的拟合模型。

让我们深入研究一个分类示例:

""" Import dependencies and load data """
import numpy as np
import pandas as pd

from sklearn.datasets import load_breast_cancer
from sklearn.feature_selection import RFE
from sklearn.metrics import precision_score
from sklearn.tree import DecisionTreeClassifier

X, y = load_breast_cancer(return_X_y=True)
rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=2)

我们现在已经加载了 breast_cancer 数据集并实例化了一个 RFE 对象(我使用了 DecisionTreeClassifier,但也可以使用其他算法)。

要了解如何在管道中处理训练数据,让我们从一个手动示例开始,该示例说明如果在其“基本步骤”中分解管道将如何工作:

from sklearn.model_selection import train_test_split

def test_and_train(X, y, random_state):
    # For simplicity, let's use 80%-20% splitting
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=random_state)

    # Fit and transform the training data by applying Recursive Feature Elimination
    X_train_transformed = rfe.fit_transform(X_train, y_train)
    # Transform the testing data to select the same features
    X_test_transformed = rfe.transform(X_test)  

    print(X_train[0:3])
    print(X_train_transformed[0:3])
    print(X_test_transformed[0:3])

    # Train on the transformed trained data
    fitted_model = DecisionTreeClassifier().fit(X_train_transformed, y_train)

    # Predict on the transformed testing data
    y_pred = fitted_model.predict(X_test_transformed)

    print('True labels: ', y_test)
    print('Predicted labels:', y_pred)

    return y_test, y_pred

precisions = list() # to store the precision scores (can be replaced by any other evaluation measure)

y_test, y_pred = test_and_train(X, y, 42)
precisions.append(precision_score(y_test, y_pred))

y_test, y_pred = test_and_train(X, y, 84)
precisions.append(precision_score(y_test, y_pred))

y_test, y_pred = test_and_train(X, y, 168)
precisions.append(precision_score(y_test, y_pred))

print('Average precision:', np.mean(precisions))
"""
Average precision: 0.92
"""

在上面的脚本中,我们创建了一个函数,给定一个数据集 X 和一个目标变量 y

  1. 按照 80%-20% 的拆分规则创建训练集和测试集。
  2. 使用 RFE 转换它们(即,选择最好的 2 个特征,如前一个代码片段中指定的那样)。在 RFE 上调用 fit_transform 时,它会运行递归特征消除,并且 它会在其对象状态 中保存有关所选特征的信息。要知道哪些是选定的功能,请调用 rfe.support_注意:在测试集上只执行transform,所以rfe.support_中的特征用来过滤掉其他特征测试集。
  3. 适合模型和 return 元组(y_test、y_pred)。

y_testy_pred 可用于分析模型的性能,例如其精度。 精度保存在数组中,重复3次。 最后,我们打印平均精度。

我们模拟了一个交叉验证过程,通过将原始数据在各自的训练和测试集中拆分 3 次,拟合模型,计算并平均其在三个折叠中的性能(即精度)。 使用 RepeatedKFold 验证可以简化此过程:

from sklearn.model_selection import RepeatedKFold

precisions = list()
rkf = RepeatedKFold(n_splits=2, n_repeats=3, random_state=1)

for train_index, test_index in rkf.split(X, y):
    # print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]
    
    X_train_transformed = rfe.fit_transform(X_train, y_train)
    X_test_transformed = rfe.transform(X_test)
    
    fitted_model = DecisionTreeClassifier().fit(X_train_transformed, y_train)
    y_pred = fitted_model.predict(X_test_transformed)

    precisions.append(precision_score(y_test, y_pred))

print('Average precision:', np.mean(precisions))
"""
Average precision: 0.93
"""

甚至更进一步的管道:

from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

rkf = RepeatedKFold(n_splits=2, n_repeats=3, random_state=1)
pipeline = Pipeline(steps=[('s',rfe),('m',DecisionTreeClassifier())])
precisions = cross_val_score(pipeline, X, y, scoring='precision', cv=rkf)

print('Average precision:', np.mean(precisions))
"""
Average precision: 0.93
"""

综上所述,当原始数据传递给Pipeline时,后者:

  1. 在训练和测试数据中拆分它;
  2. 对训练数据调用 RFE.fit_transform()
  3. 对测试数据应用RFE.transform(),使其包含相同的特征;
  4. 对训练数据调用 estimator.fit() 以拟合(即训练)模型;
  5. 调用 estimator.predict() 测试数据进行预测。
  6. 将预测值与实际值进行比较,并在内部保存性能结果(您传递给 scoring 参数的结果)。
  7. 对交叉验证过程中的每个拆分重复步骤 1-6

在程序结束时,有人可以访问性能结果并在各个折叠中取平均值。