import numpy as np
from scipy.special import expit, xlogy
from scipy.optimize import minimize
from sklearn.base import clone
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold
from sklearn.svm import LinearSVC
from sklearn.calibration import CalibratedClassifierCV as skCalibratedClassifierCV
class CalibratedClassifierCV():
def __init__(self, base_estimator=LinearSVC(random_state=0)):
self.base_estimator = base_estimator
def fit(self, X, y):
self.classes_, y_train = np.unique(y, return_inverse=True)
self.calibrated_classifiers_ = []
cv = StratifiedKFold()
for train, test in cv.split(X, y):
this_estimator = clone(self.base_estimator)
this_estimator.fit(X[train], y_train[train])
calibrated_classifier = _CalibratedClassifier(this_estimator)
calibrated_classifier.fit(X[test], y_train[test])
self.calibrated_classifiers_.append(calibrated_classifier)
return self
def predict_proba(self, X):
proba = np.zeros((X.shape[0], len(self.classes_)))
for i in range(len(self.calibrated_classifiers_)):
proba += self.calibrated_classifiers_[i].predict_proba(X)
proba /= len(self.calibrated_classifiers_)
return proba
def predict(self, X):
proba = self.predict_proba(X)
return self.classes_[np.argmax(proba, axis=1)]
class _CalibratedClassifier():
def __init__(self, base_estimator=LinearSVC(random_state=0)):
self.base_estimator = base_estimator
def fit(self, X, y):
self.n_classes = len(np.unique(y))
y_train = np.zeros((y.shape[0], self.n_classes))
for i in range(self.n_classes):
y_train[y == i, i] = 1
y_prob = self.base_estimator.decision_function(X)
if self.n_classes == 2:
y_train = y_train[:, 1].reshape(-1, 1)
y_prob = y_prob.reshape(-1, 1)
self.calibrators_ = []
for i, cur_prob in enumerate(y_prob.T):
calibrator = _SigmoidCalibration()
calibrator.fit(cur_prob, y_train[:, i])
self.calibrators_.append(calibrator)
return self
def predict_proba(self, X):
prob = np.zeros((X.shape[0], self.n_classes))
y_prob = self.base_estimator.decision_function(X)
if self.n_classes == 2:
prob[:, 1] = self.calibrators_[0].predict(y_prob)
prob[:, 0] = 1 - prob[:, 1]
else:
for i, cur_prob in enumerate(y_prob.T):
prob[:, i] = self.calibrators_[i].predict(cur_prob)
prob /= np.sum(prob, axis=1)[:, np.newaxis]
return prob
class _SigmoidCalibration():
@staticmethod
def _cost_grad(AB, X, y):
P = expit(-(AB[0] * X + AB[1]))
cost = np.sum(-(xlogy(y, P) + xlogy(1 - y, 1 - P)))
dA = np.dot(y - P, X)
dB = np.sum(y - P)
grad = np.array([dA, dB])
return cost, grad
def fit(self, X, y):
prior0 = np.sum(y == 0)
prior1 = y.shape[0] - prior0
y_train = np.zeros(y.shape[0])
y_train[y == 1] = (prior1 + 1) / (prior1 + 2)
y_train[y == 0] = 1 / (prior0 + 2)
AB0 = np.array([0, np.log((prior0 + 1) / (prior1 + 1))])
res = minimize(fun=self._cost_grad, jac=True, x0=AB0,
args=(X, y_train), method='L-BFGS-B')
self.a_, self.b_ = res.x[0], res.x[1]
return self
def predict(self, X):
return expit(-(self.a_ * X + self.b_))
X, y = load_iris(return_X_y=True)
X, y = X[y != 2], y[y != 2]
clf1 = CalibratedClassifierCV(base_estimator=LinearSVC(max_iter=10000, random_state=0)).fit(X, y)
clf2 = skCalibratedClassifierCV(base_estimator=LinearSVC(max_iter=10000, random_state=0)).fit(X, y)
prob1 = clf1.predict_proba(X)
prob2 = clf2.predict_proba(X)
assert np.allclose(prob1, prob2)
pred1 = clf1.predict(X)
pred2 = clf2.predict(X)
assert np.allclose(pred1, pred2)
X, y = load_iris(return_X_y=True)
X, y = X[y != 2], y[y != 2]
clf1 = CalibratedClassifierCV(base_estimator=LogisticRegression(max_iter=10000)).fit(X, y)
clf2 = skCalibratedClassifierCV(base_estimator=LogisticRegression(max_iter=10000)).fit(X, y)
prob1 = clf1.predict_proba(X)
prob2 = clf2.predict_proba(X)
assert np.allclose(prob1, prob2, atol=1e-5)
pred1 = clf1.predict(X)
pred2 = clf2.predict(X)
assert np.allclose(pred1, pred2)
X, y = load_iris(return_X_y=True)
clf1 = CalibratedClassifierCV(base_estimator=LinearSVC(max_iter=10000, random_state=0)).fit(X, y)
clf2 = skCalibratedClassifierCV(base_estimator=LinearSVC(max_iter=10000, random_state=0)).fit(X, y)
prob1 = clf1.predict_proba(X)
prob2 = clf2.predict_proba(X)
assert np.allclose(prob1, prob2)
pred1 = clf1.predict(X)
pred2 = clf2.predict(X)
assert np.allclose(pred1, pred2)
X, y = load_iris(return_X_y=True)
clf1 = CalibratedClassifierCV(base_estimator=LogisticRegression(max_iter=10000)).fit(X, y)
clf2 = skCalibratedClassifierCV(base_estimator=LogisticRegression(max_iter=10000)).fit(X, y)
prob1 = clf1.predict_proba(X)
prob2 = clf2.predict_proba(X)
assert np.allclose(prob1, prob2)
pred1 = clf1.predict(X)
pred2 = clf2.predict(X)
assert np.allclose(pred1, pred2)