Cara menggunakan fungsi validasi silang scikit-learn pada multi-label classifier

20

Saya menguji berbagai pengklasifikasi pada kumpulan data di mana terdapat 5 kelas dan setiap instance dapat menjadi milik satu atau lebih dari kelas-kelas ini, jadi saya menggunakan pengklasifikasi multi-label scikit-learn, khususnya sklearn.multiclass.OneVsRestClassifier. Sekarang saya ingin melakukan validasi silang menggunakan sklearn.cross_validation.StratifiedKFold. Ini menghasilkan kesalahan berikut:

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

Perhatikan bahwa melatih pengelompokan multi-label tidak macet, tetapi validasi silang tidak. Bagaimana saya harus melakukan validasi silang untuk pengelompokan multi-label ini?

Saya juga telah menulis versi kedua yang memecah masalah menjadi pelatihan dan memvalidasi silang 5 pengklasifikasi yang terpisah. Ini berfungsi dengan baik.

Ini kode saya. Fungsinya test_classifier_multilabeladalah yang memberi masalah. test_classifieradalah usaha saya yang lain (memecah masalah menjadi 5 pengklasifikasi dan 5 cross-validations).

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

Saya menggunakan Ubuntu 13.04 dan scikit-belajar 0.12. Data saya dalam bentuk dua array (X dan Y) yang memiliki bentuk (98816, 4) dan (98816, 5), yaitu 4 fitur per instance dan 5 label kelas. Labelnya adalah 1 atau 0 untuk mengindikasikan keanggotaan dalam kelas itu. Apakah saya menggunakan format yang benar karena saya tidak melihat banyak dokumentasi tentang itu?

chippies
sumber

Jawaban:

10

Pengambilan sampel bertingkat berarti bahwa distribusi keanggotaan kelas dipertahankan dalam pengambilan sampel KFold Anda. Ini tidak masuk akal dalam kasus multilabel di mana vektor target Anda mungkin memiliki lebih dari satu label per pengamatan.

Ada dua kemungkinan interpretasi bertingkat dalam arti ini.

Untuk label di mana setidaknya satu dari mereka diisi yang memberi Anda label unik. Anda dapat melakukan pengambilan sampel bertingkat pada setiap nampan label unik.n i = 1 2 nni=1n2n

Pilihan lain adalah untuk mencoba dan mengelompokkan data pelatihan bahwa massa probabilitas distribusi vektor label kira-kira sama di atas lipatan. Misalnya

import numpy as np

np.random.seed(1)
y = np.random.randint(0, 2, (5000, 5))
y = y[np.where(y.sum(axis=1) != 0)[0]]


def proba_mass_split(y, folds=7):
    obs, classes = y.shape
    dist = y.sum(axis=0).astype('float')
    dist /= dist.sum()
    index_list = []
    fold_dist = np.zeros((folds, classes), dtype='float')
    for _ in xrange(folds):
        index_list.append([])
    for i in xrange(obs):
        if i < folds:
            target_fold = i
        else:
            normed_folds = fold_dist.T / fold_dist.sum(axis=1)
            how_off = normed_folds.T - dist
            target_fold = np.argmin(np.dot((y[i] - .5).reshape(1, -1), how_off.T))
        fold_dist[target_fold] += y[i]
        index_list[target_fold].append(i)
    print("Fold distributions are")
    print(fold_dist)
    return index_list

if __name__ == '__main__':
    proba_mass_split(y)

Untuk mendapatkan pelatihan normal, pengujian indeks yang dihasilkan KFold Anda ingin menulis ulang bahwa untuk itu mengembalikan np.setdiff1d dari setiap indeks dengan np.arange (y.shape [0]), lalu bungkus dalam kelas dengan metode iter .

Jessica Mick
sumber
Terima kasih atas penjelasannya. Saya hanya ingin memeriksa sesuatu, apakah OneVsRestClassifiermenerima array 2D (misalnya ydalam kode contoh Anda) atau tupel daftar label kelas? Saya bertanya karena saya melihat contoh klasifikasi multi-label pada scikit-learn tadi dan melihat bahwa make_multilabel_classificationfungsinya mengembalikan tupel daftar label kelas, misalnya ([2], [0], [0, 2], [0]...)ketika menggunakan 3 kelas?
chippies
2
Ini bekerja dua arah. Ketika daftar tuple dilewatkan, itu cocok dengan sklearn.preprocessing.LabelBinarizer untuk itu. Anda tahu beberapa algoritme berfungsi dalam kasus multilabel multilabel. Khususnya RandomForest.
Jessica Mick
Terima kasih banyak, ini setidaknya membuat saya melewati kecelakaan. Untuk saat ini saya telah beralih ke validasi silang K-fold tapi saya pikir saya akan segera menggunakan kode Anda. Namun sekarang, skor yang dikembalikan oleh cross_val_score hanya memiliki dua kolom, yaitu seolah-olah hanya ada dua kelas. Mengubah untuk metrics.confusion_matrixmenghasilkan matriks kebingungan 2x2. Apakah ada metrik yang mendukung pengklasifikasi multi-label?
chippies
Saya telah menjawab sendiri pertanyaan saya. Metrik yang mendukung multi-label classifier hanya muncul di scikit-learn 0.14-rc, jadi saya harus memutakhirkan jika saya menginginkan kemampuan itu, atau melakukannya sendiri. Terima kasih atas bantuan dan kodenya.
chippies
Saya menghapus susunan pada pernyataan kembali. Tidak ada alasan bahwa Anda akan selalu menemukan kumpulan titik data yang dipartisi sempurna. Beri tahu saya jika ini berhasil. Anda juga harus menulis beberapa tes dalam kode Anda. Saya agak menghirup algoritma ini setelah menatap algoritma optimasi cembung sepanjang hari.
Jessica Mick
3

Anda mungkin ingin memeriksa: Pada stratifikasi data multi-label .

Di sini penulis pertama kali menceritakan ide sederhana pengambilan sampel dari label unik dan kemudian memperkenalkan pendekatan baru stratifikasi iteratif untuk dataset multi-label.

Pendekatan stratifikasi iteratif serakah.

Untuk tinjauan singkat, inilah yang dilakukan oleh stratifikasi iteratif:

Pertama, mereka mencari tahu berapa banyak contoh yang harus dimasukkan ke dalam masing-masing lipatan k.

  • ijcij

  • lDl

  • Dlkckjll

  • kc

Gagasan utamanya adalah pertama-tama fokus pada label-label yang langka, ide ini berasal dari hipotesis itu

"jika label langka tidak diperiksa dalam prioritas, maka label tersebut dapat didistribusikan dengan cara yang tidak diinginkan, dan ini tidak dapat diperbaiki selanjutnya"

Untuk memahami bagaimana ikatan rusak dan detail lainnya, saya akan merekomendasikan membaca koran. Juga, dari bagian percobaan apa yang dapat saya pahami adalah, tergantung pada labelset / contoh rasio seseorang mungkin ingin menggunakan labelset unik atau metode stratifikasi iteratif yang diusulkan ini. Untuk nilai yang lebih rendah dari rasio ini, distribusi label pada lipatan dekat atau lebih baik dalam beberapa kasus sebagai stratifikasi berulang. Untuk nilai yang lebih tinggi dari rasio ini, stratifikasi iteratif terbukti mempertahankan distribusi yang lebih baik di lipatan.

phoxis
sumber
1
tautan ke PDF dari makalah yang disebutkan: lpis.csd.auth.gr/publications/sechidis-ecmlpkdd-2011.pdf
Temak