def update_predict_fn(self, x): if np.argmax(self.models.sklearn(x).shape) == 0: self.explainer.predictor = self.models.sklearn self.explainer.samplers[0].predictor = self.models.sklearn else: self.explainer.predictor = ArgmaxTransformer(self.models.sklearn) self.explainer.samplers[0].predictor = ArgmaxTransformer(self.models.sklearn)
def __init__(self, nlp: 'spacy.language.Language', predictor: Callable, seed: int = None) -> None: """ Initialize anchor text explainer. Parameters ---------- nlp spaCy object. predictor A callable that takes a tensor of N data points as inputs and returns N outputs. seed If set, ensures identical random streams. """ super().__init__(meta=copy.deepcopy(DEFAULT_META_ANCHOR)) np.random.seed(seed) self.nlp = nlp # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(predictor(['Hello world']).shape) == 0: self.predictor = predictor else: self.predictor = ArgmaxTransformer(predictor) self._synonyms_generator = Neighbors(self.nlp) self.tokens, self.words, self.positions, self.punctuation = [], [], [], [ ] # type: List, List, List, List # dict containing an np.array of similar words with same part of speech and an np.array of similarities self.synonyms = {} # type: Dict[str, Dict[str, np.ndarray]] # the method used to generate samples self.perturbation = None # type: Union[Callable, None]
def _transform_predictor(self, predictor: Callable) -> Callable: # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(predictor(['Hello world']).shape) == 0: return predictor else: transformer = ArgmaxTransformer(predictor) return transformer
def explain(self, inputs: List) -> Explanation: arr = np.array(inputs) # set anchor_tabular predict function so it always returns predicted class # See anchor_tabular.__init__ logging.info("Arr shape %s ", (arr.shape,)) # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(self.predict_fn(arr).shape) == 0: self.anchors_tabular.predictor = self.predict_fn self.anchors_tabular.samplers[0].predictor = self.predict_fn else: self.anchors_tabular.predictor = ArgmaxTransformer(self.predict_fn) self.anchors_tabular.samplers[0].predictor = ArgmaxTransformer( self.predict_fn ) # We assume the input has batch dimension but Alibi explainers presently assume no batch anchor_exp = self.anchors_tabular.explain(arr[0], **self.kwargs) return anchor_exp
def patch_transformer(monkeypatch, request): out_dim, out_type, sz = request.param transformer = ArgmaxTransformer(None) monkeypatch.setattr( transformer, "predictor", MockPredictor(out_dim, out_type=out_type, sz=sz, key=(out_dim, out_type, sz))) return transformer
def explain(self, inputs: List) -> Explanation: arr = np.array(inputs) # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(self.predict_fn(arr).shape) == 0: self.anchors_image.predictor = self.predict_fn else: self.anchors_image.predictor = ArgmaxTransformer(self.predict_fn) logging.info("Calling explain on image of shape %s", (arr.shape, )) logging.info("anchor image call with %s", self.kwargs) anchor_exp = self.anchors_image.explain(arr[0], **self.kwargs) return anchor_exp
def __init__(self, predictor: Callable, feature_names: list, categorical_names: dict = None, seed: int = None) -> None: """ Parameters ---------- predictor A callable that takes a tensor of N data points as inputs and returns N outputs. feature_names List with feature names. categorical_names Dictionary where keys are feature columns and values are the categories for the feature. seed Used to set the random number generator for repeatability purposes. """ super().__init__(meta=copy.deepcopy(DEFAULT_META_ANCHOR)) self.feature_names = feature_names # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(predictor(np.zeros([1, len(feature_names)])).shape) == 0: self.predictor = predictor else: transformer = ArgmaxTransformer(predictor) self.predictor = transformer # define column indices of categorical and numerical (aka continuous) features if categorical_names: self.categorical_features = sorted(categorical_names.keys()) self.feature_values = categorical_names.copy( ) # dict with {col: categorical feature values} else: self.categorical_features = [] self.feature_values = {} self.numerical_features = [ x for x in range(len(feature_names)) if x not in self.categorical_features ] self.samplers = [] # type: list self.seed = seed self.instance_label = None # update metadata self.meta['params'].update(seed=seed)
def explain(self, inputs: List) -> Explanation: if self.anchors_text is None: self.anchors_text = alibi.explainers.AnchorText(self.nlp, self.predict_fn) # We assume the input has batch dimension but Alibi explainers presently assume no batch input_words = inputs[0] # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(self.predict_fn([input_words]).shape) == 0: self.anchors_text.predictor = self.predict_fn else: self.anchors_text.predictor = ArgmaxTransformer(self.predict_fn) anchor_exp = self.anchors_text.explain(input_words, **self.kwargs) return anchor_exp
def test_argmax_transformer(monkeypatch, out_dim, out_type): """ Test that the conversion of output probabilities to class labels works for a range of classifier output types. """ # setup a transformer with a mock predictor transformer = ArgmaxTransformer(None) monkeypatch.setattr( transformer, "predictor", MockPredictor( out_dim, out_type=out_type, ) ) # fake predictor input X = np.random.random(size=(50, 14)) result = transformer(X) # argmax transformer should do get rid of the feature dimension assert len(result.shape) == len(X.shape) - 1
def __init__(self, predictor: Callable, image_shape: tuple, segmentation_fn: Any = 'slic', segmentation_kwargs: dict = None, images_background: np.ndarray = None, seed: int = None) -> None: """ Initialize anchor image explainer. Parameters ---------- predictor A callable that takes a tensor of N data points as inputs and returns N outputs. image_shape Shape of the image to be explained. segmentation_fn Any of the built in segmentation function strings: 'felzenszwalb', 'slic' or 'quickshift' or a custom segmentation function (callable) which returns an image mask with labels for each superpixel. See http://scikit-image.org/docs/dev/api/skimage.segmentation.html for more info. segmentation_kwargs Keyword arguments for the built in segmentation functions. images_background Images to overlay superpixels on. seed If set, ensures different runs with the same input will yield same explanation. """ super().__init__(meta=copy.deepcopy(DEFAULT_META_ANCHOR)) np.random.seed(seed) if isinstance(segmentation_fn, str) and not segmentation_kwargs: try: segmentation_kwargs = DEFAULT_SEGMENTATION_KWARGS[ segmentation_fn] # type: ignore except KeyError: logger.warning( 'DEFAULT_SEGMENTATION_KWARGS did not contain any entry' 'for segmentation method {}. No kwargs will be passed to' 'the segmentation function!'.format(segmentation_fn)) segmentation_kwargs = {} elif callable(segmentation_fn) and segmentation_kwargs: logger.warning( 'Specified both a segmentation function to create superpixels and ' 'keyword arguments for built segmentation functions. By default ' 'the specified segmentation function will be used.') # check if predictor returns predicted class or prediction probabilities for each class # if needed adjust predictor so it returns the predicted class if np.argmax(predictor(np.zeros((1, ) + image_shape)).shape) == 0: self.predictor = predictor else: self.predictor = ArgmaxTransformer(predictor) # segmentation function is either a user-defined function or one of the values in fn_options = { 'felzenszwalb': felzenszwalb, 'slic': slic, 'quickshift': quickshift } if callable(segmentation_fn): self.custom_segmentation = True self.segmentation_fn = segmentation_fn else: self.custom_segmentation = False self.segmentation_fn = partial(fn_options[segmentation_fn], **segmentation_kwargs) self.images_background = images_background self.image_shape = image_shape # [H, W] int array; each int is a superpixel labels self.segments = None # type: np.ndarray self.segment_labels = None # type: list self.image = None # type: np.ndarray # a superpixel is perturbed with prob 1 - p_sample self.p_sample = 0.5 # type: float # update metadata self.meta['params'].update( custom_segmentation=self.custom_segmentation, segmentation_kwargs=segmentation_kwargs, p_sample=self.p_sample, seed=seed, image_shape=image_shape) if not self.custom_segmentation: self.meta['params'].update(segmentation_fn=segmentation_fn) else: self.meta['params'].update(segmentation_fn='custom')