-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Files for Paper Implementation
- Loading branch information
Showing
9 changed files
with
1,054 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,121 @@ | ||
# confidence-is-all-you-need | ||
## Confidence is All You Need for MI Attacks | ||
|
||
This directory contains code to reproduce our paper: | ||
**"Confidence is all you need for MI Attacks"** <br> | ||
https://arxiv.org/abs/2311.15373 <br> | ||
by Abhishek Sinha, Himanshi Tibrewal, Mansi Gupta, Nikhar Waghela, Shivank Garg | ||
|
||
Our work is based upon : | ||
|
||
**"Membership Inference Attacks From First Principles"** <br> | ||
https://arxiv.org/abs/2112.03570 <br> | ||
by Nicholas Carlini, Steve Chien, Milad Nasr, Shuang Song, Andreas Terzis, and Florian Tramèr. | ||
|
||
### INSTALLING DEPENDENCIES | ||
To install the basic dependencies needed to run this repository | ||
|
||
>bash requirements.sh | ||
We train our models with JAX + ObJAX so you will need to follow build instructions for that | ||
https://github.com/google/objax | ||
https://objax.readthedocs.io/en/latest/installation_setup.html | ||
|
||
### RUNNING THE CODE | ||
|
||
#### 1. Train the models | ||
|
||
The first step in our attack is to train shadow models. As a baseline that | ||
should give most of the gains in our attack, you should start by training 16 | ||
shadow models with the command | ||
|
||
> bash scripts/train_demo.sh | ||
or if you have multiple GPUs on your machine and want to train these models in | ||
parallel, then modify and run | ||
|
||
> bash scripts/train_demo_multigpu.sh | ||
This will train several CIFAR-10 wide ResNet models to ~91% accuracy each, and | ||
will output a bunch of files under the directory exp/cifar10 with structure: | ||
|
||
``` | ||
exp/cifar10/ | ||
- experiment_N_of_16 | ||
-- hparams.json | ||
-- keep.npy | ||
-- ckpt/ | ||
--- 0000000100.npz | ||
-- tb/ | ||
``` | ||
|
||
#### 2. Perform inference | ||
|
||
Once the models are trained, now it's necessary to perform inference and save | ||
the output features for each training example for each model in the dataset. | ||
|
||
> python3 inference.py --logdir=exp/cifar10/ | ||
This will add to the experiment directory a new set of files | ||
|
||
``` | ||
exp/cifar10/ | ||
- experiment_N_of_16 | ||
-- logits/ | ||
--- 0000000100.npy | ||
``` | ||
|
||
where this new file has shape (50000, 10) and stores the model's output features | ||
for each example. | ||
|
||
#### 3. Compute membership inference scores | ||
|
||
Finally we take the output features and generate our logit-scaled membership | ||
inference scores for each example for each model. | ||
|
||
> python3 score.py exp/cifar10/ | ||
We find the evaluation of scores through various experiments. The calculations of logits are implemented in the score.py file, where we explored all the commented-out calculations to find the logits. It was noted that utilizing argmax values, which doesn't require knowledge of true labels, produced results comparable to those outlined in the "LIRA Likelihood Ratio Paper." | ||
|
||
And this in turn generates a new directory | ||
|
||
``` | ||
exp/cifar10/ | ||
- experiment_N_of_16 | ||
-- scores/ | ||
--- 0000000100.npy | ||
``` | ||
|
||
with shape (50000,) storing just our scores. | ||
|
||
### PLOTTING THE RESULTS | ||
|
||
Finally we can generate pretty pictures, and run the plotting code | ||
|
||
> python3 plot.py | ||
### RESULTS {Using AUC as Metric} | ||
|
||
| | Loss Value (Baseline) | Confidence Values | log (Confidence Values) | Argmax | log (Argmax) | | ||
| :-----: | :-------------------: | :---------------: | :---------------------: | :----: | :----------: | | ||
| Attack Ours (Online) | 0.5753 | 0.5668 | 0.575 | 0.5464 | 0.5447 | | ||
| Attack Ours (Online,Fixed Variance) | 0.5879 | 0.593 | 0.6009 | 0.5622 | 0.5602 | | ||
| Attack Ours (Offline) | 0.5181 | 0.492 | 0.4721 | 0.478 | 0.4756 | | ||
| Attack Ours (Offline, Fixed Variance) | 0.5184 | 0.4928 | 0.4804 | 0.4834 | 0.4815 | | ||
| Attack Global Threshold | 0.5448 | 0.5439 | 0.5469 | 0..5376 | 0.5377 | | ||
|
||
where the global threshold attack is the baseline, and our online, | ||
online-with-fixed-variance, offline, and offline-with-fixed-variance attack | ||
variants are the four other curves. Note that because we only train a few | ||
models, the fixed variance variants perform best. | ||
|
||
### Citation | ||
|
||
You can cite this paper with | ||
|
||
``` | ||
@ title= {Confidence is All You Need For MI Attacks} | ||
author={Abhishek Sinha, Himanshi Tibrewal, Mansi Gupta, Nikhar Waghela, Shivank Garg}, | ||
journal={arXiv preprint arXiv:2311.15373}, | ||
year={2023} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Copyright 2021 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 0 --logdir exp/cifar10 &> logs/log_0 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 1 --logdir exp/cifar10 &> logs/log_1 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 2 --logdir exp/cifar10 &> logs/log_2 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 3 --logdir exp/cifar10 &> logs/log_3 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 4 --logdir exp/cifar10 &> logs/log_4 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 5 --logdir exp/cifar10 &> logs/log_5 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 6 --logdir exp/cifar10 &> logs/log_6 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 7 --logdir exp/cifar10 &> logs/log_7 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 8 --logdir exp/cifar10 &> logs/log_8 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 9 --logdir exp/cifar10 &> logs/log_9 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 10 --logdir exp/cifar10 &> logs/log_10 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 11 --logdir exp/cifar10 &> logs/log_11 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 12 --logdir exp/cifar10 &> logs/log_12 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 13 --logdir exp/cifar10 &> logs/log_13 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 14 --logdir exp/cifar10 &> logs/log_14 | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 15 --logdir exp/cifar10 &> logs/log_15 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Copyright 2021 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 0 --logdir exp/cifar10 &> logs/log_0 & | ||
CUDA_VISIBLE_DEVICES='1' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 1 --logdir exp/cifar10 &> logs/log_1 & | ||
CUDA_VISIBLE_DEVICES='2' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 2 --logdir exp/cifar10 &> logs/log_2 & | ||
CUDA_VISIBLE_DEVICES='3' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 3 --logdir exp/cifar10 &> logs/log_3 & | ||
CUDA_VISIBLE_DEVICES='4' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 4 --logdir exp/cifar10 &> logs/log_4 & | ||
CUDA_VISIBLE_DEVICES='5' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 5 --logdir exp/cifar10 &> logs/log_5 & | ||
CUDA_VISIBLE_DEVICES='6' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 6 --logdir exp/cifar10 &> logs/log_6 & | ||
CUDA_VISIBLE_DEVICES='7' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 7 --logdir exp/cifar10 &> logs/log_7 & | ||
wait; | ||
CUDA_VISIBLE_DEVICES='0' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 8 --logdir exp/cifar10 &> logs/log_8 & | ||
CUDA_VISIBLE_DEVICES='1' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 9 --logdir exp/cifar10 &> logs/log_9 & | ||
CUDA_VISIBLE_DEVICES='2' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 10 --logdir exp/cifar10 &> logs/log_10 & | ||
CUDA_VISIBLE_DEVICES='3' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 11 --logdir exp/cifar10 &> logs/log_11 & | ||
CUDA_VISIBLE_DEVICES='4' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 12 --logdir exp/cifar10 &> logs/log_12 & | ||
CUDA_VISIBLE_DEVICES='5' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 13 --logdir exp/cifar10 &> logs/log_13 & | ||
CUDA_VISIBLE_DEVICES='6' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 14 --logdir exp/cifar10 &> logs/log_14 & | ||
CUDA_VISIBLE_DEVICES='7' python3 -u train.py --dataset=cifar10 --epochs=100 --save_steps=20 --arch wrn28-2 --num_experiments 16 --expid 15 --logdir exp/cifar10 &> logs/log_15 & | ||
wait; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# Copyright 2020 Google LLC | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from typing import Callable, Optional, Tuple, List | ||
|
||
import numpy as np | ||
import tensorflow as tf | ||
|
||
|
||
def record_parse(serialized_example: str, image_shape: Tuple[int, int, int]): | ||
features = tf.io.parse_single_example(serialized_example, | ||
features={'image': tf.io.FixedLenFeature([], tf.string), | ||
'label': tf.io.FixedLenFeature([], tf.int64)}) | ||
image = tf.image.decode_image(features['image']).set_shape(image_shape) | ||
image = tf.cast(image, tf.float32) * (2.0 / 255) - 1.0 | ||
return dict(image=image, label=features['label']) | ||
|
||
|
||
class DataSet: | ||
"""Wrapper for tf.data.Dataset to permit extensions.""" | ||
|
||
def __init__(self, data: tf.data.Dataset, | ||
image_shape: Tuple[int, int, int], | ||
augment_fn: Optional[Callable] = None, | ||
parse_fn: Optional[Callable] = record_parse): | ||
self.data = data | ||
self.parse_fn = parse_fn | ||
self.augment_fn = augment_fn | ||
self.image_shape = image_shape | ||
|
||
@classmethod | ||
def from_arrays(cls, images: np.ndarray, labels: np.ndarray, augment_fn: Optional[Callable] = None): | ||
return cls(tf.data.Dataset.from_tensor_slices(dict(image=images, label=labels)), images.shape[1:], | ||
augment_fn=augment_fn, parse_fn=None) | ||
|
||
@classmethod | ||
def from_files(cls, filenames: List[str], | ||
image_shape: Tuple[int, int, int], | ||
augment_fn: Optional[Callable], | ||
parse_fn: Optional[Callable] = record_parse): | ||
filenames_in = filenames | ||
filenames = sorted(sum([tf.io.gfile.glob(x) for x in filenames], [])) | ||
if not filenames: | ||
raise ValueError('Empty dataset, files not found:', filenames_in) | ||
return cls(tf.data.TFRecordDataset(filenames), image_shape, augment_fn=augment_fn, parse_fn=parse_fn) | ||
|
||
@classmethod | ||
def from_tfds(cls, dataset: tf.data.Dataset, image_shape: Tuple[int, int, int], | ||
augment_fn: Optional[Callable] = None): | ||
return cls(dataset.map(lambda x: dict(image=tf.cast(x['image'], tf.float32) / 127.5 - 1, label=x['label'])), | ||
image_shape, augment_fn=augment_fn, parse_fn=None) | ||
|
||
def __iter__(self): | ||
return iter(self.data) | ||
|
||
def __getattr__(self, item): | ||
if item in self.__dict__: | ||
return self.__dict__[item] | ||
|
||
def call_and_update(*args, **kwargs): | ||
v = getattr(self.__dict__['data'], item)(*args, **kwargs) | ||
if isinstance(v, tf.data.Dataset): | ||
return self.__class__(v, self.image_shape, augment_fn=self.augment_fn, parse_fn=self.parse_fn) | ||
return v | ||
|
||
return call_and_update | ||
|
||
def augment(self, para_augment: int = 4): | ||
if self.augment_fn: | ||
return self.map(self.augment_fn, para_augment) | ||
return self | ||
|
||
def nchw(self): | ||
return self.map(lambda x: dict(image=tf.transpose(x['image'], [0, 3, 1, 2]), label=x['label'])) | ||
|
||
def one_hot(self, nclass: int): | ||
return self.map(lambda x: dict(image=x['image'], label=tf.one_hot(x['label'], nclass))) | ||
|
||
def parse(self, para_parse: int = 2): | ||
if not self.parse_fn: | ||
return self | ||
if self.image_shape: | ||
return self.map(lambda x: self.parse_fn(x, self.image_shape), para_parse) | ||
return self.map(self.parse_fn, para_parse) |
Oops, something went wrong.