diff options
Diffstat (limited to 'tensorflow/contrib/lite/tutorials/post_training_quant.ipynb')
-rw-r--r-- | tensorflow/contrib/lite/tutorials/post_training_quant.ipynb | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb b/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb new file mode 100644 index 0000000000..a96e2c4e1b --- /dev/null +++ b/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb @@ -0,0 +1,702 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6Y8E0lw5eYWm" + }, + "source": [ + "# Post Training Quantization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "CIGrZZPTZVeO" + }, + "source": [ + "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n", + " \u003ctd\u003e\n", + " \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n", + " \u003c/td\u003e\n", + " \u003ctd\u003e\n", + " \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/tutorials/post_training_quant.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n", + " \u003c/td\u003e\n", + "\u003c/table\u003e" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "BTC1rDAuei_1" + }, + "source": [ + "## Overview\n", + "\n", + "[TensorFlow Lite](https://www.tensorflow.org/mobile/tflite/) now supports\n", + "converting weights to 8 bit precision as part of model conversion from\n", + "tensorflow graphdefs to TFLite's flat buffer format. Weight quantization\n", + "achieves a 4x reduction in the model size. In addition, TFLite supports on the\n", + "fly quantization and dequantization of activations to allow for:\n", + "\n", + "1. Using quantized kernels for faster implementation when available.\n", + "\n", + "2. Mixing of floating-point kernels with quantized kernels for different parts\n", + " of the graph.\n", + "\n", + "Note that the activations are always stored in floating point. For ops that\n", + "support quantized kernels, the activations are quantized to 8 bits of precision\n", + "dynamically prior to processing and are de-quantized to float precision after\n", + "processing. Depending on the model being converted, this can give a speedup over\n", + "pure floating point computation.\n", + "\n", + "In contrast to\n", + "[quantization aware training](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/quantize)\n", + ", the weights are quantized post training and the activations are quantized dynamically \n", + "at inference in this method.\n", + "Therefore, the model weights are not retrained to compensate for quantization\n", + "induced errors. It is important to check the accuracy of the quantized model to\n", + "ensure that the degradation is acceptable.\n", + "\n", + "In this tutorial, we train an MNIST model from scratch, check its accuracy in\n", + "tensorflow and then convert the saved model into a Tensorflow Lite flatbuffer\n", + "with weight quantization. We finally check the\n", + "accuracy of the converted model and compare it to the original saved model. We\n", + "run the training script mnist.py from\n", + "[Tensorflow official mnist tutorial](https://github.com/tensorflow/models/tree/master/official/mnist).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "2XsEP17Zelz9" + }, + "source": [ + "## Building an MNIST model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "dDqqUIZjZjac" + }, + "source": [ + "### Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "gyqAw1M9lyab" + }, + "outputs": [], + "source": [ + "! pip uninstall -y tensorflow\n", + "! pip install -U tf-nightly" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "WsN6s5L1ieNl" + }, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "tf.enable_eager_execution()" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "00U0taBoe-w7" + }, + "outputs": [], + "source": [ + "! git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4XZPtSh-fUOc" + }, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "\n", + "if sys.version_info.major \u003e= 3:\n", + " import pathlib\n", + "else:\n", + " import pathlib2 as pathlib\n", + "\n", + "# Add `models` to the python path.\n", + "models_path = os.path.join(os.getcwd(), \"models\")\n", + "sys.path.append(models_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "eQ6Q0qqKZogR" + }, + "source": [ + "### Train and export the model" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "eMsw_6HujaqM" + }, + "outputs": [], + "source": [ + "saved_models_root = \"/tmp/mnist_saved_model\"" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hWSAjQWagIHl" + }, + "outputs": [], + "source": [ + "# The above path addition is not visible to subprocesses, add the path for the subprocess as well.\n", + "# Note: channels_last is required here or the conversion may fail. \n", + "!PYTHONPATH={models_path} python models/official/mnist/mnist.py --train_epochs=1 --export_dir {saved_models_root} --data_format=channels_last" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "5NMaNZQCkW9X" + }, + "source": [ + "For the example, we only trained the model for a single epoch, so it only trains to ~96% accuracy.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "xl8_fzVAZwOh" + }, + "source": [ + "### Convert to a TFLite model\n", + "\n", + "The `savedmodel` directory is named with a timestamp. Select the most recent one: " + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Xp5oClaZkbtn" + }, + "outputs": [], + "source": [ + "saved_model_dir = str(sorted(pathlib.Path(saved_models_root).glob(\"*\"))[-1])\n", + "saved_model_dir" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "AT8BgkKmljOy" + }, + "source": [ + "Using the python `TocoConverter`, the saved model can be converted into a TFLite model.\n", + "\n", + "First load the model using the `TocoConverter`:" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "_i8B2nDZmAgQ" + }, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "tf.enable_eager_execution()\n", + "converter = tf.contrib.lite.TocoConverter.from_saved_model(saved_model_dir)\n", + "tflite_model = converter.convert()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "F2o2ZfF0aiCx" + }, + "source": [ + "Write it out to a tflite file:" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "vptWZq2xnclo" + }, + "outputs": [], + "source": [ + "tflite_models_dir = pathlib.Path(\"/tmp/mnist_tflite_models/\")\n", + "tflite_models_dir.mkdir(exist_ok=True, parents=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Ie9pQaQrn5ue" + }, + "outputs": [], + "source": [ + "tflite_model_file = tflite_models_dir/\"mnist_model.tflite\"\n", + "tflite_model_file.write_bytes(tflite_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7BONhYtYocQY" + }, + "source": [ + "To quantize the model on export, set the `post_training_quantize` flag:" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "g8PUvLWDlmmz" + }, + "outputs": [], + "source": [ + "# Note: If you don't have a recent tf-nightly installed, the\n", + "# \"post_training_quantize\" line will have no effect.\n", + "tf.logging.set_verbosity(tf.logging.INFO)\n", + "converter.post_training_quantize = True\n", + "tflite_quant_model = converter.convert()\n", + "tflite_model_quant_file = tflite_models_dir/\"mnist_model_quant.tflite\"\n", + "tflite_model_quant_file.write_bytes(tflite_quant_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "PhMmUTl4sbkz" + }, + "source": [ + "Note how the resulting file, with `post_training_quantize` set, is approximately `1/4` the size." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "JExfcfLDscu4" + }, + "outputs": [], + "source": [ + "!ls -lh {tflite_models_dir}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L8lQHMp_asCq" + }, + "source": [ + "## Run the TFLite models" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-5l6-ciItvX6" + }, + "source": [ + "We can run the TensorFlow Lite model using the python TensorFlow Lite\n", + "Interpreter. \n", + "\n", + "### load the test data\n", + "\n", + "First let's load the mnist test data to feed to it:" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "eTIuU07NuKFL" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "mnist_train, mnist_test = tf.keras.datasets.mnist.load_data()\n", + "images, labels = tf.to_float(mnist_test[0])/255.0, mnist_test[1]\n", + "\n", + "# Note: If you change the batch size, then use \n", + "# `tf.contrib.lite.Interpreter.resize_tensor_input` to also change it for\n", + "# the interpreter.\n", + "mnist_ds = tf.data.Dataset.from_tensor_slices((images, labels)).batch(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Ap_jE7QRvhPf" + }, + "source": [ + "### Load the model into an interpreter" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Jn16Rc23zTss" + }, + "outputs": [], + "source": [ + "interpreter = tf.contrib.lite.Interpreter(model_path=str(tflite_model_file))\n", + "interpreter.allocate_tensors()\n", + "input_index = interpreter.get_input_details()[0][\"index\"]\n", + "output_index = interpreter.get_output_details()[0][\"index\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "J8Pztk1mvNVL" + }, + "outputs": [], + "source": [ + "tf.logging.set_verbosity(tf.logging.DEBUG)\n", + "interpreter_quant = tf.contrib.lite.Interpreter(model_path=str(tflite_model_quant_file))" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Afl6yGvWyqAr" + }, + "outputs": [], + "source": [ + "interpreter_quant.allocate_tensors()\n", + "input_index = interpreter_quant.get_input_details()[0][\"index\"]\n", + "output_index = interpreter_quant.get_output_details()[0][\"index\"]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "2opUt_JTdyEu" + }, + "source": [ + "### Test the model on one image" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "AKslvo2kwWac" + }, + "outputs": [], + "source": [ + "for img, label in mnist_ds.take(1):\n", + " break\n", + "\n", + "interpreter.set_tensor(input_index, img)\n", + "interpreter.invoke()\n", + "predictions = interpreter.get_tensor(output_index)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "XZClM2vo3_bm" + }, + "outputs": [], + "source": [ + "import matplotlib.pylab as plt\n", + "\n", + "plt.imshow(img[0])\n", + "template = \"True:{true}, predicted:{predict}\"\n", + "_ = plt.title(template.format(true= str(label[0].numpy()),\n", + " predict=str(predictions[0,0])))\n", + "plt.grid(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LwN7uIdCd8Gw" + }, + "source": [ + "### Evaluate the models" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "05aeAuWjvjPx" + }, + "outputs": [], + "source": [ + "def eval_model(interpreter, mnist_ds):\n", + " total_seen = 0\n", + " num_correct = 0\n", + "\n", + " for img, label in mnist_ds:\n", + " total_seen += 1\n", + " interpreter.set_tensor(input_index, img)\n", + " interpreter.invoke()\n", + " predictions = interpreter.get_tensor(output_index)\n", + " if predictions == label.numpy():\n", + " num_correct += 1\n", + "\n", + " if total_seen % 500 == 0:\n", + " print(\"Accuracy after %i images: %f\" %\n", + " (total_seen, float(num_correct) / float(total_seen)))\n", + "\n", + " return float(num_correct) / float(total_seen)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "DqXBnDfJ7qxL" + }, + "outputs": [], + "source": [ + "print(eval_model(interpreter_quant, mnist_ds))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Km3cY9ry8ZlG" + }, + "source": [ + "We can repeat the evaluation on the weight quantized model to obtain:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-9cnwiPp6EGm" + }, + "outputs": [], + "source": [ + "print(eval_model(interpreter_quant, mnist_ds))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "L7lfxkor8pgv" + }, + "source": [ + "\n", + "In this example, we have compressed model with no difference in the accuracy." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "M0o1FtmWeKZm" + }, + "source": [ + "\n", + "\n", + "## Optimizing an existing model\n", + "\n", + "We now consider another example. Resnets with pre-activation layers (Resnet-v2) are widely used for vision applications.\n", + " Pre-trained frozen graph for resnet-v2-101 is available at the\n", + " [Tensorflow Lite model repository](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/g3doc/models.md).\n", + "\n", + "We can convert the frozen graph to a TFLite flatbuffer with quantization by:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "v5p5VcNPjILQ" + }, + "outputs": [], + "source": [ + "archive_path = tf.keras.utils.get_file(\"resnet_v2_101.tgz\", \"https://storage.googleapis.com/download.tensorflow.org/models/tflite_11_05_08/resnet_v2_101.tgz\", extract=True)\n", + "archive_path = pathlib.Path(archive_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-sxnXQuC4ThD" + }, + "source": [ + "The `info.txt` file lists the input and output names. You can also find them using TensorBoard to visually inspect the graph." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "g_Q_OMEJ4LIc" + }, + "outputs": [], + "source": [ + "! cat {archive_path}/resnet_v2_101_299_info.txt" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ujCAFhqm-C6H" + }, + "outputs": [], + "source": [ + "graph_def_file = pathlib.Path(archive_path).parent/\"resnet_v2_101_299_frozen.pb\"\n", + "input_arrays = [\"input\"] \n", + "output_arrays = [\"output\"]\n", + "converter = tf.contrib.lite.TocoConverter.from_frozen_graph(\n", + " str(graph_def_file), input_arrays, output_arrays, input_shapes={\"input\":[1,299,299,3]})\n", + "converter.post_training_quantize = True\n", + "resnet_tflite_file = graph_def_file.parent/\"resnet_v2_101_quantized.tflite\"\n", + "resnet_tflite_file.write_bytes(converter.convert())\n" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "vhOjeg1x9Knp" + }, + "outputs": [], + "source": [ + "archive_dir = str(archive_path.parent)\n", + "!ls -lh {archive_dir}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qqHLaqFMCjRZ" + }, + "source": [ + "\n", + "The model size reduces from 171 MB to 43 MB.\n", + "The accuracy of this model on imagenet can be evaluated using the scripts provided for [TFLite accuracy measurement](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/lite/tools/accuracy/ilsvrc).\n", + "\n", + "The optimized model top-1 accuracy is 76.8, the same as the floating point model." + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "post-training-quant.ipynb", + "private_outputs": true, + "provenance": [], + "toc_visible": true, + "version": "0.3.2" + }, + "kernelspec": { + "display_name": "Python 2", + "name": "python2" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} |