Skip to content

Commit

Permalink
added L2 and Linf uniform noise attacks
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Rauber committed Jan 10, 2020
1 parent fada55d commit ca7af37
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 11 deletions.
4 changes: 4 additions & 0 deletions foolbox/ext/native/attacks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
from .inversion import InversionAttack # noqa: F401
from .blended_noise import LinearSearchBlendedUniformNoiseAttack # noqa: F401
from .additive_noise import L2AdditiveGaussianNoiseAttack # noqa: F401
from .additive_noise import L2AdditiveUniformNoiseAttack # noqa: F401
from .additive_noise import LinfAdditiveUniformNoiseAttack # noqa: F401
from .additive_noise import L2RepeatedAdditiveGaussianNoiseAttack # noqa: F401
from .additive_noise import L2RepeatedAdditiveUniformNoiseAttack # noqa: F401
from .additive_noise import LinfRepeatedAdditiveUniformNoiseAttack # noqa: F401
from .saltandpepper import SaltAndPepperNoiseAttack # noqa: F401
from .binarization import BinarizationRefinementAttack # noqa: F401
from .boundary_attack import BoundaryAttack # noqa: F401
Expand Down
79 changes: 73 additions & 6 deletions foolbox/ext/native/attacks/additive_noise.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
from abc import abstractmethod
import eagerpy as ep

from ..utils import flatten
from ..utils import atleast_kd


class L2AdditiveGaussianNoiseAttack:
class BaseAdditiveNoiseAttack:
def __init__(self, model):
self.model = model

def __call__(self, inputs, labels, *, epsilon):
x = ep.astensor(inputs)
min_, max_ = self.model.bounds()
p = x.normal(x.shape)
norms = flatten(p).square().sum(axis=-1).sqrt()
p = self.sample_noise(x)
norms = self.get_norms(p)
p = p / atleast_kd(norms, p.ndim)
x = x + epsilon * p
x = x.clip(min_, max_)
return x.tensor

@abstractmethod
def sample_noise(self, x):
raise NotImplementedError

class L2RepeatedAdditiveGaussianNoiseAttack:
@abstractmethod
def get_norms(self, p):
raise NotImplementedError


class L2Mixin:
def get_norms(self, p):
return flatten(p).square().sum(axis=-1).sqrt()


class LinfMixin:
def get_norms(self, p):
return flatten(p).max(axis=-1)


class GaussianMixin:
def sample_noise(self, x):
return x.normal(x.shape)


class UniformMixin:
def sample_noise(self, x):
return x.uniform(x.shape, -1, 1)


class L2AdditiveGaussianNoiseAttack(L2Mixin, GaussianMixin, BaseAdditiveNoiseAttack):
pass


class L2AdditiveUniformNoiseAttack(L2Mixin, UniformMixin, BaseAdditiveNoiseAttack):
pass


class LinfAdditiveUniformNoiseAttack(LinfMixin, UniformMixin, BaseAdditiveNoiseAttack):
pass


class BaseRepeatedAdditiveNoiseAttack:
def __init__(self, model):
self.model = model

Expand Down Expand Up @@ -48,8 +89,8 @@ def is_adversarial(p: ep.Tensor) -> ep.Tensor:
if found.all():
break

p = x0.normal(x0.shape)
norms = flatten(p).square().sum(axis=-1).sqrt()
p = self.sample_noise(x0)
norms = self.get_norms(p)
p = p / atleast_kd(norms, p.ndim)
x = x0 + epsilon * p
x = x.clip(min_, max_)
Expand All @@ -59,3 +100,29 @@ def is_adversarial(p: ep.Tensor) -> ep.Tensor:
found = ep.logical_or(found, is_adv)

return result.tensor

@abstractmethod
def sample_noise(self, x):
raise NotImplementedError

@abstractmethod
def get_norms(self, p):
raise NotImplementedError


class L2RepeatedAdditiveGaussianNoiseAttack(
L2Mixin, GaussianMixin, BaseRepeatedAdditiveNoiseAttack
):
pass


class L2RepeatedAdditiveUniformNoiseAttack(
L2Mixin, UniformMixin, BaseRepeatedAdditiveNoiseAttack
):
pass


class LinfRepeatedAdditiveUniformNoiseAttack(
LinfMixin, UniformMixin, BaseRepeatedAdditiveNoiseAttack
):
pass
31 changes: 26 additions & 5 deletions tests/test_additive_noise.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import pytest
import eagerpy as ep
import numpy as np
import torch
import torch.nn as nn

from foolbox.ext.native.models import PyTorchModel
from foolbox.ext.native.attacks import L2AdditiveGaussianNoiseAttack
from foolbox.ext.native.attacks import L2AdditiveUniformNoiseAttack
from foolbox.ext.native.attacks import LinfAdditiveUniformNoiseAttack
from foolbox.ext.native.attacks import L2RepeatedAdditiveGaussianNoiseAttack
from foolbox.ext.native.attacks import L2RepeatedAdditiveUniformNoiseAttack
from foolbox.ext.native.attacks import LinfRepeatedAdditiveUniformNoiseAttack


Attacks = [
L2AdditiveGaussianNoiseAttack,
L2AdditiveUniformNoiseAttack,
LinfAdditiveUniformNoiseAttack,
]


RepeatedAttacks = [
L2RepeatedAdditiveGaussianNoiseAttack,
L2RepeatedAdditiveUniformNoiseAttack,
LinfRepeatedAdditiveUniformNoiseAttack,
]


def misclassification(
Expand All @@ -15,7 +34,8 @@ def misclassification(
return classes != labels


def test_additive_gaussian_noise_attack():
@pytest.mark.parametrize("Attack", Attacks)
def test_additive_noise_attack(Attack):
channels = 3
batch_size = 8
h = w = 32
Expand All @@ -35,16 +55,17 @@ def forward(self, x):
x = torch.from_numpy(x).to(fmodel.device)
y = fmodel.forward(x).argmax(axis=-1)

attack = L2AdditiveGaussianNoiseAttack(fmodel)
attack = Attack(fmodel)
advs = attack(x, y, epsilon=20.0)

y_advs = fmodel.forward(advs).argmax(axis=-1)

assert x.shape == advs.shape
assert (y_advs == y).float().mean() < 0.8
assert (y_advs == y).float().mean() < 0.9


def test_repeated_additive_gaussian_noise_attack():
@pytest.mark.parametrize("Attack", RepeatedAttacks)
def test_repeated_additive_noise_attack(Attack):
channels = 3
batch_size = 8
h = w = 32
Expand All @@ -64,7 +85,7 @@ def forward(self, x):
x = torch.from_numpy(x).to(fmodel.device)
y = fmodel.forward(x).argmax(axis=-1)

attack = L2RepeatedAdditiveGaussianNoiseAttack(fmodel)
attack = Attack(fmodel)
advs = attack(x, y, epsilon=20.0, criterion=misclassification)

y_advs = fmodel.forward(advs).argmax(axis=-1)
Expand Down

0 comments on commit ca7af37

Please sign in to comment.