adv-lib 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. adv_lib/__init__.py +1 -0
  2. adv_lib/attacks/__init__.py +13 -0
  3. adv_lib/attacks/augmented_lagrangian.py +243 -0
  4. adv_lib/attacks/auto_pgd.py +523 -0
  5. adv_lib/attacks/boundary_projection_tf.py +170 -0
  6. adv_lib/attacks/carlini_wagner/__init__.py +2 -0
  7. adv_lib/attacks/carlini_wagner/l2.py +151 -0
  8. adv_lib/attacks/carlini_wagner/linf.py +158 -0
  9. adv_lib/attacks/decoupled_direction_norm.py +113 -0
  10. adv_lib/attacks/fast_adaptive_boundary/__init__.py +1 -0
  11. adv_lib/attacks/fast_adaptive_boundary/fast_adaptive_boundary.py +215 -0
  12. adv_lib/attacks/fast_adaptive_boundary/projections.py +164 -0
  13. adv_lib/attacks/fast_minimum_norm.py +218 -0
  14. adv_lib/attacks/perceptual_color_attacks/__init__.py +1 -0
  15. adv_lib/attacks/perceptual_color_attacks/differential_color_functions.py +181 -0
  16. adv_lib/attacks/perceptual_color_attacks/perceptual_color_distance_al.py +128 -0
  17. adv_lib/attacks/primal_dual_gradient_descent.py +379 -0
  18. adv_lib/attacks/projected_gradient_descent.py +109 -0
  19. adv_lib/attacks/segmentation/__init__.py +4 -0
  20. adv_lib/attacks/segmentation/alma_prox.py +283 -0
  21. adv_lib/attacks/segmentation/asma.py +92 -0
  22. adv_lib/attacks/segmentation/dense_adversary.py +83 -0
  23. adv_lib/attacks/segmentation/primal_dual_gradient_descent.py +349 -0
  24. adv_lib/attacks/self_adaptive_norm_update.py +127 -0
  25. adv_lib/attacks/sigma_zero.py +119 -0
  26. adv_lib/attacks/stochastic_sparse_attacks.py +237 -0
  27. adv_lib/attacks/structured_adversarial_attack.py +289 -0
  28. adv_lib/attacks/trust_region.py +153 -0
  29. adv_lib/distances/__init__.py +0 -0
  30. adv_lib/distances/color_difference.py +212 -0
  31. adv_lib/distances/lp_norms.py +18 -0
  32. adv_lib/distances/lpips.py +99 -0
  33. adv_lib/distances/structural_similarity.py +147 -0
  34. adv_lib/utils/__init__.py +1 -0
  35. adv_lib/utils/attack_utils.py +226 -0
  36. adv_lib/utils/color_conversions.py +71 -0
  37. adv_lib/utils/image_selection.py +27 -0
  38. adv_lib/utils/lagrangian_penalties/__init__.py +1 -0
  39. adv_lib/utils/lagrangian_penalties/all_penalties.py +67 -0
  40. adv_lib/utils/lagrangian_penalties/penalty_functions.py +79 -0
  41. adv_lib/utils/lagrangian_penalties/scripts/plot_penalties.py +42 -0
  42. adv_lib/utils/lagrangian_penalties/scripts/plot_univariates.py +32 -0
  43. adv_lib/utils/lagrangian_penalties/univariate_functions.py +299 -0
  44. adv_lib/utils/losses.py +29 -0
  45. adv_lib/utils/projections.py +100 -0
  46. adv_lib/utils/utils.py +58 -0
  47. adv_lib/utils/visdom_logger.py +109 -0
  48. adv_lib-0.2.2.dist-info/LICENSE +29 -0
  49. adv_lib-0.2.2.dist-info/METADATA +170 -0
  50. adv_lib-0.2.2.dist-info/RECORD +52 -0
  51. adv_lib-0.2.2.dist-info/WHEEL +5 -0
  52. adv_lib-0.2.2.dist-info/top_level.txt +1 -0
adv_lib/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.2.2"
@@ -0,0 +1,13 @@
1
+ from .augmented_lagrangian import alma
2
+ from .auto_pgd import apgd, apgd_targeted
3
+ from .carlini_wagner import carlini_wagner_l2, carlini_wagner_linf
4
+ from .decoupled_direction_norm import ddn
5
+ from .fast_adaptive_boundary import fab
6
+ from .fast_minimum_norm import fmn
7
+ from .perceptual_color_attacks import perc_al
8
+ from .primal_dual_gradient_descent import pdgd, pdpgd
9
+ from .projected_gradient_descent import pgd_linf
10
+ from .sigma_zero import sigma_zero
11
+ from .stochastic_sparse_attacks import fga, vfga
12
+ from .structured_adversarial_attack import str_attack
13
+ from .trust_region import tr
@@ -0,0 +1,243 @@
1
+ from functools import partial
2
+ from typing import Callable, Optional
3
+
4
+ import torch
5
+ from torch import Tensor, nn
6
+ from torch.autograd import grad
7
+
8
+ from adv_lib.distances.color_difference import ciede2000_loss
9
+ from adv_lib.distances.lp_norms import l1_distances, l2_distances
10
+ from adv_lib.distances.lpips import LPIPS
11
+ from adv_lib.distances.structural_similarity import ms_ssim_loss, ssim_loss
12
+ from adv_lib.utils.lagrangian_penalties import all_penalties
13
+ from adv_lib.utils.losses import difference_of_logits_ratio
14
+ from adv_lib.utils.visdom_logger import VisdomLogger
15
+
16
+
17
+ def init_lr_finder(inputs: Tensor, grad: Tensor, distance_function: Callable, target_distance: float) -> Tensor:
18
+ """
19
+ Performs a line search and a binary search to find the learning rate η for each sample such that:
20
+ distance_function(inputs - η * grad) = target_distance.
21
+
22
+ Parameters
23
+ ----------
24
+ inputs : Tensor
25
+ Reference to compute the distance from.
26
+ grad : Tensor
27
+ Direction to step in.
28
+ distance_function : Callable
29
+ target_distance : float
30
+ Target distance that inputs - η * grad should reach.
31
+
32
+ Returns
33
+ -------
34
+ η : Tensor
35
+ Learning rate for each sample.
36
+
37
+ """
38
+ batch_size = len(inputs)
39
+ batch_view = lambda tensor: tensor.view(batch_size, *[1] * (inputs.ndim - 1))
40
+ lr = torch.ones(batch_size, device=inputs.device)
41
+ lower = torch.zeros_like(lr)
42
+
43
+ found_upper = distance_function((inputs - grad).clamp_(min=0, max=1)) > target_distance
44
+ while (~found_upper).any():
45
+ lower = torch.where(found_upper, lower, lr)
46
+ lr = torch.where(found_upper, lr, lr * 2)
47
+ found_upper = distance_function((inputs - batch_view(lr) * grad).clamp_(min=0, max=1)) > target_distance
48
+
49
+ for i in range(20):
50
+ new_lr = (lower + lr) / 2
51
+ larger = distance_function((inputs - batch_view(new_lr) * grad).clamp_(min=0, max=1)) > target_distance
52
+ lower, lr = torch.where(larger, lower, new_lr), torch.where(larger, new_lr, lr)
53
+
54
+ return (lr + lower) / 2
55
+
56
+
57
+ _distances = {
58
+ 'ssim': ssim_loss,
59
+ 'msssim': ms_ssim_loss,
60
+ 'ciede2000': partial(ciede2000_loss, ε=1e-12),
61
+ 'lpips': LPIPS,
62
+ 'l2': l2_distances,
63
+ 'l1': l1_distances,
64
+ }
65
+
66
+
67
+ def alma(model: nn.Module,
68
+ inputs: Tensor,
69
+ labels: Tensor,
70
+ penalty: Callable = all_penalties['P2'],
71
+ targeted: bool = False,
72
+ num_steps: int = 1000,
73
+ lr_init: float = 0.1,
74
+ lr_reduction: float = 0.01,
75
+ distance: str = 'l2',
76
+ init_lr_distance: Optional[float] = None,
77
+ μ_init: float = 1,
78
+ ρ_init: float = 1,
79
+ check_steps: int = 10,
80
+ τ: float = 0.95,
81
+ γ: float = 1.2,
82
+ α: float = 0.9,
83
+ α_rms: Optional[float] = None,
84
+ momentum: Optional[float] = None,
85
+ logit_tolerance: float = 1e-4,
86
+ levels: Optional[int] = None,
87
+ callback: Optional[VisdomLogger] = None) -> Tensor:
88
+ """
89
+ Augmented Lagrangian Method for Adversarial (ALMA) attack from https://arxiv.org/abs/2011.11857.
90
+
91
+ Parameters
92
+ ----------
93
+ model : nn.Module
94
+ Model to attack.
95
+ inputs : Tensor
96
+ Inputs to attack. Should be in [0, 1].
97
+ labels : Tensor
98
+ Labels corresponding to the inputs if untargeted, else target labels.
99
+ penalty : Callable
100
+ Penalty-Lagrangian function to use. A good default choice is P2 (see the original article).
101
+ targeted : bool
102
+ Whether to perform a targeted attack or not.
103
+ num_steps : int
104
+ Number of optimization steps. Corresponds to the number of forward and backward propagations.
105
+ lr_init : float
106
+ Initial learning rate.
107
+ lr_reduction : float
108
+ Reduction factor for the learning rate. The final learning rate is lr_init * lr_reduction
109
+ distance : str
110
+ Distance to use.
111
+ init_lr_distance : float
112
+ If a float is given, the initial learning rate will be calculated such that the first step results in an
113
+ increase of init_lr_distance of the distance to minimize. This corresponds to ε in the original article.
114
+ μ_init : float
115
+ Initial value of the penalty multiplier.
116
+ ρ_init : float
117
+ Initial value of the penalty parameter.
118
+ check_steps : int
119
+ Number of steps between checks for the improvement of the constraint. This corresponds to M in the original
120
+ article.
121
+ τ : float
122
+ Constraint improvement rate.
123
+ γ : float
124
+ Penalty parameter increase rate.
125
+ α : float
126
+ Weight for the exponential moving average.
127
+ α_rms : float
128
+ Smoothing constant for RMSProp. If none is provided, defaults to α.
129
+ momentum : float
130
+ Momentum for the RMSProp. If none is provided, defaults to α.
131
+ logit_tolerance : float
132
+ Small quantity added to the difference of logits to avoid solutions where the difference of logits is 0, which
133
+ can results in inconsistent class prediction (using argmax) on GPU. This can also be used as a confidence
134
+ parameter κ as in https://arxiv.org/abs/1608.04644, however, a confidence parameter on logits is not robust to
135
+ scaling of the logits.
136
+ levels : int
137
+ Number of levels for quantization. The attack will perform quantization only if the number of levels is
138
+ provided.
139
+ callback : VisdomLogger
140
+ Callback to visualize the progress of the algorithm.
141
+
142
+ Returns
143
+ -------
144
+ best_adv : Tensor
145
+ Perturbed inputs (inputs + perturbation) that are adversarial and have smallest distance with the original
146
+ inputs.
147
+
148
+ """
149
+ device = inputs.device
150
+ batch_size = len(inputs)
151
+ batch_view = lambda tensor: tensor.view(batch_size, *[1] * (inputs.ndim - 1))
152
+ multiplier = -1 if targeted else 1
153
+
154
+ # Setup variables
155
+ δ = torch.zeros_like(inputs, requires_grad=True)
156
+ square_avg = torch.ones_like(inputs)
157
+ momentum_buffer = torch.zeros_like(inputs)
158
+ lr = torch.full((batch_size,), lr_init, device=device, dtype=torch.float)
159
+ α_rms, momentum = α if α_rms is None else α_rms, α if momentum is None else momentum
160
+
161
+ # Init rho and mu
162
+ μ = torch.full((batch_size,), μ_init, device=device, dtype=torch.float)
163
+ ρ = torch.full((batch_size,), ρ_init, device=device, dtype=torch.float)
164
+
165
+ # Init similarity metric
166
+ if distance in ['lpips']:
167
+ dist_func = _distances[distance](target=inputs)
168
+ else:
169
+ dist_func = partial(_distances[distance], inputs)
170
+
171
+ # Init trackers
172
+ best_dist = torch.full((batch_size,), float('inf'), device=device)
173
+ best_adv = inputs.clone()
174
+ adv_found = torch.zeros_like(best_dist, dtype=torch.bool)
175
+ step_found = torch.full_like(best_dist, num_steps + 1)
176
+
177
+ for i in range(num_steps):
178
+
179
+ adv_inputs = inputs + δ
180
+ logits = model(adv_inputs)
181
+ dist = dist_func(adv_inputs)
182
+
183
+ if i == 0:
184
+ labels_infhot = torch.zeros_like(logits).scatter_(1, labels.unsqueeze(1), float('inf'))
185
+ dlr_func = partial(difference_of_logits_ratio, labels=labels, labels_infhot=labels_infhot,
186
+ targeted=targeted, ε=logit_tolerance)
187
+
188
+ dlr = multiplier * dlr_func(logits)
189
+
190
+ if i == 0:
191
+ prev_dlr = dlr.detach()
192
+ elif (i + 1) % check_steps == 0:
193
+ improved_dlr = (dlr.detach() < τ * prev_dlr)
194
+ ρ = torch.where(~(adv_found | improved_dlr), γ * ρ, ρ)
195
+ prev_dlr = dlr.detach()
196
+
197
+ if i:
198
+ new_μ = grad(penalty(dlr, ρ, μ).sum(), dlr, only_inputs=True)[0]
199
+ μ.lerp_(new_μ, weight=1 - α).clamp_(min=1e-6, max=1e12)
200
+
201
+ is_adv = dlr < 0
202
+ is_smaller = dist < best_dist
203
+ is_both = is_adv & is_smaller
204
+ step_found.masked_fill_((~adv_found) & is_adv, i)
205
+ adv_found.logical_or_(is_adv)
206
+ best_dist = torch.where(is_both, dist.detach(), best_dist)
207
+ best_adv = torch.where(batch_view(is_both), adv_inputs.detach(), best_adv)
208
+
209
+ if i == 0:
210
+ loss = penalty(dlr, ρ, μ)
211
+ else:
212
+ loss = dist + penalty(dlr, ρ, μ)
213
+ δ_grad = grad(loss.sum(), δ, only_inputs=True)[0]
214
+
215
+ grad_norm = δ_grad.flatten(1).norm(p=2, dim=1)
216
+ if init_lr_distance is not None and i == 0:
217
+ randn_grad = torch.randn_like(δ_grad).renorm(dim=0, p=2, maxnorm=1)
218
+ δ_grad = torch.where(batch_view(grad_norm <= 1e-6), randn_grad, δ_grad)
219
+ lr = init_lr_finder(inputs, δ_grad, dist_func, target_distance=init_lr_distance)
220
+
221
+ exp_decay = lr_reduction ** ((i - step_found).clamp_(min=0) / (num_steps - step_found))
222
+ step_lr = lr * exp_decay
223
+ square_avg.mul_(α_rms).addcmul_(δ_grad, δ_grad, value=1 - α_rms)
224
+ momentum_buffer.mul_(momentum).addcdiv_(δ_grad, square_avg.sqrt().add_(1e-8))
225
+ δ.data.addcmul_(momentum_buffer, batch_view(step_lr), value=-1)
226
+
227
+ δ.data.add_(inputs).clamp_(min=0, max=1)
228
+ if levels is not None:
229
+ δ.data.mul_(levels - 1).round_().div_(levels - 1)
230
+ δ.data.sub_(inputs)
231
+
232
+ if callback:
233
+ cb_best_dist = best_dist.masked_select(adv_found).mean()
234
+ callback.accumulate_line([distance, 'dlr'], i, [dist.mean(), dlr.mean()])
235
+ callback.accumulate_line(['μ_c', 'ρ_c'], i, [μ.mean(), ρ.mean()])
236
+ callback.accumulate_line('grad_norm', i, grad_norm.mean())
237
+ callback.accumulate_line(['best_{}'.format(distance), 'success', 'lr'], i,
238
+ [cb_best_dist, adv_found.float().mean(), step_lr.mean()])
239
+
240
+ if (i + 1) % (num_steps // 20) == 0 or (i + 1) == num_steps:
241
+ callback.update_lines()
242
+
243
+ return best_adv