![No Maintenance Intended](https://img.shields.io/badge/No%20Maintenance%20Intended-%E2%9C%95-red.svg)
![TensorFlow Requirement: 1.x](https://img.shields.io/badge/TensorFlow%20Requirement-1.x-brightgreen)
![TensorFlow 2 Not Supported](https://img.shields.io/badge/TensorFlow%202%20Not%20Supported-%E2%9C%95-red.svg)

# Adversarial logit pairing

This directory contains implementation of
[Adversarial logit pairing](https://arxiv.org/abs/1803.06373) paper as well as
few models pre-trained on ImageNet and Tiny ImageNet.

Please contact [Alexey Kurakin](https://github.com/AlexeyKurakin) regarding
this code.

## Pre-requesites

Code dependencies:

* TensorFlow 1.8 and Python 2.7 (other versions may work, but were not tested)
* [Abseil Python](https://github.com/abseil/abseil-py).
* Script which converts Tiny Imagenet dataset into TFRecord format also
  depends on [Pandas](https://pandas.pydata.org/).

## Datasets

To use this code you need to download datasets. You only need to download
those datasets which you're going to use. Following list of datasets is
supported:

* [ImageNet](http://www.image-net.org/). Follow
  [Preparing the datasets](https://github.com/tensorflow/models/tree/master/research/slim#Data)
  instructions in TF-Slim documentation to download and convert ImageNet dataset
  to TFRecord format.

* [Tiny ImageNet](https://tiny-imagenet.herokuapp.com/).
  To obtain Tiny ImageNet dataset do following:

  ```
  # Download zip archive with TinyImagenet
  curl -O http://cs231n.stanford.edu/tiny-imagenet-200.zip

  # Extract archive
  unzip tiny-imagenet-200.zip

  # Convert dataset to TFRecord format
  mkdir tiny-imagenet-tfrecord
  python tiny_imagenet_converter/converter.py \
    --input_dir=tiny-imagenet-200 \
    --output_dir=tiny-imagenet-tfrecord
  ```

## Running the code

NOTE: Provided code supports distributed training on multiple machines,
and all provided checkpoints were trained in a distributed way. However it is
beyond the scope of this document to describe how to do distributed training.
Readed should refer to
[other material](https://www.tensorflow.org/deploy/distributed) to learn
about it.

### Training

Following command runs training:

```
# Following arguments has to be specified for training:
# - MAX_NUMBER_OF_TRAINING_STEPS - maximum number of training steps,
#     omit this flag or set it to -1 to have unlimited number of training steps.
# - MODEL_NAME - name of the model, now only "resnet_v2_50" is supported.
# - MOVING_AVG_DECAY - decay rate for exponential moving average of the
#     trainable variables. Training with exponential moving average usually
#     leads to better accuracy. Default of 0.9999. -1 disable exponential moving
#     average. Default works well, so typically you set it only if you want
#     to disable this feature.
# - HYPERPARAMETERS - string with hyperparameters,
#     see model_lib.py for full list of hyperparameters.
# - DATASET - dataset, either "imagenet" or "tiny_imagenet".
# - IMAGE_SIZE - size of the image (single number).
# - OUTPUT_DIRECTORY - directory where to write results.
# - IMAGENET_DIR - directory with ImageNet dataset in TFRecord format.
# - TINY_IMAGENET_DIR - directory with Tiny ImageNet dataset in TFRecord format.
#
# Note that only one of IMAGENET_DIR or TINY_IMAGENET_DIR has to be provided
# depending on which dataset you use.
#
python train.py \
  --max_steps="${MAX_NUMBER_OF_TRAINING_STEPS}" \
  --model_name="${MODEL_NAME}" \
  --moving_average_decay="${MOVING_AVG_DECAY}" \
  --hparams="${HYPERPARAMETERS}" \
  --dataset="${DATASET}" \
  --dataset_image_size="${IMAGE_SIZE}" \
  --output_dir="${OUTPUT_DIRECTORY}" \
  --imagenet_data_dir="${IMAGENET_DIR}" \
  --tiny_imagenet_data_dir="${TINY_IMAGENET_DIR}"
```

Full list of training hyperparameters could be found in `model_lib.py`.
These hyperparameters control learning rate schedule, optimizer, weight decay,
label smoothing and adversarial training.

Adversarial training is controlled by following hyperparameters:

* `train_adv_method` - method which is used to craft adversarial examples during
  training. Could be one of the following:

  * `clean` - perform regular training with clean examples;
  * `pgd_EPS_STEP_NITER` - use non targeted PGD with maximum size of
    perturbation equal to `EPS`, step size equal to `STEP`
    and number of iterations equal to `NITER`. Size of perturbation and step
    size are expected to be integers between 1 and 255.
  * `pgdll_EPS_STEP_NITER` - use targeted PGD, where target class is least
    likely prediction of the network.
  * `pgdrnd_EPS_STEP_NITER` - use targeted PGD, where target class is chosen
    randomly.

* `train_lp_weight` - weight of adversarial logit pairing loss. If zero or
  negarive, then no logit pairing is performed and training is done using
  mixed minibatch PGD. If positive then adversarial logit pairing term is added
  to the loss.

Below is example of how to run training with adversarial logit pairing on
ImageNet 64x64:

```
python train.py \
  --model_name="resnet_v2_50" \
  --hparams="train_adv_method=pgdll_16_2_10,train_lp_weight=0.5" \
  --dataset="imagenet" \
  --dataset_image_size=64 \
  --output_dir="/tmp/adv_train" \
  --imagenet_data_dir="${IMAGENET_DIR}"
```

### Fine tuning

Provided trainin script could be used to fine tune pre-trained checkpoint.
Following command does this:

```
# Fine tuning adds following additional arguments:
# - SCOPES_DO_NOT_LOAD_FROM_CHECKPOINT - comma separates list of scopes of
#     variables, which should not be loadeded from checkpoint (and default
#     initialization should be used instead).
#     SCOPES_DO_NOT_LOAD_FROM_CHECKPOINT should be either same or a subset of
#     LIST_OF_SCOPES_OF_TRAINABLE_VARS.
# - LIST_OF_SCOPES_OF_TRAINABLE_VARS - comma separated list of scopes of
#     trainable variables. Only variables which are prefixed with these scopes
#     will be trained.
# - PATH_TO_PRETRAINED_CHECKPOINT - directory with pretrained checkpoint which
#     is used as initialization for fine tuning.
#
python train.py \
  --max_steps="${MAX_NUMBER_OF_TRAINING_STEPS}" \
  --model_name="${MODEL_NAME}" \
  --moving_average_decay="${MOVING_AVG_DECAY}" \
  --hparams="${HYPERPARAMETERS}" \
  --dataset="${DATASET}" \
  --dataset_image_size="${IMAGE_SIZE}" \
  --output_dir="${OUTPUT_DIRECTORY}" \
  --imagenet_data_dir="${IMAGENET_DIR}" \
  --tiny_imagenet_data_dir="${TINY_IMAGENET_DIR}" \
  --finetune_exclude_pretrained_scopes="${SCOPES_DO_NOT_LOAD_FROM_CHECKPOINT}" \
  --finetune_trainable_scopes="${LIST_OF_SCOPES_OF_TRAINABLE_VARS}" \
  --finetune_checkpoint_path="${PATH_TO_PRETRAINED_CHECKPOINT}"
```

Below is an example of how to fine tune last few layers of the model on
Tiny Imagenet dataset:

```
python train.py \
  --model_name="resnet_v2_50" \
  --hparams="train_adv_method=pgdll_16_2_10,train_lp_weight=0.5,learning_rate=0.02" \
  --dataset="tiny_imagenet" \
  --dataset_image_size=64 \
  --output_dir="/tmp/adv_finetune" \
  --tiny_imagenet_data_dir="${TINY_IMAGENET_DIR}" \
  --finetune_exclude_pretrained_scopes="resnet_v2_50/logits" \
  --finetune_trainable_scopes="resnet_v2_50/logits,resnet_v2_50/postnorm" \
  --finetune_checkpoint_path="/tmp/adv_train"
```

### Evaluation

Following command runs evaluation:

```
# Following arguments should be provided for eval:
# - TRAINING_DIRECTORY - directory where training checkpoints are saved.
# - TRAINABLE_SCOPES - when loading checkpoint which was obtained by fine tuning
#     this argument should be the same as LIST_OF_SCOPES_OF_TRAINABLE_VARS
#     during training. Otherwise it should be empty.
#     This is needed to properly load exponential moving average variables.
#     If exponential moving averages are disabled then this flag could be
#     omitted.
# - EVAL_SUBDIR_NAME - name of the subdirectory inside TRAINING_DIRECTORY
#     where evaluation code will be saving event files.
# - DATASET - name of the dataset.
# - IMAGE_SIZE - size of the image in the dataset.
# - DATSET_SPLIT_NAME - name of the split in the dataset,
#     either 'train' or 'validation'. Default is 'validation'.
# - MODEL_NAME - name of the model.
# - MOVING_AVG_DECAY - decay rate for exponential moving average.
# - ADV_METHOD_FOR_EVAL - should be "clean" to evaluate on clean example or
#     description of the adversarial method to evaluate on adversarial examples.
# - HYPERPARAMETERS - hyperparameters, only "eval_batch_size" matters for eval
# - NUMBER_OF_EXAMPLES - how many examples from the dataset use for evaluation,
#     specify -1 to use all examples.
# - EVAL_ONCE - if True then evaluate only once, otherwise keep evaluation
#     running repeatedly on new checkpoints. Repeated evaluation might be useful
#     when running concurrent with training.
# - IMAGENET_DIR - directory with ImageNet dataset in TFRecord format.
# - TINY_IMAGENET_DIR - directory with Tiny ImageNet dataset in TFRecord format.
#
python eval.py \
  --train_dir="${TRAINING_DIRECTORY} \
  --trainable_scopes="${TRAINABLE_SCOPES}" \
  --eval_name="${EVAL_SUBDIR_NAME}" \
  --dataset="${DATASET}" \
  --dataset_image_size="${IMAGE_SIZE}" \
  --split_name="${DATSET_SPLIT_NAME}" \
  --model_name="${MODEL_NAME}" \
  --moving_average_decay="${MOVING_AVG_DECAY}" \
  --adv_method="${ADV_METHOD_FOR_EVAL}" \
  --hparams="${HYPERPARAMETERS}" \
  --num_examples="${NUMBER_OF_EXAMPLES}" \
  --eval_once="${EVAL_ONCE}" \
  --imagenet_data_dir="${IMAGENET_DIR}" \
  --tiny_imagenet_data_dir="${TINY_IMAGENET_DIR}"
```

Example of running evaluation on 10000 of clean examples from ImageNet
training set:

```
python eval.py \
  --train_dir=/tmp/adv_train \
  --dataset=imagenet \
  --dataset_image_size=64 \
  --split_name=train \
  --adv_method=clean \
  --hparams="eval_batch_size=50" \
  --num_examples=10000 \
  --eval_once=True \
  --imagenet_data_dir="${IMAGENET_DIR}"
```

Example of running evaluatin on adversarial images generated from Tiny ImageNet
validation set using fine-tuned checkpoint:

```
python eval.py \
  --train_dir=tmp/adv_finetune \
  --trainable_scopes="resnet_v2_50/logits,resnet_v2_50/postnorm" \
  --dataset=tiny_imagenet \
  --dataset_image_size=64 \
  --adv_method=pgdrnd_16_2_10 \
  --hparams="eval_batch_size=50" \
  --eval_once=True \
  --tiny_imagenet_data_dir="${TINY_IMAGENET_DIR}"
```

### Pre-trained models

Following set of pre-trained checkpoints released with this code:

| Model       |    Dataset   |  Accuracy on<br>clean images | Accuracy on<br>`pgdll_16_1_20` | Accuracy on<br>`pgdll_16_2_10` |
| ----------- | ------------ | --------------- | --------------------------- | -------------- |
| [Baseline ResNet-v2-50](http://download.tensorflow.org/models/adversarial_logit_pairing/imagenet64_base_2018_06_26.ckpt.tar.gz) | ImageNet 64x64 | 60.5% | 1.8% | 3.5% |
| [ALP-trained ResNet-v2-50](http://download.tensorflow.org/models/adversarial_logit_pairing/imagenet64_alp025_2018_06_26.ckpt.tar.gz) | ImageNet 64x64 | 55.7% | 27.5% | 27.8% |
| [Baseline ResNet-v2-50](http://download.tensorflow.org/models/adversarial_logit_pairing/tiny_imagenet_base_2018_06_26.ckpt.tar.gz) | Tiny ImageNet | 69.2% | 0.1% | 0.3% |
| [ALP-trained ResNet-v2-50](http://download.tensorflow.org/models/adversarial_logit_pairing/tiny_imagenet_alp05_2018_06_26.ckpt.tar.gz) | Tiny ImageNet | 72.0% | 41.3% | 40.8% |


* All provided checkpoints were initially trained with exponential moving
  average. However for ease of use they were re-saved without it.
  So to load and use provided checkpoints you need to specify
  `--moving_average_decay=-1` flag.
* All ALP models were trained with `pgdll_16_2_10` adversarial examples.
* All Tiny Imagenet models were obtained by fine tuning corresponding
  ImageNet 64x64 models. ALP-trained models were fine tuned with ALP.
