2.4K Star 8.2K Fork 4.4K

GVPMindSpore / mindspore

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
occlusion_sensitivity.py 9.53 KB
一键复制 编辑 原始数据 按行查看 历史
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""OcclusionSensitivity."""
from __future__ import absolute_import
import numpy as np
from mindspore import nn
from mindspore.common.tensor import Tensor
from mindspore import _checkparam as validator
from mindspore.train.metrics.metric import Metric, rearrange_inputs
try:
from tqdm import trange
except (ImportError, AttributeError):
trange = range
finally:
pass
class OcclusionSensitivity(Metric):
"""
Calculates the occlusion sensitivity of the model for a given image, which illustrates which parts of an image are
most important for a network's classification.
Occlusion sensitivity refers to how the predicted probability changes with the change of the occluded
part of an image. The higher the value in the output image is, the greater the decline of certainty, indicating
that the occluded area is more important in the decision-making process.
Args:
pad_val (float): The padding value of the occluded part in an image. Default: ``0.0`` .
margin (Union[int, Sequence]): Create a cuboid / cube around the voxel you want to occlude. Default: ``2`` .
n_batch (int): number of images in a batch. Default: ``128`` .
b_box (Sequence): Bounding box on which to perform the analysis. The output image will also match in size.
There should be a minimum and maximum for all dimensions except batch:
``[min1, max1, min2, max2,...]``. If no bounding box is supplied, this will be the same size
as the input image. If a bounding box is used, the output image will be cropped to this size.
Default: ``None`` .
Supported Platforms:
``Ascend`` ``GPU`` ``CPU``
Examples:
>>> import numpy as np
>>> from mindspore import nn, Tensor
>>> from mindspore.train import OcclusionSensitivity
>>>
>>> class DenseNet(nn.Cell):
... def __init__(self):
... super(DenseNet, self).__init__()
... w = np.array([[0.1, 0.8, 0.1, 0.1],[1, 1, 1, 1]]).astype(np.float32)
... b = np.array([0.3, 0.6]).astype(np.float32)
... self.dense = nn.Dense(4, 2, weight_init=Tensor(w), bias_init=Tensor(b))
...
... def construct(self, x):
... return self.dense(x)
>>>
>>> model = DenseNet()
>>> test_data = np.array([[0.1, 0.2, 0.3, 0.4]]).astype(np.float32)
>>> label = np.array(1).astype(np.int32)
>>> metric = OcclusionSensitivity()
>>> metric.clear()
>>> metric.update(model, test_data, label)
>>> score = metric.eval()
>>> print(score)
[0.29999995 0.6 1. 0.9]
"""
def __init__(self, pad_val=0.0, margin=2, n_batch=128, b_box=None):
super().__init__()
self.pad_val = validator.check_value_type("pad_val", pad_val, [float])
self.margin = validator.check_value_type("margin", margin, [int, list])
self.n_batch = validator.check_value_type("n_batch", n_batch, [int])
self.b_box = b_box if b_box is None else validator.check_value_type("b_box", b_box, [list])
self.clear()
def clear(self):
"""Clears the internal evaluation result."""
self._baseline = 0
self._sensitivity_im = 0
self._is_update = False
@rearrange_inputs
def update(self, *inputs):
"""
Updates input, including `model`, `y_pred` and `label`.
Args:
inputs: `y_pred` and `label` are a Tensor, list or numpy.ndarray.
`y_pred`: a batch of images to test, which could be 2D or 3D.
`label`: classification labels to check for changes. `label` is normally the true label, but
doesn't have to be.
`model` is the neural network.
Raises:
ValueError: If the number of inputs is not 3.
RuntimeError: If y_pred.shape[0] is not 1.
RuntimeError: If the number of labels is different from the number of batches.
"""
if len(inputs) != 3:
raise ValueError("For 'OcclusionSensitivity.update', it needs 3 inputs (classification model, "
"predicted value, label), but got {}.".format(len(inputs)))
model = inputs[0]
y_pred = self._convert_data(inputs[1])
label = self._convert_data(inputs[2])
model = validator.check_value_type("model", model, [nn.Cell])
if y_pred.shape[0] > 1:
raise RuntimeError(f"For 'OcclusionSensitivity.update', the shape at index 0 of the predicted value "
f"(input[1]) must be 1, but got {y_pred.shape[0]}.")
if isinstance(label, int):
label = np.array([[label]], dtype=int)
# If the label is a tensor, make sure there's only 1 element
elif np.prod(label.shape) != y_pred.shape[0]:
raise RuntimeError(f"For 'OcclusionSensitivity.update', the number of the label (input[2]) must be "
f"same as the batches, but got the label number {np.prod(label.shape)}, "
f"and batches {y_pred.shape[0]}.")
y_pred_shape = np.array(y_pred.shape[1:])
b_box_min, b_box_max = _check_input_bounding_box(self.b_box, y_pred_shape)
temp = model(Tensor(y_pred)).asnumpy()
self._baseline = temp[0, label].item()
batch_images = []
batch_ids = []
sensitivity_im = np.empty(0, dtype=float)
output_im_shape = y_pred_shape if self.b_box is None else b_box_max - b_box_min + 1
num_required_predictions = np.prod(output_im_shape)
for i in trange(num_required_predictions):
idx = np.unravel_index(i, output_im_shape)
if b_box_min is not None:
idx += b_box_min
min_idx = [max(0, i - self.margin) for i in idx]
max_idx = [min(j, i + self.margin) for i, j in zip(idx, y_pred_shape)]
occlu_im = y_pred.copy()
occlu_im[(...,) + tuple(slice(i, j) for i, j in zip(min_idx, max_idx))] = self.pad_val
batch_images.append(occlu_im)
batch_ids.append(label)
if len(batch_images) == self.n_batch or i == num_required_predictions - 1:
sensitivity_im = _append_to_sensitivity_im(model, batch_images, batch_ids, sensitivity_im)
batch_images = []
batch_ids = []
self._sensitivity_im = sensitivity_im.reshape(output_im_shape)
self._is_update = True
def eval(self):
"""
Computes the occlusion_sensitivity.
Returns:
A numpy ndarray.
Raises:
RuntimeError: If the update method is not called first, an error will be reported.
"""
if not self._is_update:
raise RuntimeError("Please call the 'update' method before calling 'eval' method.")
sensitivity = self._baseline - np.squeeze(self._sensitivity_im)
return sensitivity
def _append_to_sensitivity_im(model, batch_images, batch_ids, sensitivity_im):
"""
For a given number of images, the probability of predicting a given label is obtained. Attach to previous
assessment.
"""
batch_images = np.vstack(batch_images)
batch_ids = np.expand_dims(batch_ids, 1)
model_numpy = model(Tensor(batch_images)).asnumpy()
first_indices = np.arange(batch_ids.shape[0])[:, None]
scores = model_numpy[first_indices, batch_ids]
if sensitivity_im.size == 0:
return np.vstack(scores)
return np.vstack((sensitivity_im, scores))
def _check_input_bounding_box(b_box, im_shape):
"""Check that the bounding box (if supplied) is as expected."""
# If no bounding box has been supplied, set min and max to None
if b_box is None:
b_box_min = b_box_max = None
else:
if len(b_box) != 2 * len(im_shape):
raise ValueError(f"For 'OcclusionSensitivity', the bounding box must contain upper and lower for "
f"all dimensions (except batch number), and the length of 'b_box' must be twice "
f"as long as predicted value's (except batch number), but got 'b_box' length "
f"{len(b_box)}, predicted value length (except batch number) {len(im_shape)}.")
b_box_min = np.array(b_box[::2])
b_box_max = np.array(b_box[1::2])
b_box_min[b_box_min < 0] = 0
b_box_max[b_box_max < 0] = im_shape[b_box_max < 0] - 1
if np.any(b_box_max >= im_shape):
raise ValueError("For 'OcclusionSensitivity', maximum bounding box must be smaller than image size "
"for all values.")
if np.any(b_box_min > b_box_max):
raise ValueError("For 'OcclusionSensitivity', minimum bounding box must be smaller than maximum "
"bounding box for all values.")
return b_box_min, b_box_max
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/mindspore/mindspore.git
git@gitee.com:mindspore/mindspore.git
mindspore
mindspore
mindspore
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891