python - scikit - Sklearn-validación cruzada con múltiples puntuaciones
stratifiedkfold (4)
Ahora en scikit-learn: cross_validate
es una nueva función que puede evaluar un modelo con múltiples métricas. Esta función también está disponible en GridSearchCV
y RandomizedSearchCV
( doc ). Se ha fusionado recientemente en master y estará disponible en v0.19.
Desde el documento scikit-learn :
La función
cross_validate
diferencia decross_val_score
en dos formas: 1. Permite especificar múltiples métricas para la evaluación. 2. Devuelve un dictado que contiene puntajes de entrenamiento, tiempos de ajuste y tiempos de puntaje además del puntaje de la prueba.
El caso de uso típico pasa por:
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)
Véase también este ejemplo .
Me gustaría calcular la recuperación , la precisión y la medida de f de una prueba de validación cruzada para diferentes clasificadores. scikit-learn viene con cross_val_score pero, desafortunadamente, este método no devuelve múltiples valores.
Podría calcular tales medidas llamando tres veces cross_val_score pero eso no es eficiente. ¿Hay alguna solución mejor?
Por ahora escribí esta función:
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
Básicamente, resume los valores de la matriz de confusión y una vez que tenga falso positivo , falso negativo, etc., puede calcular fácilmente la recuperación, la precisión, etc. Pero todavía no me gusta esta solución :)
La solución que presenta representa exactamente la funcionalidad de cross_val_score
, perfectamente adaptada a su situación. Parece el camino correcto a seguir.
cross_val_score
toma el argumento n_jobs=
, haciendo que la evaluación sea paralelizable. Si esto es algo que necesita, debería reemplazar su bucle for por un bucle paralelo, utilizando sklearn.externals.joblib.Parallel
.
En una nota más general, se está llevando a cabo una discusión sobre el problema de las puntuaciones múltiples en el rastreador de problemas de scikit learn. Un hilo representativo se puede encontrar here . Entonces, si bien parece que las versiones futuras de scikit-learn permitirán múltiples salidas de anotadores, a partir de ahora, esto es imposible.
Una forma de pirateo (¡descargo de responsabilidad!) De evitar esto es cambiar el código en cross_validation.py
ligeramente, eliminando una verificación de condición sobre si su puntaje es un número. Sin embargo, esta sugerencia depende mucho de la versión, por lo que la presentaré para la versión 0.14
.
1) En IPython, escriba from sklearn import cross_validation
, seguido de cross_validation??
. Anote el nombre de archivo que se muestra y ábralo en un editor (es posible que necesite privilegios de root).
2) Encontrará este código , donde ya he marcado la línea correspondiente (1066). Dice
if not isinstance(score, numbers.Number):
raise ValueError("scoring must return a number, got %s (%s)"
" instead." % (str(score), type(score)))
Estas líneas deben ser eliminadas. Para mantener un registro de lo que había allí una vez (si alguna vez desea volver a cambiar), reemplácelo con el siguiente
if not isinstance(score, numbers.Number):
pass
# raise ValueError("scoring must return a number, got %s (%s)"
# " instead." % (str(score), type(score)))
Si lo que devuelve su anotador no hace que cross_val_score
otro lado, esto debería resolver su problema. Por favor, hágamelo saber si este es el caso.
Podrías usar esto:
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 fuente del multiscorer está en Github
Puede usar el siguiente código para calcular Precisión, Precisión, Recuperación y cualquier otra métrica ajustando su estimador solo una vez por paso de validación cruzada.
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)}
Con frecuencia utilizo estas funciones en lugar de cross_val_score para calcular varias estadísticas por completo. Puede cambiar las métricas de calidad por el deseado.