Browse Source

!13509 Fix explainer API documents bug to make it executable

From: @lixiaohui33
Reviewed-by: 
Signed-off-by:
pull/13509/MERGE
mindspore-ci-bot Gitee 4 years ago
parent
commit
385edf507d
15 changed files with 143 additions and 171 deletions
  1. +15
    -6
      mindspore/explainer/_image_classification_runner.py
  2. +4
    -4
      mindspore/explainer/_operators.py
  3. +24
    -35
      mindspore/explainer/_utils.py
  4. +8
    -8
      mindspore/explainer/benchmark/_attribution/class_sensitivity.py
  5. +9
    -5
      mindspore/explainer/benchmark/_attribution/faithfulness.py
  6. +12
    -8
      mindspore/explainer/benchmark/_attribution/localization.py
  7. +2
    -2
      mindspore/explainer/benchmark/_attribution/metric.py
  8. +7
    -9
      mindspore/explainer/benchmark/_attribution/robustness.py
  9. +5
    -5
      mindspore/explainer/explanation/_attribution/_backprop/backprop_utils.py
  10. +7
    -8
      mindspore/explainer/explanation/_attribution/_backprop/gradcam.py
  11. +15
    -38
      mindspore/explainer/explanation/_attribution/_backprop/gradient.py
  12. +9
    -13
      mindspore/explainer/explanation/_attribution/_backprop/modified_relu.py
  13. +2
    -2
      mindspore/explainer/explanation/_attribution/_perturbation/ablation.py
  14. +6
    -8
      mindspore/explainer/explanation/_attribution/_perturbation/occlusion.py
  15. +18
    -20
      mindspore/explainer/explanation/_attribution/_perturbation/rise.py

+ 15
- 6
mindspore/explainer/_image_classification_runner.py View File

@@ -83,21 +83,30 @@ class ImageClassificationRunner:
>>> from mindspore.explainer.benchmark import Faithfulness >>> from mindspore.explainer.benchmark import Faithfulness
>>> from mindspore.nn import Softmax >>> from mindspore.nn import Softmax
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net >>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # Prepare the dataset for explaining and evaluation, e.g., Cifar10
>>> dataset = get_dataset('/path/to/Cifar10_dataset')
>>>
>>> # The detail of AlexNet is shown in model_zoo.official.cv.alexnet.src.alexnet.py
>>> net = AlexNet(10)
>>> # Load the checkpoint
>>> param_dict = load_checkpoint("/path/to/checkpoint")
>>> load_param_into_net(net, param_dict)
>>>
>>> # Prepare the dataset for explaining and evaluation.
>>> # The detail of create_dataset_cifar10 method is shown in model_zoo.official.cv.alexnet.src.dataset.py
>>>
>>> dataset = create_dataset_cifar10("/path/to/cifar/dataset", 1)
>>> labels = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] >>> labels = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
>>> # load checkpoint to a network, e.g. checkpoint of resnet50 trained on Cifar10
>>> param_dict = load_checkpoint("checkpoint.ckpt")
>>> net = resnet50(len(labels))
>>>
>>> activation_fn = Softmax() >>> activation_fn = Softmax()
>>> load_param_into_net(net, param_dict)
>>> gbp = GuidedBackprop(net) >>> gbp = GuidedBackprop(net)
>>> gradient = Gradient(net) >>> gradient = Gradient(net)
>>> explainers = [gbp, gradient] >>> explainers = [gbp, gradient]
>>> faithfulness = Faithfulness(len(labels), activation_fn, "NaiveFaithfulness") >>> faithfulness = Faithfulness(len(labels), activation_fn, "NaiveFaithfulness")
>>> benchmarkers = [faithfulness] >>> benchmarkers = [faithfulness]
>>>
>>> runner = ImageClassificationRunner("./summary_dir", (dataset, labels), net, activation_fn) >>> runner = ImageClassificationRunner("./summary_dir", (dataset, labels), net, activation_fn)
>>> runner.register_saliency(explainers=explainers, benchmarkers=benchmarkers) >>> runner.register_saliency(explainers=explainers, benchmarkers=benchmarkers)
>>> runner.register_uncertainty()
>>> runner.register_hierarchical_occlusion()
>>> runner.run() >>> runner.run()
""" """




+ 4
- 4
mindspore/explainer/_operators.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -250,9 +250,9 @@ def summation(inputs: Tensor, axis: _Axis = (), keep_dims: bool = False) -> Tens




def stack(inputs: List[Tensor], axis: int) -> Tensor: def stack(inputs: List[Tensor], axis: int) -> Tensor:
"""Packs a list of tensors in specified axis."""
pack_op = op.Pack(axis)
outputs = pack_op(inputs)
"""Stacks a list of tensors in specified axis."""
stack_op = op.Stack(axis)
outputs = stack_op(inputs)
return outputs return outputs






+ 24
- 35
mindspore/explainer/_utils.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -104,16 +104,14 @@ def retrieve_layer_by_name(model: _Module, layer_name: str):
Retrieve the layer in the model by the given layer_name. Retrieve the layer in the model by the given layer_name.


Args: Args:
model (_Module): model which contains the target layer
layer_name (str): name of target layer
model (Cell): Model which contains the target layer.
layer_name (str): Name of target layer.


Return:
- target_layer (_Module)

Raise:
ValueError: if module with given layer_name is not found in the model,
raise ValueError.
Returns:
Cell, the target layer.


Raises:
ValueError: If module with given layer_name is not found in the model.
""" """
if not isinstance(layer_name, str): if not isinstance(layer_name, str):
raise TypeError('layer_name should be type of str, but receive {}.' raise TypeError('layer_name should be type of str, but receive {}.'
@@ -146,13 +144,14 @@ def retrieve_layer(model: _Module, target_layer: Union[str, _Module] = ''):
be raised. be raised.


Args: Args:
model (_Module): the model to retrieve the target layer
target_layer (Union[str, _Module]): target layer to retrieve. Can be
either string (layer name) or the Cell object. If '' is provided,
the input model will be returned.
model (Cell): Model which contains the target layer.
target_layer (str, Cell): Name of target layer or the target layer instance.

Returns:
Cell, the target layer.


Return:
target layer (_Module)
Raises:
ValueError: If module with given layer_name is not found in the model.
""" """
if isinstance(target_layer, str): if isinstance(target_layer, str):
target_layer = retrieve_layer_by_name(model, target_layer) target_layer = retrieve_layer_by_name(model, target_layer)
@@ -174,9 +173,7 @@ class ForwardProbe:
Probe to capture output of specific layer in a given model. Probe to capture output of specific layer in a given model.


Args: Args:
target_layer (_Module): name of target layer or just provide the
target layer.

target_layer (str, Cell): Name of target layer or the target layer instance.
""" """


def __init__(self, target_layer: _Module): def __init__(self, target_layer: _Module):
@@ -204,7 +201,7 @@ class ForwardProbe:




def format_tensor_to_ndarray(x: Union[ms.Tensor, np.ndarray]) -> np.ndarray: def format_tensor_to_ndarray(x: Union[ms.Tensor, np.ndarray]) -> np.ndarray:
"""Unify `mindspore.Tensor` and `np.ndarray` to `np.ndarray`. """
"""Unify Tensor and numpy.array to numpy.array."""
if isinstance(x, ms.Tensor): if isinstance(x, ms.Tensor):
x = x.asnumpy() x = x.asnumpy()


@@ -231,7 +228,7 @@ def calc_correlation(x: Union[ms.Tensor, np.ndarray],




def calc_auc(x: _Array) -> _Array: def calc_auc(x: _Array) -> _Array:
"""Calculate the Aera under Curve."""
"""Calculate the Area under Curve."""
# take mean for multiple patches if the model is fully convolutional model # take mean for multiple patches if the model is fully convolutional model
if len(x.shape) == 4: if len(x.shape) == 4:
x = np.mean(np.mean(x, axis=2), axis=3) x = np.mean(np.mean(x, axis=2), axis=3)
@@ -242,18 +239,11 @@ def calc_auc(x: _Array) -> _Array:


def rank_pixels(inputs: _Array, descending: bool = True) -> _Array: def rank_pixels(inputs: _Array, descending: bool = True) -> _Array:
""" """
Generate rank order fo every pixel in an 2D array.
Generate rank order for every pixel in an 2D array.


The rank order start from 0 to (num_pixel-1). If descending is True, the The rank order start from 0 to (num_pixel-1). If descending is True, the
rank order will generate in a descending order, otherwise in ascending rank order will generate in a descending order, otherwise in ascending
order. order.

Example:
x = np.array([[4., 3., 1.], [5., 9., 1.]])
rank_pixels(x, descending=True)
>> np.array([[2, 3, 4], [1, 0, 5]])
rank_pixels(x, descending=False)
>> np.array([[3, 2, 0], [4, 5, 1]])
""" """
if len(inputs.shape) < 2 or len(inputs.shape) > 3: if len(inputs.shape) < 2 or len(inputs.shape) > 3:
raise ValueError('Only support 2D or 3D inputs currently.') raise ValueError('Only support 2D or 3D inputs currently.')
@@ -275,16 +265,15 @@ def resize(inputs: _Tensor, size: Tuple[int, int], mode: str) -> _Tensor:
Resize the intermediate layer _attribution to the same size as inputs. Resize the intermediate layer _attribution to the same size as inputs.


Args: Args:
inputs (ms.Tensor): the input tensor to be resized
size (tupleint]): the targeted size resize to
mode (str): the resize mode. Options: 'nearest_neighbor', 'bilinear'
inputs (Tensor): The input tensor to be resized.
size (tuple[int]): The targeted size resize to.
mode (str): The resize mode. Options: 'nearest_neighbor', 'bilinear'.


Returns: Returns:
outputs (ms.Tensor): the resized tensor.
Tensor, the resized tensor.


Raises: Raises:
ValueError: the resize mode is not in ['nearest_neighbor',
'bilinear'].
ValueError: the resize mode is not in ['nearest_neighbor', 'bilinear'].
""" """
h, w = size h, w = size
if mode == 'nearest_neighbor': if mode == 'nearest_neighbor':
@@ -305,6 +294,6 @@ def resize(inputs: _Tensor, size: Tuple[int, int], mode: str) -> _Tensor:
resized_np = np.transpose(array_lst, [0, 3, 1, 2]) resized_np = np.transpose(array_lst, [0, 3, 1, 2])
outputs = ms.Tensor(resized_np, inputs.dtype) outputs = ms.Tensor(resized_np, inputs.dtype)
else: else:
raise ValueError('Unsupported resize mode {}'.format(mode))
raise ValueError('Unsupported resize mode {}.'.format(mode))


return outputs return outputs

+ 8
- 8
mindspore/explainer/benchmark/_attribution/class_sensitivity.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -45,19 +45,19 @@ class ClassSensitivity(LabelAgnosticMetric):
numpy.ndarray, 1D array of shape :math:`(N,)`, result of class sensitivity evaluated on `explainer`. numpy.ndarray, 1D array of shape :math:`(N,)`, result of class sensitivity evaluated on `explainer`.


Examples: Examples:
>>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.benchmark import ClassSensitivity >>> from mindspore.explainer.benchmark import ClassSensitivity
>>> from mindspore.explainer.explanation import Gradient >>> from mindspore.explainer.explanation import Gradient
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # prepare your network and load the trained checkpoint file, e.g., resnet50.
>>> network = resnet50(10)
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(network, param_dict)
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> # prepare your explainer to be evaluated, e.g., Gradient. >>> # prepare your explainer to be evaluated, e.g., Gradient.
>>> gradient = Gradient(network)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> gradient = Gradient(net)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> class_sensitivity = ClassSensitivity() >>> class_sensitivity = ClassSensitivity()
>>> res = class_sensitivity.evaluate(gradient, input_x) >>> res = class_sensitivity.evaluate(gradient, input_x)
>>> print(res)
""" """
self._check_evaluate_param(explainer, inputs) self._check_evaluate_param(explainer, inputs)




+ 9
- 5
mindspore/explainer/benchmark/_attribution/faithfulness.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -419,16 +419,20 @@ class Faithfulness(LabelSensitiveMetric):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Gradient >>> from mindspore.explainer.explanation import Gradient
>>> # init an explainer with a trained network, e.g., resnet50
>>> gradient = Gradient(network)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>>
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> gradient = Gradient(net)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> targets = 5 >>> targets = 5
>>> # usage 1: input the explainer and the data to be explained, >>> # usage 1: input the explainer and the data to be explained,
>>> # calculate the faithfulness with the specified metric
>>> # faithfulness is a Faithfulness instance
>>> res = faithfulness.evaluate(gradient, inputs, targets) >>> res = faithfulness.evaluate(gradient, inputs, targets)
>>> # usage 2: input the generated saliency map >>> # usage 2: input the generated saliency map
>>> saliency = gradient(inputs, targets) >>> saliency = gradient(inputs, targets)
>>> res = faithfulness.evaluate(gradient, inputs, targets, saliency) >>> res = faithfulness.evaluate(gradient, inputs, targets, saliency)
>>> print(res)
""" """


self._check_evaluate_param(explainer, inputs, targets, saliency) self._check_evaluate_param(explainer, inputs, targets, saliency)


+ 12
- 8
mindspore/explainer/benchmark/_attribution/localization.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ class Localization(LabelSensitiveMetric):


Examples: Examples:
>>> from mindspore.explainer.benchmark import Localization >>> from mindspore.explainer.benchmark import Localization
>>> num_labels = 100
>>> num_labels = 10
>>> localization = Localization(num_labels, "PointingGame") >>> localization = Localization(num_labels, "PointingGame")
""" """


@@ -113,18 +113,22 @@ class Localization(LabelSensitiveMetric):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Gradient >>> from mindspore.explainer.explanation import Gradient
>>> # init an explainer with a trained network, e.g., resnet50
>>> gradient = Gradient(network)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> masks = np.zeros([1, 1, 224, 224])
>>> masks[:, :, 65: 100, 65: 100] = 1
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> gradient = Gradient(net)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> masks = np.zeros([1, 1, 32, 32])
>>> masks[:, :, 10: 20, 10: 20] = 1
>>> targets = 5 >>> targets = 5
>>> # usage 1: input the explainer and the data to be explained, >>> # usage 1: input the explainer and the data to be explained,
>>> # calculate the faithfulness with the specified metric
>>> # localization is a Localization instance
>>> res = localization.evaluate(gradient, inputs, targets, mask=masks) >>> res = localization.evaluate(gradient, inputs, targets, mask=masks)
>>> print(res)
>>> # usage 2: input the generated saliency map >>> # usage 2: input the generated saliency map
>>> saliency = gradient(inputs, targets) >>> saliency = gradient(inputs, targets)
>>> res = localization.evaluate(gradient, inputs, targets, saliency, mask=masks) >>> res = localization.evaluate(gradient, inputs, targets, saliency, mask=masks)
>>> print(res)
""" """
self._check_evaluate_param_with_mask(explainer, inputs, targets, saliency, mask) self._check_evaluate_param_with_mask(explainer, inputs, targets, saliency, mask)




+ 2
- 2
mindspore/explainer/benchmark/_attribution/metric.py View File

@@ -69,7 +69,7 @@ class AttributionMetric:
if self._explainer is None: if self._explainer is None:
self._explainer = explainer self._explainer = explainer
elif self._explainer is not explainer: elif self._explainer is not explainer:
logger.info('Provided explainer is not the same as previously evaluted one. Please reset the evaluated '
logger.info('Provided explainer is not the same as previously evaluated one. Please reset the evaluated '
'results. Previous explainer: %s, current explainer: %s', self._explainer, explainer) 'results. Previous explainer: %s, current explainer: %s', self._explainer, explainer)
self._explainer = explainer self._explainer = explainer


@@ -107,7 +107,7 @@ class LabelAgnosticMetric(AttributionMetric):
raise TypeError('result should have type of float, ms.Tensor or np.ndarray, but receive %s' % type(result)) raise TypeError('result should have type of float, ms.Tensor or np.ndarray, but receive %s' % type(result))


def get_results(self): def get_results(self):
"""Return the gloabl results."""
"""Return the global results."""
return self._global_results.copy() return self._global_results.copy()


def reset(self): def reset(self):


+ 7
- 9
mindspore/explainer/benchmark/_attribution/robustness.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -80,18 +80,16 @@ class Robustness(LabelSensitiveMetric):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Gradient >>> from mindspore.explainer.explanation import Gradient
>>> from mindspore.explainer.benchmark import Robustness
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # prepare your network and load the trained checkpoint file, e.g., resnet50.
>>> network = resnet50(10)
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(network, param_dict)
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> # prepare your explainer to be evaluated, e.g., Gradient. >>> # prepare your explainer to be evaluated, e.g., Gradient.
>>> gradient = Gradient(network)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> gradient = Gradient(net)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> target_label = ms.Tensor([0], ms.int32) >>> target_label = ms.Tensor([0], ms.int32)
>>> # robustness is a Robustness instance >>> # robustness is a Robustness instance
>>> res = robustness.evaluate(gradient, input_x, target_label) >>> res = robustness.evaluate(gradient, input_x, target_label)
>>> print(res)
""" """


self._check_evaluate_param(explainer, inputs, targets, saliency) self._check_evaluate_param(explainer, inputs, targets, saliency)


+ 5
- 5
mindspore/explainer/explanation/_attribution/_backprop/backprop_utils.py View File

@@ -24,15 +24,15 @@ def get_bp_weights(model, inputs, targets=None, weights=None):
Compute the gradient of output w.r.t input. Compute the gradient of output w.r.t input.


Args: Args:
model (`ms.nn.Cell`): Differentiable black-box model.
inputs (`ms.Tensor`): Input to calculate gradient and explanation.
model (Cell): Differentiable black-box model.
inputs (Tensor): Input to calculate gradient and explanation.
targets (int, optional): Target label id specifying which category to compute gradient. Default: None. targets (int, optional): Target label id specifying which category to compute gradient. Default: None.
weights (`ms.Tensor`, optional): Custom weights for computing gradients. The shape of weights should match the
model outputs. If None is provided, an one-hot weights with one in targets positions will be used instead.
weights (Tensor, optional): Custom weights for computing gradients. The shape of weights should match the model
outputs. If None is provided, an one-hot weights with one in targets positions will be used instead.
Default: None. Default: None.


Returns: Returns:
saliency map (ms.Tensor): Gradient back-propagated to the input.
Tensor, signal to be back-propagated to the input.
""" """
inputs = unify_inputs(inputs) inputs = unify_inputs(inputs)
if targets is None and weights is None: if targets is None and weights is None:


+ 7
- 8
mindspore/explainer/explanation/_attribution/_backprop/gradcam.py View File

@@ -61,7 +61,7 @@ class GradCAM(IntermediateLayerAttribution):
Args: Args:
network (Cell): The black-box model to be explained. network (Cell): The black-box model to be explained.
layer (str, optional): The layer name to generate the explanation, usually chosen as the last convolutional layer (str, optional): The layer name to generate the explanation, usually chosen as the last convolutional
layer for better practice. If it is '', the explantion will be generated at the input layer.
layer for better practice. If it is '', the explanation will be generated at the input layer.
Default: ''. Default: ''.


Inputs: Inputs:
@@ -76,18 +76,17 @@ class GradCAM(IntermediateLayerAttribution):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import GradCAM >>> from mindspore.explainer.explanation import GradCAM
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # load a trained network
>>> net = resnet50(10)
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(net, param_dict)
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> # specify a layer name to generate explanation, usually the layer can be set as the last conv layer. >>> # specify a layer name to generate explanation, usually the layer can be set as the last conv layer.
>>> layer_name = 'layer4'
>>> layer_name = 'conv2'
>>> # init GradCAM with a trained network and specify the layer to obtain attribution >>> # init GradCAM with a trained network and specify the layer to obtain attribution
>>> gradcam = GradCAM(net, layer=layer_name) >>> gradcam = GradCAM(net, layer=layer_name)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> label = 5 >>> label = 5
>>> saliency = gradcam(inputs, label) >>> saliency = gradcam(inputs, label)
>>> print(saliency.shape)
""" """


def __init__(self, network, layer=""): def __init__(self, network, layer=""):


+ 15
- 38
mindspore/explainer/explanation/_attribution/_backprop/gradient.py View File

@@ -15,32 +15,14 @@
"""Gradient explainer.""" """Gradient explainer."""
from copy import deepcopy from copy import deepcopy


from mindspore import nn
from mindspore.train._utils import check_value_type from mindspore.train._utils import check_value_type
from mindspore.explainer._operators import reshape, sqrt, Tensor
from mindspore.explainer._operators import Tensor
from mindspore.explainer._utils import abs_max, unify_inputs, unify_targets from mindspore.explainer._utils import abs_max, unify_inputs, unify_targets


from .. import Attribution from .. import Attribution
from .backprop_utils import get_bp_weights, GradNet from .backprop_utils import get_bp_weights, GradNet




def _get_hook(bntype, cache):
"""Provide backward hook function for BatchNorm layer in eval mode."""
var, gamma, eps = cache
if bntype == "2d":
var = reshape(var, (1, -1, 1, 1))
gamma = reshape(gamma, (1, -1, 1, 1))
elif bntype == "1d":
var = reshape(var, (1, -1, 1))
gamma = reshape(gamma, (1, -1, 1))

def reset_gradient(_, grad_input, grad_output):
grad_output = grad_input[0] * gamma / sqrt(var + eps)
return grad_output

return reset_gradient


class Gradient(Attribution): class Gradient(Attribution):
r""" r"""
Provides Gradient explanation method. Provides Gradient explanation method.
@@ -72,15 +54,14 @@ class Gradient(Attribution):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Gradient >>> from mindspore.explainer.explanation import Gradient
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # init Gradient with a trained network
>>> net = resnet50(10) # please refer to model_zoo
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(net, param_dict)
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> gradient = Gradient(net) >>> gradient = Gradient(net)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> label = 5 >>> label = 5
>>> saliency = gradient(inputs, label) >>> saliency = gradient(inputs, label)
>>> print(saliency.shape)
""" """


def __init__(self, network): def __init__(self, network):
@@ -88,7 +69,6 @@ class Gradient(Attribution):
self._backward_model = deepcopy(network) self._backward_model = deepcopy(network)
self._backward_model.set_train(False) self._backward_model.set_train(False)
self._backward_model.set_grad(False) self._backward_model.set_grad(False)
self._hook_bn()
self._grad_net = GradNet(self._backward_model) self._grad_net = GradNet(self._backward_model)
self._aggregation_fn = abs_max self._aggregation_fn = abs_max


@@ -103,22 +83,19 @@ class Gradient(Attribution):
saliency = self._aggregation_fn(gradient) saliency = self._aggregation_fn(gradient)
return saliency return saliency


def _hook_bn(self):
"""Hook BatchNorm layer for `self._backward_model.`"""
for _, cell in self._backward_model.cells_and_names():
if isinstance(cell, nn.BatchNorm2d):
cache = (cell.moving_variance, cell.gamma, cell.eps)
cell.register_backward_hook(_get_hook("2d", cache=cache))
elif isinstance(cell, nn.BatchNorm1d):
cache = (cell.moving_variance, cell.gamma, cell.eps)
cell.register_backward_hook(_get_hook("1d", cache=cache))

@staticmethod @staticmethod
def _verify_data(inputs, targets): def _verify_data(inputs, targets):
"""Verify the validity of the parsed inputs."""
"""
Verify the validity of the parsed inputs.

Args:
inputs (Tensor): The inputs to be explained.
targets (Tensor, int): The label of interest. It should be a 1D or 0D tensor, or an integer.
If it is a 1D tensor, its length should be the same as `inputs`.
"""
check_value_type('inputs', inputs, Tensor) check_value_type('inputs', inputs, Tensor)
if len(inputs.shape) != 4: if len(inputs.shape) != 4:
raise ValueError('Argument inputs must be 4D Tensor')
raise ValueError(f'Argument inputs must be 4D Tensor. But got {len(inputs.shape)}D Tensor.')
check_value_type('targets', targets, (Tensor, int)) check_value_type('targets', targets, (Tensor, int))
if isinstance(targets, Tensor): if isinstance(targets, Tensor):
if len(targets.shape) > 1 or (len(targets.shape) == 1 and len(targets) != len(inputs)): if len(targets.shape) > 1 or (len(targets.shape) == 1 and len(targets) != len(inputs)):


+ 9
- 13
mindspore/explainer/explanation/_attribution/_backprop/modified_relu.py View File

@@ -109,16 +109,14 @@ class Deconvolution(ModifiedReLU):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Deconvolution >>> from mindspore.explainer.explanation import Deconvolution
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # init Deconvolution with a trained network.
>>> net = resnet50(10) # please refer to model_zoo
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(net, param_dict)
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> deconvolution = Deconvolution(net) >>> deconvolution = Deconvolution(net)
>>> # parse data and the target label to be explained and get the saliency map >>> # parse data and the target label to be explained and get the saliency map
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> label = 5 >>> label = 5
>>> saliency = deconvolution(inputs, label) >>> saliency = deconvolution(inputs, label)
>>> print(saliency.shape)
""" """


def __init__(self, network): def __init__(self, network):
@@ -154,17 +152,15 @@ class GuidedBackprop(ModifiedReLU):
Examples: Examples:
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> from mindspore.explainer.explanation import GuidedBackprop >>> from mindspore.explainer.explanation import GuidedBackprop
>>> # init GuidedBackprop with a trained network.
>>> net = resnet50(10) # please refer to model_zoo
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(net, param_dict)
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> gbp = GuidedBackprop(net) >>> gbp = GuidedBackprop(net)
>>> # parse data and the target label to be explained and get the saliency map
>>> inputs = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> # feed data and the target label to be explained and get the saliency map
>>> inputs = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> label = 5 >>> label = 5
>>> saliency = gbp(inputs, label) >>> saliency = gbp(inputs, label)
>>> print(saliency.shape)
""" """


def __init__(self, network): def __init__(self, network):


+ 2
- 2
mindspore/explainer/explanation/_attribution/_perturbation/ablation.py View File

@@ -130,7 +130,7 @@ class AblationWithSaliency(Ablation):
Generate mask for perturbations based on given saliency ranks. Generate mask for perturbations based on given saliency ranks.


Args: Args:
saliency (np.ndarray): Perturbing masks will be generated based on the given saliency map. The shape of
saliency (numpy.array): Perturbing masks will be generated based on the given saliency map. The shape of
saliency is expected to be: [batch_size, optional(num_channels), *spatial_size]. If multi-channel saliency is expected to be: [batch_size, optional(num_channels), *spatial_size]. If multi-channel
saliency is provided, an averaged saliency will be taken to calculate pixel order in spatial dimension. saliency is provided, an averaged saliency will be taken to calculate pixel order in spatial dimension.
num_channels (optional[int]): Number of channels of the input data. In order to match the shape of inputs, num_channels (optional[int]): Number of channels of the input data. In order to match the shape of inputs,
@@ -139,7 +139,7 @@ class AblationWithSaliency(Ablation):
no channel dimension. Default: None. no channel dimension. Default: None.


Return: Return:
mask (np.ndarray): boolen mask for generate perturbations.
numpy.array, boolean masks for perturbation generation.
""" """


batch_size = saliency.shape[0] batch_size = saliency.shape[0]


+ 6
- 8
mindspore/explainer/explanation/_attribution/_perturbation/occlusion.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -71,17 +71,15 @@ class Occlusion(PerturbationAttribution):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import Occlusion >>> from mindspore.explainer.explanation import Occlusion
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # prepare your network and load the trained checkpoint file, e.g., resnet50.
>>> network = resnet50(10)
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(network, param_dict)
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> # initialize Occlusion explainer with the pretrained model and activation function >>> # initialize Occlusion explainer with the pretrained model and activation function
>>> activation_fn = ms.nn.Softmax() # softmax layer is applied to transform logits to probabilities >>> activation_fn = ms.nn.Softmax() # softmax layer is applied to transform logits to probabilities
>>> occlusion = Occlusion(network, activation_fn=activation_fn)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 224, 224), ms.float32)
>>> occlusion = Occlusion(net, activation_fn=activation_fn)
>>> input_x = ms.Tensor(np.random.rand(1, 3, 32, 32), ms.float32)
>>> label = ms.Tensor([1], ms.int32) >>> label = ms.Tensor([1], ms.int32)
>>> saliency = occlusion(input_x, label) >>> saliency = occlusion(input_x, label)
>>> print(saliency.shape)
""" """


def __init__(self, network, activation_fn, perturbation_per_eval=32): def __init__(self, network, activation_fn, perturbation_per_eval=32):


+ 18
- 20
mindspore/explainer/explanation/_attribution/_perturbation/rise.py View File

@@ -1,4 +1,4 @@
# Copyright 2020 Huawei Technologies Co., Ltd
# Copyright 2020-2021 Huawei Technologies Co., Ltd
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -62,23 +62,22 @@ class RISE(PerturbationAttribution):
>>> import numpy as np >>> import numpy as np
>>> import mindspore as ms >>> import mindspore as ms
>>> from mindspore.explainer.explanation import RISE >>> from mindspore.explainer.explanation import RISE
>>> from mindspore.nn import Sigmoid
>>> from mindspore.train.serialization import load_checkpoint, load_param_into_net
>>> # prepare your network and load the trained checkpoint file, e.g., resnet50.
>>> network = resnet50(10)
>>> param_dict = load_checkpoint("resnet50.ckpt")
>>> load_param_into_net(network, param_dict)
>>>
>>> # The detail of LeNet5 is shown in model_zoo.official.cv.lenet.src.lenet.py
>>> net = LeNet5(10, num_channel=3)
>>> # initialize RISE explainer with the pretrained model and activation function >>> # initialize RISE explainer with the pretrained model and activation function
>>> activation_fn = ms.nn.Softmax() # softmax layer is applied to transform logits to probabilities >>> activation_fn = ms.nn.Softmax() # softmax layer is applied to transform logits to probabilities
>>> rise = RISE(network, activation_fn=activation_fn)
>>> rise = RISE(net, activation_fn=activation_fn)
>>> # given an instance of RISE, saliency map can be generate >>> # given an instance of RISE, saliency map can be generate
>>> inputs = ms.Tensor(np.random.rand(2, 3, 224, 224), ms.float32)
>>> inputs = ms.Tensor(np.random.rand(2, 3, 32, 32), ms.float32)
>>> # when `targets` is an integer >>> # when `targets` is an integer
>>> targets = 5 >>> targets = 5
>>> saliency = rise(inputs, targets) >>> saliency = rise(inputs, targets)
>>> print(saliency.shape)
>>> # `targets` can also be a 2D tensor >>> # `targets` can also be a 2D tensor
>>> targets = ms.Tensor([[5], [1]], ms.int32) >>> targets = ms.Tensor([[5], [1]], ms.int32)
>>> saliency = rise(inputs, targets) >>> saliency = rise(inputs, targets)
>>> print(saliency.shape)
""" """


def __init__(self, def __init__(self,
@@ -88,7 +87,7 @@ class RISE(PerturbationAttribution):
super(RISE, self).__init__(network, activation_fn, perturbation_per_eval) super(RISE, self).__init__(network, activation_fn, perturbation_per_eval)


self._num_masks = 6000 # number of masks to be sampled self._num_masks = 6000 # number of masks to be sampled
self._mask_probability = 0.2 # ratio of inputs to be masked
self._mask_probability = 0.5 # ratio of inputs to be masked
self._down_sample_size = 10 # the original size of binary masks self._down_sample_size = 10 # the original size of binary masks
self._resize_mode = 'bilinear' # mode choice to resize the down-sized binary masks to size of the inputs self._resize_mode = 'bilinear' # mode choice to resize the down-sized binary masks to size of the inputs
self._perturbation_mode = 'constant' # setting the perturbed pixels to a constant value self._perturbation_mode = 'constant' # setting the perturbed pixels to a constant value
@@ -127,7 +126,9 @@ class RISE(PerturbationAttribution):
self._num_classes = num_classes self._num_classes = num_classes


# Due to the unsupported Op of slice assignment, we use numpy array here # Due to the unsupported Op of slice assignment, we use numpy array here
attr_np = np.zeros(shape=(batch_size, self._num_classes, height, width))
targets = self._unify_targets(inputs, targets)

attr_np = np.zeros(shape=(batch_size, targets.shape[1], height, width))


cal_times = math.ceil(self._num_masks / self._perturbation_per_eval) cal_times = math.ceil(self._num_masks / self._perturbation_per_eval)


@@ -143,24 +144,21 @@ class RISE(PerturbationAttribution):
weights = self._activation_fn(self.network(masked_input)) weights = self._activation_fn(self.network(masked_input))
while len(weights.shape) > 2: while len(weights.shape) > 2:
weights = op.mean(weights, axis=2) weights = op.mean(weights, axis=2)
weights = op.reshape(weights,
(bs, self._num_classes, 1, 1))


attr_np[idx] += op.summation(weights * masks, axis=0).asnumpy()
weights = np.expand_dims(np.expand_dims(weights.asnumpy()[:, targets[idx]], 2), 3)


attr_np = attr_np / self._num_masks
targets = self._unify_targets(inputs, targets)
attr_np[idx] += np.sum(weights * masks.asnumpy(), axis=0)


attr_classes = [att_i[target] for att_i, target in zip(attr_np, targets)]
attr_np = attr_np / self._num_masks


return op.Tensor(attr_classes, dtype=inputs.dtype)
return op.Tensor(attr_np, dtype=inputs.dtype)


@staticmethod @staticmethod
def _verify_data(inputs, targets): def _verify_data(inputs, targets):
"""Verify the validity of the parsed inputs.""" """Verify the validity of the parsed inputs."""
check_value_type('inputs', inputs, Tensor) check_value_type('inputs', inputs, Tensor)
if len(inputs.shape) != 4: if len(inputs.shape) != 4:
raise ValueError('Argument inputs must be 4D Tensor')
raise ValueError(f'Argument inputs must be 4D Tensor, but got {len(inputs.shape)}D Tensor.')
check_value_type('targets', targets, (Tensor, int, tuple, list)) check_value_type('targets', targets, (Tensor, int, tuple, list))
if isinstance(targets, Tensor): if isinstance(targets, Tensor):
if len(targets.shape) > 2: if len(targets.shape) > 2:
@@ -168,7 +166,7 @@ class RISE(PerturbationAttribution):
'But got {}D.'.format(len(targets.shape))) 'But got {}D.'.format(len(targets.shape)))
if targets.shape and len(targets) != len(inputs): if targets.shape and len(targets) != len(inputs):
raise ValueError( raise ValueError(
'If `targets` is a 2D, 1D Tensor, it should have the same length as inputs {}. But got {}'.format(
'If `targets` is a 2D, 1D Tensor, it should have the same length as inputs {}. But got {}.'.format(
len(inputs), len(targets))) len(inputs), len(targets)))


@staticmethod @staticmethod


Loading…
Cancel
Save