This repository was archived by the owner on Mar 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 70
/
Copy pathdeepfool.py
213 lines (161 loc) · 6.72 KB
/
deepfool.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import tensorflow as tf
__all__ = ['deepfool']
def deepfool(model, x, noise=False, eta=0.01, epochs=3, batch=False,
clip_min=0.0, clip_max=1.0, min_prob=0.0):
"""DeepFool implementation in Tensorflow.
The original DeepFool will stop whenever we successfully cross the
decision boundary. Thus it might not run total epochs. In order to force
DeepFool to run full epochs, you could set batch=True. In that case the
DeepFool will run until the max epochs is reached regardless whether we
cross the boundary or not. See https://arxiv.org/abs/1511.04599 for
details.
:param model: Model function.
:param x: 2D or 4D input tensor.
:param noise: Also return the noise if True.
:param eta: Small overshoot value to cross the boundary.
:param epochs: Maximum epochs to run.
:param batch: If True, run in batch mode, will always run epochs.
:param clip_min: Min clip value for output.
:param clip_max: Max clip value for output.
:param min_prob: Minimum probability for adversarial samples.
:return: Adversarials, of the same shape as x.
"""
y = tf.stop_gradient(model(x))
fns = [[_deepfool2, _deepfool2_batch], [_deepfoolx, _deepfoolx_batch]]
i = int(y.get_shape().as_list()[1] > 1)
j = int(batch)
fn = fns[i][j]
if batch:
delta = fn(model, x, eta=eta, epochs=epochs, clip_min=clip_min,
clip_max=clip_max)
else:
def _f(xi):
xi = tf.expand_dims(xi, axis=0)
z = fn(model, xi, eta=eta, epochs=epochs, clip_min=clip_min,
clip_max=clip_max, min_prob=min_prob)
return z[0]
delta = tf.map_fn(_f, x, dtype=(tf.float32), back_prop=False,
name='deepfool')
if noise:
return delta
xadv = tf.stop_gradient(x + delta*(1+eta))
xadv = tf.clip_by_value(xadv, clip_min, clip_max)
return xadv
def _prod(iterable):
ret = 1
for x in iterable:
ret *= x
return ret
def _deepfool2(model, x, epochs, eta, clip_min, clip_max, min_prob):
"""DeepFool for binary classifiers.
Note that DeepFools that binary classifier outputs +1/-1 instead of 0/1.
"""
y0 = tf.stop_gradient(tf.reshape(model(x), [-1])[0])
y0 = tf.to_int32(tf.greater(y0, 0.0))
def _cond(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = tf.stop_gradient(tf.reshape(model(xadv), [-1])[0])
y = tf.to_int32(tf.greater(y, 0.0))
return tf.logical_and(tf.less(i, epochs), tf.equal(y0, y))
def _body(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = tf.reshape(model(xadv), [-1])[0]
g = tf.gradients(y, xadv)[0]
dx = - y * g / (tf.norm(g) + 1e-10) # off by a factor of 1/norm(g)
return i+1, z+dx
_, noise = tf.while_loop(_cond, _body, [0, tf.zeros_like(x)],
name='_deepfool2', back_prop=False)
return noise
def _deepfool2_batch(model, x, epochs, eta, clip_min, clip_max):
"""DeepFool for binary classifiers in batch mode.
"""
xshape = x.get_shape().as_list()[1:]
dim = _prod(xshape)
def _cond(i, z):
return tf.less(i, epochs)
def _body(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = tf.reshape(model(xadv), [-1])
g = tf.gradients(y, xadv)[0]
n = tf.norm(tf.reshape(g, [-1, dim]), axis=1) + 1e-10
d = tf.reshape(-y / n, [-1] + [1]*len(xshape))
dx = g * d
return i+1, z+dx
_, noise = tf.while_loop(_cond, _body, [0, tf.zeros_like(x)],
name='_deepfool2_batch', back_prop=False)
return noise
def _deepfoolx(model, x, epochs, eta, clip_min, clip_max, min_prob):
"""DeepFool for multi-class classifiers.
Assumes that the final label is the label with the maximum values.
"""
y0 = tf.stop_gradient(model(x))
y0 = tf.reshape(y0, [-1])
k0 = tf.argmax(y0)
ydim = y0.get_shape().as_list()[0]
xdim = x.get_shape().as_list()[1:]
xflat = _prod(xdim)
def _cond(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = tf.reshape(model(xadv), [-1])
p = tf.reduce_max(y)
k = tf.argmax(y)
return tf.logical_and(tf.less(i, epochs),
tf.logical_or(tf.equal(k0, k),
tf.less(p, min_prob)))
def _body(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = tf.reshape(model(xadv), [-1])
gs = [tf.reshape(tf.gradients(y[i], xadv)[0], [-1])
for i in range(ydim)]
g = tf.stack(gs, axis=0)
yk, yo = y[k0], tf.concat((y[:k0], y[(k0+1):]), axis=0)
gk, go = g[k0], tf.concat((g[:k0], g[(k0+1):]), axis=0)
yo.set_shape(ydim - 1)
go.set_shape([ydim - 1, xflat])
a = tf.abs(yo - yk)
b = go - gk
c = tf.norm(b, axis=1)
score = a / c
ind = tf.argmin(score)
si, bi = score[ind], b[ind]
dx = si * bi
dx = tf.reshape(dx, [-1] + xdim)
return i+1, z+dx
_, noise = tf.while_loop(_cond, _body, [0, tf.zeros_like(x)],
name='_deepfoolx', back_prop=False)
return noise
def _deepfoolx_batch(model, x, epochs, eta, clip_min, clip_max):
"""DeepFool for multi-class classifiers in batch mode.
"""
y0 = tf.stop_gradient(model(x))
B, ydim = tf.shape(y0)[0], y0.get_shape().as_list()[1]
k0 = tf.argmax(y0, axis=1, output_type=tf.int32)
k0 = tf.stack((tf.range(B), k0), axis=1)
xshape = x.get_shape().as_list()[1:]
xdim = _prod(xshape)
perm = list(range(len(xshape) + 2))
perm[0], perm[1] = perm[1], perm[0]
def _cond(i, z):
return tf.less(i, epochs)
def _body(i, z):
xadv = tf.clip_by_value(x + z*(1+eta), clip_min, clip_max)
y = model(xadv)
gs = [tf.gradients(y[:, i], xadv)[0] for i in range(ydim)]
g = tf.stack(gs, axis=0)
g = tf.transpose(g, perm)
yk = tf.expand_dims(tf.gather_nd(y, k0), axis=1)
gk = tf.expand_dims(tf.gather_nd(g, k0), axis=1)
a = tf.abs(y - yk)
b = g - gk
c = tf.norm(tf.reshape(b, [-1, ydim, xdim]), axis=-1)
# Assume 1) 0/0=tf.nan 2) tf.argmin ignores nan
score = a / c
ind = tf.argmin(score, axis=1, output_type=tf.int32)
ind = tf.stack((tf.range(B), ind), axis=1)
si, bi = tf.gather_nd(score, ind), tf.gather_nd(b, ind)
si = tf.reshape(si, [-1] + [1]*len(xshape))
dx = si * bi
return i+1, z+dx
_, noise = tf.while_loop(_cond, _body, [0, tf.zeros_like(x)],
name='_deepfoolx_batch', back_prop=False)
return noise