Skip to content

Commit 93a5839

Browse files
Irina NicolaeIrina Nicolae
authored andcommitted
Integrate preprocesing defences and classifier
1 parent 3cd034a commit 93a5839

File tree

5 files changed

+109
-52
lines changed

5 files changed

+109
-52
lines changed

art/attacks/fast_gradient_unittest.py

Lines changed: 31 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_mnist(self):
6060

6161
def _test_backend_mnist(self, classifier):
6262
# Get MNIST
63-
(x_train, y_train), (x_test, y_test), _, _ = load_mnist()
63+
(x_train, y_train), (x_test, y_test) = self.mnist
6464
x_train, y_train = x_train[:NB_TRAIN], y_train[:NB_TRAIN]
6565
x_test, y_test = x_test[:NB_TEST], y_test[:NB_TEST]
6666

@@ -130,49 +130,36 @@ def _test_backend_mnist(self, classifier):
130130
acc = np.sum(np.argmax(test_y_pred, axis=1) == np.argmax(y_test, axis=1)) / y_test.shape[0]
131131
print('\nAccuracy on adversarial test examples with L2 norm: %.2f%%' % (acc * 100))
132132

133-
# def test_with_preprocessing(self):
134-
#
135-
# session = tf.Session()
136-
# k.set_session(session)
137-
#
138-
# comp_params = {"loss": 'categorical_crossentropy',
139-
# "optimizer": 'adam',
140-
# "metrics": ['accuracy']}
141-
#
142-
# # get MNIST
143-
# batch_size, nb_train, nb_test = 100, 1000, 100
144-
# (X_train, Y_train), (X_test, Y_test), _, _ = load_mnist()
145-
# X_train, Y_train = X_train[:nb_train], Y_train[:nb_train]
146-
# X_test, Y_test = X_test[:nb_test], Y_test[:nb_test]
147-
# im_shape = X_train[0].shape
148-
#
149-
# # get classifier
150-
# classifier = CNN(im_shape, act="relu", defences=["featsqueeze1"])
151-
# classifier.compile(comp_params)
152-
# classifier.fit(X_train, Y_train, epochs=1, batch_size=batch_size)
153-
# scores = classifier.evaluate(X_train, Y_train)
154-
# print("\naccuracy on training set: %.2f%%" % (scores[1] * 100))
155-
# scores = classifier.evaluate(X_test, Y_test)
156-
# print("\naccuracy on test set: %.2f%%" % (scores[1] * 100))
157-
#
158-
# attack = FastGradientMethod(classifier, eps=1)
159-
# X_train_adv = attack.generate(X_train)
160-
# X_test_adv = attack.generate(X_test)
161-
#
162-
# self.assertFalse((X_train == X_train_adv).all())
163-
# self.assertFalse((X_test == X_test_adv).all())
164-
#
165-
# train_y_pred = get_labels_np_array(classifier.predict(X_train_adv))
166-
# test_y_pred = get_labels_np_array(classifier.predict(X_test_adv))
167-
#
168-
# self.assertFalse((Y_train == train_y_pred).all())
169-
# self.assertFalse((Y_test == test_y_pred).all())
170-
#
171-
# scores = classifier.evaluate(X_train_adv, Y_train)
172-
# print('\naccuracy on adversarial train examples: %.2f%%' % (scores[1] * 100))
173-
#
174-
# scores = classifier.evaluate(X_test_adv, Y_test)
175-
# print('\naccuracy on adversarial test examples: %.2f%%' % (scores[1] * 100))
133+
def test_with_defences(self):
134+
# Get MNIST
135+
(x_train, y_train), (x_test, y_test) = self.mnist
136+
x_train, y_train = x_train[:NB_TRAIN], y_train[:NB_TRAIN]
137+
x_test, y_test = x_test[:NB_TEST], y_test[:NB_TEST]
138+
139+
# Get the ready-trained Keras model
140+
model = self.classifier_k._model
141+
classifier = KerasClassifier((0, 1), model, defences='featsqueeze1')
142+
143+
attack = FastGradientMethod(classifier, eps=1)
144+
x_train_adv = attack.generate(x_train)
145+
x_test_adv = attack.generate(x_test)
146+
147+
self.assertFalse((x_train == x_train_adv).all())
148+
self.assertFalse((x_test == x_test_adv).all())
149+
150+
train_y_pred = get_labels_np_array(classifier.predict(x_train_adv))
151+
test_y_pred = get_labels_np_array(classifier.predict(x_test_adv))
152+
153+
self.assertFalse((y_train == train_y_pred).all())
154+
self.assertFalse((y_test == test_y_pred).all())
155+
156+
preds = classifier.predict(x_train_adv)
157+
acc = np.sum(np.argmax(preds, axis=1) == np.argmax(y_train, axis=1)) / y_train.shape[0]
158+
print('\nAccuracy on adversarial train examples with feature squeezing: %.2f%%' % (acc * 100))
159+
160+
preds = classifier.predict(x_test_adv)
161+
acc = np.sum(np.argmax(preds, axis=1) == np.argmax(y_test, axis=1)) / y_test.shape[0]
162+
print('\naccuracy on adversarial test examples: %.2f%%' % (acc * 100))
176163

177164
@staticmethod
178165
def _cnn_mnist_tf(input_shape):

art/classifiers/classifier.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import abc
44
import sys
55

6+
# TODO Add tests for defences on classifier
7+
68
# Ensure compatibility with Python 2 and 3 when using ABCMeta
79
if sys.version_info >= (3, 4):
810
ABC = abc.ABC
@@ -14,7 +16,7 @@ class Classifier(ABC):
1416
"""
1517
Base class for all classifiers.
1618
"""
17-
def __init__(self, clip_values):
19+
def __init__(self, clip_values, defences=None):
1820
"""
1921
Initialize a `Classifier` object.
2022
@@ -23,6 +25,7 @@ def __init__(self, clip_values):
2325
:type clip_values: `tuple`
2426
"""
2527
self._clip_values = clip_values
28+
self._parse_defences(defences)
2629

2730
def predict(self, inputs, logits=False):
2831
"""
@@ -110,3 +113,54 @@ def loss_gradient(self, inputs, labels):
110113
:rtype: `np.ndarray`
111114
"""
112115
raise NotImplementedError
116+
117+
def _parse_defences(self, defences):
118+
self.defences = defences
119+
120+
if defences:
121+
import re
122+
pattern = re.compile("featsqueeze[1-8]?")
123+
124+
for d in defences:
125+
if pattern.match(d):
126+
try:
127+
from art.defences import FeatureSqueezing
128+
129+
bit_depth = int(d[-1])
130+
self.feature_squeeze = FeatureSqueezing(bit_depth=bit_depth)
131+
except:
132+
raise ValueError('You must specify the bit depth for feature squeezing: featsqueeze[1-8]')
133+
134+
# Add label smoothing
135+
if d == 'labsmooth':
136+
from art.defences import LabelSmoothing
137+
self.label_smooth = LabelSmoothing()
138+
139+
# Add spatial smoothing
140+
if d == 'smooth':
141+
from art.defences import SpatialSmoothing
142+
self.smooth = SpatialSmoothing()
143+
144+
def _apply_defences_fit(self, inputs, outputs):
145+
# Apply label smoothing if option is set
146+
if hasattr(self, 'label_smooth'):
147+
_, outputs = self.label_smooth(None, outputs)
148+
else:
149+
outputs = outputs
150+
151+
# Apply feature squeezing if option is set
152+
if hasattr(self, 'feature_squeeze'):
153+
inputs = self.feature_squeeze(inputs)
154+
155+
return inputs, outputs
156+
157+
def _apply_defences_predict(self, inputs):
158+
# Apply feature squeezing if option is set
159+
if hasattr(self, 'feature_squeeze'):
160+
inputs = self.feature_squeeze(inputs)
161+
162+
# Apply inputs smoothing if option is set
163+
if hasattr(self, 'smooth'):
164+
inputs = self.smooth(inputs)
165+
166+
return inputs

art/classifiers/keras.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class KerasClassifier(Classifier):
99
"""
1010
The supported backends for Keras are TensorFlow and Theano.
1111
"""
12-
def __init__(self, clip_values, model, use_logits=False):
12+
def __init__(self, clip_values, model, use_logits=False, defences=None):
1313
"""
1414
Create a `Classifier` instance from a Keras model. Assumes the `model` passed as argument is compiled.
1515
@@ -20,11 +20,13 @@ def __init__(self, clip_values, model, use_logits=False):
2020
:type model: `keras.models.Sequential`
2121
:param use_logits: True if the output of the model are the logits
2222
:type use_logits: `bool`
23+
:param defences: Defences to be activated with the classifier.
24+
:type defences: `str` or `list(str)`
2325
"""
2426
import keras.backend as k
2527

2628
# TODO Generalize loss function?
27-
super(KerasClassifier, self).__init__(clip_values)
29+
super(KerasClassifier, self).__init__(clip_values, defences)
2830

2931
self._model = model
3032
self._input = model.input
@@ -107,8 +109,11 @@ def predict(self, inputs, logits=False):
107109
:rtype: `np.ndarray`
108110
"""
109111
import keras.backend as k
110-
111112
k.set_learning_phase(0)
113+
114+
# Apply defences
115+
inputs = self._apply_defences_predict(inputs)
116+
112117
preds = self._preds([inputs])[0]
113118
if not logits:
114119
exp = np.exp(preds - np.max(preds, axis=1, keepdims=True))
@@ -131,8 +136,11 @@ def fit(self, inputs, outputs, batch_size=128, nb_epochs=20):
131136
:return: `None`
132137
"""
133138
import keras.backend as k
134-
135139
k.set_learning_phase(1)
140+
141+
# Apply defences
142+
inputs, outputs = self._apply_defences_fit(inputs, outputs)
143+
136144
gen = generator(inputs, outputs, batch_size)
137145
self._model.fit_generator(gen, steps_per_epoch=inputs.shape[0] / batch_size, epochs=nb_epochs)
138146

art/classifiers/keras_unittest.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
from keras.layers import Dense, Activation, Flatten, Conv2D, MaxPooling2D, Dropout
77
import numpy as np
88
import unittest
9-
import shutil
109

1110
from art.classifiers import KerasClassifier
12-
from art.utils import load_mnist, make_directory
11+
from art.utils import load_mnist
1312

1413
BATCH_SIZE = 10
1514
NB_TRAIN = 500

art/classifiers/tensorflow.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class TFClassifier(Classifier):
1010
"""
1111
This class implements a classifier with the Tensorflow framework.
1212
"""
13-
def __init__(self, clip_values, input_ph, logits, output_ph=None, train=None, loss=None, learning=None, sess=None):
13+
def __init__(self, clip_values, input_ph, logits, output_ph=None, train=None, loss=None, learning=None, sess=None,
14+
defences=None):
1415
"""
1516
Initialization specifically for the Tensorflow-based implementation.
1617
@@ -32,6 +33,8 @@ def __init__(self, clip_values, input_ph, logits, output_ph=None, train=None, lo
3233
:type learning: `tf.Placeholder` of type bool.
3334
:param sess: Computation session.
3435
:type sess: `tf.Session`
36+
:param defences: Defences to be activated with the classifier.
37+
:type defences: `str` or `list(str)`
3538
"""
3639
import tensorflow as tf
3740

@@ -73,6 +76,9 @@ def predict(self, inputs, logits=False):
7376
"""
7477
import tensorflow as tf
7578

79+
# Apply defences
80+
inputs = self._apply_defences_predict(inputs)
81+
7682
# Create feed_dict
7783
fd = {self._input_ph: inputs}
7884
if self._learning is not None:
@@ -104,6 +110,9 @@ def fit(self, inputs, outputs, batch_size=128, nb_epochs=10):
104110
if self._train is None or self._output_ph is None:
105111
raise ValueError("Need the training objective and the output placeholder to train the model.")
106112

113+
# Apply defences
114+
inputs, outputs = self._apply_defences_fit(inputs, outputs)
115+
107116
num_batch = int(np.ceil(len(inputs) / batch_size))
108117
ind = np.arange(len(inputs))
109118

0 commit comments

Comments
 (0)