【问题标题】:Target transformation and feature selection in scikit-learnscikit-learn 中的目标转换和特征选择
【发布时间】:2020-01-29 00:47:56
【问题描述】:

我在 scikit-learn 中使用 RFECV 进行特征选择。我想将简单线性模型(X,y)的结果与对数转换模型(使用X, log(y))的结果进行比较

简单模型RFECVcross_val_score 提供相同的结果(我们需要将所有折叠的交叉验证的平均分数与所有特征的 RFECV 的分数进行比较:0.66 = 0.66,没问题,结果是可靠)

日志模型问题:似乎RFECV 没有提供转换y 的方法。这种情况下的分数是0.550.53。不过这是意料之中的,因为我必须手动应用np.log 来拟合数据:log_seletor = log_selector.fit(X,np.log(y))。这个 r2 分数是针对y = log(y),没有inverse_func,而我们需要的是一种在log(y_train) 上拟合模型并使用exp(y_test) 计算分数的方法。或者,如果我尝试使用TransformedTargetRegressor,我会收到代码中显示的错误:分类器没有公开“coef_”或“feature_importances_”属性

如何解决问题并确保特征选择过程可靠?

from sklearn.datasets import make_friedman1
from sklearn.feature_selection import RFECV
from sklearn import linear_model
from sklearn.model_selection import cross_val_score
from sklearn.compose import TransformedTargetRegressor
import numpy as np

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)
estimator = linear_model.LinearRegression()
log_estimator = TransformedTargetRegressor(regressor=linear_model.LinearRegression(),
                                                func=np.log,
                                                inverse_func=np.exp)
selector = RFECV(estimator, step=1, cv=5, scoring='r2')
selector = selector.fit(X, y)
###
# log_selector = RFECV(log_estimator, step=1, cv=5, scoring='r2')
# log_seletor = log_selector.fit(X,y) 
# #RuntimeError: The classifier does not expose "coef_" or "feature_importances_" attributes
###
log_selector = RFECV(estimator, step=1, cv=5, scoring='r2')
log_seletor = log_selector.fit(X,np.log(y))

print("**Simple Model**")
print("RFECV, r2 scores: ", np.round(selector.grid_scores_,2))
scores = cross_val_score(estimator, X, y, cv=5)
print("cross_val, mean r2 score: ", round(np.mean(scores),2), ", same as RFECV score with all features") 
print("no of feat: ", selector.n_features_ )

print("**Log Model**")
log_scores = cross_val_score(log_estimator, X, y, cv=5)
print("RFECV, r2 scores: ", np.round(log_selector.grid_scores_,2))
print("cross_val, mean r2 score: ", round(np.mean(log_scores),2)) 
print("no of feat: ", log_selector.n_features_ )

输出:

**Simple Model**
RFECV, r2 scores:  [0.45 0.6  0.63 0.68 0.68 0.69 0.68 0.67 0.66 0.66]
cross_val, mean r2 score:  0.66 , same as RFECV score with all features
no of feat:  6

**Log Model**
RFECV, r2 scores:  [0.39 0.5  0.59 0.56 0.55 0.54 0.53 0.53 0.53 0.53]
cross_val, mean r2 score:  0.55
no of feat:  3

【问题讨论】:

    标签: python scikit-learn cross-validation feature-selection rfe


    【解决方案1】:

    此问题的一种解决方法是确保coef_ 属性暴露给特征选择模块RFECV。所以基本上你需要扩展TransformedTargetRegressor 并确保它公开属性coef_。我创建了一个子类,它将从TransformedTargetRegressor 扩展,并且还公开coef_,如下所示。

    from sklearn.linear_model import LinearRegression
    from sklearn.datasets import make_friedman1
    from sklearn.feature_selection import RFECV
    from sklearn import linear_model
    from sklearn.model_selection import cross_val_score
    from sklearn.compose import TransformedTargetRegressor
    import numpy as np
    
    class myestimator(TransformedTargetRegressor):
    
        def __init__(self,**kwargs):
            super().__init__(regressor=LinearRegression(),func=np.log,inverse_func=np.exp)
    
        def fit(self, X, y, **kwargs):
            super().fit(X, y, **kwargs)  
            self.coef_ = self.regressor_.coef_
            return self
    

    然后您可以使用myestimator 创建您的代码,如下所示:

    X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)
    estimator = linear_model.LinearRegression()
    log_estimator = myestimator(regressor=LinearRegression(),func=np.log,inverse_func=np.exp)
    
    selector = RFECV(estimator, step=1, cv=5, scoring='r2')
    selector = selector.fit(X, y)
    log_selector = RFECV(log_estimator, step=1, cv=5, scoring='r2')
    log_seletor = log_selector.fit(X,y) 
    

    我已经运行了你的示例代码并显示了结果。

    样本输出

    print("**Simple Model**")
    print("RFECV, r2 scores: ", np.round(selector.grid_scores_,2))
    scores = cross_val_score(estimator, X, y, cv=5)
    print("cross_val, mean r2 score: ", round(np.mean(scores),2), ", same as RFECV score with all features") 
    print("no of feat: ", selector.n_features_ )
    
    print("**Log Model**")
    log_scores = cross_val_score(log_estimator, X, y, cv=5)
    print("RFECV, r2 scores: ", np.round(log_selector.grid_scores_,2))
    print("cross_val, mean r2 score: ", round(np.mean(log_scores),2)) 
    print("no of feat: ", log_selector.n_features_ )
    
    
    **Simple Model**
    RFECV, r2 scores:  [0.45 0.6  0.63 0.68 0.68 0.69 0.68 0.67 0.66 0.66]
    cross_val, mean r2 score:  0.66 , same as RFECV score with all features
    no of feat:  6
    **Log Model**
    RFECV, r2 scores:  [0.41 0.51 0.59 0.59 0.58 0.56 0.54 0.53 0.55 0.55]
    cross_val, mean r2 score:  0.55
    no of feat:  4
    

    希望这会有所帮助!

    【讨论】:

    • 谢谢!似乎是解决它的一种方法,但我将如何导入修改后的代码? RFECV 本身使用 RFE,所以事情变得有点混乱。另外,你的样本输出只是我假设的一个样本。正确的?你实际上并没有运行代码?因为这些数字没有意义。第二个应该在最后给我 0.55 而不是 0.66..
    • 我不再修改多个文件,而是让它变得更简单了!您只需将本地系统中的 rfe.py 文件(可在 /sklearn/feature_selection/rfe.py 中找到)替换为我在 github gist 中发布的文件。
    • 谢谢。这可能有效,但并不理想,因为它会使我的代码不可移植且不可重现。在不接触 Scikit 源的情况下还能做其他事情吗?
    • 是的,这是一个有效的观点,我对代码进行了必要的更改。现在您不必担心接触源代码,而且它非常易于移植和重现。
    • 谢谢@Parthasarathy。我得到了两个正确答案。现在会投票
    【解决方案2】:

    您需要做的就是将这些属性添加到TransformedTargetRegressor

    class MyTransformedTargetRegressor(TransformedTargetRegressor):
        @property
        def feature_importances_(self):
            return self.regressor_.feature_importances_
    
        @property
        def coef_(self):
            return self.regressor_.coef_
    

    然后在你的代码中,使用它:

    log_estimator = MyTransformedTargetRegressor(regressor=linear_model.LinearRegression(),
                                                 func=np.log,
                                                 inverse_func=np.exp)
    

    【讨论】:

      猜你喜欢
      • 2018-02-24
      • 2018-06-01
      • 2014-05-22
      • 2013-04-30
      • 1970-01-01
      • 2014-11-05
      • 2013-03-07
      • 2020-05-01
      相关资源
      最近更新 更多