【问题标题】:Why doesn't `best_estimator_.get_params()` agree with `best_params_`为什么 `best_estimator_.get_params()` 不同意 `best_params_`
【发布时间】:2020-04-03 02:45:37
【问题描述】:

我正在玩一些 scikit-learn 对象,在尝试调整超参数时偶然发现了以下结果。这段代码的输出

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import GridSearchCV
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline


class NaiveBayesClassifier(Pipeline):
    def __init__(self):
        super().__init__(
            [("tfidf", TfidfVectorizer()), ("clf", MultinomialNB()),]
        )

    def tune(self, data, min_df, max_df, max_features):
        gs = GridSearchCV(
            estimator=self,
            param_grid={
                "tfidf__max_df": max_df,
                "tfidf__min_df": min_df,
                "tfidf__max_features": max_features,
            },
            verbose=10,
        )

        return gs.fit(*data)


if __name__ == "__main__":
    from sklearn.datasets import fetch_20newsgroups

    categories = ["alt.atheism", "soc.religion.christian", "comp.graphics", "sci.med"]
    twenty_train = fetch_20newsgroups(
        subset="train", categories=categories, shuffle=True, random_state=42
    )

    nb = NaiveBayesClassifier()

    tuned_model = nb.tune(
        (twenty_train.data, twenty_train.target),
        min_df=[0, 0.1],
        max_df=[0.9, 1],
        max_features=[2_000, 5_000],
    )

    print(tuned_model.best_score_)
    for k, v in tuned_model.best_params_.items():
        print(f"{v}    <>    {tuned_model.best_estimator_.get_params()[k]}")

下面是

0.9437278025233994
0.9    <>    1.0
5000    <>    None
0    <>    1

查看网格搜索生成的输出,我可以看到左侧的参数确实产生了 5 折的平均分数。因此,tuned_model.best_params_ 似乎是我所期望的。但是,参数 best_estimator_ 只是默认的。

这是什么原因? Pipeline 类有一个 set_params 方法,它似乎对 tuned_model.best_estimator_.set_params(tuned_model.best_params_) 做了正确的事情(当然现在只有参数是最优的,而模型不是)。

【问题讨论】:

    标签: python scikit-learn classification


    【解决方案1】:

    看起来子类化不适用于sklearn.base.clone,因为在子类上调用get_params 的结果与在Pipeline 的实际实例上调用相同方法的结果不同(截至版本0.22)。

    为了制作正确的 sklearn 估计器,__init__ 方法必须将参数声明为显式关键字参数。覆盖def __init__(self) 有效地创建了一个没有参数的估计器。这就解释了为什么get_params(deep=False) 返回一个空的dict

    以下子类有效

    class NaiveBayesClassifier(Pipeline):
        def __init__(self, steps=[], memory=None, verbose=False):
            super().__init__(
                steps or [("tfidf", TfidfVectorizer()), ("clf", MultinomialNB())]
            )
    
        def tune(self, data, min_df, max_df, max_features):
            gs = GridSearchCV(
                estimator=self,
                param_grid={
                    "tfidf__max_df": max_df,
                    "tfidf__min_df": min_df,
                    "tfidf__max_features": max_features,
                },
                verbose=10,
            )
    
            return gs.fit(*data)
    

    虽然现在可以按预期工作,但 __init__ 的签名“丑陋”,因为原则上它不应该接受任何参数。

    【讨论】:

      猜你喜欢
      • 2018-06-17
      • 2018-05-31
      • 2017-01-28
      • 1970-01-01
      • 1970-01-01
      • 2018-09-12
      • 1970-01-01
      • 1970-01-01
      • 2013-10-27
      相关资源
      最近更新 更多