วิธีการใช้ฟังก์ชั่นการตรวจสอบความถูกต้องข้ามของ Scikit-Learn กับตัวแยกประเภทหลายฉลาก


20

ผมทดสอบลักษณนามแตกต่างกันในชุดข้อมูลที่มี 5 ชั้นเรียนและเช่นกันสามารถอยู่ในหนึ่งหรือมากกว่าหนึ่งของการเรียนเหล่านี้ดังนั้นฉันใช้ scikit sklearn.multiclass.OneVsRestClassifierการเรียนรู้ของลักษณนามหลายป้ายโดยเฉพาะ sklearn.cross_validation.StratifiedKFoldตอนนี้ผมต้องการที่จะดำเนินการตรวจสอบข้ามใช้ สิ่งนี้ทำให้เกิดข้อผิดพลาดดังต่อไปนี้:

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

โปรดทราบว่าการฝึกอบรมตัวจําแนกฉลากหลายป้ายนั้นไม่ผิดพลาด แต่การตรวจสอบข้าม ฉันจะต้องทำการตรวจสอบข้ามสำหรับตัวแยกประเภทหลายฉลากนี้ได้อย่างไร

ฉันยังได้เขียนเวอร์ชันที่สองที่แยกแยะปัญหาในการฝึกอบรมและการตรวจสอบความถูกต้องของตัวแยกประเภท 5 ตัว มันใช้งานได้ดี

นี่คือรหัสของฉัน ฟังก์ชั่นtest_classifier_multilabelเป็นสิ่งที่ทำให้เกิดปัญหา test_classifierคือความพยายามอื่นของฉัน (การแบ่งปัญหาออกเป็น 5 ตัวแยกประเภทและ 5 การตรวจสอบความถูกต้องข้าม)

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 และ scikit-learn 0.12 ข้อมูลของฉันอยู่ในรูปแบบของสองอาร์เรย์ (X และ Y) ที่มีรูปร่าง (98816, 4) และ (98816, 5) เช่น 4 คุณสมบัติต่ออินสแตนซ์และ 5 คลาสฉลาก เลเบลเป็น 1 หรือ 0 เพื่อระบุความเป็นสมาชิกภายในคลาสนั้น ฉันใช้รูปแบบที่ถูกต้องเนื่องจากฉันไม่เห็นเอกสารมากมายเกี่ยวกับสิ่งนั้นหรือไม่

คำตอบ:


10

การสุ่มตัวอย่างแบบแบ่งชั้นหมายความว่าการกระจายความเป็นสมาชิกคลาสจะถูกสงวนไว้ในการสุ่มตัวอย่าง KFold ของคุณ สิ่งนี้ไม่สมเหตุสมผลในหลายกรณีที่เวกเตอร์เป้าหมายของคุณอาจมีป้ายกำกับมากกว่าหนึ่งรายการต่อการสังเกต

มีสองการตีความที่เป็นไปได้ของการแบ่งชั้นในแง่นี้

nΣผม=1n2n

อีกทางเลือกหนึ่งคือการลองและจัดกลุ่มข้อมูลการฝึกอบรมที่มวลความน่าจะเป็นของการแจกแจงฉลากเวกเตอร์นั้นเท่ากันโดยประมาณ เช่น

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)

ที่จะได้รับการฝึกอบรมตามปกติการทดสอบดัชนีที่ KFold ผลิตที่คุณต้องการที่จะเขียนว่ามันจะกลับ np.setdiff1d ของแต่ละดัชนี np.arange (y.shape [0]) แล้วห่อว่าในชั้นเรียนกับเราเตอร์วิธี


ขอบคุณสำหรับคำอธิบายนี้ ฉันต้องการตรวจสอบบางอย่างOneVsRestClassifierรับอาร์เรย์ 2 มิติ (เช่นyในโค้ดตัวอย่างของคุณ) หรือ tuple ของรายการของป้ายชื่อชั้นเรียนหรือไม่ ฉันถามเพราะฉันดูตัวอย่างการจัดหมวดหมู่หลายฉลากบน scikit- เรียนรู้เมื่อเร็ว ๆ นี้และเห็นว่าmake_multilabel_classificationฟังก์ชั่นคืนค่า tuple ของรายการของป้ายชื่อชั้นเรียนเช่น([2], [0], [0, 2], [0]...)เมื่อใช้ 3 ชั้น?
chippies

2
มันทำงานได้ทั้งสองวิธี เมื่อรายการ tuples ถูกส่งผ่านมันจะเข้ากันได้กับ sklearn.preprocessing.LabelBinarizer คุณรู้ว่าอัลกอริธึมบางอย่างทำงานในกรณีหลายคลาสมัลติเลเบล ป่าสุ่มที่สะดุดตา
เจสสิก้ามิก

ขอบคุณมากสิ่งนี้ทำให้ฉันผ่านการล่มได้ สำหรับช่วงเวลาที่ฉันเปลี่ยนไปใช้การตรวจสอบความถูกต้องข้าม K-fold แต่ฉันคิดว่าฉันจะใช้รหัสของคุณในไม่ช้า อย่างไรก็ตามในตอนนี้คะแนนที่ส่งกลับโดย cross_val_score มีเพียงสองคอลัมน์นั่นคือถ้ามีเพียงสองคลาส เปลี่ยนเป็นmetrics.confusion_matrixเมทริกซ์ความสับสน 2x2 เมตริกใด ๆ สนับสนุนตัวแยกประเภทหลายป้ายกำกับหรือไม่
chippies

ฉันตอบคำถามย่อยของฉันเองแล้ว ตัวชี้วัดที่รองรับตัวแยกประเภทหลายฉลากจะปรากฏใน scikit-Learn 0.14-rc เท่านั้นดังนั้นฉันจะต้องอัปเกรดถ้าฉันต้องการความสามารถนั้นหรือทำด้วยตัวเอง ขอบคุณสำหรับความช่วยเหลือและรหัส
chippies

ฉันลบอาเรย์บนคำสั่งส่งคืน ไม่มีเหตุผลใดที่คุณจะพบจุดข้อมูลที่แบ่งพาร์ติชันอย่างสมบูรณ์แบบเสมอ แจ้งให้เราทราบว่าสิ่งนี้ได้ผลหรือไม่ คุณควรเขียนการทดสอบในโค้ดของคุณด้วย ฉันสูดลมหายใจออกอัลกอริธึมนี้หลังจากที่มองอัลกอริธึมการปรับให้เหมาะสมที่สุดตลอดวัน
เจสสิก้ามิก

3

คุณอาจต้องการตรวจสอบ: เกี่ยวกับการแบ่งชั้นของข้อมูลหลายฉลากในการแบ่งชั้นของข้อมูลหลายป้าย

ที่นี่ผู้เขียนแรกบอกความคิดที่เรียบง่ายของการสุ่มตัวอย่างจากป้ายชื่อที่ไม่ซ้ำกันและจากนั้นแนะนำวิธีการแบ่งชั้นซ้ำวิธีการใหม่สำหรับชุดข้อมูลหลายป้าย

วิธีการแบ่งชั้นซ้ำเป็นโลภ

สำหรับภาพรวมอย่างรวดเร็วนี่คือสิ่งที่แบ่งชั้นซ้ำ:

ก่อนอื่นพวกเขาจะพบว่ามีตัวอย่างจำนวนเท่าใดที่ควรเข้าไปใน k-fold แต่ละอัน

  • ผมJผมJ

  • ล.Dล.

  • Dล.kkJล.ล.

  • k

แนวคิดหลักคือการมุ่งเน้นไปที่ฉลากที่หายากเป็นครั้งแรกความคิดนี้มาจากสมมติฐานที่ว่า

"หากไม่มีการตรวจสอบฉลากหายากที่มีลำดับความสำคัญฉลากนั้นอาจถูกแจกจ่ายในลักษณะที่ไม่พึงประสงค์และไม่สามารถซ่อมแซมได้ในภายหลัง"

เพื่อให้เข้าใจถึงความสัมพันธ์ที่ขาดและรายละเอียดอื่น ๆ ฉันจะแนะนำให้อ่านกระดาษ นอกจากนี้จากส่วนการทดลองสิ่งที่ฉันเข้าใจคือขึ้นอยู่กับอัตราส่วนของชุดป้ายกำกับ / ตัวอย่างหนึ่งที่อาจต้องการใช้ชุดป้ายกำกับที่ไม่ซ้ำกันหรือวิธีการแบ่งชั้นซ้ำที่เสนอนี้ สำหรับค่าที่ต่ำกว่าของอัตราส่วนนี้การกระจายของฉลากข้ามครึ่งจะใกล้เคียงหรือดีกว่าในบางกรณีเนื่องจากการแบ่งชั้นซ้ำ สำหรับค่าที่สูงขึ้นของอัตราส่วนนี้การแบ่งชั้นซ้ำจะแสดงให้เห็นว่ายังคงมีการแจกแจงที่ดีขึ้นในเท่า


1
ลิงก์ไปยัง PDF ของบทความที่กล่าวถึง: lpis.csd.auth.gr/publications/sechidis-ecmlpkdd-2011.pdf
Temak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.