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.
- adv_lib/__init__.py +1 -0
- adv_lib/attacks/__init__.py +13 -0
- adv_lib/attacks/augmented_lagrangian.py +243 -0
- adv_lib/attacks/auto_pgd.py +523 -0
- adv_lib/attacks/boundary_projection_tf.py +170 -0
- adv_lib/attacks/carlini_wagner/__init__.py +2 -0
- adv_lib/attacks/carlini_wagner/l2.py +151 -0
- adv_lib/attacks/carlini_wagner/linf.py +158 -0
- adv_lib/attacks/decoupled_direction_norm.py +113 -0
- adv_lib/attacks/fast_adaptive_boundary/__init__.py +1 -0
- adv_lib/attacks/fast_adaptive_boundary/fast_adaptive_boundary.py +215 -0
- adv_lib/attacks/fast_adaptive_boundary/projections.py +164 -0
- adv_lib/attacks/fast_minimum_norm.py +218 -0
- adv_lib/attacks/perceptual_color_attacks/__init__.py +1 -0
- adv_lib/attacks/perceptual_color_attacks/differential_color_functions.py +181 -0
- adv_lib/attacks/perceptual_color_attacks/perceptual_color_distance_al.py +128 -0
- adv_lib/attacks/primal_dual_gradient_descent.py +379 -0
- adv_lib/attacks/projected_gradient_descent.py +109 -0
- adv_lib/attacks/segmentation/__init__.py +4 -0
- adv_lib/attacks/segmentation/alma_prox.py +283 -0
- adv_lib/attacks/segmentation/asma.py +92 -0
- adv_lib/attacks/segmentation/dense_adversary.py +83 -0
- adv_lib/attacks/segmentation/primal_dual_gradient_descent.py +349 -0
- adv_lib/attacks/self_adaptive_norm_update.py +127 -0
- adv_lib/attacks/sigma_zero.py +119 -0
- adv_lib/attacks/stochastic_sparse_attacks.py +237 -0
- adv_lib/attacks/structured_adversarial_attack.py +289 -0
- adv_lib/attacks/trust_region.py +153 -0
- adv_lib/distances/__init__.py +0 -0
- adv_lib/distances/color_difference.py +212 -0
- adv_lib/distances/lp_norms.py +18 -0
- adv_lib/distances/lpips.py +99 -0
- adv_lib/distances/structural_similarity.py +147 -0
- adv_lib/utils/__init__.py +1 -0
- adv_lib/utils/attack_utils.py +226 -0
- adv_lib/utils/color_conversions.py +71 -0
- adv_lib/utils/image_selection.py +27 -0
- adv_lib/utils/lagrangian_penalties/__init__.py +1 -0
- adv_lib/utils/lagrangian_penalties/all_penalties.py +67 -0
- adv_lib/utils/lagrangian_penalties/penalty_functions.py +79 -0
- adv_lib/utils/lagrangian_penalties/scripts/plot_penalties.py +42 -0
- adv_lib/utils/lagrangian_penalties/scripts/plot_univariates.py +32 -0
- adv_lib/utils/lagrangian_penalties/univariate_functions.py +299 -0
- adv_lib/utils/losses.py +29 -0
- adv_lib/utils/projections.py +100 -0
- adv_lib/utils/utils.py +58 -0
- adv_lib/utils/visdom_logger.py +109 -0
- adv_lib-0.2.2.dist-info/LICENSE +29 -0
- adv_lib-0.2.2.dist-info/METADATA +170 -0
- adv_lib-0.2.2.dist-info/RECORD +52 -0
- adv_lib-0.2.2.dist-info/WHEEL +5 -0
- 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
|