Skip to content

Commit 052c6f2

Browse files
author
Beat Buesser
committed
Update DPatch
Signed-off-by: Beat Buesser <[email protected]>
1 parent 99f053e commit 052c6f2

File tree

2 files changed

+50
-31
lines changed

2 files changed

+50
-31
lines changed

art/attacks/evasion/dpatch.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def __init__(
7878
self.learning_rate = learning_rate
7979
self.max_iter = max_iter
8080
self.batch_size = batch_size
81-
self._patch = np.ones(shape=patch_shape) * (self.estimator.clip_values[1] + self.estimator.clip_values[0]) / 2.0
81+
self._patch = np.random.randint(self.estimator.clip_values[0], self.estimator.clip_values[1], size=patch_shape).astype(np.float32)
8282
self._check_params()
8383

8484
self.target_label = []
@@ -87,7 +87,7 @@ def generate(
8787
self,
8888
x: np.ndarray,
8989
y: Optional[np.ndarray] = None,
90-
target_label: Union[int, List[int], np.ndarray] = 0,
90+
target_label: Optional[Union[int, List[int], np.ndarray]] = None,
9191
**kwargs
9292
) -> np.ndarray:
9393
"""
@@ -105,16 +105,17 @@ def generate(
105105
raise ValueError("The DPatch attack does not use target labels.")
106106
if x.ndim != 4:
107107
raise ValueError("The adversarial patch can only be applied to images.")
108-
if isinstance(target_label, int):
109-
self.target_label = [target_label] * x.shape[0]
110-
elif isinstance(target_label, np.ndarray):
111-
if not (target_label.shape == (x.shape[0], 1) or target_label.shape == (x.shape[0],)):
112-
raise ValueError("The target_label has to be a 1-dimensional array.")
113-
self.target_label = target_label.tolist()
114-
else:
115-
if not len(target_label) == x.shape[0] or not isinstance(target_label, list):
116-
raise ValueError("The target_label as list of integers needs to of length number of images in `x`.")
117-
self.target_label = target_label
108+
if target_label is not None:
109+
if isinstance(target_label, int):
110+
self.target_label = [target_label] * x.shape[0]
111+
elif isinstance(target_label, np.ndarray):
112+
if not (target_label.shape == (x.shape[0], 1) or target_label.shape == (x.shape[0],)):
113+
raise ValueError("The target_label has to be a 1-dimensional array.")
114+
self.target_label = target_label.tolist()
115+
else:
116+
if not len(target_label) == x.shape[0] or not isinstance(target_label, list):
117+
raise ValueError("The target_label as list of integers needs to of length number of images in `x`.")
118+
self.target_label = target_label
118119

119120
for i_step in trange(self.max_iter, desc="DPatch iteration"):
120121
if i_step == 0 or (i_step + 1) % 100 == 0:
@@ -125,19 +126,32 @@ def generate(
125126
)
126127
patch_target: List[Dict[str, np.ndarray]] = list()
127128

128-
for i_image in range(patched_images.shape[0]):
129+
if self.target_label:
130+
131+
for i_image in range(patched_images.shape[0]):
132+
i_x_1 = transforms[i_image]["i_x_1"]
133+
i_x_2 = transforms[i_image]["i_x_2"]
134+
i_y_1 = transforms[i_image]["i_y_1"]
135+
i_y_2 = transforms[i_image]["i_y_2"]
136+
137+
target_dict = dict()
138+
target_dict["boxes"] = np.asarray([[i_x_1, i_y_1, i_x_2, i_y_2]])
139+
target_dict["labels"] = np.asarray([self.target_label[i_image], ])
140+
target_dict["scores"] = np.asarray([1.0, ])
129141

130-
i_x_1 = transforms[i_image]["i_x_1"]
131-
i_x_2 = transforms[i_image]["i_x_2"]
132-
i_y_1 = transforms[i_image]["i_y_1"]
133-
i_y_2 = transforms[i_image]["i_y_2"]
142+
patch_target.append(target_dict)
134143

135-
target_dict = dict()
136-
target_dict["boxes"] = np.asarray([[i_x_1, i_y_1, i_x_2, i_y_2]])
137-
target_dict["labels"] = np.asarray([self.target_label[i_image],])
138-
target_dict["scores"] = np.asarray([1.0,])
144+
else:
139145

140-
patch_target.append(target_dict)
146+
predictions = self.estimator.predict(x=patched_images)
147+
148+
for i_image in range(patched_images.shape[0]):
149+
target_dict = dict()
150+
target_dict["boxes"] = predictions[i_image]["boxes"].detach().cpu().numpy()
151+
target_dict["labels"] = predictions[i_image]["labels"].detach().cpu().numpy()
152+
target_dict["scores"] = predictions[i_image]["scores"].detach().cpu().numpy()
153+
154+
patch_target.append(target_dict)
141155

142156
num_batches = math.ceil(x.shape[0] / self.batch_size)
143157
patch_gradients = np.zeros_like(self._patch)
@@ -162,9 +176,13 @@ def generate(
162176
else:
163177
patch_gradients_i = gradients[i_image, i_x_1:i_x_2, i_y_1:i_y_2, :]
164178

165-
patch_gradients += patch_gradients_i
179+
patch_gradients = patch_gradients + patch_gradients_i
180+
181+
if self.target_label:
182+
self._patch = self._patch - np.sign(patch_gradients) * self.learning_rate
183+
else:
184+
self._patch = self._patch + np.sign(patch_gradients) * self.learning_rate
166185

167-
self._patch -= patch_gradients * self.learning_rate
168186
self._patch = np.clip(
169187
self._patch, a_min=self.estimator.clip_values[0], a_max=self.estimator.clip_values[1],
170188
)

art/estimators/object_detection/pytorch_faster_rcnn.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
This module implements the task specific estimator for Faster R-CNN v3 in PyTorch.
2020
"""
2121
import logging
22-
from typing import List, Optional, Tuple, Union, TYPE_CHECKING
22+
from typing import List, Dict, Optional, Tuple, Union, TYPE_CHECKING
2323

2424
import numpy as np
2525

@@ -29,6 +29,7 @@
2929

3030
if TYPE_CHECKING:
3131
# pylint: disable=C0412
32+
import torch
3233
import torchvision
3334

3435
from art.utils import CLIP_VALUES_TYPE, PREPROCESSING_TYPE
@@ -134,7 +135,7 @@ def __init__(
134135
self._model.eval()
135136
self.attack_losses: Tuple[str, ...] = attack_losses
136137

137-
def loss_gradient(self, x: np.ndarray, y: np.ndarray, **kwargs) -> np.ndarray:
138+
def loss_gradient(self, x: np.ndarray, y: List[Dict[str, np.ndarray]], **kwargs) -> np.ndarray:
138139
"""
139140
Compute the gradient of the loss function w.r.t. `x`.
140141
@@ -158,9 +159,9 @@ def loss_gradient(self, x: np.ndarray, y: np.ndarray, **kwargs) -> np.ndarray:
158159

159160
if y is not None:
160161
for i, y_i in enumerate(y):
161-
y[i]["boxes"] = torch.tensor(y_i["boxes"], dtype=torch.float).to(self._device)
162-
y[i]["labels"] = torch.tensor(y_i["labels"], dtype=torch.int64).to(self._device)
163-
y[i]["scores"] = torch.tensor(y_i["scores"]).to(self._device)
162+
y[i]["boxes"] = torch.from_numpy(y_i["boxes"]).type(torch.float).to(self._device)
163+
y[i]["labels"] = torch.from_numpy(y_i["labels"]).type(torch.int64).to(self._device)
164+
y[i]["scores"] = torch.from_numpy(y_i["scores"]).to(self._device)
164165

165166
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
166167
image_tensor_list = list()
@@ -207,13 +208,13 @@ def loss_gradient(self, x: np.ndarray, y: np.ndarray, **kwargs) -> np.ndarray:
207208

208209
return grads
209210

210-
def predict(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> np.ndarray:
211+
def predict(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> List[Dict[str, "torch.Tensor"]]:
211212
"""
212213
Perform prediction for a batch of inputs.
213214
214215
:param x: Samples of shape (nb_samples, height, width, nb_channels).
215216
:param batch_size: Batch size.
216-
:return: Predictions of format `List[Dict[Tensor]]`, one for each input image. The
217+
:return: Predictions of format `List[Dict[str, Tensor]]`, one for each input image. The
217218
fields of the Dict are as follows:
218219
219220
- boxes (FloatTensor[N, 4]): the predicted boxes in [x1, y1, x2, y2] format, with values \

0 commit comments

Comments
 (0)