| { |
| "nbformat": 4, |
| "nbformat_minor": 0, |
| "metadata": { |
| "colab": { |
| "name": "mnist_tensorflow.ipynb", |
| "provenance": [], |
| "collapsed_sections": [] |
| }, |
| "kernelspec": { |
| "display_name": "Python 3", |
| "name": "python3" |
| } |
| }, |
| "cells": [ |
| { |
| "cell_type": "markdown", |
| "metadata": { |
| "id": "PZtRtMMUZHJS" |
| }, |
| "source": [ |
| "##### Copyright 2020 Google LLC.\n", |
| "\n", |
| "Licensed under the Apache License, Version 2.0 (the \"License\");" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "cellView": "form", |
| "id": "TouZL3JZZSQe" |
| }, |
| "source": [ |
| "#@title License header\n", |
| "# Copyright 2020 Google LLC\n", |
| "#\n", |
| "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", |
| "# you may not use this file except in compliance with the License.\n", |
| "# You may obtain a copy of the License at\n", |
| "#\n", |
| "# https://www.apache.org/licenses/LICENSE-2.0\n", |
| "#\n", |
| "# Unless required by applicable law or agreed to in writing, software\n", |
| "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", |
| "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", |
| "# See the License for the specific language governing permissions and\n", |
| "# limitations under the License." |
| ], |
| "execution_count": 1, |
| "outputs": [] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": { |
| "id": "O6c3qfq5Zv57" |
| }, |
| "source": [ |
| "# MNIST Model TensorFlow Training, IREE Execution\n", |
| "\n", |
| "## Overview\n", |
| "\n", |
| "This notebook creates and trains a TensorFlow 2.0 model for recognizing handwritten digits using the [MNIST dataset](https://en.wikipedia.org/wiki/MNIST_database), then compiles and executes that trained model using IREE." |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "colab": { |
| "base_uri": "https://localhost:8080/" |
| }, |
| "id": "ZpkMjTQxLzLq", |
| "outputId": "f2c8bee0-5407-4fda-8950-ed7a9666292e" |
| }, |
| "source": [ |
| "!python -m pip install iree-compiler-snapshot iree-runtime-snapshot iree-tools-tf-snapshot -f https://github.com/google/iree/releases" |
| ], |
| "execution_count": 2, |
| "outputs": [ |
| { |
| "output_type": "stream", |
| "text": [ |
| "Looking in links: https://github.com/google/iree/releases\n", |
| "Collecting iree-compiler-snapshot\n", |
| "\u001b[?25l Downloading https://github.com/google/iree/releases/download/snapshot-20210107.21/iree_compiler_snapshot-20210107.21-py3-none-manylinux2014_x86_64.whl (27.8MB)\n", |
| "\u001b[K |████████████████████████████████| 27.9MB 154kB/s \n", |
| "\u001b[?25hCollecting iree-runtime-snapshot\n", |
| "\u001b[?25l Downloading https://github.com/google/iree/releases/download/snapshot-20210107.21/iree_runtime_snapshot-20210107.21-cp36-cp36m-manylinux2014_x86_64.whl (1.0MB)\n", |
| "\u001b[K |████████████████████████████████| 1.0MB 56.9MB/s \n", |
| "\u001b[?25hCollecting iree-tools-tf-snapshot\n", |
| "\u001b[?25l Downloading https://github.com/google/iree/releases/download/snapshot-20210107.21/iree_tools_tf_snapshot-20210107.21-py3-none-manylinux2014_x86_64.whl (41.4MB)\n", |
| "\u001b[K |████████████████████████████████| 41.4MB 85kB/s \n", |
| "\u001b[?25hInstalling collected packages: iree-compiler-snapshot, iree-runtime-snapshot, iree-tools-tf-snapshot\n", |
| "Successfully installed iree-compiler-snapshot-20210107.21 iree-runtime-snapshot-20210107.21 iree-tools-tf-snapshot-20210107.21\n" |
| ], |
| "name": "stdout" |
| } |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "EPF7RGQDYK-M", |
| "colab": { |
| "base_uri": "https://localhost:8080/" |
| }, |
| "outputId": "99323e03-bad5-4146-ec39-5fba2e62939b" |
| }, |
| "source": [ |
| "#@title Imports and Setup\n", |
| "\n", |
| "from pyiree import rt as ireert\n", |
| "from pyiree.tf.support import module_utils\n", |
| "from pyiree.compiler2 import tf as tfc\n", |
| "\n", |
| "from matplotlib import pyplot as plt\n", |
| "import numpy as np\n", |
| "import os\n", |
| "import tempfile\n", |
| "import tensorflow as tf\n", |
| "\n", |
| "ARTIFACTS_DIR = os.path.join(tempfile.gettempdir(), 'iree', 'modules')\n", |
| "print(\"Artifacts directory is: \", ARTIFACTS_DIR)\n", |
| "\n", |
| "plt.style.use(\"seaborn-whitegrid\")\n", |
| "plt.rcParams[\"font.family\"] = \"monospace\"\n", |
| "\n", |
| "# Print version information for future notebook users to reference.\n", |
| "print(\"TensorFlow version: \", tf.__version__)\n", |
| "print(\"Numpy version: \", np.__version__)\n", |
| "\n", |
| "# (Temporary) workaround for absl flags...\n", |
| "# https://github.com/googlecolab/colabtools/issues/1323#issuecomment-756343620\n", |
| "import sys\n", |
| "from absl import app\n", |
| "sys.argv = sys.argv[:1]\n", |
| "try:\n", |
| " app.run(lambda argv: None)\n", |
| "except:\n", |
| " pass" |
| ], |
| "execution_count": 3, |
| "outputs": [ |
| { |
| "output_type": "stream", |
| "text": [ |
| "Artifacts directory is: /tmp/iree/modules\n", |
| "TensorFlow version: 2.4.0\n", |
| "Numpy version: 1.19.4\n" |
| ], |
| "name": "stdout" |
| } |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": { |
| "id": "5vkQOMOMbXdy" |
| }, |
| "source": [ |
| "# Create and Train MNIST Model in TensorFlow\n", |
| "\n", |
| "The specific details of the training process here aren't critical to the model compilation and execution through IREE." |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "XPo8ATGqqZbW", |
| "colab": { |
| "base_uri": "https://localhost:8080/", |
| "height": 348 |
| }, |
| "outputId": "b1a692a8-4523-4ba6-e00e-6cd71668fcc1" |
| }, |
| "source": [ |
| "#@title Load MNIST dataset, setup training and evaluation\n", |
| "\n", |
| "# Keras datasets don't provide metadata.\n", |
| "NUM_CLASSES = 10\n", |
| "NUM_ROWS, NUM_COLS = 28, 28\n", |
| "\n", |
| "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", |
| "\n", |
| "# Reshape into grayscale images:\n", |
| "x_train = np.reshape(x_train, (-1, NUM_ROWS, NUM_COLS, 1))\n", |
| "x_test = np.reshape(x_test, (-1, NUM_ROWS, NUM_COLS, 1))\n", |
| "\n", |
| "# Rescale uint8 pixel values into floats:\n", |
| "x_train = x_train / 255\n", |
| "x_test = x_test / 255\n", |
| "\n", |
| "# Explicitly cast to float32 because numpy defaults to double precision and\n", |
| "# IREE uses single precision:\n", |
| "x_train = x_train.astype(np.float32)\n", |
| "x_test = x_test.astype(np.float32)\n", |
| "\n", |
| "print(\"Sample image from the dataset:\")\n", |
| "sample_index = np.random.randint(x_train.shape[0])\n", |
| "plt.imshow(x_train[sample_index].reshape(NUM_ROWS, NUM_COLS), cmap=\"gray\")\n", |
| "plt.title(f\"Sample #{sample_index}, label: {y_train[sample_index]}\")\n", |
| "plt.axis(\"off\")\n", |
| "plt.tight_layout()" |
| ], |
| "execution_count": 4, |
| "outputs": [ |
| { |
| "output_type": "stream", |
| "text": [ |
| "Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz\n", |
| "11493376/11490434 [==============================] - 0s 0us/step\n", |
| "Sample image from the dataset:\n" |
| ], |
| "name": "stdout" |
| }, |
| { |
| "output_type": "display_data", |
| "data": { |
| "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQgAAAEYCAYAAACgIGhkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAARz0lEQVR4nO3dfUyV9f/H8dcBtJqCGiVF3ptZqTOSG8VKxSgllazQYTYbmuZNsbRMK+/2VZeZOVJr6tRmc1a0amlUBt7FbJCFZVpMTTFMMNFSTALOOb8/nOcX0zfXQRENn4+/Gtf7OtcH1CefA1fnuLxer1cAcB4Bl3sBAK5cBAKAiUAAMBEIACYCAcBEIACYCMQFKi4uVkREhLp06aLk5OTLvZz/nIyMDEVERKhTp05auHBhjc6dMmVKjc+52GterepNIPbs2aMRI0YoMjJSPXr00DPPPHNJrxcWFqa8vDzNmjXrkjx+dna2UlNTJUm9evXSqVOnJEnPP/+8evbsqW7duikpKUl5eXm+cxYtWqROnTopIiJCERERiouL8x0rLy/XrFmzFBsbq5iYGL322ms6ewvMgQMHNHLkSEVHR6tHjx6aPHmyTp486Tu3rKxM06dPV3R0tCIjIzVz5syL/vwSEhKUl5engQMHXvRjXUnXfPLJJ3Xfffddsseva/UmEOPHj1fv3r2Vk5OjL7/8Uvfcc8/lXtJF2bVrlzp37qzi4mIFBwerUaNGkqRRo0YpKytL3333nVJTUzVu3Di53W7fef3791deXp7y8vK0ceNG38fXrl2rH374QZ9//rk+/vhjff755/r0008lSaWlpRowYICysrK0ceNG/fPPP3r11Vd9586dO1cFBQVav369cnJyNGzYsDr6Kvy3ZGRkqLS09HIvo1bVi0AcO3ZMBQUFeuyxxxQYGKiQkBANHTrUd3zLli1KTExURESEYmNjtXDhQt93z7i4OI0ZM0Y9e/bU0qVL1bNnT02bNk2SVFhYqI4dO+qtt95St27dNGjQIO3evduvNbndbi1evFhxcXGKjY3V7NmzVVFR4ffn9NNPP6lz587auXOnunTp4vv47bffrmuvvVZer1eVlZU6duyYjh8/7vh43377rQYOHKgmTZooPDxcDz30kL766itJUufOnTV48GAFBwfruuuu08CBA7Vjxw5JZ3YPn3zyiaZNm6bmzZsrMDBQt912m9+fx4Vavny5+vbtq65duyo+Pl4ZGRlVjhcVFWnQoEGKiYnRnDlzqkRyw4YNGjBggKKiovTUU0/pyJEjfl+3uLhY/fr1831t/HXq1CktW7ZMTz/9dI3Ou9LVi0A0bdpU4eHhmjZtmnJyclReXl7luNfr1bRp05Sbm6v3339fH3zwgbKysnzHhw8frkceeUSZmZn64osvtG7dOv3zzz++43/++ae++eYbJSUladKkSfLn7vRVq1YpKytLa9eu1YYNG7R37169++67juctWLBAkZGRyszM1IQJE/Tcc88pIyOjyrZ15syZ6tKli8aMGaN+/frphhtu8B3btGmTYmJilJiYWGUHcb41FxQUnHcNeXl56tixo6QzTz9cLpcyMzMVGxurhISEGv/juRBNmjTR8uXLtWPHDr300kuaPHmyjh075ju+efNmpaWlKSMjQ19//bU+++wzSdKPP/6ol19+WXPmzNE333yjO++8U9OnT/f7uhUVFdq/f3+Vp1j+WLJkiYYMGaLg4OAanXelqxeBCAgI0DvvvKPAwEBNmDBBsbGxWrx4se947969FRkZqQYNGqhly5aKiYnRL7/84jveunVrtWrVSm3atFFwcLBCQkL0559/+o6PGDFCDRs2VHJysg4dOmT+w/q39PR0jR8/XmFhYWrcuLEef/xxbdiwwfG8SZMmacmSJYqLi1Nubq5at26trKwsbd261Tczc+ZMff/991qwYIH69+/v+/jZf7zZ2dkaP368Jk6cqF9//VWSFB0drXXr1un48eMqLCzUF198obKysnOu/9NPP+mjjz7y/fyjtLRUFRUV+u2337Rp0yZNnz5dkydPrtF35QsxZMgQtWvXTi6XS3369FFISIj27dvnOx4XF6e2bdsqNDRUgwYN0pYtWyRJH374oQYPHqyuXbsqKChIKSkp2rx58znfNCwtWrRQfn6+HnnkEb/Xum/fPm3btq3KrrW+CLrcC6gtrVu31oIFC+TxeJSTk6PU1FR16dJFvXr10o8//qj58+drz549qqysVFlZmdq0aeM7NyAgQIGBgQoMDJQkBQUFqbKy0nc8NDTU9/GQkBAdPXq0yvnnU1RUpMmTJysg4EyDPR6PbrzxxmrP2bFjh0aNGqWysjIFBQUpMjJS5eXl6tevnxYtWqQePXr4Zhs2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\n", |
| "text/plain": [ |
| "<Figure size 432x288 with 1 Axes>" |
| ] |
| }, |
| "metadata": { |
| "tags": [] |
| } |
| } |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "tHq96SIJcNfx" |
| }, |
| "source": [ |
| "#@title Define a DNN model using tf.keras API\n", |
| "\n", |
| "def simple_dnn(num_classes):\n", |
| " \"\"\"Creates a simple multi-layer perceptron model.\"\"\"\n", |
| "\n", |
| " model = tf.keras.models.Sequential()\n", |
| " # Flatten to a 1d array (e.g. 28x28x1 -> 784).\n", |
| " model.add(tf.keras.layers.Flatten())\n", |
| " # Fully-connected neural layer with 128 neurons, RELU activation.\n", |
| " model.add(tf.keras.layers.Dense(128, activation=\"relu\"))\n", |
| " # Fully-connected neural layer returning probability scores for each class.\n", |
| " model.add(tf.keras.layers.Dense(num_classes, activation=\"softmax\"))\n", |
| " return model" |
| ], |
| "execution_count": 5, |
| "outputs": [] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "43BH_9YcsGs8" |
| }, |
| "source": [ |
| "#@markdown ### Training Parameters\n", |
| "\n", |
| "batch_size = 32 #@param { type: \"slider\", min: 10, max: 400 }\n", |
| "num_epochs = 8 #@param { type: \"slider\", min: 1, max: 20 }" |
| ], |
| "execution_count": 6, |
| "outputs": [] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "7Gdxh7qWcPSO", |
| "colab": { |
| "base_uri": "https://localhost:8080/" |
| }, |
| "outputId": "ff0cf33d-353e-478b-b80c-3fb67d97744d" |
| }, |
| "source": [ |
| "#@title Train the Keras model\n", |
| "\n", |
| "tf_model = simple_dnn(NUM_CLASSES)\n", |
| "# Stateful optimizers like Adam create variable incompatible with compilation as\n", |
| "# currently implemented.\n", |
| "tf_model.compile(\n", |
| " optimizer=\"sgd\", loss=\"sparse_categorical_crossentropy\", metrics=\"accuracy\")\n", |
| "tf_model.fit(x_train, y_train, batch_size, num_epochs, validation_split=0.1)" |
| ], |
| "execution_count": 7, |
| "outputs": [ |
| { |
| "output_type": "stream", |
| "text": [ |
| "Epoch 1/8\n", |
| "1688/1688 [==============================] - 4s 2ms/step - loss: 1.1068 - accuracy: 0.7158 - val_loss: 0.3270 - val_accuracy: 0.9142\n", |
| "Epoch 2/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.3705 - accuracy: 0.8982 - val_loss: 0.2648 - val_accuracy: 0.9288\n", |
| "Epoch 3/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.3143 - accuracy: 0.9133 - val_loss: 0.2326 - val_accuracy: 0.9397\n", |
| "Epoch 4/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.2728 - accuracy: 0.9236 - val_loss: 0.2127 - val_accuracy: 0.9440\n", |
| "Epoch 5/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.2529 - accuracy: 0.9294 - val_loss: 0.1982 - val_accuracy: 0.9477\n", |
| "Epoch 6/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.2327 - accuracy: 0.9347 - val_loss: 0.1853 - val_accuracy: 0.9523\n", |
| "Epoch 7/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.2141 - accuracy: 0.9403 - val_loss: 0.1720 - val_accuracy: 0.9572\n", |
| "Epoch 8/8\n", |
| "1688/1688 [==============================] - 3s 2ms/step - loss: 0.2040 - accuracy: 0.9417 - val_loss: 0.1652 - val_accuracy: 0.9588\n" |
| ], |
| "name": "stdout" |
| }, |
| { |
| "output_type": "execute_result", |
| "data": { |
| "text/plain": [ |
| "<tensorflow.python.keras.callbacks.History at 0x7fb807694160>" |
| ] |
| }, |
| "metadata": { |
| "tags": [] |
| }, |
| "execution_count": 7 |
| } |
| ] |
| }, |
| { |
| "cell_type": "markdown", |
| "metadata": { |
| "id": "nZdVUd_dgTtc" |
| }, |
| "source": [ |
| "# Compile and Execute MNIST Model using IREE" |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "DmespEaFcSEL" |
| }, |
| "source": [ |
| "#@title Wrap the model in a tf.Module with IREE-compatible settings and convert to MLIR.\n", |
| "\n", |
| "# Since the model was written in sequential style, explicitly wrap in a module.\n", |
| "inference_module = tf.Module()\n", |
| "inference_module.model = tf_model\n", |
| "\n", |
| "# Hack: Convert to static shape. Won't be necessary once dynamic shapes are in.\n", |
| "input_shape = list(tf_model.inputs[0].shape)\n", |
| "input_shape[0] = 1 # Make fixed (batch=1)\n", |
| "\n", |
| "# Produce a concrete function to compile.\n", |
| "inference_module.predict = tf.function(input_signature=[\n", |
| " tf.TensorSpec(input_shape, tf_model.inputs[0].dtype)\n", |
| "])(lambda x: tf_model.call(x, training=False))\n", |
| "\n", |
| "# Only try to compile the function we care about:\n", |
| "exported_names = [\"predict\"]" |
| ], |
| "execution_count": 8, |
| "outputs": [] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "G7v-2EbjyggO" |
| }, |
| "source": [ |
| "#@markdown ### Backend Configuration\n", |
| "\n", |
| "backend_choice = \"iree_vmla (CPU)\" #@param [ \"iree_vmla (CPU)\", \"iree_llvmaot (CPU)\", \"iree_vulkan (GPU/SwiftShader)\" ]\n", |
| "backend_choice = backend_choice.split(\" \")[0]\n", |
| "backend = module_utils.BackendInfo(backend_choice)" |
| ], |
| "execution_count": 9, |
| "outputs": [] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "IDHI7h3khJr9", |
| "colab": { |
| "base_uri": "https://localhost:8080/" |
| }, |
| "outputId": "79f3191b-7e8e-4e10-9cea-cff168f7c958" |
| }, |
| "source": [ |
| "#@title Compile the mhlo MLIR to an IREE backend and prepare a context to execute it\n", |
| "\n", |
| "iree_module = module_utils.IreeCompiledModule.create_from_instance(\n", |
| " inference_module, backend, exported_names, ARTIFACTS_DIR)\n", |
| "\n", |
| "print(\"* Module compiled! See intermediate .mlir files in\", ARTIFACTS_DIR, \"*\")" |
| ], |
| "execution_count": 10, |
| "outputs": [ |
| { |
| "output_type": "stream", |
| "text": [ |
| "I0107 21:49:35.160961 140429953972096 module_utils.py:88] Outputting intermediate artifacts (--artifacts_dir is set):\n", |
| " output_file: /tmp/iree/modules/iree_vmla/compiled.vmfb\n", |
| " saved_model_dir: /tmp/iree/modules/tfmodule.saved_model\n", |
| " save_temp_tf_input: /tmp/iree/modules/tf_input.mlir\n", |
| " save_temp_iree_input: /tmp/iree/modules/iree_input.mlir\n", |
| " crash_reproducer_path: /tmp/iree/modules/reproducer__iree_vmla.mlir\n" |
| ], |
| "name": "stderr" |
| }, |
| { |
| "output_type": "stream", |
| "text": [ |
| "INFO:tensorflow:Assets written to: /tmp/iree/modules/tfmodule.saved_model/assets\n" |
| ], |
| "name": "stdout" |
| }, |
| { |
| "output_type": "stream", |
| "text": [ |
| "I0107 21:49:35.632078 140429953972096 builder_impl.py:775] Assets written to: /tmp/iree/modules/tfmodule.saved_model/assets\n" |
| ], |
| "name": "stderr" |
| }, |
| { |
| "output_type": "stream", |
| "text": [ |
| "* Module compiled! See intermediate .mlir files in /tmp/iree/modules *\n" |
| ], |
| "name": "stdout" |
| }, |
| { |
| "output_type": "stream", |
| "text": [ |
| "2021-01-07 21:49:35.767974: I external/org_tensorflow/tensorflow/cc/saved_model/bundle_v2.cc:32] Reading SavedModel from: /tmp/iree/modules/tfmodule.saved_model\n", |
| "2021-01-07 21:49:35.768968: I external/org_tensorflow/tensorflow/cc/saved_model/bundle_v2.cc:55] Reading SavedModel debug info (if present) from: /tmp/iree/modules/tfmodule.saved_model\n", |
| "Created IREE driver vmla: <pyiree.rt.binding.HalDriver object at 0x7fb803d9d2d0>\n", |
| "SystemContext driver=<pyiree.rt.binding.HalDriver object at 0x7fb803d9d2d0>\n" |
| ], |
| "name": "stderr" |
| } |
| ] |
| }, |
| { |
| "cell_type": "code", |
| "metadata": { |
| "id": "S2FYao92Xd6r", |
| "colab": { |
| "base_uri": "https://localhost:8080/", |
| "height": 297 |
| }, |
| "outputId": "38a1c963-f484-455e-da78-1dcabdf1f92b" |
| }, |
| "source": [ |
| "#@title Execute the compiled module and compare the results with TensorFlow\n", |
| "\n", |
| "# Invoke the 'predict' function with a single image as an argument\n", |
| "iree_prediction = iree_module.predict(x_train[sample_index][None, :])[0]\n", |
| "tf_prediction = tf_model.predict(x_train[sample_index][None, :])[0]\n", |
| "error = tf_prediction - iree_prediction\n", |
| "\n", |
| "fig, axs = plt.subplots(1, 2)\n", |
| "fig.set_figwidth(12)\n", |
| "\n", |
| "ax = axs[0]\n", |
| "ax.plot(iree_prediction, linewidth=2, label=backend.backend_name)\n", |
| "ax.plot(tf_prediction, linewidth=2, label=\"tf\")\n", |
| "\n", |
| "ax.set_title(\"Predictions\")\n", |
| "ax.set_ylabel(\"Softmax 'Probability'\")\n", |
| "ax.set_xlabel(\"Digit\")\n", |
| "ax.set_ylim(0, 1)\n", |
| "ax.set_xlim(0, 9)\n", |
| "ax.legend(frameon=True)\n", |
| "\n", |
| "ax = axs[1]\n", |
| "ax.plot(error)\n", |
| "\n", |
| "ax.set_title(\"Error\")\n", |
| "ax.set_ylabel(\"Numerical between TF and IREE\")\n", |
| "ax.set_xlabel(\"Digit\")\n", |
| "ylim = 1.25 * np.max(np.abs(error))\n", |
| "ax.set_ylim(-ylim, ylim)\n", |
| "ax.set_xlim(0, 9)\n", |
| "\n", |
| "fig.tight_layout()" |
| ], |
| "execution_count": 11, |
| "outputs": [ |
| { |
| "output_type": "display_data", |
| "data": { |
| "image/png": "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2oqOjmbw4MEkJiYCkJmZSUFBgdcWsY8//pgpU6aYjtGhHTt2kJCQ0Drs4pJLLiEvL89r/y+9++67ufvuuwG48847SUhIMJyofapPnqH65D6+VJ9UmzxD9cl9ulKbvH6I4KlrZJWWlmqNLDewLIvFixdz6623Mm7cONNxOlRWVkZlZSXQ/G5mfn4+cXFxhlOd6dFHH2Xz5s188sknzJkzh3vvvdcrixdAVVUVdXV1ABQXF1NWVuaVL16HDx9OSUkJVVVVOJ1OfvjhB5KTk03H6pC3F7Do6Gh2796N0+mkrq6O77//3qv/ni3P+6+//prq6mouvfRSw4nap/rkfqpP7uUr9Um1yXNUn9ynK7XJ689g+dIaWUuXLmXz5s1UVlYybtw4lixZwqRJk0zHOkN2djaffvop+fn5rFq1CoDXXnvN64pDSUkJTz/9NND8AchFixZ55TsavqSgoIDFixcTEhJCYGAgzz77LH369DEd6wzh4eH84Q9/YN68ebhcLm699VaveyezRU5ODn369OHiiy82HaVDWVlZXHvttdx+++0EBAQwc+ZMr/17QvP/84WFhQQFBfH8889js9lMR2o4LEYsAAACqElEQVSX6pP7qT75J9Umz1B9cq+u1CafWAdLRERERETEF3jnW20iIiIiIiI+SA2WiIiIiIiIm6jBEhERERERcRM1WCIiIiIiIm6iBktERERERMRN1GCJdIM1a9Zw1VVXMXXqVO6//36Kiopab9uyZQuvvfbaOR+ro/3feOMNamtr3ZJXRET8g+qTiPtpmnaRbrBmzRr27NnD008/zY4dO1i6dCkbN24kMDDQbT9j4sSJrF69mqioKLcdU0REejbVJxH30xkskW529dVXExkZye7du3niiScYP348f/7zn9vss3LlSm6++WYeeOABbrjhBoqLiwHa3f/LL79k6tSpHDlyhHnz5jF16lTKysq69XcSERHfp/ok4h5BpgOI+KPExESKi4v5y1/+0vruYYuSkhLef/991q9fT2lpKbfcckvrbe3tf80117B+/XomTpzIm2++qXcIRUTkvKk+iVw4ncES8TJ79+4lKyuLPn36kJaWRmJioulIIiIiqk8i50gNlogBJSUlJCcnm44hIiLShuqTyIVTgyXSzXbs2MHRo0cZPnx4u7dnZmaSnZ1NbW0t+fn5lJSUnNNx+/btS3V1tTujioiIH1F9EnEPfQZLpJts2rSJ7OxsYmNjef311yktLeXBBx+kurqauro6srOzWbhwIddffz133nkn06dPZ/DgwSQnJxMSEkJxcXGH+wPcddddPPjgg0RERPDXv/6VmJgYw7+xiIj4AtUnEffSNO0iXsjhcBAWFsbRo0eZMWMGW7duxWazmY4lIiJ+TvVJpHM6gyXihZYtW0Zubi4Af/zjH1W8RETEK6g+iXROZ7BERERERETcRJNciIiIiIiIuIkaLBERERERETdRgyUiIiIiIuImarBERERERETcRA2WiIiIiIiIm/x/EGOEhBFzbOwAAAAASUVORK5CYII=\n", |
| "text/plain": [ |
| "<Figure size 864x288 with 2 Axes>" |
| ] |
| }, |
| "metadata": { |
| "tags": [] |
| } |
| } |
| ] |
| } |
| ] |
| } |