Як використовувати функції крос-валідації 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

Зауважте, що навчання класифікатора на багато міток не виходить з ладу, але перехресне підтвердження все-таки є. Як я повинен виконати перехресну перевірку цього класифікатора на багато міток?

Я також написав другу версію, яка розбиває проблему на навчання та перехресне підтвердження 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. Це не має великого сенсу в тому випадку, коли багатоцільовий вектор може мати більше однієї мітки на спостереження.

Існує дві можливі інтерпретації стратифікованих у цьому сенсі.

нi=1н2н

Інший варіант - спробувати сегментувати дані тренінгу, що маса ймовірностей розподілу міток векторів приблизно однакова за складками. Напр

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]), а потім оберніть це в класі методом iter .


Дякую за це пояснення. Я просто хотів би щось перевірити, чи OneVsRestClassifierприймає 2D масив (наприклад, yу вашому прикладі коду) чи пакет списків міток класу? Я запитую, тому що я переглянув приклад класифікації на багато міток на scikit-learn і зараз побачив, що make_multilabel_classificationфункція повертає ряд списків міток класу, наприклад, ([2], [0], [0, 2], [0]...)при використанні 3 класів?
чіпси

2
Це працює обома способами. Коли список кортежів передається, він підходить до sklearn.preprocessing.LabelBinarizer до нього. Ви знаєте, що деякі алгоритми працюють у випадку багаторівневого багатошарового. Особливо RandomForest.
Джессіка Мік

Велике спасибі, це принаймні мене пройшло повз аварій. На даний момент я перейшов до перехресної перевірки K-кратного, але думаю, що скоро скористаюся вашим кодом. Однак тепер результат, повернутий cross_val_score, має лише два стовпці, тобто, як ніби є лише два класи. Змінення metrics.confusion_matrixстворює 2х2 матриці плутанини. Чи підтримує будь-який показник багатокласні класифікатори?
чіпси

Я відповів на власне підпитання. Метрики, які підтримують багатокласні класифікатори, з'явилися лише в scikit-learn 0,14-rc, тому мені доведеться оновити, якщо я хочу цю здатність, або зробити це самостійно. Дякуємо за допомогу та код.
чіпси

Я видалив масив на заяві про повернення. Немає причини, що ви завжди знайдете ідеально розподілений набір точок даних. Повідомте мене, якщо це вийде. Ви також повинні написати кілька тестів у своєму коді. Я якось видихнув цей алгоритм після того, як увесь день дивився на опуклі алгоритми оптимізації.
Джессіка Мік

3

Ви можете перевірити: Про стратифікацію даних, що містять багато міток .

Тут автори спочатку розповідають просту ідею вибірки з унікальних наборів міток, а потім вводять нову підхідну ітераційну стратифікацію для множинних наборів даних.

Підхід ітеративної стратифікації є жадібним.

Для швидкого огляду, ось що робить ітеративна стратифікація:

Спочатку вони з'ясовують, скільки прикладів має входити в кожну з k-складків.

  • ijcij

  • лDл

  • Dлкcкjлл

  • кc

Основна ідея полягає в тому, щоб спочатку зосередитись на етикетках, які рідкісні; ця ідея випливає з гіпотези, що

"якщо рідкісні етикетки не досліджуються в пріоритеті, вони можуть поширюватися небажаним способом, і це неможливо відновити згодом"

Щоб зрозуміти, як розриваються зв’язки та інші деталі, рекомендую прочитати статтю. Також із розділу експериментів те, що я можу зрозуміти, залежно від співвідношення набір міток / прикладів, можливо, захочеться використовувати унікальний набір міток або цей запропонований ітераційний метод стратифікації. Для менших значень цього співвідношення розподіл міток по складках в кількох випадках близький або кращий як ітеративна стратифікація. Для більш високих значень цього співвідношення показано, що ітеративна стратифікація підтримує кращі розподіли в складках.


1
посилання на PDF згаданого документу: lpis.csd.auth.gr/publications/sechidis-ecmlpkdd-2011.pdf
Temak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.