다중 레이블 분류기에서 scikit-learn의 교차 검증 기능을 사용하는 방법


20

5 개의 클래스가 있고 각 인스턴스가 하나 이상의 클래스에 속할 수있는 데이터 세트에서 다른 분류자를 테스트하고 있으므로 특히 scikit-learn의 다중 레이블 분류기를 사용하고 있습니다 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

다중 레이블 분류기 교육은 충돌하지 않지만 교차 유효성 검사는 수행됩니다. 이 다중 레이블 분류기에 대해 교차 검증을 어떻게 수행해야합니까?

또한 문제를 세 가지 분류기를 훈련하고 교차 검증하는 두 번째 버전을 작성했습니다. 이것은 잘 작동합니다.

여기 내 코드가 있습니다. 기능 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()

우분투 13.04와 scikit-learn 0.12를 사용하고 있습니다. 내 데이터는 모양 (98816, 4) 및 (98816, 5)을 갖는 두 개의 배열 (X 및 Y)의 형태입니다. 즉 인스턴스 당 4 개의 기능과 5 개의 클래스 레이블입니다. 레이블은 1 또는 0이며 해당 클래스 내 멤버십을 나타냅니다. 그것에 대해 많은 문서를 볼 수 없으므로 올바른 형식을 사용하고 있습니까?

답변:


10

계층화 된 샘플링은 클래스 멤버쉽 분포가 KFold 샘플링에서 유지됨을 의미합니다. 이는 대상 벡터가 관측 당 하나 이상의 레이블을 가질 수있는 다중 레이블 경우에 의미가 없습니다.

이러한 의미에서 계층화에 대한 두 가지 가능한 해석이 있습니다.

에 대한 나는=12

다른 옵션은 레이블 벡터 분포의 확률 질량이 접힘에 대해 거의 동일하다는 훈련 데이터를 시도하고 분할하는 것입니다. 예 :

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.arange (y.shape [0])을 사용하여 각 인덱스의 np.setdiff1d를 반환 한 다음 iter 메서드 를 사용하여 클래스에 래핑합니다 .


이 설명에 감사드립니다. 방금 무언가를 확인하고 싶습니다. OneVsRestClassifier예를 들어 2D 배열 (예 : y예제 코드) 또는 튜플 클래스 레이블 목록을 허용합니까? 나는 지금 scikit-learn에 대한 다중 레이블 분류 예제를 살펴보고make_multilabel_classification 함수가 클래스 레이블 목록의 튜플을 반환 예를 들어 ([2], [0], [0, 2], [0]...)3 클래스를 사용할 때?
chippies

2
두 가지 방식으로 작동합니다. 튜플 목록이 전달되면 sklearn.preprocessing.LabelBinarizer에 맞습니다. 멀티 클래스 멀티 라벨 사례에서 일부 알고리즘이 작동한다는 것을 알고 있습니다. 특히 RandomForest.
제시카 믹

정말 고마워요, 적어도 충돌을 지 났어요. 현재 K- 폴드 교차 검증으로 전환했지만 곧 코드를 사용할 것입니다. 그러나 이제 cross_val_score에 의해 반환 된 점수에는 두 개의 열만 있습니다 (예 : 두 개의 클래스 만있는 것처럼). 로 변경 metrics.confusion_matrix하면 2x2 혼동 행렬 이 생성됩니다. 다중 레이블 분류자를 지원하는 메트릭이 있습니까?
칩피

나는 내 자신의 하위 질문에 대답했다. 다중 레이블 분류자를 지원하는 메트릭은 scikit-learn 0.14-rc에만 나타 났으므로 해당 기능을 원하거나 직접 수행해야하는 경우 업그레이드해야합니다. 도움과 코드 감사합니다.
칩피

return 문에서 배열을 제거했습니다. 항상 완벽하게 분할 된 데이터 포인트 집합을 찾을 이유가 없습니다. 이 문제가 해결되면 알려주세요. 또한 코드에 몇 가지 테스트를 작성해야합니다. 나는 하루 종일 볼록 최적화 알고리즘을 쳐다 본 후이 알고리즘을 호흡했습니다.
Jessica Mick

3

: 당신은 확인 할 수 있습니다 멀티 라벨 데이터의 계층화에 .

여기서 저자는 먼저 고유 레이블 세트 에서 샘플링하는 간단한 아이디어를 제시 한 다음 다중 레이블 데이터 세트에 대한 새로운 접근 반복 계층화 를 소개합니다 .

반복 계층화의 접근 방식은 욕심입니다.

간단한 개요를 위해 반복 계층화가 수행하는 작업은 다음과 같습니다.

먼저 그들은 각 k- 폴드에 몇 개의 예제가 들어가야하는지 알아냅니다.

  • ijcij .

  • lDl

  • Dlkckjll

  • 폴드 현재 데이터 포인트 추가k기음

주요 아이디어는 드문 라벨에 초점을 맞추는 것입니다.이 아이디어는 다음과 같은 가설에서 비롯됩니다.

"희귀 한 라벨이 우선적으로 검사되지 않으면, 원치 않는 방식으로 배포 될 수 있으며, 이는 나중에 수리 할 수 ​​없습니다"

관계가 끊어지는 방법과 기타 세부 사항을 이해하려면 논문을 읽는 것이 좋습니다. 또한 실험 섹션에서 이해할 수있는 것은 레이블 세트 / 예제 비율에 따라 고유 레이블 세트 기반 또는이 제안 된 반복 계층화 방법을 사용하려고 할 수 있습니다. 이 비율의 더 낮은 값의 경우, 반복적 층화로서 접힘에 걸친 라벨의 분포는 몇몇 경우에 가깝거나 더 우수하다. 이 비율의 값이 높을수록 반복 성층화가 주름에서 더 나은 분포를 유지하는 것으로 나타났습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.