{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using TensorFlow backend.\n", "/home/robert/anaconda3/envs/tensorflow/lib/python3.6/site-packages/keras/engine/saving.py:269: UserWarning: No training configuration found in save file: the model was *not* compiled. Compile it manually.\n", " warnings.warn('No training configuration found in save file: '\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "(1, 224, 224, 3)\n", "963769983334ddc2\n", "963769983334ddc2\n", "malinois\n" ] } ], "source": [ "from keras.applications.mobilenet import preprocess_input\n", "from keras.models import load_model\n", "from keras.preprocessing.image import img_to_array, array_to_img\n", "from keras.applications.mobilenet import decode_predictions, preprocess_input\n", "from PIL import Image\n", "import numpy as np\n", "from imagehash import phash\n", "\n", "\n", "IMAGE_DIMS = (224, 224)\n", "TREE_FROG_IDX = 31\n", "TREE_FROG_STR = \"tree_frog\"\n", "\n", "\n", "# I'm pretty sure I borrowed this function from somewhere, but cannot remember\n", "# the source to cite them properly.\n", "def hash_hamming_distance(h1, h2):\n", " s1 = str(h1)\n", " s2 = str(h2)\n", " \n", " print(s1)\n", " print(s2)\n", " \n", " return sum(map(lambda x: 0 if x[0] == x[1] else 1, zip(s1, s2)))\n", "\n", "\n", "def is_similar_img(path1, path2):\n", " image1 = Image.open(path1)\n", " image2 = Image.open(path2)\n", "\n", " dist = hash_hamming_distance(phash(image1), phash(image2))\n", " return dist <= 2\n", "\n", "\n", "def prepare_image(image, target=IMAGE_DIMS):\n", " # if the image mode is not RGB, convert it\n", " if image.mode != \"RGB\":\n", " image = image.convert(\"RGB\")\n", "\n", " # resize the input image and preprocess it\n", " image = image.resize(target)\n", " image = img_to_array(image)\n", " image = np.expand_dims(image, axis=0)\n", " image = preprocess_input(image)\n", " # return the processed image\n", " return image\n", "\n", "\n", "def create_img(img_path, img_res_path, model_path, target_str, target_idx, des_conf=0.95):\n", " test = Image.open(img_path).resize(IMAGE_DIMS)\n", " test = prepare_image(test)\n", "\n", " print(test.shape)\n", " # TODO: YOUR SOLUTION HERE\n", "\n", "\n", " test = test.reshape((224,224,3))\n", " img = array_to_img(test)\n", " img.save(img_res_path)\n", " \n", "def get_predictions(image):\n", " preds = model.predict(image)\n", " dec_preds = decode_predictions(preds)[0]\n", " _, label1, conf1 = decode_predictions(preds)[0][0]\n", " return label1, conf1, dec_preds\n", "\n", "model = load_model(\"./model.h5\")\n", "create_img(\"./img/trixi.png\", \"./trixi_frog.png\", \"./model.h5\", TREE_FROG_STR, TREE_FROG_IDX)\n", "assert is_similar_img(\"./img/trixi.png\", \"./trixi_frog.png\")\n", "\n", "frog_mat = prepare_image(Image.open(\"./img/trixi.png\").resize(IMAGE_DIMS))\n", " \n", "# read the image in PIL format\n", "frog_label, frog_conf, top_preds = get_predictions(frog_mat)\n", "\n", "print(frog_label)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[('n02105162', 'malinois', 0.51700354),\n", " ('n02106662', 'German_shepherd', 0.48188373),\n", " ('n02105412', 'kelpie', 0.0005297822),\n", " ('n02091467', 'Norwegian_elkhound', 0.00027945824),\n", " ('n02109047', 'Great_Dane', 7.9423575e-05)]]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "decode_predictions(model.predict(frog_mat))" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "from keras import backend as K\n", "\n", "model_input_layer = model.layers[0].input\n", "model_output_layer = model.layers[-1].output\n", "\n", "object_type_to_fake = 31\n", "\n", "# Load the image to hack\n", "\n", "test = Image.open(\"./img/trixi.png\").resize(IMAGE_DIMS)\n", "test = prepare_image(test)\n", "\n", "# Add a 4th dimension for batch size (as Keras expects)\n", "original_image = test\n", "\n", "# Pre-calculate the maximum change we will allow to the image\n", "# We'll make sure our hacked image never goes past this so it doesn't look funny.\n", "# A larger number produces an image faster but risks more distortion.\n", "max_change_above = original_image + 0.1\n", "max_change_below = original_image - 0.1\n", "\n", "\n", "hacked_image = np.copy(original_image)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "shower_cap 25.31420886516571\n", "0.1320762187242508\n", "tree_frog 17.966926097869873\n", "0.17966926097869873\n", "sunglasses 20.077967643737793\n", "0.0014713054988533258\n", "Italian_greyhound 51.356059312820435\n", "1.8276870150657487e-06\n", "Italian_greyhound 82.85401463508606\n", "0.003349896287545562\n", "German_shepherd 41.97295904159546\n", "7.492299482692033e-05\n", "tree_frog 93.64550709724426\n", "0.9364550709724426\n", "bloodhound 46.38504385948181\n", "1.994923150050454e-05\n", "tree_frog 100.0\n", "1.0\n" ] } ], "source": [ "\n", "\n", "# Create a copy of the input image to hack on\n", "\n", "# How much to update the hacked image in each iteration\n", "learning_rate = 1e7\n", "\n", "# Define the cost function.\n", "# Our 'cost' will be the likelihood out image is the target class according to the pre-trained model\n", "cost_function = model_output_layer[0, object_type_to_fake]\n", "\n", "# We'll ask Keras to calculate the gradient based on the input image and the currently predicted class\n", "# In this case, referring to \"model_input_layer\" will give us back image we are hacking.\n", "gradient_function = K.gradients(cost_function, model_input_layer)[0]\n", "\n", "# Create a Keras function that we can call to calculate the current cost and gradient\n", "grab_cost_and_gradients_from_model = K.function([model_input_layer, K.learning_phase()], [cost_function, gradient_function])\n", "\n", "cost = 0.0\n", "iter = 0\n", "\n", "# In a loop, keep adjusting the hacked image slightly so that it tricks the model more and more\n", "# until it gets to at least 80% confidence\n", "while True:\n", " # Check how close the image is to our target class and grab the gradients we\n", " # can use to push it one more step in that direction.\n", " # Note: It's really important to pass in '0' for the Keras learning mode here!\n", " # Keras layers behave differently in prediction vs. train modes!\n", " cost, gradients = grab_cost_and_gradients_from_model([hacked_image, 0])\n", "\n", " # Move the hacked image one step further towards fooling the model\n", " hacked_image += gradients * learning_rate\n", "\n", " # Ensure that the image doesn't ever change too much to either look funny or to become an invalid image\n", " hacked_image = np.clip(hacked_image, max_change_below, max_change_above)\n", " hacked_image = np.clip(hacked_image, -1, 1)\n", "\n", " iter += 1\n", " learning_rate *= 0.99\n", " if iter % 100 == 0:\n", " _, label1, conf1 = decode_predictions(model.predict(hacked_image))[0][0]\n", " print(\"{} {}\".format(label1, conf1 * 100))\n", " print(\"{}\".format(model.predict(hacked_image)[0][31]))\n", " if \"tree_frog\" in label1 and conf1 >= 0.95:\n", " break" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model.predict(hacked_image)[0][31]" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "963769983334ddc2\n", "963769983334ddc2\n" ] } ], "source": [ "img = hacked_image[0]\n", "img = img.reshape((224,224,3))\n", "img = array_to_img(img)\n", "img.save(\"hacked-image3.png\")\n", "assert is_similar_img(\"./img/trixi.png\", \"./hacked-image3.png\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "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.5" } }, "nbformat": 4, "nbformat_minor": 2 }