Skip to content

Commit

Permalink
Merge pull request #1156 from Trusted-AI/development_feature_adversaries
Browse files Browse the repository at this point in the history
Add support for multiple layers in Feature Adversaries
  • Loading branch information
beat-buesser committed Jun 15, 2021
2 parents 215a68f + 76b62c2 commit fd5a5da
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 38 deletions.
Expand Up @@ -47,9 +47,9 @@ class FeatureAdversariesPyTorch(EvasionAttack):
"""

attack_params = EvasionAttack.attack_params + [
"delta",
"optimizer",
"optimizer_kwargs",
"delta",
"lambda_",
"layer",
"max_iter",
Expand All @@ -64,11 +64,11 @@ class FeatureAdversariesPyTorch(EvasionAttack):
def __init__(
self,
estimator: "PYTORCH_ESTIMATOR_TYPE",
delta: float,
optimizer: Optional["Optimizer"] = None,
optimizer_kwargs: Optional[dict] = None,
delta: float = 15 / 255,
lambda_: float = 0.0,
layer: Optional[Union[int, str]] = -1,
layer: Union[int, str] = -1,
max_iter: int = 100,
batch_size: int = 32,
step_size: Optional[Union[int, float]] = None,
Expand All @@ -79,12 +79,12 @@ def __init__(
Create a :class:`.FeatureAdversariesPyTorch` instance.
:param estimator: A trained estimator.
:param delta: The maximum deviation between source and guide images.
:param optimizer: Optimizer applied to problem constrained only by clip values if defined, if None the
Projected Gradient Descent (PGD) optimizer is used.
:param optimizer_kwargs: Additional optimizer arguments.
:param delta: The maximum deviation between source and guide images.
:param lambda_: Regularization parameter of the L-inf soft constraint.
:param layer: Index of the representation layer.
:param layer: Index or tuple of indices of the representation layer(s).
:param max_iter: The maximum number of iterations.
:param batch_size: Batch size.
:param step_size: Step size for PGD optimizer.
Expand All @@ -97,7 +97,7 @@ def __init__(
self._optimizer_kwargs = {} if optimizer_kwargs is None else optimizer_kwargs
self.delta = delta
self.lambda_ = lambda_
self.layer = layer
self.layer = layer if isinstance(layer, tuple) else (layer,)
self.batch_size = batch_size
self.max_iter = max_iter
self.step_size = step_size
Expand All @@ -116,14 +116,16 @@ def _generate_batch(self, x: "torch.Tensor", y: "torch.Tensor") -> "torch.Tensor
import torch # lgtm [py/repeated-import]

def loss_fn(source_orig, source_adv, guide):
adv_representation = self.estimator.get_activations(source_adv, self.layer, self.batch_size, True)
guide_representation = self.estimator.get_activations(guide, self.layer, self.batch_size, True)
representation_loss = torch.zeros(size=(source_orig.shape[0],)).to(self.estimator.device)
for layer_i in self.layer:
adv_representation = self.estimator.get_activations(source_adv, layer_i, self.batch_size, True)
guide_representation = self.estimator.get_activations(guide, layer_i, self.batch_size, True)

dim = tuple(range(1, len(source_adv.shape)))
soft_constraint = torch.amax(torch.abs(source_adv - source_orig), dim=dim)
dim = tuple(range(1, len(source_adv.shape)))
soft_constraint = torch.amax(torch.abs(source_adv - source_orig), dim=dim)

dim = tuple(range(1, len(adv_representation.shape)))
representation_loss = torch.sum(torch.square(adv_representation - guide_representation), dim=dim)
dim = tuple(range(1, len(adv_representation.shape)))
representation_loss += torch.sum(torch.square(adv_representation - guide_representation), dim=dim)

loss = torch.mean(representation_loss + self.lambda_ * soft_constraint)
return loss
Expand Down Expand Up @@ -221,7 +223,7 @@ def _check_params(self) -> None:
if self.lambda_ < 0.0:
raise ValueError("The regularization parameter `lambda_` has to be non-negative.")

if not isinstance(self.layer, int) and not isinstance(self.layer, str):
if not isinstance(self.layer, int) and not isinstance(self.layer, str) and not isinstance(self.layer, tuple):
raise ValueError("The value of the representation layer must be integer or string.")

if not isinstance(self.max_iter, int):
Expand Down
Expand Up @@ -21,7 +21,7 @@
| Paper link: https://arxiv.org/abs/1511.05122
"""
import logging
from typing import TYPE_CHECKING, Optional, Union
from typing import TYPE_CHECKING, Optional, Tuple, Union

import numpy as np
from tqdm.auto import trange
Expand All @@ -47,9 +47,9 @@ class FeatureAdversariesTensorFlowV2(EvasionAttack):
"""

attack_params = EvasionAttack.attack_params + [
"delta",
"optimizer",
"optimizer_kwargs",
"delta",
"lambda_",
"layer",
"max_iter",
Expand All @@ -64,11 +64,11 @@ class FeatureAdversariesTensorFlowV2(EvasionAttack):
def __init__(
self,
estimator: "TENSORFLOWV2_ESTIMATOR_TYPE",
delta: float,
optimizer: Optional["Optimizer"] = None,
optimizer_kwargs: Optional[dict] = None,
delta: float = 15 / 255,
lambda_: float = 0.0,
layer: Optional[Union[int, str]] = -1,
layer: Union[int, str, Tuple[int, ...], Tuple[str, ...]] = -1,
max_iter: int = 100,
batch_size: int = 32,
step_size: Optional[Union[int, float]] = None,
Expand All @@ -79,12 +79,12 @@ def __init__(
Create a :class:`.FeatureAdversariesTensorFlowV2` instance.
:param estimator: A trained estimator.
:param delta: The maximum deviation between source and guide images.
:param optimizer: Optimizer applied to problem constrained only by clip values if defined, if None the
Projected Gradient Descent (PGD) optimizer is used.
:param optimizer_kwargs: Additional optimizer arguments.
:param delta: The maximum deviation between source and guide images.
:param lambda_: Regularization parameter of the L-inf soft constraint.
:param layer: Index of the representation layer.
:param layer: Index or tuple of indices of the representation layer(s).
:param max_iter: The maximum number of iterations.
:param batch_size: Batch size.
:param step_size: Step size for PGD optimizer.
Expand All @@ -93,11 +93,11 @@ def __init__(
"""
super().__init__(estimator=estimator)

self.delta = delta
self.optimizer = optimizer
self._optimizer_kwargs = {} if optimizer_kwargs is None else optimizer_kwargs
self.delta = delta
self.lambda_ = lambda_
self.layer = layer
self.layer = layer if isinstance(layer, tuple) else (layer,)
self.batch_size = batch_size
self.max_iter = max_iter
self.step_size = step_size
Expand All @@ -116,14 +116,16 @@ def _generate_batch(self, x: "tf.Tensor", y: "tf.Tensor") -> "tf.Tensor":
import tensorflow as tf # lgtm [py/repeated-import]

def loss_fn(source_orig, source_adv, guide):
adv_representation = self.estimator.get_activations(source_adv, self.layer, self.batch_size, True)
guide_representation = self.estimator.get_activations(guide, self.layer, self.batch_size, True)
representation_loss = tf.zeros(shape=(source_orig.shape[0],), dtype=tf.float32)
for layer_i in self.layer:
adv_representation = self.estimator.get_activations(source_adv, layer_i, self.batch_size, True)
guide_representation = self.estimator.get_activations(guide, layer_i, self.batch_size, True)

axis = tuple(range(1, len(source_adv.shape)))
soft_constraint = tf.math.reduce_max(tf.abs(source_adv - source_orig), axis=axis)
axis = tuple(range(1, len(source_adv.shape)))
soft_constraint = tf.cast(tf.math.reduce_max(tf.abs(source_adv - source_orig), axis=axis), tf.float32)

axis = tuple(range(1, len(adv_representation.shape)))
representation_loss = tf.reduce_sum(tf.square(adv_representation - guide_representation), axis=axis)
axis = tuple(range(1, len(adv_representation.shape)))
representation_loss += tf.reduce_sum(tf.square(adv_representation - guide_representation), axis=axis)

loss = tf.math.reduce_mean(representation_loss + self.lambda_ * soft_constraint)
return loss
Expand Down Expand Up @@ -218,7 +220,7 @@ def _check_params(self) -> None:
if self.lambda_ < 0.0:
raise ValueError("The regularization parameter `lambda_` has to be non-negative.")

if not isinstance(self.layer, int) and not isinstance(self.layer, str):
if not isinstance(self.layer, int) and not isinstance(self.layer, str) and not isinstance(self.layer, tuple):
raise ValueError("The value of the representation layer must be integer or string.")

if not isinstance(self.max_iter, int):
Expand Down
Expand Up @@ -42,13 +42,14 @@ def test_images_pgd(art_warning, fix_get_mnist_subset, image_dl_estimator_for_at
(x_train_mnist, y_train_mnist, x_test_mnist, y_test_mnist) = fix_get_mnist_subset

classifier = image_dl_estimator_for_attack(FeatureAdversariesPyTorch, functional=True)
print("classifier", classifier)

attack = FeatureAdversariesPyTorch(
classifier, delta=1.0, layer=1, batch_size=32, step_size=0.05, max_iter=1, random_start=False
classifier, delta=1.0, layer=1, batch_size=32, step_size=0.05, max_iter=2, random_start=False
)
x_train_mnist_adv = attack.generate(x=x_train_mnist[0:3], y=x_test_mnist[0:3])
assert np.mean(x_train_mnist[0:3]) == pytest.approx(0.13015705, 0.01)
assert np.mean(x_train_mnist_adv) != 0
assert np.mean(np.abs(x_train_mnist_adv - x_train_mnist[0:3])) != 0.0
except ARTTestException as e:
art_warning(e)

Expand All @@ -67,14 +68,16 @@ def test_images_unconstrained_adam(art_warning, fix_get_mnist_subset, image_dl_e
)
x_train_mnist_adv = attack.generate(x=x_train_mnist[0:3], y=x_test_mnist[0:3])
assert np.mean(x_train_mnist[0:3]) == pytest.approx(0.13015705, 0.01)
assert np.mean(x_train_mnist_adv) != 0
assert np.mean(np.abs(x_train_mnist_adv - x_train_mnist[0:3])) != 0.0
except ARTTestException as e:
art_warning(e)


@pytest.mark.skip_framework("tensorflow", "keras", "kerastf", "mxnet", "non_dl_frameworks")
@pytest.mark.framework_agnostic
def test_classifier_type_check_fail(art_warning):
try:
backend_test_classifier_type_check_fail(FeatureAdversariesPyTorch, [BaseEstimator, NeuralNetworkMixin])
backend_test_classifier_type_check_fail(
FeatureAdversariesPyTorch, [BaseEstimator, NeuralNetworkMixin], delta=1.0
)
except ARTTestException as e:
art_warning(e)
Expand Up @@ -44,11 +44,11 @@ def test_images_pgd(art_warning, fix_get_mnist_subset, image_dl_estimator_for_at
classifier = image_dl_estimator_for_attack(FeatureAdversariesTensorFlowV2)

attack = FeatureAdversariesTensorFlowV2(
classifier, delta=1.0, layer=1, batch_size=32, step_size=0.05, max_iter=1, random_start=False
classifier, delta=1.0, layer=1, batch_size=32, step_size=0.05, max_iter=2, random_start=False
)
x_train_mnist_adv = attack.generate(x=x_train_mnist[0:3], y=x_test_mnist[0:3])
assert np.mean(x_train_mnist[0:3]) == pytest.approx(0.13015705, 0.01)
assert np.mean(x_train_mnist_adv) != 0
assert np.mean(np.abs(x_train_mnist_adv - x_train_mnist[0:3])) != 0.0
except ARTTestException as e:
art_warning(e)

Expand All @@ -67,14 +67,16 @@ def test_images_unconstrained_adam(art_warning, fix_get_mnist_subset, image_dl_e
)
x_train_mnist_adv = attack.generate(x=x_train_mnist[0:3], y=x_test_mnist[0:3])
assert np.mean(x_train_mnist[0:3]) == pytest.approx(0.13015705, 0.01)
assert np.mean(x_train_mnist_adv) != 0
assert np.mean(np.abs(x_train_mnist_adv - x_train_mnist[0:3])) != 0.0
except ARTTestException as e:
art_warning(e)


@pytest.mark.skip_framework("tensorflow1", "keras", "kerastf", "mxnet", "non_dl_frameworks", "pytorch")
@pytest.mark.framework_agnostic
def test_classifier_type_check_fail(art_warning):
try:
backend_test_classifier_type_check_fail(FeatureAdversariesTensorFlowV2, [BaseEstimator, NeuralNetworkMixin])
backend_test_classifier_type_check_fail(
FeatureAdversariesTensorFlowV2, [BaseEstimator, NeuralNetworkMixin], delta=1.0
)
except ARTTestException as e:
art_warning(e)

0 comments on commit fd5a5da

Please sign in to comment.