Extra estimator.fit()
call and source of wrapper predictions
#536
-
Hi, thanks for this very useful project. I have a question which is most likely trivial, but I can't seem to find the answer in the docs. I was wondering why MAPIE does an extra model = SomeEstimator()
model.fit(X_train, y_train)
y_pred_plain = model.predict(X_test) vs. model = SomeEstimator()
mapie_model = MapieRegressor(estimator=model, ...)
mapie_model.fit(X_train, y_train)
y_pred_mapie, y_pis = mapie_model.predict(X_test) I would have assumed that
However CV below shows import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from mapie.regression import MapieRegressor
from icecream import ic
class DebugLinearRegression(LinearRegression):
def fit(self, X, y, *args, **kwds):
ic("fit called", X.shape, y.shape)
return super().fit(X, y, *args, **kwds)
def ref_pred(X_train, y_train, X_test):
model = LinearRegression()
model.fit(X_train, y_train)
return model.predict(X_test)
rng = np.random.default_rng(123)
X = rng.random(size=(500, 1))
y = rng.random(size=500)
# cv + 1 fits
ic("cv+")
# ic| 'fit called', X.shape: (400, 1), y.shape: (400,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = DebugLinearRegression()
mapie_model = MapieRegressor(estimator=model, cv=5, method="plus")
mapie_model.fit(X_train, y_train)
y_pred, y_pis = mapie_model.predict(X_test, alpha=[0.05])
assert (ref_pred(X_train, y_train, X_test) == y_pred).all()
# 2 fits, maybe variable PI width depending on
# MapieRegressor(conformity_score=...)
ic("split")
# ic| 'fit called', X.shape: (400, 1), y.shape: (400,)
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = DebugLinearRegression()
mapie_model = MapieRegressor(estimator=model, cv="split", test_size=0.2)
mapie_model.fit(X_train, y_train)
y_pred, y_pis = mapie_model.predict(X_test, alpha=[0.05])
# hmm, very much not exact here, maybe b/c we see
# WARNING: at least one point of training set belongs to every resamplings.
##assert (ref_pred(X_train, y_train, X_test) == y_pred).all()
assert np.allclose(ref_pred(X_train, y_train, X_test), y_pred, atol=1e-1)
# 1 fit, constant PI width
ic("prefit")
# ic| 'fit called', X.shape: (320, 1), y.shape: (320,)
X_train_cal, X_test, y_train_cal, y_test = train_test_split(
X, y, test_size=0.2
)
X_train, X_cal, y_train, y_cal = train_test_split(
X_train_cal, y_train_cal, test_size=0.2
)
model = DebugLinearRegression()
model.fit(X_train, y_train)
mapie_model = MapieRegressor(estimator=model, cv="prefit")
mapie_model.fit(X_cal, y_cal)
y_pred, y_pis = mapie_model.predict(X_test, alpha=[0.05])
assert (ref_pred(X_train, y_train, X_test) == y_pred).all()
# 1 fit, constant PI width
ic("naive")
# ic| 'fit called', X.shape: (400, 1), y.shape: (400,)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = DebugLinearRegression()
mapie_model = MapieRegressor(estimator=model, method="naive", test_size=0.2)
mapie_model.fit(X_train, y_train)
y_pred, y_pis = mapie_model.predict(X_test, alpha=[0.05])
assert (ref_pred(X_train, y_train, X_test) == y_pred).all() |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Hello @elcorto, thank you for the warm feedback, and sorry for the response delay, we tend to forget the GitHub Q&A section! In a cross conformal setting, there is indeed an option to average predictions (using Regarding the In a Let me know if I answered to your question. |
Beta Was this translation helpful? Give feedback.
Hello @elcorto,
the internals of MAPIE are not always optimised at the moment. Particularly, split and cross methods are handled by the same mechanisms, and in the split setting, there is indeed one extra useless fit that we should remove.
This will likely be done after we release MAPIE v1.0.0. The difference you see between 0.9.2 and 0.9.1 is due to some refactoring we started to prepare for the release of v1.