具有递归特征的网格搜索在scikit-learn管道中消除会返回错误

时间:2016-04-17 23:12:06

标签: python scikit-learn

我正在尝试使用scikit-learn在管道中链接网格搜索和递归特征消除。

带有“裸”分类器的GridSearchCV和RFE工作正常:

from sklearn.datasets import make_friedman1
from sklearn import feature_selection
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVR

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)

est = SVR(kernel="linear")

selector = feature_selection.RFE(est)
param_grid = dict(estimator__C=[0.1, 1, 10])
clf = GridSearchCV(selector, param_grid=param_grid, cv=10)
clf.fit(X, y)

将分类器放在管道中会返回错误:RuntimeError:分类器不公开“coef_”或“feature_importances_”属性

from sklearn.datasets import make_friedman1
from sklearn import feature_selection
from sklearn import preprocessing
from sklearn import pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVR

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)

est = SVR(kernel="linear")

std_scaler = preprocessing.StandardScaler()
pipe_params = [('std_scaler', std_scaler), ('clf', est)]
pipe = pipeline.Pipeline(pipe_params)

selector = feature_selection.RFE(pipe)
param_grid = dict(estimator__clf__C=[0.1, 1, 10])
clf = GridSearchCV(selector, param_grid=param_grid, cv=10)
clf.fit(X, y)

修改

我意识到我并不清楚这个问题。这是更清晰的片段:

from sklearn.datasets import make_friedman1
from sklearn import feature_selection
from sklearn import pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVR

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)

# This will work
est = SVR(kernel="linear")
selector = feature_selection.RFE(est)
clf = GridSearchCV(selector, param_grid={'estimator__C': [1, 10]})
clf.fit(X, y)

# This will not work
est = pipeline.make_pipeline(SVR(kernel="linear"))
selector = feature_selection.RFE(est)
clf = GridSearchCV(selector, param_grid={'estimator__svr__C': [1, 10]})
clf.fit(X, y)

正如您所看到的,唯一的区别是将估算工具放在管道中。但是,管道隐藏了“coef_”或“feature_importances_”属性。问题是:

  1. 在scikit-learn中有一个很好的处理方法吗?
  2. 如果没有,是出于任何原因需要这种行为吗?
  3. EDIT2:

    根据@Chris

    提供的答案更新了工作代码段
    from sklearn.datasets import make_friedman1
    from sklearn import feature_selection
    from sklearn import pipeline
    from sklearn.grid_search import GridSearchCV
    from sklearn.svm import SVR
    
    
    class MyPipe(pipeline.Pipeline):
    
        def fit(self, X, y=None, **fit_params):
            """Calls last elements .coef_ method.
            Based on the sourcecode for decision_function(X).
            Link: https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/pipeline.py
            ----------
            """
            super(MyPipe, self).fit(X, y, **fit_params)
            self.coef_ = self.steps[-1][-1].coef_
            return self
    
    
    X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)
    
    # Without Pipeline
    est = SVR(kernel="linear")
    selector = feature_selection.RFE(est)
    clf = GridSearchCV(selector, param_grid={'estimator__C': [1, 10, 100]})
    clf.fit(X, y)
    print(clf.grid_scores_)
    
    # With Pipeline
    est = MyPipe([('svr', SVR(kernel="linear"))])
    selector = feature_selection.RFE(est)
    clf = GridSearchCV(selector, param_grid={'estimator__svr__C': [1, 10, 100]})
    clf.fit(X, y)
    print(clf.grid_scores_)
    

2 个答案:

答案 0 :(得分:4)

您使用管道时遇到问题。

管道的工作原理如下:

当你调用.fit(x,y)等时,第一个对象应用于数据。如果该方法公开.transform()方法,则应用此方法并将此输出用作下一个阶段的输入。

管道可以将任何有效模型作为最终对象,但所有以前的模型必须公开.transform()方法。

就像一个管道 - 你输入数据,管道中的每个对象都接受前一个输出并对其进行另一次转换。

我们可以看到,

http://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html#sklearn.feature_selection.RFE.fit_transform

RFE公开了一种转换方法,因此应该包含在管道本身中。 例如。

some_sklearn_model=RandomForestClassifier()
selector = feature_selection.RFE(some_sklearn_model)
pipe_params = [('std_scaler', std_scaler), ('RFE', rfe),('clf', est)]

您的尝试有一些问题。 首先,您正在尝试扩展一部分数据。想象一下,我有两个分区[1,1],[10,10]。如果我按分区的平均值进行标准化,则会丢失我的第二个分区明显高于平均值的信息。你应该在开始时而不是在中间进行扩展。

其次,SVR没有实现转换方法,您不能将它作为非最终元素合并到管道中。

RFE采用适合数据的模型,然后评估每个特征的权重。

修改

如果您愿意,可以通过将sklearn管道包装在您自己的类中来包含此行为。我们想要做的是当我们拟合数据时,检索最后的估算器.coef_方法并将其存储在我们的派生类中的正确名称下。 我建议你查看github上的源代码,因为这只是第一次启动,可能需要更多的错误检查等。 Sklearn使用一个名为@if_delegate_has_method的函数装饰器,这将是一个方便的东西,以确保方法的通用。我已经运行此代码以确保它可以运行,但仅此而已。

from sklearn.datasets import make_friedman1
from sklearn import feature_selection
from sklearn import preprocessing
from sklearn import pipeline
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVR

class myPipe(pipeline.Pipeline):

    def fit(self, X,y):
        """Calls last elements .coef_ method.
        Based on the sourcecode for decision_function(X).
        Link: https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/pipeline.py
        ----------
        """

        super(myPipe, self).fit(X,y)

        self.coef_=self.steps[-1][-1].coef_
        return

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)

est = SVR(kernel="linear")

selector = feature_selection.RFE(est)
std_scaler = preprocessing.StandardScaler()
pipe_params = [('std_scaler', std_scaler),('select', selector), ('clf', est)]

pipe = myPipe(pipe_params)



selector = feature_selection.RFE(pipe)
clf = GridSearchCV(selector, param_grid={'estimator__clf__C': [2, 10]})
clf.fit(X, y)

print clf.best_params_

如果有任何不清楚的地方,请询问。

答案 1 :(得分:1)

我认为你构建管道的方式与pipeline documentation中列出的方式略有不同。

你在找这个吗?

X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)

est = SVR(kernel="linear")

std_scaler = preprocessing.StandardScaler()
selector = feature_selection.RFE(est)
pipe_params = [('feat_selection',selector),('std_scaler', std_scaler), ('clf', est)]
pipe = pipeline.Pipeline(pipe_params)

param_grid = dict(clf__C=[0.1, 1, 10])
clf = GridSearchCV(pipe, param_grid=param_grid, cv=2)
clf.fit(X, y)
print clf.grid_scores_

另请参阅此useful example以了解管道中的内容。对于RFE对象,我只使用official documentation来构建它与您的SVR估算器 - 然后我将RFE对象放入管道中的方式与您使用的方法相同缩放器和估算器对象。