From f535f521ad54ab35fe88b0b05762de5be361ba96 Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Wed, 20 Mar 2024 23:15:06 +0000 Subject: [PATCH 1/6] zscore tutorial --- ...le_keras_activation_threshold_search.ipynb | 9 +- ...e_keras_activation_z_score_threshold.ipynb | 847 ++++++++++++++++++ .../utils/keras_tutorial_tools(1).py | 164 ++++ 3 files changed, 1017 insertions(+), 3 deletions(-) create mode 100644 tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb create mode 100644 tutorials/resources/utils/keras_tutorial_tools(1).py diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb index bf9936868..6def238ce 100644 --- a/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb @@ -267,10 +267,13 @@ }, "outputs": [], "source": [ - "representative_dataset_folder = './imagenet/val'\n", - "batch_size = 20\n", + "REPRESENTATIVE_DATASET_FOLDER = './imagenet/val'\n", + "BATCH_SIZE = 20\n", "fraction =0.001\n", - "representative_dataset_gen = tutorial_tools.get_representative_dataset(fraction, representative_dataset_folder, batch_size)" + "model_version = 'MobileNetV2'\n", + "\n", + "preprocessor = tutorial_tools.DatasetPreprocessor(model_version=model_version)\n", + "representative_dataset_gen = preprocessor.get_representative_dataset(fraction, REPRESENTATIVE_DATASET_FOLDER, BATCH_SIZE)" ] }, { diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb new file mode 100644 index 000000000..2251add8f --- /dev/null +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb @@ -0,0 +1,847 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20" + }, + "source": [ + "# Activation Z-Score Threshold Demonstration For Post-Training Quantization\n", + "\n", + "\n" + ], + "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9be59ea8-e208-4b64-aede-1dd6270b3540" + }, + "source": [ + "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb)" + ], + "id": "9be59ea8-e208-4b64-aede-1dd6270b3540" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "930e6d6d-4980-4d66-beed-9ff5a494acf9" + }, + "source": [ + "## Overview" + ], + "id": "930e6d6d-4980-4d66-beed-9ff5a494acf9" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "699be4fd-d382-4eec-9d3f-e2e85cfb1762" + }, + "source": [ + "This tutorial demonstrates the process used to find the activation z-score threshold, a step that MCT can use during post-training quantization.\n", + "\n", + "In this example we will explore how setting different z scores effects threshold and accuracy. We will start by demonstrating how to apply the corresponding MCT configurations, then, we will feed a representative dataset through the model, plot the activation distribution of an activation layer with their respective MCT calculated z-score thresholds, and finally compare the quantized model accuracy of the examples of different z-score.\n" + ], + "id": "699be4fd-d382-4eec-9d3f-e2e85cfb1762" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "85199e25-c587-41b1-aaf5-e1d23ce97ca1" + }, + "source": [ + "## Activation threshold explanation" + ], + "id": "85199e25-c587-41b1-aaf5-e1d23ce97ca1" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a89a17f4-30c9-4caf-a888-424f7a82fbc8" + }, + "source": [ + "During quantization process, thresholds are used to map a distribution of 32-bit float values to their quantized counterparts. Doing this with the least loss of data while maintaining the most representative range is important for final model accuracy.\n", + "\n", + "Some models exhibit anomolus values when fed a representative dataset. It is in the interest of the models accuracy to remove these values so that the quantisation threshold results in a more reliable range mapping.\n", + "\n", + "MCT has the option to remove these using z-score thresholding. Allowing the user to remove data based on standard distributions.\n", + "\n", + "The Z-score of a value is calculated by subtracting the mean of the dataset from the value and then dividing by the standard deviation of the dataset. This measures how many standard deviations an element is from the mean.\n", + "\n", + "\n", + "\n", + "To calculate a threshold $t$ for quantization based on a Z-score threshold $Z_t$, you might define $t$ as a function of $Z_t$, $\\mu$, and $\\sigma$, such as:\n", + "\n", + "$$\n", + "t(Zt)=\\frac{μ+Zt⋅σ}{μ+Zt​⋅σ}\n", + "$$\n", + "Where:\n", + "\n", + "- $t(Z_t)$: The quantization threshold calculated based on a Z-score threshold $Z_t$.\n", + "- $Z_t$: The chosen Z-score threshold value, which determines how many standard deviations from the mean an activation needs to be to be considered for special handling (e.g., removal or adjustment) before the main quantization process.\n", + "- $\\mu = \\frac{1}{n_s} \\sum_{X \\in Fl(D)} X$: The mean of activations\n", + "- $\\sigma = \\sqrt{\\frac{1}{n_s} \\sum_{X \\in Fl(D)} (X - \\mu)^2}$: The standard deviation of activations in $Fl(D)$.\n", + "where:\n", + "- $Fl(D)$ is the activation distribution and $X$ is an individual activation.\n", + "\n", + "\n", + "This equation for $t(Z_t)$ allows you to set a threshold based on the statistical distribution of activations, identifying values that are unusually high or low relative to the rest of the data. These identified values can then be removed before applying the main quantization algorithm." + ], + "id": "a89a17f4-30c9-4caf-a888-424f7a82fbc8" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "04228b7c-00f1-4ded-bead-722e2a4e89a0" + }, + "source": [ + "## Setup" + ], + "id": "04228b7c-00f1-4ded-bead-722e2a4e89a0" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2657cf1a-654d-45a6-b877-8bf42fc26d0d" + }, + "source": [ + "Install and import the relevant packages:\n" + ], + "id": "2657cf1a-654d-45a6-b877-8bf42fc26d0d" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324" + }, + "outputs": [], + "source": [ + "!pip install -q tensorflow\n", + "!pip install -q mct-nightly" + ], + "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" + }, + "outputs": [], + "source": [ + "import tensorflow as tf\n", + "import keras\n", + "import model_compression_toolkit as mct\n", + "import os" + ], + "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z8F-avk3azgZ" + }, + "source": [ + "Clone MCT to gain access to tutorial scripts" + ], + "id": "z8F-avk3azgZ" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "e3b675cf-e1b5-4249-a581-ffb9b1c16ba1" + }, + "outputs": [], + "source": [ + "!git clone https://github.com/sony/model_optimization.git local_mct\n", + "!pip install -r ./local_mct/requirements.txt\n", + "import sys\n", + "sys.path.insert(0,\"./local_mct\")\n", + "import tutorials.resources.utils.keras_tutorial_tools as tutorial_tools\n" + ], + "id": "e3b675cf-e1b5-4249-a581-ffb9b1c16ba1" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0c7fed0d-cfc8-41ee-adf1-22a98110397b" + }, + "source": [ + "## Dataset" + ], + "id": "0c7fed0d-cfc8-41ee-adf1-22a98110397b" + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "id": "aecde59e4c37b1da" + }, + "source": [ + "Load ImageNet classification dataset and seperate a small representative subsection of this dataset to use for quantization." + ], + "id": "aecde59e4c37b1da" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_ztv72uM6-UT" + }, + "outputs": [], + "source": [ + "if not os.path.isdir('imagenet'):\n", + " !mkdir imagenet\n", + " !wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_devkit_t12.tar.gz\n", + " !mv ILSVRC2012_devkit_t12.tar.gz imagenet/\n", + " !wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.tar\n", + " !mv ILSVRC2012_img_val.tar imagenet/" + ], + "id": "_ztv72uM6-UT" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YVAoUjK47Zcp" + }, + "outputs": [], + "source": [ + "import torchvision\n", + "if not os.path.isdir('imagenet/val'):\n", + " ds = torchvision.datasets.ImageNet(root='./imagenet', split='val')" + ], + "id": "YVAoUjK47Zcp" + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "id": "fcbb3eecae5346a9" + }, + "source": [ + "Here we create the representative dataset. For detail on this step see [ImageNet tutorial](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/keras/ptq/example_keras_imagenet.ipynb). If you are running locally a higher fraction of the dataset can be used." + ], + "id": "fcbb3eecae5346a9" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eda9ad33-f88c-4178-8f19-bac6b2b2e97b" + }, + "outputs": [], + "source": [ + "REPRESENTATIVE_DATASET_FOLDER = './imagenet/val'\n", + "BATCH_SIZE = 20\n", + "fraction =0.001\n", + "model_version = 'MobileNet'\n", + "\n", + "preprocessor = tutorial_tools.DatasetPreprocessor(model_version=model_version)\n", + "representative_dataset_gen = preprocessor.get_representative_dataset(fraction, REPRESENTATIVE_DATASET_FOLDER, BATCH_SIZE)" + ], + "id": "eda9ad33-f88c-4178-8f19-bac6b2b2e97b" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5" + }, + "source": [ + "## MCT Quantization" + ], + "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "55edbb99-ab2f-4dde-aa74-4ddee61b2615" + }, + "source": [ + "This step we load the model and quantize with a few z-score thresholds.\n" + ], + "id": "55edbb99-ab2f-4dde-aa74-4ddee61b2615" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VMrcPUN6jPlB" + }, + "source": [ + "First we load MobileNet from the keras library." + ], + "id": "VMrcPUN6jPlB" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "c431848f-a5f4-4737-a5c8-f046a8bca840" + }, + "outputs": [], + "source": [ + "from tensorflow.keras.applications import MobileNet\n", + "float_model = MobileNet(weights='imagenet')" + ], + "id": "c431848f-a5f4-4737-a5c8-f046a8bca840" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Pd8blHyKjWay" + }, + "source": [ + "Quantization perameters are defined. Here we will use default values apart from quantisation method." + ], + "id": "Pd8blHyKjWay" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ca971297-e00b-44b5-b9e1-e57ba5843e38" + }, + "outputs": [], + "source": [ + "from model_compression_toolkit.core import QuantizationErrorMethod\n", + "\n", + "# Specify the IMX500-v1 target platform capability (TPC)\n", + "tpc = mct.get_target_platform_capabilities(\"tensorflow\", 'imx500', target_platform_version='v1')\n", + "\n", + "# List of error methods to iterate over\n", + "q_configs_dict = {}" + ], + "id": "ca971297-e00b-44b5-b9e1-e57ba5843e38" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Vot-MCiWjzCE" + }, + "source": [ + "You can edit the code below to quantize with other values of z-score." + ], + "id": "Vot-MCiWjzCE" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jtiZzXmTjxuI" + }, + "outputs": [], + "source": [ + "# Z-score values to iterate over\n", + "z_score_values = [3,5,9]\n", + "\n", + "# Iterate and build the QuantizationConfig objects\n", + "for z_score in z_score_values:\n", + " q_config = mct.core.QuantizationConfig(\n", + " z_threshold=z_score,\n", + " )\n", + " q_configs_dict[z_score] = q_config\n", + "\n" + ], + "id": "jtiZzXmTjxuI" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8W3Dcn0jkJOH" + }, + "source": [ + "Finally we quantize the model, this can take some time. Grab a coffee!" + ], + "id": "8W3Dcn0jkJOH" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" + }, + "outputs": [], + "source": [ + "quantized_models_dict = {}\n", + "\n", + "for z_score, q_config in q_configs_dict.items():\n", + " # Create a CoreConfig object with the current quantization configuration\n", + " ptq_config = mct.core.CoreConfig(quantization_config=q_config)\n", + "\n", + " # Perform MCT post-training quantization\n", + " quantized_model, quantization_info = mct.ptq.keras_post_training_quantization(\n", + " in_model=float_model,\n", + " representative_data_gen=representative_dataset_gen,\n", + " core_config=ptq_config,\n", + " target_platform_capabilities=tpc\n", + " )\n", + "\n", + " # Update the dictionary to include the quantized model\n", + " quantized_models_dict[z_score] = {\n", + " \"quantization_config\": q_config,\n", + " \"quantized_model\": quantized_model,\n", + " \"quantization_info\": quantization_info\n", + " }\n" + ], + "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A8UHRsh2khM4" + }, + "source": [ + "### Z-Score Threshold and Distribution Visulisation" + ], + "id": "A8UHRsh2khM4" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Y-0QLWFJkpFV" + }, + "source": [ + "To assist with understanding we will now plot the activation distribution of Mobilenet's first activation layer.\n", + "\n", + "This will be obtained by feeding the representative dataset through the model.\n", + "To see the distribution of the activations the model needs to be rebuilt upto and including the layer chosen for distribution visulisation.\n", + "\n", + "To see said layers z-score threshold values. we will need to calculate these manually using the equestion stated in the introduction.\n", + "\n", + "To plot the distribution we first need to list the layer names. With keras this can be done easily using the following. We established the index of the layer of interest using various checks that can be seen in the appendix section." + ], + "id": "Y-0QLWFJkpFV" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" + }, + "outputs": [], + "source": [ + "#print layer name\n", + "for index, layer in enumerate(float_model.layers):\n", + " if index == 51:\n", + " print(layer.name)\n" + ], + "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277" + }, + "source": [ + "The example activation layer in model is 'conv_dw_8_relu'.\n", + "\n", + "Use this layer name to create a model ending at conv_dw_8_relu" + ], + "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" + }, + "outputs": [], + "source": [ + "from tensorflow.keras.models import Model\n", + "layer_name1 = 'conv_dw_8_relu'\n", + "\n", + "layer_output1 = float_model.get_layer(layer_name1).output\n", + "activation_model_relu = Model(inputs=float_model.input, outputs=layer_output1)" + ], + "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364" + }, + "source": [ + "Feed the representative dataset through these models and store the output." + ], + "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "eaeb9888-5d67-4979-af50-80781a811b4b" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "activation_batches_relu = []\n", + "activation_batches_project = []\n", + "for images in representative_dataset_gen():\n", + " activations_relu = activation_model_relu.predict(images)\n", + " activation_batches_relu.append(activations_relu)\n", + "\n", + "all_activations_relu = np.concatenate(activation_batches_relu, axis=0).flatten()" + ], + "id": "eaeb9888-5d67-4979-af50-80781a811b4b" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I5W9yY5DvOFr" + }, + "source": [ + "We can calculate the z-score for a layer using the equations stated in the introduction." + ], + "id": "I5W9yY5DvOFr" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WDx-LQSyxpDK" + }, + "outputs": [], + "source": [ + "optimal_thresholds_relu = {}\n", + "\n", + "# Calculate the mean and standard deviation of the activation data\n", + "mean = np.mean(all_activations_relu)\n", + "std_dev = np.std(all_activations_relu)\n", + "\n", + "# Calculate and store the threshold for each Z-score\n", + "for zscore in z_score_values:\n", + " optimal_threshold = zscore * std_dev + mean\n", + " optimal_thresholds_relu[f'zscore {zscore}'] = optimal_threshold" + ], + "id": "WDx-LQSyxpDK" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XRAr8L5mvuLd" + }, + "source": [ + "### Distribution Plots\n", + "\n", + "Here we plot the distribution from the resulting model along with its z score thresholds." + ], + "id": "XRAr8L5mvuLd" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VPb8tBNGpJjo" + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(all_activations_relu, bins=100, alpha=0.5, label='Activations')\n", + "for z_score, threshold in optimal_thresholds_relu.items():\n", + " random_color=np.random.rand(3,)\n", + " plt.axvline(threshold, linestyle='--', linewidth=2, color=random_color, label=f'{z_score}, zscore threshold: {threshold:.2f}')\n", + " z_score_1 = int(z_score.split(' ')[1]) # Splits the string and converts the second element to an integer\n", + " error_value = mse_error_thresholds[z_score_1] # Now using the correct integer key to access the value\n", + " plt.axvline(error_value, linestyle='-', linewidth=2, color=random_color, label=f'{z_score}, MSE error Threshold: {error_value:.2f}')\n", + "\n", + "plt.title('Activation Distribution with Optimal Quantization Thresholds - First ReLU Layer')\n", + "plt.xlabel('Activation Value')\n", + "plt.ylabel('Frequency')\n", + "plt.legend()\n", + "plt.show()" + ], + "id": "VPb8tBNGpJjo" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qbA6kFmw0vaf" + }, + "source": [ + "Here it can plainly be seen the effect of zscore on error threshold. The lowest z-score of 3 reduces the error threshold for that layer." + ], + "id": "qbA6kFmw0vaf" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4c967d41-439d-405b-815f-be641f1768fe" + }, + "source": [ + "## Accuracy\n", + "\n", + "Finally we can show the effect of these different z-score thresholds on the models accuracy." + ], + "id": "4c967d41-439d-405b-815f-be641f1768fe" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "092d9fd0-8005-4551-b853-3b52840639c2" + }, + "outputs": [], + "source": [ + "REPRESENTATIVE_DATASET_FOLDER = './imagenet/val'\n", + "BATCH_SIZE = 20\n", + "fraction =0.005\n", + "model_version = 'MobileNet'\n", + "\n", + "preprocessor = tutorial_tools.DatasetPreprocessor(model_version=model_version)\n", + "evaluation_dataset = preprocessor.get_validation_dataset_fraction(fraction, REPRESENTATIVE_DATASET_FOLDER, BATCH_SIZE)" + ], + "id": "092d9fd0-8005-4551-b853-3b52840639c2" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8ebf7d04-7816-465c-9157-6068c0a4a08a" + }, + "outputs": [], + "source": [ + "#prepare float model and evaluate\n", + "float_model.compile(loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[\"accuracy\"])\n", + "results = float_model.evaluate(evaluation_dataset)" + ], + "id": "8ebf7d04-7816-465c-9157-6068c0a4a08a" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" + }, + "outputs": [], + "source": [ + "#prepare quantised models and evaluate\n", + "evaluation_results = {}\n", + "\n", + "for z_score, data in quantized_models_dict.items():\n", + " quantized_model = data[\"quantized_model\"]\n", + "\n", + " quantized_model.compile(loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[\"accuracy\"])\n", + "\n", + " results = quantized_model.evaluate(evaluation_dataset, verbose=0) # Set verbose=0 to suppress the log messages\n", + "\n", + " evaluation_results[z_score] = results\n", + "\n", + " # Print the results\n", + " print(f\"Results for {z_score}: Loss = {results[0]}, Accuracy = {results[1]}\")" + ], + "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GpEZ2E1qzWl3" + }, + "source": [ + "Here we can see very minor gains from adjusting the zscore threshold. For the majority of simple models this trend will likely follow. From testing we have found that transformer models have a tendancy to benefit from anomoly removal but it is always worth playing with these perameters if your quantised accuracy is distinctly lower than your float model accuracy.\n", + "\n" + ], + "id": "GpEZ2E1qzWl3" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "14877777" + }, + "source": [ + "## Conclusion" + ], + "id": "14877777" + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bb7e1572" + }, + "source": [ + "In this tutorial, we demonstrated the z-score thresholding step used during quantisation. Please use this code to assist with choosing zscore thresholds for your own model.\n", + "\n", + "We have found a when adjusting z-score the sweet spot tends to be between 8 and 12. with no change above 12 and distribution distruction below 8. This will likely require a study on your part for your specific usecase.\n", + "\n", + "\n" + ], + "id": "bb7e1572" + }, + { + "cell_type": "markdown", + "source": [ + "## Appendix\n", + "\n", + "Below are a sellection of code samples used to establish the best layers to use for plotting thresholds and distributions.\n", + "\n", + "Firstly of the list of layers that are effected by this z-score adjustment" + ], + "metadata": { + "id": "BVHmePYJe7he" + }, + "id": "BVHmePYJe7he" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cn-Ac9br9Ltz" + }, + "outputs": [], + "source": [ + "# Initialize a dictionary to hold threshold values for comparison\n", + "thresholds_by_index = {}\n", + "\n", + "# Try to access each layer for each quantized model and collect threshold values\n", + "for z_score, data in quantized_models_dict.items():\n", + " quantized_model = data[\"quantized_model\"]\n", + " for layer_index in range(len(quantized_model.layers)):\n", + " try:\n", + " # Attempt to access the threshold value for this layer\n", + " threshold = quantized_model.layers[layer_index].activation_holder_quantizer.get_config()['threshold'][0]\n", + " # Store the threshold value for comparison\n", + " if layer_index not in thresholds_by_index:\n", + " thresholds_by_index[layer_index] = set()\n", + " thresholds_by_index[layer_index].add(threshold)\n", + " except Exception as e:\n", + " # Handle potential errors, e.g., index out of range, or property not found\n", + " # You can decide to log this error or simply pass\n", + " pass\n", + "\n", + "# Find indices where threshold values are not consistent\n", + "inconsistent_indices = [index for index, thresholds in thresholds_by_index.items() if len(thresholds) > 1]\n", + "\n", + "print(\"Inconsistent indices:\", inconsistent_indices)\n" + ], + "id": "cn-Ac9br9Ltz" + }, + { + "cell_type": "markdown", + "source": [ + "Choosing randomly from these we check the thresholds" + ], + "metadata": { + "id": "PiNdvojz_FDN" + }, + "id": "PiNdvojz_FDN" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Huv0u6z106lX" + }, + "outputs": [], + "source": [ + "mse_error_thresholds = {\n", + " z_score: data[\"quantized_model\"].layers[52].activation_holder_quantizer.get_config()['threshold'][0]\n", + " for z_score, data in quantized_models_dict.items()\n", + "}\n", + "print(mse_error_thresholds)" + ], + "id": "Huv0u6z106lX" + }, + { + "cell_type": "markdown", + "source": [ + "We now want to varify which layers matchup indicies based on layer names of the float model. For the example of 52 there is no matching layer as it is a quantisation of the previous layer. Checking 51 we can see that the indicies matches upto the layer name conv_dw_8_relu, we can use this to plot the distribution." + ], + "metadata": { + "id": "0YPqhQOh_N2r" + }, + "id": "0YPqhQOh_N2r" + }, + { + "cell_type": "code", + "source": [ + "target_z_score = 9\n", + "\n", + "for index, layer in enumerate(float_model.layers):\n", + " search_string = str(layer.name)\n", + "\n", + " # Check if the target_z_score is in the quantized_models_dict\n", + " if target_z_score in quantized_models_dict:\n", + " data = quantized_models_dict[target_z_score]\n", + " # Iterate over each layer of the target quantized model\n", + " for quantized_index, quantized_layer in enumerate(data[\"quantized_model\"].layers):\n", + " found = search_string in str(quantized_layer.get_config())\n", + " # If found, print details including the indices of the matching layers\n", + " if found:\n", + " print(f\"Float Model Layer Index {index} & Quantized Model Layer Index {quantized_index}: Found match in layer name {search_string}\")\n", + " else:\n", + " print(f\"Z-Score {target_z_score} not found in quantized_models_dict.\")\n" + ], + "metadata": { + "id": "rWGx5-6uu5H-" + }, + "id": "rWGx5-6uu5H-", + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "data[\"quantized_model\"].layers[51].get_config()" + ], + "metadata": { + "id": "AW_vC22Qw32E" + }, + "id": "AW_vC22Qw32E", + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" + }, + "source": [ + "\n", + "\n", + "Copyright 2024 Sony Semiconductor Israel, Inc. All rights reserved.\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", + " http://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.\n" + ], + "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/tutorials/resources/utils/keras_tutorial_tools(1).py b/tutorials/resources/utils/keras_tutorial_tools(1).py new file mode 100644 index 000000000..3ef6292da --- /dev/null +++ b/tutorials/resources/utils/keras_tutorial_tools(1).py @@ -0,0 +1,164 @@ +# Copyright 2024 Sony Semiconductor Israel, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + + +import tensorflow as tf +import keras +import model_compression_toolkit as mct +import os +import torchvision +import subprocess +import tarfile +from typing import Callable, Generator + +class DatasetPreprocessor: + """ + A class for preprocessing datasets based on the model version specified. + This class allows for the selection of preprocessing functions tailored to + different model architectures, ensuring that input data is correctly + normalized and prepared for model training or inference. + + Attributes: + model_version (str): The version of the model for which the dataset is being preprocessed. + preprocess_input (Callable): The preprocessing function selected based on the model version. + """ + def __init__(self, model_version: str): + """ + Initializes the DatasetPreprocessor with a specific model version. + + Args: + model_version (str): The version of the model for which the dataset is being preprocessed. + """ + self.model_version = model_version + self.preprocess_input = self.get_preprocess_function() + + def get_preprocess_function(self) -> Callable: + """ + Selects and returns the appropriate preprocessing function based on the model version. + + Returns: + Callable: A function that can be used to preprocess input data for the specified model version. + + Raises: + ValueError: If the model version is unsupported. + """ + if self.model_version == 'MobileNet': + return tf.keras.applications.mobilenet.preprocess_input + elif self.model_version == 'MobileNetV2': + return tf.keras.applications.mobilenet_v2.preprocess_input + elif self.model_version == 'MobileNetV3Small': + return tf.keras.applications.mobilenet_v3.preprocess_input + elif self.model_version == 'EfficientNetB1': + return tf.keras.applications.efficientnet.preprocess_input + elif self.model_version == 'Xception': + return tf.keras.applications.xception.preprocess_input + elif self.model_version == 'DenseNet121': + return tf.keras.applications.densenet.preprocess_input + elif self.model_version == 'NASNetMobile': + return tf.keras.applications.nasnet.preprocess_input + else: + raise ValueError(f"Unsupported model version: {self.model_version}") + def preprocess_dataset(self, images, labels): + return self.preprocess_input(images), labels + + + + def get_validation_dataset_fraction(self, fraction, test_dataset_folder, batch) -> tf.data.Dataset: + """ + Load a fraction of the validation dataset for evaluation. + + Args: + fraction (float, optional): Fraction of the dataset to load. Defaults to 1.0 (i.e., the entire dataset). + test_dataset_folder (str): location of dataset + batch (int): batch size when loading dataset. + + Returns: + tf.data.Dataset: A fraction of the validation dataset. + """ + assert 0 < fraction <= 1, "Fraction must be between 0 and 1." + + # Load the dataset to determine the total number of samples + initial_dataset = tf.keras.utils.image_dataset_from_directory( + directory=test_dataset_folder, + batch_size=1, # Use batch size of 1 to count samples + image_size=[224, 224], + shuffle=False, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + total_samples = initial_dataset.cardinality().numpy() + samples_to_take = int(total_samples * fraction) + + # reload the dataset again with batch size + take number of samples + dataset = tf.keras.utils.image_dataset_from_directory( + directory=test_dataset_folder, + batch_size=batch, + image_size=[224, 224], + shuffle=False, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + # Preprocess the dataset + dataset = dataset.map(self.preprocess_dataset) + # Take the calculated number of samples (adjusted for batch size) + dataset = dataset.take(samples_to_take // batch + (1 if samples_to_take % batch else 0)) + + return dataset + + + def get_representative_dataset(self, fraction, representative_dataset_folder, batch) -> Generator: + """ + A function that loads a fraction of the dataset and returns a representative dataset generator. + + Args: + fraction (float): The fraction of the dataset to load. Defaults to 1.0 (the entire dataset). + test_dataset_folder (str): location of dataset + batch (int): batch size when loading dataset. + + Returns: + Generator: A generator yielding batches of preprocessed images. + """ + assert 0 < fraction <= 1, "Fraction must be between 0 and 1." + + print('Loading dataset, this may take a few minutes ...') + dataset = tf.keras.utils.image_dataset_from_directory( + directory=representative_dataset_folder, + batch_size=batch, + image_size=[224, 224], + shuffle=True, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + # Preprocess the data + dataset = dataset.map(self.preprocess_dataset) + + # Determine the total number of batches in the dataset + total_batches = dataset.cardinality().numpy() + if total_batches == tf.data.experimental.INFINITE_CARDINALITY: + raise ValueError("Dataset size is infinite. A finite dataset is required to compute a fraction.") + + # Calculate the number of batches to use, based on the specified fraction + batches_to_use = int(total_batches * fraction) + + def representative_dataset() -> Generator: + """A generator function that yields batches of preprocessed images.""" + for image_batch, _ in dataset.take(batches_to_use): + yield image_batch.numpy() + + print('images in representative dataset: '+ str(batch*batches_to_use)) + + return representative_dataset + + From 423fb3d5ab478897add958ca4ea42205d0a33ff2 Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Wed, 20 Mar 2024 23:16:01 +0000 Subject: [PATCH 2/6] zscore tutorial --- .../utils/keras_tutorial_tools(1).py | 164 ------------- .../resources/utils/keras_tutorial_tools.py | 224 +++++++++++------- 2 files changed, 136 insertions(+), 252 deletions(-) delete mode 100644 tutorials/resources/utils/keras_tutorial_tools(1).py diff --git a/tutorials/resources/utils/keras_tutorial_tools(1).py b/tutorials/resources/utils/keras_tutorial_tools(1).py deleted file mode 100644 index 3ef6292da..000000000 --- a/tutorials/resources/utils/keras_tutorial_tools(1).py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright 2024 Sony Semiconductor Israel, Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== - - -import tensorflow as tf -import keras -import model_compression_toolkit as mct -import os -import torchvision -import subprocess -import tarfile -from typing import Callable, Generator - -class DatasetPreprocessor: - """ - A class for preprocessing datasets based on the model version specified. - This class allows for the selection of preprocessing functions tailored to - different model architectures, ensuring that input data is correctly - normalized and prepared for model training or inference. - - Attributes: - model_version (str): The version of the model for which the dataset is being preprocessed. - preprocess_input (Callable): The preprocessing function selected based on the model version. - """ - def __init__(self, model_version: str): - """ - Initializes the DatasetPreprocessor with a specific model version. - - Args: - model_version (str): The version of the model for which the dataset is being preprocessed. - """ - self.model_version = model_version - self.preprocess_input = self.get_preprocess_function() - - def get_preprocess_function(self) -> Callable: - """ - Selects and returns the appropriate preprocessing function based on the model version. - - Returns: - Callable: A function that can be used to preprocess input data for the specified model version. - - Raises: - ValueError: If the model version is unsupported. - """ - if self.model_version == 'MobileNet': - return tf.keras.applications.mobilenet.preprocess_input - elif self.model_version == 'MobileNetV2': - return tf.keras.applications.mobilenet_v2.preprocess_input - elif self.model_version == 'MobileNetV3Small': - return tf.keras.applications.mobilenet_v3.preprocess_input - elif self.model_version == 'EfficientNetB1': - return tf.keras.applications.efficientnet.preprocess_input - elif self.model_version == 'Xception': - return tf.keras.applications.xception.preprocess_input - elif self.model_version == 'DenseNet121': - return tf.keras.applications.densenet.preprocess_input - elif self.model_version == 'NASNetMobile': - return tf.keras.applications.nasnet.preprocess_input - else: - raise ValueError(f"Unsupported model version: {self.model_version}") - def preprocess_dataset(self, images, labels): - return self.preprocess_input(images), labels - - - - def get_validation_dataset_fraction(self, fraction, test_dataset_folder, batch) -> tf.data.Dataset: - """ - Load a fraction of the validation dataset for evaluation. - - Args: - fraction (float, optional): Fraction of the dataset to load. Defaults to 1.0 (i.e., the entire dataset). - test_dataset_folder (str): location of dataset - batch (int): batch size when loading dataset. - - Returns: - tf.data.Dataset: A fraction of the validation dataset. - """ - assert 0 < fraction <= 1, "Fraction must be between 0 and 1." - - # Load the dataset to determine the total number of samples - initial_dataset = tf.keras.utils.image_dataset_from_directory( - directory=test_dataset_folder, - batch_size=1, # Use batch size of 1 to count samples - image_size=[224, 224], - shuffle=False, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - total_samples = initial_dataset.cardinality().numpy() - samples_to_take = int(total_samples * fraction) - - # reload the dataset again with batch size + take number of samples - dataset = tf.keras.utils.image_dataset_from_directory( - directory=test_dataset_folder, - batch_size=batch, - image_size=[224, 224], - shuffle=False, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - # Preprocess the dataset - dataset = dataset.map(self.preprocess_dataset) - # Take the calculated number of samples (adjusted for batch size) - dataset = dataset.take(samples_to_take // batch + (1 if samples_to_take % batch else 0)) - - return dataset - - - def get_representative_dataset(self, fraction, representative_dataset_folder, batch) -> Generator: - """ - A function that loads a fraction of the dataset and returns a representative dataset generator. - - Args: - fraction (float): The fraction of the dataset to load. Defaults to 1.0 (the entire dataset). - test_dataset_folder (str): location of dataset - batch (int): batch size when loading dataset. - - Returns: - Generator: A generator yielding batches of preprocessed images. - """ - assert 0 < fraction <= 1, "Fraction must be between 0 and 1." - - print('Loading dataset, this may take a few minutes ...') - dataset = tf.keras.utils.image_dataset_from_directory( - directory=representative_dataset_folder, - batch_size=batch, - image_size=[224, 224], - shuffle=True, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - # Preprocess the data - dataset = dataset.map(self.preprocess_dataset) - - # Determine the total number of batches in the dataset - total_batches = dataset.cardinality().numpy() - if total_batches == tf.data.experimental.INFINITE_CARDINALITY: - raise ValueError("Dataset size is infinite. A finite dataset is required to compute a fraction.") - - # Calculate the number of batches to use, based on the specified fraction - batches_to_use = int(total_batches * fraction) - - def representative_dataset() -> Generator: - """A generator function that yields batches of preprocessed images.""" - for image_batch, _ in dataset.take(batches_to_use): - yield image_batch.numpy() - - print('images in representative dataset: '+ str(batch*batches_to_use)) - - return representative_dataset - - diff --git a/tutorials/resources/utils/keras_tutorial_tools.py b/tutorials/resources/utils/keras_tutorial_tools.py index 824facb2c..3ef6292da 100644 --- a/tutorials/resources/utils/keras_tutorial_tools.py +++ b/tutorials/resources/utils/keras_tutorial_tools.py @@ -21,96 +21,144 @@ import torchvision import subprocess import tarfile -from typing import Generator +from typing import Callable, Generator -def imagenet_preprocess_input(images, labels): - return tf.keras.applications.mobilenet_v2.preprocess_input(images), labels - -def get_validation_dataset_fraction(fraction, test_dataset_folder, batch) -> tf.data.Dataset: - """ - Load a fraction of the validation dataset for evaluation. - - Args: - fraction (float, optional): Fraction of the dataset to load. Defaults to 1.0 (i.e., the entire dataset). - test_dataset_folder (str): location of dataset - batch (int): batch size when loading dataset. - - Returns: - tf.data.Dataset: A fraction of the validation dataset. - """ - assert 0 < fraction <= 1, "Fraction must be between 0 and 1." - - # Load the dataset to determine the total number of samples - initial_dataset = tf.keras.utils.image_dataset_from_directory( - directory=test_dataset_folder, - batch_size=1, # Use batch size of 1 to count samples - image_size=[224, 224], - shuffle=False, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - total_samples = initial_dataset.cardinality().numpy() - samples_to_take = int(total_samples * fraction) - - # reload the dataset again with batch size + take number of samples - dataset = tf.keras.utils.image_dataset_from_directory( - directory=test_dataset_folder, - batch_size=batch, - image_size=[224, 224], - shuffle=False, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - # Preprocess the dataset - dataset = dataset.map(lambda x, y: (imagenet_preprocess_input(x, y))) - - # Take the calculated number of samples (adjusted for batch size) - dataset = dataset.take(samples_to_take // batch + (1 if samples_to_take % batch else 0)) - - return dataset - - -def get_representative_dataset(fraction, representative_dataset_folder, batch) -> Generator: +class DatasetPreprocessor: """ - A function that loads a fraction of the dataset and returns a representative dataset generator. - - Args: - fraction (float): The fraction of the dataset to load. Defaults to 1.0 (the entire dataset). - test_dataset_folder (str): location of dataset - batch (int): batch size when loading dataset. - - Returns: - Generator: A generator yielding batches of preprocessed images. + A class for preprocessing datasets based on the model version specified. + This class allows for the selection of preprocessing functions tailored to + different model architectures, ensuring that input data is correctly + normalized and prepared for model training or inference. + + Attributes: + model_version (str): The version of the model for which the dataset is being preprocessed. + preprocess_input (Callable): The preprocessing function selected based on the model version. """ - assert 0 < fraction <= 1, "Fraction must be between 0 and 1." - - print('Loading dataset, this may take a few minutes ...') - dataset = tf.keras.utils.image_dataset_from_directory( - directory=representative_dataset_folder, - batch_size=batch, - image_size=[224, 224], - shuffle=True, - crop_to_aspect_ratio=True, - interpolation='bilinear') - - # Preprocess the data - dataset = dataset.map(lambda x, y: (imagenet_preprocess_input(x, y))) - - # Determine the total number of batches in the dataset - total_batches = dataset.cardinality().numpy() - if total_batches == tf.data.experimental.INFINITE_CARDINALITY: - raise ValueError("Dataset size is infinite. A finite dataset is required to compute a fraction.") - - # Calculate the number of batches to use, based on the specified fraction - batches_to_use = int(total_batches * fraction) - - def representative_dataset() -> Generator: - """A generator function that yields batches of preprocessed images.""" - for image_batch, _ in dataset.take(batches_to_use): - yield image_batch.numpy() - - print('images in representative dataset: '+ str(batch*batches_to_use)) - - return representative_dataset + def __init__(self, model_version: str): + """ + Initializes the DatasetPreprocessor with a specific model version. + + Args: + model_version (str): The version of the model for which the dataset is being preprocessed. + """ + self.model_version = model_version + self.preprocess_input = self.get_preprocess_function() + + def get_preprocess_function(self) -> Callable: + """ + Selects and returns the appropriate preprocessing function based on the model version. + + Returns: + Callable: A function that can be used to preprocess input data for the specified model version. + + Raises: + ValueError: If the model version is unsupported. + """ + if self.model_version == 'MobileNet': + return tf.keras.applications.mobilenet.preprocess_input + elif self.model_version == 'MobileNetV2': + return tf.keras.applications.mobilenet_v2.preprocess_input + elif self.model_version == 'MobileNetV3Small': + return tf.keras.applications.mobilenet_v3.preprocess_input + elif self.model_version == 'EfficientNetB1': + return tf.keras.applications.efficientnet.preprocess_input + elif self.model_version == 'Xception': + return tf.keras.applications.xception.preprocess_input + elif self.model_version == 'DenseNet121': + return tf.keras.applications.densenet.preprocess_input + elif self.model_version == 'NASNetMobile': + return tf.keras.applications.nasnet.preprocess_input + else: + raise ValueError(f"Unsupported model version: {self.model_version}") + def preprocess_dataset(self, images, labels): + return self.preprocess_input(images), labels + + + + def get_validation_dataset_fraction(self, fraction, test_dataset_folder, batch) -> tf.data.Dataset: + """ + Load a fraction of the validation dataset for evaluation. + + Args: + fraction (float, optional): Fraction of the dataset to load. Defaults to 1.0 (i.e., the entire dataset). + test_dataset_folder (str): location of dataset + batch (int): batch size when loading dataset. + + Returns: + tf.data.Dataset: A fraction of the validation dataset. + """ + assert 0 < fraction <= 1, "Fraction must be between 0 and 1." + + # Load the dataset to determine the total number of samples + initial_dataset = tf.keras.utils.image_dataset_from_directory( + directory=test_dataset_folder, + batch_size=1, # Use batch size of 1 to count samples + image_size=[224, 224], + shuffle=False, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + total_samples = initial_dataset.cardinality().numpy() + samples_to_take = int(total_samples * fraction) + + # reload the dataset again with batch size + take number of samples + dataset = tf.keras.utils.image_dataset_from_directory( + directory=test_dataset_folder, + batch_size=batch, + image_size=[224, 224], + shuffle=False, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + # Preprocess the dataset + dataset = dataset.map(self.preprocess_dataset) + # Take the calculated number of samples (adjusted for batch size) + dataset = dataset.take(samples_to_take // batch + (1 if samples_to_take % batch else 0)) + + return dataset + + + def get_representative_dataset(self, fraction, representative_dataset_folder, batch) -> Generator: + """ + A function that loads a fraction of the dataset and returns a representative dataset generator. + + Args: + fraction (float): The fraction of the dataset to load. Defaults to 1.0 (the entire dataset). + test_dataset_folder (str): location of dataset + batch (int): batch size when loading dataset. + + Returns: + Generator: A generator yielding batches of preprocessed images. + """ + assert 0 < fraction <= 1, "Fraction must be between 0 and 1." + + print('Loading dataset, this may take a few minutes ...') + dataset = tf.keras.utils.image_dataset_from_directory( + directory=representative_dataset_folder, + batch_size=batch, + image_size=[224, 224], + shuffle=True, + crop_to_aspect_ratio=True, + interpolation='bilinear') + + # Preprocess the data + dataset = dataset.map(self.preprocess_dataset) + + # Determine the total number of batches in the dataset + total_batches = dataset.cardinality().numpy() + if total_batches == tf.data.experimental.INFINITE_CARDINALITY: + raise ValueError("Dataset size is infinite. A finite dataset is required to compute a fraction.") + + # Calculate the number of batches to use, based on the specified fraction + batches_to_use = int(total_batches * fraction) + + def representative_dataset() -> Generator: + """A generator function that yields batches of preprocessed images.""" + for image_batch, _ in dataset.take(batches_to_use): + yield image_batch.numpy() + + print('images in representative dataset: '+ str(batch*batches_to_use)) + + return representative_dataset From 913dec99b69f4e183a92666332662c78c62113c7 Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Wed, 20 Mar 2024 23:21:39 +0000 Subject: [PATCH 3/6] colab link fix --- ...e_keras_activation_z_score_threshold.ipynb | 256 +++++++++--------- 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb index 2251add8f..15dd06e81 100644 --- a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20", "metadata": { "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20" }, @@ -9,31 +10,31 @@ "# Activation Z-Score Threshold Demonstration For Post-Training Quantization\n", "\n", "\n" - ], - "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20" + ] }, { "cell_type": "markdown", + "id": "9be59ea8-e208-4b64-aede-1dd6270b3540", "metadata": { "id": "9be59ea8-e208-4b64-aede-1dd6270b3540" }, "source": [ - "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/keras/ptq/example_keras_activation_threshold_search.ipynb)" - ], - "id": "9be59ea8-e208-4b64-aede-1dd6270b3540" + "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb)" + ] }, { "cell_type": "markdown", + "id": "930e6d6d-4980-4d66-beed-9ff5a494acf9", "metadata": { "id": "930e6d6d-4980-4d66-beed-9ff5a494acf9" }, "source": [ "## Overview" - ], - "id": "930e6d6d-4980-4d66-beed-9ff5a494acf9" + ] }, { "cell_type": "markdown", + "id": "699be4fd-d382-4eec-9d3f-e2e85cfb1762", "metadata": { "id": "699be4fd-d382-4eec-9d3f-e2e85cfb1762" }, @@ -41,21 +42,21 @@ "This tutorial demonstrates the process used to find the activation z-score threshold, a step that MCT can use during post-training quantization.\n", "\n", "In this example we will explore how setting different z scores effects threshold and accuracy. We will start by demonstrating how to apply the corresponding MCT configurations, then, we will feed a representative dataset through the model, plot the activation distribution of an activation layer with their respective MCT calculated z-score thresholds, and finally compare the quantized model accuracy of the examples of different z-score.\n" - ], - "id": "699be4fd-d382-4eec-9d3f-e2e85cfb1762" + ] }, { "cell_type": "markdown", + "id": "85199e25-c587-41b1-aaf5-e1d23ce97ca1", "metadata": { "id": "85199e25-c587-41b1-aaf5-e1d23ce97ca1" }, "source": [ "## Activation threshold explanation" - ], - "id": "85199e25-c587-41b1-aaf5-e1d23ce97ca1" + ] }, { "cell_type": "markdown", + "id": "a89a17f4-30c9-4caf-a888-424f7a82fbc8", "metadata": { "id": "a89a17f4-30c9-4caf-a888-424f7a82fbc8" }, @@ -86,32 +87,32 @@ "\n", "\n", "This equation for $t(Z_t)$ allows you to set a threshold based on the statistical distribution of activations, identifying values that are unusually high or low relative to the rest of the data. These identified values can then be removed before applying the main quantization algorithm." - ], - "id": "a89a17f4-30c9-4caf-a888-424f7a82fbc8" + ] }, { "cell_type": "markdown", + "id": "04228b7c-00f1-4ded-bead-722e2a4e89a0", "metadata": { "id": "04228b7c-00f1-4ded-bead-722e2a4e89a0" }, "source": [ "## Setup" - ], - "id": "04228b7c-00f1-4ded-bead-722e2a4e89a0" + ] }, { "cell_type": "markdown", + "id": "2657cf1a-654d-45a6-b877-8bf42fc26d0d", "metadata": { "id": "2657cf1a-654d-45a6-b877-8bf42fc26d0d" }, "source": [ "Install and import the relevant packages:\n" - ], - "id": "2657cf1a-654d-45a6-b877-8bf42fc26d0d" + ] }, { "cell_type": "code", "execution_count": null, + "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "metadata": { "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324" }, @@ -119,12 +120,12 @@ "source": [ "!pip install -q tensorflow\n", "!pip install -q mct-nightly" - ], - "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324" + ] }, { "cell_type": "code", "execution_count": null, + "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19", "metadata": { "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" }, @@ -134,22 +135,22 @@ "import keras\n", "import model_compression_toolkit as mct\n", "import os" - ], - "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" + ] }, { "cell_type": "markdown", + "id": "z8F-avk3azgZ", "metadata": { "id": "z8F-avk3azgZ" }, "source": [ "Clone MCT to gain access to tutorial scripts" - ], - "id": "z8F-avk3azgZ" + ] }, { "cell_type": "code", "execution_count": null, + "id": "e3b675cf-e1b5-4249-a581-ffb9b1c16ba1", "metadata": { "id": "e3b675cf-e1b5-4249-a581-ffb9b1c16ba1" }, @@ -160,33 +161,33 @@ "import sys\n", "sys.path.insert(0,\"./local_mct\")\n", "import tutorials.resources.utils.keras_tutorial_tools as tutorial_tools\n" - ], - "id": "e3b675cf-e1b5-4249-a581-ffb9b1c16ba1" + ] }, { "cell_type": "markdown", + "id": "0c7fed0d-cfc8-41ee-adf1-22a98110397b", "metadata": { "id": "0c7fed0d-cfc8-41ee-adf1-22a98110397b" }, "source": [ "## Dataset" - ], - "id": "0c7fed0d-cfc8-41ee-adf1-22a98110397b" + ] }, { "cell_type": "markdown", + "id": "aecde59e4c37b1da", "metadata": { "collapsed": false, "id": "aecde59e4c37b1da" }, "source": [ "Load ImageNet classification dataset and seperate a small representative subsection of this dataset to use for quantization." - ], - "id": "aecde59e4c37b1da" + ] }, { "cell_type": "code", "execution_count": null, + "id": "_ztv72uM6-UT", "metadata": { "id": "_ztv72uM6-UT" }, @@ -198,12 +199,12 @@ " !mv ILSVRC2012_devkit_t12.tar.gz imagenet/\n", " !wget https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.tar\n", " !mv ILSVRC2012_img_val.tar imagenet/" - ], - "id": "_ztv72uM6-UT" + ] }, { "cell_type": "code", "execution_count": null, + "id": "YVAoUjK47Zcp", "metadata": { "id": "YVAoUjK47Zcp" }, @@ -212,23 +213,23 @@ "import torchvision\n", "if not os.path.isdir('imagenet/val'):\n", " ds = torchvision.datasets.ImageNet(root='./imagenet', split='val')" - ], - "id": "YVAoUjK47Zcp" + ] }, { "cell_type": "markdown", + "id": "fcbb3eecae5346a9", "metadata": { "collapsed": false, "id": "fcbb3eecae5346a9" }, "source": [ "Here we create the representative dataset. For detail on this step see [ImageNet tutorial](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/keras/ptq/example_keras_imagenet.ipynb). If you are running locally a higher fraction of the dataset can be used." - ], - "id": "fcbb3eecae5346a9" + ] }, { "cell_type": "code", "execution_count": null, + "id": "eda9ad33-f88c-4178-8f19-bac6b2b2e97b", "metadata": { "id": "eda9ad33-f88c-4178-8f19-bac6b2b2e97b" }, @@ -241,42 +242,42 @@ "\n", "preprocessor = tutorial_tools.DatasetPreprocessor(model_version=model_version)\n", "representative_dataset_gen = preprocessor.get_representative_dataset(fraction, REPRESENTATIVE_DATASET_FOLDER, BATCH_SIZE)" - ], - "id": "eda9ad33-f88c-4178-8f19-bac6b2b2e97b" + ] }, { "cell_type": "markdown", + "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5", "metadata": { "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5" }, "source": [ "## MCT Quantization" - ], - "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5" + ] }, { "cell_type": "markdown", + "id": "55edbb99-ab2f-4dde-aa74-4ddee61b2615", "metadata": { "id": "55edbb99-ab2f-4dde-aa74-4ddee61b2615" }, "source": [ "This step we load the model and quantize with a few z-score thresholds.\n" - ], - "id": "55edbb99-ab2f-4dde-aa74-4ddee61b2615" + ] }, { "cell_type": "markdown", + "id": "VMrcPUN6jPlB", "metadata": { "id": "VMrcPUN6jPlB" }, "source": [ "First we load MobileNet from the keras library." - ], - "id": "VMrcPUN6jPlB" + ] }, { "cell_type": "code", "execution_count": null, + "id": "c431848f-a5f4-4737-a5c8-f046a8bca840", "metadata": { "id": "c431848f-a5f4-4737-a5c8-f046a8bca840" }, @@ -284,22 +285,22 @@ "source": [ "from tensorflow.keras.applications import MobileNet\n", "float_model = MobileNet(weights='imagenet')" - ], - "id": "c431848f-a5f4-4737-a5c8-f046a8bca840" + ] }, { "cell_type": "markdown", + "id": "Pd8blHyKjWay", "metadata": { "id": "Pd8blHyKjWay" }, "source": [ "Quantization perameters are defined. Here we will use default values apart from quantisation method." - ], - "id": "Pd8blHyKjWay" + ] }, { "cell_type": "code", "execution_count": null, + "id": "ca971297-e00b-44b5-b9e1-e57ba5843e38", "metadata": { "id": "ca971297-e00b-44b5-b9e1-e57ba5843e38" }, @@ -312,22 +313,22 @@ "\n", "# List of error methods to iterate over\n", "q_configs_dict = {}" - ], - "id": "ca971297-e00b-44b5-b9e1-e57ba5843e38" + ] }, { "cell_type": "markdown", + "id": "Vot-MCiWjzCE", "metadata": { "id": "Vot-MCiWjzCE" }, "source": [ "You can edit the code below to quantize with other values of z-score." - ], - "id": "Vot-MCiWjzCE" + ] }, { "cell_type": "code", "execution_count": null, + "id": "jtiZzXmTjxuI", "metadata": { "id": "jtiZzXmTjxuI" }, @@ -343,22 +344,22 @@ " )\n", " q_configs_dict[z_score] = q_config\n", "\n" - ], - "id": "jtiZzXmTjxuI" + ] }, { "cell_type": "markdown", + "id": "8W3Dcn0jkJOH", "metadata": { "id": "8W3Dcn0jkJOH" }, "source": [ "Finally we quantize the model, this can take some time. Grab a coffee!" - ], - "id": "8W3Dcn0jkJOH" + ] }, { "cell_type": "code", "execution_count": null, + "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", "metadata": { "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" }, @@ -384,21 +385,21 @@ " \"quantized_model\": quantized_model,\n", " \"quantization_info\": quantization_info\n", " }\n" - ], - "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" + ] }, { "cell_type": "markdown", + "id": "A8UHRsh2khM4", "metadata": { "id": "A8UHRsh2khM4" }, "source": [ "### Z-Score Threshold and Distribution Visulisation" - ], - "id": "A8UHRsh2khM4" + ] }, { "cell_type": "markdown", + "id": "Y-0QLWFJkpFV", "metadata": { "id": "Y-0QLWFJkpFV" }, @@ -411,12 +412,12 @@ "To see said layers z-score threshold values. we will need to calculate these manually using the equestion stated in the introduction.\n", "\n", "To plot the distribution we first need to list the layer names. With keras this can be done easily using the following. We established the index of the layer of interest using various checks that can be seen in the appendix section." - ], - "id": "Y-0QLWFJkpFV" + ] }, { "cell_type": "code", "execution_count": null, + "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", "metadata": { "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" }, @@ -426,11 +427,11 @@ "for index, layer in enumerate(float_model.layers):\n", " if index == 51:\n", " print(layer.name)\n" - ], - "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" + ] }, { "cell_type": "markdown", + "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277", "metadata": { "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277" }, @@ -438,12 +439,12 @@ "The example activation layer in model is 'conv_dw_8_relu'.\n", "\n", "Use this layer name to create a model ending at conv_dw_8_relu" - ], - "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277" + ] }, { "cell_type": "code", "execution_count": null, + "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d", "metadata": { "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" }, @@ -454,22 +455,22 @@ "\n", "layer_output1 = float_model.get_layer(layer_name1).output\n", "activation_model_relu = Model(inputs=float_model.input, outputs=layer_output1)" - ], - "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" + ] }, { "cell_type": "markdown", + "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364", "metadata": { "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364" }, "source": [ "Feed the representative dataset through these models and store the output." - ], - "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364" + ] }, { "cell_type": "code", "execution_count": null, + "id": "eaeb9888-5d67-4979-af50-80781a811b4b", "metadata": { "id": "eaeb9888-5d67-4979-af50-80781a811b4b" }, @@ -483,22 +484,22 @@ " activation_batches_relu.append(activations_relu)\n", "\n", "all_activations_relu = np.concatenate(activation_batches_relu, axis=0).flatten()" - ], - "id": "eaeb9888-5d67-4979-af50-80781a811b4b" + ] }, { "cell_type": "markdown", + "id": "I5W9yY5DvOFr", "metadata": { "id": "I5W9yY5DvOFr" }, "source": [ "We can calculate the z-score for a layer using the equations stated in the introduction." - ], - "id": "I5W9yY5DvOFr" + ] }, { "cell_type": "code", "execution_count": null, + "id": "WDx-LQSyxpDK", "metadata": { "id": "WDx-LQSyxpDK" }, @@ -514,11 +515,11 @@ "for zscore in z_score_values:\n", " optimal_threshold = zscore * std_dev + mean\n", " optimal_thresholds_relu[f'zscore {zscore}'] = optimal_threshold" - ], - "id": "WDx-LQSyxpDK" + ] }, { "cell_type": "markdown", + "id": "XRAr8L5mvuLd", "metadata": { "id": "XRAr8L5mvuLd" }, @@ -526,12 +527,12 @@ "### Distribution Plots\n", "\n", "Here we plot the distribution from the resulting model along with its z score thresholds." - ], - "id": "XRAr8L5mvuLd" + ] }, { "cell_type": "code", "execution_count": null, + "id": "VPb8tBNGpJjo", "metadata": { "id": "VPb8tBNGpJjo" }, @@ -555,21 +556,21 @@ "plt.ylabel('Frequency')\n", "plt.legend()\n", "plt.show()" - ], - "id": "VPb8tBNGpJjo" + ] }, { "cell_type": "markdown", + "id": "qbA6kFmw0vaf", "metadata": { "id": "qbA6kFmw0vaf" }, "source": [ "Here it can plainly be seen the effect of zscore on error threshold. The lowest z-score of 3 reduces the error threshold for that layer." - ], - "id": "qbA6kFmw0vaf" + ] }, { "cell_type": "markdown", + "id": "4c967d41-439d-405b-815f-be641f1768fe", "metadata": { "id": "4c967d41-439d-405b-815f-be641f1768fe" }, @@ -577,12 +578,12 @@ "## Accuracy\n", "\n", "Finally we can show the effect of these different z-score thresholds on the models accuracy." - ], - "id": "4c967d41-439d-405b-815f-be641f1768fe" + ] }, { "cell_type": "code", "execution_count": null, + "id": "092d9fd0-8005-4551-b853-3b52840639c2", "metadata": { "id": "092d9fd0-8005-4551-b853-3b52840639c2" }, @@ -595,12 +596,12 @@ "\n", "preprocessor = tutorial_tools.DatasetPreprocessor(model_version=model_version)\n", "evaluation_dataset = preprocessor.get_validation_dataset_fraction(fraction, REPRESENTATIVE_DATASET_FOLDER, BATCH_SIZE)" - ], - "id": "092d9fd0-8005-4551-b853-3b52840639c2" + ] }, { "cell_type": "code", "execution_count": null, + "id": "8ebf7d04-7816-465c-9157-6068c0a4a08a", "metadata": { "id": "8ebf7d04-7816-465c-9157-6068c0a4a08a" }, @@ -609,12 +610,12 @@ "#prepare float model and evaluate\n", "float_model.compile(loss=keras.losses.SparseCategoricalCrossentropy(), metrics=[\"accuracy\"])\n", "results = float_model.evaluate(evaluation_dataset)" - ], - "id": "8ebf7d04-7816-465c-9157-6068c0a4a08a" + ] }, { "cell_type": "code", "execution_count": null, + "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" }, @@ -634,32 +635,32 @@ "\n", " # Print the results\n", " print(f\"Results for {z_score}: Loss = {results[0]}, Accuracy = {results[1]}\")" - ], - "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" + ] }, { "cell_type": "markdown", + "id": "GpEZ2E1qzWl3", "metadata": { "id": "GpEZ2E1qzWl3" }, "source": [ "Here we can see very minor gains from adjusting the zscore threshold. For the majority of simple models this trend will likely follow. From testing we have found that transformer models have a tendancy to benefit from anomoly removal but it is always worth playing with these perameters if your quantised accuracy is distinctly lower than your float model accuracy.\n", "\n" - ], - "id": "GpEZ2E1qzWl3" + ] }, { "cell_type": "markdown", + "id": "14877777", "metadata": { "id": "14877777" }, "source": [ "## Conclusion" - ], - "id": "14877777" + ] }, { "cell_type": "markdown", + "id": "bb7e1572", "metadata": { "id": "bb7e1572" }, @@ -669,26 +670,26 @@ "We have found a when adjusting z-score the sweet spot tends to be between 8 and 12. with no change above 12 and distribution distruction below 8. This will likely require a study on your part for your specific usecase.\n", "\n", "\n" - ], - "id": "bb7e1572" + ] }, { "cell_type": "markdown", + "id": "BVHmePYJe7he", + "metadata": { + "id": "BVHmePYJe7he" + }, "source": [ "## Appendix\n", "\n", "Below are a sellection of code samples used to establish the best layers to use for plotting thresholds and distributions.\n", "\n", "Firstly of the list of layers that are effected by this z-score adjustment" - ], - "metadata": { - "id": "BVHmePYJe7he" - }, - "id": "BVHmePYJe7he" + ] }, { "cell_type": "code", "execution_count": null, + "id": "cn-Ac9br9Ltz", "metadata": { "id": "cn-Ac9br9Ltz" }, @@ -717,22 +718,22 @@ "inconsistent_indices = [index for index, thresholds in thresholds_by_index.items() if len(thresholds) > 1]\n", "\n", "print(\"Inconsistent indices:\", inconsistent_indices)\n" - ], - "id": "cn-Ac9br9Ltz" + ] }, { "cell_type": "markdown", - "source": [ - "Choosing randomly from these we check the thresholds" - ], + "id": "PiNdvojz_FDN", "metadata": { "id": "PiNdvojz_FDN" }, - "id": "PiNdvojz_FDN" + "source": [ + "Choosing randomly from these we check the thresholds" + ] }, { "cell_type": "code", "execution_count": null, + "id": "Huv0u6z106lX", "metadata": { "id": "Huv0u6z106lX" }, @@ -743,21 +744,26 @@ " for z_score, data in quantized_models_dict.items()\n", "}\n", "print(mse_error_thresholds)" - ], - "id": "Huv0u6z106lX" + ] }, { "cell_type": "markdown", - "source": [ - "We now want to varify which layers matchup indicies based on layer names of the float model. For the example of 52 there is no matching layer as it is a quantisation of the previous layer. Checking 51 we can see that the indicies matches upto the layer name conv_dw_8_relu, we can use this to plot the distribution." - ], + "id": "0YPqhQOh_N2r", "metadata": { "id": "0YPqhQOh_N2r" }, - "id": "0YPqhQOh_N2r" + "source": [ + "We now want to varify which layers matchup indicies based on layer names of the float model. For the example of 52 there is no matching layer as it is a quantisation of the previous layer. Checking 51 we can see that the indicies matches upto the layer name conv_dw_8_relu, we can use this to plot the distribution." + ] }, { "cell_type": "code", + "execution_count": null, + "id": "rWGx5-6uu5H-", + "metadata": { + "id": "rWGx5-6uu5H-" + }, + "outputs": [], "source": [ "target_z_score = 9\n", "\n", @@ -775,28 +781,23 @@ " print(f\"Float Model Layer Index {index} & Quantized Model Layer Index {quantized_index}: Found match in layer name {search_string}\")\n", " else:\n", " print(f\"Z-Score {target_z_score} not found in quantized_models_dict.\")\n" - ], - "metadata": { - "id": "rWGx5-6uu5H-" - }, - "id": "rWGx5-6uu5H-", - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "code", - "source": [ - "data[\"quantized_model\"].layers[51].get_config()" - ], + "execution_count": null, + "id": "AW_vC22Qw32E", "metadata": { "id": "AW_vC22Qw32E" }, - "id": "AW_vC22Qw32E", - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "data[\"quantized_model\"].layers[51].get_config()" + ] }, { "cell_type": "markdown", + "id": "01c1645e-205c-4d9a-8af3-e497b3addec1", "metadata": { "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" }, @@ -816,8 +817,7 @@ "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.\n" - ], - "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" + ] } ], "metadata": { @@ -844,4 +844,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} From 6fdfa7a2a0b6c5fa04e9b69f0053951a0d27865f Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Thu, 21 Mar 2024 15:56:33 +0000 Subject: [PATCH 4/6] updates from pr --- .../example_keras_activation_z_score_threshold.ipynb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb index 15dd06e81..2460fc717 100644 --- a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb @@ -63,7 +63,7 @@ "source": [ "During quantization process, thresholds are used to map a distribution of 32-bit float values to their quantized counterparts. Doing this with the least loss of data while maintaining the most representative range is important for final model accuracy.\n", "\n", - "Some models exhibit anomolus values when fed a representative dataset. It is in the interest of the models accuracy to remove these values so that the quantisation threshold results in a more reliable range mapping.\n", + "Some models exhibit anomolus values when fed a representative dataset. It is in the interest of the models accuracy to remove these values so that the quantization threshold results in a more reliable range mapping.\n", "\n", "MCT has the option to remove these using z-score thresholding. Allowing the user to remove data based on standard distributions.\n", "\n", @@ -74,8 +74,10 @@ "To calculate a threshold $t$ for quantization based on a Z-score threshold $Z_t$, you might define $t$ as a function of $Z_t$, $\\mu$, and $\\sigma$, such as:\n", "\n", "$$\n", - "t(Zt)=\\frac{μ+Zt⋅σ}{μ+Zt​⋅σ}\n", + "t(Z_t) = μ + Z_t \\cdot σ\n", "$$\n", + "\n", + "\n", "Where:\n", "\n", "- $t(Z_t)$: The quantization threshold calculated based on a Z-score threshold $Z_t$.\n", @@ -294,7 +296,7 @@ "id": "Pd8blHyKjWay" }, "source": [ - "Quantization perameters are defined. Here we will use default values apart from quantisation method." + "Quantization perameters are defined. Here we will use default values apart from quantization method." ] }, { @@ -665,7 +667,7 @@ "id": "bb7e1572" }, "source": [ - "In this tutorial, we demonstrated the z-score thresholding step used during quantisation. Please use this code to assist with choosing zscore thresholds for your own model.\n", + "In this tutorial, we demonstrated the z-score thresholding step used during quantization. Please use this code to assist with choosing zscore thresholds for your own model.\n", "\n", "We have found a when adjusting z-score the sweet spot tends to be between 8 and 12. with no change above 12 and distribution distruction below 8. This will likely require a study on your part for your specific usecase.\n", "\n", @@ -753,7 +755,7 @@ "id": "0YPqhQOh_N2r" }, "source": [ - "We now want to varify which layers matchup indicies based on layer names of the float model. For the example of 52 there is no matching layer as it is a quantisation of the previous layer. Checking 51 we can see that the indicies matches upto the layer name conv_dw_8_relu, we can use this to plot the distribution." + "We now want to varify which layers matchup indicies based on layer names of the float model. For the example of 52 there is no matching layer as it is a quantization of the previous layer. Checking 51 we can see that the indicies matches upto the layer name conv_dw_8_relu, we can use this to plot the distribution." ] }, { From d49c18c961294a330dd3d43bbcce981d8261a4f2 Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Thu, 21 Mar 2024 16:03:58 +0000 Subject: [PATCH 5/6] changes based on pr --- .../example_keras_activation_z_score_threshold.ipynb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb index 2460fc717..894e05263 100644 --- a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb @@ -516,7 +516,7 @@ "# Calculate and store the threshold for each Z-score\n", "for zscore in z_score_values:\n", " optimal_threshold = zscore * std_dev + mean\n", - " optimal_thresholds_relu[f'zscore {zscore}'] = optimal_threshold" + " optimal_thresholds_relu[f'z-score {zscore}'] = optimal_threshold" ] }, { @@ -548,7 +548,7 @@ "plt.hist(all_activations_relu, bins=100, alpha=0.5, label='Activations')\n", "for z_score, threshold in optimal_thresholds_relu.items():\n", " random_color=np.random.rand(3,)\n", - " plt.axvline(threshold, linestyle='--', linewidth=2, color=random_color, label=f'{z_score}, zscore threshold: {threshold:.2f}')\n", + " plt.axvline(threshold, linestyle='--', linewidth=2, color=random_color, label=f'{z_score}, z-score threshold: {threshold:.2f}')\n", " z_score_1 = int(z_score.split(' ')[1]) # Splits the string and converts the second element to an integer\n", " error_value = mse_error_thresholds[z_score_1] # Now using the correct integer key to access the value\n", " plt.axvline(error_value, linestyle='-', linewidth=2, color=random_color, label=f'{z_score}, MSE error Threshold: {error_value:.2f}')\n", @@ -567,7 +567,7 @@ "id": "qbA6kFmw0vaf" }, "source": [ - "Here it can plainly be seen the effect of zscore on error threshold. The lowest z-score of 3 reduces the error threshold for that layer." + "Here it can plainly be seen the effect of z-score on error threshold. The lowest z-score of 3 reduces the error threshold for that layer." ] }, { @@ -646,7 +646,7 @@ "id": "GpEZ2E1qzWl3" }, "source": [ - "Here we can see very minor gains from adjusting the zscore threshold. For the majority of simple models this trend will likely follow. From testing we have found that transformer models have a tendancy to benefit from anomoly removal but it is always worth playing with these perameters if your quantised accuracy is distinctly lower than your float model accuracy.\n", + "Here we can see very minor gains from adjusting the z-score threshold. For the majority of simple models this trend will likely follow. From testing we have found that transformer models have a tendancy to benefit from anomoly removal but it is always worth playing with these perameters if your quantised accuracy is distinctly lower than your float model accuracy.\n", "\n" ] }, @@ -667,7 +667,7 @@ "id": "bb7e1572" }, "source": [ - "In this tutorial, we demonstrated the z-score thresholding step used during quantization. Please use this code to assist with choosing zscore thresholds for your own model.\n", + "In this tutorial, we demonstrated the z-score thresholding step used during quantization. Please use this code to assist with choosing z-score thresholds for your own model.\n", "\n", "We have found a when adjusting z-score the sweet spot tends to be between 8 and 12. with no change above 12 and distribution distruction below 8. This will likely require a study on your part for your specific usecase.\n", "\n", @@ -712,8 +712,6 @@ " thresholds_by_index[layer_index] = set()\n", " thresholds_by_index[layer_index].add(threshold)\n", " except Exception as e:\n", - " # Handle potential errors, e.g., index out of range, or property not found\n", - " # You can decide to log this error or simply pass\n", " pass\n", "\n", "# Find indices where threshold values are not consistent\n", From e1da1900106f6f5189b6c9370e897daf7f02ef32 Mon Sep 17 00:00:00 2001 From: samuel-wj-chapman Date: Wed, 27 Mar 2024 12:05:54 +0000 Subject: [PATCH 6/6] fixes based on pr --- .../ptq/example_keras_activation_z_score_threshold.ipynb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb index 894e05263..bfe5f8584 100644 --- a/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb +++ b/tutorials/notebooks/keras/ptq/example_keras_activation_z_score_threshold.ipynb @@ -426,9 +426,7 @@ "outputs": [], "source": [ "#print layer name\n", - "for index, layer in enumerate(float_model.layers):\n", - " if index == 51:\n", - " print(layer.name)\n" + "print(float_model.layers[51].name)" ] }, {