Skip to content

Commit b7c58f7

Browse files
authored
Merge branch 'dev_1.6.0' into development_issue_824
2 parents edb579c + 9fef7b5 commit b7c58f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+470
-412
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,18 @@ jobs:
3131
tensorflow: 1.15.5
3232
tf_version: v1
3333
keras: 2.2.5
34-
- name: TensorFlow 2.2.2 (Keras 2.3.1 Python 3.7)
35-
framework: tensorflow
36-
python: 3.7
37-
tensorflow: 2.2.2
38-
tf_version: v2
39-
keras: 2.3.1
40-
- name: TensorFlow 2.2.2v1 (Keras 2.3.1 Python 3.7)
41-
framework: tensorflow2v1
42-
python: 3.7
43-
tensorflow: 2.2.2
44-
tf_version: v2
45-
keras: 2.3.1
4634
- name: TensorFlow 2.3.2 (Keras 2.4.3 Python 3.7)
4735
framework: tensorflow
4836
python: 3.7
4937
tensorflow: 2.3.2
5038
tf_version: v2
5139
keras: 2.4.3
40+
- name: TensorFlow 2.4.1v1 (Keras 2.4.3 Python 3.8)
41+
framework: tensorflow2v1
42+
python: 3.8
43+
tensorflow: 2.4.1
44+
tf_version: v2
45+
keras: 2.4.3
5246
- name: TensorFlow 2.4.1 (Keras 2.4.3 Python 3.8)
5347
framework: tensorflow
5448
python: 3.8
@@ -137,6 +131,8 @@ jobs:
137131
run: |
138132
pip install tensorflow==${{ matrix.tensorflow }}
139133
pip install lingvo==${{ matrix.lingvo }}
134+
pip install tensorflow-addons==0.9.1
135+
pip install model-pruning-google-research==0.0.3
140136
pip list
141137
- name: Run ${{ matrix.name }} Tests
142138
run: ./run_tests.sh ${{ matrix.framework }}

art/attacks/evasion/adversarial_patch/adversarial_patch_tensorflow.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from art.attacks.attack import EvasionAttack
3434
from art.estimators.estimator import BaseEstimator, NeuralNetworkMixin
3535
from art.estimators.classification.classifier import ClassifierMixin
36-
from art.utils import check_and_transform_label_format
36+
from art.utils import check_and_transform_label_format, is_probability
3737

3838
if TYPE_CHECKING:
3939
import tensorflow as tf
@@ -111,6 +111,8 @@ def __init__(
111111
if self.estimator.channels_first:
112112
raise ValueError("Color channel needs to be in last dimension.")
113113

114+
self.use_logits = None
115+
114116
self.i_h_patch = 0
115117
self.i_w_patch = 1
116118

@@ -142,8 +144,8 @@ def __init__(
142144
constraint=lambda x: tf.clip_by_value(x, self.estimator.clip_values[0], self.estimator.clip_values[1]),
143145
)
144146

145-
self._train_op = tf.keras.optimizers.SGD(
146-
learning_rate=self.learning_rate, momentum=0.0, nesterov=False, name="SGD"
147+
self._train_op = tf.keras.optimizers.Adam(
148+
learning_rate=self.learning_rate, beta_1=0.9, beta_2=0.999, epsilon=1e-07, amsgrad=False, name="Adam"
147149
)
148150

149151
def _train_step(
@@ -170,7 +172,7 @@ def _train_step(
170172

171173
return loss
172174

173-
def _probabilities(self, images: "tf.Tensor", mask: Optional["tf.Tensor"]) -> "tf.Tensor":
175+
def _predictions(self, images: "tf.Tensor", mask: Optional["tf.Tensor"]) -> "tf.Tensor":
174176
import tensorflow as tf # lgtm [py/repeated-import]
175177

176178
patched_input = self._random_overlay(images, self._patch, mask=mask)
@@ -179,17 +181,17 @@ def _probabilities(self, images: "tf.Tensor", mask: Optional["tf.Tensor"]) -> "t
179181
patched_input, clip_value_min=self.estimator.clip_values[0], clip_value_max=self.estimator.clip_values[1],
180182
)
181183

182-
probabilities = self.estimator._predict_framework(patched_input)
184+
predictions = self.estimator._predict_framework(patched_input)
183185

184-
return probabilities
186+
return predictions
185187

186188
def _loss(self, images: "tf.Tensor", target: "tf.Tensor", mask: Optional["tf.Tensor"]) -> "tf.Tensor":
187189
import tensorflow as tf # lgtm [py/repeated-import]
188190

189-
probabilities = self._probabilities(images, mask)
191+
predictions = self._predictions(images, mask)
190192

191193
self._loss_per_example = tf.keras.losses.categorical_crossentropy(
192-
y_true=target, y_pred=probabilities, from_logits=False, label_smoothing=0
194+
y_true=target, y_pred=predictions, from_logits=self.use_logits, label_smoothing=0
193195
)
194196

195197
loss = tf.reduce_mean(self._loss_per_example)
@@ -381,27 +383,21 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> T
381383
mask = kwargs.get("mask")
382384
if mask is not None:
383385
mask = mask.copy()
384-
if mask is not None and (
385-
(mask.dtype != np.bool)
386-
or not (mask.shape[0] == 1 or mask.shape[0] == x.shape[0])
387-
or not (
388-
(mask.shape[1] == x.shape[1] and mask.shape[2] == x.shape[2])
389-
or (mask.shape[1] == x.shape[2] and mask.shape[2] == x.shape[3])
390-
)
391-
):
392-
raise ValueError(
393-
"The shape of `mask` has to be equal to the shape of a single samples (1, H, W) or the"
394-
"shape of `x` (N, H, W) without their channel dimensions."
395-
)
396-
397-
if mask is not None and mask.shape[0] == 1:
398-
mask = np.repeat(mask, repeats=x.shape[0], axis=0)
386+
mask = self._check_mask(mask=mask, x=x)
399387

400388
if kwargs.get("reset_patch"):
401389
self.reset_patch(initial_patch_value=self._initial_value)
402390

403391
y = check_and_transform_label_format(labels=y, nb_classes=self.estimator.nb_classes)
404392

393+
# check if logits or probabilities
394+
y_pred = self.estimator.predict(x=x[[0]])
395+
396+
if is_probability(y_pred):
397+
self.use_logits = False
398+
else:
399+
self.use_logits = True
400+
405401
if mask is None:
406402
if shuffle:
407403
dataset = (
@@ -444,6 +440,22 @@ def generate(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> T
444440
self._get_circular_patch_mask(nb_samples=1).numpy()[0],
445441
)
446442

443+
def _check_mask(self, mask: np.ndarray, x: np.ndarray) -> np.ndarray:
444+
if mask is not None and (
445+
(mask.dtype != np.bool)
446+
or not (mask.shape[0] == 1 or mask.shape[0] == x.shape[0])
447+
or not (mask.shape[1] == x.shape[self.i_h + 1] and mask.shape[2] == x.shape[self.i_w + 1])
448+
):
449+
raise ValueError(
450+
"The shape of `mask` has to be equal to the shape of a single samples (1, H, W) or the"
451+
"shape of `x` (N, H, W) without their channel dimensions."
452+
)
453+
454+
if mask is not None and mask.shape[0] == 1:
455+
mask = np.repeat(mask, repeats=x.shape[0], axis=0)
456+
457+
return mask
458+
447459
def apply_patch(
448460
self,
449461
x: np.ndarray,
@@ -464,6 +476,7 @@ def apply_patch(
464476
"""
465477
if mask is not None:
466478
mask = mask.copy()
479+
mask = self._check_mask(mask=mask, x=x)
467480
patch = patch_external if patch_external is not None else self._patch
468481
return self._random_overlay(images=x, patch=patch, scale=scale, mask=mask).numpy()
469482

art/attacks/evasion/dpatch.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from art.attacks.attack import EvasionAttack
3232
from art.estimators.estimator import BaseEstimator, LossGradientsMixin
3333
from art.estimators.object_detection.object_detector import ObjectDetectorMixin
34-
from art.utils import Deprecated, deprecated_keyword_arg
3534
from art import config
3635

3736
if TYPE_CHECKING:
@@ -225,13 +224,11 @@ def generate(
225224
return self._patch
226225

227226
@staticmethod
228-
@deprecated_keyword_arg("channel_index", end_version="1.6.0", replaced_by="channels_first")
229227
def _augment_images_with_patch(
230228
x: np.ndarray,
231229
patch: np.ndarray,
232230
random_location: bool,
233231
channels_first: bool,
234-
channel_index=Deprecated,
235232
mask: Optional[np.ndarray] = None,
236233
) -> Tuple[np.ndarray, List[Dict[str, int]]]:
237234
"""
@@ -242,21 +239,11 @@ def _augment_images_with_patch(
242239
:param random_location: If True apply patch at randomly shifted locations, otherwise place patch at origin
243240
(top-left corner).
244241
:param channels_first: Set channels first or last.
245-
:param channel_index: Index of the color channel.
246-
:type channel_index: `int`
247242
:param mask: An boolean array of shape equal to the shape of a single samples (1, H, W) or the shape of `x`
248243
(N, H, W) without their channel dimensions. Any features for which the mask is True can be the
249244
center location of the patch during sampling.
250245
:type mask: `np.ndarray`
251246
"""
252-
# Remove in 1.6.0
253-
if channel_index == 3:
254-
channels_first = False
255-
elif channel_index == 1:
256-
channels_first = True
257-
elif channel_index is not Deprecated:
258-
raise ValueError("Not a proper channel_index. Use channels_first.")
259-
260247
transformations = list()
261248
x_copy = x.copy()
262249
patch_copy = patch.copy()

art/attacks/evasion/simba.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from scipy.fftpack import idct
3030

3131
from art.attacks.attack import EvasionAttack
32-
from art.estimators.estimator import BaseEstimator
32+
from art.estimators.estimator import BaseEstimator, NeuralNetworkMixin
3333
from art.estimators.classification.classifier import ClassifierMixin
3434
from art.config import ART_NUMPY_DTYPE
3535

@@ -51,7 +51,7 @@ class SimBA(EvasionAttack):
5151
"batch_size",
5252
]
5353

54-
_estimator_requirements = (BaseEstimator, ClassifierMixin)
54+
_estimator_requirements = (BaseEstimator, ClassifierMixin, NeuralNetworkMixin)
5555

5656
def __init__(
5757
self,

art/attacks/evasion/square_attack.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
from art.config import ART_NUMPY_DTYPE
3333
from art.attacks.attack import EvasionAttack
34-
from art.estimators.estimator import BaseEstimator
34+
from art.estimators.estimator import BaseEstimator, NeuralNetworkMixin
3535
from art.estimators.classification.classifier import ClassifierMixin
3636
from art.utils import check_and_transform_label_format, get_labels_np_array
3737

@@ -53,7 +53,7 @@ class SquareAttack(EvasionAttack):
5353
"verbose",
5454
]
5555

56-
_estimator_requirements = (BaseEstimator, ClassifierMixin)
56+
_estimator_requirements = (BaseEstimator, ClassifierMixin, NeuralNetworkMixin)
5757

5858
def __init__(
5959
self,
@@ -259,7 +259,7 @@ def _get_perturbation(h):
259259

260260
return delta
261261

262-
delta_init = np.zeros(x_robust.shape)
262+
delta_init = np.zeros(x_robust.shape, dtype=ART_NUMPY_DTYPE)
263263

264264
height_start = 0
265265
for _ in range(n_tiles):

art/attacks/inference/membership_inference/black_box.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,7 @@ def fit(self, x: np.ndarray, y: np.ndarray, test_x: np.ndarray, test_y: np.ndarr
198198
import torch.nn as nn # lgtm [py/repeated-import]
199199
import torch.optim as optim # lgtm [py/repeated-import]
200200
from torch.utils.data import DataLoader # lgtm [py/repeated-import]
201-
202-
use_cuda = torch.cuda.is_available()
203-
204-
def to_cuda(x):
205-
if use_cuda:
206-
x = x.cuda()
207-
return x
201+
from art.utils import to_cuda
208202

209203
loss_fn = nn.BCELoss()
210204
optimizer = optim.Adam(self.attack_model.parameters(), lr=self.learning_rate)
@@ -261,14 +255,18 @@ def infer(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> np.n
261255
if self.default_model and self.attack_model_type == "nn":
262256
import torch # lgtm [py/repeated-import]
263257
from torch.utils.data import DataLoader # lgtm [py/repeated-import]
258+
from art.utils import to_cuda, from_cuda
264259

265260
self.attack_model.eval()
266261
inferred = None
267262
test_set = self._get_attack_dataset(f_1=features, f_2=y)
268263
test_loader = DataLoader(test_set, batch_size=self.batch_size, shuffle=True, num_workers=0)
269264
for input1, input2, _ in test_loader:
265+
input1, input2 = to_cuda(input1), to_cuda(input2)
270266
outputs = self.attack_model(input1, input2)
271267
predicted = torch.round(outputs)
268+
predicted = from_cuda(predicted)
269+
272270
if inferred is None:
273271
inferred = predicted.detach().numpy()
274272
else:

art/defences/detector/evasion/detector.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
from art.estimators.estimator import BaseEstimator, NeuralNetworkMixin, LossGradientsMixin
3030
from art.estimators.classification.classifier import ClassifierMixin, ClassGradientsMixin
31-
from art.utils import deprecated
3231

3332
if TYPE_CHECKING:
3433
from art.utils import CLIP_VALUES_TYPE
@@ -58,7 +57,6 @@ def __init__(self, detector: "ClassifierNeuralNetwork") -> None:
5857
super().__init__(
5958
model=None,
6059
clip_values=detector.clip_values,
61-
channel_index=detector.channel_index,
6260
channels_first=detector.channels_first,
6361
preprocessing_defences=detector.preprocessing_defences,
6462
preprocessing=detector.preprocessing,
@@ -122,11 +120,6 @@ def input_shape(self) -> Tuple[int, ...]:
122120
def clip_values(self) -> Optional["CLIP_VALUES_TYPE"]:
123121
return self.detector.clip_values
124122

125-
@property # type: ignore
126-
@deprecated(end_version="1.6.0", replaced_by="channels_first")
127-
def channel_index(self) -> Optional[int]:
128-
return self.detector.channel_index
129-
130123
@property
131124
def channels_first(self) -> Optional[bool]:
132125
"""
@@ -189,7 +182,6 @@ def __init__(
189182
super().__init__(
190183
model=None,
191184
clip_values=detector.clip_values,
192-
channel_index=detector.channel_index,
193185
channels_first=detector.channels_first,
194186
preprocessing_defences=detector.preprocessing_defences,
195187
preprocessing=detector.preprocessing,
@@ -270,11 +262,6 @@ def input_shape(self) -> Tuple[int, ...]:
270262
def clip_values(self) -> Optional["CLIP_VALUES_TYPE"]:
271263
return self.detector.clip_values
272264

273-
@property # type: ignore
274-
@deprecated(end_version="1.6.0", replaced_by="channels_first")
275-
def channel_index(self) -> Optional[int]:
276-
return self.detector.channel_index
277-
278265
@property
279266
def channels_first(self) -> Optional[bool]:
280267
"""

art/defences/detector/evasion/subsetscanning/detector.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
from art.defences.detector.evasion.subsetscanning.scanner import Scanner
3333
from art.estimators.classification.classifier import ClassifierNeuralNetwork
34-
from art.utils import deprecated
3534

3635

3736
if TYPE_CHECKING:
@@ -68,7 +67,6 @@ def __init__(
6867
super().__init__(
6968
model=None,
7069
clip_values=classifier.clip_values,
71-
channel_index=classifier.channel_index,
7270
channels_first=classifier.channels_first,
7371
preprocessing_defences=classifier.preprocessing_defences,
7472
preprocessing=classifier.preprocessing,
@@ -245,11 +243,6 @@ def input_shape(self) -> Tuple[int, ...]:
245243
def clip_values(self) -> Optional["CLIP_VALUES_TYPE"]:
246244
return self.detector.clip_values
247245

248-
@property # type: ignore
249-
@deprecated(end_version="1.6.0", replaced_by="channels_first")
250-
def channel_index(self) -> Optional[int]:
251-
return self.detector.channel_index
252-
253246
@property
254247
def channels_first(self) -> bool:
255248
"""

0 commit comments

Comments
 (0)