Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions captum/attr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
from ._core.layer.internal_influence import InternalInfluence # noqa
from ._core.layer.grad_cam import LayerGradCam # noqa
from ._core.layer.layer_deep_lift import LayerDeepLift, LayerDeepLiftShap # noqa
from ._core.layer.layer_gradient_shap import LayerGradientShap # noqa
from ._core.neuron.neuron_conductance import NeuronConductance # noqa
from ._core.neuron.neuron_gradient import NeuronGradient # noqa
from ._core.neuron.neuron_integrated_gradients import NeuronIntegratedGradients # noqa
from ._core.neuron.neuron_deep_lift import NeuronDeepLift, NeuronDeepLiftShap # noqa
from ._core.neuron.neuron_gradient_shap import NeuronGradientShap # noqa
from ._core.neuron.neuron_guided_backprop_deconvnet import (
NeuronDeconvolution,
NeuronGuidedBackprop,
Expand Down
27 changes: 3 additions & 24 deletions captum/attr/_core/deep_lift.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
_expand_additional_forward_args,
_tensorize_baseline,
_call_custom_attribution_func,
_compute_conv_delta_and_format_attrs,
ExpansionTypes,
)
from .._utils.attribution import GradientAttribution
Expand Down Expand Up @@ -280,7 +281,8 @@ def attribute(
self._remove_hooks()

undo_gradient_requirements(inputs, gradient_mask)
return self._compute_conv_delta_and_format_attrs(
return _compute_conv_delta_and_format_attrs(
self,
return_convergence_delta,
attributions,
baselines,
Expand All @@ -290,29 +292,6 @@ def attribute(
is_inputs_tuple,
)

def _compute_conv_delta_and_format_attrs(
self,
return_convergence_delta,
attributions,
start_point,
end_point,
additional_forward_args,
target,
is_inputs_tuple,
):
if return_convergence_delta:
# computes convergence error
delta = self.compute_convergence_delta(
attributions,
start_point,
end_point,
additional_forward_args=additional_forward_args,
target=target,
)
return _format_attributions(is_inputs_tuple, attributions), delta
else:
return _format_attributions(is_inputs_tuple, attributions)

def _is_non_linear(self, module):
return type(module) in SUPPORTED_NON_LINEAR.keys()

Expand Down
66 changes: 35 additions & 31 deletions captum/attr/_core/gradient_shap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import numpy as np

from .._utils.attribution import GradientAttribution
from .._utils.common import _format_attributions, _format_callable_baseline
from .._utils.common import (
_format_callable_baseline,
_compute_conv_delta_and_format_attrs,
)

from .noise_tunnel import NoiseTunnel

Expand Down Expand Up @@ -33,7 +36,8 @@ def attribute(
Implements gradient SHAP based on the implementation from SHAP's primary
author. For reference, please, view:

https://github.com/slundberg/shap/#deep-learning-example-with-gradientexplainer-tensorflowkeraspytorch-models
https://github.com/slundberg/shap\
#deep-learning-example-with-gradientexplainer-tensorflowkeraspytorch-models

A Unified Approach to Interpreting Model Predictions
http://papers.nips.cc/paper/7062-a-unified-approach-to-interpreting-model-predictions
Expand Down Expand Up @@ -63,10 +67,10 @@ def attribute(

Args:

inputs (tensor or tuple of tensors): Input for which integrated
gradients are computed. If forward_func takes a single
inputs (tensor or tuple of tensors): Input for which SHAP attribution
values are computed. If `forward_func` takes a single
tensor as input, a single input tensor should be provided.
If forward_func takes multiple tensors as input, a tuple
If `forward_func` takes multiple tensors as input, a tuple
of the input tensors should be provided. It is assumed
that for all given input tensors, dimension 0 corresponds
to the number of examples, and if multiple input tensors
Expand Down Expand Up @@ -198,6 +202,7 @@ def attribute(

"""
input_min_baseline_x_grad = InputBaselineXGradient(self.forward_func)
input_min_baseline_x_grad.gradient_func = self.gradient_func

nt = NoiseTunnel(input_min_baseline_x_grad)

Expand Down Expand Up @@ -241,20 +246,6 @@ def attribute(
additional_forward_args=None,
return_convergence_delta=False,
):
def scale_input(input, baseline, rand_coefficient):
# batch size
bsz = input.shape[0]
inp_shape_wo_bsz = input.shape[1:]
inp_shape = (bsz,) + tuple([1] * len(inp_shape_wo_bsz))

# expand and reshape the indices
rand_coefficient = rand_coefficient.view(inp_shape).requires_grad_()

input_baseline_scaled = (
rand_coefficient * input + (1 - rand_coefficient) * baseline
)
return input_baseline_scaled

# Keeps track whether original input is a tuple or not before
# converting it into a tuple.
is_inputs_tuple = isinstance(inputs, tuple)
Expand All @@ -266,7 +257,7 @@ def scale_input(input, baseline, rand_coefficient):
)

input_baseline_scaled = tuple(
scale_input(input, baseline, rand_coefficient)
self._scale_input(input, baseline, rand_coefficient)
for input, baseline in zip(inputs, baselines)
)
grads = self.gradient_func(
Expand All @@ -281,17 +272,30 @@ def scale_input(input, baseline, rand_coefficient):
for input_baseline_diff, grad in zip(input_baseline_diffs, grads)
)

if return_convergence_delta:
delta = self.compute_convergence_delta(
attributions,
baselines,
inputs,
additional_forward_args=additional_forward_args,
target=target,
)
return _format_attributions(is_inputs_tuple, attributions), delta
else:
return _format_attributions(is_inputs_tuple, attributions)
return _compute_conv_delta_and_format_attrs(
self,
return_convergence_delta,
attributions,
baselines,
inputs,
additional_forward_args,
target,
is_inputs_tuple,
)

def has_convergence_delta(self):
return True

def _scale_input(self, input, baseline, rand_coefficient):
# batch size
bsz = input.shape[0]
inp_shape_wo_bsz = input.shape[1:]
inp_shape = (bsz,) + tuple([1] * len(inp_shape_wo_bsz))

# expand and reshape the indices
rand_coefficient = rand_coefficient.view(inp_shape).requires_grad_()

input_baseline_scaled = (
rand_coefficient * input + (1 - rand_coefficient) * baseline
)
return input_baseline_scaled
16 changes: 8 additions & 8 deletions captum/attr/_core/layer/layer_deep_lift.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
_validate_input,
_tensorize_baseline,
_call_custom_attribution_func,
_compute_conv_delta_and_format_attrs,
)


Expand Down Expand Up @@ -192,7 +193,7 @@ def attribute(

Returns:
**attributions** or 2-element tuple of **attributions**, **delta**:
- **attributions** (*tensor* or tuple of *tensors*):
- **attributions** (*tensor*):
Attribution score computed based on DeepLift's rescale rule with
respect to layer's inputs or outputs. Attributions will always be the
same size as the provided layer's inputs or outputs, depending on
Expand Down Expand Up @@ -244,9 +245,6 @@ def attribute(
additional_forward_args=additional_forward_args,
attribute_to_layer_input=attribute_to_layer_input,
)
# Fixme later: we need to do this because `_forward_layer_eval`
# always returns a tensor
attr_baselines = (attr_baselines,)

# remove forward hook set for baselines
for forward_handles_ref in self.forward_handles_refs:
Expand All @@ -261,8 +259,9 @@ def attribute(
additional_forward_args=additional_forward_args,
attribute_to_layer_input=attribute_to_layer_input,
)
# Fixme later: we need to do this because
# `compute_layer_gradients_and_eval` always returns a tensor
# Fixme later: we need to do this because `compute_layer_gradients_and_eval`
# and `_forward_layer_eval` always returns a tensor
attr_baselines = (attr_baselines,)
attr_inputs = (attr_inputs,)
gradients = (gradients,)

Expand All @@ -283,7 +282,8 @@ def attribute(

undo_gradient_requirements(inputs, gradient_mask)

return self._compute_conv_delta_and_format_attrs(
return _compute_conv_delta_and_format_attrs(
self,
return_convergence_delta,
attributions,
baselines,
Expand Down Expand Up @@ -449,7 +449,7 @@ def attribute(

Returns:
**attributions** or 2-element tuple of **attributions**, **delta**:
- **attributions** (*tensor* or tuple of *tensors*):
- **attributions** (*tensor*):
Attribution score computed based on DeepLift's rescale rule
with respect to layer's inputs or outputs. Attributions
will always be the same size as the provided layer's inputs
Expand Down
Loading