Beispiel #1
0
 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)
Beispiel #2
0
    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]
Beispiel #3
0
 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
Beispiel #4
0
    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
Beispiel #5
0
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
Beispiel #6
0
 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
Beispiel #7
0
    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
Beispiel #9
0
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
Beispiel #10
0
    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')