{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "aPIA-10zdv4P"
   },
   "source": [
    "## ART Randomized Smoothing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "CGDOyI0HgDfx",
    "outputId": "2d61711f-6f8a-41b5-f05c-1085fd00fa13"
   },
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "import os\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \n",
    "\n",
    "import tensorflow as tf\n",
    "tf.keras.backend.set_floatx('float32')\n",
    "from tensorflow.keras.models import load_model\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPooling2D, Dropout\n",
    "\n",
    "from art import config\n",
    "from art.attacks.evasion import FastGradientMethod\n",
    "from art.estimators.classification import TensorFlowV2Classifier\n",
    "from art.estimators.certification.randomized_smoothing import TensorFlowV2RandomizedSmoothing\n",
    "from art.utils import load_dataset, get_file, compute_accuracy\n",
    "\n",
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "FqXvuMM9dv4U"
   },
   "source": [
    "### Load data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "z9OztmSidv4V"
   },
   "outputs": [],
   "source": [
    "# Read MNIST dataset\n",
    "(x_train, y_train), (x_test, y_test), min_, max_ = load_dataset('mnist')\n",
    "\n",
    "nb_classes = 10\n",
    "input_shape = x_train.shape[1:]\n",
    "\n",
    "num_samples_test = 250\n",
    "x_test = x_test[0:num_samples_test].astype(np.float32)\n",
    "y_test = y_test[0:num_samples_test]\n",
    "\n",
    "x_train = x_train.astype(np.float32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xDCzquK1dv4X"
   },
   "source": [
    "### Train classifiers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "G-mh9wSAHm-Z"
   },
   "outputs": [],
   "source": [
    "# Create convolutional neural network model\n",
    "def get_model(input_shape, min_, max_):\n",
    "    \n",
    "    model = Sequential()\n",
    "    model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "    model.add(Conv2D(64, (3, 3), activation='relu'))\n",
    "    model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "    model.add(Dropout(0.25))\n",
    "    model.add(Flatten())\n",
    "    model.add(Dense(128, activation='relu'))\n",
    "    model.add(Dense(10, activation='softmax'))\n",
    "    \n",
    "    return model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "nb_epochs = 40\n",
    "batch_size = 128\n",
    "sample_size = 100\n",
    "alpha = 0.001\n",
    "\n",
    "loss_object = tf.keras.losses.CategoricalCrossentropy(from_logits=False)\n",
    "optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "tGbe8Cjmdv4a"
   },
   "outputs": [],
   "source": [
    "# Construct and train a convolutional neural network in standard (non-smoothed) classifier\n",
    "\n",
    "classifier = TensorFlowV2Classifier(model=get_model(input_shape, min_, max_),\n",
    "                                    nb_classes=nb_classes,\n",
    "                                    input_shape=input_shape,\n",
    "                                    loss_object=loss_object,\n",
    "                                    optimizer=optimizer,\n",
    "                                    channels_first=False,\n",
    "                                    clip_values=(min_, max_))\n",
    "\n",
    "classifier.fit(x_train, y_train, nb_epochs=nb_epochs, batch_size=batch_size)\n",
    "\n",
    "sigma_0 = 0.5\n",
    "\n",
    "classifier_rs_0 = TensorFlowV2RandomizedSmoothing(model=classifier.model,\n",
    "                                                  nb_classes=nb_classes,\n",
    "                                                  input_shape=input_shape,\n",
    "                                                  loss_object=loss_object,\n",
    "                                                  optimizer=optimizer,\n",
    "                                                  channels_first=False,\n",
    "                                                  clip_values=(min_, max_),\n",
    "                                                  sample_size=sample_size,\n",
    "                                                  scale=sigma_0,\n",
    "                                                  alpha=alpha)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create and train smoothed classifier, sigma = 0.25\n",
    "\n",
    "sigma_1 = 0.25\n",
    "\n",
    "classifier_rs_1 = TensorFlowV2RandomizedSmoothing(model=get_model(input_shape, min_, max_),\n",
    "                                                 nb_classes=nb_classes,\n",
    "                                                 input_shape=input_shape,\n",
    "                                                 loss_object=loss_object,\n",
    "                                                 optimizer=optimizer,\n",
    "                                                 channels_first=False,\n",
    "                                                 clip_values=(min_, max_),\n",
    "                                                 sample_size=sample_size,\n",
    "                                                 scale=sigma_1,\n",
    "                                                 alpha=alpha)\n",
    "\n",
    "classifier_rs_1.fit(x_train, y_train, nb_epochs=nb_epochs, batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create and train smoothed classifier, sigma = 0.5\n",
    "\n",
    "sigma_2 = 0.5\n",
    "\n",
    "classifier_rs_2 = TensorFlowV2RandomizedSmoothing(model=get_model(input_shape, min_, max_),\n",
    "                                                  nb_classes=nb_classes,\n",
    "                                                  input_shape=input_shape,\n",
    "                                                  loss_object=loss_object,\n",
    "                                                  optimizer=optimizer,\n",
    "                                                  channels_first=False,\n",
    "                                                  clip_values=(min_, max_),\n",
    "                                                  sample_size=sample_size,\n",
    "                                                  scale=sigma_2,\n",
    "                                                  alpha=alpha)\n",
    "\n",
    "classifier_rs_2.fit(x_train, y_train, nb_epochs=nb_epochs, batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kukXRDcedv4j"
   },
   "source": [
    "### Prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 221
    },
    "colab_type": "code",
    "id": "jcPkXptcdv4k",
    "outputId": "ee65b562-0839-483b-9b1f-b7c911e3131a"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Randomized smoothing: 100%|██████████| 250/250 [00:04<00:00, 58.59it/s]\n",
      "Randomized smoothing: 100%|██████████| 250/250 [00:04<00:00, 58.63it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Original test data:\n",
      "Original Classifier\n",
      "Accuracy: 1.0\n",
      "Coverage: 1.0\n",
      "\n",
      "Smoothed Classifier, sigma=0.25\n",
      "Accuracy: 0.9959839357429718\n",
      "Coverage: 0.996\n",
      "\n",
      "Smoothed Classifier, sigma=0.5\n",
      "Accuracy: 1.0\n",
      "Coverage: 0.988\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "# compare prediction of randomized smoothed models to original model\n",
    "x_preds = classifier.predict(x_test)\n",
    "x_preds_rs_1 = classifier_rs_1.predict(x_test)\n",
    "x_preds_rs_2 = classifier_rs_2.predict(x_test)\n",
    "\n",
    "acc, cov = compute_accuracy(x_preds, y_test)\n",
    "acc_rs_1, cov_rs_1 = compute_accuracy(x_preds_rs_1, y_test)\n",
    "acc_rs_2, cov_rs_2 = compute_accuracy(x_preds_rs_2, y_test)\n",
    "\n",
    "print(\"\\nOriginal test data:\")\n",
    "print(\"Original Classifier\")\n",
    "print(\"Accuracy: {}\".format(acc))\n",
    "print(\"Coverage: {}\".format(cov))\n",
    "\n",
    "print(\"\\nSmoothed Classifier, sigma=\" + str(sigma_1))\n",
    "print(\"Accuracy: {}\".format(acc_rs_1))\n",
    "print(\"Coverage: {}\".format(cov_rs_1))\n",
    "\n",
    "print(\"\\nSmoothed Classifier, sigma=\" + str(sigma_2))\n",
    "print(\"Accuracy: {}\".format(acc_rs_2))\n",
    "print(\"Coverage: {}\".format(cov_rs_2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hqea3xvMdv4n"
   },
   "source": [
    "## Certification of Accuracy and L2-Radius"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "D6Va8ST8dv4n"
   },
   "outputs": [],
   "source": [
    "# Calculate certification accuracy for a given radius\n",
    "def getCertAcc(radius, pred, y_test):\n",
    "\n",
    "    rad_list = np.linspace(0, 2.25, 201)\n",
    "    cert_acc = []\n",
    "    num_cert = len(radius)\n",
    "    \n",
    "    for r in rad_list:\n",
    "        rad_idx = np.where(radius >= r)[0]\n",
    "        y_test_subset = y_test[rad_idx]\n",
    "        cert_acc.append(np.sum(pred[rad_idx] == np.argmax(y_test_subset, axis=1)) / num_cert)\n",
    "        \n",
    "    return cert_acc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "iPWY6KFMdv4p"
   },
   "outputs": [],
   "source": [
    "# Compute certification\n",
    "prediction_0, radius_0 = classifier_rs_0.certify(x_test, n=500)\n",
    "prediction_1, radius_1 = classifier_rs_1.certify(x_test, n=500)\n",
    "prediction_2, radius_2 = classifier_rs_2.certify(x_test, n=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 283
    },
    "colab_type": "code",
    "id": "ZZv5wDHSdv4s",
    "outputId": "a6fbe7ba-dfbb-47bd-8e56-794fb689cb14"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot certification accuracy w.r.t. to radius\n",
    "rad_list = np.linspace(0, 2.25, 201)\n",
    "plt.plot(rad_list, getCertAcc(radius_0, prediction_0, y_test), 'r-', label='original')\n",
    "plt.plot(rad_list, getCertAcc(radius_1, prediction_1, y_test), '-', color='green',\n",
    "         label='smoothed, $\\sigma=$' + str(sigma_1))\n",
    "plt.plot(rad_list, getCertAcc(radius_2, prediction_2, y_test), '-', color='blue',\n",
    "         label='smoothed, $\\sigma=$' + str(sigma_2))\n",
    "plt.xlabel('radius')\n",
    "plt.ylabel('certified accuracy')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "randomized_smoothing_mnist.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}