在网格搜索中尝试多个估算器

30

Sklearn或任何其他库中,有没有一种方法可以同时对多个估算器进行网格搜索(grid-search)。例如,我们可以在一个网格搜索中传递SVM和随机森林(Random Forest)吗?


你想通过那个做什么? - sascha
1
我正在尝试同时为多个算法创建网格搜索。 - tj89
5个回答

33

是的。例如:

pipeline = Pipeline([
    ('vect', CountVectorizer()),
    ('clf', SGDClassifier()),
])
parameters = [
    {
        'vect__max_df': (0.5, 0.75, 1.0),
        'clf': (SGDClassifier(),),
        'clf__alpha': (0.00001, 0.000001),
        'clf__penalty': ('l2', 'elasticnet'),
        'clf__n_iter': (10, 50, 80),
    }, {
        'vect__max_df': (0.5, 0.75, 1.0),
        'clf': (LinearSVC(),),
        'clf__C': (0.01, 0.5, 1.0)
    }
]
grid_search = GridSearchCV(pipeline, parameters)

4
嗨j-a,感谢你的回答。我更想知道如何创建一个管道,在其中同时使用SGDClassifier和SVM这两个模型。在这种情况下,CountVectorizer的结果将被传递给SGDClassifier。无论如何,我已经稍微改变了我的方法来解决这个问题。 - tj89
1
@tj89 它将并行运行,但我想你特别指的是CountVectorizer应该只运行一次,然后它的结果被重复使用于每个分类器中?你是如何改变你的方法的? - j-a
1
我发现(sklearn==0.23.2)在管道中可以将'clf'设置为None。不需要虚拟的SGDClassifier。 - Ryan J McCall

22
    from sklearn.base import BaseEstimator
    from sklearn.model_selection import GridSearchCV
    
    class DummyEstimator(BaseEstimator):
        def fit(self): pass
        def score(self): pass
        
    # Create a pipeline
    pipe = Pipeline([('clf', DummyEstimator())]) # Placeholder Estimator
    
    # Candidate learning algorithms and their hyperparameters
    search_space = [{'clf': [LogisticRegression()], # Actual Estimator
                     'clf__penalty': ['l1', 'l2'],
                     'clf__C': np.logspace(0, 4, 10)},
                    
                    {'clf': [DecisionTreeClassifier()],  # Actual Estimator
                     'clf__criterion': ['gini', 'entropy']}]
    
    
    # Create grid search 
    gs = GridSearchCV(pipe, search_space)

如果使用OneVsRestClassifier,其中你正在测试的估计器在OneVsRestClassifier中被调用,你会如何进行?你似乎能够将不同的估计器/参数网格传递给外部估计器,但我就是找不到一种方法来传递参数给内部估计器。只是想知道是否有任何魔法可以一起完成所有这些。即使我为每个内部估计器单独进行网格搜索,我仍然面临一个问题,就是我不知道如何为网格搜索传递参数给内部估计器。 - Julian C
你认为只需将None放在DummyEstimator的位置吗? - Ryan J McCall

13

我想你在寻找的是这个:

from sklearn.svm import LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

names = [
         "Naive Bayes",
         "Linear SVM",
         "Logistic Regression",
         "Random Forest",
         "Multilayer Perceptron"
        ]

classifiers = [
    MultinomialNB(),
    LinearSVC(),
    LogisticRegression(),
    RandomForestClassifier(),
    MLPClassifier()
]

parameters = [
              {'vect__ngram_range': [(1, 1), (1, 2)],
              'clf__alpha': (1e-2, 1e-3)},
              {'vect__ngram_range': [(1, 1), (1, 2)],
              'clf__C': (np.logspace(-5, 1, 5))},
              {'vect__ngram_range': [(1, 1), (1, 2)],
              'clf__C': (np.logspace(-5, 1, 5))},
              {'vect__ngram_range': [(1, 1), (1, 2)],
              'clf__max_depth': (1, 2)},
              {'vect__ngram_range': [(1, 1), (1, 2)],
              'clf__alpha': (1e-2, 1e-3)}
             ]

for name, classifier, params in zip(names, classifiers, parameters):
    clf_pipe = Pipeline([
        ('vect', TfidfVectorizer(stop_words='english')),
        ('clf', classifier),
    ])
    gs_clf = GridSearchCV(clf_pipe, param_grid=params, n_jobs=-1)
    clf = gs_clf.fit(X_train, y_train)
    score = clf.score(X_test, y_test)
    print("{} score: {}".format(name, score))

你为什么在前面加上了clf?你可以随便起名字吗? - Maths12
你可以随意命名,@Maths12,但选择前缀并保持一致性可以允许你使用GridSearchCV对每个估计器进行参数调整。不过,你也可以通过上面的示例中使用的名称来获得相同的效果。 - Jakob
2
这将创建多个网格搜索,但问题要求进行1个网格搜索。 - Ryan J McCall

3
您可以使用TransformedTargetRegressor。这个类是为了在拟合之前转换目标变量而设计的,它需要一个回归器和一组转换器作为参数。但是如果您没有提供转换器,则应用恒等转换器(即不进行转换)。由于回归器是一个类参数,我们可以通过网格搜索对象来更改它。
import numpy as np
from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import LinearRegression
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import GridSearchCV

Y = np.array([1,2,3,4,5,6,7,8,9,10])
X = np.array([0,1,3,5,3,5,7,9,8,9]).reshape((-1, 1))

对于进行网格搜索,我们应该将param_grid指定为字典列表,每个字典对应不同的估算器。这是因为不同的估算器使用不同的参数集(例如,在MLPRegressor中设置fit_intercept会导致错误)。请注意,“回归器”这个名称会自动赋给回归器。

model = TransformedTargetRegressor()
params = [
    {
        "regressor": [LinearRegression()],
        "regressor__fit_intercept": [True, False]
    },
    {
        "regressor": [MLPRegressor()],
        "regressor__hidden_layer_sizes": [1, 5, 10]
    }
]

我们可以像往常一样适配。
g = GridSearchCV(model, params)
g.fit(X, Y)

g.best_estimator_, g.best_score_, g.best_params_

# results in like
(TransformedTargetRegressor(check_inverse=True, func=None, inverse_func=None,
               regressor=LinearRegression(copy_X=True, fit_intercept=False, n_jobs=None,
          normalize=False),
               transformer=None),
 -0.419213380219391,
 {'regressor': LinearRegression(copy_X=True, fit_intercept=False, n_jobs=None,
           normalize=False), 'regressor__fit_intercept': False})

2

您可以创建一个类,接受任何分类器和任何参数设置。

创建一个适用于任何估算器的开关类

from sklearn.base import BaseEstimator
class ClfSwitcher(BaseEstimator):

def __init__(
    self, 
    estimator = SGDClassifier(),
):
    """
    A Custom BaseEstimator that can switch between classifiers.
    :param estimator: sklearn object - The classifier
    """ 

    self.estimator = estimator


def fit(self, X, y=None, **kwargs):
    self.estimator.fit(X, y)
    return self


def predict(self, X, y=None):
    return self.estimator.predict(X)


def predict_proba(self, X):
    return self.estimator.predict_proba(X)


def score(self, X, y):
    return self.estimator.score(X, y)

现在你可以按照自己的需求预训练tfidf。

from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer()
tfidf.fit(data, labels)

现在使用预先训练好的tfidf创建一个流水线。
from sklearn.pipeline import Pipeline

pipeline = Pipeline([
    ('tfidf',tfidf), # Already pretrained/fit
    ('clf', ClfSwitcher()),
])

执行超参数优化

from sklearn.naive_bayes import MultinomialNB
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import GridSearchCV



parameters = [
    {
        'clf__estimator': [SGDClassifier()], # SVM if hinge loss / logreg if log loss
        'clf__estimator__penalty': ('l2', 'elasticnet', 'l1'),
        'clf__estimator__max_iter': [50, 80],
        'clf__estimator__tol': [1e-4],
        'clf__estimator__loss': ['hinge', 'log', 'modified_huber'],
    },
    {
        'clf__estimator': [MultinomialNB()],
        'clf__estimator__alpha': (1e-2, 1e-3, 1e-1),
    },
]

gscv = GridSearchCV(pipeline, parameters, cv=5, n_jobs=12, verbose=3)
# param optimization
gscv.fit(train_data, train_labels)

如何解释clf__estimator__loss

clf__estimator__loss被解释为estimatorloss参数,其中estimator是任何一个estimator,在顶部示例中estimator = SGDClassifier(),它本身是clf的一个参数,而clf是一个ClfSwitcher对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接