Skip to content

Commit ee3a443

Browse files
author
Beat Buesser
committed
Update DPatchRobust
Signed-off-by: Beat Buesser <[email protected]>
1 parent 72daf46 commit ee3a443

File tree

1 file changed

+21
-9
lines changed

1 file changed

+21
-9
lines changed

art/attacks/evasion/dpatch_robust.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ class RobustDPatch(EvasionAttack):
5959
"learning_rate",
6060
"max_iter",
6161
"batch_size",
62-
"verbose",
6362
"patch_location",
6463
"crop_range",
6564
"brightness_range",
6665
"rotation_weights",
6766
"sample_size",
67+
"targeted",
68+
"verbose",
6869
]
6970

7071
_estimator_requirements = (BaseEstimator, LossGradientsMixin, ObjectDetectorMixin)
@@ -81,6 +82,7 @@ def __init__(
8182
learning_rate: float = 5.0,
8283
max_iter: int = 500,
8384
batch_size: int = 16,
85+
targeted: bool = False,
8486
verbose: bool = True,
8587
):
8688
"""
@@ -96,6 +98,7 @@ def __init__(
9698
:param learning_rate: The learning rate of the optimization.
9799
:param max_iter: The number of optimization steps.
98100
:param batch_size: The size of the training batch.
101+
:param targeted: Indicates whether the attack is targeted (True) or untargeted (False).
99102
:param verbose: Show progress bars.
100103
"""
101104

@@ -120,9 +123,10 @@ def __init__(
120123
self.brightness_range = brightness_range
121124
self.rotation_weights = rotation_weights
122125
self.sample_size = sample_size
126+
self._targeted = targeted
123127
self._check_params()
124128

125-
def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> np.ndarray:
129+
def generate(self, x: np.ndarray, y: Optional[List[Dict[str, np.ndarray]]] = None, **kwargs) -> np.ndarray:
126130
"""
127131
Generate RobustDPatch.
128132
@@ -133,7 +137,9 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
133137
channel_index = 1 if self.estimator.channels_first else x.ndim - 1
134138
if x.shape[channel_index] != self.patch_shape[channel_index - 1]:
135139
raise ValueError("The color channel index of the images and the patch have to be identical.")
136-
if y is not None:
140+
if y is None and self.targeted:
141+
raise ValueError("The targeted version of RobustDPatch attack requires target labels provided to `y`.")
142+
if y is not None and not self.targeted:
137143
raise ValueError("The RobustDPatch attack does not use target labels.")
138144
if x.ndim != 4:
139145
raise ValueError("The adversarial patch can only be applied to images.")
@@ -183,9 +189,14 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
183189
i_batch_start = i_batch * self.batch_size
184190
i_batch_end = min((i_batch + 1) * self.batch_size, x.shape[0])
185191

192+
if y is None:
193+
y_batch = y
194+
else:
195+
y_batch = y[i_batch_start:i_batch_end]
196+
186197
# Sample and apply the random transformations:
187198
patched_images, patch_target, transforms = self._augment_images_with_patch(
188-
x[i_batch_start:i_batch_end], self._patch, channels_first=self.estimator.channels_first
199+
x[i_batch_start:i_batch_end], y_batch, self._patch, channels_first=self.estimator.channels_first
189200
)
190201

191202
gradients = self.estimator.loss_gradient(
@@ -206,7 +217,7 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
206217

207218
patch_gradients_old = patch_gradients
208219

209-
self._patch = self._patch + np.sign(patch_gradients) * self.learning_rate
220+
self._patch = self._patch + np.sign(patch_gradients) * (1 - 2 * int(self.targeted)) * self.learning_rate
210221

211222
if self.estimator.clip_values is not None:
212223
self._patch = np.clip(
@@ -218,12 +229,13 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> n
218229
return self._patch
219230

220231
def _augment_images_with_patch(
221-
self, x: np.ndarray, patch: np.ndarray, channels_first: bool
232+
self, x: np.ndarray, y: Optional[List[Dict[str, np.ndarray]]], patch: np.ndarray, channels_first: bool
222233
) -> Tuple[np.ndarray, List[Dict[str, np.ndarray]], Dict[str, Union[int, float]]]:
223234
"""
224235
Augment images with patch.
225236
226237
:param x: Sample images.
238+
:param y: Target labels.
227239
:param patch: The patch to be applied.
228240
:param channels_first: Set channels first or last.
229241
"""
@@ -269,9 +281,6 @@ def _augment_images_with_patch(
269281
y_b = y[i_image]["boxes"].copy()
270282
image_width = x.shape[2]
271283
image_height = x.shape[1]
272-
# TODO: fix. This assumes y_b is in Pytorch box format [xmin,ymin,xmax,ymax],
273-
# where 0 <= x < W and 0 <= y < H. Tensorflow box format [ymin,xmin,ymax,xmax],
274-
# where 0 <= x < 1 and 0 <= y < 1, is not supported
275284
x_1_arr = y_b[:, 0]
276285
y_1_arr = y_b[:, 1]
277286
x_2_arr = y_b[:, 2]
@@ -486,3 +495,6 @@ def _check_params(self) -> None:
486495
raise ValueError("The EOT sample size must be of type int.")
487496
if self.sample_size <= 0:
488497
raise ValueError("The EOT sample size must be greater than 0.")
498+
499+
if not isinstance(self.targeted, bool):
500+
raise ValueError("The argument `targeted` has to be of type bool.")

0 commit comments

Comments
 (0)