sklearn - 具有多个分数的交叉验证

Posted

技术标签:

【中文标题】sklearn - 具有多个分数的交叉验证【英文标题】:sklearn - Cross validation with multiple scores 【发布时间】:2014-06-13 21:46:15 【问题描述】:

我想计算不同分类器的交叉验证测试的 recallprecisionf-measurescikit-learn 带有 cross_val_score 但不幸的是这种方法不会返回多个值。

我可以通过调用 3 次 cross_val_score 来计算此类度量,但这并不高效。有没有更好的解决方案?

现在我写了这个函数:

from sklearn import metrics

def mean_scores(X, y, clf, skf):

    cm = np.zeros(len(np.unique(y)) ** 2)
    for i, (train, test) in enumerate(skf):
        clf.fit(X[train], y[train])
        y_pred = clf.predict(X[test])
        cm += metrics.confusion_matrix(y[test], y_pred).flatten()

    return compute_measures(*cm / skf.n_folds)

def compute_measures(tp, fp, fn, tn):
     """Computes effectiveness measures given a confusion matrix."""
     specificity = tn / (tn + fp)
     sensitivity = tp / (tp + fn)
     fmeasure = 2 * (specificity * sensitivity) / (specificity + sensitivity)
     return sensitivity, specificity, fmeasure

它基本上总结了混淆矩阵的值,一旦你有 false positivefalse positive 等,你可以轻松计算召回率、精度等......但我仍然不喜欢这个解决方案:)

【问题讨论】:

使用classification_report有什么问题?见scikit-learn.org/stable/modules/generated/… 你基本上已经吸取了cross_val_score的精髓,并根据你的情况进行了改编。这似乎是一个完全可行的选择,我不知道如何做得更好。如果您想修改 sklearn 代码,请参阅我对问题的解释和解决方法的回答。 @EdChum 这不做任何交叉验证。您可以根据单个预训练阶段来衡量模型的性能。 调用cross_val_score 3 次不是一个好主意。您必须小心使用相同的测试/训练集。 @Dror 是的,你是对的,我当时看错了问题 【参考方案1】:

现在在 scikit-learn 中:cross_validate 是一个可以在多个指标上评估模型的新函数。 GridSearchCVRandomizedSearchCV (doc) 也提供此功能。 一直是merged recently in master,将在 v0.19 中提供。

来自scikit-learn doc:

cross_validate 函数在两个方面与cross_val_score 不同: 1. 它允许指定多个评估指标。 2. 它返回一个字典,其中包含训练分数、拟合时间和分数时间以及测试分数。

典型用例如下:

from sklearn.svm import SVC
from sklearn.datasets import load_iris
from sklearn.model_selection import cross_validate
iris = load_iris()
scoring = ['precision', 'recall', 'f1']
clf = SVC(kernel='linear', C=1, random_state=0)
scores = cross_validate(clf, iris.data, iris.target == 1, cv=5,
                        scoring=scoring, return_train_score=False)

另见this example。

【讨论】:

【参考方案2】:

您提出的解决方案完全代表cross_val_score 的功能,完全适合您的情况。这似乎是正确的方法。

cross_val_score 接受参数n_jobs=,使评估可并行化。如果这是您需要的,您应该考虑使用 sklearn.externals.joblib.Parallel 将您的 for 循环替换为并行循环。

在更一般的说明中,正在讨论 scikit learn 的问题跟踪器中的多个分数问题。可以找到一个有代表性的线程here。因此,虽然看起来未来版本的 scikit-learn 将允许记分器的多个输出,但到目前为止,这是不可能的。

hacky(免责声明!)解决此问题的方法是稍微更改cross_validation.py 中的代码,方法是删除对您的分数是否为数字的条件检查。但是,这个建议是非常依赖版本的,所以我将它呈现为版本0.14

1) 在 IPython 中,输入from sklearn import cross_validation,然后输入cross_validation??。记下显示的文件名并在编辑器中打开它(您可能需要 root 权限)。

2) 你会找到this code,我已经在其中标记了相关行 (1066)。它说

    if not isinstance(score, numbers.Number):
        raise ValueError("scoring must return a number, got %s (%s)"
                         " instead." % (str(score), type(score)))

需要删除这些行。为了跟踪曾经存在的内容(如果您想改回来),请将其替换为以下内容

    if not isinstance(score, numbers.Number):
        pass
        # raise ValueError("scoring must return a number, got %s (%s)"
        #                 " instead." % (str(score), type(score)))

如果您的记分员返回的内容没有让cross_val_score 在其他地方窒息,这应该可以解决您的问题。如果是这种情况,请告诉我。

【讨论】:

如果这听起来很可怕:自从引入这个numbers.Number 条件以来,我一直在使用这个补丁版本的 scikit learn,因为我的得分者返回的得分在 20000 到 100000 之间。虽然我承认修补代码不是理想的解决方案,而且我不应该向广大公众认可这个想法,但条件是为了捕捉其他地方可能发生的错误。如果您知道您的记分器不会错误地返回多个值,那么您可以安全地删除该条件。 感谢您的回复,是的,这很可怕:D 我想我会坚持我的解决方案(或类似的东西),希望未来的版本能够实现这一点。 谢谢。我仍然会继续回答,以防其他人发现这很有用。如前所述,这是 scikit learn 中的一个持续问题,而且您不是唯一遇到此问题的人。我编辑了我的答案,以明确您编写的代码非常适合您尝试做的事情。 0.17+ 版本是否已解决此问题,如果可以,请更新您的答案...谢谢... 正如@eickenberg 所说,您只需评论isinstance 检查,然后通过scikit-learn 内置的任何评分函数(例如sklearn.metrics.precision_recall_fscore_support)。请务必使用sklearn.metrics.make_scorer 函数进行评分。【参考方案3】:

你可以用这个:

from sklearn import metrics
from multiscorer import MultiScorer
import numpy as np

scorer = MultiScorer(
    'F-measure' : (f1_score, ...),
    'Precision' : (precision_score, ...),
    'Recall' : (recall_score, ...)
)

...

cross_val_score(clf, X, target, scoring=scorer)
results = scorer.get_results()

for name in results.keys():
     print '%s: %.4f' % (name, np.average(results[name]) )

multiscorer的来源在Github

【讨论】:

【参考方案4】:

您可以使用以下代码通过在每个交叉验证步骤中仅拟合一次估算器来计算准确度、精确度、召回率和任何其他指标。

def get_true_and_pred_CV(estimator, X, y, n_folds, cv, params):
    ys = []
    for train_idx, valid_idx in cv:
        clf = estimator(**params)
        if isinstance(X, np.ndarray):
            clf.fit(X[train_idx], y[train_idx])
            cur_pred = clf.predict(X[valid_idx])
        elif isinstance(X, pd.DataFrame):
            clf.fit(X.iloc[train_idx, :], y[train_idx]) 
            cur_pred = clf.predict(X.iloc[valid_idx, :])
        else:
            raise Exception('Only numpy array and pandas DataFrame ' \
                            'as types of X are supported')

        ys.append((y[valid_idx], cur_pred))
    return ys


def fit_and_score_CV(estimator, X, y, n_folds=10, stratify=True, **params):
    if not stratify:
        cv_arg = sklearn.cross_validation.KFold(y.size, n_folds)
    else:
        cv_arg = sklearn.cross_validation.StratifiedKFold(y, n_folds)

    ys = get_true_and_pred_CV(estimator, X, y, n_folds, cv_arg, params)    
    cv_acc = map(lambda tp: sklearn.metrics.accuracy_score(tp[0], tp[1]), ys)
    cv_pr_weighted = map(lambda tp: sklearn.metrics.precision_score(tp[0], tp[1], average='weighted'), ys)
    cv_rec_weighted = map(lambda tp: sklearn.metrics.recall_score(tp[0], tp[1], average='weighted'), ys)
    cv_f1_weighted = map(lambda tp: sklearn.metrics.f1_score(tp[0], tp[1], average='weighted'), ys)

    # the approach below makes estimator fit multiple times
    #cv_acc = sklearn.cross_validation.cross_val_score(algo, X, y, cv=cv_arg, scoring='accuracy')
    #cv_pr_weighted = sklearn.cross_validation.cross_val_score(algo, X, y, cv=cv_arg, scoring='precision_weighted')
    #cv_rec_weighted = sklearn.cross_validation.cross_val_score(algo, X, y, cv=cv_arg, scoring='recall_weighted')   
    #cv_f1_weighted = sklearn.cross_validation.cross_val_score(algo, X, y, cv=cv_arg, scoring='f1_weighted')
    return 'CV accuracy': np.mean(cv_acc), 'CV precision_weighted': np.mean(cv_pr_weighted),
            'CV recall_weighted': np.mean(cv_rec_weighted), 'CV F1_weighted': np.mean(cv_f1_weighted)

我经常使用这些函数而不是 cross_val_score 来计算多个统计数据。您可以根据需要更改质量指标。

【讨论】:

【参考方案5】:

如果您使用 multi-classes 查看multi-metrics,这可能会有所帮助。用最新的doc in scikit learn 0.19及以上;您可以使用度量函数传递您自己的字典;

custom_scorer = 'accuracy': make_scorer(accuracy_score),
                 'balanced_accuracy': make_scorer(balanced_accuracy_score),
                 'precision': make_scorer(precision_score, average='macro'),
                 'recall': make_scorer(recall_score, average='macro'),
                 'f1': make_scorer(f1_score, average='macro'),
                 
scores = cross_validation.cross_val_score(clf, X_train, y_train,
        cv = 10, scoring = custom_scorer)

【讨论】:

以上是关于sklearn - 具有多个分数的交叉验证的主要内容,如果未能解决你的问题,请参考以下文章

SKlearn中具有嵌套交叉验证的分类报告(平均值/个体值)

在 sklearn 中使用网格搜索和管道获得正确的交叉验证分数

sklearn 交叉验证 R^2 分数与使用训练模型对训练和验证数据进行的手动检查不匹配

使用 sklearn 进行交叉验证的高级特征提取

交叉验证(Cross Validation)比较

sklearn中的交叉验证+决策树