{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using Theano backend.\n", "WARNING (theano.sandbox.cuda): The cuda backend is deprecated and will be removed in the next release (v0.10). Please switch to the gpuarray backend. You can get more information about how to switch at this URL:\n", " https://github.com/Theano/Theano/wiki/Converting-to-the-new-gpu-back-end%28gpuarray%29\n", "\n", "Using gpu device 0: GeForce GTX 980 Ti (CNMeM is disabled, cuDNN 6021)\n", "/home/hiranumn/anaconda2/lib/python2.7/site-packages/theano/sandbox/cuda/__init__.py:631: UserWarning: Your cuDNN version is more recent than the one Theano officially supports. If you see any problems, try updating Theano or downgrading cuDNN to version 5.1.\n", " warnings.warn(warn)\n" ] } ], "source": [ "import numpy as np\n", "\n", "import sys\n", "\n", "import numpy as np\n", "from time import sleep\n", "import sys\n", "import keras.backend as K\n", "\n", "from keras.models import Model, Sequential\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# IRIS DATA\n", "_X = np.array([[float(j) for j in i.rstrip().split(\",\")[:-1]] for i in open(\"iris.data\").readlines()][:-1])\n", "_Y = np.array([0 for i in range(100)] + [1 for i in range(50)])" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Extracting MNIST_data/train-images-idx3-ubyte.gz\n", "Extracting MNIST_data/train-labels-idx1-ubyte.gz\n", "Extracting MNIST_data/t10k-images-idx3-ubyte.gz\n", "Extracting MNIST_data/t10k-labels-idx1-ubyte.gz\n" ] } ], "source": [ "# MNIST DATA\n", "from tensorflow.examples.tutorials.mnist import input_data\n", "mnist = input_data.read_data_sets(\"MNIST_data/\", one_hot=True)\n", "\n", "X = mnist.train._images.reshape(55000,28, 28)\n", "Y = mnist.train._labels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Linear interpolation of samples" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "'''\n", "Input: numpy array of a sample\n", "Optional inputs:\n", " - reference: reference values (defaulted to 0s).\n", " - steps: # steps from reference values to the actual sample.\n", "Output: list of numpy arrays to integrated over.\n", "'''\n", "\n", "def linearly_interpolate(sample, reference=None, num_steps=50):\n", " # Use default reference values if reference is not specified\n", " if reference is None: reference = np.zeros(sample.shape);\n", " \n", " # Reference and sample shape needs to match exactly\n", " assert sample.shape == reference.shape\n", " \n", " # Calcuated stepwise difference from reference to the actual sample.\n", " ret = np.zeros(tuple([num_steps] +[i for i in sample.shape]))\n", " for s in range(num_steps):\n", " ret[s] = reference+(sample-reference)*(s*1.0/num_steps)\n", " \n", " return ret, num_steps, (sample-reference)*(1.0/num_steps)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Interpolate to 0\n", "samples, num_steps, step_sizes = linearly_interpolate(X[0].reshape(28,28), num_steps=100)\n", "\n", "image = np.zeros((int(np.ceil(num_steps/10)*28), 10*28))\n", "count = 0\n", "for i in range(10):\n", " for j in range(int(np.ceil(num_steps/10))):\n", " s = samples[count]\n", " count += 1\n", " image[j*28:(j+1)*28, i*28:(i+1)*28] = s\n", "plt.figure(figsize=(int(np.ceil(num_steps/10)*0.25), 10*0.25))\n", "plt.imshow(image)\n", "plt.xticks([],[])\n", "plt.yticks([],[])\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Interpolate to 1\n", "samples, num_steps, step_sizes = linearly_interpolate(X[0].reshape(28,28), np.ones((28,28)), num_steps=100)\n", "\n", "image = np.zeros((int(np.ceil(num_steps/10)*28), 10*28))\n", "count = 0\n", "for i in range(10):\n", " for j in range(int(np.ceil(num_steps/10))):\n", " s = samples[count]\n", " count += 1\n", " image[j*28:(j+1)*28, i*28:(i+1)*28] = s\n", "plt.figure(figsize=(int(np.ceil(num_steps/10)*0.25), 10*0.25))\n", "plt.imshow(image)\n", "plt.xticks([],[])\n", "plt.yticks([],[])\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Integrated gradients" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "collapsed": false }, "outputs": [], "source": [ "################################################################\n", "# Implemented by Naozumi Hiranuma (hiranumn@uw.edu) #\n", "# #\n", "# Kears-compatible implmentation of Integrated Gradients # \n", "# proposed in \"Axiomatic attribution for deep neuron networks\" #\n", "# (https://arxiv.org/abs/1703.01365). #\n", "# #\n", "# Keywords: Shapley values, interpretable machine learning #\n", "################################################################\n", "import numpy as np\n", "from time import sleep\n", "import sys\n", "import keras.backend as K\n", "\n", "from keras.models import Model, Sequential\n", "\n", "'''\n", "Integrated gradients approximates Shapley values by integrating partial\n", "gradients with respect to input features from reference input to the\n", "actual input. The following class implements this concept.\n", "'''\n", "class integrated_gradients:\n", " # model: Keras model that you wish to explain.\n", " # outchannels: In case the model are multi tasking, you can specify which channels you want.\n", " def __init__(self, model, outchannels=[], verbose=1):\n", " \n", " # Bacnend: either tensorflow or theano)\n", " self.backend = K.backend()\n", " \n", " #load model supports keras.Model and keras.Sequential\n", " if isinstance(model, Sequential):\n", " self.model = model.model\n", " elif isinstance(model, Model):\n", " self.model = model\n", " else:\n", " print \"Invalid input model\"\n", " return -1\n", " \n", " #load input tensors\n", " self.input_tensors = []\n", " for i in self.model.inputs:\n", " self.input_tensors.append(i)\n", " # The learning phase flag is a bool tensor (0 = test, 1 = train)\n", " # to be passed as input to any Keras function that uses \n", " # a different behavior at train time and test time.\n", " self.input_tensors.append(K.learning_phase())\n", " \n", " #If outputchanel is specified, use it.\n", " #Otherwise evalueate all outputs.\n", " self.outchannels = outchannels\n", " if len(self.outchannels) == 0: \n", " if verbose: print \"Evaluated output channel (0-based index): All\"\n", " if K.backend() == \"tensorflow\":\n", " self.outchannels = range(self.model.output.shape[1]._value)\n", " elif K.backend() == \"theano\":\n", " self.outchannels = range(model1.output._keras_shape[1])\n", " else:\n", " if verbose: \n", " print \"Evaluated output channels (0-based index):\", \n", " for i in self.outchannels: print i\n", " print\n", " \n", " #Build gradient functions for desired output channels.\n", " self.get_gradients = {}\n", " if verbose: print \"Building gradient functions\"\n", " \n", " # Evaluate over all channels.\n", " for c in self.outchannels:\n", " # Get tensor that calcuates gradient\n", " if K.backend() == \"tensorflow\":\n", " gradients = self.model.optimizer.get_gradients(self.model.output[:, c], self.model.input)\n", " if K.backend() == \"theano\":\n", " gradients = self.model.optimizer.get_gradients(self.model.output[:, c].sum(), self.model.input)\n", " \n", " # Build computational graph that calculates the tensfor given inputs\n", " self.get_gradients[c] = K.function(inputs=self.input_tensors, outputs=gradients)\n", " \n", " # This takes a lot of time for a big model with many tasks.\n", " # So lets pring the progress.\n", " if verbose:\n", " sys.stdout.write('\\r')\n", " sys.stdout.write(\"Progress: \"+str(int((c+1)*1.0/len(self.outchannels)*1000)*1.0/10)+\"%\")\n", " sys.stdout.flush()\n", " # Done\n", " if verbose: print \"\\nDone.\"\n", " \n", " \n", " '''\n", " Input: sample to explain, channel to explain\n", " Optional inputs:\n", " - reference: reference values (defaulted to 0s).\n", " - steps: # steps from reference values to the actual sample.\n", " Output: list of numpy arrays to integrated over.\n", " '''\n", " def explain(self, sample, outc=0, reference=False, num_steps=50, verbose=0):\n", " \n", " # Each element for each input stream.\n", " samples = []\n", " numsteps = []\n", " step_sizes = []\n", " \n", " # If multiple inputs are present, feed them as list of np arrays. \n", " if isinstance(sample, list):\n", " #If reference is present, reference and sample size need to be equal.\n", " if reference != False: \n", " assert len(sample) == len(reference)\n", " for i in range(len(sample)):\n", " if reference == False:\n", " _output = integrated_gradients.linearly_interpolate(sample[i], False, num_steps)\n", " else:\n", " _output = integrated_gradients.linearly_interpolate(sample[i], False, num_steps)\n", " samples.append(_output[0])\n", " numsteps.append(_output[1])\n", " step_sizes.append(_output[2])\n", " \n", " # Or you can feed just a single numpy arrray. \n", " elif isinstance(sample, np.ndarray):\n", " _output = integrated_gradients.linearly_interpolate(sample, reference, num_steps)\n", " samples.append(_output[0])\n", " numsteps.append(_output[1])\n", " step_sizes.append(_output[2])\n", " \n", " # Desired channel must be in the list of outputchannels\n", " assert outc in self.outchannels\n", " if verbose: print \"Explaning the \"+str(self.outchannels[outc])+\"th output.\"\n", " \n", " # For tensorflow backend\n", " _input = []\n", " for s in samples:\n", " _input.append(s)\n", " _input.append(0)\n", " \n", " if K.backend() == \"tensorflow\": \n", " gradients = self.get_gradients[outc](_input)\n", " elif K.backend() == \"theano\":\n", " gradients = self.get_gradients[outc](_input)\n", " if len(self.model.inputs) == 1:\n", " gradients = [gradients]\n", " \n", " explanation = []\n", " for i in range(len(gradients)):\n", " _temp = np.sum(gradients[i], axis=0)\n", " explanation.append(np.multiply(_temp, step_sizes[i]))\n", " \n", "\n", " if isinstance(sample, list):\n", " return explanation\n", " elif isinstance(sample, np.ndarray):\n", " return explanation[0]\n", " return -1\n", "\n", " \n", " '''\n", " Input: numpy array of a sample\n", " Optional inputs:\n", " - reference: reference values (defaulted to 0s).\n", " - steps: # steps from reference values to the actual sample.\n", " Output: list of numpy arrays to integrated over.\n", " '''\n", " @staticmethod\n", " def linearly_interpolate(sample, reference=False, num_steps=50):\n", " # Use default reference values if reference is not specified\n", " if reference is False: reference = np.zeros(sample.shape);\n", "\n", " # Reference and sample shape needs to match exactly\n", " assert sample.shape == reference.shape\n", "\n", " # Calcuated stepwise difference from reference to the actual sample.\n", " ret = np.zeros(tuple([num_steps] +[i for i in sample.shape]))\n", " for s in range(num_steps):\n", " ret[s] = reference+(sample-reference)*(s*1.0/num_steps)\n", "\n", " return ret, num_steps, (sample-reference)*(1.0/num_steps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using keras Model API" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from keras.layers import Input, Dense\n", "from keras.models import Model\n", "\n", "_input = Input(shape=[4])\n", "probs = Dense(1, activation='sigmoid')(_input)\n", "\n", "model1 = Model(inputs=_input, outputs=probs)\n", "model1.compile(optimizer='sgd', loss='binary_crossentropy') " ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model1.fit(_X, _Y, epochs=10000, batch_size=128, verbose=0)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Evaluated output channel (0-based index): All\n", "Building gradient functions\n", "Progress: 100.0%\n", "Done.\n" ] } ], "source": [ "ig = integrated_gradients(model1)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "array([-0.13235252, -0.07901507, 0.05334638, 0.00674734])" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ig.explain(_X[0], outc=0, num_steps=1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using Keras two inputs" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import keras\n", "from keras.layers import Input, Dense, Flatten\n", "from keras.models import Model\n", "\n", "_input1 = Input(shape=[2])\n", "probs1 = Dense(2, activation='tanh')(_input1)\n", "\n", "_input2 = Input(shape=[2])\n", "probs2 = Dense(2, activation='tanh')(_input2)\n", "\n", "\n", "x = keras.layers.concatenate([probs1, probs2])\n", "\n", "x_out = Dense(1, activation='sigmoid')(x)\n", "\n", "model3 = Model(inputs=[_input1, _input2], outputs=x_out)\n", "model3.compile(optimizer='sgd', loss='binary_crossentropy') " ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model3.fit([_X[:, :2], _X[:, 2:]], _Y, epochs=1000, batch_size=128, verbose=0)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Evaluated output channel (0-based index): All\n", "Building gradient functions\n", "Progress: 100.0%\n", "Done.\n" ] } ], "source": [ "ig = integrated_gradients(model3)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[array([-0.15271009, 0.03400158]), array([ 0.06474359, 0.00295305])]" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ig.explain([_X[0, :2], _X[0, 2:]], outc=0, num_steps=1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Using Sequential Model" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from keras.models import Sequential\n", "from keras.layers import Dense\n", "from keras.layers.core import Activation\n", "\n", "model2 = Sequential([\n", " Dense(1, input_dim=4),\n", " Activation('sigmoid'),\n", "])\n", "model2.compile(optimizer='sgd', loss='binary_crossentropy')" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model2.fit(_X, _Y, epochs=10000, batch_size=128, verbose=0)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Evaluated output channel (0-based index): All\n", "Building gradient functions\n", "Progress: 100.0%\n", "Done.\n" ] }, { "data": { "text/plain": [ "array([-0.14350999, -0.09356359, 0.0575112 , 0.00818367])" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ig = integrated_gradients(model2)\n", "ig.explain(_X[0], outc=0, num_steps=1000)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 2 }