Spaces:
Runtime error
Runtime error
| """ | |
| Projected Gradient Descent (PGD) under L∞ for CIFAR-scale inputs. | |
| """ | |
| from __future__ import annotations | |
| from typing import Callable | |
| import torch | |
| import torch.nn as nn | |
| from adverscan.attacks.result import AttackResult | |
| def _batch_l2_perturbation(clean: torch.Tensor, adversarial: torch.Tensor) -> torch.Tensor: | |
| diff = (adversarial - clean).view(clean.shape[0], -1) | |
| return torch.linalg.vector_norm(diff, ord=2, dim=1) | |
| def pgd_attack( | |
| model: nn.Module, | |
| input_tensor: torch.Tensor, | |
| true_label: torch.Tensor, | |
| *, | |
| epsilon: float, | |
| criterion: Callable[..., torch.Tensor] | None = None, | |
| steps: int = 10, | |
| alpha: float | None = None, | |
| clamp: tuple[float, float] | None = None, | |
| targeted: bool = False, | |
| random_start: float | None = None, | |
| **_: object, | |
| ) -> AttackResult: | |
| """ | |
| PGD attack with boxed constraints and projected L∞ ball around ``input_tensor``. | |
| Parameters | |
| ---------- | |
| model | |
| Differentiable victim emitting logits matching ``true_label``. | |
| input_tensor | |
| Clean batch tensor ``(N, C, H, W)``. | |
| true_label | |
| ``(N,)`` label tensor (ground-truth or targets depending on ``targeted``). | |
| Other parameters match standard PGD nomenclature; ``epsilon`` is the L∞ radius. | |
| Returns | |
| ------- | |
| AttackResult | |
| Adversarial examples and per-sample L₂ norms :math:`\\|x' - x\\|_2`. | |
| """ | |
| ce = criterion or nn.CrossEntropyLoss() | |
| if alpha is None: | |
| alpha = epsilon / float(max(steps / 4, 1)) | |
| ori = input_tensor.detach() | |
| clamp_lo, clamp_hi = clamp if clamp is not None else (-float("inf"), float("inf")) | |
| x_adv = ori.clone() | |
| if random_start is not None: | |
| eta = torch.empty_like(x_adv).uniform_(-random_start, random_start) | |
| eta = torch.clamp(eta, -epsilon, epsilon) | |
| x_adv = ori + eta | |
| if clamp is not None: | |
| x_adv = torch.clamp(x_adv, clamp_lo, clamp_hi) | |
| lbl = true_label.long() | |
| for _ in range(steps): | |
| x_adv.requires_grad_(True) | |
| logits = model(x_adv) | |
| loss = ce(logits, lbl) | |
| grad = torch.autograd.grad(loss, x_adv)[0] | |
| signed = -grad.sign() if targeted else grad.sign() | |
| x_adv = x_adv.detach() + alpha * signed | |
| x_adv = torch.max(torch.min(x_adv, ori + epsilon), ori - epsilon) | |
| if clamp is not None: | |
| x_adv = torch.clamp(x_adv, clamp_lo, clamp_hi) | |
| l2_mag = _batch_l2_perturbation(ori, x_adv.detach()) | |
| return AttackResult(adversarial_examples=x_adv.detach(), perturbation_magnitude_l2=l2_mag) | |