Pipeline: 多个分类器?

19

我在Python中阅读了有关管道和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],
}

SVM:

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],
}

逻辑回归和支持向量机能否结合成一个管道?比如,我有一个TfidfVectorizer,想测试多个分类器,然后每个分类器都输出最佳模型/参数。


可能是在GridSearchCV中交替不同模型的备选项的重复问题。 - Vivek Kumar
你在这个问题中所做的是正确的。这就是我在上面的答案中所做的。 - Vivek Kumar
3个回答

29

以下是针对任何分类器及其参数设置的简单优化方法。

创建一个适用于任何评估器的切换器类

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)

现在你可以针对估计器参数传入任何内容。并且,你可以按照以下方式优化传入的任何估计器的任何参数:

执行超参数优化

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被解释为estimatorloss参数,其中estimator是任何一个estimator,在顶部的例子中estimator = SGDClassifier(),它本身是clf的一个参数,而clf是一个ClfSwitcher对象。


你好 @cgnorthcutt, 我已使用您的解决方案一段时间了,它是我目前为止见过的最好的尝试多个模型的方法。 唯一的缺点是,在使用 ConfusionMatrix.from_estimator(我将我的 grid_search 对象传递给它,但它似乎找不到最佳拟合估计器)和像 OneVsRestClassifier(SVC()) 这样已经包装了估计器的情况下,它的工作效果不是很好。 您是否已找到解决方法,或者您从那以后使用了完全不同的方法? 非常感谢。 安东尼 - Antoine101

6

是的,您可以通过构建一个包装函数来实现。思路是传递两个字典:模型和参数;

然后使用GridSearchCV迭代调用所有参数进行测试。

请查看此示例,添加了额外功能,以便最终输出不同模型/参数和不同性能得分的数据框摘要。

编辑:这里粘贴太多代码了,您可以在此处检查一个完整的工作示例:

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


你可以在你的代码中增加其他度量指标(如准确率、召回率等)来展示吗? - scientific_explorer
我想是这样,只需要时间来做,你能为此编写一个问题吗?否则我会忘记它。谢谢 :) - David Batista

5

以下是我在没有使用封装函数的情况下完成操作的方式。 您可以评估任意数量的分类器。每个分类器都可以有多个参数进行超参数优化。

具有最佳得分的分类器将使用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')


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