{"cells":[{"metadata":{"_cell_guid":"e355df2d-ac50-44bb-bd2d-c74b0788e684","_execution_state":"idle","_uuid":"dd7d4793fa65476631f78441818c25832b98fb50"},"cell_type":"markdown","source":"*This kernel flows.... work in progress! I currently work on better story telling & visualisations.* ;-)\n\n## Welcome Kaggler!\n\n...\n\n    Do you know what your machine model has learnt after training?\n\n...\n\n    Does your model know what a dog is if it makes perfect predictions for dog or cat classification?\n\n...\n\n    How vulnerable is a model with almost prefect prediction performance to hacker attacks?\n\n...\n\n    How can we attack and defense our algorithms?\n    \n...\n\nIt's some time ago, but there was a [competition on kaggle](https://www.kaggle.com/c/nips-2017-targeted-adversarial-attack) that addressed these questions and asked the community to build attacks and defenses for deep learning models. It was hosted by the google [brain group ](https://www.kaggle.com/google-brain) and you can find more information as well as a github repository on their own website [cleverhans.io](http://www.cleverhans.io/about/). The time when this competition was active I heard the first time about fooling machine learning models and as I always like to understand why and how, I wrote this notebook that fools a simpler model, namely logistic regression, on the MNIST digit dataset.\n\n![Cleverhans](https://upload.wikimedia.org/wikipedia/commons/5/57/Osten_und_Hans.jpg)\n\nDo you know this guy and his horse? The man on this image was Wilhelm von Osten and his horse was named Kluger Hans (cleverhans). In years before the First World War this horse was famous as one belived that it was able to count and to do arithmetic computations. Which sounds like a miracle turned out to be a more nebulous skill of Kugler Hans: Instead of doing computations the horse analyzed the gestures and body postures of the audiance in the show. Dependent on their expressions Kluger Hans made its decisions which result to choose. \n\nEven though one is not sure about the true skills of Kluger Hans and on which factors the horse made its decisions, this is still an analogy to our loved machine learning models. Do you know what they really learn?\n\n\nWithin this kernel I invite you to open your eyes and get to know the illusion. If you like my kernel, you can make me very happy with an upvote! ;-) \n"},{"metadata":{"_cell_guid":"d746e523-67f5-46b4-a0fc-8611cbc02e2d","_uuid":"0b1a679918e749283c22f627d6df89f3af35e951"},"cell_type":"markdown","source":"## Walkthrough contents\n\n* [Loading data and packages](#load)\n* [Attack methods and class](#methods)\n* [Training the model](#training)\n* [Non-Targeted-Attack](#nontargeted): \n    * Maximizing the output-target discrepance\n    * Natural fooling targets \n* [Targeted-Attack](#targeted): \n    * One image example\n    * Natural vs non-natural fooling targets & accuracy score breakdown for all images (Reworked)\n* [Comparison to Fast Gradient Method](#fastgradient): \n    * Thoughts: Maximizing discrepance vs. minimizing likelihood\n* [The Jacobian enters the door](#jacobian)\n* [Conclusion](#conclusion)"},{"metadata":{"_cell_guid":"cbbf0e2f-7721-4547-8943-c72c7b8b935c","_uuid":"20f2087b01f05a02c802694920aa660e44d97bb0"},"cell_type":"markdown","source":"## Loading packages <a class=\"anchor\" id=\"load\"></a>\n\nFirst of all, let's load some packages..."},{"metadata":{"_cell_guid":"bc70953a-0e83-4581-8390-e8bef8f3c092","_execution_state":"idle","_uuid":"1ca8c05008e02ffb425117e95e9b53221b2a3717","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"from sklearn.linear_model import LogisticRegression\nfrom sklearn import datasets as dt\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA\n\nimport pandas as pd\nimport numpy as np\nimport seaborn as sns\n\n\nimport plotly.offline as py\npy.init_notebook_mode(connected=True)\nimport plotly.graph_objs as go\nimport plotly.tools as tls\n\nimport matplotlib.image as mpimg\nimport matplotlib.pyplot as plt\nimport matplotlib\n%matplotlib inline\n\nfrom skimage.io import imread, imshow\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\nwarnings.filterwarnings(\"ignore\", category=FutureWarning)\nwarnings.filterwarnings(\"ignore\", category=UserWarning)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"c863d722-33b0-40cf-bac5-23ff40b0bf61","_uuid":"f6ccc694a5a23c4599effb19e6ed99be37b588a9"},"cell_type":"markdown","source":"## Loading data \n\nNow we will use the digits of the digit-recognizer competition. Let's check: "},{"metadata":{"_cell_guid":"487b5a94-2ee7-4e8d-a6a1-2db33cf1e229","_execution_state":"idle","_uuid":"c6a722537925697fcd66c70d0827e6b3fa3d4e43","trusted":true},"cell_type":"code","source":"from subprocess import check_output\nprint(check_output([\"ls\", \"../input\"]).decode(\"utf8\"))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"21c7aed6-1e25-4e1d-9cd1-e0b315ac309e","_execution_state":"idle","_uuid":"08fc0af13cc8650f7d25a2207a78e5c386fe1043","trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/digit-recognizer/train.csv\")\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"06fe1f51901b76ad7f39bd6d0bdf30667a29aa6a"},"cell_type":"markdown","source":"Ok, the label holds the true digit and the other columns all 784 pixel of an image with 28 times 28 pixels. Let's split our data intro train and test. This way we can measure our model performance on the test set and we can see how this score breaks down during the attack."},{"metadata":{"_cell_guid":"692864db-8c5a-4cb5-ab38-7347364f05fd","_execution_state":"idle","_uuid":"0d9eae00137ebfc5f4bf37c24b7630491acc7059","trusted":true},"cell_type":"code","source":"y = df.label.values\nX = df.drop(\"label\",axis=1).values\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=0)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"cfb1e742-c6d1-4630-a170-c5d3fa658649","_uuid":"5918a034cd5e580d744ba56597637cd9549972a1"},"cell_type":"markdown","source":"Before we start with building targeted and non-targeted attacks, let's have a look at the first digits of the test set:"},{"metadata":{"_cell_guid":"695f1427-1925-460f-b495-498795788572","_execution_state":"idle","_uuid":"6479e12ade27f0932ec149cad7b7fc488f8d12cf","trusted":true},"cell_type":"code","source":"fig1, ax1 = plt.subplots(1,15, figsize=(15,10))\nfor i in range(15):\n    ax1[i].imshow(X_test[i].reshape((28,28)), cmap=\"gray_r\")\n    ax1[i].axis('off')\n    ax1[i].set_title(y_test[i])","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"ba0d171f-788d-43cc-a17b-66a4ed6ab583","_uuid":"744cee6d8f4ccf3f960e0365d4b6286a67327bc8"},"cell_type":"markdown","source":"Some digits are not easily recognized by human eyes... "},{"metadata":{"_cell_guid":"31927fcd-d45f-42fc-82bc-32052e029601","_execution_state":"idle","_uuid":"c6a26adf8bf68097202c8df4f0bc26311aeb3456"},"cell_type":"markdown","source":"## Code - methods and classes <a class=\"anchor\" id=\"methods\"></a>\n\nAs I don't like to fill this kernel with same code used for different cases again and again, I will store it here. I hope, this makes it also easier for you to play with this kernel if you like to fork :-)"},{"metadata":{"_uuid":"31099a044f01583b073c4253d24edb53a314c694"},"cell_type":"markdown","source":"### Attack class\n\nI have written a small class that performs the attack of logistic regression:"},{"metadata":{"trusted":true,"_uuid":"acfe1e3ba341dd5749f4301e0c8b2505d7eac170"},"cell_type":"code","source":"class Attack:\n\n    def __init__(self, model):\n        self.fooling_targets = None\n        self.model = model\n    \n    def prepare(self, X_train, y_train, X_test, y_test):\n        self.images = X_test\n        self.true_targets = y_test\n        self.num_samples = X_test.shape[0]\n        self.train(X_train, y_train)\n        print(\"Model training finished.\")\n        self.test(X_test, y_test)\n        print(\"Model testing finished. Initial accuracy score: \" + str(self.initial_score))\n    \n    def set_fooling_targets(self, fooling_targets):\n        self.fooling_targets = fooling_targets\n    \n    def train(self, X_train, y_train):\n        self.model.fit(X_train, y_train)\n        self.weights = self.model.coef_\n        self.num_classes = self.weights.shape[0]\n\n    def test(self, X_test, y_test):\n        self.preds = self.model.predict(X_test)\n        self.preds_proba = self.model.predict_proba(X_test)\n        self.initial_score = accuracy_score(y_test, self.preds)\n    \n    def create_one_hot_targets(self, targets):\n        self.one_hot_targets = np.zeros(self.preds_proba.shape)\n        for n in range(targets.shape[0]):\n            self.one_hot_targets[n, targets[n]] = 1\n            \n    def attack(self, attackmethod, epsilon):\n        perturbed_images, highest_epsilon = self.perturb_images(epsilon, attackmethod)\n        perturbed_preds = self.model.predict(perturbed_images)\n        score = accuracy_score(self.true_targets, perturbed_preds)\n        return perturbed_images, perturbed_preds, score, highest_epsilon\n    \n    def perturb_images(self, epsilon, gradient_method):\n        perturbed = np.zeros(self.images.shape)\n        max_perturbations = []\n        for n in range(self.images.shape[0]):\n            perturbation = self.get_perturbation(epsilon, gradient_method, self.one_hot_targets[n], self.preds_proba[n])\n            perturbed[n] = self.images[n] + perturbation\n            max_perturbations.append(np.max(perturbation))\n        highest_epsilon = np.max(np.array(max_perturbations))\n        return perturbed, highest_epsilon\n    \n    def get_perturbation(self, epsilon, gradient_method, target, pred_proba):\n        gradient = gradient_method(target, pred_proba, self.weights)\n        inf_norm = np.max(gradient)\n        perturbation = epsilon/inf_norm * gradient\n        return perturbation\n    \n    def attack_to_max_epsilon(self, attackmethod, max_epsilon):\n        self.max_epsilon = max_epsilon\n        self.scores = []\n        self.epsilons = []\n        self.perturbed_images_per_epsilon = []\n        self.perturbed_outputs_per_epsilon = []\n        for epsilon in range(0, self.max_epsilon):\n            perturbed_images, perturbed_preds, score, highest_epsilon = self.attack(attackmethod, epsilon)\n            self.epsilons.append(highest_epsilon)\n            self.scores.append(score)\n            self.perturbed_images_per_epsilon.append(perturbed_images)\n            self.perturbed_outputs_per_epsilon.append(perturbed_preds)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"915db0f1-ae8c-407c-9c9b-9f76ec042b8c","_uuid":"405f1b87ed32bf2a872e01be1493c25aa7745e3b"},"cell_type":"markdown","source":"### Attack methods "},{"metadata":{"_cell_guid":"6529c8b8-bb06-483b-a5d5-648aebd388f6","_execution_state":"idle","_uuid":"5546e41f11c910868d1c83c0578aa1607c27ad67","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def calc_output_weighted_weights(output, w):\n    for c in range(len(output)):\n        if c == 0:\n            weighted_weights = output[c] * w[c]\n        else:\n            weighted_weights += output[c] * w[c]\n    return weighted_weights\n\ndef targeted_gradient(foolingtarget, output, w):\n    ww = calc_output_weighted_weights(output, w)\n    for k in range(len(output)):\n        if k == 0:\n            gradient = foolingtarget[k] * (w[k]-ww)\n        else:\n            gradient += foolingtarget[k] * (w[k]-ww)\n    return gradient\n\ndef non_targeted_gradient(target, output, w):\n    ww = calc_output_weighted_weights(output, w)\n    for k in range(len(target)):\n        if k == 0:\n            gradient = (1-target[k]) * (w[k]-ww)\n        else:\n            gradient += (1-target[k]) * (w[k]-ww)\n    return gradient\n\ndef non_targeted_sign_gradient(target, output, w):\n    gradient = non_targeted_gradient(target, output, w)\n    return np.sign(gradient)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b633f74c-8833-4be7-aa78-09081f4b612d","_uuid":"e466a4d6b44b9077874749bf986f7d4a3146ff33"},"cell_type":"markdown","source":"## Training the model <a class=\"anchor\" id=\"training\"></a>\n\nFirst of all we need a model for multiclass logistic regression:"},{"metadata":{"_cell_guid":"55e834b0-9fa4-4f2a-9a24-34130b54155e","_execution_state":"idle","_uuid":"cecf10684e01d0d1f70a7687a150f4af6961311c","trusted":true},"cell_type":"code","source":"model = LogisticRegression(multi_class='multinomial', solver='lbfgs', fit_intercept=False)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"1af94c5e-20f2-4668-ad8e-09a6cf86d00e","_uuid":"d005b92bd181ea337b3a75a10d42cc7a847aa063"},"cell_type":"markdown","source":"And we will pass it to our class and call prepare. This way we train our model on training data and we will obtain the initial accuracy score on test data. Later on we want to break down this score by perturbing the test data."},{"metadata":{"_cell_guid":"398d0ad7-42b6-4ebe-957d-f9a0a714fb51","_execution_state":"idle","_uuid":"5838fa53c94e550242436cc476ae963a8ab3442f","trusted":true},"cell_type":"code","source":"attack = Attack(model)\nattack.prepare(X_train, y_train, X_test, y_test)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"8adb53ba-5d38-40af-9517-3cd58812fb6b","_uuid":"6a7c8af89ecd0808a79764728fcb1426a0d0a020"},"cell_type":"markdown","source":"Ok, around 90 percent of test data was classified correctly and for us this is sufficient to play with. In multiclass logistic regression the probability that the model outputs $y_{n}$ of $N$ inputs $x_{n}$ matches their targets $t_{n}$ is given by:\n\n$$ \np(t|y(x,w)) = \\prod_{n=1}^{N} \\prod_{k=1}^{K} y_{n,k}^{t_{n,k}} \n$$\n\nWe assumed that inputs and class memberships are independent and identically distributed. The target $t_{n}$ of one input $x_{n}$ is a vector with K elements following one-hot-encoding (the true label class is 1, all others are 0). Maximizing the probability of matches above is also called the maximum likelihood approach. Each class in a multiclass logistic regression has its own weight vector and inputs are passed with weights through the softmax function to obtain the model output:\n\n$$ \ny_{n,k} = \\frac{\\exp(w_{k}^{T}x_{n})} {\\sum_{c=1}^{K}\\exp(w_{c}^{T}x_{n})}\n$$"},{"metadata":{"_cell_guid":"52517038-c7c7-41cf-8950-26811694e2ee","_uuid":"aa07a74d3bf760b92e8f3ddc396351cf42747922"},"cell_type":"markdown","source":"Let's check if we have as much weight vectors as classes:"},{"metadata":{"_cell_guid":"f50ad7d2-3b7c-4371-ab52-b55fff8efe2e","_execution_state":"idle","_uuid":"6278622538769c377689abf4036898cd43c8094a","trusted":true},"cell_type":"code","source":"weights = attack.weights\nweights.shape","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"e444b1be-b561-49dc-9786-043f3b3dcdff","_execution_state":"idle","_uuid":"9f48657c0c7eeb5a088f0f75cfa6b3ef945b4efb","trusted":true},"cell_type":"code","source":"num_classes = len(np.unique(y_train))\nnum_classes","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"933989e0-004b-4b23-831d-9b7b0e264ca1","_uuid":"c66d82eaa0038cba41f3bf9e8b43257dc21dd388"},"cell_type":"markdown","source":":-)"},{"metadata":{"_cell_guid":"b6234953-94cc-48ff-9e27-7668f46e15e0","_uuid":"8ce8c9c442f93f56d229e201ca7d8acc0c10b949"},"cell_type":"markdown","source":"## Non-targeted Attack: Maximizing output-target discrepance <a class=\"anchor\" id=\"nontargeted\"></a>\n\nIn analogy to the maximum likelihood approach I define a discrepancy function as my objective. Usually the likelihood gives us the probability that our model output y matches the target t. Thus maximizing the likelihood yields us the best matches. Creating an attack one has to think the other way round: We want to maximize the probability that the outputs do not match the targets. Let's write down this descrepancy function by using the multinomial distribution again:\n\n$$ D(t|y(w,x)) = \\prod_{n=1}^{N} \\prod_{k=1}^{K} y_{n,k}^{1-t_{n,k}} $$"},{"metadata":{"_cell_guid":"51fcb213-8225-4822-b4d4-1035604b3362","_uuid":"aaeafcc85783895f3a513796646dd3763b9be24c"},"cell_type":"markdown","source":"Notice that in contrast to the likelihood function we now have 1-t instead of t. If our true target $t_{n,k}$ of class k for input $x_{n}$ is 1, but our model predicts $y_{n,k}=0$, then we have $0^{1-1}=1$ and vice versa $1^{1-0}=1$.  "},{"metadata":{"_cell_guid":"dd8f9518-eb63-4240-b9fd-fb519e00332d","_uuid":"b64270844197c91c87d5471b2b905e9b1a2c2446"},"cell_type":"markdown","source":"To train a model we usually would maximize the likelihood with respect to the weight parameters whereas inputs are fixed. In our case we already have a trained model and fixed weights. But we can add tiny perturbations to our input images such that we maximize our discrepancy function. Let's do that and for making things simpler we use the log! :-)\n\n$$\\nabla_{x} \\hat{D} = \\nabla_{x} log D = \\nabla_{x} \\sum_{n=1}^{N} \\sum_{k=1}^{K} (1-t_{k}) \\log y_{k} $$\n\n$$\\hat{D} = \\sum_{n=1}^{N} \\sum_{k=1}^{K} (1-t_{n,k}) \\log y_{n,k}$$\n\n$$ \\partial_{x} \\hat{D} = \\sum_{n=1}^{N} \\sum_{k=1}^{K} \\frac {\\partial \\hat{D}}{\\partial y_{k}} \\frac {\\partial y_{k}}{\\partial x} $$"},{"metadata":{"_cell_guid":"e718e2ed-fa0c-4fd4-bbe6-d27a4fe4a518","_uuid":"49c278b314d45e30d6bbbc16cb5d2231794b6918"},"cell_type":"markdown","source":"The first part is easy:\n\n$$ \\frac {\\partial \\hat{D}}{\\partial y_{k}}  = \\frac {1-t_{k}} {y_{k}}$$"},{"metadata":{"_cell_guid":"8e1e1276-9279-4cb5-82b1-956e009f6c82","_uuid":"3e05b8abe586345db60806a1604c52fcdcd882c1"},"cell_type":"markdown","source":"For the second part we should keep in mind the gradient of activations yields the class weight vector:\n\n$$ a_{c} = \\vec{w_{c}}^{T} \\vec{x} = \\vec{x}^{T} \\vec{w_{c}} $$\n\n$$ \\nabla_{x} a_{c} = \\vec{w_{c}} $$\n\n$$ y_{k} = \\frac{\\exp(a_{k})} {\\sum_{c=1}^{K}\\exp(a_{c})}$$"},{"metadata":{"_cell_guid":"d4b2c405-4b5d-4522-a2ed-453c894e5c62","_uuid":"457438b97020763d18313eb2d383f39368ceac42"},"cell_type":"markdown","source":"Now this second part is a bit cumbersome (... hopefully I didn't make a mistake ...):\n\n$$ \\frac {\\partial y_{k}}{\\partial x} = \\frac{\\Sigma \\cdot \\partial_{x} \\exp(a_{k}) - \\exp(a_{k}) \\partial_{x} \\Sigma}{\\Sigma^{2}} = \\frac {\\sum_{c=1}^{K} \\exp(a_{c}) \\cdot \\vec{w}_{k} \\exp(a_{k}) - \\exp(a_{k}) \\sum_{c=1}^{K} \\vec{w}_{c} \\exp(a_{c})} {\\sum_{c=1}^{K}\\exp(a_{c}) \\cdot \\sum_{c=1}^{K}\\exp(a_{c})} $$\n\n$$ \\frac {\\partial y_{k}}{\\partial x} =  y_{k} \\vec{w}_{k} - y_{k} \\cdot \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}  $$"},{"metadata":{"_cell_guid":"e2325039-e6f2-4128-991e-392f8b8594e6","_uuid":"091098ba9f8a6473eb539e40ec6f5d7bc5ab742c"},"cell_type":"markdown","source":"Finally I end up with:\n\n$$ \\nabla_{x} \\log D = \\sum_{k=1}^{K} (1-t_{k}) \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}) $$"},{"metadata":{"_cell_guid":"feecabd8-e3d8-4f12-a7c4-34239540b63e","_uuid":"fc3b321ef45b2b99be67082b90d5a5ff1f907859"},"cell_type":"markdown","source":"Let's try to understand this: For the true label the summand is 0 whereas all other classes contribute to the gradient with their class weight vector $\\vec{w_{k}}$ reduced by an output \"weighted\" sum of all other class weights. What does that mean? ..."},{"metadata":{"_cell_guid":"bd6c14b5-14c6-4ea0-aba6-d1df0b33ca81","_uuid":"5eb2ac652c23ebd2877fcabdb995a1a20a1d39a6"},"cell_type":"markdown","source":"To maximize the discrepance for playing around we can use gradient ascent. Though we have to find a sufficient rate $\\eta$. Given an input $x_{m}$ we will then add a perturbation with:"},{"metadata":{"_cell_guid":"eb109b37-1505-47e1-bbdc-81db3c35d02b","_uuid":"146cf80b0f39a253addb9b5f4b83b8af5ec7dc3b"},"cell_type":"markdown","source":"$$ x_{p, m} = x_{m} + \\delta x_{m} = x_{m} + \\eta \\cdot \\nabla_{x_{m,c}} D_{m,c} $$\n\n$$ x_{p, m} = x_{m} + \\eta \\sum_{k=1}^{K} (1-t_{m,k}) \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{m,c} \\vec{w}_{c}) $$"},{"metadata":{"_cell_guid":"ae560b3b-0b34-4a05-a38c-2e862f986b05","_uuid":"a86e0f812ada3ea10117120ab6a727f38b16fe05"},"cell_type":"markdown","source":"Let's give it a try! :-)"},{"metadata":{"_cell_guid":"0400e84b-2aa9-4a0d-9770-96b3699e2d1c","_uuid":"b51ccee09183e6aee7a87e410d0fad7f02260b48"},"cell_type":"markdown","source":"### Adding tiny perturbations"},{"metadata":{"_cell_guid":"243afa5b-4e4e-45b9-831d-dda132b165d7","_uuid":"0b5f78e4456a379e6fe8c62fad239ade0a6aa789"},"cell_type":"markdown","source":"First of all we need to calculate the perturbations for each image in the test set. To do this we have to transform our true targets to one-hot-targets and call attack :-). As I want to see, how much epsilon we need to create a good breakdown, I use the attack_to_max_epsilon method. "},{"metadata":{"_cell_guid":"dde231aa-12b5-47f2-8de0-c1cfdd60d519","_execution_state":"idle","_uuid":"c82590af33833f2567f0206f3f6137471fee1bf4","trusted":true},"cell_type":"code","source":"attack.create_one_hot_targets(y_test)\nattack.attack_to_max_epsilon(non_targeted_gradient, 30)\nnon_targeted_scores = attack.scores","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"a33b845c-0dc6-4d49-babb-50ffa5572520","_execution_state":"idle","_uuid":"ab8237ddeeca26485ee6e2e66bb1a4a95ecb32cb","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"sns.set()\nplt.figure(figsize=(10,5))\nplt.plot(attack.epsilons, attack.scores, 'g*')\nplt.ylabel('accuracy_score')\nplt.xlabel('epsilon')\nplt.title('Accuracy score breakdown - non-targeted attack');","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"babf47d9-5add-4453-abe2-0cbd478966c8","_uuid":"8ef513bea5cba8730cf15bd8abfc2ae7bb8a5a05"},"cell_type":"markdown","source":"Uii, the threshold is given by a max of 16 pixel that are allowed to be added as perturbation per pixel per image. Given this $\\epsilon$ we would end up with a model that still predicts around 40 % correctly. If we would use max $\\epsilon=30$ the model would fail with almoast 90 % digits in the test set :-) . Let's have a look at one example of successful fooling for a range of epsilons until max of $\\epsilon = 16$."},{"metadata":{"_cell_guid":"36a77cd4-90c1-4ed3-bb76-a153af0bf06a","_execution_state":"idle","_uuid":"5488992c63f23a1ceb054d9b2abb14461f042bdb","trusted":true},"cell_type":"code","source":"eps = 16\nattack.epsilons[eps]","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"8be8bee7-e497-4dce-b04a-c69fcb1da737","_uuid":"eb6d99993025adab182e73bbed6d67192e291ddc"},"cell_type":"markdown","source":"We need the perturbed images as well as the fooling results of that epsilon:"},{"metadata":{"_cell_guid":"50bde7ea-a653-4358-8846-b09a2d98d4ed","_execution_state":"idle","_uuid":"6a63f4acd8e7318059d97e86cfaef351fdb71f34","trusted":true},"cell_type":"code","source":"example_images = attack.perturbed_images_per_epsilon[eps]\nexample_preds = attack.perturbed_outputs_per_epsilon[eps]","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"17bd2225-5ee1-4515-9858-8f50f2eb1be4","_uuid":"d28e5a106f3aaf1eb306dadc379ed60e4a61997c"},"cell_type":"markdown","source":"And I will store results in a pandas dataframe such that we can easily find successful foolings:"},{"metadata":{"_cell_guid":"49cd9f0c-0ebc-44cf-a1ae-86ac6deb4bd6","_execution_state":"idle","_uuid":"5e50d8a32c92a86fe3d93580a3828da32cadfe6d","trusted":true},"cell_type":"code","source":"example_results = pd.DataFrame(data=attack.true_targets, columns=['y_true'])\nexample_results['y_fooled'] = example_preds\nexample_results['y_predicted'] = attack.preds\nexample_results['id'] = example_results.index.values\nexample_results.head()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"2cff8ade-eef2-4f85-ac5a-411f6af2cbe6","_execution_state":"idle","_uuid":"628ef80fd435adb9fd9d9fb602c7b2def02f9c1b","trusted":true},"cell_type":"code","source":"success_df = example_results[example_results.y_fooled != example_results.y_true]\nsuccess_df.head()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"5b791596-caec-4a08-a7f9-e16f75d01f79","_uuid":"c2446d6390df0c59acf1d81492b1f6b6f5fb1455"},"cell_type":"markdown","source":"Ok, we will choose one of these successful examples and plot its related perturbed image over a range of epsilons: "},{"metadata":{"_cell_guid":"48f68809-43a7-4a24-b949-c093970a9437","_execution_state":"idle","_uuid":"ba80cab01d80d8b5685e98ce5e3b225e15d1cf20","trusted":true},"cell_type":"code","source":"example_id = success_df.id.values[0]\nexample_id","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"850ec3a5-a332-41aa-b7cd-3d435336ddd7","_execution_state":"idle","_uuid":"16915e0cc91c906803a61640ba26142591091280","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig2, ax2 = plt.subplots(4,4, figsize=(15,15))\nfor i in range(4):\n    for j in range(4):\n        image = attack.perturbed_images_per_epsilon[i*4 + j][example_id]\n        y_fooled = attack.perturbed_outputs_per_epsilon[i*4 + j][example_id]\n        epsilon = attack.epsilons[i*4 +j]\n        ax2[i,j].imshow(image.reshape((28,28)), cmap=\"gray_r\")\n        ax2[i,j].axis('off')\n        ax2[i,j].set_title(\"true: \" + str(y_test[example_id]) + \", fooled: \" + str(y_fooled)  + \"\\n\" \n                           + \"epsilon: \" + str(np.int(epsilon)))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"8ef83b99-c96d-4291-bbbf-ffaddca88ab9","_uuid":"123f61520916def391c1233e0416d58b772f3791"},"cell_type":"markdown","source":"Yeah! :-) We can still see the true target and not the fooling target. That's amazing. But we can also see, that the background has increased intensitiy. Let's visualize the difference between the original true label and the adversarial image for $\\epsilon = 16$: "},{"metadata":{"_cell_guid":"f0291249-2815-4c1e-bc6a-901c2bdd2f47","_execution_state":"idle","_uuid":"bdbe09c9e03efde2af49099c95cd83645db68e5b","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"fig, (axA, axB, axC) = plt.subplots(1, 3, figsize=(15,5))\naxB.imshow(example_images[example_id].reshape((28,28)), cmap='Greens')\naxB.set_title(\"Non-targeted attack result: \" + str(example_preds[example_id]))\naxA.imshow(X_test[example_id].reshape((28,28)), cmap='Greens')\naxA.set_title(\"True label: \" + str(y_test[example_id]))\naxC.imshow((X_test[example_id]-example_images[example_id]).reshape((28,28)), cmap='Reds')\naxC.set_title(\"Perturbation: epsilon 16\");","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"2150fd1a-5717-4a4e-8b1b-1689dd3c03bc","_execution_state":"idle","_uuid":"3c3061bc08977563423846b218ee903ec2f6a9e9"},"cell_type":"markdown","source":"### The gradient travel guide - natural fooling targets\n\nI'm happy that it was possible to fool our model but it's still diffuse and unclear where the one-step-gradient guides us through (remember we do not iterate with gradient ascent, we just take one step and size is given by strength of gradient times eta). I assume that some numbers are closer to each other in weight space than to others. As the model training draws decision boundaries dependent on the quality of the input data and flexibility of model architecture, there will be regions where a 3 is not predicted as 3 but as 8. Those regions where the model makes an incorrect prediction. And I think, that there are preffered numbers to be wrong predictions given a digit input image. Perhaps the fooling gradients drives us to those \"natural\" fooling target numbers? "},{"metadata":{"_cell_guid":"9a58deac-8ede-48a2-a353-1614d7edae56","_execution_state":"idle","_uuid":"dd50bcaf51f98491c2c517a6476d3b4e328c60cf","trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\nsns.countplot(x='y_fooled', data=example_results[example_results.y_true != example_results.y_fooled])","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"cc733bdd-bb3f-46db-94ca-cdf7febabf36","_uuid":"fa304a9daf1d4cbdd95a359e73ee5825c2ca7fd3"},"cell_type":"markdown","source":"Ok, we see that 8 was selected most often as fooling target. But 9, 3, 5 and 2 have high counts as well in contrast to 0, 1, 6 and 7. If our assumption is true that the gradient drives us to targets where the model tends to fail in prediction we should see a similar pattern of counts for wrong predictions:"},{"metadata":{"_cell_guid":"9e46660d-99e9-460c-bcde-94c3d01c2251","_execution_state":"idle","_uuid":"c1dcda977d59668fa31b4882fec51b2930bb0086","trusted":true},"cell_type":"code","source":"wrong_predictions = example_results[example_results.y_true != example_results.y_predicted]\nwrong_predictions.shape","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"4c5562f2-cbcb-42f7-b33b-3092649ad3a4","_execution_state":"idle","_uuid":"63da90b5890b9b4a4a9efc9b49a4f557c6ca1523","trusted":true},"cell_type":"code","source":"X_test.shape","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"bb259da0-7721-4180-b833-80efe1537ab6","_uuid":"30f0bfc8f3a49e89098cdb1f40c584dcdb6a9d23"},"cell_type":"markdown","source":"Ok, so out of 16800 samples, the model failed to predict around 1600. That's why our intital accuracy score is close to 90 % (means 10 % failing). Now, which digit was selected as wrong prediction result most often?"},{"metadata":{"_cell_guid":"131fdbd7-9e90-4f50-b46d-129403e9fd73","_execution_state":"idle","_uuid":"989368c698badd3b824ecfaf04a57db059547cf1","trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\nsns.countplot(x='y_predicted', data=wrong_predictions)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"cfe6a812-c679-48a0-8a82-627b305ed044","_uuid":"dc278495378b9a565803f6cba55fecf15d404108"},"cell_type":"markdown","source":"Yes, that's the same pattern as for the fooling targets. As this is caused by the difficulty of our model to draw good decision boundaries we should see this pattern as well for the true labels of those digits that were wrong predicted:"},{"metadata":{"_cell_guid":"ec52935f-ec3e-4966-87f5-3474eebd23e9","_execution_state":"idle","_uuid":"d0486299bde081f8ea34bc85a2a5a1fba67716bb","trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\nsns.countplot(x='y_true', data=wrong_predictions)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"f048ecfa-f8ae-4215-93bb-74fbb1fe433e","_uuid":"789e40f6a6dc68011d10012b410c8a0571d493bc"},"cell_type":"markdown","source":" Now I want to see it in more detail: Which are the natural fooling targets (for successful foolings) for each digit?"},{"metadata":{"trusted":true,"_uuid":"2263947f369d0fdae5b5a37bed901f67c7ca06f7"},"cell_type":"code","source":"attacktargets = example_results.loc[example_results.y_true != example_results.y_fooled].groupby(\n    'y_true').y_fooled.value_counts()\ncounts = example_results.loc[example_results.y_true != example_results.y_fooled].groupby(\n    'y_true').y_fooled.count()\nattacktargets = attacktargets/counts * 100\nattacktargets = attacktargets.unstack()\nattacktargets = attacktargets.fillna(0.0) \nattacktargets = attacktargets.apply(np.round).astype(np.int)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"14fc9785-cc79-453a-af63-c6391bcd47dc","_execution_state":"idle","_uuid":"de0c2c38c9dd0168195a71791e2387fbee15c23a","trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(figsize=(10, 10))\nsns.heatmap(attacktargets, annot=True, ax=ax, cbar=False, square=True, cmap=\"Reds\", fmt=\"g\");\nax.set_title(\"How often was y_true predicted as some y_fooled digit in percent?\");","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"7cf3f365-9e98-42c8-b67a-1de9c70cde0f","_uuid":"e9f66adbc62e0a68324ca0e2cf513a964a767490"},"cell_type":"markdown","source":"We found out that each digit has its natural fooling target. For example 3 was likely predicted as 5 or 8 which makes sense as all three have \"two bubbles\" on top of each other as a sketch shape. You can see that 8 was a good fooling choice for several digits... interestingly for 1 as well. In addition we find that for some images both holds: 4 and 9 for example are possible in both ways."},{"metadata":{"_cell_guid":"6e1cc309-133e-4bd6-a24d-8efddfe6a4ac","_execution_state":"idle","_uuid":"7d3e0012dba9671732f913a6663ee66f0810e8c1"},"cell_type":"markdown","source":"## Targeted-Attack <a class=\"anchor\" id=\"targeted\"></a>\n\nWe have seen that fooling the multiclass logistic regression model was easy with gradient ascent and the only cumbersome part was to calculate the gradient with respect to the inputs of our discrepance function. Instead of forcing the function to yield the maximum discrepance we could have also construct it such that outputs have to match a specific false target. To emphasize the difference to the true target $t_{n,k}$ let's call it $f_{n,k}$ for \"fooling\" target.\n\n$$ D(t|y(w,x)) = \\prod_{n=1}^{N} \\prod_{k=1}^{K} y_{n,k}^{f_{n,k}} $$\n\nLuckily we do not have to calculate everything again, the only thing that changes is:\n\n$$ \\frac {\\partial D}{\\partial y_{k}}  = \\frac {f_{k}} {y_{k}} $$\n\nConsequently we have:\n\n$$ \\nabla_{x} \\log D = \\sum_{k=1}^{K} f_{k} \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}) $$\n\n$$ x_{p, m} = x_{m} + \\eta \\sum_{k=1}^{K} f_{k} \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{m,c} \\vec{w}_{c}) $$"},{"metadata":{"_cell_guid":"ffc82675-14f0-4126-bb99-e262b9fb52d2","_execution_state":"idle","_uuid":"54b35dc38386b547d6cfa20d0687f7e978afbb0a"},"cell_type":"markdown","source":"### One example image\n\nTo play around, let's select one input of $X_{test}$ and try to make targeted attacks for each class $f_{k}$ except for the true label target $t_{k}$. "},{"metadata":{"_cell_guid":"01cbc8ce-8e8f-4aea-82be-100b1307c7af","_execution_state":"idle","_uuid":"4bb0ae6ecea99b47625cf6281b5303121423419e","trusted":true},"cell_type":"code","source":"example = X_test[0]\nimshow(example.reshape((28,28)), cmap='Greens');","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"4e8196c3-7cd2-4834-9b32-e1d4f2e27e80","_execution_state":"idle","_uuid":"879b9b408c0b8335f7edbb349c8eab8ee6e191af","trusted":true},"cell_type":"code","source":"print(\"true label target: \" + str(y_test[0]))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"3449159e-682e-43cc-b71d-a35482c444f1","_execution_state":"idle","_uuid":"9b6b79a28d807bb217b015a77a1f1f6a0a7d9620"},"cell_type":"markdown","source":"### Generating the fooling/attack classes and targets"},{"metadata":{"_cell_guid":"f46439a4-e4e0-44bf-bd87-14c973e562e4","_uuid":"e8628614022112c07fa5852473073d3246278a60"},"cell_type":"markdown","source":"First of all, we need some fooling targets. For our example digit all others are possible:"},{"metadata":{"_cell_guid":"f57b8693-1ad2-4d39-a294-817c9e3a8978","_execution_state":"idle","_uuid":"e1b72b65d3b659f2357a8cbab394f6bbddc93793","trusted":true},"cell_type":"code","source":"fooling_classes = []\nfor k in range(num_classes):\n    if k != y_test[0]:\n        fooling_classes.append(k)\nfooling_classes","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"ae25c0d1-e37e-4a91-a2db-0cc1c170a46b","_execution_state":"idle","_uuid":"e09fb9f938373bf83202a1c195bea8c4610b7a77","trusted":true},"cell_type":"code","source":"foolingtargets = np.zeros((len(fooling_classes), num_classes))\nfor n in range(len(fooling_classes)):\n    foolingtargets[n,fooling_classes[n]] = 1\nfoolingtargets","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"70e8c3eb-e339-40d0-ac5a-4f83af964fe1","_execution_state":"idle","_uuid":"1714f1cc23e02378f8f96f5a9af7d4cb51dc4946"},"cell_type":"markdown","source":"### Attacking the model\n\nI will force the attack to success by allowing an epsilon high enough to yield all targets. This way we can still find out, if we can see the true label or the fooling target."},{"metadata":{"_cell_guid":"c3398542-d3d2-41ec-938a-bbfed255a4f7","_execution_state":"idle","_uuid":"87836dd1f87fec6af354847670837f409e7b46c2","trusted":true},"cell_type":"code","source":"eps=100\ntargeted_perturbed_images = []\ntargeted_perturbed_predictions = []\nfor fooling_target in foolingtargets:   \n    targeted_perturbation = attack.get_perturbation(eps, targeted_gradient, fooling_target, attack.preds_proba[0])\n    targeted_perturbed_image = X_test[0] + targeted_perturbation\n    targeted_perturbed_prediction = attack.model.predict(targeted_perturbed_image.reshape(1, -1))\n    targeted_perturbed_images.append(targeted_perturbed_image)\n    targeted_perturbed_predictions.append(targeted_perturbed_prediction)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b9dd623f-fcd7-4070-b2bb-8ee32cb2b40f","_execution_state":"idle","_uuid":"b0adc609f7fc895e156f5e116cdc9ab7d4ee412d","trusted":true},"cell_type":"code","source":"targeted_perturbed_predictions","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"6137de98-1c0a-4a38-a0ed-13996d77a0fd","_execution_state":"idle","_uuid":"3ba45c5c8d76cca2bc974c671c1b9032d9848fbc","trusted":true},"cell_type":"code","source":"fig3, ax3 = plt.subplots(3,3, figsize=(9,9))\nfor i in range(3):\n    for j in range(3):\n        ax3[i,j].imshow(targeted_perturbed_images[i*3+j].reshape((28,28)), cmap=\"Greens\")\n        ax3[i,j].axis('off')\n        ax3[i,j].set_title(\"fooling result: \" + str(targeted_perturbed_predictions[i*3+j][0]))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"eb3f4a08-6a55-45c9-952f-e8d6c960390a","_uuid":"1f2d42f2405a6a7eb8a784503e0e0ceec3887240"},"cell_type":"markdown","source":"Even though we can see high background noise the true label is not destroyed. I can still see the true label with my eyes whereas the model predicts the desired fooling target (0 to 9, except true label). :-) That's cool."},{"metadata":{"_cell_guid":"f46b17bb-e310-4baf-ad1d-8645589f2188","_uuid":"7ba37092db956441bf5010d86e0c671f54ce2fc2"},"cell_type":"markdown","source":"## Natural vs. non-natural targeted attack\n\nNow, I like to see what happens with the accuracy score if we fool the model for each image in the test set. \nBy analyzing non-targeted attacks we found that some digits are more used as \"fooling\" target than others and that each digit has its fooling digit counterpart. I assume that fooling takes place in regions where the model fails to draw good decision boundaries. Using targeted attack we should see that we can breakdown the accuracy score easier with natural fooling targets than with the other digits. Let's try this! :-)"},{"metadata":{"_cell_guid":"0838f111-c757-4536-a517-b0b3c1b89b3e","_uuid":"e826c6dd80d198cdd8096523720d5faf8cd5f18f"},"cell_type":"markdown","source":"### Prepare natural and non-natural fooling targets\n\nThe gradient travel guide showed us the occurences of fooling target digits for each true digit. The highest count stands for the natural fooling target whereas the lowest corresponds to the non-natural fooling target. Given the heatmap we could create the targets by argmin and argmax per row (y_true) as follows:"},{"metadata":{"_cell_guid":"6943db4c-5be7-4280-b8ee-6f779e426e38","_execution_state":"idle","_uuid":"919feed729201ee23dc3a99615d533b26b5856f1","trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(figsize=(10, 10))\nsns.heatmap(attacktargets, annot=True, ax=ax, cbar=False, cmap=\"Purples\", fmt=\"g\");","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"8293b1f1-7370-4153-b34e-42ad7cd79d15","_execution_state":"idle","_uuid":"daa73a73256512bb2a7a4b8b1fa8f9227253e823","trusted":true},"cell_type":"code","source":"natural_targets_dict = {}\nnon_natural_targets_dict = {}\nfor ix, series in attacktargets.iterrows():\n    natural_targets_dict[ix] = series.argmax()\n    non_natural_targets_dict[ix] = series.drop(ix).argmin()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"4a90cedb-4b1c-49cb-a6e0-56027dbad74d","_execution_state":"idle","_uuid":"2a8234e379b8c3b4394eeb72d75d49718287c761","trusted":true},"cell_type":"code","source":"natural_targets_dict","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"feeada47-b155-4091-b826-395503edb676","_execution_state":"idle","_uuid":"805809275409c6b68ae9c6ebbbe83e0df33c0668","trusted":true},"cell_type":"code","source":"natural_foolingtargets = np.zeros((y_test.shape[0]))\nnon_natural_foolingtargets = np.zeros((y_test.shape[0]))\n\nfor n in range(len(natural_foolingtargets)):\n    target = y_test[n]\n    natural_foolingtargets[n] = natural_targets_dict[target]\n    non_natural_foolingtargets[n] = non_natural_targets_dict[target]","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"16269dbf-42e0-4ba4-8825-4c17b3d611e6","_execution_state":"idle","_uuid":"acf9f375aa4bb06fbb86f77c9ad3c70e12801ae5","trusted":true},"cell_type":"code","source":"attack.create_one_hot_targets(natural_foolingtargets.astype(np.int))\nattack.attack_to_max_epsilon(targeted_gradient, 30)\nnatural_scores = attack.scores\nattack.create_one_hot_targets(non_natural_foolingtargets.astype(np.int))\nattack.attack_to_max_epsilon(targeted_gradient, 30)\nnon_natural_scores = attack.scores","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"4ade18c0-0b84-438d-9605-f5f11a30f3bf","_execution_state":"idle","_uuid":"9f8203a7bfd048766726505ebcb2532e202a6580","trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\nnf, = plt.plot(attack.epsilons, natural_scores, 'g*', label='natural fooling')\nnnf, = plt.plot(attack.epsilons, non_natural_scores, 'b*', label='non-natural fooling')\nplt.legend(handles=[nf, nnf])\nplt.ylabel('accuracy_score')\nplt.xlabel('epsilon')\nplt.title('Accuracy score breakdown: natural vs non-natural targeted attack');","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"10385e49-5501-4d40-9293-71938a008c36","_uuid":"b343bb2b2854b90c82928d146c62564f42174255"},"cell_type":"markdown","source":"Ahhh! :-) We can clearly see that it was easier to fool the model with natural fooling targets."},{"metadata":{"_cell_guid":"656a9170-d3b0-464f-8a7c-349632b2bf67","_execution_state":"idle","_uuid":"19380ce6ecf0fc1f64a55207583add0615ef2747"},"cell_type":"markdown","source":"## Comparison to Fast Gradient Method <a class=\"anchor\" id=\"fastgradient\"></a>\n\n\nSo far we have used gradient ascent to maximize the probability of no-matches (non-targeted & targeted) and we derived the gradient with respect to inputs for objectives that are only slightly changed in comparison to the model defining likelihood function. This way we are very close to the Fast Gradient Method given in the cleverhans library:\n"},{"metadata":{"_cell_guid":"da3e76db-12e7-4bd4-b594-fd029e0890ac","_uuid":"8be0d6952f92c88c586cd61ec08675a09c215693"},"cell_type":"markdown","source":"* Maximizing probability of no-matches:\n\n$$ x_{p, m} = x_{m} + \\delta x_{m} = x_{m} + \\eta \\cdot \\nabla_{x_{m}} D(f_{m}|y_{m}(x_{m},w)) $$\n\n* Fast Gradient Method:\n\n$$ x_{p,m} = x_{m} + \\delta x_{m} = x_{m} + \\eta \\cdot sign (\\nabla_{x_{m}} J(w,x,y)) $$"},{"metadata":{"_cell_guid":"d9c91b82-2ff7-49f9-b099-98e444af8d82","_uuid":"a3af92bcbc0f0ae602716183f4f37218811c6034"},"cell_type":"markdown","source":"Whereas $J(w,x,y)$ stands for the cost function used to train the model.  In many cases this is $E=- \\log (L(t|y(x,w))$, the negative log likelihood function. One might think that this should also be the case for this example and that we could have also simply maximize the cost funtion. As this would be the same as to minimize the log-likelihood, one might think:"},{"metadata":{"_cell_guid":"e038b6f2-6d71-42d9-abc9-f7e5b91e2eeb","_uuid":"27415f6b36be921b21890934bfa339510fc9e130"},"cell_type":"markdown","source":"** ... instead of defining a discrepance function one could have also just minimize the likelihood function to yield the lowest probability of matches... **"},{"metadata":{"_cell_guid":"e665c999-2d12-4411-9a78-a245564b251c","_uuid":"df31b56c77c8180a9d64b30d4fcd7b3c71cc21f8"},"cell_type":"markdown","source":"Ohoh! Is this true? Sounds so nice, but... "},{"metadata":{"_cell_guid":"d7364d34-f7a3-45cd-a64f-4ff1873c2e1c","_uuid":"663edc31462ec122efef66fc3e09c87da37dd2aa"},"cell_type":"markdown","source":"### Maximizing discrepance = minimizing likelihood?\n\nNormally we use the likelihood to maximize the probability and we want to count those outputs $y_{n,c}$ related to target-values of $t_{n,c}=1$. Look at one example image $m$ that belongs to class 2 of 3, then for this one we would have:\n\n$$ l = y_{1}^{0} * y_{2}^{1} * y_{3}^{0} = y_{2} $$\n\nHence this image contribute to the overall product over $n$ by a factor of $y_{2}$. Consequently each image $n$ would give us a y-factor of its true class whereas all the others only give us a factor of 1 such that it does not disturb our nice probability. If we would now use the same likelihood to represent the probability of no-matches we encounter a problem: To minimize the likelihood the only thing that can be done now is to set $y_{2}=0$ for image m. We would do this for all outputs that enter the likelihood by themselves. But what is with all the others? What to do with $y_{1}$ and $y_{3}$ for image m? Looking for a minimum of $y_{2}$ we could select anything for $y_{1}$ and $y_{3}$ as they just stay 1. That's ill-defined in contrast to our discrepance probability. There the situation for image m would look like this:\n\n$$ l = y_{1}^{1} * y_{2}^{0} * y_{3}^{1} = y_{1} * y_{3} $$\n\nAnd also our targeted-attack looks well for image m, if we define class 1 as our fooling class:\n\n$$ l = y_{1}^{1} * y_{2}^{0} * y_{3}^{0} = y_{1} $$\n\nWe have made sure that only those output-values enter the likelihood that are not related to the true target and we made sure that they carry their values and not just a value of 1. "},{"metadata":{"_cell_guid":"a1730861-920b-4044-a474-5db79f67c644","_uuid":"4dbcf9cee5ca5f8d8f73759bdcb938707a7648af"},"cell_type":"markdown","source":"## Comparison to \"discrepance\" Gradient Sign Method"},{"metadata":{"_cell_guid":"d9478884-e873-47a1-b0cd-8cb0ca7b1903","_uuid":"0e25368e2d4f51105e839fcd2af4b5dcbd8a6827"},"cell_type":"markdown","source":"Some small experiment: Let's only take the sign of the gradient of our attack. I think we will not be as goog as we could be with the full gradient:"},{"metadata":{"_cell_guid":"b095d974-2401-4700-adef-86cbeeffa4e6","_execution_state":"idle","_uuid":"24daf535a94bd1672f205ca6ad71ccbb74bd6da7","trusted":true},"cell_type":"code","source":"attack.create_one_hot_targets(y_test)\nattack.attack_to_max_epsilon(non_targeted_sign_gradient, 30)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"d19b486e-f624-42c0-a307-88d5912e0950","_execution_state":"idle","_uuid":"3341d04deed2974333d470355c8ae076bed038d1","trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,5))\ngm, = plt.plot(attack.epsilons, non_targeted_scores, 'g*', label='gradient method')\ngsm, = plt.plot(attack.epsilons, attack.scores, 'r*', label='gradient sign method')\nplt.ylabel('accuracy_score')\nplt.xlabel('eta')\nplt.legend(handles=[gm, gsm])\nplt.title('Accuracy score breakdown')","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"34483793-364e-4b4a-a867-c12cc02bdc04","_uuid":"247084eaf01a5d3332b528406c95479eee3535c5"},"cell_type":"markdown","source":"Uiih!! :-o That's a surprise. "},{"metadata":{"_cell_guid":"7d8b9f78-bcd4-41f2-9813-76a1be9d0099","_uuid":"0a6ae56d84ff05f1bd1f02e5b2d9cf18035e573d"},"cell_type":"markdown","source":"## One step further: The Jacobian matrix <a class=\"anchor\" id=\"jacobian\"></a>"},{"metadata":{"_cell_guid":"a3c96afd-1af4-434a-a691-8cfe3dfa65e0","_uuid":"8c5b9b219742522eee2ace9421d6da593f4c18f3"},"cell_type":"markdown","source":"What if we would use the likelihood instead of the discrepance function to yield the perturbations? If you look at the equations, you can find a clear similarity:"},{"metadata":{"_cell_guid":"3d8d7d9d-7df9-451c-91ad-60242feb6551","_uuid":"6bc9c29e03fa367f93cf1751aa3ba2c24152c5d0"},"cell_type":"markdown","source":"$$ D(t|y(w,x)) = \\prod_{n=1}^{N} \\prod_{k=1}^{K} y_{n,k}^{f_{n,k}} $$\n\n$$ \\nabla_{x} \\log D = \\sum_{k=1}^{K} f_{k} \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}) $$\n\n$$ L(t|y(x,w)) = \\prod_{n=1}^{N} \\prod_{k=1}^{K} y_{n,k}^{t_{n,k}} $$\n\n$$ \\nabla_{x} \\log L = \\sum_{k=1}^{K} t_{k} \\cdot  (\\vec{w}_{k} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}) $$"},{"metadata":{"_cell_guid":"a2da1ca7-574a-407d-b1e2-ba620f42219c","_uuid":"9eeac0d6ee534a03c0a5e5a76e0d22f546dcf7f0"},"cell_type":"markdown","source":"The difference is the use of either the true label $t_{n,k}$ or the fooling target $f_{n,k}$. What remains the same is the gradient of output $y_{k}$ of class $k$ with respect to all features $x$ (pixels) of one single image.\n\n$$ \\frac {\\partial y_{k}}{\\partial x} =  y_{k} \\vec{w}_{k} - y_{k} \\cdot \\sum_{c=1}^{K} y_{c} \\vec{w}_{c}  $$"},{"metadata":{"_cell_guid":"722c14a7-648c-4b39-aef4-a6e817996a91","_uuid":"04b3504d535ae970f2618641aa011f71b3a301ff"},"cell_type":"markdown","source":"Taking the derivative of the outputs for each class $\\partial y_{k}$ with respect to the input features $\\partial x_{d}$ (with d in range for 1 to 64 pixel) we have arrived at the **Jacobian matrix**: "},{"metadata":{"_cell_guid":"099caa15-4ee1-4345-9080-d8f87df06941","_uuid":"3b08e92739c65005b30f31743b0c3a39afeb4b53"},"cell_type":"markdown","source":"$$\n\\begin{bmatrix}\n\\frac{\\partial y_{1}}{\\partial x_{1}}\t& \\frac{\\partial y_{1}}{\\partial x_{2}}\t& \\dots\t & \\frac{\\partial y_{1}}{\\partial x_{64}}     \\\\\n\\frac{\\partial y_{2}}{\\partial x_{1}}\t& \\frac{\\partial y_{2}}{\\partial x_{2}} \t& \\dots  & \\frac{\\partial y_{2}}{\\partial x_{64}} \t  \\\\\n\\vdots\t& \\vdots \t& \\ddots & \\vdots \\\\\n\\frac{\\partial y_{10}}{\\partial x_{1}} \t& \\dots & \\dots\t & \\frac{\\partial y_{10}}{\\partial x_{64}}\n\\end{bmatrix}\n$$ "},{"metadata":{"_cell_guid":"d365aa22-e254-48ac-ba81-3a5db6fe7309","_uuid":"a2a4bafccaba68084229e3781c92505de5fdebb9"},"cell_type":"markdown","source":"Now, in each row we have one vector of $\\frac {\\partial y_{k}}{\\partial x}$. But that's not all! Have a closer look at the equations of $ \\nabla_{x} \\log L $ and $ \\nabla_{x} \\log D$. What are $t_{n,k}$ and $f_{n,k}$ doing here? ...\n\nThey are working like a mask. :-)\n\n...\n\nNot every class contributes to the gradient! As we use one-hot-encoding, only the class with the $1$ yields a summand which is not 0. Looking at the Jacobian this means that all rows are 0 except for the class that is represented by 1 in the encoding of $t_{n,k}$ or $f_{n,k}$. Let's call this specific class $\\hat{k}$, then we have:\n\n$$ \\nabla_{x} \\log D =  \\vec{w}_{\\hat{k}} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c} $$\n\n$$ \\nabla_{x} \\log L =  \\vec{w}_{\\hat{k}} - \\sum_{c=1}^{K} y_{c} \\vec{w}_{c} $$\n\nBut take in mind: $\\hat{k}$ is different for the both equations: one is the fooling class and the other the true label class and of course they are not the same! :-)\n\n### Why could this be important?\n\n"},{"metadata":{"_cell_guid":"fbfb53e5-5dd1-4ebb-9ab3-131507939312","_uuid":"27c6a8abe59aff24c6d5b1260fee1c5ec3e298ff"},"cell_type":"markdown","source":"This looks like playing with equations but we gained an insight: We have found that we could express the perturbation by a masking of the Jacobian matrix. The letter tells us more generally how the outputs of all classes change with input perturbations. This is already a hint that we could built attacks and defenses on approaches based on the Jacobian. Besides the Fast Gradient Attack you can find a Jacobian-based approach as well. Have fun to explore! :-)"},{"metadata":{"_cell_guid":"e95daec7-22ea-4613-9050-a394d79017b5","_uuid":"72e3f2e1b1baa83f51a1bec36578da3a27dec690"},"cell_type":"markdown","source":"# Conclusion <a class=\"anchor\" id=\"conclusion\"></a>\n\nMy public journey has come to end. I have still ideas to play with but this kernel is already too long for an introduction. Feel free to fork, try different epsilons, only use the sign method, use dimensionality reductions as influence property.... there is so much to do :-) . For me it was the first time to fool a machine learning model and I learnt:\n\n1. One could easily fool a model if one has access to the learned weights and by defining own objectives that maximize the discrepance between targets and model outputs. (One could also fool without knowing weights or model architecture, but that's another topic...)\n2. Fooling takes place in regions where the model fails to draw good decision boundaries which of course depends on the model architecture/flexibility but on the input data quality and preprocessing as well.\n3. Due to the fact that some inputs are closer to each other in meanings of decision boundaries, there exist natural and non-natural fooling targets.\n\n\nHave fun and good luck, so far :-)"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}