@@ -928,43 +928,42 @@ def _score(self, X, y=None, cast_to_float=True, return_predictions=False):
928928 score_node ._skrub_impl .scorers , mode = "fit_transform" , environment = env
929929 )
930930 all_scores = []
931- predictions = env .get ("_skrub_predictions" , {})
932- cache = {(k , id (X )): v for k , v in predictions .items ()}
931+ cache = dict (env .get ("_skrub_predictions" , {}))
933932 caching_estimator = _CachingXyPipeline (
934- self .data_op , self .environment , cache = cache
933+ self .data_op , self .environment , X_id = id ( X ), cache = cache
935934 )
936935 _copy_attr (self , caching_estimator , ["_is_fitted" ])
937936 for scorer_info in scorers :
938937 scorer = self ._prepare_scorer (scorer_info ["scoring" ], scorer_info ["kwargs" ])
939938 scorer_output = scorer (caching_estimator , X , y )
940939 all_scores .extend (self ._process_scores (scorer_info , scorer_output ))
941- updated_predictions = {
942- k : v for ((k , X_id ), v ) in cache .items () if X_id == id (X )
943- }
944940 rename = unique_renaming ()
945941 result = {rename (name ): score for name , score in all_scores }
946942 if cast_to_float and len (result ) == 1 and not return_predictions :
947943 # If there is a single score stick to scikit-learn interface which
948944 # returns a number.
949945 return next (iter (result .values ()))
950- return (result , updated_predictions ) if return_predictions else result
946+ return (result , caching_estimator . cache ) if return_predictions else result
951947
952948
953949class _CachingXyPipeline (_XyPipeline ):
954- def __init__ (self , data_op , environment , cache ):
950+ def __init__ (self , data_op , environment , X_id , cache ):
955951 super ().__init__ (data_op , environment )
952+ self .X_id = X_id
956953 self .cache = cache
957954
958955 def _eval_in_mode (self , mode , X , y = None ):
959- if y is not None :
956+ if y is not None or id ( X ) != self . X_id :
960957 # Only use caching for methods like predict, predict_proba etc.
961- # (They are the only ones to be called anyway unless a scorer does
962- # something very weird)
958+ # (for which y is None), and when they are called on the "main" X,
959+ # the X that was passed to SkrubLearner.score . For example if a
960+ # scorer computes a metric on a subsample of the dataset (e.g. a
961+ # group for fairness etc.), we do not use the cached result for
962+ # that different input.
963963 return super ()._eval_in_mode (mode , X , y = y )
964- key = (mode , id (X ))
965- if key not in self .cache :
966- self .cache [key ] = super ()._eval_in_mode (mode , X , y = y )
967- return self .cache [key ]
964+ if mode not in self .cache :
965+ self .cache [mode ] = super ()._eval_in_mode (mode , X , y = y )
966+ return self .cache [mode ]
968967
969968 def _score (self , X , y = None ):
970969 # If a scorer calls score(), eval in "score" mode (i.e. ignoring
0 commit comments