web-dev-qa-db-ja.com

sklearn-複数のスコアによる相互検証

recallprecisionおよびf-measureさまざまな分類子の相互検証テスト。 scikit-learnには cross_val_score が付属していますが、残念ながらこのようなメソッドは複数の値を返しません。

three回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

それは基本的に混同行列値を合計し、偽陽性偽陰性などを取得すると、再現率、精度を簡単に計算できますなど...しかし、私はまだこのソリューションが好きではありません:)

29
blueSurfer

現在scikit-learnで:cross_validateは、複数のメトリックでモデルを評価できる新しい関数です。この機能は、GridSearchCVおよびRandomizedSearchCVdoc )でも使用できます。 最近masterにマージ になり、v0.19で利用できるようになります。

scikit-learn doc から:

cross_validate関数は、2つの点で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)

この例 も参照してください。

11
TomDLT

あなたが提示するソリューションは、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) このコード が見つかります。ここで、関連する行(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を妨げない場合は、これで問題が解決するはずです。その場合はお知らせください。

6
eickenberg

次のコードを使用して、相互検証ステップごとに1回だけ推定量を近似することにより、精度、精度、再現率、およびその他のメトリックを計算できます。

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の代わりにこれらの関数を頻繁に使用して、複数の統計をまとめて計算します。必要に応じて品質メトリックを変更できます。

2
Artem S

あなたはこれを使うことができます:

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 にあります

2
kyriakosSt