Ejemplo n.º 1
0
        def hook_removal_test_assert(self) -> None:
            attr_method: Attribution
            expect_error = False
            if layer is not None:
                if mode is HookRemovalMode.invalid_module:
                    expect_error = True
                    if isinstance(layer, list):
                        _set_deep_layer_value(model, layer[0], ErrorModule())
                    else:
                        _set_deep_layer_value(model, layer, ErrorModule())
                target_layer = get_target_layer(model, layer)
                internal_algorithm = cast(Type[InternalAttribution], algorithm)
                attr_method = internal_algorithm(model, target_layer)
            else:
                attr_method = algorithm(model)

            if noise_tunnel:
                attr_method = NoiseTunnel(attr_method)

            if mode is HookRemovalMode.incorrect_target_or_neuron:
                # Overwriting target and neuron index arguments to
                # incorrect values.
                if "target" in args:
                    args["target"] = (9999, ) * 20
                    expect_error = True
                if "neuron_selector" in args:
                    args["neuron_selector"] = (9999, ) * 20
                    expect_error = True

            if expect_error:
                with self.assertRaises(AssertionError):
                    attr_method.attribute(**args)
            else:
                attr_method.attribute(**args)

            def check_leftover_hooks(module):
                self.assertEqual(len(module._forward_hooks), 0)
                self.assertEqual(len(module._backward_hooks), 0)
                self.assertEqual(len(module._forward_pre_hooks), 0)

            model.apply(check_leftover_hooks)
Ejemplo n.º 2
0
    def make_single_target_test(
        cls,
        algorithm: Type[Attribution],
        model: Module,
        layer: Optional[str],
        args: Dict[str, Any],
        target_delta: float,
        noise_tunnel: bool,
        baseline_distr: bool,
    ) -> Callable:
        """
        This method creates a single target test for the given algorithm and parameters.
        """

        target_layer = get_target_layer(model,
                                        layer) if layer is not None else None
        # Obtains initial arguments to replace with each example
        # individually.
        original_inputs = args["inputs"]
        original_targets = args["target"]
        original_additional_forward_args = (_format_additional_forward_args(
            args["additional_forward_args"]) if "additional_forward_args"
                                            in args else None)
        num_examples = (len(original_inputs) if isinstance(
            original_inputs, Tensor) else len(original_inputs[0]))
        replace_baselines = "baselines" in args and not baseline_distr
        if replace_baselines:
            original_baselines = args["baselines"]

        def target_test_assert(self) -> None:
            attr_method: Attribution
            if target_layer:
                internal_algorithm = cast(Type[InternalAttribution], algorithm)
                attr_method = internal_algorithm(model, target_layer)
            else:
                attr_method = algorithm(model)

            if noise_tunnel:
                attr_method = NoiseTunnel(attr_method)
            attributions_orig = attr_method.attribute(**args)
            self.setUp()
            for i in range(num_examples):
                args["target"] = (original_targets[i] if len(original_targets)
                                  == num_examples else original_targets)
                args["inputs"] = (original_inputs[i:i + 1] if isinstance(
                    original_inputs, Tensor) else tuple(
                        original_inp[i:i + 1]
                        for original_inp in original_inputs))
                if original_additional_forward_args is not None:
                    args["additional_forward_args"] = tuple(
                        single_add_arg[i:i + 1] if isinstance(
                            single_add_arg, Tensor) else single_add_arg
                        for single_add_arg in original_additional_forward_args)
                if replace_baselines:
                    if isinstance(original_inputs, Tensor):
                        args["baselines"] = original_baselines[i:i + 1]
                    elif isinstance(original_baselines, tuple):
                        args["baselines"] = tuple(
                            single_baseline[i:i + 1] if isinstance(
                                single_baseline, Tensor) else single_baseline
                            for single_baseline in original_baselines)
                # Since Lime methods compute attributions for a batch
                # sequentially, random seed should not be reset after
                # each example after the first.
                if not issubclass(algorithm, Lime):
                    self.setUp()
                single_attr = attr_method.attribute(**args)
                current_orig_attributions = (
                    attributions_orig[i:i + 1] if isinstance(
                        attributions_orig, Tensor) else tuple(
                            single_attrib[i:i + 1]
                            for single_attrib in attributions_orig))
                assertTensorTuplesAlmostEqual(
                    self,
                    current_orig_attributions,
                    single_attr,
                    delta=target_delta,
                    mode="max",
                )
                if (not issubclass(algorithm, Lime)
                        and len(original_targets) == num_examples):
                    # If original_targets contained multiple elements, then
                    # we also compare with setting targets to a list with
                    # a single element.
                    args["target"] = original_targets[i:i + 1]
                    self.setUp()
                    single_attr_target_list = attr_method.attribute(**args)
                    assertTensorTuplesAlmostEqual(
                        self,
                        current_orig_attributions,
                        single_attr_target_list,
                        delta=target_delta,
                        mode="max",
                    )

        return target_test_assert
Ejemplo n.º 3
0
        def data_parallel_test_assert(self) -> None:
            # Construct cuda_args, moving all tensor inputs in args to CUDA device
            cuda_args = {}
            for key in args:
                if isinstance(args[key], Tensor):
                    cuda_args[key] = args[key].cuda()
                elif isinstance(args[key], tuple):
                    cuda_args[key] = tuple(
                        elem.cuda() if isinstance(elem, Tensor) else elem
                        for elem in args[key])
                else:
                    cuda_args[key] = args[key]

            alt_device_ids = None
            cuda_model = copy.deepcopy(model).cuda()
            # Initialize models based on DataParallelCompareMode
            if mode is DataParallelCompareMode.cpu_cuda:
                model_1, model_2 = model, cuda_model
                args_1, args_2 = args, cuda_args
            elif mode is DataParallelCompareMode.data_parallel_default:
                model_1, model_2 = (
                    cuda_model,
                    torch.nn.parallel.DataParallel(cuda_model),
                )
                args_1, args_2 = cuda_args, cuda_args
            elif mode is DataParallelCompareMode.data_parallel_alt_dev_ids:
                alt_device_ids = [0] + [
                    x for x in range(torch.cuda.device_count() - 1, 0, -1)
                ]
                model_1, model_2 = (
                    cuda_model,
                    torch.nn.parallel.DataParallel(cuda_model,
                                                   device_ids=alt_device_ids),
                )
                args_1, args_2 = cuda_args, cuda_args
            elif mode is DataParallelCompareMode.dist_data_parallel:

                model_1, model_2 = (
                    cuda_model,
                    torch.nn.parallel.DistributedDataParallel(cuda_model,
                                                              device_ids=[0],
                                                              output_device=0),
                )
                args_1, args_2 = cuda_args, cuda_args
            else:
                raise AssertionError(
                    "DataParallel compare mode type is not valid.")

            attr_method_1: Attribution
            attr_method_2: Attribution
            if target_layer:
                internal_algorithm = cast(Type[InternalAttribution], algorithm)
                attr_method_1 = internal_algorithm(
                    model_1, get_target_layer(model_1, target_layer))
                # cuda_model is used to obtain target_layer since DataParallel
                # adds additional wrapper.
                # model_2 is always either the CUDA model itself or DataParallel
                if alt_device_ids is None:
                    attr_method_2 = internal_algorithm(
                        model_2, get_target_layer(cuda_model, target_layer))
                else:
                    # LayerDeepLift and LayerDeepLiftShap do not take device ids
                    # as a parameter, since they must always have the DataParallel
                    # model object directly.
                    # Some neuron methods and GuidedGradCAM also require the
                    # model and cannot take a forward function.
                    if issubclass(
                            internal_algorithm,
                        (
                            LayerDeepLift,
                            LayerDeepLiftShap,
                            NeuronDeepLift,
                            NeuronDeepLiftShap,
                            NeuronDeconvolution,
                            NeuronGuidedBackprop,
                            GuidedGradCam,
                        ),
                    ):
                        attr_method_2 = internal_algorithm(
                            model_2, get_target_layer(cuda_model,
                                                      target_layer))
                    else:
                        attr_method_2 = internal_algorithm(
                            model_2.forward,
                            get_target_layer(cuda_model, target_layer),
                            device_ids=alt_device_ids,
                        )
            else:
                attr_method_1 = algorithm(model_1)
                attr_method_2 = algorithm(model_2)

            if noise_tunnel:
                attr_method_1 = NoiseTunnel(attr_method_1)
                attr_method_2 = NoiseTunnel(attr_method_2)
            if attr_method_1.has_convergence_delta():
                attributions_1, delta_1 = attr_method_1.attribute(
                    return_convergence_delta=True, **args_1)
                self.setUp()
                attributions_2, delta_2 = attr_method_2.attribute(
                    return_convergence_delta=True, **args_2)
                if isinstance(attributions_1, list):
                    for i in range(len(attributions_1)):
                        assertTensorTuplesAlmostEqual(
                            self,
                            attributions_1[i],
                            attributions_2[i],
                            mode="max",
                            delta=dp_delta,
                        )
                else:
                    assertTensorTuplesAlmostEqual(self,
                                                  attributions_1,
                                                  attributions_2,
                                                  mode="max",
                                                  delta=dp_delta)
                assertTensorTuplesAlmostEqual(self,
                                              delta_1,
                                              delta_2,
                                              mode="max",
                                              delta=dp_delta)
            else:
                attributions_1 = attr_method_1.attribute(**args_1)
                self.setUp()
                attributions_2 = attr_method_2.attribute(**args_2)
                if isinstance(attributions_1, list):
                    for i in range(len(attributions_1)):
                        assertTensorTuplesAlmostEqual(
                            self,
                            attributions_1[i],
                            attributions_2[i],
                            mode="max",
                            delta=dp_delta,
                        )
                else:
                    assertTensorTuplesAlmostEqual(self,
                                                  attributions_1,
                                                  attributions_2,
                                                  mode="max",
                                                  delta=dp_delta)