Skip to content

Commit a79472f

Browse files
Irina NicolaeIrina Nicolae
authored andcommitted
Implement VAT independently from Cleverhans
1 parent 36cf7b2 commit a79472f

File tree

2 files changed

+67
-35
lines changed

2 files changed

+67
-35
lines changed

src/attacks/fast_gradient.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def generate(self, x_val, **kwargs):
122122
Labels should be one-hot-encoded.
123123
:param clip_min: (optional float) Minimum input component value
124124
:param clip_max: (optional float) Maximum input component value
125+
:return: A Numpy array holding the adversarial examples.
125126
"""
126127

127128
input_shape = list(x_val.shape)

src/attacks/virtual_adversarial.py

Lines changed: 66 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,109 @@
1-
from __future__ import absolute_import, division, print_function
1+
from __future__ import absolute_import, division, print_function, unicode_literals
22

3-
from config import config_dict
4-
5-
from cleverhans.attacks_tf import vatm
6-
from keras import backend as k
3+
import numpy as np
74
import tensorflow as tf
85

9-
from src.attacks.attack import Attack
6+
from src.attacks.attack import Attack, class_derivative
107

118

129
class VirtualAdversarialMethod(Attack):
1310
"""
1411
This attack was originally proposed by Miyato et al. (2016) and was used for virtual adversarial training.
1512
Paper link: https://arxiv.org/abs/1507.00677
1613
"""
17-
attack_params = ['max_iter', 'xi', 'clip_min', 'clip_max']
14+
attack_params = ['eps', 'finite_diff', 'max_iter', 'clip_min', 'clip_max']
1815

19-
def __init__(self, classifier, sess=None, max_iter=5, xi=1e-6, clip_min=None, clip_max=None):
16+
def __init__(self, classifier, sess=None, max_iter=1, finite_diff=1e-6, eps=.1, clip_min=0., clip_max=1.):
2017
"""
2118
Create a VirtualAdversarialMethod instance.
19+
2220
:param classifier: A function that takes a symbolic input and returns the symbolic output for the classifier's
2321
predictions.
24-
:param sess: The tf session to run graphs in.
25-
:param max_iter: (optional integer) The maximum number of iterations.
26-
:param xi: (optional float) The finite difference parameter.
22+
:param sess: The tf session to run graphs in
23+
:param eps: (optional float) the epsilon (max input variation parameter)
24+
:param finite_diff: (optional float) The finite difference parameter
25+
:param max_iter: (optional integer) The maximum number of iterations
2726
:param clip_min: (optional float) Minimum input component value
2827
:param clip_max: (optional float) Maximum input component value
2928
"""
3029
super(VirtualAdversarialMethod, self).__init__(classifier, sess)
3130

32-
kwargs = {'max_iter': max_iter, 'xi': xi, 'clip_min': clip_min, 'clip_max': clip_max}
31+
kwargs = {'finite_diff': finite_diff, 'eps': eps, 'max_iter': max_iter, 'clip_min': clip_min, 'clip_max': clip_max}
3332
self.set_params(**kwargs)
3433

35-
def generate_graph(self, x, eps=0.1, **kwargs):
34+
def generate(self, x_val, **kwargs):
3635
"""
37-
Generate symbolic graph for adversarial examples and return.
38-
:param x: The model's symbolic inputs.
36+
Generate adversarial samples and return them in a Numpy array.
37+
38+
:param x_val: (required) A Numpy array with the original inputs
3939
:param eps: (optional float) the epsilon (max input variation parameter)
40-
:param max_iter: (optional integer) The maximum number of iterations.
41-
:param xi: (optional float) The finite difference parameter.
40+
:param finite_diff: (optional float) The finite difference parameter
41+
:param max_iter: (optinal integer) The maximum number of iterations
4242
:param clip_min: (optional float) Minimum input component value
4343
:param clip_max: (optional float) Maximum input component value
44+
:return: A Numpy array holding the adversarial examples
45+
:rtype: np.ndarray
4446
"""
47+
# TODO Consider computing attack for a batch of samples at a time (no for loop)
4548
# Parse and save attack-specific parameters
4649
assert self.set_params(**kwargs)
4750

48-
return vatm(self.classifier, x, self.classifier._get_predictions(x, log=False), eps=eps,
49-
num_iterations=self.max_iter, xi=self.xi, clip_min=self.clip_min, clip_max=self.clip_max)
51+
x_adv = np.copy(x_val)
52+
dims = [None] + list(x_val.shape[1:])
53+
self._x = tf.placeholder(tf.float32, shape=dims)
54+
dims[0] = 1
55+
self._preds = self.classifier._get_predictions(self._x, log=False)
56+
preds_val = self.sess.run(self._preds, {self._x: x_adv})
57+
58+
for ind, val in enumerate(x_adv):
59+
d = np.random.randn(*dims[1:])
60+
e = np.random.randn(*dims[1:])
61+
for _ in range(self.max_iter):
62+
d = self.finite_diff * self._normalize(d)
63+
e = self.finite_diff * self._normalize(e)
64+
preds_val_d = self.sess.run(self._preds, {self._x: [val + d]})[0]
65+
preds_val_e = self.sess.run(self._preds, {self._x: [val + e]})[0]
66+
67+
# Compute KL divergence between logits
68+
from scipy.stats import entropy
69+
kl_div1 = entropy(preds_val[ind], preds_val_d)
70+
kl_div2 = entropy(preds_val[ind], preds_val_e)
71+
d = (kl_div1 - kl_div2) / np.abs(d - e)
72+
73+
# Apply perturbation and clip
74+
val += self.eps * self._normalize(d)
75+
if self.clip_min is not None or self.clip_max is not None:
76+
val = np.clip(val, self.clip_min, self.clip_max)
5077

51-
def generate(self, x_val, eps=0.1, **kwargs):
78+
return x_adv
79+
80+
def _normalize(self, x):
5281
"""
53-
Generate adversarial samples and return them in a Numpy array.
54-
:param x_val: (required) A Numpy array with the original inputs.
55-
:param eps: (optional float) the epsilon (max input variation parameter)
56-
:param max_iter: (optinal integer) The maximum number of iterations.
57-
:param xi: (optional float) The finite difference parameter
58-
:param clip_min: (optional float) Minimum input component value
59-
:param clip_max: (optional float) Maximum input component value
82+
Apply L_2 batch normalization on `x`.
83+
84+
:param x: (np.ndarray) The input array to normalize
85+
:return: The nornmalized version of `x`
86+
:rtype: np.ndarray
6087
"""
61-
# Generate this attack's graph if it hasn't been done previously
62-
input_shape = list(x_val.shape)
63-
input_shape[0] = None
64-
self._x = tf.placeholder(tf.float32, shape=input_shape)
65-
self._x_adv = self.generate_graph(self._x, eps, **kwargs)
88+
tol = 1e-12
89+
dims = x.shape
6690

67-
return self.sess.run(self._x_adv, feed_dict={self._x: x_val, k.learning_phase(): 0})
91+
x = x.flatten()
92+
x /= np.max(np.abs(x)) + tol
93+
inverse = (np.sum(x**2) + np.sqrt(tol)) ** -.5
94+
x = x * inverse
95+
x = np.reshape(x, dims)
96+
97+
return x
6898

6999
def set_params(self, **kwargs):
70100
"""
71101
Take in a dictionary of parameters and applies attack-specific checks before saving them as attributes.
72102
73103
Attack-specific parameters:
74-
:param max_iter: (optional integer) The maximum number of iterations.
75-
:param xi: (optional float) The finite difference parameter
104+
:param eps: (optional float) the epsilon (max input variation parameter)
105+
:param finite_diff: (optional float) The finite difference parameter
106+
:param max_iter: (optional integer) The maximum number of iterations
76107
:param clip_min: (optional float) Minimum input component value
77108
:param clip_max: (optional float) Maximum input component value
78109
"""

0 commit comments

Comments
 (0)