我正在尝试一些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_)做了正确的事情(当然,现在只有参数是最优的,而模型不是)。
发布于 2019-12-10 09:37:42
看起来子类化不能很好地处理sklearn.base.clone,因为在子类上调用get_params的结果与在Pipeline的实际实例上调用相同方法的结果不同(从0.22版开始)。
为了获得正确的sklearn估计器,__init__方法必须将参数声明为显式关键字参数。override 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__的签名是“丑陋的”,因为原则上它不应该接受任何参数。
https://stackoverflow.com/questions/59258339
复制相似问题