Skip to content

CalibratedClassifierCV should return an estimator calibrated via CV, not an ensemble of calibrated estimators #16145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
aesuli opened this issue Jan 17, 2020 · 5 comments · Fixed by #17856

Comments

@aesuli
Copy link
Contributor

aesuli commented Jan 17, 2020

The current cross validation procedure adopted in the CalibratedClassifierCV does not follow the cross validation procedure described in the original Platt paper:
[Platt99] Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods, J. Platt, (1999)

I checked also the other papers cited in the references for the CalibratedClassifierCV class and none of them describes the cross validation process it implements.

CalibratedClassifierCV currently fits and calibrates an estimator for each fold (calibration is performed on the test part of the fold).
All the estimators fit at each fold are kept in a list.
At prediction time, every estimator makes a prediction and the average of the returned values is the final prediction.

The estimator produced by CalibratedClassifierCV is thus an ensemble and not a single estimator calibrated on the whole training set via CV.
When using cross validation the original base_estimator is not used to make the prediction.

Platt99, describes a cross validation procedure that fits an estimator on each fold and the predictions for the test fold are saved.
Then the predictions from all the folds are concatenated in a single list, and calibration parameters for the base_estimator are determined using such list.

Cross validation should be only a mean to calibrate the base_estimator on the same data it has been fit, not to fit a different estimator.

The procedure described in Platt99 is what one would expect from a proper application of a cross validation procedure, as the cross validation only determines the parameters of the calibration and does not fit the estimator.
It is also more efficient, as is does not store the estimators for each fold and requires a single predict at prediction time.

@glemaitre
Copy link
Member

Then the predictions from all the folds are concatenated in a single list, and calibration parameters for the base_estimator are determined using such list.

In other words, we should use cross_val_predict instead of a cross_val_score.

The procedure described in Platt99 is what one would expect from a proper application of a cross validation procedure, as the cross validation only determines the parameters of the calibration and does not fit the estimator.
It is also more efficient, as is does not store the estimators for each fold and requires a single predict at prediction time.

IMO, using the ensemble will be more stable and powerful than using a single learner. I assume that it should attenuate changes linked to model parameters found for different splits.

ping @GaelVaroquaux @agramfort you should be better aware than me for commenting.

@agramfort
Copy link
Member

@aesuli I thing I understand what you asking. Before suggesting any change to the way we do things 2 questions:

  • you suggest that CalibratedClassifierCV should not change the base_estimator . If so how do you end a pipeline with such an estimator? where do you get your pretrain model from? can I still do this with fit(X, y) from scratch?
  • in libsvm https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/svm/src/libsvm/svm.cpp#L2099 I see a for loop over folds when I fit with probability=True which is what we tried to replicate offering more than platt scaling / sigmoid for calibration method. Are you suggesting that what we implement is not exactly what libsvm does? thanks

@aesuli
Copy link
Contributor Author

aesuli commented Jan 19, 2020

@glemaitre right! cross_val_predict can be the way to get CV predictions for the whole training set and then compute calibration from them.

@agramfort

  • you suggest that CalibratedClassifierCV should not change the base_estimator . If so how do you end a pipeline with such an estimator? where do you get your pretrain model from? can I still do this with fit(X, y) from scratch?
    I meant that the CalibratedClassifierCV should not alter the form of the base_estimator.
    If the base estimator is a linear_svm it should fit a linear_svm on the whole training set, not fit an ensemble of linear_svm on folds.

The fit(X,y) of the CalibratedClassifierCV could be something like (rough pseudo code):

def fit(X,y):
  if not self.cv == 'prefit': #Platt99-style CV calibration
    self.base_estimator.fit(X,y)
    cv_predictions = cross_val_predict(self.base_estimator, X, y, cv=self.cv)
    self.calibration_params = self.estimate_calibration(cv_predictions)
  else: # prefit, assuming base_estimator is already fit on different data
    predictions = self.base_estimator.predict_proba(X)
    self.calibration_params = self.estimate_calibration(predictions)

def predict_proba(X):
    predictions = self.base_estimator.predict_proba(X)
    calibrated_predictions = self.apply_calibration_model(predictions, self.calibration_params)
    return calibrated_predictions

Yes the current CalibratedClassiferCV implementation differs from libsvm.
svm.cpp performs CV only to get predictions that are all stored together in dec_values
dev_values is populated in various points of the code depending on the composition of the training set for a fold (see here and here).
At the end of each fold the model fitted for that fold is destroyed.
The calibration parameters are computed on all the predictions for the training set.

@agramfort
Copy link
Member

agramfort commented Jan 19, 2020 via email

@aesuli
Copy link
Contributor Author

aesuli commented Jan 20, 2020

OK, I'll work on this in the next days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants