sklearn-validation croisée avec scores multiples

je voudrais calculer l' rappel,précision et f-mesure d'un test de validation croisée pour différents classificateurs. scikit-learn vient cross_val_score mais malheureusement cette méthode ne renvoie pas de valeurs multiples.

j'ai pu calculer de telles mesures en appelant trois foiscross_val_score mais ce n'est pas efficace. Est-il mieux la solution?

maintenant j'ai écrit cette fonction:

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

il résume essentiellement les valeurs de la matrice de confusion et une fois que vous avez faux positif,faux négatif etc vous pouvez facilement calculer le rappel, précision, etc... Mais encore, je n'aime pas cette solution :)

25
demandé sur blueSurfer 2014-04-28 15:09:23

4 réponses

la solution que vous présentez représente exactement la fonctionnalité de cross_val_score parfaitement adapté à votre situation. Il semble que la bonne façon de faire.

cross_val_score prend l'argument n_jobs=, ce qui rend l'évaluation parallélisable. Si c'est quelque chose dont vous avez besoin, vous devriez chercher à remplacer votre boucle for par une boucle parallèle, en utilisant sklearn.externals.joblib.Parallel.

sur une note plus générale, une discussion est en cours sur le problème des scores multiples dans le traceur de scikit learn. Un thread représentatif peut être trouvé ici. Donc, bien qu'il semble que les versions futures de scikit-learn permettront des sorties multiples de marqueurs, dès maintenant, c'est impossible.

hacky (avis de non responsabilité!) moyen de contourner ce problème est de changer le code cross_validation.py toujours aussi légèrement, en enlevant un contrôle de condition sur si votre score est un nombre. Cependant, cette suggestion est très dépendante de la version, donc je vais la présenter pour la version 0.14.

1) dans IPython, tapez from sklearn import cross_validation, suivi par cross_validation??. Notez le nom de fichier affiché et ouvrez-le dans un éditeur (vous pourriez avoir besoin des privilèges root).

2), Vous trouverez ce code, où j'ai déjà balisé la ligne correspondante (1066). Il dit

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

Ces lignes doivent être supprimées. Afin de garder une trace de ce qui était là une fois (si jamais vous voulez revenir), le remplacer par le suivant

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

si ce que votre marqueur renvoie ne fait pas cross_val_score étouffer ailleurs, cela devrait résoudre votre problème. S'il vous plaît laissez-moi savoir si c'est le cas.

6
répondu eickenberg 2014-04-28 12:53:38

Maintenant dans scikit-learn: cross_validate est une nouvelle fonction qui permet d'évaluer un modèle sur plusieurs paramètres. Cette fonctionnalité est également disponible dans GridSearchCV et RandomizedSearchCV ( doc). Il a été fusionné récemment dans master et sera disponible dans v0.19.

scikit-learn doc:

cross_validate la fonction diffère de cross_val_score de deux façons: 1. Il permet de spécifier plusieurs paramètres pour l'évaluation. 2. Il renvoie un dict contenant les résultats à l'entraînement, les temps d'ajustement et les temps d'ajustement en plus des résultats au test.

Le cas d'utilisation typique se passe:

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)

Voir aussi cet exemple.

6
répondu TomDLT 2017-07-12 14:22:28

Vous pouvez utiliser le code suivant pour calculer L'exactitude, la précision, le rappel et toute autre mesure en ajustant votre estimateur une seule fois par étape de validation croisée.

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)}

j'utilise souvent ces fonctions au lieu de cross_val_score pour calculer des statistiques multiples. Vous pouvez changer les paramètres de qualité par le nombre désiré.

2
répondu Artem S 2016-04-12 10:34:10

Vous pouvez utiliser ceci:

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]) )

la source de multiscorer Github

2
répondu KyrSt 2017-07-11 19:04:49