Почему CalibratedClassifierCV не соответствует прямому класриферу?

Я заметил, что новый sklearn CalibratedClassifierCV, кажется, хуже прямого base_estimator, когда base_estimator равен GradientBoostingClassifer (я не тестировал другие классификаторы). Интересно, что если параметры make_classification:

n_features = 10
n_informative = 3
n_classes = 2

тогда CalibratedClassifierCV кажется небольшим превышением (оценка потерь в лотерее).

Однако в соответствии со следующим классификационным набором данных CalibratedClassifierCV кажется, как правило, нижестоящим:

from sklearn.datasets import make_classification
from sklearn import ensemble
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import log_loss
from sklearn import cross_validation
# Build a classification task using 3 informative features

X, y = make_classification(n_samples=1000,
                           n_features=100,
                           n_informative=30,
                           n_redundant=0,
                           n_repeated=0,
                           n_classes=9,
                           random_state=0,
                           shuffle=False)

skf = cross_validation.StratifiedShuffleSplit(y, 5)

for train, test in skf:

    X_train, X_test = X[train], X[test]
    y_train, y_test = y[train], y[test]

    clf = ensemble.GradientBoostingClassifier(n_estimators=100)
    clf_cv = CalibratedClassifierCV(clf, cv=3, method='isotonic')
    clf_cv.fit(X_train, y_train)
    probas_cv = clf_cv.predict_proba(X_test)
    cv_score = log_loss(y_test, probas_cv)

    clf = ensemble.GradientBoostingClassifier(n_estimators=100)
    clf.fit(X_train, y_train)
    probas = clf.predict_proba(X_test)
    clf_score = log_loss(y_test, probas) 

    print 'calibrated score:', cv_score
    print 'direct clf score:', clf_score
    print

Один проход дал:

enter image description here

Возможно, мне не хватает чего-то о том, как работает CalibratedClassifierCV, или я не использую его правильно, но у меня создалось впечатление, что если что-либо, передача классификатора в CalibratedClassifierCV приведет к повышению производительности по сравнению с base_estimator один.

Может ли кто-нибудь объяснить это наблюдаемое отставание?

Ответы

Ответ 1

Сама калибровка вероятности требует перекрестной проверки, поэтому CalibratedClassifierCV обучает калиброванный классификатор за раз (в этом случае с использованием StratifiedKFold) и принимает среднее значение прогнозируемых вероятностей от каждого классификатора, когда вы вызываете pred_proba(), Это может привести к объяснению эффекта.

Моя гипотеза заключается в том, что если набор обучения невелик по отношению к числу функций и классов, уменьшенный набор тренировок для каждого подклассификатора влияет на производительность, и ансамбль не компенсирует его (или делает его хуже). Кроме того, GradientBoostingClassifier может обеспечить уже довольно хорошие оценки вероятности с самого начала, поскольку функция потерь оптимизирована для оценки вероятности.

Если это правильно, то сборщики классов так же, как и CalibratedClassifierCV, но без калибровки, должны быть хуже, чем один классификатор. Кроме того, эффект должен исчезнуть при использовании большего количества сгибов для калибровки.

Чтобы проверить это, я расширил ваш script, чтобы увеличить количество сгибов и включить ансамблированный классификатор без калибровки, и я смог подтвердить свои прогнозы. 10-кратный откалиброванный классификатор всегда работал лучше, чем отдельный классификатор, а неукалиброванный ансамбль был значительно хуже. В моем прогоне 3-кратный откалиброванный классификатор также на самом деле не хуже, чем у одного классификатора, поэтому это может быть также неустойчивым эффектом. Это подробные результаты по одному и тому же набору данных:

Результаты потери в результате перекрестной проверки

Это код из моего эксперимента:

import numpy as np
from sklearn.datasets import make_classification
from sklearn import ensemble
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import log_loss
from sklearn import cross_validation

X, y = make_classification(n_samples=1000,
                           n_features=100,
                           n_informative=30,
                           n_redundant=0,
                           n_repeated=0,
                           n_classes=9,
                           random_state=0,
                           shuffle=False)

skf = cross_validation.StratifiedShuffleSplit(y, 5)

for train, test in skf:

    X_train, X_test = X[train], X[test]
    y_train, y_test = y[train], y[test]

    clf = ensemble.GradientBoostingClassifier(n_estimators=100)
    clf_cv = CalibratedClassifierCV(clf, cv=3, method='isotonic')
    clf_cv.fit(X_train, y_train)
    probas_cv = clf_cv.predict_proba(X_test)
    cv_score = log_loss(y_test, probas_cv)
    print 'calibrated score (3-fold):', cv_score


    clf = ensemble.GradientBoostingClassifier(n_estimators=100)
    clf_cv = CalibratedClassifierCV(clf, cv=10, method='isotonic')
    clf_cv.fit(X_train, y_train)
    probas_cv = clf_cv.predict_proba(X_test)
    cv_score = log_loss(y_test, probas_cv)
    print 'calibrated score (10-fold:)', cv_score

    #Train 3 classifiers and take average probability
    skf2 = cross_validation.StratifiedKFold(y_test, 3)
    probas_list = []
    for sub_train, sub_test in skf2:
        X_sub_train, X_sub_test = X_train[sub_train], X_train[sub_test]
        y_sub_train, y_sub_test = y_train[sub_train], y_train[sub_test]
        clf = ensemble.GradientBoostingClassifier(n_estimators=100)
        clf.fit(X_sub_train, y_sub_train)
        probas_list.append(clf.predict_proba(X_test))
    probas = np.mean(probas_list, axis=0)
    clf_ensemble_score = log_loss(y_test, probas)
    print 'uncalibrated ensemble clf (3-fold) score:', clf_ensemble_score

    clf = ensemble.GradientBoostingClassifier(n_estimators=100)
    clf.fit(X_train, y_train)
    probas = clf.predict_proba(X_test)
    score = log_loss(y_test, probas)
    print 'direct clf score:', score
    print

Ответ 2

Существует несколько проблем с методом изотонической регрессии (и его реализация в sklearn), которые делают его неоптимальным выбором для калибровки.

В частности:

1) Он соответствует кусочно-постоянной функции, а не плавно меняющейся кривой для калибровочной функции.

2) Кросс-валидация усредняет результаты моделей/калибровок, которые она получает из каждой складки. Однако каждый из этих результатов по-прежнему подходит и откалиброван только на соответствующих складках.

Часто лучшим выбором является класс SplineCalibratedClassifierCV в пакете ML-insights (Отказ от ответственности: я являюсь автором этого пакета). Репозиторий github для пакета здесь.

Он имеет следующие преимущества:

1) Он подходит для кубического сглаживающего сплайна, а не кусочно-постоянной функции.

2) Он использует весь (кросс-валидированный) набор ответов для калибровки и устанавливает базовую модель на полный набор данных. Таким образом, как калибровочная функция, так и базовая модель эффективно обучены полному набору данных.

Вы можете увидеть примеры сравнений здесь и здесь.

В первом примере представлен график, в котором показаны бинарные вероятности обучающего набора (красные точки), независимый тестовый набор (зеленые + знаки) и калибровки, вычисленные методом сплайсинга ML-insights (синяя линия) и метод изотонический-склеарн (серые точки/линия).

Сплайн против изотонической калибровки

Я изменил ваш код, чтобы сравнить методы (и поднял количество примеров). Он демонстрирует, что принцип сплайна-подхода typicall работает лучше (как и в примерах, связанных с выше).

Вот код и результаты:

Код (сначала вам нужно pip install ml_insights):

import numpy as np from sklearn.datasets import make_classification from sklearn import ensemble from sklearn.calibration import CalibratedClassifierCV from sklearn.metrics import log_loss from sklearn import cross_validation import ml_insights as mli

X, y = make_classification(n_samples=10000, n_features=100, n_informative=30, n_redundant=0, n_repeated=0, n_classes=9, random_state=0, shuffle=False)

skf = cross_validation.StratifiedShuffleSplit(y, 5)

for train, test in skf:

X_train, X_test = X[train], X[test]
y_train, y_test = y[train], y[test]

clf = ensemble.GradientBoostingClassifier(n_estimators=100)    
clf_cv_mli = mli.SplineCalibratedClassifierCV(clf, cv=3)
clf_cv_mli.fit(X_train, y_train)
probas_cv_mli = clf_cv_mli.predict_proba(X_test)
cv_score_mli = log_loss(y_test, probas_cv_mli)

clf = ensemble.GradientBoostingClassifier(n_estimators=100)    
clf_cv = CalibratedClassifierCV(clf, cv=3, method='isotonic')
clf_cv.fit(X_train, y_train)
probas_cv = clf_cv.predict_proba(X_test)
cv_score = log_loss(y_test, probas_cv)

clf = ensemble.GradientBoostingClassifier(n_estimators=100)
clf.fit(X_train, y_train)
probas = clf.predict_proba(X_test)
clf_score = log_loss(y_test, probas) 

clf = ensemble.GradientBoostingClassifier(n_estimators=100)    
clf_cv_mli = mli.SplineCalibratedClassifierCV(clf, cv=10)
clf_cv_mli.fit(X_train, y_train)
probas_cv_mli = clf_cv_mli.predict_proba(X_test)
cv_score_mli_10 = log_loss(y_test, probas_cv_mli)

clf = ensemble.GradientBoostingClassifier(n_estimators=100)    
clf_cv = CalibratedClassifierCV(clf, cv=10, method='isotonic')
clf_cv.fit(X_train, y_train)
probas_cv = clf_cv.predict_proba(X_test)
cv_score_10 = log_loss(y_test, probas_cv)

print('\nuncalibrated score: {}'.format(clf_score))
print('\ncalibrated score isotonic-sklearn (3-fold): {}'.format(cv_score))
print('calibrated score mli (3-fold): {}'.format(cv_score_mli))
print('\ncalibrated score isotonic-sklearn (10-fold): {}'.format(cv_score_10))
print('calibrated score mli (10-fold): {}\n'.format(cv_score_mli_10))`

Результаты

uncalibrated score: 1.4475396740876696

calibrated score isotonic-sklearn (3-fold): 1.465140552847886 calibrated score mli (3-fold): 1.3651638065446683

calibrated score isotonic-sklearn (10-fold): 1.4158622673607426 calibrated score mli (10-fold): 1.3620771116522705

uncalibrated score: 1.5097320476479625

calibrated score isotonic-sklearn (3-fold): 1.5189534673089442 calibrated score mli (3-fold): 1.4386253950100405

calibrated score isotonic-sklearn (10-fold): 1.4976505139437257 calibrated score mli (10-fold): 1.4408912879989917

uncalibrated score: 1.4654527691892194

calibrated score isotonic-sklearn (3-fold): 1.493355643575107 calibrated score mli (3-fold): 1.388789694535648

calibrated score isotonic-sklearn (10-fold): 1.419760490609242 calibrated score mli (10-fold): 1.3830851694161692

uncalibrated score: 1.5163851866969407

calibrated score isotonic-sklearn (3-fold): 1.5532628847926322 calibrated score mli (3-fold): 1.459797287154743

calibrated score isotonic-sklearn (10-fold): 1.4748100659449732 calibrated score mli (10-fold): 1.4620173012979816

uncalibrated score: 1.4760935523959617

calibrated score isotonic-sklearn (3-fold): 1.469434735152088 calibrated score mli (3-fold): 1.402024502986732

calibrated score isotonic-sklearn (10-fold): 1.4702032019673137 calibrated score mli (10-fold): 1.3983943648572212

Ответ 3

Точка использования калибровочного классификатора состоит в том, чтобы придумать предсказание вероятности, которое будет немного более гладким, чем обычный классификатор. Это не значит, что вы повышаете производительность базы.

Таким образом, нет никакой гарантии, что вероятности или лог-потеря будут одинаковыми (в той же окрестности, но не одинаковыми). Но если вы построили ваши образцы + вероятности, вы, вероятно, увидите гораздо более приятное распределение.

В основном будут сохранены # образцы выше и ниже порога принятия решения (0.5).