From 5d6ede141d087186c315c969aa5825859b09088a Mon Sep 17 00:00:00 2001 From: Jonas Rauber Date: Tue, 23 Oct 2018 20:29:56 +0000 Subject: [PATCH 1/4] updated attacks and gradient estimators to use foolbox-owned RNGs --- foolbox/__init__.py | 2 ++ foolbox/attacks/additive_noise.py | 5 +++-- foolbox/attacks/blended_noise.py | 3 ++- foolbox/attacks/iterative_projected_gradient.py | 3 ++- foolbox/attacks/lbfgs.py | 4 ++-- foolbox/attacks/localsearch.py | 7 ++++--- foolbox/attacks/pointwise.py | 6 +++--- foolbox/attacks/saliency.py | 4 ++-- foolbox/attacks/saltandpepper.py | 3 ++- foolbox/attacks/slsqp.py | 5 ++--- foolbox/attacks/spatial.py | 3 ++- foolbox/gradient_estimators.py | 3 ++- foolbox/rngs.py | 5 +++++ foolbox/tests/test_rngs.py | 17 +++++++++++++++++ 14 files changed, 50 insertions(+), 20 deletions(-) create mode 100644 foolbox/rngs.py create mode 100644 foolbox/tests/test_rngs.py diff --git a/foolbox/__init__.py b/foolbox/__init__.py index 63372331..719eb370 100755 --- a/foolbox/__init__.py +++ b/foolbox/__init__.py @@ -9,5 +9,7 @@ from . import attacks # noqa: F401 from . import utils # noqa: F401 from . import gradient_estimators # noqa: F401 +from .rngs import rng # noqa: F401 +from .rngs import nprng # noqa: F401 from .adversarial import Adversarial # noqa: F401 diff --git a/foolbox/attacks/additive_noise.py b/foolbox/attacks/additive_noise.py index e536c83b..45bf4334 100644 --- a/foolbox/attacks/additive_noise.py +++ b/foolbox/attacks/additive_noise.py @@ -5,6 +5,7 @@ from .base import Attack from .base import call_decorator +from .. import nprng class AdditiveNoiseAttack(Attack): @@ -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 @@ -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 diff --git a/foolbox/attacks/blended_noise.py b/foolbox/attacks/blended_noise.py index 66261ee4..d81349b3 100644 --- a/foolbox/attacks/blended_noise.py +++ b/foolbox/attacks/blended_noise.py @@ -6,6 +6,7 @@ from .base import Attack from .base import call_decorator +from .. import nprng class BlendedUniformNoiseAttack(Attack): @@ -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: diff --git a/foolbox/attacks/iterative_projected_gradient.py b/foolbox/attacks/iterative_projected_gradient.py index b8ec6918..09348099 100644 --- a/foolbox/attacks/iterative_projected_gradient.py +++ b/foolbox/attacks/iterative_projected_gradient.py @@ -8,6 +8,7 @@ from .base import call_decorator from .. import distances from ..utils import crossentropy +from .. import nprng class IterativeProjectedGradientBaseAttack(Attack): @@ -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) diff --git a/foolbox/attacks/lbfgs.py b/foolbox/attacks/lbfgs.py index 86f810ea..d8850173 100644 --- a/foolbox/attacks/lbfgs.py +++ b/foolbox/attacks/lbfgs.py @@ -1,5 +1,4 @@ from __future__ import division -import random import logging import numpy as np @@ -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): @@ -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] diff --git a/foolbox/attacks/localsearch.py b/foolbox/attacks/localsearch.py index a285a5e9..0b2e2078 100644 --- a/foolbox/attacks/localsearch.py +++ b/foolbox/attacks/localsearch.py @@ -4,6 +4,7 @@ from .base import Attack from .base import call_decorator from ..utils import softmax +from .. import nprng class SinglePixelAttack(Attack): @@ -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 @@ -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)) @@ -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): diff --git a/foolbox/attacks/pointwise.py b/foolbox/attacks/pointwise.py index 66eb15df..d410361e 100644 --- a/foolbox/attacks/pointwise.py +++ b/foolbox/attacks/pointwise.py @@ -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): @@ -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 @@ -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 diff --git a/foolbox/attacks/saliency.py b/foolbox/attacks/saliency.py index 10eff940..7b410bef 100644 --- a/foolbox/attacks/saliency.py +++ b/foolbox/attacks/saliency.py @@ -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): @@ -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] diff --git a/foolbox/attacks/saltandpepper.py b/foolbox/attacks/saltandpepper.py index ffe26dcb..bd9c6aad 100644 --- a/foolbox/attacks/saltandpepper.py +++ b/foolbox/attacks/saltandpepper.py @@ -2,6 +2,7 @@ from .base import Attack from .base import call_decorator +from .. import nprng class SaltAndPepperNoiseAttack(Attack): @@ -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 diff --git a/foolbox/attacks/slsqp.py b/foolbox/attacks/slsqp.py index 1fd579a9..357b3172 100644 --- a/foolbox/attacks/slsqp.py +++ b/foolbox/attacks/slsqp.py @@ -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): @@ -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} diff --git a/foolbox/attacks/spatial.py b/foolbox/attacks/spatial.py index 63d6e72a..2cd2e1a4 100644 --- a/foolbox/attacks/spatial.py +++ b/foolbox/attacks/spatial.py @@ -8,6 +8,7 @@ from .base import Attack from .base import call_decorator +from .. import nprng class SpatialAttack(Attack): @@ -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) diff --git a/foolbox/gradient_estimators.py b/foolbox/gradient_estimators.py index 9c9c52f9..1231c175 100644 --- a/foolbox/gradient_estimators.py +++ b/foolbox/gradient_estimators.py @@ -7,6 +7,7 @@ import numpy as np from .utils import batch_crossentropy +from . import nprng class CoordinateWiseGradientEstimator(object): @@ -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 diff --git a/foolbox/rngs.py b/foolbox/rngs.py new file mode 100644 index 00000000..790b9c79 --- /dev/null +++ b/foolbox/rngs.py @@ -0,0 +1,5 @@ +import random +import numpy as np + +rng = random.Random() +nprng = np.random.RandomState() diff --git a/foolbox/tests/test_rngs.py b/foolbox/tests/test_rngs.py new file mode 100644 index 00000000..7d4f8fa6 --- /dev/null +++ b/foolbox/tests/test_rngs.py @@ -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.sample() + random.seed(66) + np.random.seed(77) + x2 = rng.sample() + assert x1 != x2 From 48d822345d747552cc93868c7f725f3bf8d59401 Mon Sep 17 00:00:00 2001 From: Jonas Rauber Date: Tue, 23 Oct 2018 20:57:54 +0000 Subject: [PATCH 2/4] fixed import order --- foolbox/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/foolbox/__init__.py b/foolbox/__init__.py index 719eb370..5f2fc5e8 100755 --- a/foolbox/__init__.py +++ b/foolbox/__init__.py @@ -3,13 +3,14 @@ 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 . import models # noqa: F401 from . import criteria # noqa: F401 from . import distances # noqa: F401 from . import attacks # noqa: F401 from . import utils # noqa: F401 from . import gradient_estimators # noqa: F401 -from .rngs import rng # noqa: F401 -from .rngs import nprng # noqa: F401 from .adversarial import Adversarial # noqa: F401 From fa60aa14ce6b8253649aaf3535c3471b887be13d Mon Sep 17 00:00:00 2001 From: Jonas Rauber Date: Wed, 24 Oct 2018 06:58:38 +0000 Subject: [PATCH 3/4] fixed some tests and added set_seeds function --- foolbox/__init__.py | 1 + foolbox/rngs.py | 13 +++++++++++++ foolbox/tests/test_attacks_localsearch.py | 4 ++++ foolbox/tests/test_model_wrappers.py | 5 +++-- foolbox/tests/test_rngs.py | 4 ++-- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/foolbox/__init__.py b/foolbox/__init__.py index 5f2fc5e8..88923fca 100755 --- a/foolbox/__init__.py +++ b/foolbox/__init__.py @@ -5,6 +5,7 @@ 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 diff --git a/foolbox/rngs.py b/foolbox/rngs.py index 790b9c79..c91bda8c 100644 --- a/foolbox/rngs.py +++ b/foolbox/rngs.py @@ -3,3 +3,16 @@ 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) diff --git a/foolbox/tests/test_attacks_localsearch.py b/foolbox/tests/test_attacks_localsearch.py index 1c99cc40..b18aedce 100644 --- a/foolbox/tests/test_attacks_localsearch.py +++ b/foolbox/tests/test_attacks_localsearch.py @@ -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) @@ -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) @@ -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) diff --git a/foolbox/tests/test_model_wrappers.py b/foolbox/tests/test_model_wrappers.py index 91f6e2fa..ce033a87 100644 --- a/foolbox/tests/test_model_wrappers.py +++ b/foolbox/tests/test_model_wrappers.py @@ -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 @@ -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) diff --git a/foolbox/tests/test_rngs.py b/foolbox/tests/test_rngs.py index 7d4f8fa6..4b3d258f 100644 --- a/foolbox/tests/test_rngs.py +++ b/foolbox/tests/test_rngs.py @@ -10,8 +10,8 @@ def test_rng(rng): random.seed(66) np.random.seed(77) - x1 = rng.sample() + x1 = rng.randint(0, 1000000) random.seed(66) np.random.seed(77) - x2 = rng.sample() + x2 = rng.randint(0, 1000000) assert x1 != x2 From 3af6620771b9d4faabe53be31f1035a84a78aae0 Mon Sep 17 00:00:00 2001 From: Jonas Rauber Date: Wed, 24 Oct 2018 07:22:20 +0000 Subject: [PATCH 4/4] ignore typically ignored code style errors caused by flake8 upgrade --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8939bcfe..3c76b218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: