@@ -13,11 +13,12 @@ class FastGradientMethod(Attack):
1313 Gradient Sign Method"). This implementation extends the attack to other norms, and is therefore called the Fast
1414 Gradient Method. Paper link: https://arxiv.org/abs/1412.6572
1515 """
16- attack_params = ['ord' , 'y' , 'y_val' , 'targeted' , 'clip_min' , 'clip_max' ]
16+ attack_params = ['ord' , 'eps' , ' y' , 'y_val' , 'targeted' , 'clip_min' , 'clip_max' ]
1717
18- def __init__ (self , classifier , sess = None , ord = np .inf , y = None , targeted = False , clip_min = None , clip_max = None ):
18+ def __init__ (self , classifier , sess = None , ord = np .inf , eps = .3 , y = None , targeted = False , clip_min = 0 , clip_max = 1 ):
1919 """Create a FastGradientMethod instance.
2020 :param ord: (optional) Order of the norm. Possible values: np.inf, 1 or 2.
21+ :param eps: (required float) attack step size (input variation)
2122 :param y: (optional) A placeholder for the model labels. Only provide this parameter if you'd like to use true
2223 labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the
2324 "label leaking" effect (explained in this paper: https://arxiv.org/abs/1611.01236). Default is None.
@@ -28,13 +29,13 @@ def __init__(self, classifier, sess=None, ord=np.inf, y=None, targeted=False, cl
2829 """
2930 super (FastGradientMethod , self ).__init__ (classifier , sess )
3031
31- kwargs = {'ord' : ord , 'targeted' : targeted , 'clip_min' : clip_min , 'clip_max' : clip_max , 'y' : y }
32+ kwargs = {'ord' : ord , 'eps' : eps , ' targeted' : targeted , 'clip_min' : clip_min , 'clip_max' : clip_max , 'y' : y }
3233 self .set_params (** kwargs )
3334
34- def generate_graph (self , x , eps , ** kwargs ):
35+ def generate_graph (self , x_op , eps_op , ** kwargs ):
3536 """Generate symbolic graph for adversarial examples and return.
36- :param x : The model's symbolic inputs.
37- :param eps : (optional tf.placeholder) The placeholder for input variation (noise amplitude)
37+ :param x_op : The model's symbolic inputs.
38+ :param eps_op : (optional tf.placeholder) The placeholder for input variation (noise amplitude)
3839 :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2.
3940 :param y: (optional) A placeholder for the model labels. Only provide this parameter if you'd like to use true
4041 labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the
@@ -45,7 +46,7 @@ def generate_graph(self, x, eps, **kwargs):
4546 """
4647 self .set_params (** kwargs )
4748
48- preds = self .classifier ._get_predictions (x , log = False )
49+ preds = self .classifier ._get_predictions (x_op , log = False )
4950
5051 if not hasattr (self , 'y' ) or self .y is None :
5152 # Use model predictions as correct outputs
@@ -59,20 +60,20 @@ def generate_graph(self, x, eps, **kwargs):
5960 loss = tf .nn .softmax_cross_entropy_with_logits (logits = preds , labels = y )
6061 if self .targeted :
6162 loss = - loss
62- grad , = tf .gradients (loss , x )
63+ grad , = tf .gradients (loss , x_op )
6364
6465 # Apply norm bound
6566 if self .ord == np .inf :
6667 grad = tf .sign (grad )
6768 elif self .ord == 1 :
68- ind = list (range (1 , len (x .get_shape ())))
69+ ind = list (range (1 , len (x_op .get_shape ())))
6970 grad = grad / tf .reduce_sum (tf .abs (grad ), reduction_indices = ind , keep_dims = True )
7071 elif self .ord == 2 :
71- ind = list (range (1 , len (x .get_shape ())))
72+ ind = list (range (1 , len (x_op .get_shape ())))
7273 grad = grad / tf .sqrt (tf .reduce_sum (tf .square (grad ), reduction_indices = ind , keep_dims = True ))
7374
7475 # Apply perturbation and clip
75- x_adv_op = x + eps * grad
76+ x_adv_op = x_op + eps_op * grad
7677 if self .clip_min is not None and self .clip_max is not None :
7778 x_adv_op = tf .clip_by_value (x_adv_op , self .clip_min , self .clip_max )
7879
@@ -133,16 +134,17 @@ def generate(self, x_val, **kwargs):
133134 return self .minimal_perturbations (self ._x , x_val , ** kwargs )
134135
135136 # Generate computation graph
136- self ._x_adv = self .generate_graph (self ._x , ** kwargs )
137+ eps = tf .placeholder (tf .float32 , None )
138+ self ._x_adv = self .generate_graph (self ._x , eps , ** kwargs )
137139
138140 # Run symbolic graph without or with true labels
139141 if 'y_val' not in kwargs or kwargs ['y_val' ] is None :
140- feed_dict = {self ._x : x_val }
142+ feed_dict = {self ._x : x_val , eps : self . eps }
141143 else :
142144 # Verify label placeholder was given in params if using true labels
143145 if self .y is None :
144146 raise Exception ("True labels given but label placeholder not given." )
145- feed_dict = {self ._x : x_val , self .y : kwargs ['y_val' ]}
147+ feed_dict = {self ._x : x_val , self .y : kwargs ['y_val' ], eps : self . eps }
146148
147149 return self .sess .run (self ._x_adv , feed_dict = feed_dict )
148150
@@ -152,6 +154,7 @@ def set_params(self, **kwargs):
152154
153155 Attack-specific parameters:
154156 :param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2.
157+ :param eps: (required float) attack step size (input variation)
155158 :param y: (optional) A placeholder for the model labels. Only provide this parameter if you'd like to use true
156159 labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the
157160 "label leaking" effect (explained in this paper: https://arxiv.org/abs/1611.01236). Default is None.
@@ -166,4 +169,7 @@ def set_params(self, **kwargs):
166169 if self .ord not in [np .inf , int (1 ), int (2 )]:
167170 raise ValueError ("Norm order must be either np.inf, 1, or 2." )
168171
172+ if self .eps <= self .clip_min or self .eps > self .clip_max :
173+ raise ValueError ('The amount of perturbation has to be in the data range.' )
174+
169175 return True
0 commit comments