【问题标题】:Pipeline: Multiple classifiers?管道:多个分类器?
【发布时间】:2018-10-21 11:54:30
【问题描述】:

我在 Python 中阅读了以下有关 Pipelines 和 GridSearchCV 的示例: http://www.davidsbatista.net/blog/2017/04/01/document_classification/

逻辑回归:

pipeline = Pipeline([
    ('tfidf', TfidfVectorizer(stop_words=stop_words)),
    ('clf', OneVsRestClassifier(LogisticRegression(solver='sag')),
])
parameters = {
    'tfidf__max_df': (0.25, 0.5, 0.75),
    'tfidf__ngram_range': [(1, 1), (1, 2), (1, 3)],
    "clf__estimator__C": [0.01, 0.1, 1],
    "clf__estimator__class_weight": ['balanced', None],
}

支持向量机:

pipeline = Pipeline([
    ('tfidf', TfidfVectorizer(stop_words=stop_words)),
    ('clf', OneVsRestClassifier(LinearSVC()),
])
parameters = {
    'tfidf__max_df': (0.25, 0.5, 0.75),
    'tfidf__ngram_range': [(1, 1), (1, 2), (1, 3)],
    "clf__estimator__C": [0.01, 0.1, 1],
    "clf__estimator__class_weight": ['balanced', None],
}

有没有一种方法可以将逻辑回归和 SVM 组合到 one 管道中?比如说,我有一个 TfidfVectorizer,并且喜欢针对多个分类器进行测试,然后每个分类器都输出最佳模型/参数。

【问题讨论】:

标签: python scikit-learn pipeline grid-search


【解决方案1】:

这是一种优化任何分类器以及为每个分类器优化参数设置的简单方法。

创建适用于任何估算器的切换器类

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)

现在您可以为 estimator 参数传入任何内容。您可以为传入的任何估算器优化任何参数,如下所示:

执行超参数优化

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

pipeline = Pipeline([
    ('tfidf', TfidfVectorizer()),
    ('clf', ClfSwitcher()),
])

parameters = [
    {
        'clf__estimator': [SGDClassifier()], # SVM if hinge loss / logreg if log loss
        'tfidf__max_df': (0.25, 0.5, 0.75, 1.0),
        'tfidf__stop_words': ['english', None],
        '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()],
        'tfidf__max_df': (0.25, 0.5, 0.75, 1.0),
        'tfidf__stop_words': [None],
        'clf__estimator__alpha': (1e-2, 1e-3, 1e-1),
    },
]

gscv = GridSearchCV(pipeline, parameters, cv=5, n_jobs=12, return_train_score=False, verbose=3)
gscv.fit(train_data, train_labels)

如何解读clf__estimator__loss

clf__estimator__loss 被解释为loss 参数,无论estimator 是什么,其中estimator = SGDClassifier() 在最上面的示例中,它本身就是clf 的一个参数,它是一个ClfSwitcher 对象。

【讨论】:

    【解决方案2】:

    是的,您可以通过构建包装函数来做到这一点。这个想法是传递给它两个字典:模型和参数;

    然后您使用 GridSearchCV 迭代地调用具有所有参数的模型进行测试。

    查看此示例,添加了额外的功能,以便在最后输出一个数据框,其中包含不同模型/参数和不同性能分数的摘要。

    编辑:此处粘贴的代码太多,您可以在此处查看完整的工作示例:

    http://www.davidsbatista.net/blog/2018/02/23/model_optimization/

    【讨论】:

    • 您是否可以在代码中添加其他指标,例如精确度、召回率等以显示?
    • 我猜是这样,只是需要时间去做,你能不能写一个issue给它,否则我会忘记它。谢谢:)
    【解决方案3】:

    这就是我在没有包装函数的情况下所做的。 您可以评估任意数量的分类器。每一个都可以有多个参数用于超参数优化。

    得分最高的将使用pickle保存到磁盘

    from sklearn.svm import SVC
    from operator import itemgetter
    from sklearn.utils import shuffle
    from sklearn.pipeline import Pipeline
    from sklearn.naive_bayes import MultinomialNB
    from sklearn.tree import DecisionTreeClassifier
    from sklearn.model_selection import GridSearchCV
    from sklearn.feature_extraction.text import TfidfVectorizer
    
    #pipeline parameters
        parameters = \
            [ \
                {
                    'clf': [MultinomialNB()],
                    'tf-idf__stop_words': ['english', None],
                    'clf__alpha': [0.001, 0.1, 1, 10, 100]
                },
    
                {
                    'clf': [SVC()],
                    'tf-idf__stop_words': ['english', None],
                    'clf__C': [0.001, 0.1, 1, 10, 100, 10e5],
                    'clf__kernel': ['linear', 'rbf'],
                    'clf__class_weight': ['balanced'],
                    'clf__probability': [True]
                },
    
                {
                    'clf': [DecisionTreeClassifier()],
                    'tf-idf__stop_words': ['english', None],
                    'clf__criterion': ['gini','entropy'],
                    'clf__splitter': ['best','random'],
                    'clf__class_weight':['balanced', None]
                }
            ]
    
        #evaluating multiple classifiers
        #based on pipeline parameters
        #-------------------------------
        result=[]
    
        for params in parameters:
    
            #classifier
            clf = params['clf'][0]
    
            #getting arguments by
            #popping out classifier
            params.pop('clf')
    
            #pipeline
            steps = [('tf-idf', TfidfVectorizer()), ('clf',clf)]
    
            #cross validation using
            #Grid Search
            grid = GridSearchCV(Pipeline(steps), param_grid=params, cv=3)
            grid.fit(features, labels)
    
            #storing result
            result.append\
            (
                {
                    'grid': grid,
                    'classifier': grid.best_estimator_,
                    'best score': grid.best_score_,
                    'best params': grid.best_params_,
                    'cv': grid.cv
                }
            )
    
        #sorting result by best score
        result = sorted(result, key=itemgetter('best score'),reverse=True)
    
        #saving best classifier
        grid = result[0]['grid']
        joblib.dump(grid, 'classifier.pickle')
    
    

    【讨论】:

      猜你喜欢
      • 2018-07-08
      • 2022-01-27
      • 2021-06-03
      • 2019-02-25
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 2020-06-15
      • 1970-01-01
      相关资源
      最近更新 更多