From f5ec52cdbd4a80a1edb8b86b2745212a1b546038 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Wed, 4 Sep 2024 00:22:59 +0800 Subject: [PATCH 01/14] BEYOND Detector and test case --- art/defences/detector/evasion/__init__.py | 2 + .../detector/evasion/beyond_detector.py | 163 ++++++++++++++++ .../detector/evasion/test_beyond_detector.py | 180 ++++++++++++++++++ 3 files changed, 345 insertions(+) create mode 100644 art/defences/detector/evasion/beyond_detector.py create mode 100644 tests/defences/detector/evasion/test_beyond_detector.py diff --git a/art/defences/detector/evasion/__init__.py b/art/defences/detector/evasion/__init__.py index 26112a2afe..3546431c38 100644 --- a/art/defences/detector/evasion/__init__.py +++ b/art/defences/detector/evasion/__init__.py @@ -6,3 +6,5 @@ from art.defences.detector.evasion.binary_input_detector import BinaryInputDetector from art.defences.detector.evasion.binary_activation_detector import BinaryActivationDetector from art.defences.detector.evasion.subsetscanning.detector import SubsetScanningDetector +from art.defences.detector.evasion.beyond_detector import BeyondDetector + diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py new file mode 100644 index 0000000000..5f0cccd10a --- /dev/null +++ b/art/defences/detector/evasion/beyond_detector.py @@ -0,0 +1,163 @@ +# MIT License +# +# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2023 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +""" +This module implements the abstract base class for all evasion detectors. +""" +from __future__ import absolute_import, division, print_function, unicode_literals, annotations + +import abc +from typing import Any + +import numpy as np + +from art.defences.detector.evasion.evasion_detector import EvasionDetector + +class BeyondDetector(EvasionDetector): + """ + BEYOND detector for adversarial samples detection. + This detector uses a combination of SSL and target model predictions to detect adversarial samples. + """ + + defence_params = ["target_model", "ssl_model", "augmentations", "aug_num", "alpha", "K", "percentile"] + + def __init__(self, + target_model, + ssl_model, + augmentations=None, + aug_num=50, + alpha=0.8, + K=20, + percentile=5) -> None: + """ + Initialize the BEYOND detector. + + :param target_model: The target model to be protected + :param ssl_model: The self-supervised learning model used for feature extraction + :param augmentation: data augmentations for generating neighborhoods + :param aug_num: Number of augmentations to apply to each sample (default: 50) + :param alpha: Weight factor for combining label and representation similarities (default: 0.8) + :param K: Number of top similarities to consider (default: 20) + :param percentile: using to calculate the threshold + """ + super().__init__() + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + self.target_model = target_model.to(self.device) + self.ssl_model = ssl_model.to(self.device) + self.aug_num = aug_num + self.alpha = alpha + self.K = K + + self.backbone = ssl_model.backbone + self.classifier = ssl_model.classifier + self.projector = ssl_model.projector + + self.img_augmentations = augmentations + + self.percentile = percentile # determinate the threshold + self.threshold = None + + + + def _multi_transform(self, img): + return torch.stack([self.img_augmentations(img) for _ in range(self.aug_num)], dim=1) + + def _get_metrics(self, x: np.ndarray, batch_size: int = 128) -> tuple[dict, np.ndarray]: + """ + Calculate similarities that combining label consistency and representation similarity for given samples + :param x: Input samples + :param batch_size: Batch size for processing + :return: A report similarities + """ + samples = torch.from_numpy(x).to(self.device) + + self.target_model.eval() + self.backbone.eval() + self.classifier.eval() + self.projector.eval() + + number_batch = int(math.ceil(len(samples) / batch_size)) + + similarities = [] + + with torch.no_grad(): + for index in range(number_batch): + start = index * batch_size + end = min((index + 1) * batch_size, len(samples)) + + batch_samples = samples[start:end] + b, c, h, w = batch_samples.shape + + trans_images = self._multi_transform(batch_samples).to(self.device) + ssl_backbone_out = self.backbone(batch_samples) + + ssl_repre = self.projector(ssl_backbone_out) + ssl_pred = self.classifier(ssl_backbone_out) + ssl_label = torch.max(ssl_pred, -1)[1] + + aug_backbone_out = self.backbone(trans_images.reshape(-1, c, h, w)) + aug_repre = self.projector(aug_backbone_out) + aug_pred = self.classifier(aug_backbone_out) + aug_pred = aug_pred.reshape(b, self.aug_num, -1) + + sim_repre = F.cosine_similarity(ssl_repre.unsqueeze(dim=1), aug_repre.reshape(b, self.aug_num, -1), dim=2) + sim_preds = F.cosine_similarity(F.one_hot(torch.argmax(ssl_label, dim=1), num_classes=ssl_pred.shape[-1]).unsqueeze(dim=1), aug_pred, dim=2) + + similarities.append((self.alpha * sim_preds + (1-self.alpha)*sim_repre).sort(descending=True)[0].cpu().numpy()) + + similarities = np.concatenate(similarities, axis=0) + + return similarities + + + def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: int = 20, **kwargs) -> None: + """ + Determine a threshold that covers 95% of clean samples. + :param x: Clean sample data + :param y: Clean sample labels (not used in this method) + :param batch_size: Batch size for processing + :param nb_epochs: Number of training epochs (not used in this method) + """ + clean_similarities = self._get_metrics(x, batch_size) + + # 使用第K-1列的值来确定阈值 + k_minus_one_metrics = clean_metrics[:, self.K-1] + + # 计算95%分位数作为阈值 + self.threshold = np.percentile(k_minus_one_metrics, self.threshold) + + print(f"Threshold set to: {self.threshold}") + + def detect(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> tuple[dict, np.ndarray]: + """ + Detect whether given samples are adversarial + :param x: Input samples + :param batch_size: Batch size for processing + :return: (report, is_adversarial): + where report containing detection results + where is_adversarial is a boolean list indicating whether samples are adversarial or not + """ + if self.threshold is None: + raise ValueError("Detector has not been fitted. Call fit() before detect().") + + similarities = self._get_metrics(x, batch_size) + + report = similarities[:, self.K-1] + is_adversarial = report < self.threshold + + return report, is_adversarial diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py new file mode 100644 index 0000000000..9e0b6a3406 --- /dev/null +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -0,0 +1,180 @@ +# MIT License +# +# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2023 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from __future__ import absolute_import, division, print_function, unicode_literals + +import logging +import pytest +import numpy as np + +import sys +import os + +from art.attacks.evasion.fast_gradient import FastGradientMethod +from art.estimators.classification import PyTorchClassifier +from art.defences.detector.evasion import BeyondDetector +from art.utils import load_dataset, get_file + + +from tests.utils import ARTTestException + +logger = logging.getLogger(__name__) + +import torch.nn as nn +from torchvision import models, transforms + +class SimSiamWithCls(nn.Module): + ''' + SimSiam with Classifier + ''' + def __init__(self, arch='resnet18', feat_dim=2048, num_proj_layers=2): + + super(SimSiamWithCls, self).__init__() + self.backbone = models.resnet18() + out_dim = self.backbone.fc.weight.shape[1] + self.backbone.conv1 = nn.Conv2d( + 3, 64, kernel_size=3, stride=1, padding=2, bias=False + ) + self.backbone.maxpool = nn.Identity() + self.backbone.fc = nn.Identity() + self.classifier = nn.Linear(out_dim, 10) + + pred_hidden_dim = int(feat_dim / 4) + + self.projector = nn.Sequential( + nn.Linear(out_dim, feat_dim, bias=False), + nn.BatchNorm1d(feat_dim), + nn.ReLU(), + nn.Linear(feat_dim, feat_dim, bias=False), + nn.BatchNorm1d(feat_dim), + nn.ReLU(), + nn.Linear(feat_dim, feat_dim), + nn.BatchNorm1d(feat_dim, affine=False), + ) + self.projector[6].bias.requires_grad = False + + self.predictor = nn.Sequential( + nn.Linear(feat_dim, pred_hidden_dim, bias=False), + nn.BatchNorm1d(pred_hidden_dim), + nn.ReLU(), + nn.Linear(pred_hidden_dim, feat_dim), + ) + + def forward(self, img, im_aug1=None, im_aug2=None): + + r_ori = self.backbone(img) + if im_aug1 is None and im_aug2 is None: + cls = self.classifier(r_ori) + rep = self.projector(r_ori) + return {'cls': cls, 'rep':rep} + else: + + r1 = self.backbone(im_aug1) + r2 = self.backbone(im_aug2) + + z1 = self.projector(r1) + z2 = self.projector(r2) + # print("shape of z:", z1.shape) + + p1 = self.predictor(z1) + p2 = self.predictor(z2) + # print("shape of p:", p1.shape) + + return {'z1': z1, 'z2': z2, 'p1': p1, 'p2': p2} + +@pytest.fixture +def get_cifar10(): + """ + Loads CIFAR10 dataset. + """ + (x_train, y_train), (x_test, y_test), min_, max_ = load_cifar10() + return (x_train, y_train), (x_test, y_test), min_, max_ + + +@pytest.fixture +def get_ssl_model(weights_path): + """ + Loads the SSL model (SimSiamWithCls). + """ + model = SimSiamWithCls() + model.load_state_dict(torch.load(weights_path)) + return model + +@pytest.mark.only_with_platform("pytorch") +def test_beyond_detector(art_warning, get_cifar10, get_ssl_model): + try: + # Load CIFAR10 data + (x_train, y_train), (x_test, y_test), min_, max_ = get_cifar10 + + # Load models + # Download pretrained weights from https://drive.google.com/drive/folders/1ieEdd7hOj2CIl1FQfu4-3RGZmEj-mesi?usp=sharing + target_model = models.resnet18() + target_model.load_state_dict(torch.load("./resnet_c10.pth")) + ssl_model = get_ssl_model() + ssl_model.load_state_dict(torch.load("./simsiam_c10.pth")) + + + # Generate adversarial samples + attack = FastGradientMethod(estimator=target_model, eps=0.05) + x_test_adv = attack.generate(x_test) + + img_augmentations = transforms.Compose([ + transforms.RandomResizedCrop(32, scale=(0.2, 1.)), + transforms.RandomHorizontalFlip(), + transforms.RandomApply([ + transforms.ColorJitter(0.4, 0.4, 0.4, 0.1) # not strengthened + ], p=0.8), + transforms.RandomGrayscale(p=0.2), + transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) + ]) + + # Initialize BeyondDetector + detector = BeyondDetector( + target_model=target_model, + ssl_model=ssl_model, + img_augmentation=img_augmentations, + aug_num=50, + alpha=0.8, + K=20, + percentile=5 + ) + + # Fit the detector + detector.fit(x_train, y_train, batch_size=128) + + # Apply detector on clean and adversarial test data + _, test_detection = detector.detect(x_test) + _, test_adv_detection = detector.detect(x_test_adv) + + # Assert there is at least one true positive and negative + nb_true_positives = np.sum(test_adv_detection) + nb_true_negatives = len(test_detection) - np.sum(test_detection) + assert nb_true_positives > 0 + assert nb_true_negatives > 0 + + # Calculate and print detection accuracy + clean_accuracy = 1 - np.mean(test_detection) + adv_accuracy = np.mean(test_adv_detection) + print(f"Clean Detection Accuracy: {clean_accuracy:.4f}") + print(f"Adversarial Detection Accuracy: {adv_accuracy:.4f}") + + except ARTTestException as e: + art_warning(e) + +if __name__ == "__main__": + + test_beyond_detector() \ No newline at end of file From d9130cf6a923bcd18227438687fbe169957cdabc Mon Sep 17 00:00:00 2001 From: allenhzy Date: Thu, 5 Dec 2024 23:26:16 +0800 Subject: [PATCH 02/14] revision by comments --- .../detector/evasion/beyond_detector.py | 19 ++++++-------- .../detector/evasion/test_beyond_detector.py | 25 +++---------------- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index 5f0cccd10a..8206c0278e 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -1,6 +1,6 @@ # MIT License # -# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2023 +# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2024 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -16,21 +16,19 @@ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """ -This module implements the abstract base class for all evasion detectors. +This module implements the BEYOND detector for adversarial examples detection. +| Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ -from __future__ import absolute_import, division, print_function, unicode_literals, annotations - -import abc -from typing import Any - import numpy as np from art.defences.detector.evasion.evasion_detector import EvasionDetector class BeyondDetector(EvasionDetector): """ - BEYOND detector for adversarial samples detection. - This detector uses a combination of SSL and target model predictions to detect adversarial samples. + BEYOND detector for adversarial samples detection. + This detector uses a combination of SSL and target model predictions to detect adversarial examples. + + | Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ defence_params = ["target_model", "ssl_model", "augmentations", "aug_num", "alpha", "K", "percentile"] @@ -133,9 +131,6 @@ def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: in :param batch_size: Batch size for processing :param nb_epochs: Number of training epochs (not used in this method) """ - clean_similarities = self._get_metrics(x, batch_size) - - # 使用第K-1列的值来确定阈值 k_minus_one_metrics = clean_metrics[:, self.K-1] # 计算95%分位数作为阈值 diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index 9e0b6a3406..7747e9de2b 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -21,11 +21,7 @@ import pytest import numpy as np -import sys -import os - from art.attacks.evasion.fast_gradient import FastGradientMethod -from art.estimators.classification import PyTorchClassifier from art.defences.detector.evasion import BeyondDetector from art.utils import load_dataset, get_file @@ -88,22 +84,12 @@ def forward(self, img, im_aug1=None, im_aug2=None): z1 = self.projector(r1) z2 = self.projector(r2) - # print("shape of z:", z1.shape) p1 = self.predictor(z1) p2 = self.predictor(z2) - # print("shape of p:", p1.shape) return {'z1': z1, 'z2': z2, 'p1': p1, 'p2': p2} -@pytest.fixture -def get_cifar10(): - """ - Loads CIFAR10 dataset. - """ - (x_train, y_train), (x_test, y_test), min_, max_ = load_cifar10() - return (x_train, y_train), (x_test, y_test), min_, max_ - @pytest.fixture def get_ssl_model(weights_path): @@ -115,10 +101,10 @@ def get_ssl_model(weights_path): return model @pytest.mark.only_with_platform("pytorch") -def test_beyond_detector(art_warning, get_cifar10, get_ssl_model): +def test_beyond_detector(art_warning, load_cifar10, get_ssl_model): try: # Load CIFAR10 data - (x_train, y_train), (x_test, y_test), min_, max_ = get_cifar10 + (x_train, y_train), (x_test, y_test), _, _ = load_cifar10() # Load models # Download pretrained weights from https://drive.google.com/drive/folders/1ieEdd7hOj2CIl1FQfu4-3RGZmEj-mesi?usp=sharing @@ -146,7 +132,7 @@ def test_beyond_detector(art_warning, get_cifar10, get_ssl_model): detector = BeyondDetector( target_model=target_model, ssl_model=ssl_model, - img_augmentation=img_augmentations, + augmentations=img_augmentations, aug_num=50, alpha=0.8, K=20, @@ -163,14 +149,9 @@ def test_beyond_detector(art_warning, get_cifar10, get_ssl_model): # Assert there is at least one true positive and negative nb_true_positives = np.sum(test_adv_detection) nb_true_negatives = len(test_detection) - np.sum(test_detection) - assert nb_true_positives > 0 - assert nb_true_negatives > 0 - # Calculate and print detection accuracy clean_accuracy = 1 - np.mean(test_detection) adv_accuracy = np.mean(test_adv_detection) - print(f"Clean Detection Accuracy: {clean_accuracy:.4f}") - print(f"Adversarial Detection Accuracy: {adv_accuracy:.4f}") except ARTTestException as e: art_warning(e) From c2ec510bb2c091a24fa8ba2388d93db85217dab9 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Thu, 5 Dec 2024 23:35:47 +0800 Subject: [PATCH 03/14] revision by comments --- tests/defences/detector/evasion/test_beyond_detector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index 7747e9de2b..fa7435e1bf 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -1,6 +1,6 @@ # MIT License # -# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2023 +# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2024 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the From 32cb990f91a35ebee073b7af4636ba159afa6577 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Sat, 7 Dec 2024 00:05:44 +0800 Subject: [PATCH 04/14] revision by comment --- art/defences/detector/evasion/beyond_detector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index 8206c0278e..11e6527f84 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -133,7 +133,6 @@ def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: in """ k_minus_one_metrics = clean_metrics[:, self.K-1] - # 计算95%分位数作为阈值 self.threshold = np.percentile(k_minus_one_metrics, self.threshold) print(f"Threshold set to: {self.threshold}") From 4628d64ba3a3413b291ddf7c248dd4e9a96701ac Mon Sep 17 00:00:00 2001 From: allenhzy Date: Sat, 7 Dec 2024 00:08:22 +0800 Subject: [PATCH 05/14] remove print --- art/defences/detector/evasion/beyond_detector.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index 11e6527f84..848fab3b2b 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -134,8 +134,6 @@ def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: in k_minus_one_metrics = clean_metrics[:, self.K-1] self.threshold = np.percentile(k_minus_one_metrics, self.threshold) - - print(f"Threshold set to: {self.threshold}") def detect(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> tuple[dict, np.ndarray]: """ From 2e7b1a453c6ae752d17477ba181b528272a385c4 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Sat, 7 Dec 2024 00:19:41 +0800 Subject: [PATCH 06/14] adding Type of arguments --- .../detector/evasion/beyond_detector.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index 848fab3b2b..ae1cba33e0 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -20,6 +20,12 @@ | Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ import numpy as np +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from art.utils import CLASSIFIER_NEURALNETWORK_TYPE + + +logger = logging.getLogger(__name__) from art.defences.detector.evasion.evasion_detector import EvasionDetector @@ -34,13 +40,13 @@ class BeyondDetector(EvasionDetector): defence_params = ["target_model", "ssl_model", "augmentations", "aug_num", "alpha", "K", "percentile"] def __init__(self, - target_model, - ssl_model, - augmentations=None, - aug_num=50, - alpha=0.8, - K=20, - percentile=5) -> None: + target_model: "CLASSIFIER_NEURALNETWORK_TYPE", + ssl_model: "CLASSIFIER_NEURALNETWORK_TYPE", + augmentations: Optional[Callable] = None, + aug_num: int=50, + alpha: float=0.8, + K:int=20, + percentile:int=5) -> None: """ Initialize the BEYOND detector. @@ -72,7 +78,7 @@ def __init__(self, - def _multi_transform(self, img): + def _multi_transform(self, img: torch.Tensor) -> torch.Tensor: return torch.stack([self.img_augmentations(img) for _ in range(self.aug_num)], dim=1) def _get_metrics(self, x: np.ndarray, batch_size: int = 128) -> tuple[dict, np.ndarray]: From 5a92ea31f7bd47a22e186ce9d20b931920b6a56a Mon Sep 17 00:00:00 2001 From: allenhzy Date: Sat, 7 Dec 2024 00:23:29 +0800 Subject: [PATCH 07/14] method comments --- art/defences/detector/evasion/beyond_detector.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index ae1cba33e0..9b1ac81a90 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -84,6 +84,7 @@ def _multi_transform(self, img: torch.Tensor) -> torch.Tensor: def _get_metrics(self, x: np.ndarray, batch_size: int = 128) -> tuple[dict, np.ndarray]: """ Calculate similarities that combining label consistency and representation similarity for given samples + :param x: Input samples :param batch_size: Batch size for processing :return: A report similarities @@ -132,6 +133,7 @@ def _get_metrics(self, x: np.ndarray, batch_size: int = 128) -> tuple[dict, np.n def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: int = 20, **kwargs) -> None: """ Determine a threshold that covers 95% of clean samples. + :param x: Clean sample data :param y: Clean sample labels (not used in this method) :param batch_size: Batch size for processing @@ -144,6 +146,7 @@ def fit(self, x: np.ndarray, y: np.ndarray, batch_size: int = 128, nb_epochs: in def detect(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> tuple[dict, np.ndarray]: """ Detect whether given samples are adversarial + :param x: Input samples :param batch_size: Batch size for processing :return: (report, is_adversarial): From 7902e01438aba7d4c2fa2c22a60713c1255ae665 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Sat, 7 Dec 2024 00:26:32 +0800 Subject: [PATCH 08/14] delete unused import --- tests/defences/detector/evasion/test_beyond_detector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index fa7435e1bf..7da225de9e 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -21,16 +21,16 @@ import pytest import numpy as np +import torch.nn as nn + from art.attacks.evasion.fast_gradient import FastGradientMethod from art.defences.detector.evasion import BeyondDetector -from art.utils import load_dataset, get_file from tests.utils import ARTTestException logger = logging.getLogger(__name__) -import torch.nn as nn from torchvision import models, transforms class SimSiamWithCls(nn.Module): From d5a0cd9bab02a11a3ddbc8ddc9e630e6642f680c Mon Sep 17 00:00:00 2001 From: allenhzy Date: Tue, 10 Dec 2024 16:13:51 +0800 Subject: [PATCH 09/14] follow codeQL and remove unused variable --- art/defences/detector/evasion/beyond_detector.py | 3 +-- tests/defences/detector/evasion/test_beyond_detector.py | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index 9b1ac81a90..fdfa5aaf79 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -17,6 +17,7 @@ # SOFTWARE. """ This module implements the BEYOND detector for adversarial examples detection. + | Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ import numpy as np @@ -25,8 +26,6 @@ from art.utils import CLASSIFIER_NEURALNETWORK_TYPE -logger = logging.getLogger(__name__) - from art.defences.detector.evasion.evasion_detector import EvasionDetector class BeyondDetector(EvasionDetector): diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index 7da225de9e..9cca7f54a4 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -29,8 +29,6 @@ from tests.utils import ARTTestException -logger = logging.getLogger(__name__) - from torchvision import models, transforms class SimSiamWithCls(nn.Module): From f197bbef68001b551331c8a65b0eb34fb0119238 Mon Sep 17 00:00:00 2001 From: Beat Buesser <49047826+beat-buesser@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:47:26 +0100 Subject: [PATCH 10/14] Update tests/defences/detector/evasion/test_beyond_detector.py --- tests/defences/detector/evasion/test_beyond_detector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index 9cca7f54a4..4d8737e1d3 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -17,7 +17,6 @@ # SOFTWARE. from __future__ import absolute_import, division, print_function, unicode_literals -import logging import pytest import numpy as np From a7afbd1b575cf67251256a77e048cc0212d283a1 Mon Sep 17 00:00:00 2001 From: Beat Buesser <49047826+beat-buesser@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:47:37 +0100 Subject: [PATCH 11/14] Update art/defences/detector/evasion/beyond_detector.py --- art/defences/detector/evasion/beyond_detector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index fdfa5aaf79..e420759745 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -41,7 +41,7 @@ class BeyondDetector(EvasionDetector): def __init__(self, target_model: "CLASSIFIER_NEURALNETWORK_TYPE", ssl_model: "CLASSIFIER_NEURALNETWORK_TYPE", - augmentations: Optional[Callable] = None, + augmentations: Callable | None, aug_num: int=50, alpha: float=0.8, K:int=20, From 94c6cedef491f55d6033fb73162d622ff9ece870 Mon Sep 17 00:00:00 2001 From: Beat Buesser <49047826+beat-buesser@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:47:49 +0100 Subject: [PATCH 12/14] Update art/defences/detector/evasion/beyond_detector.py --- art/defences/detector/evasion/beyond_detector.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index e420759745..af4bf81058 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -20,6 +20,8 @@ | Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ +from __future__ import annotations + import numpy as np from typing import TYPE_CHECKING if TYPE_CHECKING: From 318f9deac42c94a4e6283b91f8b3120090534da5 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Thu, 12 Dec 2024 22:31:21 +0800 Subject: [PATCH 13/14] remove unused import --- art/defences/detector/evasion/beyond_detector.py | 12 ++++++++---- .../detector/evasion/test_beyond_detector.py | 4 +--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index fdfa5aaf79..66b73abab3 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -20,14 +20,18 @@ | Paper link: https://openreview.net/pdf?id=S4LqI6CcJ3 """ -import numpy as np +from __future__ import annotations + from typing import TYPE_CHECKING -if TYPE_CHECKING: - from art.utils import CLASSIFIER_NEURALNETWORK_TYPE +import numpy as np from art.defences.detector.evasion.evasion_detector import EvasionDetector +if TYPE_CHECKING: + from art.utils import CLASSIFIER_NEURALNETWORK_TYPE + + class BeyondDetector(EvasionDetector): """ BEYOND detector for adversarial samples detection. @@ -41,7 +45,7 @@ class BeyondDetector(EvasionDetector): def __init__(self, target_model: "CLASSIFIER_NEURALNETWORK_TYPE", ssl_model: "CLASSIFIER_NEURALNETWORK_TYPE", - augmentations: Optional[Callable] = None, + augmentations: Callable | None, aug_num: int=50, alpha: float=0.8, K:int=20, diff --git a/tests/defences/detector/evasion/test_beyond_detector.py b/tests/defences/detector/evasion/test_beyond_detector.py index 9cca7f54a4..b4f2ced48d 100644 --- a/tests/defences/detector/evasion/test_beyond_detector.py +++ b/tests/defences/detector/evasion/test_beyond_detector.py @@ -17,19 +17,17 @@ # SOFTWARE. from __future__ import absolute_import, division, print_function, unicode_literals -import logging import pytest import numpy as np import torch.nn as nn +from torchvision import models, transforms from art.attacks.evasion.fast_gradient import FastGradientMethod from art.defences.detector.evasion import BeyondDetector - from tests.utils import ARTTestException -from torchvision import models, transforms class SimSiamWithCls(nn.Module): ''' From f6e337162d2775a01e379d63dddc35b9e12aeda3 Mon Sep 17 00:00:00 2001 From: allenhzy Date: Thu, 12 Dec 2024 22:41:32 +0800 Subject: [PATCH 14/14] follow import standard --- art/defences/detector/evasion/beyond_detector.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/art/defences/detector/evasion/beyond_detector.py b/art/defences/detector/evasion/beyond_detector.py index e43c3778b9..66b73abab3 100644 --- a/art/defences/detector/evasion/beyond_detector.py +++ b/art/defences/detector/evasion/beyond_detector.py @@ -22,10 +22,6 @@ """ from __future__ import annotations -<<<<<<< HEAD -======= -import numpy as np ->>>>>>> 94c6cedef491f55d6033fb73162d622ff9ece870 from typing import TYPE_CHECKING import numpy as np