【问题标题】:Using statsmodel estimations with scikit-learn cross validation, is it possible?使用 statsmodel 估计和 scikit-learn 交叉验证,有可能吗?
【发布时间】:2017-04-24 01:35:43
【问题描述】:

我将此问题发布到交叉验证论坛,后来意识到这可能会在 stackoverlfow 中找到合适的受众。

我正在寻找一种方法,我可以使用从 python statsmodel 获取的 fit 对象(结果)来输入 scikit-learn cross_validation 方法的 cross_val_score? 附加的链接表明它可能是可能的,但我没有成功。

我收到以下错误

估计器应该是实现“适合”方法的估计器 statsmodels.discrete.discrete_model.BinaryResultsWrapper 对象位于 0x7fa6e801c590 已通过

Refer this link

【问题讨论】:

    标签: python scikit-learn cross-validation statsmodels


    【解决方案1】:

    事实上,你不能直接在statsmodels对象上使用cross_val_score,因为不同的接口:在statsmodels中

    • 训练数据直接传入构造函数
    • 一个单独的对象包含模型估计的结果

    但是,您可以编写一个简单的包装器来使 statsmodels 对象看起来像 sklearn 估算器:

    import statsmodels.api as sm
    from sklearn.base import BaseEstimator, RegressorMixin
    
    class SMWrapper(BaseEstimator, RegressorMixin):
        """ A universal sklearn-style wrapper for statsmodels regressors """
        def __init__(self, model_class, fit_intercept=True):
            self.model_class = model_class
            self.fit_intercept = fit_intercept
        def fit(self, X, y):
            if self.fit_intercept:
                X = sm.add_constant(X)
            self.model_ = self.model_class(y, X)
            self.results_ = self.model_.fit()
            return self
        def predict(self, X):
            if self.fit_intercept:
                X = sm.add_constant(X)
            return self.results_.predict(X)
    

    该类包含正确的fitpredict 方法,可以与sklearn 一起使用,例如交叉验证或包含在管道中。喜欢这里:

    from sklearn.datasets import make_regression
    from sklearn.model_selection import cross_val_score
    from sklearn.linear_model import LinearRegression
    
    X, y = make_regression(random_state=1, n_samples=300, noise=100)
    
    print(cross_val_score(SMWrapper(sm.OLS), X, y, scoring='r2'))
    print(cross_val_score(LinearRegression(), X, y, scoring='r2'))
    

    您可以看到两个模型的输出是相同的,因为它们都是 OLS 模型,以相同的方式进行交叉验证。

    [0.28592315 0.37367557 0.47972639]
    [0.28592315 0.37367557 0.47972639]
    

    【讨论】:

    • 我用包装器为我的 cross_val_score() 得到了 nan。任何想法可能是什么原因?
    • 你每次都在重新初始化 cross_val_score 中的模型,我认为它应该在 cross_val_score 之外
    • 初始化时刻不影响结果
    【解决方案2】:

    按照suggestion of David(这给了我一个错误,抱怨缺少函数get_parameters)和scikit learn documentation,我为线性回归创建了以下包装器。 它具有与sklearn.linear_model.LinearRegression 相同的界面,但还具有summary() 功能,它提供有关p 值、R2 和其他统计信息的信息,如statsmodels.OLS

    import statsmodels.api as sm
    from sklearn.base import BaseEstimator, RegressorMixin
    import pandas as pd
    import numpy as np
    
    from sklearn.utils.multiclass import check_classification_targets
    from sklearn.utils.validation import check_X_y, check_is_fitted, check_array
    from sklearn.utils.multiclass import unique_labels
    from sklearn.utils.estimator_checks import check_estimator
    
    
    
    class MyLinearRegression(BaseEstimator, RegressorMixin):
        def __init__(self, fit_intercept=True):
    
            self.fit_intercept = fit_intercept
    
    
        """
        Parameters
        ------------
        column_names: list
                It is an optional value, such that this class knows 
                what is the name of the feature to associate to 
                each column of X. This is useful if you use the method
                summary(), so that it can show the feature name for each
                coefficient
        """ 
        def fit(self, X, y, column_names=() ):
    
            if self.fit_intercept:
                X = sm.add_constant(X)
    
            # Check that X and y have correct shape
            X, y = check_X_y(X, y)
    
    
            self.X_ = X
            self.y_ = y
    
            if len(column_names) != 0:
                cols = column_names.copy()
                cols = list(cols)
                X = pd.DataFrame(X)
                cols = column_names.copy()
                cols.insert(0,'intercept')
                print('X ', X)
                X.columns = cols
    
            self.model_ = sm.OLS(y, X)
            self.results_ = self.model_.fit()
            return self
    
    
    
        def predict(self, X):
            # Check is fit had been called
            check_is_fitted(self, 'model_')
    
            # Input validation
            X = check_array(X)
    
            if self.fit_intercept:
                X = sm.add_constant(X)
            return self.results_.predict(X)
    
    
        def get_params(self, deep = False):
            return {'fit_intercept':self.fit_intercept}
    
    
        def summary(self):
            print(self.results_.summary() )
    

    使用示例:

    cols = ['feature1','feature2']
    X_train = df_train[cols].values
    X_test = df_test[cols].values
    y_train = df_train['label']
    y_test = df_test['label']
    model = MyLinearRegression()
    model.fit(X_train, y_train)
    model.summary()
    model.predict(X_test)
    

    如果要显示列名,可以调用

    model.fit(X_train, y_train, column_names=cols)
    

    在 cross_validation 中使用它:

    from sklearn.model_selection import cross_val_score
    scores = cross_val_score(MyLinearRegression(), X_train, y_train, cv=10, scoring='neg_mean_squared_error')
    scores
    

    【讨论】:

    • 在最后一条评论“To use it in cross_validation”中,为什么你在 cross_val_score 中使用 X_train 和 y_train 而不仅仅是 X 和 y?
    • 因为我考虑了以下协议:(i)将样本划分为训练集和测试集(ii)选择最佳模型,即交叉验证分数最高的模型,只需使用训练集,以避免任何数据泄漏 (iii) 在测试集中包含的“未见”数据上检查此类模型的性能。如果您使用整个集合进行交叉验证,您将根据您判断模型的相同数据选择模型。从技术上讲,这将是数据泄漏。事实上,它不会让你知道你的模型如何处理完全看不见的数据。
    【解决方案3】:

    作为参考,如果您使用statsmodels 公式API 和/或使用fit_regularized 方法,您可以通过这种方式修改@David Dale 的包装类。

    import pandas as pd
    from sklearn.base import BaseEstimator, RegressorMixin
    from statsmodels.formula.api import glm as glm_sm
    
    # This is an example wrapper for statsmodels GLM
    class SMWrapper(BaseEstimator, RegressorMixin):
        def __init__(self, family, formula, alpha, L1_wt):
            self.family = family
            self.formula = formula
            self.alpha = alpha
            self.L1_wt = L1_wt
            self.model = None
            self.result = None
        def fit(self, X, y):
            data = pd.concat([pd.DataFrame(X), pd.Series(y)], axis=1)
            data.columns = X.columns.tolist() + ['y']
            self.model = glm_sm(self.formula, data, family=self.family)
            self.result = self.model.fit_regularized(alpha=self.alpha, L1_wt=self.L1_wt, refit=True)
            return self.result
        def predict(self, X):
            return self.result.predict(X)
    

    【讨论】:

      【解决方案4】:

      虽然我认为这在技术上不是 scikit-learn,但有一个包 pmdarima(链接到 PyPi 上的 pmdarima 包)包装了 statsmodel 并提供了一个类似于 scikit-learn 的界面。

      【讨论】:

      • 你好,安德烈。请考虑在您的答案中添加更多信息,而不是链接到外部来源。
      • 请总结一下链接的内容,以防链接失效
      猜你喜欢
      • 2016-04-25
      • 2015-06-22
      • 2019-07-04
      • 2017-09-02
      • 1970-01-01
      • 2018-03-30
      • 2017-04-12
      • 2018-01-20
      • 2015-09-04
      相关资源
      最近更新 更多