Exemplo n.º 1
0
    def test_point(self):
        res = PointDoi()(self.z)

        self.assertEqual(len(res), 1, 'PointDoi should return a single point')

        self.assertTrue(np.array_equal(B.as_array(res[0]), B.as_array(self.z)),
                        'Value of point should not change')
Exemplo n.º 2
0
    def calc_doi(self, x_input, tf_cell=False):
        x = x_input[0] if tf_cell else x_input
        batch_size = len(x)
        if (self._baseline is None):
            if B.is_tensor(x):
                x = B.as_array(x)
            baseline = np.zeros_like(x)
        else:
            baseline = self._baseline

        tile_dims = [1] * len(baseline.shape)
        tile_dims[0] = batch_size
        baseline = baseline[0, ...]
        baseline = np.tile(baseline, tuple(tile_dims))

        if (B.is_tensor(x) and not B.is_tensor(baseline)):
            baseline = B.as_tensor(baseline)

        if (not B.is_tensor(x) and B.is_tensor(baseline)):
            baseline = B.as_array(baseline)

        r = self._resolution - 1.
        doi_out = [(1. - i / r) * x + i / r * baseline
                   for i in range(self._resolution)]
        if tf_cell:
            doi_out = [[d, x_input[1]] for d in doi_out]
        return doi_out
Exemplo n.º 3
0
    def test_linear_point(self):
        doi = LinearDoi(resolution=1)
        res = doi(self.z)

        self.assertEqual(len(res), 1,
                         'LinearDoi should return `resolution` points')

        self.assertTrue(
            np.array_equal(B.as_array(res[0]), B.as_array(self.z)),
            'When `resolution` is 1, should be the same as PointDoi')
Exemplo n.º 4
0
    def test_internal_channel_axis3(self):
        qoi = InternalChannelQoI(1, channel_axis=3)
        res = qoi(self.z)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([21., 14.])))
Exemplo n.º 5
0
    def test_internal_channel_1d(self):
        qoi = InternalChannelQoI(2)
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([3., -2.])))
Exemplo n.º 6
0
    def test_linear(self):
        doi = LinearDoi(baseline=np.ones(B.int_shape(self.z)), resolution=21)
        res = doi(self.z)

        self.assertEqual(len(res), 21,
                         'LinearDoi should return `resolution` points')

        self.assertTrue(np.array_equal(B.as_array(res[0]), B.as_array(self.z)),
                        'First point should be the original point')

        self.assertTrue(np.all(B.as_array(res[-1]) == 1.),
                        'Last point should be baseline')

        self.assertTrue(
            np.allclose(B.as_array(res[-2]),
                        np.array([[1., 1.05, 1.1], [0.95, 0.9, 0.85]])),
            'Intermediate points should interpolate from baseline')
Exemplo n.º 7
0
    def test_comparative(self):
        qoi = ComparativeQoI(1, 0)
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([1., -1.])))
Exemplo n.º 8
0
    def test_threshold_low_minus_high(self):
        qoi = ThresholdQoI(1.5, low_minus_high=True)
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([-4., -3.])))
Exemplo n.º 9
0
    def test_lambda(self):
        qoi = LambdaQoI(lambda y: y[:, 0] + y[:, 1])
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([3., -1.])))
Exemplo n.º 10
0
    def test_gaussian_non_tensor(self):
        doi = GaussianDoi(var=1., resolution=10)
        res = doi(B.as_array(self.z))

        self.assertEqual(len(res), 10,
                         'GaussianDoi should return `resolution` points')

        self.assertEqual(res[0].shape, B.int_shape(self.z))
Exemplo n.º 11
0
    def test_class(self):
        qoi = ClassQoI(1)
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(np.allclose(B.as_array(res), np.array([2., -1.])))
Exemplo n.º 12
0
    def test_threshold_activation(self):
        qoi = ThresholdQoI(0.75, activation='sigmoid')
        res = qoi(self.y)

        self.assertEqual(B.int_shape(res), (2, ),
                         'Should return one scalar per row in the batch')

        self.assertTrue(
            np.allclose(B.as_array(res), np.array([1.1023126, -0.8881443])))
Exemplo n.º 13
0
    def get_activation_multiplier(self, activation):
        batch_size = len(activation)
        if (self._baseline is None):
            baseline = np.zeros_like(activation)
        else:
            baseline = self._baseline

        tile_dims = [1] * len(baseline.shape)
        tile_dims[0] = batch_size
        baseline = baseline[0, ...]
        baseline = np.tile(baseline, tuple(tile_dims))
        if (B.is_tensor(activation) and not B.is_tensor(baseline)):
            baseline = B.as_tensor(baseline)

        if (not B.is_tensor(activation) and B.is_tensor(baseline)):
            baseline = B.as_array(baseline)

        batch_size = len(activation)
        return activation - baseline
Exemplo n.º 14
0
    def __concatenate_doi(D):
        if len(D) == 0:
            raise ValueError(
                'Got empty distribution of interest. `DoI` must return at '
                'least one point.')

        if isinstance(D[0], DATA_CONTAINER_TYPE):
            transposed = [[] for _ in range(len(D[0]))]
            for point in D:
                for i, v in enumerate(point):
                    transposed[i].append(v)

            return [
                np.concatenate(D_i)
                if isinstance(D_i[0], np.ndarray) else D_i[0]
                for D_i in transposed
            ]

        else:
            if not isinstance(D[0], np.ndarray):
                D = [B.as_array(d) for d in D]
            return np.concatenate(D)
Exemplo n.º 15
0
    def __call__(self, z: ArrayLike) -> List[ArrayLike]:
        if isinstance(z, (list, tuple)) and len(z) == 1:
            z = z[0]

        self._assert_cut_contains_only_one_tensor(z)

        if self._baseline is None:
            baseline = B.zeros_like(z)
        else:
            baseline = self._baseline

        if (B.is_tensor(z) and not B.is_tensor(baseline)):
            baseline = B.as_tensor(baseline)

        if (not B.is_tensor(z) and B.is_tensor(baseline)):
            baseline = B.as_array(baseline)

        r = 1. if self._resolution is 1 else self._resolution - 1.

        return [
            (1. - i / r) * z + i / r * baseline
            for i in range(self._resolution)
        ]
Exemplo n.º 16
0
    def test_max_class_activation_function(self):
        qoi = MaxClassQoI(activation=B.softmax)
        res = qoi(self.y)

        self.assertTrue(
            np.allclose(B.as_array(res), np.array([0.66524096, 0.66524096])))
Exemplo n.º 17
0
    def qoi_bprop(self,
                  qoi,
                  model_args,
                  model_kwargs={},
                  doi_cut=None,
                  to_cut=None,
                  attribution_cut=None,
                  intervention=None):
        """
        qoi_bprop Run the model from the from_layer to the qoi layer
            and give the gradients w.r.t `attribution_cut`

        Parameters
        ----------
        model_args, model_kwargs: 
            The args and kwargs given to the call method of a model.
            This should represent the instances to obtain attributions for, 
            assumed to be a *batched* input. if `self.model` supports evaluation 
            on *data tensors*, the  appropriate tensor type may be used (e.g.,
            Pytorch models may accept Pytorch tensors in additon to 
            `np.ndarray`s). The shape of the inputs must match the input shape
            of `self.model`. 
        
        qoi: a Quantity of Interest
            This method will accumulate all gradients of the qoi w.r.t
            `attribution_cut`.
        doi_cut: Cut, 
            if `doi_cut` is None, this refers to the InputCut.
            Cut from which to begin propagation. The shape of `intervention`
            must match the output shape of this layer.
        attribution_cut: Cut, optional
            if `attribution_cut` is None, this refers to the InputCut.
            The Cut in which attribution will be calculated. This is generally
            taken from the attribution slyce's attribution_cut.
        to_cut: Cut, optional
            if `to_cut` is None, this refers to the OutputCut.
            The Cut in which qoi will be calculated. This is generally
            taken from the attribution slyce's to_cut.
        intervention : backend.Tensor or np.array
            Input tensor to propagate through the model. If an np.array,
            will be converted to a tensor on the same device as the model.

        Returns
        -------
        (backend.Tensor or np.ndarray)
            the gradients of `qoi` w.r.t. `attribution_cut`, keeping same type 
            as the input.
        """
        if attribution_cut is None:
            attribution_cut = InputCut()
        if to_cut is None:
            to_cut = OutputCut()

        y, zs = self.fprop(model_args,
                           model_kwargs,
                           doi_cut=doi_cut if doi_cut else InputCut(),
                           to_cut=to_cut,
                           attribution_cut=attribution_cut,
                           intervention=intervention,
                           return_tensor=True)

        y = to_cut.access_layer(y)
        grads_list = []
        for z in zs:
            z_flat = ModelWrapper._flatten(z)
            qoi_out = qoi(y)

            grads_flat = [B.gradient(B.sum(q), z_flat)
                          for q in qoi_out] if isinstance(
                              qoi_out, DATA_CONTAINER_TYPE) else B.gradient(
                                  B.sum(qoi_out), z_flat)

            grads = [
                ModelWrapper._unflatten(g, z, count=[0]) for g in grads_flat
            ] if isinstance(qoi_out,
                            DATA_CONTAINER_TYPE) else ModelWrapper._unflatten(
                                grads_flat, z, count=[0])

            grads = [
                attribution_cut.access_layer(g) for g in grads
            ] if isinstance(
                qoi_out,
                DATA_CONTAINER_TYPE) else attribution_cut.access_layer(grads)

            grads = [B.as_array(g) for g in grads] if isinstance(
                qoi_out, DATA_CONTAINER_TYPE) else B.as_array(grads)

            grads_list.append(grads)

        del y  # TODO: garbage collection

        return grads_list[0] if len(grads_list) == 1 else grads_list
Exemplo n.º 18
0
    def test_linear_default_baseline(self):
        doi = LinearDoi(baseline=None, resolution=10)
        res = doi(self.z)

        self.assertTrue(np.all(B.as_array(res[-1]) == 0.),
                        'Default baseline should be zeros')
Exemplo n.º 19
0
    def test_max_class_no_activation(self):
        qoi = MaxClassQoI()
        res = qoi(self.y)

        self.assertTrue(np.allclose(B.as_array(res), np.array([3., 0.])))
Exemplo n.º 20
0
    def test_max_class_axis(self):
        qoi = MaxClassQoI(axis=0)
        res = qoi(self.y)

        self.assertTrue(np.allclose(B.as_array(res), np.array([1., 2., 3.])))