├── tiny_imagenet ├── datasets │ ├── __init__.py │ ├── dataset_factory.py │ ├── tiny_imagenet_input.py │ └── imagenet_input.py ├── tiny_imagenet.sh ├── eval.sh ├── train.sh ├── eval_single.sh ├── eval.py ├── model_lib.py ├── adversarial_attack.py ├── tiny_imagenet_converter │ └── converter.py └── train.py ├── images ├── heatmaps.png ├── histograms.png ├── loss_surfaces.png └── other │ ├── test_acc_50at_100at.png │ └── train_loss_50at_100at.png ├── mnist_cifar10 ├── train.sh ├── eval.sh ├── utils.py ├── data_loader.py ├── train_utils.py ├── eval.py ├── train.py └── models.py ├── README.md └── LICENSE /tiny_imagenet/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/heatmaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uds-lsv/evaluating-logit-pairing-methods/HEAD/images/heatmaps.png -------------------------------------------------------------------------------- /images/histograms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uds-lsv/evaluating-logit-pairing-methods/HEAD/images/histograms.png -------------------------------------------------------------------------------- /images/loss_surfaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uds-lsv/evaluating-logit-pairing-methods/HEAD/images/loss_surfaces.png -------------------------------------------------------------------------------- /images/other/test_acc_50at_100at.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uds-lsv/evaluating-logit-pairing-methods/HEAD/images/other/test_acc_50at_100at.png -------------------------------------------------------------------------------- /images/other/train_loss_50at_100at.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uds-lsv/evaluating-logit-pairing-methods/HEAD/images/other/train_loss_50at_100at.png -------------------------------------------------------------------------------- /mnist_cifar10/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dataset='mnist' 4 | data_dir='/tmp' 5 | save_dir='/tmp' 6 | log_dir='/tmp' 7 | epochs=100 8 | 9 | python train.py \ 10 | --dataset=${dataset} \ 11 | --data_dir=${data_dir} \ 12 | --save_dir=${save_dir} \ 13 | --log_dir=${log_dir} \ 14 | --epochs=${epochs} -------------------------------------------------------------------------------- /mnist_cifar10/eval.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dataset='mnist' 4 | data_dir='/tmp' 5 | restore_path='/tmp' 6 | log_dir='/tmp' 7 | attack='PGD' 8 | samples=1000 9 | batch_size=1000 10 | restarts=1 11 | 12 | python eval.py \ 13 | --dataset=${dataset} \ 14 | --data_dir=${data_dir} \ 15 | --restore_path=${restore_path} \ 16 | --log_dir=${log_dir} \ 17 | --attack=${attack} \ 18 | --samples=${samples} \ 19 | --batch_size=${batch_size} \ 20 | --restarts=${restarts} 21 | 22 | -------------------------------------------------------------------------------- /tiny_imagenet/tiny_imagenet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ### This script is used to download Tiny ImageNet dataset and convert it to tf-records format 3 | 4 | # Download zip archive with TinyImagenet 5 | curl -O http://cs231n.stanford.edu/tiny-imagenet-200.zip 6 | 7 | # Extract archive 8 | unzip tiny-imagenet-200.zip 9 | 10 | # Convert dataset to TFRecord format 11 | mkdir tiny-imagenet-tfrecord 12 | python tiny_imagenet_converter/converter.py \ 13 | --input_dir=tiny-imagenet-200 \ 14 | --output_dir=tiny-imagenet-tfrecord 15 | 16 | 17 | -------------------------------------------------------------------------------- /tiny_imagenet/eval.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ### Runs evaluation of a trained model 3 | 4 | 5 | # Path to tf.records with Tiny ImageNet 6 | data_path="/scratch/maksym/tiny-imagenet-tfrecord" 7 | 8 | # Specify the model folder 9 | model_name="../models_github/LSQ" 10 | 11 | # Examples of evaluation methods: clean (only on test examples), pgdrnd_16_2_10 (PGD attack with a random target, 12 | # eps=16, step_size=2, n_iters=10), pgdll_16_4_400 (PGD attack with a least-likely target, eps=16, step_size=4, 13 | # n_iters=400) 14 | adv_method=clean 15 | 16 | # Number of random restarts of the PGD attack 17 | n_restarts=10 18 | 19 | # Number of test examples to evaluate on 20 | num_examples=1000 21 | 22 | export CUDA_VISIBLE_DEVICES=0 # Specify the GPU number 23 | python eval.py \ 24 | --train_dir=${model_name} \ 25 | --num_examples=${num_examples} \ 26 | --n_restarts=${n_restarts} \ 27 | --adv_method=${adv_method} \ 28 | --tiny_imagenet_data_dir=${data_path} 29 | 30 | -------------------------------------------------------------------------------- /tiny_imagenet/train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ### Runs synchronous training on 8 GPUs within the same server (starting from 0th GPU) 3 | 4 | 5 | # Path to tf.records with Tiny ImageNet 6 | data_path="/scratch/maksym/tiny-imagenet-tfrecord" 7 | 8 | # 100 epochs with batch size=256 means 100000/256 * 100 = 39062 steps 9 | max_steps=40000 10 | 11 | # Note, that the model name matters defines the method used. If it contains 'Plain' substring, the plain loss will be 12 | # used for training. If it contains '50% AT', adv. training with 50% adv. examples will be used. If it contains 13 | # '100% AT', adv. training with 100% adv. examples will be used. 14 | model_name="50% AT LL + ALP LL" 15 | 16 | # How to generate adv. examples for adv. training: clean (only on training examples), 17 | # pgdrnd_16_2_10 (PGD attack with a random target, eps=16, step_size=2, n_iters=10), 18 | # pgdll_16_2_10 (PGD attack with a least-likely target, eps=16, step_size=2, n_iters=10) 19 | adv_method=pgdll_16_2_10 # clean, pgd_16_2_10, pgdll_16_2_10, pgdrnd_16_2_10 20 | 21 | # strength of LSQ/CLP/ALP regularization 22 | train_lp_weight=0.5 23 | 24 | gpu_ids="0 1 2 3 4 5 6 7" 25 | 26 | n_tasks=8 # total number of workers (should match to the number of `gpu_ids`) 27 | 28 | # There are also other options, e.g.: --add_noise or --logit_squeezing. See `python train.py --helpfull` for details. 29 | 30 | # First run a parameter server on CPU 31 | export CUDA_VISIBLE_DEVICES= 32 | nohup python train.py \ 33 | --output_dir="models/${model_name}" \ 34 | --task_ids="${gpu_ids}" \ 35 | --job_name=ps \ 36 | --task=0 \ 37 | --worker_replicas=${n_tasks} \ 38 | --replicas_to_aggregate=${n_tasks} & 39 | 40 | # Then start 8 workers on 8 GPUs 41 | # note that batch_size=32 is per GPU, so the effective batch size will be 32*8=256 images 42 | for gpu_id in ${gpu_ids}; do 43 | task_id=$(( $gpu_id % $n_tasks )) 44 | echo $gpu_id $task_id 45 | export CUDA_VISIBLE_DEVICES=${gpu_id} 46 | nohup python train.py \ 47 | --max_steps=${max_steps} \ 48 | --model_name="resnet_v2_50" \ 49 | --hparams="train_adv_method=${adv_method},train_lp_weight=${train_lp_weight},batch_size=32" \ 50 | --output_dir="models/${model_name}" \ 51 | --tiny_imagenet_data_dir=${data_path} \ 52 | --task_ids="${gpu_ids}" \ 53 | --job_name=worker \ 54 | --task=${task_id} \ 55 | --worker_replicas=${n_tasks} \ 56 | --replicas_to_aggregate=${n_tasks} & 57 | done 58 | 59 | -------------------------------------------------------------------------------- /tiny_imagenet/datasets/dataset_factory.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google Inc. All Rights Reserved. 2 | # Modifications copyright (C) 2018, Maksym Andriushchenko . 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | """Library which creates datasets.""" 18 | 19 | from __future__ import absolute_import 20 | from __future__ import division 21 | from __future__ import print_function 22 | 23 | from datasets import imagenet_input 24 | from datasets import tiny_imagenet_input 25 | 26 | 27 | def get_dataset(dataset_name, split, batch_size, image_size, is_training, bounds): 28 | """Returns dataset. 29 | 30 | Args: 31 | dataset_name: name of the dataset, "imagenet" or "tiny_imagenet". 32 | split: name of the split, "train" or "validation". 33 | batch_size: size of the minibatch. 34 | image_size: size of the one side of the image. Output images will be 35 | resized to square shape image_size*image_size. 36 | is_training: if True then training preprocessing is done, otherwise eval 37 | preprocessing is done. 38 | 39 | Raises: 40 | ValueError: if dataset_name is invalid. 41 | 42 | Returns: 43 | dataset: instance of tf.data.Dataset with the dataset. 44 | num_examples: number of examples in given split of the dataset. 45 | num_classes: number of classes in the dataset. 46 | bounds: tuple with bounds of image values. All returned image pixels 47 | are between bounds[0] and bounds[1]. 48 | """ 49 | if dataset_name == 'tiny_imagenet': 50 | num_classes = 200 51 | dataset = tiny_imagenet_input.tiny_imagenet_input( 52 | split, batch_size, image_size, is_training, bounds) 53 | num_examples = tiny_imagenet_input.num_examples_per_epoch(split) 54 | elif dataset_name == 'imagenet': 55 | num_classes = 1001 56 | dataset = imagenet_input.imagenet_input( 57 | split, batch_size, image_size, bounds, is_training) 58 | num_examples = imagenet_input.num_examples_per_epoch(split) 59 | else: 60 | raise ValueError('Invalid dataset %s' % dataset_name) 61 | return dataset, num_examples, num_classes, bounds 62 | -------------------------------------------------------------------------------- /tiny_imagenet/eval_single.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | eval_id=3 4 | model_name=models_eval${eval_id}/baseline_nolabelsmooth_adam 5 | short_model_name=`echo ${model_name} | cut -d'/' -f 2` 6 | printf "${model_name} ${short_model_name}\n" 7 | 8 | 9 | gpu=0 10 | adv_method=clean # clean, pgdll_16_2_10, pgdll_16_1_20, pgdll_16_0.5_400, pgdll_16_0.1_1000 11 | n_restarts=1 12 | num_examples=10000 13 | export CUDA_VISIBLE_DEVICES=${gpu} 14 | nohup python eval.py \ 15 | --train_dir=${model_name} \ 16 | --num_examples=${num_examples} \ 17 | --n_restarts=${n_restarts} \ 18 | --dataset=tiny_imagenet \ 19 | --dataset_image_size=64 \ 20 | --adv_method=${adv_method} \ 21 | --hparams="eval_batch_size=10000" \ 22 | --eval_once=True \ 23 | --tiny_imagenet_data_dir="/scratch/maksym/tiny-imagenet-tfrecord" >> eval${eval_id}/${short_model_name}__${adv_method}.out & 24 | 25 | 26 | gpu=1 27 | adv_method=pgdrnd_16_2.0_10 # clean, pgdll_16_2_10, pgdll_16_1_20, pgdll_16_0.5_400, pgdll_16_0.1_1000 28 | n_restarts=1 29 | num_examples=1000 30 | export CUDA_VISIBLE_DEVICES=${gpu} 31 | nohup python eval.py \ 32 | --train_dir=${model_name} \ 33 | --num_examples=${num_examples} \ 34 | --n_restarts=${n_restarts} \ 35 | --dataset=tiny_imagenet \ 36 | --dataset_image_size=64 \ 37 | --adv_method=${adv_method} \ 38 | --hparams="eval_batch_size=1000" \ 39 | --eval_once=True \ 40 | --tiny_imagenet_data_dir="/scratch/maksym/tiny-imagenet-tfrecord" >> eval${eval_id}/${short_model_name}__${adv_method}.out & 41 | 42 | 43 | gpu=2 44 | adv_method=pgdll_16_2.0_10 # clean, pgdll_16_2_10, pgdll_16_1_20, pgdll_16_0.5_400, pgdll_16_0.1_1000 45 | n_restarts=1 46 | num_examples=1000 47 | export CUDA_VISIBLE_DEVICES=${gpu} 48 | nohup python eval.py \ 49 | --train_dir=${model_name} \ 50 | --num_examples=${num_examples} \ 51 | --n_restarts=${n_restarts} \ 52 | --dataset=tiny_imagenet \ 53 | --dataset_image_size=64 \ 54 | --adv_method=${adv_method} \ 55 | --hparams="eval_batch_size=1000" \ 56 | --eval_once=True \ 57 | --tiny_imagenet_data_dir="/scratch/maksym/tiny-imagenet-tfrecord" >> eval${eval_id}/${short_model_name}__${adv_method}.out & 58 | 59 | 60 | gpu=3 61 | adv_method=pgdrnd_16_4.0_400 # clean, pgdll_16_2_10, pgdll_16_1_20, pgdll_16_0.5_400, pgdll_16_0.1_1000 62 | n_restarts=100 63 | num_examples=1000 64 | export CUDA_VISIBLE_DEVICES=${gpu} 65 | nohup python eval.py \ 66 | --train_dir=${model_name} \ 67 | --num_examples=${num_examples} \ 68 | --n_restarts=${n_restarts} \ 69 | --dataset=tiny_imagenet \ 70 | --dataset_image_size=64 \ 71 | --adv_method=${adv_method} \ 72 | --hparams="eval_batch_size=1000" \ 73 | --eval_once=True \ 74 | --tiny_imagenet_data_dir="/scratch/maksym/tiny-imagenet-tfrecord" >> eval${eval_id}/${short_model_name}__${adv_method}.out & 75 | 76 | 77 | gpu=4 78 | adv_method=pgdll_16_4.0_400 # clean, pgdll_16_2_10, pgdll_16_1_20, pgdll_16_0.5_400, pgdll_16_0.1_1000 79 | n_restarts=100 80 | num_examples=1000 81 | export CUDA_VISIBLE_DEVICES=${gpu} 82 | nohup python eval.py \ 83 | --train_dir=${model_name} \ 84 | --num_examples=${num_examples} \ 85 | --n_restarts=${n_restarts} \ 86 | --dataset=tiny_imagenet \ 87 | --dataset_image_size=64 \ 88 | --adv_method=${adv_method} \ 89 | --hparams="eval_batch_size=1000" \ 90 | --eval_once=True \ 91 | --tiny_imagenet_data_dir="/scratch/maksym/tiny-imagenet-tfrecord" >> eval${eval_id}/${short_model_name}__${adv_method}.out & 92 | 93 | -------------------------------------------------------------------------------- /mnist_cifar10/utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | # ------------------------------------------------------------- 5 | # Helpers 6 | # ------------------------------------------------------------- 7 | 8 | def load_mnist(batch_size, data_dir, augmentation=False, stddev=0.0, adv_subset=1000, workers=4): 9 | from data_loader import get_mnist 10 | 11 | trainloader, _, classes = get_mnist(batch_size=batch_size, 12 | train=True, 13 | path=data_dir, 14 | augmentation=augmentation, 15 | std=stddev, 16 | shuffle=True, 17 | workers=workers 18 | ) 19 | 20 | testloader, _, _ = get_mnist(batch_size=batch_size, 21 | train=False, 22 | path=data_dir, 23 | shuffle=False, 24 | workers=workers 25 | ) 26 | 27 | adv_testloader, _, _ = get_mnist(batch_size=batch_size, 28 | train=False, 29 | path=data_dir, 30 | shuffle=False, 31 | adversarial=True, 32 | subset=adv_subset, 33 | workers=workers 34 | ) 35 | 36 | input_shape = (None, 28, 28, 1) 37 | 38 | return trainloader, testloader, adv_testloader, input_shape, len(classes) 39 | 40 | 41 | def load_cifar10(batch_size, data_dir, augmentation=False, stddev=0.0, adv_subset=1000, workers=4): 42 | from data_loader import get_cifar10 43 | 44 | trainloader, _, classes = get_cifar10(batch_size=batch_size, 45 | train=True, 46 | path=data_dir, 47 | augmentation=augmentation, 48 | std=stddev, 49 | shuffle=True, 50 | workers=workers 51 | ) 52 | 53 | testloader, _, _ = get_cifar10(batch_size=batch_size, 54 | train=False, 55 | path=data_dir, 56 | shuffle=False, 57 | workers=workers 58 | ) 59 | 60 | adv_testloader, _, _ = get_cifar10(batch_size=batch_size, 61 | train=False, 62 | path=data_dir, 63 | shuffle=False, 64 | adversarial=True, 65 | subset=adv_subset, 66 | workers=workers 67 | ) 68 | 69 | input_shape = (None, 32, 32, 3) 70 | 71 | return trainloader, testloader, adv_testloader, input_shape, len(classes) 72 | 73 | 74 | def variable_summaries(var, name=None, collections=['training'], histo=True): 75 | with tf.device('/gpu:0'): 76 | if name is None: 77 | name = var.op.name 78 | 79 | var_shape = var.get_shape().as_list() 80 | var_dim = 1.0 81 | for dim in var_shape[1:]: 82 | var_dim *= dim 83 | 84 | with tf.name_scope('Compute-Mean'): 85 | mean = tf.reduce_mean(var) 86 | 87 | with tf.name_scope('Compute-Stddev'): 88 | stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean))) 89 | 90 | with tf.name_scope('Compute-Max'): 91 | max = tf.reduce_max(var) 92 | 93 | with tf.name_scope('Compute-Min'): 94 | min = tf.reduce_min(var) 95 | 96 | # Write summaries 97 | tf.summary.scalar(name + '/mean', mean, collections=collections) 98 | tf.summary.scalar(name + '/stddev', stddev, collections=collections) 99 | tf.summary.scalar(name + '/max', max, collections=collections) 100 | tf.summary.scalar(name + '/min', min, collections=collections) 101 | if histo: 102 | tf.summary.histogram(name, tf.identity(var), collections=collections) 103 | -------------------------------------------------------------------------------- /mnist_cifar10/data_loader.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torchvision 4 | import torchvision.transforms as transforms 5 | from torch.utils.data.sampler import Sampler 6 | 7 | 8 | def get_mnist(batch_size, train, path, augmentation=False, std=0.0, shuffle=True, adversarial=False, subset=1000, 9 | workers=0): 10 | classes = np.arange(0, 10) 11 | 12 | if augmentation: 13 | transform = transforms.Compose([ 14 | transforms.ToTensor(), 15 | transforms.Lambda(lambda x: x + torch.Tensor(x.size()).normal_(mean=0.0, std=std)), # add gaussian noise 16 | transforms.Lambda(lambda x: torch.clamp(x, 0.0, 1.0)) 17 | ]) 18 | else: 19 | transform = transforms.Compose([ 20 | transforms.ToTensor(), 21 | transforms.Lambda(lambda x: torch.clamp(x, 0.0, 1.0)) 22 | ]) 23 | 24 | dataset = torchvision.datasets.MNIST(root=path, 25 | train=train, 26 | download=True, 27 | transform=transform) 28 | 29 | if adversarial: 30 | np.random.seed(123) # load always the same random subset 31 | indices = np.random.choice(np.arange(dataset.__len__()), subset) 32 | 33 | subset_sampler = SubsetSampler(indices) 34 | 35 | dataloader = torch.utils.data.DataLoader(dataset, 36 | batch_size=batch_size, 37 | shuffle=False, 38 | sampler=subset_sampler, 39 | num_workers=workers 40 | ) 41 | 42 | else: 43 | dataloader = torch.utils.data.DataLoader(dataset, 44 | batch_size=batch_size, 45 | shuffle=shuffle, 46 | num_workers=workers 47 | ) 48 | return dataloader, dataset, classes 49 | 50 | 51 | def get_cifar10(batch_size, train, path, augmentation=False, std=0.0, shuffle=True, adversarial=False, subset=1000, 52 | workers=0): 53 | classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] 54 | 55 | if augmentation: 56 | transform = transforms.Compose([ 57 | transforms.ToTensor(), 58 | transforms.Lambda(lambda x: x + torch.Tensor(x.size()).normal_(0.0, std)), # add gaussian noise 59 | transforms.Lambda(lambda x: torch.clamp(x, 0.0, 1.0)) 60 | ]) 61 | else: 62 | transform = transforms.Compose([ 63 | transforms.ToTensor(), 64 | transforms.Lambda(lambda x: torch.clamp(x, 0.0, 1.0)) 65 | ]) 66 | 67 | dataset = torchvision.datasets.CIFAR10(root=path, 68 | train=train, 69 | download=True, 70 | transform=transform) 71 | 72 | if adversarial: 73 | np.random.seed(123) # load always the same random subset 74 | indices = np.random.choice(np.arange(dataset.__len__()), subset) 75 | 76 | subset_sampler = SubsetSampler(indices) 77 | dataloader = torch.utils.data.DataLoader(dataset, 78 | batch_size=batch_size, 79 | shuffle=False, 80 | sampler=subset_sampler, 81 | num_workers=workers 82 | ) 83 | else: 84 | dataloader = torch.utils.data.DataLoader(dataset, 85 | batch_size=batch_size, 86 | shuffle=shuffle, 87 | num_workers=workers 88 | ) 89 | return dataloader, dataset, classes 90 | 91 | 92 | class SubsetSampler(Sampler): 93 | def __init__(self, indices): 94 | self.indices = indices 95 | self.shuffle = False 96 | 97 | def __iter__(self): 98 | if self.shuffle: 99 | np.random.shuffle(self.indices) 100 | return (self.indices[i] for i in range(len(self.indices))) 101 | 102 | def __len__(self): 103 | return len(self.indices) 104 | 105 | def set_shuffle(self, shuffle): 106 | self.shuffle = shuffle 107 | -------------------------------------------------------------------------------- /tiny_imagenet/datasets/tiny_imagenet_input.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google Inc. All Rights Reserved. 2 | # Modifications copyright (C) 2018, Maksym Andriushchenko . 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | """Tiny imagenet input.""" 18 | 19 | from __future__ import absolute_import 20 | from __future__ import division 21 | from __future__ import print_function 22 | 23 | import os 24 | from absl import flags 25 | import tensorflow as tf 26 | 27 | FLAGS = flags.FLAGS 28 | 29 | 30 | flags.DEFINE_string('tiny_imagenet_data_dir', None, 31 | 'Directory with Tiny Imagenet dataset in TFRecord format.') 32 | 33 | 34 | def tiny_imagenet_parser(value, image_size, is_training, bounds): 35 | """Parses tiny imagenet example. 36 | 37 | Args: 38 | value: encoded example. 39 | image_size: size of the image. 40 | is_training: if True then do training preprocessing (which includes 41 | random cropping), otherwise do eval preprocessing. 42 | 43 | Returns: 44 | image: tensor with the image. 45 | label: true label of the image. 46 | """ 47 | keys_to_features = { 48 | 'image/encoded': tf.FixedLenFeature((), tf.string, ''), 49 | 'label/tiny_imagenet': tf.FixedLenFeature([], tf.int64, -1), 50 | } 51 | 52 | parsed = tf.parse_single_example(value, keys_to_features) 53 | 54 | image_buffer = tf.reshape(parsed['image/encoded'], shape=[]) 55 | image = tf.image.decode_image(image_buffer, channels=3) 56 | image = tf.image.convert_image_dtype( 57 | image, dtype=tf.float32) 58 | 59 | # Crop image 60 | if is_training: 61 | bbox_begin, bbox_size, _ = tf.image.sample_distorted_bounding_box( 62 | tf.shape(image), 63 | bounding_boxes=tf.constant([0.0, 0.0, 1.0, 1.0], 64 | dtype=tf.float32, 65 | shape=[1, 1, 4]), 66 | min_object_covered=0.5, 67 | aspect_ratio_range=[0.75, 1.33], 68 | area_range=[0.5, 1.0], 69 | max_attempts=20, 70 | use_image_if_no_bounding_boxes=True) 71 | image = tf.slice(image, bbox_begin, bbox_size) 72 | # Data augmentation 73 | image = tf.image.random_flip_left_right(image) 74 | image = tf.image.random_saturation(image, 0.5, 2.0) 75 | 76 | # resize image 77 | image = tf.image.resize_bicubic([image], [image_size, image_size])[0] 78 | 79 | # Rescale image to [-1, 1] range. 80 | if bounds == (-1, 1): 81 | image = tf.multiply(tf.subtract(image, 0.5), 2.0) 82 | 83 | image = tf.reshape(image, [image_size, image_size, 3]) 84 | 85 | # Labels are in [0, 199] range 86 | label = tf.cast( 87 | tf.reshape(parsed['label/tiny_imagenet'], shape=[]), dtype=tf.int32) 88 | 89 | return image, label 90 | 91 | 92 | def tiny_imagenet_input(split, batch_size, image_size, is_training, bounds): 93 | """Returns Tiny Imagenet Dataset. 94 | 95 | Args: 96 | split: name of the split, "train" or "validation". 97 | batch_size: size of the minibatch. 98 | image_size: size of the one side of the image. Output images will be 99 | resized to square shape image_size*image_size. 100 | is_training: if True then training preprocessing is done, otherwise eval 101 | preprocessing is done.instance of tf.data.Dataset with the dataset. 102 | 103 | Raises: 104 | ValueError: if name of the split is incorrect. 105 | 106 | Returns: 107 | Instance of tf.data.Dataset with the dataset. 108 | """ 109 | if split.lower().startswith('train'): 110 | filepath = os.path.join(FLAGS.tiny_imagenet_data_dir, 'train.tfrecord') 111 | elif split.lower().startswith('validation'): 112 | filepath = os.path.join(FLAGS.tiny_imagenet_data_dir, 'validation.tfrecord') 113 | else: 114 | raise ValueError('Invalid split: %s' % split) 115 | 116 | dataset = tf.data.TFRecordDataset(filepath, buffer_size=8*1024*1024) 117 | 118 | if is_training: 119 | dataset = dataset.shuffle(100000) 120 | dataset = dataset.repeat() 121 | 122 | 123 | dataset = dataset.apply( 124 | tf.contrib.data.map_and_batch( 125 | lambda value: tiny_imagenet_parser(value, image_size, is_training, bounds), 126 | batch_size=batch_size, 127 | num_parallel_batches=4, 128 | drop_remainder=True)) 129 | 130 | def set_shapes(images, labels): 131 | """Statically set the batch_size dimension.""" 132 | images.set_shape(images.get_shape().merge_with( 133 | tf.TensorShape([batch_size, None, None, None]))) 134 | labels.set_shape(labels.get_shape().merge_with( 135 | tf.TensorShape([batch_size]))) 136 | return images, labels 137 | 138 | # Assign static batch size dimension 139 | dataset = dataset.map(set_shapes) 140 | 141 | dataset = dataset.prefetch(tf.contrib.data.AUTOTUNE) 142 | 143 | return dataset 144 | 145 | 146 | def num_examples_per_epoch(split): 147 | """Returns the number of examples in the data set. 148 | 149 | Args: 150 | split: name of the split, "train" or "validation". 151 | 152 | Raises: 153 | ValueError: if split name is incorrect. 154 | 155 | Returns: 156 | Number of example in the split. 157 | """ 158 | if split.lower().startswith('train'): 159 | return 100000 160 | elif split.lower().startswith('validation'): 161 | return 10000 162 | else: 163 | raise ValueError('Invalid split: %s' % split) 164 | -------------------------------------------------------------------------------- /tiny_imagenet/eval.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google Inc. All Rights Reserved. 2 | # Modifications copyright (C) 2018, Maksym Andriushchenko . 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | """Program which runs evaluation of Tiny Imagenet models.""" 18 | 19 | from __future__ import absolute_import 20 | from __future__ import division 21 | from __future__ import print_function 22 | 23 | import os 24 | 25 | from absl import app 26 | from absl import flags 27 | 28 | import tensorflow as tf 29 | 30 | import adversarial_attack 31 | import model_lib 32 | import time 33 | import numpy as np 34 | from datasets import dataset_factory 35 | 36 | FLAGS = flags.FLAGS 37 | 38 | 39 | flags.DEFINE_string('train_dir', None, 40 | 'Directory with a checkpoint file to restore a model.') 41 | 42 | flags.DEFINE_string('output_file', None, 43 | 'Name of output file. Used only in single evaluation mode.') 44 | 45 | flags.DEFINE_string('eval_name', 'default', 'Name for eval subdirectory.') 46 | 47 | flags.DEFINE_string('model_name', 'resnet_v2_50', 'Name of the model.') 48 | 49 | flags.DEFINE_string('adv_method', 'clean', 50 | 'Method which is used to generate adversarial examples.') 51 | 52 | flags.DEFINE_integer('n_restarts', 1, 53 | 'Number of restarts for the PGD attack.') 54 | 55 | flags.DEFINE_string('hparams', '', 'Hyper parameters.') 56 | 57 | flags.DEFINE_float('moving_average_decay', 0.9999, 58 | 'The decay to use for the moving average.') 59 | 60 | flags.DEFINE_integer( 61 | 'num_examples', -1, 62 | 'The number of example to use for evaluation. Note that all of them should fit in 1 GPU.') 63 | 64 | 65 | def get_latest_checkpoint(train_dir): 66 | ckpt_files = [train_dir + '/' + fname for fname in os.listdir(train_dir) if 'ckpt' in fname] 67 | last_mtime, last_fname = 0.0, '' 68 | for cur_fname in ckpt_files: 69 | cur_mtime = os.stat(cur_fname).st_mtime # modification time 70 | if cur_mtime > last_mtime: 71 | last_mtime = cur_mtime 72 | last_fname = cur_fname 73 | last_fname = last_fname.replace('.meta', '').replace('.index', '').replace('.data-00000-of-00001', '') 74 | return last_fname 75 | 76 | 77 | def main(_): 78 | num_examples = FLAGS.num_examples 79 | params = model_lib.default_hparams() 80 | params.parse(FLAGS.hparams) 81 | tf.logging.info('User provided hparams: %s', FLAGS.hparams) 82 | tf.logging.info('All hyper parameters: %s', params) 83 | graph = tf.Graph() 84 | with graph.as_default(): 85 | img_bounds = (-1, 1) if 'Fine-tuned' in FLAGS.train_dir else (0, 1) 86 | dataset, total_num_examples, num_classes, bounds = dataset_factory.get_dataset( 87 | 'tiny_imagenet', 88 | 'validation', 89 | 10000, 90 | 64, 91 | is_training=False, 92 | bounds=img_bounds) 93 | dataset_iterator = dataset.make_one_shot_iterator() 94 | images, labels = dataset_iterator.get_next() 95 | images, labels = images[:num_examples], labels[:num_examples] 96 | 97 | # setup model 98 | global_step = tf.train.get_or_create_global_step() 99 | model_fn_two_args = model_lib.get_model(FLAGS.model_name, num_classes) 100 | model_fn = lambda x: model_fn_two_args(x, is_training=False) # thin wrapper; args: images, is_training 101 | 102 | # clean first 103 | clean_logits = model_fn(images) 104 | 105 | labels_for_ae = tf.identity(labels) # used only if 'rnd' in FLAGS.adv_method and FLAGS.n_restarts > 0 106 | if FLAGS.adv_method == 'clean': 107 | logits = clean_logits 108 | else: 109 | adv_examples = adversarial_attack.generate_adversarial_examples( 110 | images, labels_for_ae, bounds, model_fn, FLAGS.adv_method, n_restarts=FLAGS.n_restarts) 111 | adv_logits = model_fn(adv_examples) 112 | logits = adv_logits 113 | 114 | correct = tf.equal(tf.argmax(logits, 1), tf.to_int64(labels)) 115 | # correct = tf.equal(tf.argmax(adv_logits, 1), tf.argmax(clean_logits, 1)) 116 | acc = tf.reduce_mean(tf.cast(correct, tf.float32)) 117 | 118 | # Setup the moving averages 119 | variable_averages = tf.train.ExponentialMovingAverage( 120 | FLAGS.moving_average_decay, global_step) 121 | variables_to_restore = variable_averages.variables_to_restore( 122 | tf.contrib.framework.get_model_variables()) 123 | variables_to_restore[global_step.op.name] = global_step 124 | 125 | saver = tf.train.Saver() 126 | 127 | gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.7) 128 | config = tf.ConfigProto(gpu_options=gpu_options) 129 | with tf.Session(graph=graph, config=config) as sess: 130 | saver.restore(sess, get_latest_checkpoint(FLAGS.train_dir)) 131 | correct_vals = np.zeros([FLAGS.n_restarts, num_examples]) 132 | time_start = time.time() 133 | # we select random classes only once, and fix them across multiple restarts 134 | labels_for_rnd_multrest = np.random.random_integers(0, num_classes-1, size=[num_examples, ]) 135 | for i_restart in range(FLAGS.n_restarts): 136 | logits_val, correct_val, acc_val = sess.run([logits, correct, acc], feed_dict={labels_for_ae: labels_for_rnd_multrest}) 137 | print('Accuracy: {:.2%}'.format(acc_val)) 138 | correct_vals[i_restart, :] = correct_val 139 | 140 | print('[Elapsed {:.2f} min] avg_acc={:.2%} min_acc={:.2%} (n_restarts={})'. 141 | format((time.time() - time_start) / 60, correct_vals.mean(), correct_vals.min(axis=0).mean(), FLAGS.n_restarts)) 142 | 143 | 144 | if __name__ == '__main__': 145 | app.run(main) 146 | -------------------------------------------------------------------------------- /tiny_imagenet/model_lib.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google Inc. All Rights Reserved. 2 | # Modifications copyright (C) 2018, Maksym Andriushchenko . 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | """Library with common functions for training and eval.""" 18 | 19 | from __future__ import absolute_import 20 | from __future__ import division 21 | from __future__ import print_function 22 | 23 | import six 24 | 25 | import tensorflow as tf 26 | 27 | from tensorflow.contrib.slim.nets import resnet_v2 28 | 29 | 30 | def default_hparams(): 31 | """Returns default hyperparameters.""" 32 | optimizer = 'adam' # possible values are: 'rms', 'momentum', 'adam' 33 | 34 | if optimizer == 'rms': 35 | lr = 0.045 36 | lr_list = [1.0 / 6, 2.0 / 6, 3.0 / 6, 4.0 / 6, 5.0 / 6, 1.0, 0.1, 0.01, 0.001, 0.0001] 37 | lr_decay_epochs = [1, 2, 3, 4, 5, 30, 60, 80, 90] 38 | elif optimizer == 'adam': 39 | lr = 0.001 40 | lr_list = [lr, lr/10, lr/100] 41 | lr_decay_epochs = [80, 90] 42 | else: 43 | lr = 0.1 44 | lr_list = [lr, lr/10, lr/100] 45 | lr_decay_epochs = [80, 90] 46 | return tf.contrib.training.HParams( 47 | # Batch size for training and evaluation. 48 | batch_size=128, 49 | eval_batch_size=50, 50 | 51 | # General training parameters. 52 | weight_decay=0.0001, 53 | label_smoothing=0.1, 54 | 55 | # Parameters of the adversarial training. 56 | train_adv_method='clean', # adversarial training method 57 | train_lp_weight=0.0, # Weight of adversarial logit pairing loss 58 | 59 | # Parameters of the optimizer. 60 | optimizer=optimizer, 61 | learning_rate=lr, 62 | momentum=0.9, # momentum 63 | rmsprop_decay=0.9, # Decay term for RMSProp 64 | rmsprop_epsilon=1.0, # Epsilon term for RMSProp 65 | 66 | # Parameters of learning rate schedule. 67 | lr_schedule='step', # Possible values: 'exp_decay', 'step', 'fixed' 68 | lr_decay_factor=0.94, # Learning exponential decay 69 | lr_num_epochs_per_decay=2.0, # Number of epochs per lr decay 70 | lr_list=lr_list, 71 | lr_decay_epochs=lr_decay_epochs) 72 | 73 | 74 | def get_lr_schedule(hparams, examples_per_epoch, replicas_to_aggregate=1): 75 | """Returns TensorFlow op which compute learning rate. 76 | 77 | Args: 78 | hparams: hyper parameters. 79 | examples_per_epoch: number of training examples per epoch. 80 | replicas_to_aggregate: number of training replicas running in parallel. 81 | 82 | Raises: 83 | ValueError: if learning rate schedule specified in hparams is incorrect. 84 | 85 | Returns: 86 | learning_rate: tensor with learning rate. 87 | steps_per_epoch: number of training steps per epoch. 88 | """ 89 | global_step = tf.train.get_or_create_global_step() 90 | steps_per_epoch = float(examples_per_epoch) / float(hparams.batch_size) 91 | if replicas_to_aggregate > 0: 92 | steps_per_epoch /= replicas_to_aggregate 93 | 94 | if hparams.lr_schedule == 'exp_decay': 95 | decay_steps = long(steps_per_epoch * hparams.lr_num_epochs_per_decay) 96 | learning_rate = tf.train.exponential_decay( 97 | hparams.learning_rate, 98 | global_step, 99 | decay_steps, 100 | hparams.lr_decay_factor, 101 | staircase=True) 102 | elif hparams.lr_schedule == 'step': 103 | lr_decay_steps = [long(epoch * steps_per_epoch) 104 | for epoch in hparams.lr_decay_epochs] 105 | learning_rate = tf.train.piecewise_constant( 106 | global_step, lr_decay_steps, hparams.lr_list) 107 | elif hparams.lr_schedule == 'fixed': 108 | learning_rate = hparams.learning_rate 109 | else: 110 | raise ValueError('Invalid value of lr_schedule: %s' % hparams.lr_schedule) 111 | 112 | # learning_rate *= batch_size // 32 # we increase the learning rate as much as batch size is larger than 32 113 | # if replicas_to_aggregate > 0: 114 | # learning_rate *= replicas_to_aggregate 115 | 116 | return learning_rate, steps_per_epoch 117 | 118 | 119 | def get_optimizer(hparams, learning_rate): 120 | """Returns optimizer. 121 | 122 | Args: 123 | hparams: hyper parameters. 124 | learning_rate: learning rate tensor. 125 | 126 | Raises: 127 | ValueError: if type of optimizer specified in hparams is incorrect. 128 | 129 | Returns: 130 | Instance of optimizer class. 131 | """ 132 | if hparams.optimizer == 'rms': 133 | optimizer = tf.train.RMSPropOptimizer(learning_rate, 134 | hparams.rmsprop_decay, 135 | hparams.momentum, 136 | hparams.rmsprop_epsilon) 137 | elif hparams.optimizer == 'momentum': 138 | optimizer = tf.train.MomentumOptimizer(learning_rate, 139 | hparams.momentum) 140 | elif hparams.optimizer == 'adam': 141 | optimizer = tf.train.AdamOptimizer(learning_rate) 142 | else: 143 | raise ValueError('Invalid value of optimizer: %s' % hparams.optimizer) 144 | return optimizer 145 | 146 | 147 | RESNET_MODELS = {'resnet_v2_50': resnet_v2.resnet_v2_50} 148 | 149 | 150 | def get_model(model_name, num_classes): 151 | """Returns function which creates model. 152 | 153 | Args: 154 | model_name: Name of the model. 155 | num_classes: Number of classes. 156 | 157 | Raises: 158 | ValueError: If model_name is invalid. 159 | 160 | Returns: 161 | Function, which creates model when called. 162 | """ 163 | if model_name.startswith('resnet'): 164 | def resnet_model(images, is_training, reuse=tf.AUTO_REUSE): 165 | with tf.contrib.framework.arg_scope(resnet_v2.resnet_arg_scope()): 166 | resnet_fn = RESNET_MODELS[model_name] 167 | logits, _ = resnet_fn(images, num_classes, is_training=is_training, 168 | reuse=reuse) 169 | logits = tf.reshape(logits, [-1, num_classes]) # from [bs x 1 x 1 x num_classes] to [bs x num_classes] 170 | return logits 171 | return resnet_model 172 | else: 173 | raise ValueError('Invalid model: %s' % model_name) 174 | 175 | 176 | def filter_trainable_variables(trainable_scopes): 177 | """Keep only trainable variables which are prefixed with given scopes. 178 | 179 | Args: 180 | trainable_scopes: either list of trainable scopes or string with comma 181 | separated list of trainable scopes. 182 | 183 | This function removes all variables which are not prefixed with given 184 | trainable_scopes from collection of trainable variables. 185 | Useful during network fine tuning, when you only need to train subset of 186 | variables. 187 | """ 188 | # for var in tf.trainable_variables(): 189 | # print(var) 190 | # raise ValueError 191 | 192 | if not trainable_scopes: 193 | return 194 | if isinstance(trainable_scopes, six.string_types): 195 | trainable_scopes = [scope.strip() for scope in trainable_scopes.split(',')] 196 | trainable_scopes = {scope for scope in trainable_scopes if scope} 197 | if not trainable_scopes: 198 | return 199 | 200 | trainable_collection = tf.get_collection_ref( 201 | tf.GraphKeys.TRAINABLE_VARIABLES) 202 | non_trainable_vars = [ 203 | v for v in trainable_collection 204 | if not any([v.op.name.startswith(s) for s in trainable_scopes]) 205 | ] 206 | for v in non_trainable_vars: 207 | trainable_collection.remove(v) 208 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Logit Pairing Methods Can Fool Gradient-Based Attacks](https://arxiv.org/abs/1810.12042) 2 | #### [Presented at NeurIPS 2018 Workshop on Security in Machine Learning](https://secml2018.github.io/) 3 | #### Marius Mosbach*, Maksym Andriushchenko*, Thomas Trost, Matthias Hein, Dietrich Klakow 4 | #### Saarland University and University of Tübingen 5 | 6 | 7 | ## Main findings 8 | The proposed logit regularization methods in ["Adversarial Logit Pairing"](https://arxiv.org/abs/1803.06373) distort the input loss surface which makes gradient-based attacks difficult. 9 | ![](images/loss_surfaces.png) 10 | 11 | 12 | We highlight that a proper evaluation of the adv. robustness is still an unresolved task. 13 | The PGD attack with the default settings is not necessarily a strong attack. Testing different attack hyperparameters (the number of iterations, stepsize) is crucial when evaluating the robustness of a new defense. 14 | ![](images/heatmaps.png) 15 | 16 | Performing many random restarts is very important when an attack deteriorates the loss surface. Below is an example of the loss values achieved by the PGD attack on different test points (a CLP model, MNIST). Using only 1 random restart (a standard practice) may be not sufficient to find an adversarial example. 17 | ![](images/histograms.png) 18 | The vertical red line denotes the loss value −ln(0.1), which guarantees that for this and a higher value of the loss 19 | we find an adversarial example. 20 | 21 | 22 | ## Results and models 23 | In the table below, we provide our independently trained models on MNIST, CIFAR-10, and Tiny ImageNet together with the [Fine-tuned Plain + ALP LL]() model released [here](https://github.com/tensorflow/models/tree/master/research/adversarial_logit_pairing). 24 | 25 | Note that we could break CLP, LSQ, and some ALP models only by using the PGD attack with many iterations and many random restarts. 26 | This is computationally expensive, thus an interesting direction is to come up with new attacks that can break such models with less computational efforts. 27 | For this purposes, we encourage researchers to use the models given below for an evaluation of new attacks. 28 | 29 | #### MNIST 30 | | Model | Accuracy | Adversarial accuracy
with the baseline attack | Adversarial accuracy
with our strongest attack| 31 | | ----------- | -----: | -----: | -----: | 32 | | [Plain](https://oc.cs.uni-saarland.de/owncloud/index.php/s/7SppqtzC5PB8kz4) | **99.2%** | 0.0% | 0.0% | 33 | | [CLP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/w2yegcfx8mc8kNa) | 98.8% | 62.4% | 4.1% | 34 | | [LSQ](https://oc.cs.uni-saarland.de/owncloud/index.php/s/a5ZY72BDCPEtb2S) | 98.8% | 70.6% | 5.0% | 35 | | [Plain + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/Sm2zqLwCBp8JH7z) | 98.5% | 96.0% | 88.9% | 36 | | [50% AT + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/XQ4ZSHjWiE4HB3B) | 98.3% | **97.2%** | **89.9%** | 37 | | [50% AT](https://oc.cs.uni-saarland.de/owncloud/index.php/s/LCtkkL6kHz4YZLo) | 99.1% | 92.7% | 88.2% | 38 | | [100% AT + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/ESXgi3LYfHgNc5F) | 98.4% | 96.6% | 85.7% | 39 | | [100% AT](https://oc.cs.uni-saarland.de/owncloud/index.php/s/cPA5HFYcgEoskMS) | 98.9% | 95.2% | 88.0% | 40 | 41 | 42 | #### CIFAR-10 43 | | Model | Accuracy | Adversarial accuracy
with the baseline attack | Adversarial accuracy
with our strongest attack| 44 | | ----------- | -----: | -----: | -----: | 45 | | [Plain](https://oc.cs.uni-saarland.de/owncloud/index.php/s/NKojskBc5377YbP) | **83.0%** | 0.0% | 0.0% | 46 | | [CLP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/odcd7FgFdbqq6zL) | 73.9% | 2.8% | 0.0% | 47 | | [LSQ](https://oc.cs.uni-saarland.de/owncloud/index.php/s/EYnbHDeMbe4mq5M) | 81.7% | **27.0%** | 1.7% | 48 | | [Plain + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/MPneF76Njp2ZmEj) | 71.5% | 23.6% | **10.7%** | 49 | | [50% AT + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/ToYDkSQoeQxqfyX) | 70.4% | 21.8% | 10.5% | 50 | | [50% AT](https://oc.cs.uni-saarland.de/owncloud/index.php/s/P66RwZjdKxsjCgr) | 73.8% | 18.6% | 7.3% | 51 | | [100% AT + ALP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/MyPkqTf8JSWtsx7) | 65.7% | 19.0% | 6.4% | 52 | | [100% AT](https://oc.cs.uni-saarland.de/owncloud/index.php/s/pBr62SQzCgK2iR2) | 65.7% | 16.0% | 6.7% | 53 | 54 | 55 | #### Tiny ImageNet 56 | | Model | Accuracy | Adversarial accuracy
with the baseline attack | Adversarial accuracy
with our strongest attack| 57 | | ----------- | -----: | -----: | -----: | 58 | | [Plain](https://oc.cs.uni-saarland.de/owncloud/index.php/s/PSDQwxyix5PAkwM) | 53.0% | 3.9% | 0.4% | 59 | | [CLP](https://oc.cs.uni-saarland.de/owncloud/index.php/s/X4M97pp5ZMMdDnF) | 48.5% | 12.2% | 0.7% | 60 | | [LSQ](https://oc.cs.uni-saarland.de/owncloud/index.php/s/KLC97NCebtyKXtS) | 49.4% | 12.8% | 0.8% | 61 | | [Plain + ALP LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/5jjw9CzABWGXrKz) | 53.5% | 17.2% | 0.8% | 62 | | [Fine-tuned Plain + ALP LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/x6M6aZCK8GYEQgf) | **72.0%** | **31.8%** | 3.6% | 63 | | [50% AT LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/8FKqzd862bm6b5k) | 46.3% | 25.1% | 9.4% | 64 | | [50% AT LL + ALP LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/qp3o4GLCQiB69Jb) | 45.2% | 26.3% | 13.5% | 65 | | [100% AT LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/xcMBbdwt7xKBj4m) | 41.2% | 25.5% | 16.3% | 66 | | [100% AT LL + ALP LL](https://oc.cs.uni-saarland.de/owncloud/index.php/s/b4fsTjiPQ6DgF6t) | 37.0% | 25.4% | **16.5%** | 67 | 68 | 69 | ## Requirements 70 | * For Tiny ImageNet code: TensorFlow 1.8 and Python 2.7 71 | * For MNIST and CIFAR-10 code: Tensorflow 1.8, PyTorch0.4, CleverHans 3.0.1, and Python 3.5 72 | * In order to set up [Tiny ImageNet](https://tiny-imagenet.herokuapp.com/) 73 | dataset in the correct format (tf.records), run `tiny_imagenet.sh`. 74 | 75 | 76 | ## How to run the code 77 | 78 | ### Tiny ImageNet code 79 | You can find examples on how to run the code for Tiny ImageNet in the following scripts: 80 | * `train.sh` Trains a model from scratch. 81 | * `eval.sh` Evaluates an existing model (see the table for the particular Tiny ImageNet models). Note, that with this script you might not be able to 82 | reproduce the results from the paper exactly, since for those evaluations the random seed was not fixed. 83 | But the results are expected to be very close to the reported ones. 84 | 85 | More details are available by `python