Repository: bethgelab/foolbox (language: python)

Issue:
No guarantee about the input bound 
**In attacks/base.py, inside the function '__call__' with keyword 'final'**, it should first check if input 'x' exceeds the 'model.bounds' or not. Because if 'x' does not reside in the bound, when it is sent into self.distance.clip_perturbation() line, the difference between **x and xp** is not equal to perturbation, which results in the failure of attack methods.


Discussion / hints:
Hmm, I think I understand what you are saying. And we could probably add a check for that. But I am not sure it's necessary. You pass in some clean, unperturbed data (e.g., images from the dataset). They should, by definition, be within the bounds.

Of course, checking for this could help the user to avoid certain mistakes. But there are many ways to use something in the wrong way, we will not be able to catch them all and this does not seem like a problem one would commonly run into.

Could you explain why you ran into it / how often that happens.
Hi,

I tried to use your tools to generate adversarial examples for training. Before that, I normalized CIFAR10 training dataset and then the scale of it is beyond (0, 1). In such case, when performing some kind of fixed-epsilon attack(PGD, FGSM) on data, the perturbation does not work since some examples already exceed (0, 1) by larger value than epsilon. You can check the code “xpc = self.distance.clip_perturbation(x, xp, epsilon)” in attacks/base.py. 

Thanks,
Qibing
发件人: Jonas Rauber
发送时间: 2020年12月18日 23:44
收件人: bethgelab/foolbox
抄送: Qibing Ren; Author
主题: Re: [bethgelab/foolbox] No guarantee about the input bound (#602)

Hmm, I think I understand what you are saying. And we could probably add a check for that. But I am not sure it's necessary. You pass in some clean, unperturbed data (e.g., images from the dataset). They should, by definition, be within the bounds.
Of course, checking for this could help the user to avoid certain mistakes. But there are many ways to use something in the wrong way, we will not be able to catch them all and this does not seem like a problem one would commonly run into.
Could you explain why you ran into it / how often that happens.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.


I think you are using it wrong. The bounds and the inputs need to match. You can let Foolbox handle the normalization using the preprocessing parameter.
I see. Thank you for your response. It’s my wrong usage that causes this problem. Maybe is it better to throw a warning when some users, like me, try to send inputs that do not match the model bounds? 
发件人: Jonas Rauber
发送时间: 2020年12月20日 16:40
收件人: bethgelab/foolbox
抄送: Qibing Ren; Author
主题: Re: [bethgelab/foolbox] No guarantee about the input bound (#602)

I think you are using it wrong. The bounds and the inputs need to match. You can let Foolbox handle the normalization using the preprocessing parameter.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.


Yes, maybe we should catch this problem.
I use Cifar10 data to train my neural network. To attack my network with PGD I pass my preprocessing and set the bounds to (0,1), but the success of the attack stays very low. 

`preprocessing = dict(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], axis=-3)`
`fb_model = fbox.PyTorchModel(model, bounds=(0, 1), preprocessing=preprocessing)`

If I train on non-normalized data and perform the attack, the attack is very successful. What am I doing wrong? I would greatly appreciate your help.


Fix patch:
diff --git a/foolbox/attacks/base.py b/foolbox/attacks/base.py
index 201de19f..8c8dd2d3 100644
--- a/foolbox/attacks/base.py
+++ b/foolbox/attacks/base.py
@@ -130,6 +130,10 @@ def __call__(  # noqa: F811
         x, restore_type = ep.astensor_(inputs)
         del inputs
 
+        # verify that input to the attack lies within model's input bounds
+        assert x.min() >= model.bounds.lower
+        assert x.max() >= model.bounds.upper
+
         criterion = get_criterion(criterion)
 
         was_iterable = True


Test patch:
diff --git a/tests/test_attacks_base.py b/tests/test_attacks_base.py
index b06ea051..98b74979 100644
--- a/tests/test_attacks_base.py
+++ b/tests/test_attacks_base.py
@@ -1,6 +1,9 @@
+from typing import Any
+
 import pytest
 import eagerpy as ep
 import foolbox as fbn
+import numpy as np
 
 from conftest import ModeAndDataAndDescription
 
@@ -43,3 +46,20 @@ class Model:
     model.data_format = "invalid"  # type: ignore
     with pytest.raises(ValueError):
         assert fbn.attacks.base.get_channel_axis(model, 3)  # type: ignore
+
+
+def test_model_bounds() -> None:
+    class MeanModel:
+        def __call__(self, inputs: Any) -> Any:
+            return inputs.mean(axis=(2, 3))
+
+    model = MeanModel()
+    fmodel = fbn.NumPyModel(model, bounds=(0, 1), data_format="channels_last")
+    attack = fbn.attacks.SaltAndPepperNoiseAttack(steps=5)
+
+    x = ep.astensor(np.zeros(16, 5, 5, 3))
+
+    with pytest.raises(AssertionError):
+        attack(fmodel, x - 0.1)
+    with pytest.raises(AssertionError):
+        attack(fmodel, x + 1.1)