Skip to content

Commit 1186069

Browse files
authored
Merge pull request #1197 from minaremeli/new_boundary_attack
New boundary attack
2 parents 353adb2 + d6e8934 commit 1186069

File tree

3 files changed

+590
-2
lines changed

3 files changed

+590
-2
lines changed

art/attacks/inference/membership_inference/label_only_boundary_distance.py

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"""
1919
This module implements the Label-Only Inference Attack based on Decision Boundary.
2020
21-
| Paper link: https://arxiv.org/abs/2007.14321
21+
| Paper link: https://arxiv.org/abs/2007.14321 (Choquette-Choo et al.) and https://arxiv.org/abs/2007.15528 (Li and
22+
Zhang)
2223
"""
2324
import logging
2425
from typing import Optional, TYPE_CHECKING
@@ -40,7 +41,10 @@ class LabelOnlyDecisionBoundary(MembershipInferenceAttack):
4041
"""
4142
Implementation of Label-Only Inference Attack based on Decision Boundary.
4243
43-
| Paper link: https://arxiv.org/abs/2007.14321
44+
| Paper link: https://arxiv.org/abs/2007.14321 (Choquette-Choo et al.) and https://arxiv.org/abs/2007.15528 (Li
45+
and Zhang)
46+
47+
You only need to call ONE of the calibrate methods, depending on which attack you want to launch.
4448
"""
4549

4650
attack_params = MembershipInferenceAttack.attack_params + [
@@ -136,6 +140,8 @@ def calibrate_distance_threshold(
136140
"""
137141
Calibrate distance threshold maximising the membership inference accuracy on `x_train` and `x_test`.
138142
143+
| Paper link: https://arxiv.org/abs/2007.14321
144+
139145
:param x_train: Training data.
140146
:param y_train: Labels of training data `x_train`.
141147
:param x_test: Test data.
@@ -196,6 +202,70 @@ def calibrate_distance_threshold(
196202

197203
self.distance_threshold_tau = distance_threshold_tau
198204

205+
def calibrate_distance_threshold_unsupervised(
206+
self, top_t: int = 50, num_samples: int = 100, max_queries: int = 1, **kwargs
207+
):
208+
"""
209+
Calibrate distance threshold on randomly generated samples, choosing the top-t percentile of the noise needed
210+
to change the classifier's initial prediction. This method requires the model's clip_values to be set.
211+
212+
| Paper link: https://arxiv.org/abs/2007.15528
213+
214+
:param top_t: Top-t percentile.
215+
:param num_samples: Number of random samples to generate.
216+
:param max_queries: Maximum number of queries. Maximum number of HSJ iterations on a single sample will be
217+
max_queries * max_iter.
218+
:Keyword Arguments for HopSkipJump:
219+
* *norm*: Order of the norm. Possible values: "inf", np.inf or 2.
220+
* *max_iter*: Maximum number of iterations.
221+
* *max_eval*: Maximum number of evaluations for estimating gradient.
222+
* *init_eval*: Initial number of evaluations for estimating gradient.
223+
* *init_size*: Maximum number of trials for initial generation of adversarial examples.
224+
* *verbose*: Show progress bars.
225+
"""
226+
from art.attacks.evasion.hop_skip_jump import HopSkipJump
227+
228+
if self.estimator.clip_values is not None:
229+
x_min, x_max = self.estimator.clip_values
230+
else:
231+
raise RuntimeError(
232+
"You need to set the estimator's clip_values in order to calibrate the distance threshold."
233+
)
234+
235+
x_rand = np.random.rand(*(num_samples,) + self.estimator.input_shape).astype(np.float32)
236+
x_rand *= x_max - x_min # scale
237+
x_rand += x_min # shift
238+
239+
y_rand = self.estimator.predict(x=x_rand)
240+
y_rand = check_and_transform_label_format(y_rand, self.estimator.nb_classes)
241+
242+
hsj = HopSkipJump(classifier=self.estimator, targeted=False, **kwargs)
243+
244+
distances = []
245+
246+
i = 0
247+
while len(x_rand) != 0 and i < max_queries:
248+
x_adv = hsj.generate(x=x_rand, y=y_rand)
249+
250+
distance = np.linalg.norm((x_adv - x_rand).reshape((x_rand.shape[0], -1)), ord=2, axis=1)
251+
252+
y_pred = self.estimator.predict(x=x_adv)
253+
254+
changed_predictions = np.argmax(y_pred, axis=1) != np.argmax(y_rand, axis=1)
255+
256+
distances.extend(distance[changed_predictions])
257+
258+
x_rand, y_rand = x_adv[~changed_predictions], y_rand[~changed_predictions]
259+
260+
i += 1
261+
262+
if len(distances) == 0:
263+
raise RuntimeWarning(
264+
"No successful adversarial examples were generated - no distances were obtained."
265+
"Distance threshold will not be set."
266+
)
267+
self.distance_threshold_tau = np.percentile(distances, top_t)
268+
199269
def _check_params(self) -> None:
200270
if self.distance_threshold_tau is not None and (
201271
not isinstance(self.distance_threshold_tau, (int, float)) or self.distance_threshold_tau <= 0.0

0 commit comments

Comments
 (0)