def create_local(self, local_importance_values, evaluation_examples=None, expected_values=None): """Create a local explanation from the list of local feature importance values. :param local_importance_values: The feature importance values. :type local_importance_values: numpy.array or scipy.sparse.csr_matrix or list[scipy.sparse.csr_matrix] :param evaluation_examples: A matrix of feature vector examples (# examples x # features) on which to explain the model's output. :type evaluation_examples: numpy.array or pandas.DataFrame or scipy.sparse.csr_matrix :param expected_values: The expected values of the model. :type expected_values: numpy.array """ local_importance_values = np.array(local_importance_values) # handle the case that the local importance values have a 2d shape for classification scenario # and only specify the positive class if len(local_importance_values.shape) == 2 and self.classification: local_importance_values = np.array( [-local_importance_values, local_importance_values]) kwargs = {ExplainParams.METHOD: self.method} kwargs[ExplainParams.FEATURES] = self.features if self.classification: kwargs[ExplainParams.MODEL_TASK] = ExplainType.CLASSIFICATION else: kwargs[ExplainParams.MODEL_TASK] = ExplainType.REGRESSION kwargs[ExplainParams.LOCAL_IMPORTANCE_VALUES] = local_importance_values kwargs[ExplainParams.EXPECTED_VALUES] = expected_values kwargs[ExplainParams.CLASSIFICATION] = self.classification if evaluation_examples is not None: kwargs[ExplainParams.EVAL_DATA] = evaluation_examples return _create_local_explanation(**kwargs)
def test_get_raw_explanation_no_datasets_mixin(self, boston, mimic_explainer): model = create_sklearn_random_forest_regressor( boston[DatasetConstants.X_TRAIN], boston[DatasetConstants.Y_TRAIN]) explainer = mimic_explainer(model, boston[DatasetConstants.X_TRAIN], LGBMExplainableModel) global_explanation = explainer.explain_global( boston[DatasetConstants.X_TEST]) assert global_explanation.method == LIGHTGBM_METHOD kwargs = {ExplainParams.METHOD: global_explanation.method} kwargs[ExplainParams.FEATURES] = global_explanation.features kwargs[ExplainParams.MODEL_TASK] = ExplainType.REGRESSION kwargs[ ExplainParams. LOCAL_IMPORTANCE_VALUES] = global_explanation._local_importance_values kwargs[ExplainParams.EXPECTED_VALUES] = 0 kwargs[ExplainParams.CLASSIFICATION] = False kwargs[ExplainParams.IS_ENG] = True synthetic_explanation = _create_local_explanation(**kwargs) num_engineered_feats = boston[DatasetConstants.X_TRAIN].shape[1] feature_map = np.eye(5, num_engineered_feats) feature_names = [str(i) for i in range(feature_map.shape[0])] raw_names = feature_names[:feature_map.shape[0]] assert not _DatasetsMixin._does_quack(synthetic_explanation) global_raw_explanation = synthetic_explanation.get_raw_explanation( [feature_map], raw_feature_names=raw_names) self.validate_local_explanation_regression(synthetic_explanation, global_raw_explanation, feature_map, has_eng_eval_data=False, has_raw_eval_data=False, has_dataset_data=False)
def explain_local(self, evaluation_examples): """Explain the function locally by using LIME. :param evaluation_examples: A matrix of feature vector examples (# examples x # features) on which to explain the model's output. :type evaluation_examples: DatasetWrapper :param features: A list of feature names. :type features: list[str] :param classes: Class names as a list of strings. The order of the class names should match that of the model output. Only required if explaining classifier. :type classes: list[str] :return: A model explanation object containing the local explanation. :rtype: LocalExplanation """ if self._datamapper is not None: evaluation_examples = transform_with_datamapper( evaluation_examples, self._datamapper) if self._column_indexer: evaluation_examples.apply_indexer(self._column_indexer) # Compute subset info prior if self.explain_subset: evaluation_examples.take_subset(self.explain_subset) # sample the evaluation examples # note: the sampled data is also used by KNN if self.sampling_policy is not None and self.sampling_policy.allow_eval_sampling: sampling_method = self.sampling_policy.sampling_method max_dim_clustering = self.sampling_policy.max_dim_clustering evaluation_examples.sample(max_dim_clustering, sampling_method=sampling_method) features = self.features if self.explain_subset: features = [features[i] for i in self.explain_subset] kwargs = {ExplainParams.METHOD: ExplainType.LIME} kwargs[ExplainParams.FEATURES] = features kwargs[ExplainParams.NUM_FEATURES] = evaluation_examples.num_features original_evaluation = evaluation_examples.original_dataset evaluation_examples = evaluation_examples.dataset if len(evaluation_examples.shape) == 1: evaluation_examples = evaluation_examples.reshape(1, -1) self._logger.debug('Running LIMEExplainer') if self.classification: kwargs[ExplanationParams.CLASSES] = self.classes kwargs[ExplainType.MODEL_TASK] = ExplainType.CLASSIFICATION num_classes = len(self.classes) labels = list(range(num_classes)) else: kwargs[ExplainType.MODEL_TASK] = ExplainType.REGRESSION num_classes = 1 labels = None lime_explanations = [] tqdm = get_tqdm(self._logger, self.show_progress) if self.explain_subset: self.original_data_ref[0] = original_evaluation self.current_index_list.append(0) for ex_idx, example in tqdm(enumerate(evaluation_examples)): self.current_index_list[0] = ex_idx lime_explanations.append( self.explainer.explain_instance(example, self.explainer.function, labels=labels)) self.current_index_list = [0] else: for ex_idx, example in tqdm(enumerate(evaluation_examples)): lime_explanations.append( self.explainer.explain_instance(example, self.explainer.function, labels=labels)) if self.classification: lime_values = [None] * num_classes for lime_explanation in lime_explanations: for label in labels: map_values = dict(lime_explanation.as_list(label=label)) if lime_values[label - 1] is None: lime_values[label - 1] = [[ map_values.get(feature, 0.0) for feature in self._lime_feature_names ]] else: lime_values[label - 1].append([ map_values.get(feature, 0.0) for feature in self._lime_feature_names ]) else: lime_values = None for lime_explanation in lime_explanations: map_values = dict(lime_explanation.as_list()) if lime_values is None: lime_values = [[ map_values.get(feature, 0.0) for feature in self._lime_feature_names ]] else: lime_values.append([ map_values.get(feature, 0.0) for feature in self._lime_feature_names ]) expected_values = None if self.model is not None: kwargs[ExplainParams.MODEL_TYPE] = str(type(self.model)) else: kwargs[ExplainParams.MODEL_TYPE] = ExplainType.FUNCTION kwargs[ExplainParams.CLASSIFICATION] = self.classification kwargs[ExplainParams.LOCAL_IMPORTANCE_VALUES] = np.array(lime_values) kwargs[ExplainParams.EXPECTED_VALUES] = np.array(expected_values) kwargs[ExplainParams.EVAL_DATA] = original_evaluation explanation = _create_local_explanation(**kwargs) # if transformations have been passed, then return raw features explanation raw_kwargs = _get_raw_explainer_create_explanation_kwargs( kwargs=kwargs) return explanation if self._datamapper is None else _create_raw_feats_local_explanation( explanation, feature_maps=[self._datamapper.feature_map], features=self.features, **raw_kwargs)