Skip to content

Commit

Permalink
Merge pull request #228 from jonasrauber/rng
Browse files Browse the repository at this point in the history
updated attacks and gradient estimators to use foolbox-owned RNGs
  • Loading branch information
jonasrauber authored Oct 24, 2018
2 parents 348c220 + 3af6620 commit 964b5ee
Show file tree
Hide file tree
Showing 17 changed files with 73 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ script:
# tf eager cannot be run in the same process as standard tf
- pytest --ignore=foolbox/tests/test_models_tensorflow_eager.py
- pytest --cov-append foolbox/tests/test_models_tensorflow_eager.py
- flake8 --ignore E402,E741 .
- flake8 --ignore E402,E741,W503,W504 .
after_success:
- coveralls
cache:
Expand Down
4 changes: 4 additions & 0 deletions foolbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
with open(join(dirname(__file__), 'VERSION')) as f:
__version__ = f.read().strip()

from .rngs import rng # noqa: F401
from .rngs import nprng # noqa: F401
from .rngs import set_seeds # noqa: F401

from . import models # noqa: F401
from . import criteria # noqa: F401
from . import distances # noqa: F401
Expand Down
5 changes: 3 additions & 2 deletions foolbox/attacks/additive_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .base import Attack
from .base import call_decorator
from .. import nprng


class AdditiveNoiseAttack(Attack):
Expand Down Expand Up @@ -70,7 +71,7 @@ class AdditiveUniformNoiseAttack(AdditiveNoiseAttack):
def _sample_noise(self, epsilon, image, bounds):
min_, max_ = bounds
w = epsilon * (max_ - min_)
noise = np.random.uniform(-w, w, size=image.shape)
noise = nprng.uniform(-w, w, size=image.shape)
noise = noise.astype(image.dtype)
return noise

Expand All @@ -84,6 +85,6 @@ class AdditiveGaussianNoiseAttack(AdditiveNoiseAttack):
def _sample_noise(self, epsilon, image, bounds):
min_, max_ = bounds
std = epsilon / np.sqrt(3) * (max_ - min_)
noise = np.random.normal(scale=std, size=image.shape)
noise = nprng.normal(scale=std, size=image.shape)
noise = noise.astype(image.dtype)
return noise
3 changes: 2 additions & 1 deletion foolbox/attacks/blended_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .base import Attack
from .base import call_decorator
from .. import nprng


class BlendedUniformNoiseAttack(Attack):
Expand Down Expand Up @@ -57,7 +58,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
# random noise images tend to be classified into the same class,
# so we might need to make very many draws if the original class
# is that one
random_image = np.random.uniform(
random_image = nprng.uniform(
min_, max_, size=image.shape).astype(image.dtype)
_, is_adversarial = a.predictions(random_image)
if is_adversarial:
Expand Down
3 changes: 2 additions & 1 deletion foolbox/attacks/iterative_projected_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .base import call_decorator
from .. import distances
from ..utils import crossentropy
from .. import nprng


class IterativeProjectedGradientBaseAttack(Attack):
Expand Down Expand Up @@ -111,7 +112,7 @@ def _run_one(self, a, epsilon, stepsize, iterations,
if random_start:
# using uniform noise even if the perturbation clipping uses
# a different norm because cleverhans does it the same way
noise = np.random.uniform(
noise = nprng.uniform(
-epsilon * s, epsilon * s, original.shape).astype(
original.dtype)
x = original + self._clip_perturbation(a, noise, epsilon)
Expand Down
4 changes: 2 additions & 2 deletions foolbox/attacks/lbfgs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from __future__ import division
import random
import logging

import numpy as np
Expand All @@ -9,6 +8,7 @@
from .base import call_decorator
from .gradient import GradientAttack
from ..utils import crossentropy as utils_ce
from .. import rng


class LBFGSAttack(Attack):
Expand Down Expand Up @@ -118,7 +118,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
# remove original class from samples
# should be more efficient than other approaches, see
# https://github.com/numpy/numpy/issues/2764
target_classes = random.sample(
target_classes = rng.sample(
range(num_classes), num_random_targets + 1)
target_classes = [t for t in target_classes if t != original_class] # noqa: E501
target_classes = target_classes[:num_random_targets]
Expand Down
7 changes: 4 additions & 3 deletions foolbox/attacks/localsearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .base import Attack
from .base import call_decorator
from ..utils import softmax
from .. import nprng


class SinglePixelAttack(Attack):
Expand Down Expand Up @@ -47,7 +48,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,

min_, max_ = a.bounds()

pixels = np.random.permutation(h * w)
pixels = nprng.permutation(h * w)
pixels = pixels[:max_pixels]
for i, pixel in enumerate(pixels):
x = pixel % w
Expand Down Expand Up @@ -159,7 +160,7 @@ def unnormalize(im):
def random_locations():
n = int(0.1 * h * w)
n = min(n, 128)
locations = np.random.permutation(h * w)[:n]
locations = nprng.permutation(h * w)[:n]
p_x = locations % w
p_y = locations // w
pxy = list(zip(p_x, p_y))
Expand Down Expand Up @@ -189,7 +190,7 @@ def cyclic(r, Ibxy):
for _ in range(R):
# Computing the function g using the neighborhood
# IMPORTANT: random subset for efficiency
PxPy = PxPy[np.random.permutation(len(PxPy))[:128]]
PxPy = PxPy[nprng.permutation(len(PxPy))[:128]]
L = [pert(Ii, p, x, y) for x, y in PxPy]

def score(Its):
Expand Down
6 changes: 3 additions & 3 deletions foolbox/attacks/pointwise.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import warnings
import random
import logging

from .base import Attack
from .base import call_decorator
from .saltandpepper import SaltAndPepperNoiseAttack
from .. import rng


class PointwiseAttack(Attack):
Expand Down Expand Up @@ -70,7 +70,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
while True:
# draw random shuffling of all indices
indices = list(range(N))
random.shuffle(indices)
rng.shuffle(indices)

for index in indices:
# change index
Expand Down Expand Up @@ -100,7 +100,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
while True:
# draw random shuffling of all indices
indices = list(range(N))
random.shuffle(indices)
rng.shuffle(indices)

# whether that run through all values made any improvement
improved = False
Expand Down
4 changes: 2 additions & 2 deletions foolbox/attacks/saliency.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import logging
import random

import numpy as np

from .base import Attack
from .base import call_decorator
from .gradient import GradientAttack
from .. import rng


class SaliencyMapAttack(Attack):
Expand Down Expand Up @@ -93,7 +93,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
# remove original class from samples
# should be more efficient than other approaches, see
# https://github.com/numpy/numpy/issues/2764
target_classes = random.sample(
target_classes = rng.sample(
range(num_classes), num_random_targets + 1)
target_classes = [t for t in target_classes if t != original_class] # noqa: E501
target_classes = target_classes[:num_random_targets]
Expand Down
3 changes: 2 additions & 1 deletion foolbox/attacks/saltandpepper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .base import Attack
from .base import call_decorator
from .. import nprng


class SaltAndPepperNoiseAttack(Attack):
Expand Down Expand Up @@ -57,7 +58,7 @@ def __call__(self, input_or_adv, label=None, unpack=True,
for epsilon in np.linspace(0, max_epsilon, num=epsilons + 1)[1:]:
p = epsilon

u = np.random.uniform(size=shape)
u = nprng.uniform(size=shape)
u = u.repeat(channels, axis=axis)

salt = (u >= 1 - p / 2).astype(image.dtype) * r
Expand Down
5 changes: 2 additions & 3 deletions foolbox/attacks/slsqp.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import numpy as np
import scipy.optimize as so

from .base import Attack
from .base import call_decorator
from .. import nprng


class SLSQPAttack(Attack):
Expand Down Expand Up @@ -47,8 +47,7 @@ def __call__(self, input_or_adv, label=None, unpack=True):
n = image.size
image = image.flatten()

np.random.seed(42)
x0 = np.random.uniform(min_, max_, size=image.shape)
x0 = nprng.uniform(min_, max_, size=image.shape)
bounds = [(min_, max_)] * n
options = {'maxiter': 500}

Expand Down
3 changes: 2 additions & 1 deletion foolbox/attacks/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .base import Attack
from .base import call_decorator
from .. import nprng


class SpatialAttack(Attack):
Expand Down Expand Up @@ -82,7 +83,7 @@ def get_samples(limits, num, do_flag):
if not do_flag:
return [0]
elif random_sampling:
return np.random.uniform(lb, up, num)
return nprng.uniform(lb, up, num)
else:
return np.linspace(lb, up, num)

Expand Down
3 changes: 2 additions & 1 deletion foolbox/gradient_estimators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import numpy as np

from .utils import batch_crossentropy
from . import nprng


class CoordinateWiseGradientEstimator(object):
Expand Down Expand Up @@ -73,7 +74,7 @@ def _get_noise(self, shape, dtype):
samples = self._samples
assert samples % 2 == 0
shape = (samples // 2,) + shape
noise = np.random.normal(size=shape).astype(np.float32)
noise = nprng.normal(size=shape).astype(np.float32)
noise = np.concatenate([noise, -noise])
return noise

Expand Down
18 changes: 18 additions & 0 deletions foolbox/rngs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import random
import numpy as np

rng = random.Random()
nprng = np.random.RandomState()


def set_seeds(seed):
"""Sets the seeds of both random number generators used by Foolbox.
Parameters
----------
seed : int
The seed for both random number generators.
"""
rng.seed(seed)
nprng.seed(seed)
4 changes: 4 additions & 0 deletions foolbox/tests/test_attacks_localsearch.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import numpy as np

from foolbox import set_seeds
from foolbox.attacks import LocalSearchAttack as Attack


def test_attack(bn_adversarial):
set_seeds(22)
adv = bn_adversarial
attack = Attack()
attack(adv, d=1, t=10)
Expand All @@ -12,6 +14,7 @@ def test_attack(bn_adversarial):


def test_attack_gl(gl_bn_adversarial):
set_seeds(22)
adv = gl_bn_adversarial
attack = Attack()
attack(adv, d=1, t=10)
Expand All @@ -20,6 +23,7 @@ def test_attack_gl(gl_bn_adversarial):


def test_targeted_attack(bn_targeted_adversarial):
set_seeds(22)
adv = bn_targeted_adversarial
attack = Attack()
attack(adv, d=1)
Expand Down
5 changes: 3 additions & 2 deletions foolbox/tests/test_model_wrappers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np

from foolbox import set_seeds
from foolbox.models import ModelWrapper
from foolbox.models import DifferentiableModelWrapper
from foolbox.models import CompositeModel
Expand Down Expand Up @@ -63,9 +64,9 @@ def test_composite_model(gl_bn_model, bn_model, bn_image, bn_label):

def test_estimate_gradient_wrapper(eg_bn_adversarial, bn_image):
p, ia = eg_bn_adversarial.predictions(bn_image)
np.random.seed(22)
set_seeds(22)
g = eg_bn_adversarial.gradient(bn_image)
np.random.seed(22)
set_seeds(22)
p2, g2, ia2 = eg_bn_adversarial.predictions_and_gradient(bn_image)
assert np.all(p == p2)
assert np.all(g == g2)
Expand Down
17 changes: 17 additions & 0 deletions foolbox/tests/test_rngs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest
import random
import numpy as np

from foolbox import rng
from foolbox import nprng


@pytest.mark.parametrize('rng', [rng, nprng])
def test_rng(rng):
random.seed(66)
np.random.seed(77)
x1 = rng.randint(0, 1000000)
random.seed(66)
np.random.seed(77)
x2 = rng.randint(0, 1000000)
assert x1 != x2

0 comments on commit 964b5ee

Please sign in to comment.