Skip to content

Commit 6713332

Browse files
Irina NicolaeIrina Nicolae
authored andcommitted
Merge from dev
2 parents 3aba2c4 + d164140 commit 6713332

File tree

11 files changed

+200
-81
lines changed

11 files changed

+200
-81
lines changed

.travis.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
sudo: required
2+
dist: trusty
3+
language: python
4+
matrix:
5+
include:
6+
- python: 2.7
7+
env:
8+
- KERAS_BACKEND=tensorflow
9+
- TENSORFLOW_V=1.4.1
10+
- python: 2.7
11+
env:
12+
- KERAS_BACKEND=tensorflow
13+
- TENSORFLOW_V=1.1.0
14+
- python: 3.5
15+
env:
16+
- KERAS_BACKEND=tensorflow
17+
- TENSORFLOW_V=1.1.0
18+
- python: 3.5
19+
env:
20+
- KERAS_BACKEND=tensorflow
21+
- TENSORFLOW_V=1.4.1
22+
23+
before_install:
24+
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
25+
wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh -O miniconda.sh;
26+
else
27+
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
28+
fi
29+
- bash miniconda.sh -b -p $HOME/miniconda
30+
- export PATH="$HOME/miniconda/bin:$PATH"
31+
- hash -r
32+
- conda config --set always_yes yes --set changeps1 no
33+
- conda update -q conda
34+
- conda info -a
35+
36+
install:
37+
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib pandas h5py
38+
- source activate test-environment
39+
# Install TensorFlow
40+
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" && "$TENSORFLOW_V" == "1.4.1" ]]; then
41+
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.4.1-cp27-none-linux_x86_64.whl;
42+
elif [[ "$TRAVIS_PYTHON_VERSION" == "2.7" && "$TENSORFLOW_V" == "1.1.0" ]]; then
43+
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.1.0-cp27-none-linux_x86_64.whl;
44+
elif [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TENSORFLOW_V" == "1.4.1" ]]; then
45+
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.4.1-cp35-cp35m-linux_x86_64.whl;
46+
elif [[ "$TRAVIS_PYTHON_VERSION" == "3.5" && "$TENSORFLOW_V" == "1.1.0" ]]; then
47+
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.1.0-cp35-cp35m-linux_x86_64.whl;
48+
fi
49+
- pip install keras
50+
- conda install libgcc
51+
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/travis/miniconda/envs/test-environment/lib
52+
- export PYTHONPATH=".":$PYTHONPATH
53+
54+
script:
55+
- mkdir ./data
56+
- python -m unittest discover src/ -p '*_unittest.py'

README.md

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Nemesis (v1.0.0)
1+
# Nemesis (v0.1)
2+
[![Build Status](https://travis.ibm.com/Maria-Irina-Nicolae/nemesis.svg?token=gRzs7KGtxQXDzQo1SRTx&branch=master)](https://travis.ibm.com/Maria-Irina-Nicolae/nemesis)
3+
24
This is a library dedicated to **adversarial machine learning**. Its purpose is to allow rapid crafting and analysis of attacks and defense methods for machine learning models. Nemesis provides an implementation for many state-of-the-art methods for attacking and defending classifiers.
35

46
The library is still under development. Feedback, bug reports and extension requests are highly appreciated.
@@ -12,51 +14,47 @@ Nemesis contains implementations of the following attacks:
1214
* Universal Perturbation ([Moosavi-Dezfooli et al., 2016](https://arxiv.org/abs/1610.08401))
1315
* Virtual Adversarial Method ([Moosavi-Dezfooli et al., 2015](https://arxiv.org/abs/1507.00677))
1416
* C&W Attack ([Carlini and Wagner, 2016](https://arxiv.org/abs/1608.04644))
17+
* NewtonFool ([Jang et al., 2017](http://doi.acm.org/10.1145/3134600.3134635))
1518

1619
The following defense methods are also supported:
1720
* Feature squeezing ([Xu et al., 2017](http://arxiv.org/abs/1704.01155))
21+
* Spatial smoothing ([Xu et al., 2017](http://arxiv.org/abs/1704.01155))
1822
* Label smoothing (Warde-Farley and Goodfellow, 2016)
1923
* Adversarial training ([Szegedy et al., 2013](http://arxiv.org/abs/1312.6199))
2024
* Virtual adversarial training ([Miyato et al., 2017](https://arxiv.org/abs/1704.03976))
2125

2226
## Setup
2327

24-
### Requirements
25-
2628
Nemesis is designed to run with Python 3 (and most likely Python 2 with small changes). You can either download the source code of Nemesis or clone the repository in your directory of choice:
2729
```bash
2830
git clone https://github.ibm.com/Maria-Irina-Nicolae/nemesis
2931
```
3032

3133
To install the project dependencies, use the requirements file:
3234
```bash
33-
pip install -r requirements.txt
35+
pip install .
3436
```
3537

36-
You will additionally need to download [Cleverhans](https://github.com/tensorflow/cleverhans).
37-
38-
### Installation
38+
The library comes with a basic set of unit tests. To check your install, you can run all the unit tests by calling in the Nemesis folder:
39+
```bash
40+
bash run_tests.sh
41+
```
3942

40-
Nemesis is linked against Cleverhans through the configuration file `config/config.ini`. When installing Nemesis on your local machine, you need to set the appropriate paths and the `LOCAL` configuration profile as follows:
43+
The configuration file `config/config.ini` allows to set custom paths for data. By default, data is downloaded in the `nemesis/data` folder as follows:
4144

4245
```text
4346
[DEFAULT]
4447
profile=LOCAL
4548
4649
[LOCAL]
47-
data_path=/local/path/here
48-
mnist_path=/local/path/here
49-
cifar10_path=/local/path/here
50-
stl10_path=/local/path/here
51-
cleverhans_path=/local/path/here
50+
data_path=./data
51+
mnist_path=./data/mnist
52+
cifar10_path=./data/cifar-10
53+
stl10_path=./data/stl-10
5254
```
5355

5456
If the datasets are not present at the indicated path, loading them will also download the data.
5557

56-
The library comes with a basic set of unit tests. To check that the installation has succeeded, you can run all the unit tests by calling in the Nemesis folder:
57-
```bash
58-
bash run_tests.sh
59-
```
6058

6159
## Running Nemesis
6260

@@ -66,11 +64,11 @@ The library contains three main scripts for:
6664
* testing model accuracy on different test sets using (`test_accuracies.py`)
6765

6866
Detailed instructions for each script are available by typing
69-
```python
67+
```bash
7068
python3 <script_name> -h
7169
```
7270

7371
Some examples of how to use Nemesis when writing your own code can be found in the `examples` folder. See `examples/README.md` for more information about what each example does. To run an example, use the following command:
7472
```bash
7573
python3 examples/<example_name>.py
76-
```
74+
```

config/__init__.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,50 @@
33
from os.path import abspath, dirname, expanduser, join
44
import sys
55

6-
import configparser
6+
# Ensure compatibility with Python 2 and 3 when using ConfigParser
7+
if sys.version_info >= (3, 0):
8+
import configparser as cp
9+
else:
10+
import ConfigParser as cp
11+
12+
13+
def config_to_dict(section):
14+
dict = {}
15+
options = parser.options(section)
16+
for option in options:
17+
try:
18+
dict[option] = parser.get(section, option)
19+
except:
20+
print('Exception on configuration option %s!' % option)
21+
dict[option] = None
22+
return dict
23+
724

825
__all__ = ['config_dict', 'MNIST_PATH', 'CIFAR10_PATH', 'IMAGENET_PATH', 'STL10_PATH', 'DATA_PATH']
926

1027
config_file = join(dirname(__file__), 'config.ini')
11-
parser = configparser.ConfigParser()
12-
with open(config_file, mode='rt') as f:
13-
parser.read_file(f)
28+
parser = cp.ConfigParser()
29+
if sys.version_info >= (3, 0):
30+
with open(config_file, mode='rt') as f:
31+
parser.read_file(f)
32+
else:
33+
parser.read(config_file)
1434

1535
# Generate config dictionary
1636
config_dict = dict()
17-
config_dict.update(parser['DEFAULT'])
37+
if sys.version_info >= (3, 0):
38+
config_dict.update(parser['DEFAULT'])
39+
else:
40+
config_dict['profile'] = parser.get('DEFAULT', 'profile')
1841

19-
profile = config_dict['profile'] = config_dict.get('profile', 'profile1')
42+
profile = config_dict['profile'] = config_dict.get('profile')
2043

2144
# Load the configuration for the current profile
2245
if parser.has_section(profile):
23-
config_dict.update(parser[profile])
46+
if sys.version_info >= (3, 0):
47+
config_dict.update(parser[profile])
48+
else:
49+
config_dict.update(config_to_dict(profile))
2450

2551
# Add configured paths to PYTHONPATH
2652
for key in config_dict:

config/config.ini

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
[DEFAULT]
2-
profile=CLUSTER
2+
profile=LOCAL
33

44
[LOCAL]
5-
data_path=/local/path/here
6-
mnist_path=/local/path/here
7-
cifar10_path=/local/path/here
8-
stl10_path=/local/path/here
9-
imagenet_path=/local/path/here
10-
cleverhans_path=/local/path/here
5+
data_path=./data
6+
mnist_path=./data/mnist
7+
cifar10_path=./data/cifar-10
8+
stl10_path=./data/stl-10
9+
imagenet_path=./data/imagenet
1110

1211
[CLUSTER]
1312
data_path=/dccstor/dlw/data/adversarial_learning
1413
mnist_path=/dccstor/dlw/data/adversarial_learning/mnist
1514
cifar10_path=/dccstor/dlw/data/adversarial_learning/cifar-10
1615
stl10_path=/dccstor/dlw/data/adversarial_learning/stl-10
1716
imagenet_path=/dccstor/dlw/data/kg_completion/images/jpegs_ILSVRC/n02119789
18-
cleverhans_path=/u/nicolae/cleverhans

requirements.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
Keras==2.0.4
2-
matplotlib==2.0.1
3-
scipy==0.19.0
4-
tensorflow==1.1.0
1+
h5py
2+
Keras
3+
matplotlib
4+
scipy
5+
tensorflow

setup.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from setuptools import setup
2+
from setuptools import find_packages
3+
4+
5+
setup(name='Adversarial Robustness Toolbox',
6+
version='0.1',
7+
description='IBM Adversarial machine learning toolbox',
8+
author='Irina Nicolae',
9+
author_email='[email protected]',
10+
url='https://github.com/ririnicolae/adversarial-robustness-toolbox',
11+
license='MIT',
12+
install_requires=['h5py',
13+
'Keras',
14+
'scipy',
15+
'matplotlib',
16+
'tensorflow',
17+
'setuptools'],
18+
# extras_require={
19+
# 'tests': ['pytest',
20+
# 'pytest-pep8',
21+
# 'pytest-xdist',
22+
# 'pytest-cov'],
23+
# },
24+
classifiers=[
25+
'Development Status :: 3 - Alpha',
26+
'Intended Audience :: Developers',
27+
'Intended Audience :: Education',
28+
'Intended Audience :: Science/Research',
29+
'License :: OSI Approved :: MIT License',
30+
'Programming Language :: Python :: 2',
31+
'Programming Language :: Python :: 3',
32+
'Topic :: Software Development :: Libraries',
33+
'Topic :: Software Development :: Libraries :: Python Modules',
34+
'Topic :: Scientific/Engineering :: Artificial Intelligence',
35+
],
36+
packages=find_packages())

src/attacks/deepfool.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ def generate(self, x_val, **kwargs):
3838
:return: A Numpy array holding the adversarial examples.
3939
"""
4040
assert self.set_params(**kwargs)
41+
k.set_learning_phase(0)
42+
4143
dims = list(x_val.shape)
4244
nb_instances = dims[0]
4345
dims[0] = None
@@ -54,9 +56,8 @@ def generate(self, x_val, **kwargs):
5456
for j, x in enumerate(x_adv):
5557
xi = x[None, ...]
5658

57-
f = self.sess.run(self.model(xi_op), feed_dict={xi_op: xi, k.learning_phase(): 0})[0]
58-
grd = self.sess.run(grads, feed_dict={xi_op: xi, k.learning_phase(): 0})
59-
grd = [g[0] for g in grd]
59+
f, grd = self.sess.run([self.model(xi_op), grads], {xi_op: xi})
60+
f, grd = f[0], [g[0] for g in grd]
6061
fk_hat = np.argmax(f)
6162
fk_i_hat = fk_hat
6263
nb_iter = 0
@@ -79,9 +80,9 @@ def generate(self, x_val, **kwargs):
7980
xi = np.clip(xi, self.clip_min, self.clip_max)
8081

8182
# Recompute prediction for new xi
82-
f = self.sess.run(self.model(xi_op), feed_dict={xi_op: xi, k.learning_phase(): 0})[0]
83-
grd = self.sess.run(grads, feed_dict={xi_op: xi, k.learning_phase(): 0})
84-
grd = [g[0] for g in grd]
83+
84+
f, grd = self.sess.run([self.model(xi_op), grads], {xi_op: xi})
85+
f, grd = f[0], [g[0] for g in grd]
8586
fk_i_hat = np.argmax(f)
8687

8788
nb_iter += 1

src/attacks/fast_gradient.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ def __init__(self, classifier, sess=None, ord=np.inf, y=None, targeted=False, cl
3131
kwargs = {'ord': ord, 'targeted': targeted, 'clip_min': clip_min, 'clip_max': clip_max, 'y': y}
3232
self.set_params(**kwargs)
3333

34-
def generate_graph(self, x, eps=0.3, **kwargs):
34+
def generate_graph(self, x, eps, **kwargs):
3535
"""Generate symbolic graph for adversarial examples and return.
3636
:param x: The model's symbolic inputs.
37-
:param eps: (optional float) attack step size (input variation)
37+
:param eps: (optional tf.placeholder) The placeholder for input variation (noise amplitude)
3838
:param ord: (optional) Order of the norm (mimics Numpy). Possible values: np.inf, 1 or 2.
3939
:param y: (optional) A placeholder for the model labels. Only provide this parameter if you'd like to use true
4040
labels when crafting adversarial samples. Otherwise, model predictions are used as labels to avoid the
@@ -89,25 +89,24 @@ def minimal_perturbations(self, x, x_val, eps_step=0.1, eps_max=1., **kwargs):
8989
:param kwargs: Other parameters to send to generate_graph
9090
:return: A Numpy array holding the adversarial examples.
9191
"""
92+
k.set_learning_phase(0)
9293
y = np.argmax(self.model.predict(x_val), 1)
9394
adv_x = x_val.copy()
9495

95-
curr_indexes = np.arange(len(x_val))
96-
eps = eps_step
96+
curr_indexes = np.arange(len(adv_x))
97+
eps = tf.placeholder(tf.float32, None)
98+
adv_x_op = self.generate_graph(x, eps, **kwargs)
99+
adv_y = tf.argmax(self.model(adv_x_op), 1)
100+
eps_val = eps_step
97101

98-
while len(curr_indexes) != 0 and eps <= eps_max:
102+
while len(curr_indexes) != 0 and eps_val <= eps_max:
99103
# Adversarial crafting
100-
adv_x_op = self.generate_graph(x, eps=eps, **kwargs)
101-
adv_y = tf.argmax(self.model(adv_x_op), 1)
102-
103-
feed_dict = {x: x_val[curr_indexes], k.learning_phase(): 0}
104-
new_adv_x, new_y = self.sess.run([adv_x_op, adv_y], feed_dict=feed_dict)
104+
new_adv_x, new_y = self.sess.run([adv_x_op, adv_y], {x: x_val[curr_indexes], eps: eps_val})
105105

106106
# Update
107107
adv_x[curr_indexes] = new_adv_x
108108
curr_indexes = np.where(y[curr_indexes] == new_y)[0]
109-
110-
eps += eps_step
109+
eps_val += eps_step
111110

112111
return adv_x
113112

@@ -125,23 +124,25 @@ def generate(self, x_val, **kwargs):
125124
:return: A Numpy array holding the adversarial examples.
126125
"""
127126

128-
input_shape = list(x_val.shape)
129-
input_shape[0] = None
127+
input_shape = [None] + list(x_val.shape[1:])
130128
self._x = tf.placeholder(tf.float32, shape=input_shape)
129+
k.set_learning_phase(0)
131130

131+
# Return adversarial examples computed with minimal perturbation if option is active
132132
if "minimal" in kwargs and kwargs["minimal"]:
133133
return self.minimal_perturbations(self._x, x_val, **kwargs)
134134

135+
# Generate computation graph
135136
self._x_adv = self.generate_graph(self._x, **kwargs)
136137

137138
# Run symbolic graph without or with true labels
138139
if 'y_val' not in kwargs or kwargs['y_val'] is None:
139-
feed_dict = {self._x: x_val, k.learning_phase(): 0}
140+
feed_dict = {self._x: x_val}
140141
else:
141142
# Verify label placeholder was given in params if using true labels
142143
if self.y is None:
143144
raise Exception("True labels given but label placeholder not given.")
144-
feed_dict = {self._x: x_val, self.y: kwargs['y_val'], k.learning_phase(): 0}
145+
feed_dict = {self._x: x_val, self.y: kwargs['y_val']}
145146

146147
return self.sess.run(self._x_adv, feed_dict=feed_dict)
147148

src/attacks/fast_gradient_unittest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ def test_mnist(self):
8383
self.assertFalse((Y_test == test_y_pred).all())
8484

8585
scores = classifier.evaluate(X_train_adv_min, Y_train)
86-
print('\naccuracy on adversarial train examples: %.2f%%' % (scores[1] * 100))
86+
print('\naccuracy on adversarial train examples with minimal perturbation: %.2f%%' % (scores[1] * 100))
8787

8888
scores = classifier.evaluate(X_test_adv_min, Y_test)
89-
print('\naccuracy on adversarial test examples: %.2f%%' % (scores[1] * 100))
89+
print('\naccuracy on adversarial test examples with minimal perturbation: %.2f%%' % (scores[1] * 100))
9090

9191
def test_with_preprocessing(self):
9292

0 commit comments

Comments
 (0)