diff --git a/fawkes/LICENSE b/LICENSE similarity index 100% rename from fawkes/LICENSE rename to LICENSE diff --git a/fawkes/build/lib/fawkes/__init__.py b/build/lib/fawkes/__init__.py similarity index 100% rename from fawkes/build/lib/fawkes/__init__.py rename to build/lib/fawkes/__init__.py diff --git a/fawkes/build/lib/fawkes/differentiator.py b/build/lib/fawkes/differentiator.py similarity index 100% rename from fawkes/build/lib/fawkes/differentiator.py rename to build/lib/fawkes/differentiator.py diff --git a/fawkes/build/lib/fawkes/protection.py b/build/lib/fawkes/protection.py similarity index 100% rename from fawkes/build/lib/fawkes/protection.py rename to build/lib/fawkes/protection.py diff --git a/fawkes/build/lib/fawkes/utils.py b/build/lib/fawkes/utils.py similarity index 100% rename from fawkes/build/lib/fawkes/utils.py rename to build/lib/fawkes/utils.py diff --git a/fawkes/dist/fawkes-0.0.1-py3-none-any.whl b/dist/fawkes-0.0.1-py3-none-any.whl similarity index 100% rename from fawkes/dist/fawkes-0.0.1-py3-none-any.whl rename to dist/fawkes-0.0.1-py3-none-any.whl diff --git a/fawkes/dist/fawkes-0.0.1.tar.gz b/dist/fawkes-0.0.1.tar.gz similarity index 100% rename from fawkes/dist/fawkes-0.0.1.tar.gz rename to dist/fawkes-0.0.1.tar.gz diff --git a/fawkes/fawkes.egg-info/PKG-INFO b/fawkes.egg-info/PKG-INFO similarity index 100% rename from fawkes/fawkes.egg-info/PKG-INFO rename to fawkes.egg-info/PKG-INFO diff --git a/fawkes/fawkes.egg-info/SOURCES.txt b/fawkes.egg-info/SOURCES.txt similarity index 100% rename from fawkes/fawkes.egg-info/SOURCES.txt rename to fawkes.egg-info/SOURCES.txt diff --git a/fawkes/fawkes.egg-info/dependency_links.txt b/fawkes.egg-info/dependency_links.txt similarity index 100% rename from fawkes/fawkes.egg-info/dependency_links.txt rename to fawkes.egg-info/dependency_links.txt diff --git a/fawkes/fawkes.egg-info/top_level.txt b/fawkes.egg-info/top_level.txt similarity index 100% rename from fawkes/fawkes.egg-info/top_level.txt rename to fawkes.egg-info/top_level.txt diff --git a/fawkes/README.md b/fawkes/README.md deleted file mode 100644 index 42f90b4..0000000 --- a/fawkes/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Fawkes -Code implementation of the paper "[Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models](https://arxiv.org/pdf/2002.08327.pdf)", at *USENIX Security 2020*. - -### BEFORE YOU RUN OUR CODE -We appreciate your interest in our work and for trying out our code. We've noticed several cases where incorrect configuration leads to poor performances of protection. If you also observe low detection performance far away from what we presented in the paper, please feel free to open an issue in this repo or contact any of the authors directly. We are more than happy to help you debug your experiment and find out the correct configuration. - -### ABOUT - -This repository contains code implementation of the paper "[Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models](https://arxiv.org/pdf/2002.08327.pdf)", at *USENIX Security 2020*. - -### DEPENDENCIES - -Our code is implemented and tested on Keras with TensorFlow backend. Following packages are used by our code. - -- `keras==2.3.1` -- `numpy==1.18.4` -- `tensorflow-gpu==1.13.1` - -Our code is tested on `Python 3.6.8` - -### HOWTO - -#### Download and Config Datasets -The first step is to download several datasets for protection and target selection. -1. Download the following dataset to your local machine. After downloading the datasets, restructure it the same way as the FaceScrub dataset downloaded. - - FaceScrub -- used for protection evaluation (link) - - VGGFace1 -- used for target select (link) - - VGGFace2 -- used for target select (link) - - WebFace -- used for target select (link) - -2. Config datasets -open `fawkes/config.py` and update the `DATASETS` dictionary with the path to each dataset. Then run `python fawkes/config.py`. Every time the datasets are updated or moved, remember to rerun the command with the updated path. - -3. Calculate embeddings using feature extractor. -Run `python3 fawkes/prepare_feature_extractor.py --candidate-datasets scrub vggface1 vggface2 webface`. This will calculate and cache the embeddings using the default feature extractor we provide. To use a customized feature extractor, please look at the Advance section at the end. - -#### Generate Cloak for Images -To generate cloak, run -`python3 fawkes/protection.py --gpu 0 --dataset scrub --feature-extractor webface_dense_robust_extract` -For more information about the detailed parameters, please read `fawkes/protection.py`. -The code will output a directory in `results/` with `cloak_data.p` inside. You can check the cloaked images or inspect the changes in `this notebook`. - -#### Evaluate Cloak Effectiveness -To evaluate the cloak, run `python3 fawkes/eval_cloak.py --gpu 0 --cloak_data PATH-TO-RESULT-DIRECTORY --transfer_model vggface2_inception_extract`. - -The code will print out the tracker model accuracy on uncloaked/original test images of the protected user, which should be close to 0. - - -### Citation -``` -@inproceedings{shan2020fawkes, - title={Fawkes: Protecting Personal Privacy against Unauthorized Deep Learning Models}, - author={Shan, Shawn and Wenger, Emily and Zhang, Jiayun and Li, Huiying and Zheng, Haitao and Zhao, Ben Y}, - booktitle="Proc. of USENIX Security", - year={2020} -} -``` \ No newline at end of file diff --git a/fawkes/fawkes/__init__.py b/fawkes/__init__.py similarity index 100% rename from fawkes/fawkes/__init__.py rename to fawkes/__init__.py diff --git a/fawkes/fawkes/differentiator.py b/fawkes/differentiator.py similarity index 100% rename from fawkes/fawkes/differentiator.py rename to fawkes/differentiator.py diff --git a/fawkes/fawkes/protection.py b/fawkes/protection.py similarity index 82% rename from fawkes/fawkes/protection.py rename to fawkes/protection.py index a7afd55..1a44958 100644 --- a/fawkes/fawkes/protection.py +++ b/fawkes/protection.py @@ -37,19 +37,34 @@ def generate_cloak_images(sess, feature_extractors, image_X, target_X=None, th=0 return cloaked_image_X +def get_mode_config(mode): + if mode == 'low': + args.feature_extractor = "low_extract" + args.th = 0.001 + elif mode == 'mid': + args.feature_extractor = "mid_extract" + args.th = 0.001 + elif mode == 'high': + args.feature_extractor = "high_extract" + args.th = 0.005 + elif mode == 'ultra': + args.feature_extractor = "high_extract" + args.th = 0.007 + elif mode == 'custom': + pass + else: + raise Exception("mode must be one of 'low', 'mid', 'high', 'ultra', 'custom'") + + def extract_faces(img): - # foo + # wait on Huiying return preprocess_input(resize(img, (224, 224))) def fawkes(): - assert os.path.exists(args.directory) - assert os.path.isdir(args.directory) + get_mode_config(args.mode) sess = init_gpu(args.gpu) - - print("Loading {} for optimization".format(args.feature_extractor)) - feature_extractors_ls = [load_extractor(args.feature_extractor)] image_paths = glob.glob(os.path.join(args.directory, "*")) @@ -70,10 +85,6 @@ def fawkes(): else: target_images = select_target_label(orginal_images, feature_extractors_ls, [args.feature_extractor]) - # file_name = args.directory.split("/")[-1] - # os.makedirs(args.result_directory, exist_ok=True) - # os.makedirs(os.path.join(args.result_directory, file_name), exist_ok=True) - protected_images = generate_cloak_images(sess, feature_extractors_ls, orginal_images, target_X=target_images, th=args.th) @@ -85,18 +96,20 @@ def fawkes(): def parse_arguments(argv): parser = argparse.ArgumentParser() - parser.add_argument('--gpu', type=str, - help='GPU id', default='0') parser.add_argument('--directory', type=str, help='directory that contain images for cloaking', default='imgs/') + parser.add_argument('--gpu', type=str, + help='GPU id', default='0') + + parser.add_argument('--mode', type=str, + help='cloak generation mode', default='mid') parser.add_argument('--feature-extractor', type=str, help="name of the feature extractor used for optimization", - default="webface_dense_robust_extract") + default="mid_extract") parser.add_argument('--th', type=float, default=0.005) parser.add_argument('--sd', type=int, default=1e9) - parser.add_argument('--protect_class', type=str, default=None) parser.add_argument('--lr', type=float, default=1) parser.add_argument('--result_directory', type=str, default="../results") diff --git a/fawkes/fawkes/utils.py b/fawkes/utils.py similarity index 100% rename from fawkes/fawkes/utils.py rename to fawkes/utils.py diff --git a/fawkes_dev/README.md b/fawkes_dev/README.md index 42f90b4..0e8e9cb 100644 --- a/fawkes_dev/README.md +++ b/fawkes_dev/README.md @@ -46,6 +46,13 @@ To evaluate the cloak, run `python3 fawkes/eval_cloak.py --gpu 0 --cloak_data PA The code will print out the tracker model accuracy on uncloaked/original test images of the protected user, which should be close to 0. +#### Exisiting Feature extractors + +We shared three different feature extractors under feature_extractors/ +1. low_extract.h5: trained on WebFace dataset with DenseNet architecture. +2. mid_extract.h5: VGGFace2 dataset with DenseNet architecture. Trained with PGD adversarial training for 5 epochs. +3. high_extract.h5: WebFace dataset with DenseNet architecture. Trained with PGD adversarial training for 20 epochs. + ### Citation ``` @inproceedings{shan2020fawkes, diff --git a/fawkes_dev/config.py b/fawkes_dev/config.py index 64881c3..cd14f4e 100644 --- a/fawkes_dev/config.py +++ b/fawkes_dev/config.py @@ -3,8 +3,8 @@ import json import os DATASETS = { + "pubfig": "../data/pubfig", "scrub": "/home/shansixioing/cloak/fawkes/data/scrub/", - # "vggface1": "/mnt/data/sixiongshan/data/vggface/", "vggface2": "/mnt/data/sixiongshan/data/vggface2/", "webface": "/mnt/data/sixiongshan/data/webface/", "youtubeface": "/mnt/data/sixiongshan/data/youtubeface/keras_flow_data/", diff --git a/fawkes_dev/differentiator.py b/fawkes_dev/differentiator.py index 38dfada..2b61764 100644 --- a/fawkes_dev/differentiator.py +++ b/fawkes_dev/differentiator.py @@ -207,8 +207,7 @@ class FawkesMaskGeneration: # we're creating start_vars = set(x.name for x in tf.global_variables()) self.learning_rate_holder = tf.placeholder(tf.float32, shape=[]) - # optimizer = tf.train.AdadeltaOptimizer(self.learning_rate_holder) - optimizer = tf.train.AdamOptimizer(self.learning_rate_holder) + optimizer = tf.train.AdadeltaOptimizer(self.learning_rate_holder) self.train = optimizer.minimize(self.loss_sum, var_list=[self.modifier]) diff --git a/fawkes_dev/prepare_feature_extractor.py b/fawkes_dev/prepare_feature_extractor.py index a4d2bf4..4867d07 100644 --- a/fawkes_dev/prepare_feature_extractor.py +++ b/fawkes_dev/prepare_feature_extractor.py @@ -44,7 +44,7 @@ def main(): for k, v in path2emb.items(): path2emb[k] = normalize(v) - pickle.dump(path2emb, open("../feature_extractors/embeddings/{}_emb_norm.p".format(args.feature_extractor), "wb")) + pickle.dump(path2emb, open("../feature_extractors/embeddings/{}_emb.p".format(args.feature_extractor), "wb")) def parse_arguments(argv): diff --git a/fawkes_dev/protection.py b/fawkes_dev/protection.py index 10cc0ee..cd97235 100644 --- a/fawkes_dev/protection.py +++ b/fawkes_dev/protection.py @@ -83,9 +83,9 @@ def parse_arguments(argv): help="name of the feature extractor used for optimization", default="webface_dense_robust_extract") parser.add_argument('--th', type=float, default=0.007) - parser.add_argument('--sd', type=int, default=1e5) + parser.add_argument('--sd', type=int, default=1e9) parser.add_argument('--protect_class', type=str, default=None) - parser.add_argument('--lr', type=float, default=0.1) + parser.add_argument('--lr', type=float, default=1) return parser.parse_args(argv) diff --git a/fawkes_dev/utils.py b/fawkes_dev/utils.py index b2a9fe3..f3a5a31 100644 --- a/fawkes_dev/utils.py +++ b/fawkes_dev/utils.py @@ -13,7 +13,7 @@ from keras.models import Model from keras.preprocessing import image from keras.utils import to_categorical from sklearn.metrics import pairwise_distances - +# from keras.utils import get_file def clip_img(X, preprocessing='raw'): X = reverse_preprocess(X, preprocessing) @@ -172,6 +172,7 @@ def imagenet_reverse_preprocessing(x, data_format=None): return x + def build_bottleneck_model(model, cut_off): bottleneck_model = Model(model.input, model.get_layer(cut_off).output) bottleneck_model.compile(loss='categorical_crossentropy', diff --git a/fawkes/setup.py b/setup.py similarity index 100% rename from fawkes/setup.py rename to setup.py