Skip to content

Commit df6b772

Browse files
authored
Merge pull request #2650 from c4ts0up/detector-refactoring
Addition of CCA-UD poison detector for DNNs
2 parents 4f21e6b + 386d73a commit df6b772

File tree

12 files changed

+6495
-352
lines changed

12 files changed

+6495
-352
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ RUN pip3 install tensorflow==2.9.1 keras==2.9.0 numpy==1.22.4 scipy==1.8.1 matpl
99
resampy==0.3.1 ffmpeg-python==0.2.0 cma==3.2.2 pandas==1.4.3 h5py==3.7.0 tensorflow-addons==0.17.1 \
1010
torch==1.12.0 torchaudio==0.12.0 torchvision==0.13.0 catboost==1.0.6 GPy==1.10.0 \
1111
lightgbm==3.3.2 xgboost==1.6.1 kornia==0.6.6 lief==0.12.1 pytest==7.1.2 pytest-pep8==1.0.6 \
12-
pytest-mock==3.8.2 requests==2.28.1
12+
pytest-mock==3.8.2 requests==2.28.1 umap-learn==0.5.7
1313

1414
RUN apt-get -y install ffmpeg libavcodec-extra vim git
1515

art/attacks/poisoning/perturbations/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Module providing perturbation functions under a common interface
33
"""
44

5-
from art.attacks.poisoning.perturbations.image_perturbations import (
5+
from .image_perturbations import (
66
add_pattern_bd,
77
add_single_bd,
88
insert_image,

art/defences/detector/poison/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from art.defences.detector.poison.poison_filtering_defence import PoisonFilteringDefence
66
from art.defences.detector.poison.ground_truth_evaluator import GroundTruthEvaluator
77
from art.defences.detector.poison.activation_defence import ActivationDefence
8-
from art.defences.detector.poison.clustering_analyzer import ClusteringAnalyzer
8+
import art.defences.detector.poison.clustering_analyzer as ClusteringAnalyzer
99
from art.defences.detector.poison.provenance_defense import ProvenanceDefense
1010
from art.defences.detector.poison.roni import RONIDefense
1111
from art.defences.detector.poison.spectral_signature_defense import SpectralSignatureDefense

art/defences/detector/poison/activation_defence.py

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@
3636
from sklearn.cluster import KMeans, MiniBatchKMeans
3737

3838
from art.data_generators import DataGenerator
39-
from art.defences.detector.poison.clustering_analyzer import ClusteringAnalyzer
4039
from art.defences.detector.poison.ground_truth_evaluator import GroundTruthEvaluator
4140
from art.defences.detector.poison.poison_filtering_defence import PoisonFilteringDefence
4241
from art.utils import segment_by_class
4342
from art.visualization import create_sprite, save_image, plot_3d
4443

44+
from art.defences.detector.poison.clustering_analyzer import ClusterAnalysisType, get_cluster_analyzer
45+
4546
if TYPE_CHECKING:
4647
from art.utils import CLASSIFIER_NEURALNETWORK_TYPE
4748

@@ -311,44 +312,27 @@ def analyze_clusters(self, **kwargs) -> tuple[dict[str, Any], np.ndarray]:
311312
:return: (report, assigned_clean_by_class), where the report is a dict object and assigned_clean_by_class
312313
is a list of arrays that contains what data points where classified as clean.
313314
"""
315+
# default argument setting
314316
self.set_params(**kwargs)
315317

316318
if not self.clusters_by_class:
317319
self.cluster_activations()
318320

319-
analyzer = ClusteringAnalyzer()
320-
if self.cluster_analysis == "smaller":
321-
(
322-
self.assigned_clean_by_class,
323-
self.poisonous_clusters,
324-
report,
325-
) = analyzer.analyze_by_size(self.clusters_by_class)
326-
elif self.cluster_analysis == "relative-size":
327-
(
328-
self.assigned_clean_by_class,
329-
self.poisonous_clusters,
330-
report,
331-
) = analyzer.analyze_by_relative_size(self.clusters_by_class)
332-
elif self.cluster_analysis == "distance":
333-
(
334-
self.assigned_clean_by_class,
335-
self.poisonous_clusters,
336-
report,
337-
) = analyzer.analyze_by_distance(
338-
self.clusters_by_class,
339-
separated_activations=self.red_activations_by_class,
321+
analysis_type = ClusterAnalysisType(self.cluster_analysis)
322+
analyzer = get_cluster_analyzer(analysis_type)
323+
324+
if analysis_type in [ClusterAnalysisType.SMALLER, ClusterAnalysisType.RELATIVE_SIZE]:
325+
self.assigned_clean_by_class, self.poisonous_clusters, report = analyzer(self.clusters_by_class)
326+
elif analysis_type == ClusterAnalysisType.DISTANCE:
327+
self.assigned_clean_by_class, self.poisonous_clusters, report = analyzer(
328+
self.clusters_by_class, separated_activations=self.red_activations_by_class
340329
)
341-
elif self.cluster_analysis == "silhouette-scores":
342-
(
343-
self.assigned_clean_by_class,
344-
self.poisonous_clusters,
345-
report,
346-
) = analyzer.analyze_by_silhouette_score(
347-
self.clusters_by_class,
348-
reduced_activations_by_class=self.red_activations_by_class,
330+
elif analysis_type == ClusterAnalysisType.SILHOUETTE_SCORES:
331+
self.assigned_clean_by_class, self.poisonous_clusters, report = analyzer(
332+
self.clusters_by_class, reduced_activations_by_class=self.red_activations_by_class
349333
)
350334
else:
351-
raise ValueError("Unsupported cluster analysis technique " + self.cluster_analysis)
335+
raise ValueError("Unsupported cluster analysis technique " + analysis_type.value)
352336

353337
# Add to the report current parameters used to run the defence and the analysis summary
354338
report = dict(list(report.items()) + list(self.get_params().items()))

0 commit comments

Comments
 (0)