Skip to content

Commit 9ca30ed

Browse files
authored
Merge pull request #1845 from Trusted-AI/dev_1.11.1
Update to ART 1.11.1
2 parents 5c1b122 + 976ba93 commit 9ca30ed

File tree

10 files changed

+176
-127
lines changed

10 files changed

+176
-127
lines changed

art/attacks/evasion/adversarial_patch/adversarial_patch.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,6 @@ def generate( # type: ignore
164164
"""
165165
logger.info("Creating adversarial patch.")
166166

167-
if y is None: # pragma: no cover
168-
raise ValueError("Adversarial Patch attack requires target values `y`.")
169-
170167
if len(x.shape) == 2: # pragma: no cover
171168
raise ValueError(
172169
"Feature vectors detected. The adversarial patch can only be applied to data with spatial "

art/attacks/evasion/adversarial_patch/adversarial_patch_pytorch.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,13 +682,13 @@ def apply_patch(
682682
if mask is not None:
683683
mask = mask.copy()
684684
mask = self._check_mask(mask=mask, x=x)
685-
x_tensor = torch.Tensor(x)
685+
x_tensor = torch.Tensor(x).to(self.estimator.device)
686686
if mask is not None:
687-
mask_tensor = torch.Tensor(mask)
687+
mask_tensor = torch.Tensor(mask).to(self.estimator.device)
688688
else:
689689
mask_tensor = None
690690
if isinstance(patch_external, np.ndarray):
691-
patch_tensor = torch.Tensor(patch_external)
691+
patch_tensor = torch.Tensor(patch_external).to(self.estimator.device)
692692
else:
693693
patch_tensor = self._patch
694694
return (

art/attacks/evasion/elastic_net.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def _decay_learning_rate(self, global_step: int, end_learning_rate: float, decay
190190
:return: The decayed learning rate
191191
"""
192192
learn_rate = self.learning_rate - end_learning_rate
193-
decayed_learning_rate = learn_rate * (1 - global_step / decay_steps) ** 2 + end_learning_rate
193+
decayed_learning_rate = learn_rate * (1 - global_step / decay_steps) ** 0.5 + end_learning_rate
194194

195195
return decayed_learning_rate
196196

art/estimators/classification/pytorch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def predict( # pylint: disable=W0221
322322
output = model_outputs[-1]
323323
output = output.detach().cpu().numpy().astype(np.float32)
324324
if len(output.shape) == 1:
325-
output = np.expand_dims(output.detach().cpu().numpy(), axis=1).astype(np.float32)
325+
output = np.expand_dims(output, axis=1).astype(np.float32)
326326

327327
results_list.append(output)
328328

art/estimators/regression/pytorch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def predict( # pylint: disable=W0221
269269
output = model_outputs[-1]
270270
output = output.detach().cpu().numpy().astype(np.float32)
271271
if len(output.shape) == 1:
272-
output = np.expand_dims(output.detach().cpu().numpy(), axis=1).astype(np.float32)
272+
output = np.expand_dims(output, axis=1).astype(np.float32)
273273

274274
results_list.append(output)
275275

art/metrics/privacy/membership_leakage.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
This module implements membership leakage metrics.
2020
"""
2121
from __future__ import absolute_import, division, print_function, unicode_literals
22-
from typing import TYPE_CHECKING, Optional
22+
from typing import TYPE_CHECKING, Optional, Tuple
2323

2424
import numpy as np
2525
import scipy
2626

27-
from art.utils import check_and_transform_label_format, is_probability
27+
from art.utils import check_and_transform_label_format, is_probability_array
2828

2929
if TYPE_CHECKING:
3030
from art.estimators.classification.classifier import Classifier
@@ -37,7 +37,7 @@ def PDTP( # pylint: disable=C0103
3737
y: np.ndarray,
3838
indexes: Optional[np.ndarray] = None,
3939
num_iter: Optional[int] = 10,
40-
) -> np.ndarray:
40+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
4141
"""
4242
Compute the pointwise differential training privacy metric for the given classifier and training set.
4343
@@ -52,8 +52,8 @@ def PDTP( # pylint: disable=C0103
5252
computed for all samples in `x`.
5353
:param num_iter: the number of iterations of PDTP computation to run for each sample. If not supplied,
5454
defaults to 10. The result is the average across iterations.
55-
:return: an array containing the average PDTP value for each sample in the training set. The higher the value,
56-
the higher the privacy leakage for that sample.
55+
:return: A tuple of three arrays, containing the average (worse, standard deviation) PDTP value for each sample in
56+
the training set respectively. The higher the value, the higher the privacy leakage for that sample.
5757
"""
5858
from art.estimators.classification.pytorch import PyTorchClassifier
5959
from art.estimators.classification.tensorflow import TensorFlowV2Classifier
@@ -77,14 +77,15 @@ def PDTP( # pylint: disable=C0103
7777
iter_results = []
7878
# get probabilities from original model
7979
pred = target_estimator.predict(x)
80-
if not is_probability(pred):
80+
if not is_probability_array(pred):
8181
try:
8282
pred = scipy.special.softmax(pred, axis=1)
8383
except Exception as exc: # pragma: no cover
8484
raise ValueError("PDTP metric only supports classifiers that output logits or probabilities.") from exc
8585
# divide into 100 bins and return center of bin
8686
bins = np.array(np.arange(0.0, 1.01, 0.01).round(decimals=2))
8787
pred_bin_indexes = np.digitize(pred, bins)
88+
pred_bin_indexes[pred_bin_indexes == 101] = 100
8889
pred_bin = bins[pred_bin_indexes] - 0.005
8990

9091
if not indexes:
@@ -102,10 +103,11 @@ def PDTP( # pylint: disable=C0103
102103
extra_estimator.fit(alt_x, alt_y)
103104
# get probabilities from new model
104105
alt_pred = extra_estimator.predict(x)
105-
if not is_probability(alt_pred):
106+
if not is_probability_array(alt_pred):
106107
alt_pred = scipy.special.softmax(alt_pred, axis=1)
107108
# divide into 100 bins and return center of bin
108109
alt_pred_bin_indexes = np.digitize(alt_pred, bins)
110+
alt_pred_bin_indexes[alt_pred_bin_indexes == 101] = 100
109111
alt_pred_bin = bins[alt_pred_bin_indexes] - 0.005
110112
ratio_1 = pred_bin / alt_pred_bin
111113
ratio_2 = alt_pred_bin / pred_bin
@@ -118,6 +120,8 @@ def PDTP( # pylint: disable=C0103
118120
# We now have a list of list, internal lists represent an iteration. We need to transpose and get averages.
119121
per_sample = list(map(list, zip(*results)))
120122
avg_per_sample = np.array([sum(val) / len(val) for val in per_sample])
123+
worse_per_sample = np.max(per_sample, axis=1)
124+
std_dev_per_sample = np.std(per_sample, axis=1)
121125

122-
# return leakage per sample
123-
return avg_per_sample
126+
# return avg+worse leakage + standard deviation per sample
127+
return avg_per_sample, worse_per_sample, std_dev_per_sample

art/utils.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,8 @@ def random_sphere(
531531
norm: Union[int, float, str],
532532
) -> np.ndarray:
533533
"""
534-
Generate randomly `m x n`-dimension points with radius `radius` and centered around 0.
534+
Generate uniformly at random `m x n`-dimension points in the `norm`-norm ball with radius `radius` and centered
535+
around 0.
535536
536537
:param nb_points: Number of random data points.
537538
:param nb_dims: Dimensionality of the sphere.
@@ -545,13 +546,11 @@ def random_sphere(
545546
"The parameter `radius` of type `np.ndarray` is not supported to use with norm 1."
546547
)
547548

548-
a_tmp = np.zeros(shape=(nb_points, nb_dims + 1))
549-
a_tmp[:, -1] = np.sqrt(np.random.uniform(0, radius ** 2, nb_points))
550-
551-
for i in range(nb_points):
552-
a_tmp[i, 1:-1] = np.sort(np.random.uniform(0, a_tmp[i, -1], nb_dims - 1))
553-
554-
res = (a_tmp[:, 1:] - a_tmp[:, :-1]) * np.random.choice([-1, 1], (nb_points, nb_dims))
549+
var_u = np.random.uniform(size=(nb_points, nb_dims))
550+
var_v = np.sort(var_u)
551+
v_pre = np.concatenate((np.zeros((nb_points, 1)), var_v[:, : nb_dims - 1]), axis=-1)
552+
x = var_v - v_pre
553+
res = radius * x * np.random.choice([-1, 1], (nb_points, nb_dims))
555554

556555
elif norm == 2:
557556
if isinstance(radius, np.ndarray):
@@ -1563,6 +1562,24 @@ def is_probability(vector: np.ndarray) -> bool:
15631562
return is_sum_1 and is_smaller_1 and is_larger_0
15641563

15651564

1565+
def is_probability_array(array: np.ndarray) -> bool:
1566+
"""
1567+
Check if a multi-dimensional array is an array of probabilities.
1568+
1569+
:param vector: A numpy array.
1570+
:return: True if it is an array of probabilities.
1571+
"""
1572+
if len(array.shape) == 1:
1573+
return is_probability(array)
1574+
sum_array = np.sum(array, axis=1)
1575+
ones = np.ones_like(sum_array)
1576+
is_sum_1 = np.allclose(sum_array, ones, rtol=1e-03)
1577+
is_smaller_1 = np.amax(array) <= 1.0
1578+
is_larger_0 = np.amin(array) >= 0.0
1579+
1580+
return is_sum_1 and is_smaller_1 and is_larger_0
1581+
1582+
15661583
def pad_sequence_input(x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
15671584
"""
15681585
Apply padding to a batch of 1-dimensional samples such that it has shape of (batch_size, max_length).

0 commit comments

Comments
 (0)