Estoy probando diferentes clasificadores en un conjunto de datos donde hay 5 clases y cada instancia puede pertenecer a una o más de estas clases, así que estoy usando clasificadores multi-etiqueta de scikit-learn, específicamente sklearn.multiclass.OneVsRestClassifier
. Ahora quiero realizar una validación cruzada utilizando el sklearn.cross_validation.StratifiedKFold
. Esto produce el siguiente error:
Traceback (most recent call last):
File "mlfromcsv.py", line 93, in <module>
main()
File "mlfromcsv.py", line 77, in main
test_classifier_multilabel(svm.LinearSVC(), X, Y, 'Linear Support Vector Machine')
File "mlfromcsv.py", line 44, in test_classifier_multilabel
scores = cross_validation.cross_val_score(clf_ml, X, Y_list, cv=cv, score_func=metrics.precision_recall_fscore_support, n_jobs=jobs)
File "/usr/lib/pymodules/python2.7/sklearn/cross_validation.py", line 1046, in cross_val_score
X, y = check_arrays(X, y, sparse_format='csr')
File "/usr/lib/pymodules/python2.7/sklearn/utils/validation.py", line 144, in check_arrays
size, n_samples))
ValueError: Found array with dim 5. Expected 98816
Observe que el entrenamiento del clasificador multietiqueta no se bloquea, pero sí la validación cruzada. Cómo debo realizar la validación cruzada para este clasificador multietiqueta?
También he escrito una segunda versión que divide el problema en el entrenamiento y la validación cruzada de 5 clasificadores distintos. Esto funciona muy bien.
Este es mi código. La función test_classifier_multilabel
es el que da problemas. test_classifier
es mi otro intento (dividiendo el problema en 5 clasificadores y 5 validaciones cruzadas).
import numpy as np
from sklearn import *
from sklearn.multiclass import OneVsRestClassifier
from sklearn.neighbors import KNeighborsClassifier
import time
def test_classifier(clf, X, Y, description, jobs=1):
print '=== Testing classifier {0} ==='.format(description)
for class_idx in xrange(Y.shape[1]):
print ' > Cross-validating for class {:d}'.format(class_idx)
n_samples = X.shape[0]
cv = cross_validation.StratifiedKFold(Y[:,class_idx], 3)
t_start = time.clock()
scores = cross_validation.cross_val_score(clf, X, Y[:,class_idx], cv=cv, score_func=metrics.precision_recall_fscore_support, n_jobs=jobs)
t_end = time.clock();
print 'Cross validation time: {:0.3f}s.'.format(t_end-t_start)
str_tbl_fmt = '{:>15s}{:>15s}{:>15s}{:>15s}{:>15s}'
str_tbl_entry_fmt = '{:0.2f} +/- {:0.2f}'
print str_tbl_fmt.format('', 'Precision', 'Recall', 'F1 score', 'Support')
for (score_class, lbl) in [(0, 'Negative'), (1, 'Positive')]:
mean_precision = scores[:,0,score_class].mean()
std_precision = scores[:,0,score_class].std()
mean_recall = scores[:,1,score_class].mean()
std_recall = scores[:,1,score_class].std()
mean_f1_score = scores[:,2,score_class].mean()
std_f1_score = scores[:,2,score_class].std()
support = scores[:,3,score_class].mean()
print str_tbl_fmt.format(
lbl,
str_tbl_entry_fmt.format(mean_precision, std_precision),
str_tbl_entry_fmt.format(mean_recall, std_recall),
str_tbl_entry_fmt.format(mean_f1_score, std_f1_score),
'{:0.2f}'.format(support))
def test_classifier_multilabel(clf, X, Y, description, jobs=1):
print '=== Testing multi-label classifier {0} ==='.format(description)
n_samples = X.shape[0]
Y_list = [value for value in Y.T]
print 'Y_list[0].shape:', Y_list[0].shape, 'len(Y_list):', len(Y_list)
cv = cross_validation.StratifiedKFold(Y_list, 3)
clf_ml = OneVsRestClassifier(clf)
accuracy = (clf_ml.fit(X, Y).predict(X) != Y).sum()
print 'Accuracy: {:0.2f}'.format(accuracy)
scores = cross_validation.cross_val_score(clf_ml, X, Y_list, cv=cv, score_func=metrics.precision_recall_fscore_support, n_jobs=jobs)
str_tbl_fmt = '{:>15s}{:>15s}{:>15s}{:>15s}{:>15s}'
str_tbl_entry_fmt = '{:0.2f} +/- {:0.2f}'
print str_tbl_fmt.format('', 'Precision', 'Recall', 'F1 score', 'Support')
for (score_class, lbl) in [(0, 'Negative'), (1, 'Positive')]:
mean_precision = scores[:,0,score_class].mean()
std_precision = scores[:,0,score_class].std()
mean_recall = scores[:,1,score_class].mean()
std_recall = scores[:,1,score_class].std()
mean_f1_score = scores[:,2,score_class].mean()
std_f1_score = scores[:,2,score_class].std()
support = scores[:,3,score_class].mean()
print str_tbl_fmt.format(
lbl,
str_tbl_entry_fmt.format(mean_precision, std_precision),
str_tbl_entry_fmt.format(mean_recall, std_recall),
str_tbl_entry_fmt.format(mean_f1_score, std_f1_score),
'{:0.2f}'.format(support))
def main():
nfeatures = 13
nclasses = 5
ncolumns = nfeatures + nclasses
data = np.loadtxt('./feature_db.csv', delimiter=',', usecols=range(ncolumns))
print data, data.shape
X = np.hstack((data[:,0:3], data[:,(nfeatures-1):nfeatures]))
print 'X.shape:', X.shape
Y = data[:,nfeatures:ncolumns]
print 'Y.shape:', Y.shape
test_classifier(svm.LinearSVC(), X, Y, 'Linear Support Vector Machine', jobs=-1)
test_classifier_multilabel(svm.LinearSVC(), X, Y, 'Linear Support Vector Machine')
if __name__ =='__main__':
main()
Estoy usando Ubuntu 13.04 y scikit-learn 0.12. Mis datos están en forma de dos matrices (X e Y) que tienen formas (98816, 4) y (98816, 5), es decir, 4 características por instancia y 5 etiquetas de clase. Las etiquetas son 1 o 0 para indicar la pertenencia a esa clase. ¿Estoy utilizando el formato correcto, ya que no veo mucha documentación al respecto?