-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FIX + Enhancement] FGM
and PGD
: fix L1 and extend to Lp
#2382
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev_1.18.0 #2382 +/- ##
==============================================
+ Coverage 85.53% 85.66% +0.12%
==============================================
Files 327 327
Lines 29936 29954 +18
Branches 5546 5540 -6
==============================================
+ Hits 25607 25661 +54
+ Misses 2903 2866 -37
- Partials 1426 1427 +1
|
I already exended Can anyone tell me why that is? There should be no projection in vanilla Thanks! |
Hi @eliegoudout I think for FGSM the projection to |
I agree that projection shouldn't have any impact but the function is still called, which makes tests fail as long as I thought about extending projection, and as it happens, it's a non trivial problem, and the only open source implementation I found was stale for 2 years, but might still be very good. I have not had time to try it yet, but it's on todo list. All of a sudden, it will sadly add a bit of complexity to the projection part and to ART. Is that a problem? |
Hi,
Cheers! |
Hi @eliegoudout Thank you very much! Adding a bit of complexity is ok if it leads to an accurate calculation. I'll take a closer look soon. I like the |
Thanks for your feedback! If we restrict this PR to Adding good projection for every p would still require some more work and can be left for even later. |
art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_pytorch.py
Fixed
Show fixed
Hide fixed
art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_tensorflow_v2.py
Fixed
Show fixed
Hide fixed
Trying to pass the final tests, I now understand (I think) that the |
Regarding my previous message I think I need some opinion to go further. The problem: What I think should be good for the user:
Current implementation What I think looks better:
As you can see, this implies that when using feature-wise bounds, the user must provide Possible Solutions:
Do you have any opinion? Is the solution a propose really breaking anything? Thanks! |
In the end, I decided to revert to previous implementation scheme, which prioritizes the feature-wise bounds, since it doesn't break anything and it may make more sense anyways, for attacks designed for batch generation. I documened as follows the use of
I started debugging the failing tests, but I have trouble finding the issue for the last two tests. It seems that the Cheers! |
@beat-buesser I think my PR is ready for review! |
FGM
and PGD
: fix L1 and extend to Lp
It is now ready and I would be grateful for any review :) I also fixed the small casting issue that was remaining, as well as fixed a small overview. |
@beat-buesser Hello :) Do you think someone can review / merge this PR at some point? I'm not in a hurry, but I'm worried it gets forgotten/frozen. Thanks! |
Hi @eliegoudout Thank you for your patience! I'll review and if possible merge it this week. |
@eliegoudout Could you please take a look at passing the DCO check? |
Signed-off-by: Beat Buesser <[email protected]> Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Beat Buesser <[email protected]> Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Élie Goudout <[email protected]>
… appropriately Signed-off-by: Élie Goudout <[email protected]>
Okay, very cool, thanks! It seems that these errors occur because I forgot to change the expected value for
I think it is expected to have a smaller (i.e. better) |
) -> "torch.Tensor": | ||
""" | ||
Project `values` on the L_p norm ball of size `eps`. | ||
|
||
:param values: Values to clip. | ||
:param eps: Maximum norm allowed. | ||
:param norm_p: L_p norm to use for clipping supporting 1, 2, `np.Inf` and "inf". | ||
:param eps: If a scalar, the norm of the L_p ball onto which samples are projected. Equivalently in general, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add this extended documentation of eps
also to the class level in line 84 of this files and similar lines for TensorFlow and Numpy implementations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure if this is the exact same epsilon. As such, I don't really know what the description should be at the class level to be honest. I think the only difference is that the class-level eps
is potentially split if the method is iterative, but I'm not sure that there is no other difference. As such, I would appreciate if you could provide the wanted doc for this one.
Hi @eliegoudout About the failing test, I think we should take a closer look at why this is happening. |
Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Élie Goudout <[email protected]>
art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_tensorflow_v2.py
Fixed
Show resolved
Hide resolved
art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_tensorflow_v2.py
Outdated
Show resolved
Hide resolved
Hi @eliegoudout Could you please take a look at the failing unit tests? It seems the relative tolerance is too tight for some tests. |
Thank you for your message. I do intend to get around this, but I'm struggling to find the necessary time, sorry! I'll do my best :) |
Signed-off-by: Élie Goudout <[email protected]>
Done ✅ |
…r safeguard Signed-off-by: Élie Goudout <[email protected]>
Hi @eliegoudout Thank you very much! It looks like we need one more change, the MXNet workflow is still failing at two unit tests and requires a larger tolerance for the updated tests. You don't have to install MXNet and can use the information in the test logs to adjust the test tolerances:
After fixing this last item, I can merge this pull request. I think your changes are a significant contribution to ART. Therefore, if you like, you may add your employer's name to the AUTHORS file. |
Okay, indeed I couldn't test those locally and didn't catch them before, thanks. One question: is this high tolerance to be expected? Going from
Great to read!
Thank you for this recognition, I'm happy this is perceived as significant. Since I made these contributions on my spare time, I would need to discuss this with my company. Let me get back to you on this before the merge (during next week at the latest). |
Hi @eliegoudout Ok, that sounds great. Instead of one single and large tolerance you could check for the framework and set two expected values with an if-block like this: def test_generate_parallel(art_warning, fix_get_mnist_subset, image_dl_estimator, framework):
try:
classifier, _ = image_dl_estimator(from_logits=True)
if framework == "tensorflow2": where if framework == "mxnet":
expected_value = ...
else:
expected_value = ... Notice to add |
Signed-off-by: Élie Goudout <[email protected]>
Signed-off-by: Élie Goudout <[email protected]>
@beat-buesser Thanks for clarifying the way to go (I'm not proficient with pytest, would not have thought that the signature would be understood out of the box). I've made the changes. Regarding the AUTHORS, I wrote my name directly for transparency, as I do not think it would have made sense to put my company at this stage. I hope this is now mergeable :) Sincerely. |
Hi @eliegoudout Thank you very much for your first contribution to ART! We will release ART 1.18 in the coming days which will bring your updates to ART's main branch and packages. |
Signed-off-by: Beat Buesser <[email protected]>
Summary for review
Object of the PR
It corrects the$L^1$ extension of FGM evasion attack (+ PGD) and properly extends them to $L^p$ for any $p\geqslant 1$ . The conversation started here, but essentially, I fix the current extension of FGM and PGD to $L^1$ and propose a proper extension to $L^p, (p\geqslant 1)$ , where the added noise is defined by
$$\text{noise direction}:=\left(\frac{\vert\nabla\vert}{\Vert\nabla\Vert_q}\right)^{q/p}\text{sign}(\nabla)$$ $\frac{1}{p}+\frac{1}{q} = 1$ (and some abuse of notation when $p=1$ or $p=+\infty$ ). This is optimal since it maximizes $\langle\nabla,\text{noise direction}\rangle$ subject to the constraint $\Vert\text{noise direction}\Vert_p = 1$ .
with
Previously, for$p=1$ , the package used $\text{noise direction} = \frac{\vert\nabla\vert}{\Vert\nabla\Vert_1}$ instead of the better and optimal $\text{noise direction}=(0,…,0,\text{sign}(\nabla_i),0,…,0),(i=\text{argmax}_j|\nabla_j|)$ .
What's changed?
FastGradientMethod
andProjectedGradientDescent
now allownorm
to be anyprojection
/_projection
functions had to be updated:3.a. They now work for any
3.b. When
3.c. The supboptimal behaviour is explicited via the addition of the keyword
suboptimal=True
(by default) and in the__doc__
.3.d. An "optimal" projection is something more computationally intensive to find (via an iterative method) and was not added in this PR (future work?)
3.e. An optimal implementation was already in the files for
projection
fromart.utils.py
, but it remains inaccessible from the attack classes (no control over thesuboptimal
kw).3.f. It is now possible to pass a different radius of projection sample-wise (see doc for the way to do it),
3.g. For a given sample, the special case of imposing feature-wise bound when
Please give your feedbacks
I will be glad to hear what you think. Specifically, I'm not at all an expert in tensorflow and pytorch, so I'm not really sure about the potential type cast issues. I know I had a bit of trouble debugging the code in the first place.
Also, I think there may be potential for improved documentation or unit tests, but I would like to hear what you have to say about this.
All the best!
Élie
Archive
Conversation started here, noticing previous$L^1$ norm was not optimal and that other $L^p$ spaces were not supported.
FGM
extension toTasks:
art.utils.py
. For this:suboptimal
keyword to allow a first approach for anyTrue
, which corresponds to current implementationprojection_l1_1
andprojection_l1_2
and incorporate them whensuboptimal=False
fortest_projection
totest_projection_norm
because it's the only thing it does.NotImplemented
whensuboptimal=False
for now._compute_perturbation
changes:_projection
changes: Followart.utils.projection
modsmomentum
update.Edit: As I pointed out, it would be better (imo) to entirely generalize to any$p\in[1, +\infty]$ with
$$\text{noise direction}:=\left(\frac{\vert\nabla\vert}{\Vert\nabla\Vert_q}\right)^{q/p}\text{sign}(\nabla)$$
and the appropriate abuses of notations, which is more natural.
Closes #2381.