Почему 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).