Scikit-learn'un çoklu etiket sınıflandırıcılarında çapraz doğrulama işlevlerini kullanma


20

Ben 5 sınıf vardır ve her örnek bu sınıflardan biri veya daha fazla olabilir bir veri kümesi üzerinde farklı sınıflandırıcılar test ediyorum, bu yüzden özellikle scikit-learn çok etiketli sınıflandırıcılar kullanıyorum sklearn.multiclass.OneVsRestClassifier. Şimdi kullanarak çapraz doğrulama yapmak istiyorum sklearn.cross_validation.StratifiedKFold. Bu, aşağıdaki hatayı üretir:

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

Çoklu etiket sınıflandırıcısının eğitiminin çökmediğini, ancak çapraz doğrulamanın başarısız olduğunu unutmayın. Bu çok etiketli sınıflandırıcı için çapraz doğrulamayı nasıl yapmalıyım?

Ayrıca, 5 ayrı sınıflandırıcıyı eğitmek ve çapraz doğrulamak için sorunu parçalayan ikinci bir versiyon da yazdım. Bu gayet iyi çalışıyor.

İşte kodum. Fonksiyon test_classifier_multilabelproblem veren kişidir. test_classifierdiğer girişimim (sorunu 5 sınıflandırıcı ve 5 çapraz doğrulamaya bölmek).

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

Ubuntu 13.04 kullanıyorum ve scikit-learn 0.12. Verilerim (98816, 4) ve (98816, 5) şekline sahip iki dizi (X ve Y) biçimindedir, yani örnek başına 4 özellik ve 5 sınıf etiketi. Etiketler, o sınıf içindeki üyelik için 1 veya 0'dır. Bununla ilgili çok fazla belge görmediğim için doğru biçimi mi kullanıyorum?

Yanıtlar:


10

Tabakalı örnekleme, KFold örnekleminizde sınıf üyeliği dağılımının korunduğu anlamına gelir. Bu, hedef vektörünüzün gözlem başına birden fazla etikete sahip olabileceği çok etiketli durumda çok anlamlı değildir.

Bu bağlamda tabakalı iki olası yorum vardır.

İçin bunlardan en az birinin verir doldurulur etiket benzersiz etiket. Benzersiz etiket kutularının her birinde tabakalı örnekleme yapabilirsiniz.n i = 1 2 nni=1n2n

Diğer seçenek, eğitim verilerinin etiket vektörlerinin dağılımının olasılık kütlesinin kıvrımlar üzerinde yaklaşık olarak aynı olduğunu deneyerek segmentlere ayırmaktır. Örneğin

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)

Normal eğitimi almak için, KFold'un ürettiği test indeksleri, her dizinin np.setdiff1d değerini np.arange (y.shape [0]) ile döndürdüğünü yeniden yazmak istediğinizi, ardından bunu bir iter yöntemiyle bir sınıfa sarın .


Bu açıklama için teşekkürler. Ben sadece bir şey kontrol etmek istiyorum, OneVsRestClassifierkabul bir 2D dizi (örneğin yörnek kodunda) veya sınıf etiketlerinin bir dizi kabul ? Ben soruyorum çünkü ben şimdi scikit-learn çok etiketli sınıflandırma örneğine baktım ve make_multilabel_classificationfonksiyonun, örneğin ([2], [0], [0, 2], [0]...)3 sınıf kullanırken sınıf etiketlerinin bir listesini döndürür gördüm ?
chippies

2
Her iki şekilde de çalışır. Bir tuples listesi geçtiğinde, sklearn.preprocessing.LabelBinarizer'a uyuyor. Algoritmanın birkaçının çok sınıflı çok etiketli durumda çalıştığını biliyorsunuz. Özellikle RandomForest.
Jessica Mick

Çok teşekkürler, bu en azından beni çökmeleri aştı. Şu an K-fold çapraz doğrulamaya geçtim ama yakında kodunuzu kullanacağımı düşünüyorum. Ancak şimdi, cross_val_score tarafından döndürülen puanın sadece iki sütunu vardır, yani sadece iki sınıf varmış gibi. İçin değiştirme metrics.confusion_matrix2x2 karışıklık matrisleri üretir. Metriklerden herhangi biri çoklu etiket sınıflandırıcılarını destekliyor mu?
chippies

Kendi alt sorumu cevapladım. Çoklu etiket sınıflandırıcılarını destekleyen metrikler yalnızca scikit-learn 0.14-rc'de göründü, bu yüzden bu yeteneği istiyorsam yükseltmem veya kendim yapmam gerekir. Yardım ve kod için teşekkürler.
chippies

Dönüş ifadesinde dizi kaldırıldı. Her zaman mükemmel şekilde bölümlenmiş bir veri noktası kümesi bulmanız için hiçbir neden yoktur. Bunun işe yarayıp yaramadığını bana bildirin. Ayrıca kodunuza bazı testler yazmalısınız. Bütün gün dışbükey optimizasyon algoritmalarına baktıktan sonra bu algoritmayı soludum.
Jessica Mick

3

Şunları kontrol etmek isteyebilirsiniz: Çok etiketli verilerin sınıflandırılması hakkında .

Burada yazarlar ilk önce benzersiz etiket kümelerinden basit örnekleme fikrini anlatır ve daha sonra çoklu etiket veri kümeleri için yeni bir yaklaşım yinelemeli katmanlama sunar .

Yinelemeli tabakalaşma yaklaşımı açgözlüdür.

Hızlı bir genel bakış için, yinelemeli tabakalaşmanın yaptığı şudur:

İlk önce k-katlarının her birine kaç örnek girmesi gerektiğini öğrenirler.

  • Etiket başına kat sayısı için istenilen örnek sayısını bulun , .j c j benijcij

  • Henüz k-katlarına dağıtılmamış olan veri kümesinden , örnek sayısının minimum olduğu olan etiketi tanımlanır .D llDl

  • Daha sonra deki her veri noktası için en üst düzeye katını bulun (burada kopmalar). Yani başka bir deyişle: hangi kat etiket için maksimum talebe sahiptir veya etiket göre en dengesizdir .Dlc j k lkckjll

  • Mevcut veri noktasını yukarıdaki adımda bulunan katlama ekleyin, veri noktasını orijinal veri kümesinden kaldırın ve sayım değerlerini ayarlayın ve tüm veri noktaları kıvrımlara dağıtılıncaya kadar devam edin.ckc

Ana fikir ilk olarak nadir bulunan etiketlere odaklanmaktır, bu fikir şu hipotezden gelir:

"nadir etiketler öncelikli olarak incelenmezse, istenmeyen bir şekilde dağıtılabilir ve bu daha sonra tamir edilemez"

Bağların nasıl kırıldığını ve diğer detayları anlamak için makaleyi okumanızı tavsiye ederim. Ayrıca, deneyler bölümünden anlayabildiğim şey, etiket kümesi / örnekler oranına bağlı olarak, benzersiz etiket kümesi tabanlı veya bu önerilen yinelemeli tabakalaşma yöntemini kullanmak isteyebilir. Bu oranın daha düşük değerleri için, etiketlerin kıvrımlar boyunca dağılımı, yinelemeli tabakalaşma olarak birkaç durumda yakın veya daha iyidir. Bu oranın daha yüksek değerleri için, yinelemeli tabakalaşmanın kıvrımlarda daha iyi dağılımları koruduğu gösterilmiştir.


1
Söz konusu makalenin PDF'sine
Temak
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.