def __init__(self, model, back='tf', sess=None): """ Create a FastGradientMethod instance. """ super(FastGradientMethod, self).__init__(model, back, sess) self.feedable_kwargs = { 'eps': np.float32, 'y': np.float32, 'clip_min': np.float32, 'clip_max': np.float32 } self.structural_kwargs = ['ord'] if not isinstance(self.model, Model): self.model = CallableModelWrapper(self.model, 'probs')
def __init__(self, models, back='tf', sess=None): """ Create a BasicIterativeMethod instance. """ super(MultiModelIterativeMethod, self).__init__(models, back, sess) self.feedable_kwargs = { 'eps': np.float32, 'eps_iter': np.float32, 'y': np.float32, 'clip_min': np.float32, 'clip_max': np.float32 } self.structural_kwargs = ['ord', 'nb_iter'] if not isinstance(self.model1, Model): self.model1 = CallableModelWrapper(self.model1, 'probs') if not isinstance(self.model2, Model): self.model2 = CallableModelWrapper(self.model2, 'probs') if not isinstance(self.model3, Model): self.model3 = CallableModelWrapper(self.model3, 'probs')
class MultiModelIterativeMethod(MultipleModelAttack): """ The Basic Iterative Method (Kurakin et al. 2016). The original paper used hard labels for this attack; no label smoothing. """ def __init__(self, models, back='tf', sess=None): """ Create a BasicIterativeMethod instance. """ super(MultiModelIterativeMethod, self).__init__(models, back, sess) self.feedable_kwargs = { 'eps': np.float32, 'eps_iter': np.float32, 'y': np.float32, 'clip_min': np.float32, 'clip_max': np.float32 } self.structural_kwargs = ['ord', 'nb_iter'] if not isinstance(self.model1, Model): self.model1 = CallableModelWrapper(self.model1, 'probs') if not isinstance(self.model2, Model): self.model2 = CallableModelWrapper(self.model2, 'probs') if not isinstance(self.model3, Model): self.model3 = CallableModelWrapper(self.model3, 'probs') def generate(self, x, **kwargs): """ Generate symbolic graph for adversarial examples and return. :param x: The model's symbolic inputs. :param eps: (required float) maximum distortion of adversarial example compared to original input :param eps_iter: (required float) step size for each attack iteration :param nb_iter: (required int) Number of attack iterations. :param y: (required) A tensor with the model labels. :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2. :param clip_min: (optional float) Minimum input component value :param clip_max: (optional float) Maximum input component value """ import tensorflow as tf # Parse and save attack-specific parameters assert self.parse_params(**kwargs) # Initialize loop variables eta = 0 # Fix labels to the first model predictions for loss computation # model_preds1 = self.model1.get_probs(x) # model_preds2 = self.model2.get_probs(x) model_preds3 = self.model3.get_probs(x) model_preds = model_preds3 preds_max = tf.reduce_max(model_preds, 1, keep_dims=True) y = tf.to_float(tf.equal(model_preds, preds_max)) fgsm_params = {'eps': self.eps_iter, 'y': y, 'ord': self.ord} for i in range(self.nb_iter): FGSM1 = FastGradientMethod(self.model1, back=self.back, sess=self.sess) FGSM2 = FastGradientMethod(self.model2, back=self.back, sess=self.sess) FGSM3 = FastGradientMethod(self.model3, back=self.back, sess=self.sess) # Compute this step's perturbation eta1 = FGSM1.generate(x + eta, **fgsm_params) - x eta2 = FGSM2.generate(x + eta, **fgsm_params) - x eta3 = FGSM3.generate(x + eta, **fgsm_params) - x eta = eta1 * 0.333 + eta2 * 0.333 + eta3 * 0.333 # Clipping perturbation eta to self.ord norm ball if self.ord == np.inf: eta = tf.clip_by_value(eta, -self.eps, self.eps) elif self.ord in [1, 2]: reduc_ind = list(xrange(1, len(eta.get_shape()))) if self.ord == 1: norm = tf.reduce_sum(tf.abs(eta), reduction_indices=reduc_ind, keep_dims=True) elif self.ord == 2: norm = tf.sqrt( tf.reduce_sum(tf.square(eta), reduction_indices=reduc_ind, keep_dims=True)) eta = eta * self.eps / norm # Define adversarial example (and clip if necessary) adv_x = x + eta if self.clip_min is not None and self.clip_max is not None: adv_x = tf.clip_by_value(adv_x, self.clip_min, self.clip_max) return adv_x def parse_params(self, eps=0.3, eps_iter=0.05, nb_iter=10, y=None, ord=np.inf, clip_min=None, clip_max=None, **kwargs): """ Take in a dictionary of parameters and applies attack-specific checks before saving them as attributes. Attack-specific parameters: :param eps: (required float) maximum distortion of adversarial example compared to original input :param eps_iter: (required float) step size for each attack iteration :param nb_iter: (required int) Number of attack iterations. :param y: (required) A tensor with the model labels. :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2. :param clip_min: (optional float) Minimum input component value :param clip_max: (optional float) Maximum input component value """ # Save attack-specific parameters self.eps = eps self.eps_iter = eps_iter self.nb_iter = nb_iter self.y = y self.ord = ord self.clip_min = clip_min self.clip_max = clip_max # Check if order of the norm is acceptable given current implementation if self.ord not in [np.inf, 1, 2]: raise ValueError("Norm order must be either np.inf, 1, or 2.") if self.back == 'th': error_string = "BasicIterativeMethod is not implemented in Theano" raise NotImplementedError(error_string) return True
class FastGradientMethod(Attack): """ This attack was originally implemented by Goodfellow et al. (2015) with the infinity norm (and is known as the "Fast Gradient Sign Method"). This implementation extends the attack to other norms, and is therefore called the Fast Gradient Method. Paper link: https://arxiv.org/abs/1412.6572 """ def __init__(self, model, back='tf', sess=None): """ Create a FastGradientMethod instance. """ super(FastGradientMethod, self).__init__(model, back, sess) self.feedable_kwargs = { 'eps': np.float32, 'y': np.float32, 'clip_min': np.float32, 'clip_max': np.float32 } self.structural_kwargs = ['ord'] if not isinstance(self.model, Model): self.model = CallableModelWrapper(self.model, 'probs') def generate(self, x, **kwargs): """ Generate symbolic graph for adversarial examples and return. :param x: The model's symbolic inputs. :param eps: (optional float) attack step size (input variation) :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2. :param y: (optional) A tensor with the model labels. Only provide this parameter if you'd like to use true labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the "label leaking" effect (explained in this paper: https://arxiv.org/abs/1611.01236). Default is None. Labels should be one-hot-encoded. :param clip_min: (optional float) Minimum input component value :param clip_max: (optional float) Maximum input component value """ # Parse and save attack-specific parameters assert self.parse_params(**kwargs) if self.back == 'tf': from .attacks_tf import fgm else: from .attacks_th import fgm return fgm(x, self.model.get_probs(x), y=self.y, eps=self.eps, ord=self.ord, clip_min=self.clip_min, clip_max=self.clip_max) def parse_params(self, eps=0.3, ord=np.inf, y=None, clip_min=None, clip_max=None, **kwargs): """ Take in a dictionary of parameters and applies attack-specific checks before saving them as attributes. Attack-specific parameters: :param eps: (optional float) attack step size (input variation) :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2. :param y: (optional) A tensor with the model labels. Only provide this parameter if you'd like to use true labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the "label leaking" effect (explained in this paper: https://arxiv.org/abs/1611.01236). Default is None. Labels should be one-hot-encoded. :param clip_min: (optional float) Minimum input component value :param clip_max: (optional float) Maximum input component value """ # Save attack-specific parameters self.eps = eps self.ord = ord self.y = y self.clip_min = clip_min self.clip_max = clip_max # Check if order of the norm is acceptable given current implementation if self.ord not in [np.inf, int(1), int(2)]: raise ValueError("Norm order must be either np.inf, 1, or 2.") if self.back == 'th' and self.ord != np.inf: raise NotImplementedError("The only FastGradientMethod norm " "implemented for Theano is np.inf.") return True