@@ -0,0 +1,2 @@ | |||||
mindspore: | |||||
'mindspore/mindspore/version/202110/20211016/r1.5_20211016173415_b550abc902290739ca38bda0d01cfea7e053e77d/' |
@@ -35,13 +35,13 @@ if __name__ == '__main__': | |||||
model = Model(net) | model = Model(net) | ||||
# load data | # load data | ||||
ds_train = np.load('../../tests/ut/python/dataset/concept_train_lenet.npy') | ds_train = np.load('../../tests/ut/python/dataset/concept_train_lenet.npy') | ||||
ds_test1 = np.load('../../tests/ut/python/dataset/concept_test_lenet1.npy') | |||||
ds_test2 = np.load('../../tests/ut/python/dataset/concept_test_lenet2.npy') | |||||
ds_eval = np.load('../../tests/ut/python/dataset/concept_test_lenet1.npy') | |||||
ds_test = np.load('../../tests/ut/python/dataset/concept_test_lenet2.npy') | |||||
# ood detector initialization | # ood detector initialization | ||||
detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | ||||
# get optimal threshold with ds_test1 | |||||
num = int(len(ds_test1) / 2) | |||||
# get optimal threshold with ds_eval | |||||
num = int(len(ds_eval) / 2) | |||||
label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | ||||
optimal_threshold = detector.get_optimal_threshold(label, ds_test1) | |||||
# get result of ds_test2. We can also set threshold by ourself. | |||||
result = detector.ood_predict(optimal_threshold, ds_test2) | |||||
optimal_threshold = detector.get_optimal_threshold(label, ds_eval) | |||||
# get result of ds_test2. We can also set threshold by ourselves. | |||||
result = detector.ood_predict(optimal_threshold, ds_test) |
@@ -35,13 +35,13 @@ if __name__ == '__main__': | |||||
model = Model(net) | model = Model(net) | ||||
# load data | # load data | ||||
ds_train = np.load('train.npy') | ds_train = np.load('train.npy') | ||||
ds_test1 = np.load('test1.npy') | |||||
ds_test2 = np.load('test2.npy') | |||||
ds_eval = np.load('test1.npy') | |||||
ds_test = np.load('test2.npy') | |||||
# ood detector initialization | # ood detector initialization | ||||
detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | ||||
# get optimal threshold with ds_test1 | |||||
num = int(len(ds_test1) / 2) | |||||
# get optimal threshold with ds_eval | |||||
num = int(len(ds_eval) / 2) | |||||
label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | ||||
optimal_threshold = detector.get_optimal_threshold(label, ds_test1) | |||||
# get result of ds_test2. We can also set threshold by ourself. | |||||
result = detector.ood_predict(optimal_threshold, ds_test2) | |||||
optimal_threshold = detector.get_optimal_threshold(label, ds_eval) | |||||
# get result of ds_test2. We can also set threshold by ourselves. | |||||
result = detector.ood_predict(optimal_threshold, ds_test) |
@@ -274,11 +274,12 @@ class SimilarityDetector(Detector): | |||||
""" | """ | ||||
Filter adversarial noises in input samples. | Filter adversarial noises in input samples. | ||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): Data been used as references to create adversarial examples. | |||||
Raises: | Raises: | ||||
NotImplementedError: This function is not available | |||||
in class `SimilarityDetector`. | |||||
NotImplementedError: This function is not available in class `SimilarityDetector`. | |||||
""" | """ | ||||
msg = 'The function transform() is not available in the class ' \ | |||||
'`SimilarityDetector`.' | |||||
msg = 'The function transform() is not available in the class `SimilarityDetector`.' | |||||
LOGGER.error(TAG, msg) | LOGGER.error(TAG, msg) | ||||
raise NotImplementedError(msg) | raise NotImplementedError(msg) |
@@ -133,8 +133,11 @@ from mindarmour.reliability.concept_drift.concept_drift_check_images import OodD | |||||
#### Load Classification Model | #### Load Classification Model | ||||
For convenience, we use a pre-trained model file `checkpoint_lenet-10_1875.ckpt` | |||||
in 'mindarmour/tests/ut/python/dataset/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt'. | |||||
```python | ```python | ||||
ckpt_path = '../../dataset/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
ckpt_path = 'checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | net = LeNet5() | ||||
load_dict = load_checkpoint(ckpt_path) | load_dict = load_checkpoint(ckpt_path) | ||||
load_param_into_net(net, load_dict) | load_param_into_net(net, load_dict) | ||||
@@ -143,21 +146,111 @@ model = Model(net) | |||||
>`ckpt_path(str)`: the model path. | >`ckpt_path(str)`: the model path. | ||||
We can also use self-constructed model. | |||||
It is important that we need to name the model layer, and get the layer outputs. | |||||
Take LeNet as an example. | |||||
Firstly, we import `TensorSummary` module. | |||||
Secondly, we initialize it as `self.summary = TensorSummary()`. | |||||
Finally, we add `self.summary('name', x)` after each layer we pay attention to. Here, `name` of each layer is given by users. | |||||
After the above process, we can train the model and load it. | |||||
```python | |||||
from mindspore import nn | |||||
from mindspore.common.initializer import TruncatedNormal | |||||
from mindspore.ops import TensorSummary | |||||
def conv(in_channels, out_channels, kernel_size, stride=1, padding=0): | |||||
"""Wrap conv.""" | |||||
weight = weight_variable() | |||||
return nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding, | |||||
weight_init=weight, has_bias=False, pad_mode="valid") | |||||
def fc_with_initialize(input_channels, out_channels): | |||||
"""Wrap initialize method of full connection layer.""" | |||||
weight = weight_variable() | |||||
bias = weight_variable() | |||||
return nn.Dense(input_channels, out_channels, weight, bias) | |||||
def weight_variable(): | |||||
"""Wrap initialize variable.""" | |||||
return TruncatedNormal(0.05) | |||||
class LeNet5(nn.Cell): | |||||
""" | |||||
Lenet network | |||||
""" | |||||
def __init__(self): | |||||
super(LeNet5, self).__init__() | |||||
self.conv1 = conv(1, 6, 5) | |||||
self.conv2 = conv(6, 16, 5) | |||||
self.fc1 = fc_with_initialize(16*5*5, 120) | |||||
self.fc2 = fc_with_initialize(120, 84) | |||||
self.fc3 = fc_with_initialize(84, 10) | |||||
self.relu = nn.ReLU() | |||||
self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) | |||||
self.flatten = nn.Flatten() | |||||
self.summary = TensorSummary() | |||||
def construct(self, x): | |||||
""" | |||||
construct the network architecture | |||||
Returns: | |||||
x (tensor): network output | |||||
""" | |||||
x = self.conv1(x) | |||||
self.summary('1', x) | |||||
x = self.relu(x) | |||||
self.summary('2', x) | |||||
x = self.max_pool2d(x) | |||||
self.summary('3', x) | |||||
x = self.conv2(x) | |||||
self.summary('4', x) | |||||
x = self.relu(x) | |||||
self.summary('5', x) | |||||
x = self.max_pool2d(x) | |||||
self.summary('6', x) | |||||
x = self.flatten(x) | |||||
self.summary('7', x) | |||||
x = self.fc1(x) | |||||
self.summary('8', x) | |||||
x = self.relu(x) | |||||
self.summary('9', x) | |||||
x = self.fc2(x) | |||||
self.summary('10', x) | |||||
x = self.relu(x) | |||||
self.summary('11', x) | |||||
x = self.fc3(x) | |||||
self.summary('output', x) | |||||
return x | |||||
``` | |||||
#### Load Data | #### Load Data | ||||
We prepare three datasets. The training dataset, that is the same as the dataset to train the Lenet. Two testing datasets, the first testing dataset is with OOD label(0 for non-ood, and 1 for ood) for finding an optimal threshold for ood detection. | We prepare three datasets. The training dataset, that is the same as the dataset to train the Lenet. Two testing datasets, the first testing dataset is with OOD label(0 for non-ood, and 1 for ood) for finding an optimal threshold for ood detection. | ||||
The second testing dataset is for ood validation. The first testing dataset is not necessary if we would like to set threshold by ourselves | The second testing dataset is for ood validation. The first testing dataset is not necessary if we would like to set threshold by ourselves | ||||
```python | ```python | ||||
ds_train = np.load('../../dataset/concept_train_lenet.npy') | ds_train = np.load('../../dataset/concept_train_lenet.npy') | ||||
ds_test1 = np.load('../../dataset/concept_test_lenet1.npy') | |||||
ds_test2 = np.load('../../dataset/concept_test_lenet2.npy') | |||||
ds_eval = np.load('../../dataset/concept_test_lenet1.npy') | |||||
ds_test = np.load('../../dataset/concept_test_lenet2.npy') | |||||
``` | ``` | ||||
> `ds_train(numpy.ndarray)`: the train data. | > `ds_train(numpy.ndarray)`: the train data. | ||||
> `ds_test1(numpy.ndarray)`: the data for finding an optimal threshold. This dataset is not necessary. | |||||
> `ds_test2(numpy.ndarray)`: the test data for ood detection. | |||||
> `ds_eval(numpy.ndarray)`: the data for finding an optimal threshold. This dataset is not necessary. | |||||
> `ds_test(numpy.ndarray)`: the test data for ood detection. | |||||
#### OOD detector initialization | #### OOD detector initialization | ||||
@@ -172,30 +265,32 @@ detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='outpu | |||||
> `model(Model)`: the model trained by the `ds_train`. | > `model(Model)`: the model trained by the `ds_train`. | ||||
> `ds_train(numpy.ndarray)`: the training data. | > `ds_train(numpy.ndarray)`: the training data. | ||||
> `n_cluster(int)`: the feature cluster number. | > `n_cluster(int)`: the feature cluster number. | ||||
> `layer(str)`: the feature extraction layer. In our example, The layer name could be 'output[:Tensor]', '9[:Tensor]', '10[:Tensor]', '11[:Tensor]' for LeNet. | |||||
> `layer(str)`: the name of the feature extraction layer. | |||||
In our example, we input the layer name `output[:Tensor]`, which can also be`9[:Tensor]`, `10[:Tensor]`, `11[:Tensor]` for LeNet. | |||||
#### Optimal Threshold | #### Optimal Threshold | ||||
This step is optional. If we have a labeled dataset, named ds_test1, we can use the following code to find the optimal detection threshold. | |||||
This step is optional. If we have a labeled dataset, named `ds_eval`, we can use the following code to find the optimal detection threshold. | |||||
```python | ```python | ||||
# get optimal threshold with ds_test1 | |||||
num = int(len(ds_test1) / 2) | |||||
# get optimal threshold with ds_eval | |||||
num = int(len(ds_eval) / 2) | |||||
label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | ||||
optimal_threshold = detector.get_optimal_threshold(label, ds_test1) | |||||
optimal_threshold = detector.get_optimal_threshold(label, ds_eval) | |||||
``` | ``` | ||||
> `ds_test1(numpy.ndarray)`: the data for finding an optimal threshold. . | |||||
> `label(numpy.ndarray)`: the ood label of ds_test1. 0 means non-ood data, and 1 means ood data. | |||||
> `ds_eval(numpy.ndarray)`: the data for finding an optimal threshold. | |||||
> `label(numpy.ndarray)`: the ood label of ds_eval. 0 means non-ood data, and 1 means ood data. | |||||
#### Detection result | #### Detection result | ||||
```python | ```python | ||||
result = detector.ood_predict(optimal_threshold, ds_test2) | |||||
result = detector.ood_predict(optimal_threshold, ds_test) | |||||
``` | ``` | ||||
> `ds_test2(numpy.ndarray)`: the testing data for ood detection. | |||||
> `ds_test(numpy.ndarray)`: the testing data for ood detection. | |||||
> `optimal_threshold(float)`: the optimal threshold to judge out-of-distribution data. We can also set the threshold value by ourselves. | > `optimal_threshold(float)`: the optimal threshold to judge out-of-distribution data. We can also set the threshold value by ourselves. | ||||
## Script Description | ## Script Description | ||||
@@ -24,7 +24,6 @@ from mindspore.train.summary.summary_record import _get_summary_tensor_data | |||||
""" | """ | ||||
Out-of-Distribution detection for images. | Out-of-Distribution detection for images. | ||||
The sample can be run on Ascend 910 AI processor. | |||||
""" | """ | ||||
@@ -46,32 +45,60 @@ class OodDetector: | |||||
Args: | Args: | ||||
model (Model): The model for extracting features. | model (Model): The model for extracting features. | ||||
data (numpy.ndarray): Input data. | data (numpy.ndarray): Input data. | ||||
layer (str): The feature layer. The layer name could be 'output[:Tensor]', | |||||
'9[:Tensor]', '10[:Tensor]', '11[:Tensor]' for LeNet, and 'output[:Tensor]', | |||||
'1[:Tensor]' for Resnet. | |||||
layer (str): The name of the feature layer. layer (str) is represented as | |||||
'name[:Tensor]', where 'name' is given by users when training the model. | |||||
Please see more details about how to name the model layer in 'README.md'. | |||||
Returns: | Returns: | ||||
numpy.ndarray, the feature of input data. | |||||
numpy.ndarray, the data feature extracted by a certain neural layer. | |||||
""" | """ | ||||
model.predict(Tensor(data)) | model.predict(Tensor(data)) | ||||
layer_out = _get_summary_tensor_data() | layer_out = _get_summary_tensor_data() | ||||
return layer_out[layer].asnumpy() | return layer_out[layer].asnumpy() | ||||
def get_optimal_threshold(self, score, label, ds_test1): | |||||
def get_optimal_threshold(self, label, ds_eval): | |||||
""" | |||||
Get the optimal threshold. | |||||
Args: | |||||
label (numpy.ndarray): The label whether an image is in-distribution and out-of-distribution. | |||||
ds_eval (numpy.ndarray): The testing dataset to help find the threshold. | |||||
Returns: | |||||
- float, the optimal threshold. | |||||
""" | |||||
pass | pass | ||||
def ood_predict(self, threshold, ds_test2): | |||||
def ood_predict(self, threshold, ds_test): | |||||
""" | |||||
The out-of-distribution detection. | |||||
Args: | |||||
threshold (float): the threshold to judge ood data. One can set value by experience | |||||
or use function get_optimal_threshold. | |||||
ds_test (numpy.ndarray): The testing dataset. | |||||
Returns: | |||||
- numpy.ndarray, the detection result. 0 means the data is not ood, 1 means the data is ood. | |||||
""" | |||||
pass | pass | ||||
class OodDetectorFeatureCluster(OodDetector): | class OodDetectorFeatureCluster(OodDetector): | ||||
""" | """ | ||||
Train the OOD detector. | |||||
Train the OOD detector. Extract the training data features, and obtain the clustering centers. The distance between | |||||
the testing data features and the clustering centers determines whether an image is an out-of-distribution(OOD) | |||||
image or not. | |||||
Args: | Args: | ||||
model (Model):The training model. | model (Model):The training model. | ||||
ds_train (numpy.ndarray): The training dataset. | ds_train (numpy.ndarray): The training dataset. | ||||
n_cluster (int): The cluster number. | |||||
n_cluster (int): The cluster number. Belonging to [2,100]. | |||||
Usually, n_cluster equals to the class number of the training dataset. | |||||
If the OOD detector performs poor in the testing dataset, we can increase the value of n_cluster | |||||
appropriately. | |||||
layer (str): The name of the feature layer. layer (str) is represented by | |||||
'name[:Tensor]', where 'name' is given by users when training the model. | |||||
Please see more details about how to name the model layer in 'README.md'. | |||||
""" | """ | ||||
def __init__(self, model, ds_train, n_cluster, layer): | def __init__(self, model, ds_train, n_cluster, layer): | ||||
@@ -118,21 +145,20 @@ class OodDetectorFeatureCluster(OodDetector): | |||||
score = np.array(score) | score = np.array(score) | ||||
return score | return score | ||||
def get_optimal_threshold(self, label, test_data_threshold): | |||||
def get_optimal_threshold(self, label, ds_eval): | |||||
""" | """ | ||||
Get the optimal threshold. | Get the optimal threshold. | ||||
Args: | Args: | ||||
score (numpy.ndarray): The detection score of images. | |||||
label (numpy.ndarray): The label whether an image is in-distribution and out-of-distribution. | label (numpy.ndarray): The label whether an image is in-distribution and out-of-distribution. | ||||
test_data_threshold (numpy.ndarray): The testing dataset to help find the threshold. | |||||
ds_eval (numpy.ndarray): The testing dataset to help find the threshold. | |||||
Returns: | Returns: | ||||
- float, the optimal threshold. | - float, the optimal threshold. | ||||
""" | """ | ||||
check_param_type('label', label, np.ndarray) | check_param_type('label', label, np.ndarray) | ||||
check_param_type('ds_test1', test_data_threshold, np.ndarray) | |||||
score = self._get_ood_score(test_data_threshold) | |||||
check_param_type('ds_eval', ds_eval, np.ndarray) | |||||
score = self._get_ood_score(ds_eval) | |||||
acc = [] | acc = [] | ||||
threshold = [] | threshold = [] | ||||
for threshold_change in np.arange(0.0, 1.0, 0.01): | for threshold_change in np.arange(0.0, 1.0, 0.01): | ||||
@@ -154,7 +180,7 @@ class OodDetectorFeatureCluster(OodDetector): | |||||
The out-of-distribution detection. | The out-of-distribution detection. | ||||
Args: | Args: | ||||
threshold (float): the threshold to judge ood data. One can set value by experience | threshold (float): the threshold to judge ood data. One can set value by experience | ||||
or use function get_optimal_threshold. | |||||
or use function get_optimal_threshold. | |||||
ds_test (numpy.ndarray): The testing dataset. | ds_test (numpy.ndarray): The testing dataset. | ||||
Returns: | Returns: | ||||
@@ -49,16 +49,16 @@ def test_cp(): | |||||
model = Model(net) | model = Model(net) | ||||
# load data | # load data | ||||
ds_train = np.load('../../dataset/concept_train_lenet.npy') | ds_train = np.load('../../dataset/concept_train_lenet.npy') | ||||
ds_test1 = np.load('../../dataset/concept_test_lenet1.npy') | |||||
ds_test2 = np.load('../../dataset/concept_test_lenet2.npy') | |||||
ds_eval = np.load('../../dataset/concept_test_lenet1.npy') | |||||
ds_test = np.load('../../dataset/concept_test_lenet2.npy') | |||||
# ood detector initialization | # ood detector initialization | ||||
detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | detector = OodDetectorFeatureCluster(model, ds_train, n_cluster=10, layer='output[:Tensor]') | ||||
# get optimal threshold with ds_test1 | |||||
num = int(len(ds_test1) / 2) | |||||
# get optimal threshold with ds_eval | |||||
num = int(len(ds_eval) / 2) | |||||
label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | label = np.concatenate((np.zeros(num), np.ones(num)), axis=0) # ID data = 0, OOD data = 1 | ||||
optimal_threshold = detector.get_optimal_threshold(label, ds_test1) | |||||
# get result of ds_test2. We can also set threshold by ourself. | |||||
result = detector.ood_predict(optimal_threshold, ds_test2) | |||||
optimal_threshold = detector.get_optimal_threshold(label, ds_eval) | |||||
# get result of ds_test. We can also set threshold by ourselves. | |||||
result = detector.ood_predict(optimal_threshold, ds_test) | |||||
# result log | # result log | ||||
LOGGER.set_level(logging.DEBUG) | LOGGER.set_level(logging.DEBUG) | ||||
LOGGER.debug(TAG, '--start ood test--') | LOGGER.debug(TAG, '--start ood test--') | ||||