"""
Copyright 2020 Johns Hopkins University (Author: Jesus Villalba)
Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
"""
import math
from jsonargparse import ArgumentParser, ActionParser
import torch
from .attack_factory import AttackFactory as AF
[docs]class RandomAttackFactory(object):
[docs] def __init__(
self,
attack_types,
min_eps=1e-5,
max_eps=0.1,
min_snr=30,
max_snr=60,
min_alpha=1e-5,
max_alpha=0.02,
norms=[float("inf")],
random_eps=False,
min_num_random_init=0,
max_num_random_init=3,
min_confidence=0,
max_confidence=1,
min_lr=1e-3,
max_lr=1e-2,
min_binary_search_steps=9,
max_binary_search_steps=9,
min_iter=5,
max_iter=10,
abort_early=True,
min_c=1e-3,
max_c=1e-2,
reduce_c=False,
c_incr_factor=2,
tau_decr_factor=0.9,
indep_channels=False,
norm_time=False,
time_dim=None,
use_snr=False,
loss=None,
targeted=False,
range_min=None,
range_max=None,
eps_scale=1,
):
self.attack_types = attack_types
self.min_eps = min_eps
self.max_eps = max_eps
self.min_snr = min_snr
self.max_snr = max_snr
self.min_alpha = min_alpha
self.max_alpha = max_alpha
self.norms = norms
self.random_eps = random_eps
self.min_num_random_init = min_num_random_init
self.max_num_random_init = max_num_random_init
self.min_confidence = min_confidence
self.max_confidence = max_confidence
self.min_lr = min_lr
self.max_lr = max_lr
self.min_binary_search_steps = min_binary_search_steps
self.max_binary_search_steps = max_binary_search_steps
self.abort_early = abort_early
self.min_iter = min_iter
self.max_iter = max_iter
self.min_c = min_c
self.max_c = max_c
self.reduce_c = reduce_c
self.c_incr_factor = c_incr_factor
self.tau_decr_factor = tau_decr_factor
self.indep_channels = indep_channels
self.norm_time = norm_time
self.time_dim = time_dim
self.use_snr = use_snr
self.loss = loss
self.targeted = targeted
self.range_min = range_min
self.range_max = range_max
self.eps_scale = eps_scale
@staticmethod
def _choice(n):
return torch.randint(low=0, high=n, size=(1,)).item()
@staticmethod
def _randint(min_val, max_val):
return torch.randint(low=min_val, high=max_val + 1, size=(1,)).item()
@staticmethod
def _uniform(min_val, max_val):
return (max_val - min_val) * torch.rand(size=(1,)).item() + min_val
@staticmethod
def _log_uniform(min_val, max_val):
log_x = (math.log(max_val) - math.log(min_val)) * torch.rand(
size=(1,)
).item() + math.log(min_val)
return math.exp(log_x)
def _sample_attack_args(self):
attack_args = {}
attack_idx = self._choice(len(self.attack_types))
attack_args["attack_type"] = self.attack_types[attack_idx]
eps = self._log_uniform(self.min_eps, self.max_eps)
attack_args["eps"] = eps
attack_args["alpha"] = self._log_uniform(
min(eps, self.min_alpha), min(eps, self.max_alpha)
)
attack_args["norm"] = self.norms[self._choice(len(self.norms))]
attack_args["random_eps"] = self.random_eps
attack_args["num_random_init"] = self._randint(
self.min_num_random_init, self.max_num_random_init
)
attack_args["confidence"] = self._uniform(
self.min_confidence, self.max_confidence
)
attack_args["lr"] = self._uniform(self.min_lr, self.max_lr)
attack_args["binary_search_steps"] = self._randint(
self.min_binary_search_steps, self.max_binary_search_steps
)
attack_args["max_iter"] = self._randint(self.min_iter, self.max_iter)
attack_args["abort_early"] = self.abort_early
attack_args["c"] = self._uniform(self.min_c, self.max_c)
attack_args["reduce_c"] = self.reduce_c
attack_args["c_incr_factor"] = self.c_incr_factor
attack_args["tau_decr_factor"] = self.tau_decr_factor
attack_args["indep_channels"] = self.indep_channels
attack_args["norm_time"] = self.norm_time
attack_args["time_dim"] = self.time_dim
attack_args["use_snr"] = self.use_snr
attack_args["targeted"] = self.targeted
attack_args["range_min"] = self.range_min
attack_args["range_max"] = self.range_max
attack_args["eps_scale"] = self.eps_scale
attack_args["loss"] = self.loss
return attack_args
[docs] def sample_attack(self, model=None):
attack_args = self._sample_attack_args()
attack_args["model"] = model
return AF.create(**attack_args)
[docs] @staticmethod
def filter_args(**kwargs):
if "no_abort" in kwargs:
kwargs["abort_early"] = not kwargs["no_abort"]
if "norms" in kwargs:
kwargs["norms"] = [float(a) for a in kwargs["norms"]]
valid_args = (
"attack_types",
"min_eps",
"max_eps",
"min_snr",
"max_snr",
"norms",
"random_eps",
"min_num_random_init",
"max_num_random_init",
"min_alpha",
"max_alpha",
"min_confidence",
"max_confidence",
"min_lr",
"max_lr",
"min_binary_search_steps",
"max_binary_search_steps",
"min_iter",
"max_iter",
"abort_early",
"min_c",
"max_c",
"reduce_c",
"c_incr_factor",
"tau_decr_factor",
"indep_channels",
"use_snr",
"norm_time",
"targeted",
)
args = dict((k, kwargs[k]) for k in valid_args if k in kwargs)
return args
[docs] @staticmethod
def add_class_args(parser, prefix=None):
if prefix is not None:
outer_parser = parser
parser = ArgumentParser(prog="")
parser.add_argument(
"--attack-types",
type=str.lower,
default=["fgsm"],
nargs="+",
choices=[
"fgsm",
"snr-fgsm",
"rand-fgsm",
"iter-fgsm",
"cw-l0",
"cw-l2",
"cw-linf",
"pgd",
],
help=("Attack types"),
)
parser.add_argument(
"--norms",
type=float,
default=[float("inf")],
nargs="+",
choices=[float("inf"), 1, 2],
help=("Attack perturbation norms"),
)
parser.add_argument(
"--min-eps",
default=1e-5,
type=float,
help=("attack min epsilon, upper bound for the perturbation norm"),
)
parser.add_argument(
"--max-eps",
default=0.1,
type=float,
help=("attack max epsilon, upper bound for the perturbation norm"),
)
parser.add_argument(
"--min-snr",
default=30,
type=float,
help=(
"min upper bound for the signal-to-noise ratio of the "
"perturbed signal"
),
)
parser.add_argument(
"--max-snr",
default=60,
type=float,
help=(
"max upper bound for the signal-to-noise ratio of the "
"perturbed signal"
),
)
parser.add_argument(
"--min-alpha",
default=1e-5,
type=float,
help=("min alpha for iter and rand fgsm attack"),
)
parser.add_argument(
"--max-alpha",
default=0.02,
type=float,
help=("max alpha for iter and rand fgsm attack"),
)
parser.add_argument(
"--random-eps",
default=False,
action="store_true",
help=("use random epsilon in PGD attack"),
)
parser.add_argument(
"--min-confidence",
default=0,
type=float,
help=("min confidence for carlini-wagner attack"),
)
parser.add_argument(
"--max-confidence",
default=1,
type=float,
help=("max confidence for carlini-wagner attack"),
)
parser.add_argument(
"--min-lr",
default=1e-3,
type=float,
help=("min learning rate for attack optimizers"),
)
parser.add_argument(
"--max-lr",
default=1e-2,
type=float,
help=("max learning rate for attack optimizers"),
)
parser.add_argument(
"--min-binary-search-steps",
default=9,
type=int,
help=("min num bin. search steps in carlini-wagner-l2 attack"),
)
parser.add_argument(
"--max-binary-search-steps",
default=9,
type=int,
help=("max num bin. search steps in carlini-wagner-l2 attack"),
)
parser.add_argument(
"--min-iter",
default=5,
type=int,
help=("min maximum. num. of optim iters in attack"),
)
parser.add_argument(
"--max-iter",
default=10,
type=int,
help=("max maximum num. of optim iters in attack"),
)
parser.add_argument(
"--min-c",
default=1e-3,
type=float,
help=(
"min initial weight of constraint function f "
"in carlini-wagner attack"
),
)
parser.add_argument(
"--max-c",
default=1e-2,
type=float,
help=(
"max initial weight of constraint function f "
"in carlini-wagner attack"
),
)
parser.add_argument(
"--reduce-c",
default=False,
action="store_true",
help=("allow to reduce c in carline-wagner-l0/inf attack"),
)
parser.add_argument(
"--c-incr-factor",
default=2,
type=float,
help=("factor to increment c in carline-wagner-l0/inf attack"),
)
parser.add_argument(
"--tau-decr-factor",
default=0.75,
type=float,
help=("factor to reduce tau in carline-wagner-linf attack"),
)
parser.add_argument(
"--indep-channels",
default=False,
action="store_true",
help=("consider independent input channels in " "carlini-wagner-l0 attack"),
)
parser.add_argument(
"--no-abort",
default=False,
action="store_true",
help=("do not abort early in optimizer iterations"),
)
parser.add_argument(
"--min-num-random-init",
default=1,
type=int,
help=("min number of random initializations in PGD attack"),
)
parser.add_argument(
"--max-num-random-init",
default=5,
type=int,
help=("max number of random initializations in PGD attack"),
)
parser.add_argument(
"--targeted",
default=False,
action="store_true",
help="use targeted attack intead of non-targeted",
)
parser.add_argument(
"--use-snr",
default=False,
action="store_true",
help=(
"In carlini-wagner attack maximize SNR instead of "
"minimize perturbation norm"
),
)
parser.add_argument(
"--norm-time",
default=False,
action="store_true",
help=("normalize norm by number of samples in time dimension"),
)
if prefix is not None:
outer_parser.add_argument("--" + prefix, action=ActionParser(parser=parser))
# help='adversarial attack options')
add_argparse_args = add_class_args