Skip to content
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

Improve attack model types in inference attacks #2253

Merged

Conversation

abigailgold
Copy link
Collaborator

Description

Support additional attack model types (e.g., KNN, LR, etc.) in both membership inference and attribute inference attacks (blackbox and baseline).
Replace use of sklearn MLPClassifier in attribute attacks with pytorch NN model to improve performance.

Fixes #2153

Type of change

Please check all relevant options.

  • Improvement (non-breaking)
  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Testing

Added to existing tests the new model params. All tests pass.

Test Configuration:

  • OS: MacOS 12.6.8
  • Python version: 3.9

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

…x to improve performance (partially fixes Trusted-AI#2153).

Signed-off-by: abigailt <abigailt@il.ibm.com>
…attribute attacks (Trusted-AI#2153)

Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
@codecov-commenter
Copy link

codecov-commenter commented Aug 22, 2023

Codecov Report

Merging #2253 (5d0fc3b) into dev_1.16.0 (7f33be1) will decrease coverage by 1.12%.
Report is 3 commits behind head on dev_1.16.0.
The diff coverage is 95.43%.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Impacted file tree graph

@@              Coverage Diff               @@
##           dev_1.16.0    #2253      +/-   ##
==============================================
- Coverage       85.80%   84.68%   -1.12%     
==============================================
  Files             318      318              
  Lines           28365    28716     +351     
  Branches         5173     5260      +87     
==============================================
- Hits            24338    24318      -20     
- Misses           2718     3072     +354     
- Partials         1309     1326      +17     
Files Changed Coverage Δ
...ttacks/inference/membership_inference/black_box.py 92.34% <93.75%> (+0.43%) ⬆️
.../attacks/inference/attribute_inference/baseline.py 92.34% <95.16%> (+3.80%) ⬆️
...ference/attribute_inference/true_label_baseline.py 89.51% <95.27%> (+6.33%) ⬆️
...attacks/inference/attribute_inference/black_box.py 89.76% <96.06%> (+5.73%) ⬆️

... and 16 files with indirect coverage changes

@beat-buesser beat-buesser self-requested a review August 23, 2023 13:49
@beat-buesser beat-buesser self-assigned this Aug 23, 2023
@beat-buesser beat-buesser added the improvement Improve implementation label Aug 23, 2023
@beat-buesser beat-buesser added this to the ART 1.16.0 milestone Aug 23, 2023
Signed-off-by: abigailt <abigailt@il.ibm.com>
Copy link
Collaborator

@beat-buesser beat-buesser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @abigailgold Thank you very much for upgrading the inference attacks! I have a few questions and suggestions added to the review, what do you think?

@@ -38,6 +38,7 @@
num_classes_mnist = 10


@pytest.mark.skip_framework("scikitlearn")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for this skip?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I will remove it.

@@ -308,8 +314,8 @@ def test_true_label_baseline_regression(art_warning, get_diabetes_dataset, model
baseline_inferred_test
)

assert 0.6 <= baseline_train_acc
assert 0.6 <= baseline_test_acc
assert 0.45 <= baseline_train_acc
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does each parametric model have a different expectation value? Should we use a dictionary of expectation values?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this was the reason for reducing the value. How do you suggest to deal with it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of it could be worth having a dictionary of expectation value like

expected_accuracy = {"nn": 0.65, "rf": 0.45, ...}

and use it like

assert expected_accuracy[model_type] == baseline_train_acc 

assert np.allclose(inferred_test, x_test_feature.reshape(1, -1), atol=0.4)
assert (
np.count_nonzero(np.isclose(inferred_train, x_train_feature.reshape(1, -1), atol=0.4))
> inferred_train.shape[0] * 0.75
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for the factor 0.75?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a relatively relaxed way of measuring accuracy for a regression task. Since not 100% of the values are inferred correctly, we check that at least 75% are (within a given distance).

@@ -102,10 +112,15 @@ def __init__(
"""
super().__init__(estimator=None, attack_feature=attack_feature)

self._values: Optional[list] = None
self._values: list = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty lists should not be default values for arguments because if multiple instances of this class are created they will overwrite each others arguments.

Suggested change
self._values: list = []
self._values: Optional[list] = None

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an argument but an assignment within the method, I don't think the same issue exists. When using an optional value it caused mypy errors.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I was looking at the wrong line. Please ignore.

Comment on lines 119 to 123
self._attack_model_type: Optional[str] = attack_model_type
self.attack_model: Optional[Any] = None
self.epochs = 100
self.batch_size = 100
self.learning_rate = 0.0001
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these parameters that should be exposed to the user?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They could be. They were not exposed until now in any of the inference attacks but it's possible of course if you think this is desirable.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you often adjust the parameters to optimise the attack?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't, but perhaps a more savvy user would want to... I can do this with default values and then there is no harm... People can either use it or not.

_, targets = torch.autograd.Variable(input1), torch.autograd.Variable(targets)

optimizer.zero_grad()
outputs = self.attack_model(input1) # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this type ignore needed?

Copy link
Collaborator Author

@abigailgold abigailgold Sep 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was there before (in other classes), I just copied the code. Not sure why it's needed (I can try to remove and see if it causes any errors).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing it causes a mypy error: "Tensor" not callable

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, leave it for now.

attack_train_set = self._get_attack_dataset(feature=x_train, label=y_ready)
train_loader = DataLoader(attack_train_set, batch_size=self.batch_size, shuffle=True, num_workers=0)

self.attack_model = to_cuda(self.attack_model) # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this type ignore needed?

self.attack_model = MembershipInferenceAttackModel(x_train.shape[1], len(self._values))
loss_fn = nn.CrossEntropyLoss()

optimizer = optim.Adam(self.attack_model.parameters(), lr=self.learning_rate) # type: ignore
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this type ignore needed?

@@ -90,10 +100,15 @@ def __init__(
"""
super().__init__(estimator=None, attack_feature=attack_feature)

self._values: Optional[list] = None
self._values: list = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty lists should not be default values for arguments because if multiple instances of this class are created they will overwrite each others arguments.

Suggested change
self._values: list = []
self._values: Optional[list] = None

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore.

self._values: Optional[list] = None
self._attack_model_type = attack_model_type
self._attack_model = attack_model
self._values: list = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty lists should not be default values for arguments because if multiple instances of this class are created they will overwrite each others arguments.

Suggested change
self._values: list = []
self._values: Optional[list] = None

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore.

Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
abigailgold and others added 5 commits September 12, 2023 15:45
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
Signed-off-by: abigailt <abigailt@il.ibm.com>
@beat-buesser beat-buesser merged commit 6af9e5a into Trusted-AI:dev_1.16.0 Sep 14, 2023
37 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
improvement Improve implementation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants