Skip to content

Commit 258d714

Browse files
Irina NicolaeIrina Nicolae
authored andcommitted
Merge PyTorch backend
Also: - rename inputs to classifier - correct bug in CW attack - add clipping in NewtonFool - update docs in preprocessing defences
2 parents a9c7531 + c7e0c1e commit 258d714

20 files changed

+873
-162
lines changed

art/attacks/carlini.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
class CarliniL2Method(Attack):
1313
"""
1414
The L_2 optimized attack of Carlini and Wagner (2016). This attack is the most efficient and should be used as the
15-
primary attack to evaluate potential defenses (wrt the L_0 and L_inf attacks). This implementation is inspired by
15+
primary attack to evaluate potential defences (wrt the L_0 and L_inf attacks). This implementation is inspired by
1616
the one in Cleverhans, which reproduces the authors' original code (https://github.com/carlini/nn_robust_attacks).
1717
Paper link: https://arxiv.org/pdf/1608.04644.pdf
1818
"""
@@ -160,7 +160,7 @@ def generate(self, x, **kwargs):
160160

161161
# No labels provided, use model prediction as correct class
162162
if y is None:
163-
y = np.argmax(self.classifier.predict(inputs=x, logits=False), axis=1)
163+
y = np.argmax(self.classifier.predict(x, logits=False), axis=1)
164164
y = to_categorical(y, self.classifier.nb_classes)
165165

166166
# Images to be attacked:
@@ -240,7 +240,11 @@ def generate(self, x, **kwargs):
240240
# Abort binary search if c exceeds upper bound:
241241
if c > self._c_upper_bound:
242242
break
243-
243+
244+
# Transform best_adv_image back into tanh space if attack is failed
245+
if (best_adv_image == ex).all():
246+
best_adv_image = (np.tanh(best_adv_image) / self._tanh_smoother + 1) / 2
247+
244248
x_adv[j] = best_adv_image
245249

246250
return x_adv
@@ -270,10 +274,10 @@ def set_params(self, **kwargs):
270274
# Save attack-specific parameters
271275
super(CarliniL2Method, self).set_params(**kwargs)
272276

273-
if type(self.binary_search_steps) is not int or self.binary_search_steps <= 0:
274-
raise ValueError("The number of binary search steps must be a positive integer.")
277+
if type(self.binary_search_steps) is not int or self.binary_search_steps < 0:
278+
raise ValueError("The number of binary search steps must be a non-negative integer.")
275279

276-
if type(self.max_iter) is not int or self.max_iter <= 0:
277-
raise ValueError("The number of iterations must be a positive integer.")
280+
if type(self.max_iter) is not int or self.max_iter < 0:
281+
raise ValueError("The number of iterations must be a non-negative integer.")
278282

279283
return True

art/attacks/carlini_unittest.py

Lines changed: 149 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,84 @@
77
import keras.backend as k
88
from keras.models import Sequential
99
from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D
10+
import torch.nn as nn
11+
import torch.nn.functional as F
12+
import torch.optim as optim
1013

1114
from art.attacks.carlini import CarliniL2Method
1215
from art.classifiers.tensorflow import TFClassifier
1316
from art.classifiers.keras import KerasClassifier
17+
from art.classifiers.pytorch import PyTorchClassifier
1418
from art.utils import load_mnist, random_targets
1519

1620

21+
class Model(nn.Module):
22+
def __init__(self):
23+
super(Model, self).__init__()
24+
self.conv = nn.Conv2d(1, 16, 5)
25+
self.pool = nn.MaxPool2d(2, 2)
26+
self.fc = nn.Linear(2304, 10)
27+
28+
def forward(self, x):
29+
x = self.pool(F.relu(self.conv(x)))
30+
x = x.view(-1, 2304)
31+
logit_output = self.fc(x)
32+
output = F.softmax(logit_output, dim=1)
33+
34+
return logit_output, output
35+
36+
1737
class TestCarliniL2(unittest.TestCase):
1838
"""
1939
A unittest class for testing the Carlini2 attack.
2040
"""
41+
def test_failure_attack(self):
42+
"""
43+
Test the corner case when attack is failed.
44+
:return:
45+
"""
46+
# Build a TFClassifier
47+
# Define input and output placeholders
48+
self._input_ph = tf.placeholder(tf.float32, shape=[None, 28, 28, 1])
49+
self._output_ph = tf.placeholder(tf.int32, shape=[None, 10])
50+
51+
# Define the tensorflow graph
52+
conv = tf.layers.conv2d(self._input_ph, 4, 5, activation=tf.nn.relu)
53+
conv = tf.layers.max_pooling2d(conv, 2, 2)
54+
fc = tf.contrib.layers.flatten(conv)
55+
56+
# Logits layer
57+
self._logits = tf.layers.dense(fc, 10)
58+
59+
# Train operator
60+
self._loss = tf.reduce_mean(tf.losses.softmax_cross_entropy(logits=self._logits, onehot_labels=self._output_ph))
61+
optimizer = tf.train.AdamOptimizer(learning_rate=0.01)
62+
self._train = optimizer.minimize(self._loss)
63+
64+
# Tensorflow session and initialization
65+
self._sess = tf.Session()
66+
self._sess.run(tf.global_variables_initializer())
67+
68+
# Get MNIST
69+
batch_size, nb_train, nb_test = 100, 1000, 10
70+
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
71+
x_train, y_train = x_train[:nb_train], y_train[:nb_train]
72+
x_test, y_test = x_test[:nb_test], y_test[:nb_test]
73+
74+
# Train the classifier
75+
tfc = TFClassifier((0, 1), self._input_ph, self._logits, self._output_ph,
76+
self._train, self._loss, None, self._sess)
77+
tfc.fit(x_train, y_train, batch_size=batch_size, nb_epochs=2)
78+
79+
# Failure attack
80+
cl2m = CarliniL2Method(classifier=tfc, targeted=True, max_iter=0, binary_search_steps=0,
81+
learning_rate=2e-2, initial_const=3, decay=1e-2)
82+
params = {'y': random_targets(y_test, tfc.nb_classes)}
83+
x_test_adv = cl2m.generate(x_test, **params)
84+
self.assertTrue((x_test_adv <= 1.0001 ).all())
85+
self.assertTrue((x_test_adv >= -0.0001 ).all())
86+
np.testing.assert_almost_equal(x_test, x_test_adv, 3)
87+
2188
def test_tfclassifier(self):
2289
"""
2390
First test with the TFClassifier.
@@ -46,7 +113,7 @@ def test_tfclassifier(self):
46113
self._sess.run(tf.global_variables_initializer())
47114

48115
# Get MNIST
49-
batch_size, nb_train, nb_test = 100, 1000, 10
116+
batch_size, nb_train, nb_test = 100, 500, 5
50117
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
51118
x_train, y_train = x_train[:nb_train], y_train[:nb_train]
52119
x_test, y_test = x_test[:nb_test], y_test[:nb_test]
@@ -57,31 +124,38 @@ def test_tfclassifier(self):
57124
tfc.fit(x_train, y_train, batch_size=batch_size, nb_epochs=2)
58125

59126
# First attack
60-
cl2m = CarliniL2Method(classifier=tfc, targeted=True, max_iter=100, binary_search_steps=10,
127+
cl2m = CarliniL2Method(classifier=tfc, targeted=True, max_iter=10, binary_search_steps=10,
61128
learning_rate=2e-2, initial_const=3, decay=1e-2)
62129
params = {'y': random_targets(y_test, tfc.nb_classes)}
63130
x_test_adv = cl2m.generate(x_test, **params)
64131
self.assertFalse((x_test == x_test_adv).all())
132+
#print(x_test_adv)
133+
self.assertTrue((x_test_adv <= 1.0001).all())
134+
self.assertTrue((x_test_adv >= -0.0001).all())
65135
target = np.argmax(params['y'], axis=1)
66136
y_pred_adv = np.argmax(tfc.predict(x_test_adv), axis=1)
67137
self.assertTrue((target == y_pred_adv).all())
68138

69139
# Second attack
70-
cl2m = CarliniL2Method(classifier=tfc, targeted=False, max_iter=100, binary_search_steps=10,
140+
cl2m = CarliniL2Method(classifier=tfc, targeted=False, max_iter=10, binary_search_steps=10,
71141
learning_rate=2e-2, initial_const=3, decay=1e-2)
72142
params = {'y': random_targets(y_test, tfc.nb_classes)}
73143
x_test_adv = cl2m.generate(x_test, **params)
74144
self.assertFalse((x_test == x_test_adv).all())
145+
self.assertTrue((x_test_adv <= 1.0001).all())
146+
self.assertTrue((x_test_adv >= -0.0001).all())
75147
target = np.argmax(params['y'], axis=1)
76148
y_pred_adv = np.argmax(tfc.predict(x_test_adv), axis=1)
77149
self.assertTrue((target != y_pred_adv).all())
78150

79151
# Third attack
80-
cl2m = CarliniL2Method(classifier=tfc, targeted=False, max_iter=100, binary_search_steps=10,
152+
cl2m = CarliniL2Method(classifier=tfc, targeted=False, max_iter=10, binary_search_steps=10,
81153
learning_rate=2e-2, initial_const=3, decay=1e-2)
82154
params = {}
83155
x_test_adv = cl2m.generate(x_test, **params)
84156
self.assertFalse((x_test == x_test_adv).all())
157+
self.assertTrue((x_test_adv <= 1.0001).all())
158+
self.assertTrue((x_test_adv >= -0.0001).all())
85159
y_pred = np.argmax(tfc.predict(x_test), axis=1)
86160
y_pred_adv = np.argmax(tfc.predict(x_test_adv), axis=1)
87161
self.assertTrue((y_pred != y_pred_adv).all())
@@ -96,7 +170,7 @@ def test_krclassifier(self):
96170
k.set_session(session)
97171

98172
# Get MNIST
99-
batch_size, nb_train, nb_test = 100, 1000, 10
173+
batch_size, nb_train, nb_test = 100, 500, 5
100174
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
101175
x_train, y_train = x_train[:nb_train], y_train[:nb_train]
102176
x_test, y_test = x_test[:nb_test], y_test[:nb_test]
@@ -116,35 +190,102 @@ def test_krclassifier(self):
116190
krc.fit(x_train, y_train, batch_size=batch_size, nb_epochs=2)
117191

118192
# First attack
119-
cl2m = CarliniL2Method(classifier=krc, targeted=True, max_iter=100, binary_search_steps=10,
193+
cl2m = CarliniL2Method(classifier=krc, targeted=True, max_iter=10, binary_search_steps=10,
120194
learning_rate=2e-2, initial_const=3, decay=1e-2)
121195
params = {'y': random_targets(y_test, krc.nb_classes)}
122196
x_test_adv = cl2m.generate(x_test, **params)
123197
self.assertFalse((x_test == x_test_adv).all())
198+
self.assertTrue((x_test_adv <= 1.0001).all())
199+
self.assertTrue((x_test_adv >= -0.0001).all())
124200
target = np.argmax(params['y'], axis=1)
125201
y_pred_adv = np.argmax(krc.predict(x_test_adv), axis=1)
126202
self.assertTrue((target == y_pred_adv).any())
127203

128204
# Second attack
129-
cl2m = CarliniL2Method(classifier=krc, targeted=False, max_iter=100, binary_search_steps=10,
205+
cl2m = CarliniL2Method(classifier=krc, targeted=False, max_iter=10, binary_search_steps=10,
130206
learning_rate=2e-2, initial_const=3, decay=1e-2)
131207
params = {'y': random_targets(y_test, krc.nb_classes)}
132208
x_test_adv = cl2m.generate(x_test, **params)
133209
self.assertFalse((x_test == x_test_adv).all())
210+
self.assertTrue((x_test_adv <= 1.0001).all())
211+
self.assertTrue((x_test_adv >= -0.0001).all())
134212
target = np.argmax(params['y'], axis=1)
135213
y_pred_adv = np.argmax(krc.predict(x_test_adv), axis=1)
136214
self.assertTrue((target != y_pred_adv).all())
137215

138216
# Third attack
139-
cl2m = CarliniL2Method(classifier=krc, targeted=False, max_iter=100, binary_search_steps=10,
217+
cl2m = CarliniL2Method(classifier=krc, targeted=False, max_iter=10, binary_search_steps=10,
140218
learning_rate=2e-2, initial_const=3, decay=1e-2)
141219
params = {}
142220
x_test_adv = cl2m.generate(x_test, **params)
143221
self.assertFalse((x_test == x_test_adv).all())
222+
self.assertTrue((x_test_adv <= 1.0001).all())
223+
self.assertTrue((x_test_adv >= -0.0001).all())
144224
y_pred = np.argmax(krc.predict(x_test), axis=1)
145225
y_pred_adv = np.argmax(krc.predict(x_test_adv), axis=1)
146226
self.assertTrue((y_pred != y_pred_adv).any())
147227

228+
def test_ptclassifier(self):
229+
"""
230+
Third test with the PyTorchClassifier.
231+
:return:
232+
"""
233+
# Get MNIST
234+
batch_size, nb_train, nb_test = 100, 1000, 10
235+
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
236+
x_train, y_train = x_train[:nb_train], np.argmax(y_train[:nb_train], axis=1)
237+
x_test, y_test = x_test[:nb_test], y_test[:nb_test]
238+
x_train = np.swapaxes(x_train, 1, 3)
239+
x_test = np.swapaxes(x_test, 1, 3)
240+
241+
# Create simple CNN
242+
# Define the network
243+
model = Model()
244+
245+
# Define a loss function and optimizer
246+
loss_fn = nn.CrossEntropyLoss()
247+
optimizer = optim.Adam(model.parameters(), lr=0.01)
248+
249+
# Get classifier
250+
ptc = PyTorchClassifier((0, 1), model, loss_fn, optimizer, (1, 28, 28), (10,))
251+
ptc.fit(x_train, y_train, batch_size=batch_size, nb_epochs=1)
252+
253+
# First attack
254+
cl2m = CarliniL2Method(classifier=ptc, targeted=True, max_iter=100, binary_search_steps=10,
255+
learning_rate=2e-2, initial_const=3, decay=1e-2)
256+
params = {'y': random_targets(y_test, ptc.nb_classes)}
257+
x_test_adv = cl2m.generate(x_test, **params)
258+
self.assertFalse((x_test == x_test_adv).all())
259+
self.assertTrue((x_test_adv <= 1.0001).all())
260+
self.assertTrue((x_test_adv >= -0.0001).all())
261+
target = np.argmax(params['y'], axis=1)
262+
y_pred_adv = np.argmax(ptc.predict(x_test_adv), axis=1)
263+
self.assertTrue((target == y_pred_adv).any())
264+
265+
# Second attack
266+
cl2m = CarliniL2Method(classifier=ptc, targeted=False, max_iter=100, binary_search_steps=10,
267+
learning_rate=2e-2, initial_const=3, decay=1e-2)
268+
params = {'y': random_targets(y_test, ptc.nb_classes)}
269+
x_test_adv = cl2m.generate(x_test, **params)
270+
self.assertFalse((x_test == x_test_adv).all())
271+
self.assertTrue((x_test_adv <= 1.0001).all())
272+
self.assertTrue((x_test_adv >= -0.0001).all())
273+
target = np.argmax(params['y'], axis=1)
274+
y_pred_adv = np.argmax(ptc.predict(x_test_adv), axis=1)
275+
self.assertTrue((target != y_pred_adv).all())
276+
277+
# Third attack
278+
cl2m = CarliniL2Method(classifier=ptc, targeted=False, max_iter=100, binary_search_steps=10,
279+
learning_rate=2e-2, initial_const=3, decay=1e-2)
280+
params = {}
281+
x_test_adv = cl2m.generate(x_test, **params)
282+
self.assertFalse((x_test == x_test_adv).all())
283+
self.assertTrue((x_test_adv <= 1.0001).all())
284+
self.assertTrue((x_test_adv >= -0.0001).all())
285+
y_pred = np.argmax(ptc.predict(x_test), axis=1)
286+
y_pred_adv = np.argmax(ptc.predict(x_test_adv), axis=1)
287+
self.assertTrue((y_pred != y_pred_adv).any())
288+
148289

149290
if __name__ == '__main__':
150291
unittest.main()

art/attacks/newtonfool.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def generate(self, x, **kwargs):
4242
x_adv = x.copy()
4343

4444
# Initialize variables
45+
clip_min, clip_max = self.classifier.clip_values
4546
y_pred = self.classifier.predict(x, logits=False)
4647
pred_class = np.argmax(y_pred, axis=1)
4748

@@ -68,6 +69,9 @@ def generate(self, x, **kwargs):
6869
# Update xi and pertubation
6970
ex += di
7071

72+
# Apply clip
73+
x_adv[j] = np.clip(ex, clip_min, clip_max)
74+
7175
return x_adv
7276

7377
def set_params(self, **kwargs):

art/attacks/newtonfool_unittest.py

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,34 @@
66
from keras.models import Sequential
77
from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D
88
import tensorflow as tf
9+
import numpy as np
10+
import torch.nn as nn
11+
import torch.nn.functional as F
12+
import torch.optim as optim
913

1014
from art.attacks.newtonfool import NewtonFool
1115
from art.classifiers.tensorflow import TFClassifier
1216
from art.classifiers.keras import KerasClassifier
17+
from art.classifiers.pytorch import PyTorchClassifier
1318
from art.utils import load_mnist
1419

1520

21+
class Model(nn.Module):
22+
def __init__(self):
23+
super(Model, self).__init__()
24+
self.conv = nn.Conv2d(1, 16, 5)
25+
self.pool = nn.MaxPool2d(2, 2)
26+
self.fc = nn.Linear(2304, 10)
27+
28+
def forward(self, x):
29+
x = self.pool(F.relu(self.conv(x)))
30+
x = x.view(-1, 2304)
31+
logit_output = self.fc(x)
32+
output = F.softmax(logit_output, dim=1)
33+
34+
return logit_output, output
35+
36+
1637
class TestNewtonFool(unittest.TestCase):
1738
"""
1839
A unittest class for testing the NewtonFool attack.
@@ -106,8 +127,44 @@ def test_krclassifier(self):
106127
y_pred_adv_max = y_pred_adv[y_pred_bool]
107128
self.assertTrue((y_pred_max >= y_pred_adv_max).all())
108129

130+
def test_ptclassifier(self):
131+
"""
132+
Third test with the PyTorchClassifier.
133+
:return:
134+
"""
135+
# Get MNIST
136+
batch_size, nb_train, nb_test = 100, 1000, 10
137+
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
138+
x_train, y_train = x_train[:nb_train], np.argmax(y_train[:nb_train], axis=1)
139+
x_test, y_test = x_test[:nb_test], np.argmax(y_test[:nb_test], axis=1)
140+
x_train = np.swapaxes(x_train, 1, 3)
141+
x_test = np.swapaxes(x_test, 1, 3)
142+
143+
# Create simple CNN
144+
# Define the network
145+
model = Model()
109146

110-
if __name__ == '__main__':
111-
unittest.main()
147+
# Define a loss function and optimizer
148+
loss_fn = nn.CrossEntropyLoss()
149+
optimizer = optim.Adam(model.parameters(), lr=0.01)
112150

151+
# Get classifier
152+
ptc = PyTorchClassifier((0, 1), model, loss_fn, optimizer, (1, 28, 28), (10,))
153+
ptc.fit(x_train, y_train, batch_size=batch_size, nb_epochs=1)
154+
155+
# Attack
156+
nf = NewtonFool(ptc)
157+
nf.set_params(max_iter=5)
158+
x_test_adv = nf.generate(x_test)
159+
self.assertFalse((x_test == x_test_adv).all())
160+
161+
y_pred = ptc.predict(x_test)
162+
y_pred_adv = ptc.predict(x_test_adv)
163+
y_pred_bool = y_pred.max(axis=1, keepdims=1) == y_pred
164+
y_pred_max = y_pred.max(axis=1)
165+
y_pred_adv_max = y_pred_adv[y_pred_bool]
166+
self.assertTrue((y_pred_max >= y_pred_adv_max).all())
113167

168+
169+
if __name__ == '__main__':
170+
unittest.main()

0 commit comments

Comments
 (0)