From a81fd1efc07a201836577c4c7fa559e0efdecde2 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Tue, 28 Jan 2025 17:02:35 +0900 Subject: [PATCH 01/30] pytorch_activation_threshold_search(Debugging and exploration code remains) --- ..._pytorch_activation_threshold_search.ipynb | 2406 +++++++++++++++++ 1 file changed, 2406 insertions(+) create mode 100644 tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb new file mode 100644 index 000000000..74cfe2b7f --- /dev/null +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -0,0 +1,2406 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20", + "metadata": { + "id": "f8194007-6ea7-4e00-8931-a37ca2d0dd20" + }, + "source": [ + "# A Practical Guide to Activation Threshold Search in Post-Training Quantization\n", + "\n", + "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/mct_features_notebooks/keras/example_keras_activation_threshold_search.ipynb)\n", + "\n", + "## Overview\n", + "This tutorial demonstrates how to find the optimal activation threshold, a key component in MCT's post-training quantization workflow.\n", + "\n", + "In this example, we will explore two different metrics for threshold selection. We will begin by applying the appropriate MCT configurations, followed by inferring a representative dataset through the model. Next, we will plot the activation distributions of two layers along with their corresponding MCT-calculated thresholds, and finally, we will compare the quantized model accuracy using both methods.\n", + "\n", + "## Activation threshold explanation\n", + "During the quantization process, thresholds are used to map a distribution of 32-bit floating-point values to their quantized equivalents. Minimizing data loss while preserving the most representative range is crucial for maintaining the final model's accuracy.\n", + "\n", + "### How Is It Done in MCT?\n", + "\n", + "MCT's post-training quantization leverages a representative dataset to evaluate a range of typical output activation values. The challenge lies in determining the best way to map these values to their quantized versions. To address this, a grid search is performed to find the optimal threshold using various error metrics. Typically, mean squared error (MSE) is the most effective and is used as the default metric.\n", + "\n", + "The error is calculated based on the difference between the original float and the quantized distributions. The optimal threshold is then selected based on the metric that results in the minimum error. For example, for the case of MSE.\n", + "\n", + "$$\n", + "ERR(t) = \\frac{1}{n_s} \\sum_{X \\in Fl(D)} (Q(X, t, n_b) - X)^2\n", + "$$\n", + "\n", + "- $ERR(t)$ : The quantization error function dependent on the threshold $t$.\n", + "- \n", + "- $n_s$: The size of the representative dataset.\n", + "\n", + "- $\\sum$: Summation over all elements $X$ in the flattened dataset $F_l(D)$.\n", + "\n", + "- $F_l(D)$: The set of activation tensors in the $l$-th layer, flattened for processing.\n", + "\n", + "- $Q(X, t, n_b)$: The quantized approximation of $X$, given a threshold $t$ and bit width $n_b$.\n", + "\n", + "- $X$: The original activation tensor before quantization.\n", + "\n", + "- $t$: The quantization threshold, a key parameter for controlling the quantization process.\n", + "\n", + "- $n_b$: The number of bits used in quantization, impacting model precision and size.\n", + "\n", + "\n", + "Quantization thresholds often have specific limitations, typically imposed for deployment purposes. In MCT, activation thresholds are restricted by default to Power-of-Two values and can represent either signed values within the range $(-T, T)$ or unsigned values within $(0, T)$. Other restriction settings are also configurable.\n", + "\n", + "### Error methods supported by MCT:\n", + "\n", + "- **NOCLIPPING:** Use min/max values as thresholds.\n", + "\n", + "- **MSE:** Minimizes quantization noise by using the mean squared error (MSE).\n", + "\n", + "- **MAE:** Minimizes quantization noise by using the mean absolute error (MAE).\n", + "\n", + "- **KL:** Uses Kullback-Leibler (KL) divergence to align the distributions, ensuring that the quantized distribution is as similar as possible to the original.\n", + "\n", + "- **Lp:** Minimizes quantization noise using the Lp norm, where `p` is a configurable parameter that determines the type of distance metric.\n", + "\n", + "## Setup\n", + "Install the relevant packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "cb67cee7", + "metadata": {}, + "outputs": [], + "source": [ + "import importlib" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", + "metadata": { + "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", + "tags": [] + }, + "outputs": [], + "source": [ + "TORCH_VER = \"2.4.0\"\n", + "if not importlib.util.find_spec('torch'):\n", + " !pip install -q torch=={TORCH_VER} torchvision" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7837babf2112542b", + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "\n", + "if not importlib.util.find_spec('model_compression_toolkit'):\n", + " !pip install model_compression_toolkit" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19", + "metadata": { + "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" + }, + "outputs": [], + "source": [ + "import torch\n", + "from torch.utils.data import DataLoader\n", + "from torchvision.models import mobilenet_v2, MobileNet_V2_Weights\n", + "from torchvision.datasets import ImageNet\n", + "import numpy as np\n", + "import random" + ] + }, + { + "cell_type": "markdown", + "id": "4d691159f5bfc53e", + "metadata": { + "collapsed": false + }, + "source": [ + "Load a pre-trained MobileNetV2 model from Keras, in 32-bits floating-point precision format." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "468d67cd5f25886e", + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "weights = MobileNet_V2_Weights.IMAGENET1K_V2\n", + "\n", + "float_model = mobilenet_v2(weights=weights)" + ] + }, + { + "cell_type": "markdown", + "id": "de5a1be0c4fc4847", + "metadata": { + "collapsed": false + }, + "source": [ + "## Dataset preparation\n", + "### Download the ImageNet validation set\n", + "Download the ImageNet dataset with only the validation split.\n", + "**Note:** For demonstration purposes we use the validation set for the model quantization routines. Usually, a subset of the training dataset is used, but loading it is a heavy procedure that is unnecessary for the sake of this demonstration.\n", + "\n", + "This step may take several minutes..." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "_ztv72uM6-UT", + "metadata": { + "id": "_ztv72uM6-UT" + }, + "outputs": [], + "source": [ + "import os\n", + " \n", + "if not os.path.isdir('imagenet'):\n", + " !mkdir imagenet\n", + " !wget -P imagenet https://image-net.org/data/ILSVRC/2012/ILSVRC2012_devkit_t12.tar.gz\n", + " !wget -P imagenet https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_val.tar" + ] + }, + { + "cell_type": "markdown", + "id": "67d878e0", + "metadata": {}, + "source": [ + "Extract ImageNet validation dataset using torchvision \"datasets\" module." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9e5658c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "50000\n", + "Dataset ImageNet\n", + " Number of datapoints: 50000\n", + " Root location: ./imagenet\n", + " Split: val\n", + " StandardTransform\n", + "Transform: ImageClassification(\n", + " crop_size=[224]\n", + " resize_size=[232]\n", + " mean=[0.485, 0.456, 0.406]\n", + " std=[0.229, 0.224, 0.225]\n", + " interpolation=InterpolationMode.BILINEAR\n", + " )\n" + ] + } + ], + "source": [ + "dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", + "print(type(dataset))\n", + "print(len(dataset))\n", + "print(dataset)\n" + ] + }, + { + "cell_type": "markdown", + "id": "8aa0bca3e15fba91", + "metadata": { + "collapsed": false + }, + "source": [ + "## Representative Dataset\n", + "For quantization with MCT, we need to define a representative dataset required by the PTQ algorithm. This dataset is a generator that returns a list of images:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1bdb4144e4ce2ab6", + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "batch_size = 8\n", + "n_iter = 10\n", + "\n", + "#dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n", + "dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=False)\n", + "\n", + "def representative_dataset_gen():\n", + " dataloader_iter = iter(dataloader)\n", + " for _ in range(n_iter):\n", + " yield [next(dataloader_iter)[0]]" + ] + }, + { + "cell_type": "markdown", + "id": "98f4bbca00996989", + "metadata": { + "collapsed": false + }, + "source": [ + "## Target Platform Capabilities\n", + "MCT optimizes the model for dedicated hardware. This is done using TPC (for more details, please visit our [documentation](https://sony.github.io/model_optimization/api/api_docs/modules/target_platform.html)). Here, we use the default Tensorflow TPC:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "554719effaf90250", + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import model_compression_toolkit as mct\n", + "\n", + "# Get a TargetPlatformCapabilities object that models the hardware platform for the quantized model inference. Here, for example, we use the default platform that is attached to a Pytorch layers representation.\n", + "target_platform_cap = mct.get_target_platform_capabilities('pytorch', 'default')" + ] + }, + { + "cell_type": "markdown", + "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5", + "metadata": { + "id": "4a1e9ba6-2954-4506-ad5c-0da273701ba5" + }, + "source": [ + "## Post-Training Quantization using MCT\n", + "In this step, we load the model and apply post-training quantization using two threshold error calculation methods: **\"No Clipping\"** and **MSE**.\n", + "\n", + "- **\"No Clipping\"** selects the lowest power-of-two threshold that ensures no data is lost (clipped).\n", + "- **MSE** selects a power-of-two threshold that minimizes the mean square error between the original float distribution and the quantized distribution.\n", + "\n", + "- As a result, the \"No Clipping\" method typically results in a larger threshold, as we will demonstrate later in this tutorial.\n", + "\n", + "The quantization parameters are predefined, and we use the default values except for the quantization method. Feel free to modify the code below to experiment with other error metrics supported by MCT." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "jtiZzXmTjxuI", + "metadata": { + "id": "jtiZzXmTjxuI" + }, + "outputs": [], + "source": [ + "from model_compression_toolkit.core import QuantizationErrorMethod\n", + "\n", + "q_configs_dict = {}\n", + "# Error methods to iterate over\n", + "error_methods = [\n", + " QuantizationErrorMethod.MSE,\n", + " QuantizationErrorMethod.NOCLIPPING\n", + "]\n", + "\n", + "# If you are curious you can add any of the below quantization methods as well.\n", + "# QuantizationErrorMethod.MAE\n", + "# QuantizationErrorMethod.KL\n", + "# QuantizationErrorMethod.LP\n", + "\n", + "# Iterate and build the QuantizationConfig objects\n", + "for error_method in error_methods:\n", + " q_config = mct.core.QuantizationConfig(\n", + " activation_error_method=error_method,\n", + " )\n", + "\n", + " q_configs_dict[error_method] = q_config" + ] + }, + { + "cell_type": "markdown", + "id": "8W3Dcn0jkJOH", + "metadata": { + "id": "8W3Dcn0jkJOH" + }, + "source": [ + "Now we will run post-training quantization for each configuration:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b609660e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FOUND_TORCH True\n" + ] + } + ], + "source": [ + "FOUND_TORCH = importlib.util.find_spec(\"torch\") is not None\n", + "print(\"FOUND_TORCH\", FOUND_TORCH)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", + "metadata": { + "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DepthwiseConv2D is not in model.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Statistics Collection: 10it [00:10, 1.05s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Running quantization parameters search. This process might take some time, depending on the model size and the selected quantization methods.\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.95it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Please run your accuracy evaluation on the exported quantized model to verify it's accuracy.\n", + "Checkout the FAQ and Troubleshooting pages for resolving common issues and improving the quantized model accuracy:\n", + "FAQ: https://github.com/sony/model_optimization/tree/main/FAQ.md\n", + "Quantization Troubleshooting: https://github.com/sony/model_optimization/tree/main/quantization_troubleshooting.md\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DepthwiseConv2D is not in model.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Statistics Collection: 10it [00:09, 1.01it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Running quantization parameters search. This process might take some time, depending on the model size and the selected quantization methods.\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.15it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Please run your accuracy evaluation on the exported quantized model to verify it's accuracy.\n", + "Checkout the FAQ and Troubleshooting pages for resolving common issues and improving the quantized model accuracy:\n", + "FAQ: https://github.com/sony/model_optimization/tree/main/FAQ.md\n", + "Quantization Troubleshooting: https://github.com/sony/model_optimization/tree/main/quantization_troubleshooting.md\n" + ] + } + ], + "source": [ + "quantized_models_dict = {}\n", + "\n", + "for error_method, 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.pytorch_post_training_quantization(\n", + " in_module=float_model,\n", + " representative_data_gen=representative_dataset_gen,\n", + " core_config=ptq_config,\n", + " target_platform_capabilities=target_platform_cap\n", + " )\n", + "\n", + " # Update the dictionary to include the quantized model\n", + " quantized_models_dict[error_method] = {\n", + " \"quantization_config\": q_config,\n", + " \"quantized_model\": quantized_model,\n", + " \"quantization_info\": quantization_info\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "24ae88fa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "PytorchActivationQuantizationHolder()\n", + "\n", + "\n", + "\n", + "8.0\n" + ] + } + ], + "source": [ + "import mct_quantizers\n", + "\n", + "import mct_quantizers.pytorch.quantizers.activation_inferable_quantizers.activation_pot_inferable_quantizer\n", + "\n", + "#print(quantized_models_dict)\n", + "print(type(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model']))\n", + "#print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'].features_0_2.activation_holder_quantizer)\n", + "\n", + "wkm = quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model']\n", + "wkc = quantized_models_dict[QuantizationErrorMethod.MSE]['quantization_config']\n", + "wki = quantized_models_dict[QuantizationErrorMethod.MSE]['quantization_info']\n", + "\n", + "print(wkm.features_1_conv_0_2_activation_holder_quantizer)\n", + "print(type(wkm))\n", + "print(type(wkm.features_1_conv_0_2_activation_holder_quantizer))\n", + "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer)\n", + "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer.threshold_np)\n", + "#print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer.get_config())\n", + "#print(wkc)\n", + "#print(wki)\n", + "\n", + "\n", + "\n", + "\n", + "for error_method, data in quantized_models_dict.items():\n", + " ##print(error_method)\n", + " #print(data['quantized_model'].features_0_0_bn.layer)\n", + " #print(type(data['quantized_model'].features_0_0_bn))\n", + " #print(error_method, data)\n", + " pass\n" + ] + }, + { + "cell_type": "markdown", + "id": "A8UHRsh2khM4", + "metadata": { + "id": "A8UHRsh2khM4" + }, + "source": [ + "## Threshold and Distribution Visualization\n", + "To facilitate understanding, we will plot the activation distributions for two layers of MobileNetV2. For each layer, we will show the thresholds determined by both **MSE** and **No Clipping** methods, along with the corresponding activation distributions obtained by infering the representative dataset through the model. This visualization highlights the trade-off between data loss and data resolution under different thresholds during quantization.\n", + "\n", + "MCT’s `quantization_info` stores the threshold values for each layer. However, to view the actual activation distributions, the model needs to be reconstructed up to and including the target layer selected for visualization.\n", + "\n", + "To do this, we first need to identify the layer names. In Keras, this can be easily done for the first 10 layers using the following code snippet." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", + "metadata": { + "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "### 0\n", + " MobileNetV2(\n", + " (features): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (2): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (3): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (4): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (5): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (6): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (7): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (8): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (9): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (10): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (11): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (12): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (13): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (14): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (15): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (16): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (17): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (18): Conv2dNormActivation(\n", + " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " )\n", + " (classifier): Sequential(\n", + " (0): Dropout(p=0.2, inplace=False)\n", + " (1): Linear(in_features=1280, out_features=1000, bias=True)\n", + " )\n", + ")\n", + "### 1\n", + "features Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (2): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (3): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (4): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (5): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (6): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (7): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (8): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (9): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (10): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (11): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (12): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (13): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (14): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (15): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (16): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (17): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (18): Conv2dNormActivation(\n", + " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + ")\n", + "### 2\n", + "features.0 Conv2dNormActivation(\n", + " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + ")\n", + "### 3\n", + "features.0.0 Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + "### 4\n", + "features.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + "### 5\n", + "features.0.2 ReLU6(inplace=True)\n", + "### 6\n", + "features.1 InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + ")\n", + "### 7\n", + "features.1.conv Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + ")\n", + "### 8\n", + "features.1.conv.0 Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + ")\n", + "### 9\n", + "features.1.conv.0.0 Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n" + ] + } + ], + "source": [ + "for index, (name, layer) in enumerate(float_model.named_modules()):\n", + " if index < 10:\n", + " print(\"###\", index)\n", + " print(name, layer)\n", + " else:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277", + "metadata": { + "id": "c38d28f3-c947-4c7c-aafa-e96cc3864277" + }, + "source": [ + "The first activation layer in the model is named `ReLU6`.\n", + "\n", + "For this particular model, testing has shown that the `BatchNorm2d` layer exhibits different thresholds for the two error metrics. Therefore, we will also include this layer in the visualization. For context, MobileNetV2 uses an inverted residual structure, where the input is first expanded in the channel dimension, then passed through a depthwise convolution, and finally projected back to a lower dimension. The `BatchNorm2d` layer represents this projection, and the BN suffix indicates the presence of Batch Normalization.\n", + "\n", + "We will use these layer names to create two separate models, each ending at one of these respective layers." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d", + "metadata": { + "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" + }, + "outputs": [], + "source": [ + "layer_name1 = 'features.0.2' # Conv1_reluに相当\n", + "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BNに相当\n", + "\n", + "# 特定のレイヤーの出力を取得するためのフックを設定\n", + "def get_layer_output(model, layer_name):\n", + " outputs = {}\n", + "\n", + " def hook(module, input, output):\n", + " outputs[layer_name] = output\n", + "\n", + " # 指定したレイヤーにフックを追加\n", + " layer = dict(model.named_modules())[layer_name] # モジュール名を辞書形式で取得\n", + " layer.register_forward_hook(hook)\n", + "\n", + " return outputs\n", + "\n", + "\n", + "\n", + "# Conv1_reluの出力を取得\n", + "output_dict_relu = get_layer_output(float_model, layer_name1)\n", + "\n", + "# expanded_conv_project_BNの出力を取得(features.1.conv.1 は活性化関数の次)\n", + "output_dict_project = get_layer_output(float_model, layer_name2)\n", + "\n", + "# モデルに入力データを通す\n", + "#input_tensor = torch.randn(1, 3, 224, 224) # バッチサイズ1の入力データ\n", + "#float_model(input_tensor)\n", + "\n", + "# それぞれの出力を取得\n", + "#output_relu = output_dict_relu[layer_name1]\n", + "#output_project = output_dict_project[layer_name2]\n", + "\n", + "#print(f\"Output from {layer_name1}:\", output_relu.shape)\n", + "#print(f\"Output from {layer_name2}:\", output_project.shape)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364", + "metadata": { + "id": "ccc81508-01e5-421c-9b48-6ed3ce5b7364" + }, + "source": [ + "Infer the representative dataset using these models and store the outputs for further analysis." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "eaeb9888-5d67-4979-af50-80781a811b4b", + "metadata": { + "id": "eaeb9888-5d67-4979-af50-80781a811b4b" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.4.0+cu121\n", + "cuda:0\n", + "NVIDIA GeForce RTX 3090\n", + "(8, 6)\n", + "0\n", + "1\n" + ] + } + ], + "source": [ + "import torch\n", + "print(torch.__version__)\n", + "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", + "print(device)\n", + "print(torch.cuda.get_device_name())\n", + "print(torch.cuda.get_device_capability())\n", + "print(torch.cuda.current_device())\n", + "print(torch.cuda.device_count())" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4a5bf3dd", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 10/6250 [00:00<06:27, 16.10it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0. 0. 0. ... 0.24012035 1.2019204 1.4022245 ] 35323904\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.25255647\n", + "0.6007481\n", + "0.6139736\n", + "0.25284094\n", + "0.0\n", + "0.0\n", + "0.25791883\n", + "0.15303095\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n", + "0.0\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "import numpy as np\n", + "activation_batches_relu = []\n", + "activation_batches_project = []\n", + "with torch.no_grad():\n", + " float_model = float_model.to(device)\n", + " for index, data in enumerate(tqdm(dataloader)):\n", + " images, labels = data\n", + " images, labels = images.to(device), labels.to(device)\n", + "\n", + " float_model(images)\n", + "\n", + " activations_relu = output_dict_relu[layer_name1]\n", + " #activation_batches_relu.append(activations_relu.to('cpu'))\n", + " activation_batches_relu.append(activations_relu.to('cpu').detach().numpy().copy())\n", + " activations_project = output_dict_project[layer_name2]\n", + " #activation_batches_project.append(activations_project.to('cpu'))\n", + " activation_batches_project.append(activations_project.to('cpu').detach().numpy().copy())\n", + "\n", + " #print(\"images.shape\", images.shape, type(images))\n", + " #print(\"images0\", images[0])\n", + " #print(\"activations_relu\", type(activations_relu))\n", + " #print(\"activations_relu.shape\", activations_relu.shape)\n", + " #print(\"activations_project\", type(activations_project))\n", + " #print(\"activations_project.shape\", activations_project.shape)\n", + "\n", + " del(activations_relu)\n", + " del(activations_project)\n", + " del(data)\n", + " del(images)\n", + " del(labels)\n", + " \n", + " if index >= 10:\n", + " break\n", + "\n", + " all_activations_relu = np.concatenate(activation_batches_relu, axis=0).flatten()\n", + " all_activations_project = np.concatenate(activation_batches_project, axis=0).flatten()\n", + "\n", + "print(all_activations_relu, len(all_activations_relu))\n", + "for i in range(20):\n", + " print(all_activations_relu[i])\n" + ] + }, + { + "cell_type": "markdown", + "id": "I5W9yY5DvOFr", + "metadata": { + "id": "I5W9yY5DvOFr" + }, + "source": [ + "Thresholds calculated by MCT during quantization can be accessed using the following approach. The layer indices correspond to the order of the layers listed in the previous steps.\n", + "\n", + "As noted earlier, we focus on the first ReLU activation layer and the Batch Normalization layer (`expanded_conv_project_BN`) since they effectively illustrate the impact of the two threshold error methods." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "20b6ad11", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PytorchModel(\n", + " (x): DummyPlaceHolder()\n", + " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " (features_0_2): ReLU6(inplace=True)\n", + " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", + " )\n", + " (features_1_conv_0_2): ReLU6(inplace=True)\n", + " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_0_2): ReLU6(inplace=True)\n", + " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", + " )\n", + " (features_2_conv_1_2): ReLU6(inplace=True)\n", + " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_0_2): ReLU6(inplace=True)\n", + " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", + " )\n", + " (features_3_conv_1_2): ReLU6(inplace=True)\n", + " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_0_2): ReLU6(inplace=True)\n", + " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", + " )\n", + " (features_4_conv_1_2): ReLU6(inplace=True)\n", + " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_0_2): ReLU6(inplace=True)\n", + " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_5_conv_1_2): ReLU6(inplace=True)\n", + " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_0_2): ReLU6(inplace=True)\n", + " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_6_conv_1_2): ReLU6(inplace=True)\n", + " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_0_2): ReLU6(inplace=True)\n", + " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", + " )\n", + " (features_7_conv_1_2): ReLU6(inplace=True)\n", + " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_0_2): ReLU6(inplace=True)\n", + " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_8_conv_1_2): ReLU6(inplace=True)\n", + " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_0_2): ReLU6(inplace=True)\n", + " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_9_conv_1_2): ReLU6(inplace=True)\n", + " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_0_2): ReLU6(inplace=True)\n", + " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_10_conv_1_2): ReLU6(inplace=True)\n", + " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_0_2): ReLU6(inplace=True)\n", + " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_11_conv_1_2): ReLU6(inplace=True)\n", + " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_0_2): ReLU6(inplace=True)\n", + " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_12_conv_1_2): ReLU6(inplace=True)\n", + " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_0_2): ReLU6(inplace=True)\n", + " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_13_conv_1_2): ReLU6(inplace=True)\n", + " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_0_2): ReLU6(inplace=True)\n", + " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", + " )\n", + " (features_14_conv_1_2): ReLU6(inplace=True)\n", + " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_0_2): ReLU6(inplace=True)\n", + " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_15_conv_1_2): ReLU6(inplace=True)\n", + " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_0_2): ReLU6(inplace=True)\n", + " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_16_conv_1_2): ReLU6(inplace=True)\n", + " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_0_2): ReLU6(inplace=True)\n", + " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_17_conv_1_2): ReLU6(inplace=True)\n", + " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_18_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_18_2): ReLU6(inplace=True)\n", + " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (classifier_0): Dropout(p=0.2, inplace=False)\n", + " (classifier_1): PytorchQuantizationWrapper(\n", + " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", + " )\n", + " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + ")\n" + ] + } + ], + "source": [ + "print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "NGnjrPD_uTd5", + "metadata": { + "id": "NGnjrPD_uTd5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{: np.float64(8.0), : np.float64(8.0)}\n", + "{: np.float64(32.0), : np.float64(64.0)}\n" + ] + } + ], + "source": [ + "# layer 4 is the first activation layer - Conv1_relu\n", + "optimal_thresholds_relu = {\n", + " error_method: data[\"quantized_model\"].features_0_2_activation_holder_quantizer.activation_holder_quantizer.threshold_np\n", + " for error_method, data in quantized_models_dict.items()\n", + "}\n", + "\n", + "print(optimal_thresholds_relu)\n", + "\n", + "# layer 9 is the batch normalisation projection layer - Expanded_conv_project_BN\n", + "optimal_thresholds_project = {\n", + " error_method: data[\"quantized_model\"].features_2_conv_2_bn_activation_holder_quantizer.activation_holder_quantizer.threshold_np\n", + " for error_method, data in quantized_models_dict.items()\n", + "}\n", + "\n", + "print(optimal_thresholds_project)" + ] + }, + { + "cell_type": "markdown", + "id": "XRAr8L5mvuLd", + "metadata": { + "id": "XRAr8L5mvuLd" + }, + "source": [ + "### Distribution Plots\n", + "Below are the activation distributions for the two selected layers: first, the ReLU activation layer, `Conv1_relu`, followed by the `expanded_conv_project_BN` layer.\n", + "\n", + "The second distribution clearly highlights the differences between the two error metrics, showing the impact of each on the resulting quantization threshold." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "VPb8tBNGpJjo", + "metadata": { + "id": "VPb8tBNGpJjo" + }, + "outputs": [ + { + "data": { + "image/png": "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pD4nNWvWVIkSJXTgwAHVqFFD6dOnV4ECBfTTTz9JkjZs2KBKlSrJy8tLhQsX1urVq63iPXfunD766CMVLlxYXl5eypo1q5o3b273F90xY8bozp07+uabb6wSIEnKli2bpk+frsjISI0ZM8ZSntCcxPhex7NmzdIrr7wif39/eXh4qFixYpbXwMMS894QGhqq5s2bS5Jq1aplOd9x5/fRuTmPGzIWt09izmdS25WkK1euKDg4WNmzZ5enp6dKly6t2bNnW9V5eG6MPa9rezypnbj3glOnTqlBgwby8fFRmzZtJElms1kTJkxQ8eLF5enpqezZs6tTp05Wfw+StGvXLtWrV0/ZsmWTl5eXgoKC1LFjR7vikaS1a9eqevXq8vb2VqZMmdSkSRMdOXLkiY/VMAwNHz5cuXPnVvr06VWrVi0dOnTIpt79+/c1dOhQFSxYUJ6ensqaNauqVaumVatWPbGNJ3l0TlDc38jhw4fVunVrZc6cWdWqVZP0dJ9NSVG0aFFly5ZNp06dsiqPiorS4MGDVaBAAXl4eCggIEB9+/a1+Rx/VGI/v57G9evX1adPH5UsWVIZMmRQxowZVb9+fe3fv99SJzIyUt7e3urevbvN/v/8849cXV0VEhJiKbt586Z69OihgIAAeXh4qECBAho9erTVKJuH/0YnTJhgea0+b8OEUxN6gtK4uXPn6o033pC7u7tatWqlqVOnaufOnVbd5ZGRkapevbqOHDmijh076sUXX9S1a9f066+/6p9//lHRokX1+eefa9CgQfrggw9UvXp1SfEPbUiXLp2aNWumxYsXa/r06VZXJ5YuXaqoqCjL+OSIiAh99913atWqld5//33dunVLM2bMUL169bRjxw6VKVNGfn5+mjp1qj788EM1a9ZMb7zxhiSpVKlSCT7m9957T7Nnz9Zbb72l3r17a/v27QoJCdGRI0e0ZMkSq7onT57UW2+9peDgYLVr104zZ85U+/btVa5cORUvXtzu854nTx7VqFFD69atU0REhDJmzBhvvTfffFOHDh3Sxx9/rMDAQF25ckWrVq3S+fPnFRgYqAkTJujjjz9WhgwZNGDAAElS9uzZrY7x0Ucfyc/PT4MGDdLt27cfG9eJEyfUokULde7cWe3atdOsWbPUvHlz/fnnn6pTp06SHmNiYntYaGioOnTooAoVKigkJESXL1/WxIkTtWXLFu3du1eZMmWy1I2NjVW9evVUqVIlffnll1q9erXGjh2r/Pnz68MPP0ywjerVq2v8+PE6dOiQSpQoIUnatGmTXFxctGnTJnXr1s1SJj0YIhafTp066eLFi1q1apW+//77eOvMmzdPt27dUqdOnWQymfTFF1/ojTfe0OnTpx/bO/rbb7/J09NTb7/9drzbg4KCVK1aNa1du1Z3796Vl5eXZVvXrl2VKVMmDRkyRMeOHdPUqVN17tw5S4KT1OdEetAz1ahRI7Vs2VLNmzfX1KlT1bJlS82dO1c9evRQ586d1bp1a40ZM0ZvvfWWLly4IB8fH0nSzp07tXXrVrVs2VK5c+fW2bNnNXXqVNWsWVOHDx9W+vTpH9t2fOcmMDDQ8h7zqJdfflmBgYH67bffNGXKlCQdW5KmTp2q4sWL6/XXX5ebm5t+++03ffTRRzKbzerSpYtV3Se9N7z88svq1q2bvvrqK3366acqWrSoJFn+fdSjQ8Ykafz48dq3b5+yZs0qKXHnM6nt3r17VzVr1tTJkyfVtWtXBQUFadGiRWrfvr1u3rxp8yXN3td1UiW2nZiYGNWrV0/VqlXTl19+aXlNderUyfKe0q1bN505c0aTJ0/W3r17tWXLFqVLl05XrlxR3bp15efnp379+ilTpkw6e/aszYWfxMazevVq1a9fX/ny5dOQIUN09+5dTZo0SVWrVtWePXseu0DQoEGDNHz4cDVo0EANGjTQnj17VLduXUVHR1vVGzJkiEJCQvTee++pYsWKioiI0K5du7Rnz55EvUffunXLZj5LlixZ5OKS8DXv5s2bq2DBgho5cqTlAlJyfDYlRnh4uG7cuGF1QchsNuv111/X5s2b9cEHH6ho0aL6+++/NX78eB0/flxLly5NcjvJ6fTp01q6dKmaN2+uoKAgXb58WdOnT1eNGjV0+PBh5cqVSxkyZFCzZs20YMECjRs3zmpkxo8//ijDMCwJ/Z07d1SjRg39+++/6tSpk/LkyaOtW7eqf//+CgsL04QJE6zanzVrlu7du6cPPvhAHh4eypIly7N8+GmLgTRr165dhiRj1apVhmEYhtlsNnLnzm10797dqt6gQYMMScbixYttjmE2mw3DMIydO3cakoxZs2bZ1GnXrp2RN29ey/0VK1YYkozffvvNql6DBg2MfPnyWe7HxMQYUVFRVnVu3LhhZM+e3ejYsaOl7OrVq4YkY/DgwTZtDx482Hj4Zbxv3z5DkvHee+9Z1evTp48hyVi7dq2lLG/evIYkY+PGjZayK1euGB4eHkbv3r1t2nqUJKNLly4Jbu/evbshydi/f79hGIZx5swZq3N448YNQ5IxZsyYx7ZTvHhxo0aNGjbls2bNMiQZ1apVM2JiYuLddubMGUtZ3OP9+eefLWXh4eFGzpw5jbJly1rKHj2njztmQrGtW7fOkGSsW7fOMAzDiI6ONvz9/Y0SJUoYd+/etdRbtmyZIckYNGiQpaxdu3aGJOPzzz+3OmbZsmWNcuXK2bT1sCtXrhiSjClTphiGYRg3b940XFxcjObNmxvZs2e31OvWrZuRJUsWy+v70efGMAyjS5cu8Z6HuLpZs2Y1rl+/bin/5Zdf4n3dPypTpkxG6dKlH1unW7duhiTjwIEDhmH837kvV66cER0dban3xRdfGJKMX375xVKW2OfEMAyjRo0ahiRj3rx5lrKjR48akgwXFxfjr7/+spTH/V0/fI7u3Llj0862bdsMScacOXMe2/ajbt68aUgymjRpkmAdwzCM119/3ZBkREREGIZh+/4TJ77XcXzx1qtXz+p9yTAS/96waNGiBB9XjRo14n0e4ixcuNDmdZ7Y85mUdidMmGBIMn744QdLWXR0tFG5cmUjQ4YMlvP4tK/rh40ZM8bmvSJOUtqJey/o16+f1TE2bdpkSDLmzp1rVf7nn39alS9ZssSQZOzcuTPBWJMST5kyZQx/f3/jv//+s5Tt37/fcHFxMd59911L2aPvlVeuXDHc3d2Nhg0bWt5zDMMwPv30U0OS0a5dO0tZ6dKljYYNGyYYb0Li/sbiu8XF8ejnaNzfSKtWrayO9bSfTQmRZAQHBxtXr141rly5Yuzatct47bXXbNr6/vvvDRcXF2PTpk1W+0+bNs2QZGzZssVSljdvXqvzl5TPr/jE7X/16tUE69y7d8+IjY21Kjtz5ozh4eFh9fcc9565fPlyq7qlSpWyOm/Dhg0zvL29jePHj1vV69evn+Hq6mqcP3/e0oYkI2PGjMaVK1ce+ziQOAyHS8Pmzp2r7Nmzq1atWpIedIW3aNFC8+fPtxpW9PPPP6t06dJq1qyZzTHsWX76lVdeUbZs2bRgwQJL2Y0bN7Rq1Sq1aNHCUubq6mrpKTKbzbp+/bpiYmJUvnx57dmzJ8ntSg8m5EqyWpBAknr37i1JNqvQFCtWzOqqs5+fnwoXLqzTp0/b1f7DMmTIIOnBlbn4eHl5yd3dXevXr7cZwpEU77//fqLn/+TKlcvqeY4bUrV3715dunTJ7hieZNeuXbpy5Yo++ugjq3HpDRs2VJEiReJdHahz585W96tXr/7E58XPz09FihTRxo0bJT1YgMDV1VWffPKJLl++rBMnTkh60BNUrVq1p1pevUWLFsqcObNVfJKeGOOtW7csPSkJidseERFhVf7BBx9YXSX/8MMP5ebmZnnd2yNDhgxWq0cVLlxYmTJlUtGiRa1WlIz7/8OP7+Feqvv37+u///5TgQIFlClTpiT/Dcf9nST23CT0d/U4D8cbHh6ua9euqUaNGjp9+rTCw8Ot6qbke8Phw4fVsWNHNWnSRJ999lm88T3t+Yzzxx9/KEeOHGrVqpWlLF26dOrWrZsiIyO1YcMGq/r2vq6TKintPNr7u2jRIvn6+qpOnTq6du2a5VauXDllyJDBMqQ6rnd52bJlun///lPFExYWpn379ql9+/ZWV95LlSqlOnXqPPZvcPXq1YqOjrYMzY3To0cPm7qZMmXSoUOHLO9VSTVo0CCtWrXK6pYjR47H7vPoe21yfTbFZ8aMGfLz85O/v7/Kly+vNWvWqG/fvlaf2YsWLVLRokVVpEgRq+f3lVdekSSrIfOO4OHhYelZi42N1X///acMGTKocOHCVn+ntWvXVq5cuaymIRw8eFAHDhywmhe1aNEiVa9eXZkzZ7Z6vLVr11ZsbKzl8yzOm2++aTNcGPZJM0nQxo0b1bhxY+XKlUsmk8mu7lLDMPTll1+qUKFC8vDw0AsvvGA1pyM1iY2N1fz581WrVi2dOXNGJ0+e1MmTJ1WpUiVdvnxZa9assdQ9deqUZehQcnBzc9Obb76pX375xTJ+d/Hixbp//75VEiRJs2fPVqlSpSxjn/38/PT777/bfCFJrHPnzsnFxcVmBbwcOXIoU6ZMOnfunFV5njx5bI6ROXPmZHnjjxv6ktCXOg8PD40ePVrLly9X9uzZ9fLLL+uLL75IcjKSlAniBQoUsPniX6hQIUlPP2H5ceLOe+HChW22FSlSxOZ58fT0tHmTT+zzUr16dctwt02bNql8+fIqX768smTJok2bNikiIkL79+9PcMhVYj362on7AvWkGH18fJ74BT6hhKBgwYJW9zNkyKCcOXM+1XOXO3dum9eEr6+vAgICbMok68d39+5dDRo0yDKOPVu2bPLz89PNmzeT/Dec2OTm1q1bMplMVquOJdaWLVtUu3Zty3wOPz8/ffrpp5JkE29KvTdERETojTfe0AsvvKA5c+ZYnfvkPJ9xzp07p4IFC9oMh4obPvek98TEvq6TKrHtuLm5WeY6xjlx4oTCw8Pl7+8vPz8/q1tkZKSuXLkiSapRo4befPNNDR06VNmyZVOTJk00a9aseOeVPCmex72HFS1aVNeuXUtwOHLcvo/+/fr5+VklXpL0+eef6+bNmypUqJBKliypTz75RAcOHIj3uPEpWbKkateubXV70oIIj36GJNdnU3yaNGmiVatW6ffff7fM37lz547V6/PEiRM6dOiQzXMb91kV9/w6itls1vjx41WwYEGrv9MDBw5Y/Z26uLioTZs2Wrp0qe7cuSPpwcVpT09Py7w+6cHj/fPPP20eb+3atSXZPt7nfVGY1CTNzAm6ffu2SpcurY4dO1rmjSRV9+7dtXLlSn355ZcqWbKkrl+/ruvXrydzpM/G2rVrFRYWpvnz52v+/Pk22+fOnau6deumWPstW7bU9OnTtXz5cjVt2lQLFy5UkSJFVLp0aUudH374Qe3bt1fTpk31ySefyN/f3zJZ8NFJkkmV2Cv8CfWgGA9NrLfXwYMH5erq+tg3rB49eqhx48ZaunSpVqxYoYEDByokJERr165V2bJlE9XOw1ePk0NC5+5JixIkp6dZ2a5atWr69ttvdfr0aW3atEnVq1eXyWRStWrVtGnTJuXKlUtms/mpkyB7XztFixbV3r17FRUVleAS5gcOHFC6dOlsvjSlhIQeR2Ie38cff6xZs2apR48eqly5snx9fS2/S5LUZfN9fX2VK1euJ37hO3DggHLnzm3pRU7s6/XUqVN69dVXVaRIEY0bN04BAQFyd3fXH3/8ofHjx9vEm1LvDe3bt9fFixe1Y8cOm7mCyXk+7ZWS74n2tPPwVfc4ZrNZ/v7+CS70E3cBxWQy6aefftJff/2l3377TStWrFDHjh01duxY/fXXX5be+qTEk9JefvllnTp1Sr/88otWrlyp7777TuPHj9e0adP03nvvpUib8X2GJMdnU3xy585t+XLfoEEDZcuWTV27dlWtWrUs393MZrNKliypcePGxXuMRy/QPOxZfH6NHDlSAwcOVMeOHTVs2DDLnKsePXrY/J2+++67GjNmjJYuXapWrVpp3rx5atSokeWikvTg8dapU0d9+/aNt7245C9Ocn/mO7M0kwTVr19f9evXT3B7VFSUBgwYoB9//FE3b95UiRIlNHr0aMtqJkeOHNHUqVN18OBBy5We1Jxtz507V/7+/pYVrh62ePFiLVmyRNOmTZOXl5fy58+vgwcPPvZ4SR029PLLLytnzpxasGCBZZJ33OTJOD/99JPy5cunxYsXWx3/0SW8k9J23rx5ZTabdeLECauJwpcvX9bNmzeVN2/eJD0Oe50/f14bNmxQ5cqVnzi8J3/+/Ordu7d69+6tEydOqEyZMho7dqx++OEHSfYNSUzIyZMnZRiG1TGPHz8u6f9+SyLuyuTNmzetFit49IpxUmKLO+/Hjh2zDGmIc+zYsWR9XuKSm1WrVmnnzp2W35F6+eWXNXXqVOXKlUve3t4qV67cY4+TnOf9YY0aNdK2bdu0aNEim+XVpQc9cps2bVLt2rVtPuxOnDhhGd4qPehtDAsLsyzDnpJxx+enn35Su3btNHbsWEvZvXv3dPPmTbuO17hxY02fPl2bN2+2rFL1sE2bNuns2bNWQ2cyZ84cb3uPvl5/++03RUVF6ddff7W66v80Q2uSeq5HjRqlpUuXavHixSpSpIjN9sSez6S+Jx44cEBms9kqmTh69Khle2qTP39+rV69WlWrVk3UF8KXXnpJL730kkaMGKF58+apTZs2mj9/fpKSioffwx519OhRZcuWLcGlpeP2PXHihPLly2cpv3r1arw9bHErrHbo0EGRkZF6+eWXNWTIkBRLghLyLD6bOnXqpPHjx+uzzz5Ts2bNZDKZlD9/fu3fv1+vvvpqkttIyueXvX766SfVqlVLM2bMsCq/efOmTQ91iRIlVLZsWc2dO1e5c+fW+fPnNWnSJKs6+fPnV2RkpCU5xLOTZobDPUnXrl21bds2zZ8/XwcOHFDz5s312muvWcbd/vbbb8qXL5+WLVumoKAgBQYG6r333kuVPUF3797V4sWL1ahRI7311ls2t65du+rWrVv69ddfJT0YX7p//36bldOk/7sKFvfmntgvNy4uLnrrrbf022+/6fvvv1dMTIzNULi4K28PX2nbvn27tm3bZlUvbjWgxLQd92Xw0dVU4q4oNWzYMFHxP43r16+rVatWio2NtUn8Hnbnzh3du3fPqix//vzy8fGxGq7h7e1t95fKR128eNHqeY6IiNCcOXNUpkwZy7jxuFV6Hh6HfPv2bZsldZMSW/ny5eXv769p06ZZPbbly5fryJEjyfq8BAUF6YUXXtD48eN1//59Va1aVdKD5OjUqVP66aef9NJLLz3x96WS+ppPrE6dOsnf31+ffPKJzfyHe/fuqUOHDjIMQ4MGDbLZ95tvvrGa2zB16lTFxMRYXQBKztfLk7i6utpcKZ80aZLdV1379Omj9OnTq1OnTvrvv/+stl2/fl2dO3dWxowZ1bVrV0t5/vz5FR4ebtWDFBYWZvN+Ft/7TXh4uGbNmmVXrFLSXiOrV6/WZ599pgEDBtj8rs7DMSbmfCal3QYNGujSpUtWczRjYmI0adIkZciQQTVq1HjiMZ43b7/9tmJjYzVs2DCbbTExMZbzcuPGDZvzGfdj309aavlROXPmVJkyZTR79myr837w4EGtXLnS6kLEo2rXrq106dJp0qRJVvE8+jklyeZ1nyFDBhUoUCDJ8T6NZ/nZ5Obmpt69e+vIkSP65ZdfJD14fv/99199++23NvXv3r372FVQk/L5Za/4/k4XLVpk83MPcd555x2tXLlSEyZMUNasWW0u2L/99tvatm2bVqxYYbPvzZs3FRMTk2yxw1qa6Ql6nPPnz2vWrFk6f/68cuXKJenBh+2ff/6pWbNmaeTIkTp9+rTOnTunRYsWac6cOYqNjVXPnj311ltvae3atQ5+BEnz66+/6tatW3r99dfj3f7SSy/Jz89Pc+fOVYsWLfTJJ5/op59+UvPmzdWxY0eVK1dO169f16+//qpp06apdOnSyp8/vzJlyqRp06bJx8dH3t7eqlSp0mN7y1q0aKFJkyZp8ODBKlmypM0Sro0aNdLixYvVrFkzNWzYUGfOnNG0adNUrFgxq6Vkvby8VKxYMS1YsECFChVSlixZVKJEiXjnMZUuXVrt2rXTN998o5s3b6pGjRrasWOHZs+eraZNm1pdRU8Ox48f1w8//CDDMCxzTRYtWqTIyEiNGzdOr7322mP3ffXVV/X222+rWLFicnNz05IlS3T58mWrierlypXT1KlTNXz4cBUoUED+/v42vSmJVahQIQUHB2vnzp3Knj27Zs6cqcuXL1t9Eaxbt67y5Mmj4OBgffLJJ3J1ddXMmTPl5+en8+fPWx0vsbGlS5dOo0ePVocOHVSjRg21atXKskR2YGCgevbsadfjSUj16tU1f/58lSxZ0nJl8MUXX5S3t7eOHz+u1q1bP/EYcT1F3bp1U7169eTq6mr1vNgra9as+umnn9SwYUO9+OKLeu+991SsWDFdunRJoaGhOnnypCZOnBjvEvTR0dGW18yxY8c0ZcoUVatWzepvPTlfL0/SqFEjff/99/L19VWxYsW0bds2rV692rLkc1IVKFBAc+bMUatWrVSyZEkFBwcrKChIZ8+e1YwZM3Tjxg3Nnz/f6n2nZcuW+t///qdmzZqpW7duunPnjqZOnapChQpZTVKuW7eu3N3d1bhxY3Xq1EmRkZH69ttv5e/vr7CwMLviLVOmjFxdXTV69GiFh4fLw8PD8jtEj2rVqpX8/PxUsGBBy5X0OHXq1FH27NkTfT6T0u4HH3yg6dOnq3379tq9e7cCAwP1008/acuWLZowYcITe6qfRzVq1FCnTp0UEhKiffv2qW7dukqXLp1OnDihRYsWaeLEiXrrrbc0e/ZsTZkyRc2aNVP+/Pl169Ytffvtt8qYMeNjk5aEjBkzRvXr11flypUVHBxsWSLb19fX6vd3HhX3O2chISFq1KiRGjRooL1792r58uU2PQfFihVTzZo1Va5cOWXJkkW7du3STz/9ZJX4p7Rn/dnUvn17DRo0SKNHj1bTpk31zjvvaOHChercubPWrVunqlWrKjY2VkePHtXChQu1YsUKlS9fPt5jJeXz63HGjRtns8S/i4uLPv30UzVq1Eiff/65OnTooCpVqujvv//W3LlzrXr5Hta6dWv17dtXS5Ys0Ycffmiz1Pwnn3yiX3/9VY0aNbIsw3/79m39/fff+umnn3T27Fm75kAiEZ7xanTPhCRjyZIllvtxy/B6e3tb3dzc3Iy3337bMAzDeP/99w1JxrFjxyz77d6925BkHD169Fk/hKfSuHFjw9PT07h9+3aCddq3b2+kS5fOuHbtmmEYhvHff/8ZXbt2NV544QXD3d3dyJ07t9GuXTvLdsN4sGRosWLFDDc3N6ulchNaotZsNhsBAQGGJGP48OHxbh85cqSRN29ew8PDwyhbtqyxbNmyeI+3detWo1y5coa7u7vVMp/xLYd5//59Y+jQoUZQUJCRLl06IyAgwOjfv79x7949q3p58+aNdynSJy1tG0cPLUHq4uJiZMqUyShbtqzRvXt349ChQzb1H12G+dq1a0aXLl2MIkWKGN7e3oavr69RqVIlY+HChVb7Xbp0yWjYsKHh4+NjSLLEFrfkZ3zLvya0RHbDhg2NFStWGKVKlTI8PDyMIkWKGIsWLbLZf/fu3UalSpUMd3d3I0+ePMa4cePiPWZCsSW0JPKCBQuMsmXLGh4eHkaWLFmMNm3aGP/8849VnXbt2hne3t42MSW09Gl8vv76a0OS8eGHH1qV165d25BkrFmzxqo8viWyY2JijI8//tjw8/MzTCaTpe24uvEtH/vwa/NJzpw5Y7z//vtGnjx5jHTp0hnZsmUzXn/9dZtlYQ3j/57PDRs2GB988IGROXNmI0OGDEabNm2slus1jKQ9JzVq1DCKFy9u015Cfxt6ZFn4GzduGB06dDCyZctmZMiQwahXr55x9OhRm2VrE7NE9sP+/vtvo3Xr1kaOHDkMFxcXQ5Lh6ekZ79+VYRjGypUrjRIlShju7u5G4cKFjR9++CHe18uvv/5qlCpVyvD09DQCAwON0aNHGzNnzkzwb+VR8b03fPvtt0a+fPkMV1dXq8f4aN2H3y8evcXtk9jzmZR2DcMwLl++bDmuu7u7UbJkSZufO0iu17VhJG6J7MS0k9B7QZxvvvnGKFeunOHl5WX4+PgYJUuWNPr27WtcvHjRMAzD2LNnj9GqVSsjT548hoeHh+Hv7280atTI2LVrl92Pe/Xq1UbVqlUNLy8vI2PGjEbjxo2Nw4cPW9WJ770yNjbWGDp0qJEzZ07Dy8vLqFmzpnHw4EGb53b48OFGxYoVjUyZMhleXl5GkSJFjBEjRlgtjR+fuL+x+N7PE3o8CS0H/bSfTY9rP6GflRgyZIjV6zg6OtoYPXq0Ubx4ccPDw8PInDmzUa5cOWPo0KFGeHi4Zb/4/jYS+/kVn7hzEt/N1dXVMIwHS2T37t3b8lxWrVrV2LZt22O/OzRo0MCQZGzdujXe7bdu3TL69+9vFChQwHB3dzeyZctmVKlSxfjyyy8tz/3jXquwj8kwnvGsv2fAZDJpyZIlliEHCxYsUJs2bXTo0CGbyY8ZMmRQjhw5NHjwYI0cOdJqqMndu3eVPn16rVy5Msk/JAkAySXuRyF37tyZ4BXQtGzOnDlq37692rZtqzlz5jg6HABIkmbNmunvv//WyZMnHR0KHuIUw+HKli2r2NhYXblyJcEVoapWraqYmBidOnXKMqY0bsJ4apw4CgBpxbvvvquwsDD169dPuXPn1siRIx0dEgAkSlhYmH7//ffHzhGGY6SZnqDIyEhLhl22bFmNGzdOtWrVUpYsWZQnTx61bdtWW7Zs0dixY1W2bFldvXpVa9asUalSpdSwYUOZzWZVqFBBGTJk0IQJE2Q2m9WlSxdlzJhRK1eudPCjA+DMnL0nCABSmzNnzmjLli367rvvtHPnTp06deqJP1yLZyvNrA63a9culS1b1rJ+fa9evVS2bFnLCkuzZs3Su+++q969e6tw4cJq2rSpdu7caVkq1cXFRb/99puyZcuml19+WQ0bNlTRokXj/Y0dAAAAICEbNmzQO++8ozNnzmj27NkkQM+hNNMTBAAAAACJkWZ6ggAAAAAgMUiCAAAAADiVVL06nNls1sWLF+Xj4yOTyeTocAAAAAA4iGEYunXrlnLlyiUXl8f39aTqJOjixYsKCAhwdBgAAAAAnhMXLlxQ7ty5H1snVSdBPj4+kh480IwZMzo4GgAAAMC5lBqyQmZDcjFJB4bUc2gsERERCggIsOQIj5Oqk6C4IXAZM2YkCQIAAACeMReP9NL/T4Kel+/jiZkmw8IIAAAAAJwKSRAAAAAAp0ISBAAAAMCppOo5QQCSV2xsrO7fv+/oMADAqbi6usrNzY2f+0CqtOzjarofa1Y619TVt0ISBECSFBkZqX/++UeGYTg6FABwOunTp1fOnDnl7u7u6FCAJCmWy9fRIdiFJAiAYmNj9c8//yh9+vTy8/PjaiQAPCOGYSg6OlpXr17VmTNnVLBgwSf+yCOAp0cSBED379+XYRjy8/OTl5eXo8MBAKfi5eWldOnS6dy5c4qOjpanp6ejQwLSPJIgABb0AAGAY9D7g9Sqx/x9ioy6rwwe6TShZRlHh5NoJEEAAAAA7PLr/n9l/v8/lpqakiAuOwAAAABwKvQEAUjQ+FXHn2l7PesUeqbtnT17VkFBQdq7d6/KlCmTqH1CQ0PVo0cP3bx506FxAAAA+9ETBCDVu3Dhgjp27KhcuXLJ3d1defPmVffu3fXff/89dr+AgACFhYWpRIkSiW6rRYsWOn782SaHAAAgeZEEAUjVTp8+rfLly+vEiRP68ccfdfLkSU2bNk1r1qxR5cqVdf369Xj3i46Olqurq3LkyCE3t8R3int5ecnf3z+5wgcAAA5AEgQgVevSpYvc3d21cuVK1ahRQ3ny5FH9+vW1evVq/fvvvxowYIAkKTAwUMOGDdO7776rjBkz6oMPPtDZs2dlMpm0b98+y/F+/fVXFSxYUJ6enqpVq5Zmz54tk8lkGf4WGhqqTJkyWeoPGTJEZcqU0ffff6/AwED5+vqqZcuWunXrlqXOn3/+qWrVqilTpkzKmjWrGjVqpFOnTj2L0wMAAOJBEgQg1bp+/bpWrFihjz76yOb3jXLkyKE2bdpowYIFMgxDkvTll1+qdOnS2rt3rwYOHGhzvDNnzuitt95S06ZNtX//fnXq1MmSRD3OqVOntHTpUi1btkzLli3Thg0bNGrUKMv227dvq1evXtq1a5fWrFkjFxcXNWvWTGaz+SnPAAAAsAcLIwBItU6cOCHDMFS0aNF4txctWlQ3btzQ1atXJUmvvPKKevfubdl+9uxZq/rTp09X4cKFNWbMGElS4cKFdfDgQY0YMeKxcZjNZoWGhsrHx0eS9M4772jNmjWW/d58802r+jNnzpSfn58OHz6cpPlIAAAgedATBCDVi+vpeZLy5cs/dvuxY8dUoUIFq7KKFSs+8biBgYGWBEiScubMqStXrljunzhxQq1atVK+fPmUMWNGBQYGSpLOnz+fqLgBAEDyIgkCkGoVKFBAJpNJR44ciXf7kSNHlDlzZvn5+UmSvL29UySOdOnSWd03mUxWQ90aN26s69ev69tvv9X27du1fft2SQ8WZwAAIDXL4OEmd1cXZfBIXQPMHJoExcbGauDAgQoKCpKXl5fy58+vYcOGJfqqLgDnljVrVtWpU0dTpkzR3bt3rbZdunRJc+fOVYsWLWQymRJ1vMKFC2vXrl1WZTt37nyqGP/77z8dO3ZMn332mV599VXLED0AANKCA0Pq6fiI+jowpJ6jQ0kSh6Zso0eP1tSpUzV79mwVL15cu3btUocOHeTr66tu3bo5MjS7JPTDks/6ByABZzJ58mRVqVJF9erV0/DhwxUUFKRDhw7pk08+0QsvvPDE+TwP69Spk8aNG6f//e9/Cg4O1r59+xQaGipJiU6kHpU5c2ZlzZpV33zzjXLmzKnz58+rX79+dh0LAAAkD4cmQVu3blWTJk3UsGFDSQ/G1f/444/asWOHI8MC8P+lhgS+YMGC2rVrlwYPHqy3335b169fV44cOdS0aVMNHjxYWbJkSfSxgoKC9NNPP6l3796aOHGiKleurAEDBujDDz+Uh4eHXfG5uLho/vz56tatm0qUKKHChQvrq6++Us2aNe06HgAAeHomw4Fjz0aOHKlvvvlGK1euVKFChbR//37VrVtX48aNU5s2bWzqR0VFKSoqynI/IiJCAQEBCg8PV8aMGZ9l6PGiJwip1b1793TmzBkFBQXJ09PT0eE8V0aMGKFp06bpwoULjg4FQBrG+zDw9CIiIuTr65uo3MChPUH9+vVTRESEihQpIldXV8XGxmrEiBHxJkCSFBISoqFDhz7jKAE4kylTpqhChQrKmjWrtmzZojFjxqhr166ODgsAgOdStdFrFRkVowwebtr8v1ccHU6iOTQJWrhwoebOnat58+apePHi2rdvn3r06KFcuXKpXbt2NvX79++vXr16We7H9QQBQHI5ceKEhg8fruvXrytPnjzq3bu3+vfv7+iwAAB4Ll28eVdmQ4q4e9/RoSSJQ5OgTz75RP369VPLli0lSSVLltS5c+cUEhISbxLk4eFh97h8AEiM8ePHa/z48Y4OAwAApCCHLpF9584dubhYh+Dq6mr1+xoAAAAAkJwc2hPUuHFjjRgxQnny5FHx4sW1d+9ejRs3Th07dnRkWAAAAADSMIcmQZMmTdLAgQP10Ucf6cqVK8qVK5c6deqkQYMGOTIsAAAAAGmYQ5MgHx8fTZgwQRMmTHBkGAAAAACciEPnBAEAAADAs0YSBAAAAMCpkAQBQApbv369TCaTbt68maLtBAYGMrz4KdWsWVM9evRI9uMOGTJEZcqUSfbjAgDsQxIEIFW7cOGCOnbsqFy5csnd3V158+ZV9+7d9d9//zkknvi+RFepUkVhYWHy9fVNljZCQ0OVKVMmm/KdO3fqgw8+SJY24rRv314mk8nm9tprryVrO4mNo3PnzjbbunTpIpPJpPbt2yf6eM8qMU2ss2fPymQyydXVVf/++6/VtrCwMLm5uclkMuns2bOW8iVLluill16Sr6+vfHx8VLx4cavXXmhoaLzPnaenZ5Jii42N1cCBAxUUFCQvLy/lz59fw4YNk2EYj91v/fr1evHFF+Xh4aECBQooNDTUps7XX3+twMBAeXp6qlKlStqxY0eSYgPgeBWDsqh4royqGJTF0aEkCUkQgFTr9OnTKl++vE6cOKEff/xRJ0+e1LRp07RmzRpVrlxZ169fd3SIkiR3d3flyJFDJpMpRdvx8/NT+vTpk/24r732msLCwqxuP/74Y4L179+3/dXw6Ohou9p+eL+AgADNnz9fd+/etZTdu3dP8+bNU548eew6/vPmhRde0Jw5c6zKZs+erRdeeMGqbM2aNWrRooXefPNN7dixQ7t379aIESNszn3GjBltnrtz584lKabRo0dr6tSpmjx5so4cOaLRo0friy++0KRJkxLc58yZM2rYsKFq1aqlffv2qUePHnrvvfe0YsUKS50FCxaoV69eGjx4sPbs2aPSpUurXr16unLlSpLiA+BY8z+orN+7Vdf8Dyo7OpQkIQkCkGp16dJF7u7uWrlypWrUqKE8efKofv36Wr16tf79918NGDDAUtdkMmnp0qVW+2fKlMnq6vT//vc/FSpUSOnTp1e+fPk0cOBAqy+VcUOavv/+ewUGBsrX11ctW7bUrVu3JD3ordiwYYMmTpxouep+9uxZm16HmjVrxnuFPu4q/7hx41SyZEl5e3srICBAH330kSIjIyU9uLreoUMHhYeHW/YbMmSIJNvhcOfPn1eTJk2UIUMGZcyYUW+//bYuX76c6McTx8PDQzly5LC6Zc6c2ercTp06Va+//rq8vb01YsQIy7G/++47BQUFWXofEhvTo/tJ0osvvqiAgAAtXrzYUrZ48WLlyZNHZcuWtYrZbDYrJCTE0ntRunRp/fTTT5Ie9LrUqlVLkpQ5c2abXiSz2ay+ffsqS5YsypEjh+X8Jva8StKoUaOUPXt2+fj4KDg4WPfu3VNitGvXTrNmzbIqmzVrltq1a2dV9ttvv6lq1ar65JNPVLhwYRUqVEhNmzbV119/bVXPZDLZPHfZs2dPVCxxtm7dqiZNmqhhw4YKDAzUW2+9pbp16z6212batGkKCgrS2LFjVbRoUXXt2lVvvfWWxo8fb6kzbtw4vf/+++rQoYOKFSumadOmKX369Jo5c2aS4gMAe5AEAUjQd5tO66WRa554e2/2Tpt935u9M1H7frfptF2xXb9+XStWrNBHH30kLy8vq205cuRQmzZttGDBgicO2XmYj4+PQkNDdfjwYU2cOFHffvut1Zc2STp16pSWLl2qZcuWadmyZdqwYYNGjRolSZo4caIqV66s999/33LVPSAgwKadxYsXW12Zf+ONN1S4cGHLl1MXFxd99dVXOnTokGbPnq21a9eqb9++kh4MrZswYYLVFf4+ffrYtGE2m9WkSRNdv35dGzZs0KpVq3T69Gm1aNEi0Y8nKYYMGaJmzZrp77//tvzg9cmTJ/Xzzz9r8eLF2rdvX6JjenS/h3Xs2NEqSZg5c6Y6dOhgE09ISIjmzJmjadOm6dChQ+rZs6fatm2rDRs2KCAgQD///LMk6dixYwoLC9PEiRMt+86ePVve3t7avn27vvjiC33++edatWpVos/rwoULNWTIEI0cOVK7du1Szpw5NWXKlESdx9dff103btzQ5s2bJUmbN2/WjRs31LhxY6t6OXLk0KFDh3Tw4MFEHTchcUPmHqdKlSpas2aNjh8/Lknav3+/Nm/erPr16ye4z7Zt21S7dm2rsnr16mnbtm2SHvTw7d6926qOi4uLateubakDACnJob8TBOD5dutejC5FPPkKds5MtnMM/rsdnah9b92LsSu2EydOyDAMFS1aNN7tRYsW1Y0bN3T16lX5+/sn6pifffaZ5f+BgYHq06eP5s+fb0lApAdfgkNDQ+Xj4yNJeuedd7RmzRqNGDFCvr6+cnd3V/r06ZUjR44E28mS5f/GTY8fP15r167V9u3bLcncw/M6AgMDNXz4cHXu3FlTpkyRu7u7fH19LVf4E7JmzRr9/fffOnPmjCURmzNnjooXL66dO3eqQoUKT3w8cZYtW6YMGTJYHf/TTz/Vp59+arnfunVrm2QkOjpac+bMkZ+fnyRp1apViYrp0f0e1rZtW/Xv398ypGvLli2aP3++1q9fb6kTFRWlkSNHavXq1apc+cHwjHz58mnz5s2aPn26atSoYXkO/P39beZXlSpVSoMHD5YkFSxYUJMnT9aaNWtUp06dRJ3XCRMmKDg4WMHBwZKk4cOHa/Xq1YnqDUqXLp3atm2rmTNnqlq1apo5c6batm2rdOnSWdX7+OOPtWnTJpUsWVJ58+bVSy+9pLp166pNmzby8PCw1AsPD7d57qpXr67ly5dLknx9fVW4cOHHxtSvXz9FRESoSJEicnV1VWxsrEaMGKE2bdokuM+lS5dsepyyZ8+uiIgI3b17Vzdu3FBsbGy8dY4ePfrYeAAgOZAEAUiQj6ebcmR88iTqrN7u8ZYlZl8fz6d7G3pST4+7u21sCVmwYIG++uornTp1SpGRkYqJiVHGjBmt6gQGBloSBknKmTOn3XMYli9frn79+um3335ToUKFLOWrV69WSEiIjh49qoiICMXExOjevXu6c+dOouf8HDlyRAEBAVY9UcWKFVOmTJl05MgRS8KRmMdTq1YtTZ061ars4UROksqXL28TQ968ea0SmcTG9Oh+D/Pz81PDhg0VGhoqwzDUsGFDZcuWzarOyZMndefOHdWpU8eqPDo62mbYXHxKlSpldf/hc5KYx3DkyBGbBRwqV66sdevWPbFt6UFvV5UqVTRy5EgtWrRI27ZtU0yM9cUCb29v/f777zp16pTWrVunv/76S71799bEiRO1bds2y+vEx8dHe/bssdr34Z7TZs2aqVmzZo+NZ+HChZo7d67mzZun4sWLW+b45MqVy2aYHgDnk6//7zIbkotJOh3S0NHhJBpJEIAEvVc9n96rns+ufb9rVyGZo7FWoEABmUwmHTlyJN4vcUeOHJGfn5/lKr/JZLJJmB6e77Nt2za1adNGQ4cOVb169eTr66v58+dr7NixVvs8ekXeZDLJbDYnOf7Dhw+rZcuWGjVqlOrWrWspP3v2rBo1aqQPP/xQI0aMUJYsWbR582YFBwcrOjo62Rc+SMzj8fb2VoECBR57HG9v70SVJcaT9uvYsaO6du0qSTZzYCRZ5k/9/vvvNgsKPNxLkpDkeo7tVbJkSRUpUkStWrVS0aJFVaJECZthgXHy58+v/Pnz67333tOAAQNUqFAhLViwwNIr5+Li8sTn7kk++eQT9evXTy1btrTEd+7cOYWEhCSYBOXIkcNmntTly5eVMWNGeXl5ydXVVa6urvHWeVwPJwAkF+YEAUiVsmbNqjp16mjKlClWq4VJD4bizJ0712qyu5+fn8LCwiz3T5w4oTt37ljub926VXnz5tWAAQNUvnx5FSxYMMmraEkPep5iY2MfW+fatWtq3Lix3nzzTfXs2dNq2+7du2U2mzV27Fi99NJLKlSokC5evJjkNooWLaoLFy7owoULlrLDhw/r5s2bKlasWBIfVfJIrphee+01RUdH6/79+6pXr57N9mLFisnDw0Pnz59XgQIFrG5xPThxPYRPOo/2PIaiRYtq+/btVvv99ddfSWqnY8eOWr9+vWV+VWIEBgYqffr0un37dpLaepI7d+7IxcX664Krq+tjE8PKlStrzZo1VmWrVq2yDE90d3dXuXLlrOqYzWbLyo4AkNJIggCkWpMnT1ZUVJTq1aunjRs36sKFC/rzzz9Vp04dFSpUSIMGDbLUfeWVVzR58mTt3btXu3btUufOna2u+BcsWFDnz5/X/PnzderUKX311VdasmRJkmMKDAzU9u3bdfbsWV27di3eL4pvvvmm0qdPryFDhujSpUuWW2xsrAoUKKD79+9r0qRJOn36tL7//ntNmzbNpo3IyEitWbNG165ds0rm4tSuXVslS5ZUmzZttGfPHu3YsUPvvvuuatSoEe/QtceJioqyivPSpUu6du1a0k5MMsbk6uqqI0eO6PDhw3J1dbXZ7uPjoz59+qhnz56aPXu2Tp06pT179mjSpEmaPXu2pAdD7kwmk5YtW6arV69aeo+S4zF0795dM2fO1KxZs3T8+HENHjxYhw4dsjrOkiVLVKRIkQTbef/993X16lW999578W4fMmSI+vbtq/Xr1+vMmTPau3evOnbsqPv371sNAzQMw+a5u3TpkuV1+aQ4JKlx48YaMWKEfv/9d509e1ZLlizRuHHjrHpg+/fvr3fffddyv3Pnzjp9+rT69u2ro0ePasqUKVq4cKFV0t+rVy99++23mj17to4cOaIPP/xQt2/fjnehCwBIbiRBAFKtggULaufOncqXL5/efvtt5c2bV/Xr11ehQoW0ZcsWqwnhY8eOVUBAgKpXr67WrVurT58+VkPLXn/9dfXs2VNdu3ZVmTJltHXrVg0cODDJMfXp00eurq4qVqyY/Pz8dP78eZs6Gzdu1MGDB5U3b17lzJnTcrtw4YJKly6tcePGafTo0SpRooTmzp2rkJAQq/2rVKmizp07q0WLFvLz89MXX3xh04bJZNIvv/yizJkz6+WXX1bt2rWVL18+LViwIMmP6c8//7SKM2fOnKpWrVqSj5OcMWXMmNFmvtbDhg0bpoEDByokJERFixbVa6+9pt9//11BQUGSHvwez9ChQ9WvXz9lz57dMrwuOR5DixYtNHDgQPXt21flypXTuXPn9OGHH1odJzw8XMeOHUuwHTc3N2XLlk1ubvGPWq9Ro4ZOnz6td999V0WKFFH9+vV16dIlrVy50mqhg4iICJvn7uE5Tk+KQ5ImTZqkt956Sx999JGKFi2qPn36qFOnTho2bJilTlhYmNVrPSgoSL///rtWrVql0qVLa+zYsfruu++seu5atGihL7/8UoMGDVKZMmW0b98+/fnnn0lewhsA7GEykrJ+7HMmIiJCvr6+Cg8Pf+yH4bMyftXxeMt71ikUbznwvLh3757OnDlj87ssqdHgwYM1btw4rVq1Si+99JKjwwGARElL78NwLs/TwghJyQ1YGAFAmjJ06FAFBgbqr7/+UsWKFW3mMgAAAJAEAUhzmFMAAAAeh0ukAAAAAJwKSRAAAAAAp8JwOAAAAAB2GdCgqO7ej5VXOtufLHiekQQBAAAAsEtw9XyODsEuDIcDAAAA4FRIggAAAAA4FYbDAQAAALDLjE2nLXOCUtPQOHqCACCFrV+/XiaTSTdv3kzRdgIDAzVhwoQUbSOtq1mzpnr06JHsxx0yZIjKlCmT7McFAEcb8ccRfbnyuEb8ccTRoSQJSRCAVO3ChQvq2LGjcuXKJXd3d+XNm1fdu3fXf//955B44vsSXaVKFYWFhcnX1zdZ2ggNDVWmTJlsynfu3KkPPvggWdqI0759e5lMJpvba6+9lqztJDaOzp0722zr0qWLTCaT2rdvn+jjPavENLHOnj0rk8kkf39/3bp1y2pbmTJlNGTIEKuyQ4cO6e2335afn588PDxUqFAhDRo0SHfu3LE59t69e9W8eXNlz55dnp6eKliwoN5//30dP37cqu19+/bFG9ujr7fQ0FDL68DFxUW5c+dWhw4ddOXKFUudh18rvr6+qlq1qtauXWvZ3r59ezVt2tTqvslk0qhRo6zaXrp0qUwmk1WZYRj69ttvVblyZWXMmFEZMmRQ8eLF1b17d508eTLex/A4EyZMUOHCheXl5aWAgAD17NlT9+7de+w+Bw4cUPXq1eXp6amAgAB98cUXNnUWLVqkIkWKyNPTUyVLltQff/yR5NgApBySIACp1unTp1W+fHmdOHFCP/74o06ePKlp06ZpzZo1qly5sq5fv+7oECVJ7u7uypEjh82XueTm5+en9OnTJ/txX3vtNYWFhVndfvzxxwTr379/36YsOjrarrYf3i8gIEDz58/X3bt3LWX37t3TvHnzlCdPHruO/7y5deuWvvzyy8fW+euvv1SpUiVFR0fr999/1/HjxzVixAiFhoaqTp06Vuds2bJleumllxQVFaW5c+fqyJEj+uGHH+Tr66uBAwfaHWfGjBkVFhamf/75R99++62WL1+ud955x6rOrFmzFBYWpi1btihbtmxq1KiRTp8+neAxPT09NXr0aN24cSPBOoZhqHXr1urWrZsaNGiglStX6vDhw5oxY4Y8PT01fPjwJD2OefPmqV+/fho8eLCOHDmiGTNmaMGCBfr0008T3CciIkJ169ZV3rx5tXv3bo0ZM0ZDhgzRN998Y6mzdetWtWrVSsHBwdq7d6+aNm2qpk2b6uDBg0mKD0DKIQkCkGp16dJF7u7uWrlypWrUqKE8efKofv36Wr16tf79918NGDDAUtdkMmnp0qVW+2fKlEmhoaGW+//73/9UqFAhpU+fXvny5dPAgQOtvtDHDWn6/vvvFRgYKF9fX7Vs2dJy5b59+/basGGDJk6caLkKfvbsWZteh5o1a8bbu3L27FlJ0rhx41SyZEl5e3srICBAH330kSIjIyU96MHo0KGDwsPDLfvF9RI8Ohzu/PnzatKkiTJkyKCMGTPq7bff1uXLlxP9eOJ4eHgoR44cVrfMmTNbndupU6fq9ddfl7e3t0aMGGE59nfffaegoCB5enomKaZH95OkF198UQEBAVq8eLGlbPHixcqTJ4/Kli1rFbPZbFZISIiCgoLk5eWl0qVL66effpL0oOejVq1akqTMmTPb9CKZzWb17dtXWbJkUY4cOWx6YZ70GCRp1KhRyp49u3x8fBQcHPzEnoU4H3/8scaNG2fVq/IwwzAUHBysokWLavHixapYsaLy5s2r5s2b67ffftO2bds0fvx4SdKdO3fUoUMHNWjQQL/++qtq166toKAgVapUSV9++aWmT5+eqJjiYzKZlCNHDuXKlUv169dXt27dtHr1aqsENVOmTMqRI4dKlCihqVOn6u7du1q1alWCx6xdu7Zy5MihkJCQBOssWLBA8+fP14IFCzRw4EC99NJLypMnj1566SWNHj1as2bNStLj2Lp1q6pWrarWrVsrMDBQdevWVatWrbRjx44E95k7d66io6M1c+ZMFS9eXC1btlS3bt00btw4S52JEyfqtdde0yeffKKiRYtq2LBhevHFFzV58uQkxQcg5ZAEAUjQd5tO66WRa554e2/2Tpt935u9M1H7frcp4SvDj3P9+nWtWLFCH330kby8vKy25ciRQ23atNGCBQtkGEaij+nj46PQ0FAdPnxYEydO1Lfffmv5Qhnn1KlTWrp0qZYtW6Zly5Zpw4YNliE8EydOVOXKlfX+++9bekwCAgJs2lm8eLFVr8obb7yhwoULK3v27JIkFxcXffXVVzp06JBmz56ttWvXqm/fvpIeDK2bMGGC5Up8WFiY+vTpY9OG2WxWkyZNdP36dW3YsEGrVq3S6dOn1aJFi0Q/nqQYMmSImjVrpr///lsdO3aUJJ08eVI///yzFi9erH379iU6pkf3e1jHjh2tvujOnDlTHTp0sIknJCREc+bM0bRp03To0CH17NlTbdu21YYNGxQQEKCff/5ZknTs2DGFhYVp4sSJln1nz54tb29vbd++XV988YU+//xzy5f3xDyGhQsXasiQIRo5cqR27dqlnDlzasqUKYk6j61atVKBAgX0+eefx7t93759Onz4sHr16iUXF+uP8NKlS6t27dqWXroVK1bo2rVrltfOo+IbUmkvLy8vmc1mxcTEJLhdenyPoKurq0aOHKlJkybpn3/+ibfOjz/+qMKFC+v111+Pd/vDva1xFx/iLi7Ep0qVKtq9e7cl6Tl9+rT++OMPNWjQIMF9tm3bppdfflnu7u6Wsnr16unYsWOWXqxt27apdu3aVvvVq1dP27ZtS/C4AJ4tVocDkKBb92J0KeLJV7BzZvK0KfvvdnSi9r11L/4vTU9y4sQJGYahokWLxru9aNGiunHjhq5evSp/f/9EHfOzzz6z/D8wMFB9+vTR/Pnzrb5Ems1mhYaGysfHR5L0zjvvaM2aNRoxYoR8fX3l7u6u9OnTK0eOHAm2kyVLFsv/x48fr7Vr12r79u2WL4oPzykKDAzU8OHD1blzZ02ZMkXu7u7y9fW1XIlPyJo1a/T333/rzJkzlkRszpw5Kl68uHbu3KkKFSo88fHEWbZsmTJkyGB1/E8//dRqyFDr1q1tkpHo6GjNmTNHfn5+kqRVq1YlKqZH93tY27Zt1b9/f507d06StGXLFs2fP1/r16+31ImKitLIkSO1evVqVa5cWZKUL18+bd68WdOnT1eNGjUsz4G/v79NMlCqVCkNHjxYklSwYEFNnjxZa9asUZ06dRJ1XidMmKDg4GAFBwdLkoYPH67Vq1cnqjcobl5M48aN1bNnT+XPn99qe9w8nse97jdv3izpwd+IJBUpUuSJ7T6NEydOaNq0aSpfvrzldfSwO3fu6LPPPpOrq6tq1Kjx2GM1a9ZMZcqU0eDBgzVjxgyb7cePH1fhwoWtynr06KHvvvtO0oPELi6BSp8+vQoXLqx06dIl2F7r1q117do1VatWTYZhKCYmRp07d37scLhLly4pKCjIqizuAsalS5eUOXNmXbp0yVL2cJ1Lly495tEDeJZIggAkyMfTTTky2iY4j8rq7R5vWWL29fF8urehJ/X0PHy19kkWLFigr776SqdOnVJkZKRiYmKUMWNGqzqBgYFWX/Ry5syZ4NClJ1m+fLn69eun3377TYUKFbKUr169WiEhITp69KgiIiIUExOje/fu6c6dO4me83PkyBEFBARY9UQVK1ZMmTJl0pEjRywJR2IeT61atTR16lSrsocTOUkqX768TQx58+a1SmQSG9Oj+z3Mz89PDRs2VGhoqAzDUMOGDZUtWzarOidPntSdO3dUp04dq/Lo6GibYXPxKVWqlNX9h89JYh7DkSNHbBZwqFy5statW/fEtqUHPQbVqlXTwIEDNW/evHjrJKaHMym9oEkVHh6uDBkyyGw26969e6pWrZolEYnTqlUrubq66u7du/Lz89OMGTNszm18Ro8erVdeeSXeHs74DBgwQF27dtXixYs1cuRIS3nFihV19OjRx+67fv16jRw5UlOmTFGlSpV08uRJde/eXcOGDXuqOVMAnn8kQQAS9F71fHrPzjX/v2tXIZmjsVagQAGZTCYdOXJEzZo1s9l+5MgR+fn5Wa7ym0wmmy+FD8/32bZtm9q0aaOhQ4eqXr168vX11fz58zV27FirfR69qmwymWQ2m5Mc/+HDh9WyZUuNGjVKdevWtZSfPXtWjRo10ocffqgRI0YoS5Ys2rx5s4KDgxUdHZ3sCx8k5vF4e3urQIECjz2Ot7d3osoS40n7dezYUV27dpUkff311zbb4+ZP/f7773rhhRestnl4eDyx/eR6jp/GqFGjVLlyZX3yySdW5XHJ8pEjR+JN6I4cOWKpE/fv0aNHLT1iycXHx0d79uyRi4uLcubMaTMkVXrQy1m7dm35+vommNTG5+WXX1a9evXUv39/mxX/ChYsqGPHjlmV+fn5yc/PL9E9vg8bOHCg3nnnHb333nuSpJIlS+r27dv64IMPNGDAAJshh9KD4baPzgGLux/XO5tQncf13gJ4tpgTBCBVypo1q+rUqaMpU6ZYTcaWHgxJmTt3rtUXKD8/P4WFhVnunzhxwmo54a1btypv3rwaMGCAypcvr4IFC1qGXCWFu7u7YmNjH1vn2rVraty4sd5880317NnTatvu3btlNps1duxYvfTSSypUqJAuXryY5DaKFi2qCxcu6MKFC5ayw4cP6+bNmypWrFgSH1XySK6YXnvtNUVHR+v+/fuqV6+ezfZixYrJw8ND58+fV4ECBaxucT04cT2ETzqP9jyGokWLavv27Vb7/fXXX0lqp2LFinrjjTfUr18/q/IyZcqoSJEiGj9+vE1itn//fq1evVqtWrWSJNWtW1fZsmWLd/lmSU+1PLiLi4sKFCigfPnyxZsASQ8SgQIFCiQpAYozatQoy0IPD2vVqpWOHTumX375xa64H3Xnzh2bRMfV1VVSwj1plStX1saNG60uoqxatUqFCxe2LBhSuXJlrVmzxmq/VatWJXsyCsB+JEEAUq3JkycrKipK9erV08aNG3XhwgX9+eefqlOnjuV3U+K88sormjx5svbu3atdu3apc+fOVlf8CxYsqPPnz2v+/Pk6deqUvvrqKy1ZsiTJMQUGBmr79u06e/asrl27Fm8Pwptvvqn06dNryJAhunTpkuUWGxurAgUK6P79+5o0aZJOnz6t77//XtOmTbNpIzIyUmvWrNG1a9fi/W2Y2rVrq2TJkmrTpo327NmjHTt26N1331WNGjXiHbr2OFFRUVZxXrp0SdeuXUvaiUnGmFxdXXXkyBEdPnzY8oX1YT4+PurTp4969uyp2bNn69SpU9qzZ48mTZqk2bNnS3ow5M5kMmnZsmW6evWqpfcoOR5D9+7dNXPmTM2aNUvHjx/X4MGDdejQIavjLFmy5IlzdUaMGKG1a9da9XyYTCbNmDFDhw8f1ptvvqkdO3bo/PnzWrRokRo3bqzKlStb5pR5e3vru+++0++//67XX39dq1ev1tmzZ7Vr1y717dvXZsjesWPHtG/fPqtbfMudPwtx5/irr76yKm/ZsqXeeusttWzZUp9//rnlb23Dhg1asGCB1ethx44dKlKkiP79998E22ncuLGmTp2q+fPn68yZM1q1apUGDhyoxo0bW441efJkvfrqq5Z9WrduLXd3dwUHB+vQoUNasGCBJk6cqF69elnqdO/eXX/++afGjh2ro0ePasiQIdq1a5elBxOA45EEAUi1ChYsqJ07dypfvnx6++23lTdvXtWvX1+FChXSli1brCbzjx07VgEBAapevbpat26tPn36WA0te/3119WzZ0917dpVZcqU0datW+2aE9Cn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lVde0f79+x97XABA0pEEAYCTWbhwoYoUKaLChQurbdu2mjlzpgzDsGz//fff1axZMzVo0EB79+7VmjVrVLFiRUnS4sWLlTt3bn3++ecKCwtTWFiYzfELFSqk8uXLa+7cuVblc+fOVevWrSVJZrNZuXPn1qJFi3T48GENGjRIn376qRYuXChJ6tOnj95++2299tprlnaqVKli09a///6rBg0aqEKFCtq/f7+mTp2qGTNmaPjw4Vb1Zs+eLW9vb23fvl1ffPGFPv/8c61atSrBcxQcHKyFCxfq9u3blrL169frzJkz6tix4xPjt1fz5s115coVLV++XLt379aLL76oV199VdevX3+q4wIAHmEAAJxKlSpVjAkTJhiGYRj37983smXLZqxbt86yvXLlykabNm0S3D9v3rzG+PHjrcpmzZpl+Pr6Wu6PHz/eyJ8/v+X+sWPHDEnGkSNHEjxuly5djDfffNNyv127dkaTJk2s6pw5c8aQZOzdu9cwDMP49NNPjcKFCxtms9lS5+uvvzYyZMhgxMbGGoZhGDVq1DCqVatmdZwKFSoY//vf/xKM5caNG4anp6cxa9YsS9k777xjc5ykxB/feStdurQxePBgwzAMY9OmTUbGjBmNe/fuWdXJnz+/MX369ATbBQAkHT1BAOBEjh07ph07dqhVq1aSJDc3N7Vo0UIzZsyw1Nm3b59effXVp2qnZcuWOnv2rP766y9JD3qBXnzxRRUpUsRS5+uvv1a5cuXk5+enDBky6JtvvtH58+eT1M6RI0dUuXJly7A8SapataoiIyP1zz//WMpKlSpltV/OnDl15cqVBI+bKVMmvfHGG5YhcREREfr5558VHBycrPE/bP/+/YqMjFTWrFmVIUMGy+3MmTM6deqU3ccFANhKuZmlAIDnzowZMxQTE6NcuXJZygzDkIeHhyZPnixfX195eXk9dTs5cuTQK6+8onnz5umll17SvHnz9OGHH1q2z58/X3369NHYsWNVuXJl+fj4aMyYMdq+fftTtx2fdOnSWd03mUwym82P3Sc4OFivvvqqTp48qXXr1snV1VXNmze3O34XFxerYYeSdP/+fcv/IyMjlTNnTq1fv95m32ex/DgAOBOSIABwEjExMZozZ47Gjh2runXrWm1r2rSpfvzxR3Xu3FmlSpXSmjVr1KFDh3iP4+7urtjY2Ce216ZNG/Xt21etWrXS6dOn1bJlS8u2LVu2qEqVKvroo48sZY/2diSmnaJFi+rnn3+WYRiW3qAtW7bIx8dHuXPnfmKMj1OrVi0FBQVp1qxZWrdunVq2bClvb+9Ex/8oPz8/qzlUEREROnPmjOX+iy++qEuXLsnNzU2BgYFPFTsA4PEYDgcATmLZsmW6ceOGgoODVaJECavbm2++aRkSN3jwYP34448aPHiwjhw5or///lujR4+2HCcwMFAbN27Uv//+q2vXriXY3htvvKFbt27pww8/VK1atax6nwoWLKhdu3ZpxYoVOn78uAYOHKidO3da7R8YGKgDBw7o2LFjunbtmlWvSZyPPvpIFy5c0Mcff6yjR4/ql19+0eDBg9WrVy+5uDzdR5zJZFLHjh01depUbdu2zWooXGLif9Qrr7yi77//Xps2bdLff/+tdu3aydXV1bK9du3aqly5spo2baqVK1fq7Nmz2rp1qwYMGKBdu3Y91WMBAFgjCQIAJzFjxgzVrl1bvr6+NtvefPNN7dq1SwcOHFDNmjW1aNEi/frrrypTpoxeeeUV7dixw1L3888/19mzZ5U/f375+fkl2J6Pj48aN26s/fv3q02bNlbbOnXqpDfeeEMtWrRQpUqV9N9//1n1qkjS+++/r8KFC6t8+fLy8/PTli1bbNp44YUX9Mcff2jHjh0qXbq0OnfurODgYH322WdJPT3xat++vcLDw1W8eHFVqlQpSfE/qn///qpRo4YaNWqkhg0bqmnTppYlw6UHSdcff/yhl19+WR06dFChQoXUsmVLnTt3TtmzZ0+WxwMAeMBkPDpAGQD+X3t2QAMAAIAgrH9re8jfggEAcMwJAgAAUkQQAACQIoIAAIAUEQQAAKSIIAAAIEUEAQAAKSIIAABIEUEAAECKCAIAAFJEEAAAkCKCAACAlAGd9q23/SDfIQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(all_activations_relu, bins=100, alpha=0.5, label='Original')\n", + "for method, threshold in optimal_thresholds_relu.items():\n", + " plt.axvline(threshold, linestyle='--', linewidth=2, label=f'{method}: {threshold:.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()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "Df7eKzh4oj5X", + "metadata": { + "id": "Df7eKzh4oj5X" + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACM20lEQVR4nOzdd3gU1fv38c8mpJOElgYEQi9SBcEoCEoApQjYqFItKEi3oNIUCIo0C0WUpmgQvoAUBUMQUKQjgvQqCAlFIKGlkJ3nD57sj2UTNgkJm4X367r2gp05M3Pv7uzJ3HvKmAzDMAQAAAAAyJCLowMAAAAAgLyOxAkAAAAA7CBxAgAAAAA7SJwAAAAAwA4SJwAAAACwg8QJAAAAAOwgcQIAAAAAO0icAAAAAMAOEicAAAAAsIPECXdV165dFRYW5pBjDx8+XCaTySHHzqpjx47JZDJp1qxZuX6sWbNmyWQy6dixY5ZlYWFhatGiRa4fW5LWrFkjk8mkNWvW3JXj3amsfDZpZT/55JPcDywHOfIzcbbzISc0bNhQDRs2vG+Om1l57fuTG/GkV/9mJCwsTF27ds2xY98Nef0cuxuc8XNDxkicYGXy5MkymUyqW7dutvdx6tQpDR8+XDt27Mi5wDLp6tWrGj58eJ676DKZTJZHvnz5VKhQIdWqVUt9+/bVnj17cuw4kydPvivJVnbk5dju1E8//aThw4fn2v6PHz+unj17KiwsTB4eHgoMDFTr1q21fv36O9rvvfCZ7N69W506dVKxYsXk4eGhokWLqlOnTjn6vcoJe/bs0fDhwzN1gXwvHDcjYWFhVvVhRg9nPy/zsrQfJ9Iebm5uKl26tDp37qwjR444OrwsyWoddut55uPjo8qVK2vkyJG6evWqVdmuXbvKZDKpWrVqMgwj3X317t37Tl8CnEw+RweAvGXu3LkKCwvT5s2bdejQIZUtWzbL+zh16pRGjBihsLAw1ahRw2rd9OnTZTabcyhaW1evXtWIESMkyeZXrvfff1/vvPNOrh3bnsaNG6tz584yDEPx8fH666+/NHv2bE2ePFkfffSRBgwYYClbsmRJXbt2TW5ublk6xuTJk1WkSJEs/br14osvql27dvLw8MjSsbIqo9gee+wxXbt2Te7u7rl6/JyS3mfz008/6YsvvsiV5Gn9+vVq1qyZJOmll15S5cqVFRcXp1mzZql+/fqaNGmS3njjjWzt29k/k4ULF6p9+/YqVKiQevTooVKlSunYsWP6+uuvtWDBAs2bN0+tWrVydJiSbiQwI0aMUMOGDW1a3X/55Zd77rgZmThxoi5fvmx5/tNPP+n777/XhAkTVKRIEcvyRx555K7Hdr/p06ePHnroIaWkpGj79u368ssvtXz5cu3atUtFixbNkWPk9jmWnb95aX+LJeny5cv67bffNGTIEP3111+aP3++Tfldu3Zp4cKFevbZZ3MqbDgxEidYHD16VH/88YcWLlyoV199VXPnztWwYcNy9BhZTQRyUr58+ZQvn+NO+fLly6tTp05Wy8aMGaOWLVtq4MCBqlixouUC2WQyydPTM1fjuXLlinx8fOTq6ipXV9dcPdbtuLi45PprzUl347NJc+HCBT333HPy8vLS+vXrVaZMGcu6AQMGqGnTpurXr59q1aqVoxeazvCZHD58WC+++KJKly6tdevWKSAgwLKub9++ql+/vjp16qSdO3eqVKlSDozUPkclqI44buvWra2ex8XF6fvvv1fr1q1tErs7bSVLq+OQvvr16+u5556TJHXr1k3ly5dXnz59NHv2bA0ePDjdbbL6nubFH19u/Vvcs2dPJScna+HChUpMTLSq+7y8vBQaGqoPPvhAzzzzjNN098+s69evy2w258nPKa+iqx4s5s6dq4IFC6p58+Z67rnnNHfu3HTLXbx4Uf3797d0GypevLg6d+6sc+fOac2aNXrooYck3aiIb+12cfMYp5SUFBUqVEjdunWzOUZCQoI8PT01aNAgSVJycrKGDh2qWrVqyd/fXz4+Pqpfv75+/fVXyzbHjh2zXDyNGDHCcuy0VoD0xjhdv35dH374ocqUKSMPDw+FhYXp3XffVVJSklW5tDE/v//+u+rUqSNPT0+VLl1ac+bMydqbfIvChQsrKipK+fLl06hRo6xey63dVeLi4tStWzcVL15cHh4eCgkJUatWrSwXF2FhYdq9e7fWrl1ree1prW5p/ejXrl2r119/XYGBgSpevLjVuvQuUn755RfVqFFDnp6eqly5shYuXGi1PqNxY7fu83axZTSmZf78+apVq5a8vLxUpEgRderUSSdPnrQq07VrV+XPn18nT55U69atlT9/fgUEBGjQoEFKTU297Xs/YMAAFS5c2KoLxhtvvCGTyaRPP/3Usuz06dMymUyaMmWKJNvPpmvXrvriiy8kWXcDudWXX35pOc8eeughbdmy5bbxSdK0adMUFxensWPHWiVN0o0/6LNnz5bJZNIHH3xgWZ723q9bt06vvvqqChcuLD8/P3Xu3FkXLlywlMvqZ9KwYUNVqVJFO3fuVIMGDeTt7a2yZctqwYIFkqS1a9eqbt268vLyUoUKFbRq1SqreP/55x+9/vrrqlChgry8vFS4cGE9//zz2b44Hjt2rK5evaovv/zSKmmSpCJFimjatGm6fPmyxo4da1me0RjL9M7jmTNn6oknnlBgYKA8PDxUuXJlyzlws8zUDbNmzdLzzz8vSXr88cct73fa+3vrOJDbdWdL2yYz72dWjytJZ86cUY8ePRQUFCRPT09Vr15ds2fPtipz81if7JzX2WHvOGl1weHDh9WsWTP5+vqqY8eOkiSz2ayJEyfqgQcekKenp4KCgvTqq69afR8kaevWrWratKmKFCkiLy8vlSpVSt27d89WPJK0evVq1a9fXz4+PipQoIBatWqlvXv32n2thmFo5MiRKl68uLy9vfX4449r9+7dNuVSUlI0YsQIlStXTp6enipcuLDq1aun6Ohou8dIzxNPPCHpxo+o0v99L/bs2aMOHTqoYMGCqlevnqTM/+1M7xxLSkrSsGHDVLZsWXl4eCg0NFRvvfWWzbaS9O2336pOnTry9vZWwYIF9dhjj1lasW5Xh2VVcHCwpSv9zVxcXPT+++9r586dWrRoUbb2favz589r0KBBqlq1qvLnzy8/Pz899dRT+uuvvyxlLl++LB8fH/Xt29dm+3///Veurq6KjIy0LLt48aL69eun0NBQeXh4qGzZsvroo4+sevjc/L2dOHGi5bPLa92a8zpanGAxd+5cPfPMM3J3d1f79u01ZcoUbdmyxZIISTe+zPXr19fevXvVvXt3Pfjggzp37pyWLFmif//9V5UqVdIHH3ygoUOH6pVXXlH9+vUlpd/tws3NTW3atNHChQs1bdo0q188Fi9erKSkJLVr107SjUTqq6++Uvv27fXyyy/r0qVL+vrrr9W0aVNt3rxZNWrUUEBAgKZMmaLXXntNbdq00TPPPCNJqlatWoav+aWXXtLs2bP13HPPaeDAgdq0aZMiIyO1d+9em0ry0KFDeu6559SjRw916dJFM2bMUNeuXVWrVi098MAD2X7fS5QooQYNGujXX39VQkKC/Pz80i337LPPavfu3XrjjTcUFhamM2fOKDo6WsePH1dYWJgmTpyoN954Q/nz59d7770nSQoKCrLax+uvv66AgAANHTpUV65cuW1cBw8eVNu2bdWzZ0916dJFM2fO1PPPP68VK1aocePGWXqNmYntZrNmzVK3bt300EMPKTIyUqdPn9akSZO0fv16/fnnnypQoIClbGpqqpo2baq6devqk08+0apVqzRu3DiVKVNGr732WobHqF+/viZMmKDdu3erSpUqkqTffvtNLi4u+u2339SnTx/LMulG97X0vPrqqzp16pSio6P1zTffpFvmu+++06VLl/Tqq6/KZDLp448/1jPPPKMjR47cthV26dKl8vT01AsvvJDu+lKlSqlevXpavXq1rl27Ji8vL8u63r17q0CBAho+fLj279+vKVOm6J9//rEkRVn9TKQbLWAtWrRQu3bt9Pzzz2vKlClq166d5s6dq379+qlnz57q0KGDxo4dq+eee04nTpyQr6+vJGnLli36448/1K5dOxUvXlzHjh3TlClT1LBhQ+3Zs0fe3t63PXZ6701YWJiljrnVY489prCwMC1dulSTJ0/O0r4lacqUKXrggQf09NNPK1++fFq6dKlef/11mc1m9erVy6qsvbrhscceU58+ffTpp5/q3XffVaVKlSTJ8u+tbu3OJkkTJkzQjh07VLhwYUmZez+zetxr166pYcOGOnTokHr37q1SpUpp/vz56tq1qy5evGhzEZfd8zqrMnuc69evq2nTpqpXr54++eQTyzn16quvWuqUPn366OjRo/r888/1559/av369XJzc9OZM2fUpEkTBQQE6J133lGBAgV07Ngxmx+LMhvPqlWr9NRTT6l06dIaPny4rl27ps8++0yPPvqotm/ffttJkoYOHaqRI0eqWbNmatasmbZv364mTZooOTnZqtzw4cMVGRmpl156SXXq1FFCQoK2bt2q7du3Z7mOlm604kqynGNpnn/+eZUrV06jR4+2/NCUlb+dNzObzXr66af1+++/65VXXlGlSpW0a9cuTZgwQQcOHNDixYstZUeMGKHhw4frkUce0QcffCB3d3dt2rRJq1evVpMmTbJVh0lSYmKizp07J+lGC9r69es1e/ZsdejQId1eKR06dNCHH36oDz74QG3atLnjVqcjR45o8eLFev75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7bOsdzZlypTRqlWr9Oijj2bqgvHhhx/Www8/rFGjRum7775Tx44dFRUVlalEJM3Nddit9u3bpyJFimQ4pXfatgcPHlTp0qUty8+ePZtuS17azLTdunXT5cuX9dhjj2n48OFZijer7uRvZ5kyZfTXX3+pUaNGtz03y5QpI7PZrD179tjcD/JmOVWHXb9+XZJsJmW5WVqrU9euXfXjjz9m+1gLFizQ448/rq+//tpq+cWLF21ax6tUqaKaNWtq7ty5Kl68uI4fP67PPvvMqkyZMmV0+fLlTCXBuHP3dVc9e3r37q0NGzYoKipKO3fu1PPPP68nn3zS0p946dKlKl26tJYtW6ZSpUopLCxML730klO1OF27dk0LFy5UixYt9Nxzz9k8evfurUuXLmnJkiWSbvSN/euvv9KdNSft17a0PwiZvSBycXHRc889p6VLl+qbb77R9evXbbrppf3Cd/Mveps2bdKGDRusyqXNopSZY6ddQN4640zaL2HNmzfPVPx34vz582rfvr1SU1NtksWbXb16VYmJiVbLypQpI19fX6uuJD4+Ptm+EL3VqVOnrD7nhIQEzZkzRzVq1FBwcLAlBklWfajT+snfKrOx1a5dW4GBgZo6darVa/v555+1d+/eHP1cSpUqpWLFimnChAlKSUnRo48+KulGQnX48GEtWLBADz/8sN37f2X1nM+sV199VYGBgXrzzTdtxnMkJiaqW7duMgxDQ4cOtdn2yy+/tBqrMWXKFF2/ft3qx6KcPF/scXV1tflF/rPPPst26+SgQYPk7e2tV199Vf/995/VuvPnz6tnz57y8/NT7969LcvLlCmj+Ph4q5aq2NhYm/osvfomPj5eM2fOzFasUtbOkVWrVun999/Xe++9l+Ev25l9P7Ny3GbNmikuLs5qzOn169f12WefKX/+/GrQoIHdfeQ1L7zwglJTU/Xhhx/arLt+/brlfblw4YLN+5l2wZ5ed73bCQkJUY0aNTR79myr9/3vv//WL7/8YvXjxa0iIiLk5uamzz77zCqeW/9OSbI57/Pnz6+yZctmOd6supO/nS+88IJOnjyp6dOn26y7du2apQtj69at5eLiog8++MCmFebm9yWn6rClS5dKklVPl/R06tRJZcuW1YgRI7J9rPS+u/Pnz7e53UaaF198Ub/88osmTpyowoUL2/zg/8ILL2jDhg1auXKlzbYXL160JIXIGfd1i9PtHD9+XDNnztTx48ctd9AeNGiQVqxYoZkzZ2r06NE6cuSI/vnnH82fP19z5sxRamqq+vfvr+eee06rV6928CvInCVLlujSpUt6+umn013/8MMPKyAgQHPnzlXbtm315ptvasGCBXr++efVvXt31apVS+fPn9eSJUs0depUVa9eXWXKlFGBAgU0depU+fr6ysfHR3Xr1r1ta1zbtm312WefadiwYapatarNdLktWrTQwoUL1aZNGzVv3lxHjx7V1KlTVblyZatfiLy8vFS5cmXNmzdP5cuXV6FChVSlSpV0x2VVr15dXbp00ZdffqmLFy+qQYMG2rx5s2bPnq3WrVtb/VqfEw4cOKBvv/1WhmFYxs7Mnz9fly9f1vjx4/Xkk0/edttGjRrphRdeUOXKlZUvXz4tWrRIp0+fthqsX6tWLU2ZMkUjR45U2bJlFRgYaNNqk1nly5dXjx49tGXLFgUFBWnGjBk6ffq01cVjkyZNVKJECfXo0UNvvvmmXF1dNWPGDAUEBOj48eNW+8tsbG5ubvroo4/UrVs3NWjQQO3bt7dMRx4WFqb+/ftn6/VkpH79+oqKilLVqlUtLWgPPvigfHx8dODAAXXo0MHuPtJapPr06aOmTZvK1dXV6nPJrsKFC2vBggVq3ry5HnzwQb300kuqXLmy4uLiNGvWLB06dEiTJk1Kd7r/5ORkyzmzf/9+TZ48WfXq1bP6rufk+WJPixYt9M0338jf31+VK1fWhg0btGrVKpupjzOrbNmymjNnjtq3b6+qVauqR48eKlWqlI4dO6avv/5aFy5cUFRUlFW9065dO7399ttq06aN+vTpo6tXr2rKlCkqX7681aDsJk2ayN3dXS1bttSrr76qy5cva/r06QoMDFRsbGy24q1Ro4ZcXV310UcfKT4+Xh4eHpb7RN2qffv2CggIULly5SytyWkaN26soKCgTL+fWTnuK6+8omnTpqlr167atm2bwsLCtGDBAq1fv14TJ0602yKeFzVo0ECvvvqqIiMjtWPHDjVp0kRubm46ePCg5s+fr0mTJum5557T7NmzNXnyZLVp00ZlypTRpUuXNH36dPn5+d020cnI2LFj9dRTTyk8PFw9evSwTEfu7+9vubdgetLuQxcZGakWLVqoWbNm+vPPP/Xzzz/btEZUrlxZDRs2VK1atVSoUCFt3bpVCxYssPqxIDfcyd/OF198UT/88IN69uypX3/9VY8++qhSU1O1b98+/fDDD1q5cqVq166tsmXL6r333tOHH36o+vXr65lnnpGHh4e2bNmiokWLWu5hlJ06LO1vsXTjR8mNGzdq9uzZKlu2bLpjvG7m6uqq9957L937T2ZWixYt9MEHH6hbt2565JFHtGvXLs2dO9eqhfFmHTp00FtvvaVFixbptddes5nq/80339SSJUvUokULy20Qrly5ol27dmnBggU6duxYtsZ5IgN3cwq/vEySsWjRIsvztKmPfXx8rB758uUzXnjhBcMwDOPll182JBn79++3bLdt2zZDkrFv3767/RKypWXLloanp6dx5cqVDMt07drVcHNzM86dO2cYhmH8999/Ru/evY1ixYoZ7u7uRvHixY0uXbpY1hvGjelZK1eubOTLl89qWuKMpgM2m81GaGioIckYOXJkuutHjx5tlCxZ0vDw8DBq1qxpLFu2LN39/fHHH0atWrUMd3d3q6nJ05tyOCUlxRgxYoRRqlQpw83NzQgNDTUGDx5sMy1pyZIl05321d40wml007SnLi4uRoECBYyaNWsaffv2NXbv3m1T/tYpr8+dO2f06tXLqFixouHj42P4+/sbdevWNX744Qer7eLi4ozmzZsbvr6+hiRLbGlT3qY31W5G05E3b97cWLlypVGtWjXDw8PDqFixojF//nyb7bdt22bUrVvXcHd3N0qUKGGMHz8+3X1mFFtG00/PmzfPqFmzpuHh4WEUKlTI6Nixo/Hvv/9albl5utibZTRNenq++OILQ5Lx2muvWS2PiIgwJBkxMTFWy9Objvz69evGG2+8YQQEBBgmk8ly7LSy6U0jf/O5ac/Ro0eNl19+2ShRooTh5uZmFClSxHj66adtpvQ1jP/7PNeuXWu88sorRsGCBY38+fMbHTt2tJoa2TCy9pk0aNDAeOCBB2yOl9F3Q7dM1XvhwgWjW7duRpEiRYz8+fMbTZs2Nfbt22czVW9mpiO/2a5du4wOHToYwcHBhouLiyHJ8PT0TPd7ZRiG8csvvxhVqlQx3N3djQoVKhjffvttuufLkiVLjGrVqhmenp5GWFiY8dFHHxkzZszI8Ltyq/TqhunTpxulS5c2XF1drV7jrWVvri9ufaRtk9n3MyvHNQzDOH36tGW/7u7uRtWq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e1r9Il6uXDkdP35cUVFROnz4sD799FMtWrQoyzGFhYVp06ZNOnbsmM6dO5duS8Wzzz4rb29vDR8+XHFxcZZHamqqypYtq5SUFH322Wc6cuSIvvnmG02dOtXmGJcvX1ZMTIzOnTtnlQCmiYiIUNWqVdWxY0dt375dmzdvVufOndWgQYN0u9XdTlJSklWccXFxOnfuXNbemByMydXVVXv37tWePXvk6upqs97X11eDBg1S//79NXv2bB0+fFjbt2/XZ599ptmzZ0u60R3QZDJp2bJlOnv2bKbvDZSZ19C3b1/NmDFDM2fO1IEDBzRs2DDL1N1pFi1apIoVK2Z4nJdffllnz57VSy+9lO764cOH66233tKaNWt09OhR/fnnn+revbtSUlKsuigahmHz2cXFxVnOS3txSNLgwYO1bt06HTt2TLt27dLgwYO1Zs0adezYUdL/JU1XrlzR119/rYSEBKtzOk3FihUt3ymTyaR+/fpp5MiRWrJkiXbt2qXOnTuraNGiat26taQbCeiTTz6pl19+WZs3b9b69evVu3dvtWvXTkWLFr1tzMgYiRMAAPehcuXKacuWLSpdurReeOEFlSxZUk899ZTKly+v9evXWw2KHzdunEJDQ1W/fn116NBBgwYNsur29vTTT6t///7q3bu3atSooT/++ENDhgzJckyDBg2Sq6urKleurICAAB0/ftymzLp16/T333+rZMmSCgkJsTxOnDih6tWra/z48froo49UpUoVzZ07V5GRkVbbP/LII+rZs6fatm2rgIAAffzxxzbHMJlM+vHHH1WwYEE99thjioiIUOnSpTVv3rwsv6YVK1ZYxRkSEqJ69epleT85GVPazVgz8uGHH2rIkCGKjIy0XIAvX75cpUqVknTjfkkjRozQO++8o6CgIEvXv5x4DW3bttWQIUP01ltvqVatWvrnn3/02muvWe0nPj5e+/fvz/A4+fLlU5EiRTKcla5BgwY6cuSIOnfurIoVK+qpp55SXFycfvnlF6vJHhISEmw+u5vHbNmLQ5LOnDmjzp07q0KFCmrUqJG2bNmilStXWhK07du3a9OmTdq1a5fKli1rc06n2b9/v9VNcd966y298cYbeuWVV/TQQw/p8uXLWrFihVVr1dy5c1WxYkU1atRIzZo1U7169fTll1/eNl7cnsnIylyj94CEhAT5+/tbpnoEkLsmRB+47fr+jcvfdj2Q1yUmJuro0aMqVaqU3S42ed2wYcM0fvx4RUdH6+GHH86RfTrrIHAAuedu1wu3q6ezkhswOQQAAJAkjRgxQmFhYdq4caPq1KkjFxc6pgBAGhInAABgkd4NUQEAjHECAAAAALtInAAAAADADrrqAQCAXBNa0FuGDJlkcnQoAPIIZ60XSJwAAECuye/JpQYAa85aL9BVDwAAAADscM50D0CeZe++TQAAAM6IxAkAAOSay4nXLWMZnLV7DoCc5az1Al31AABArjlx4aqOnruiExeuZmv7NWvWyGQy6eLFizkb2C3CwsI0ceLEXD3Gva5hw4bq169fju93+PDhqlGjRo7vF45zp/WCo5A4AQBwnzpx4oS6d++uokWLyt3dXSVLllTfvn3133//OSSe9C68H3nkEcXGxsrf3z9HjjFr1iwVKFDAZvmWLVv0yiuv5Mgx0nTt2lUmk8nm8eSTT+bocTIbR8+ePW3W9erVSyaTSV27ds30/u5WMptZx44dk8lkUmBgoC5dumS1rkaNGho+fLjVst27d+uFF15QQECAPDw8VL58eQ0dOlRXr9pexP/55596/vnnFRQUJE9PT5UrV04vv/yyDhw4YHXsHTt2pBvbrefbrFmzLOeBi4uLihcvrm7duunMmTOWMjefK/7+/nr00Ue1evVqy/quXbuqdevWVs9NJpPGjBljdezFixfLZLKetc4wDE2fPl3h4eHy8/NT/vz59cADD6hv3746dOhQuq/hdi5evKhevXopJCTE8l7+9NNP6ZYdM2aMTCZTppLr+fPnq2LFivL09FTVqlVt9mkYhoYOHaqQkBB5eXkpIiJCBw8ezHL8WUXiBADAfejIkSOqXbu2Dh48qO+//16HDh3S1KlTFRMTo/DwcJ0/f97RIUqS3N3dFRwcbHMBmNMCAgLk7e2d4/t98sknFRsba/X4/vvvMyyfkpJisyw5OTlbx755u9DQUEVFRenatWuWZYmJifruu+9UokSJbO0/r7l06ZI++eST25bZuHGj6tatq+TkZC1fvlwHDhzQqFGjNGvWLDVu3NjqPVu2bJkefvhhJSUlae7cudq7d6++/fZb+fv7a8iQIdmO08/PT7Gxsfr33381ffp0/fzzz3rxxRetysycOVOxsbFav369ihQpohYtWujIkSMZ7tPT01MfffSRLly4kGEZwzDUoUMH9enTR82aNdMvv/yiPXv26Ouvv5anp6dGjhyZpdeRnJysxo0b69ixY1qwYIH279+v6dOnq1ixYjZlt2zZomnTpqlatWp29/vHH3+offv26tGjh/7880+1bt1arVu31t9//20p8/HHH+vTTz/V1KlTtWnTJvn4+Khp06ZKTEzM0mvIKhInAADuQ7169ZK7u7t++eUXNWjQQCVKlNBTTz2lVatW6eTJk3rvvfcsZU0mkxYvXmy1fYECBTRr1izL87ffflvly5eXt7e3SpcurSFDhlglAZ9/EqkaNWrom2++UVhYmPz9/dWuXTtLC0HXrl21du1aTZo0yfJr+7Fjx2xaNxo2bJhuK86xY8ckSePHj1fVqlXl4+Oj0NBQvf7667p8+bKkGy0l3bp1U3x8vGW7tNaIW7vqHT9+XK1atVL+/Pnl5+enF154QadPn7asT+s+ltHrSePh4aHg4GCrR8GCBa3e2ylTpujpp5+Wj4+PRo0aZdn3V199pVKlSsnT0zNLMd26nSQ9+OCDCg0N1cKFCy3LFi5cqBIlSqhmzZpWMZvNZkVGRqpUqVLy8vJS9erVtWDBAkk3Wlgef/xxSVLBggVtWqvMZrPeeustFSpUSMHBwTatPfZeg3SjZSIoKEi+vr7q0aNHpi+G33jjDY0fP96q9eZmhmGoR48eqlSpkhYuXKg6deqoZMmSev7557V06VJt2LBBEyZMkCRdvXpV3bp1U7NmzbRkyRJFRESoVKlSqlu3rj755BNNmzYtUzGlx2QyKTg4WEWLFtVTTz2lPn36aNWqVVZJbYECBRQcHKwqVapoypQpunbtmqKjozPcZ0REhIKDgxUZGZlhmXnz5ikqKkrz5s3TkCFD9PDDD6tEiRJ6+OGH9dFHH2nmzJlZeh0zZszQ+fPntXjxYj366KMKCwtTgwYNVL16datyly9fVseOHTV9+nSrcz8jkyZN0pNPPqk333xTlSpV0ocffqgHH3xQn3/+uaQbn+PEiRP1/vvvq1WrVqpWrZrmzJmjU6dO2dRTOY3ECQCAXPDVb0f08OgYu4+XZm+x2fal2Vsyte1Xv2X8C/TtnD9/XitXrtTrr78uLy8vq3XBwcHq2LGj5s2bJ8MwMr1PX19fzZo1S3v27NGkSZM0ffp0y0VomsOHD2vx4sVatmyZli1bprVr11q6F02aNEnh4eF6+eWXLS0zoaGhNsdZuHChVevNM888owoVKigoKEiS5OLiok8//VS7d+/W7NmztXr1ar311luSbnT7mzhxouUX/9jYWA0aNMjmGGazWa1atdL58+e1du1aRUdH68iRI2rbtm2mX09WDB8+XG3atNGuXbvUvXt3SdKhQ4f0v//9TwsXLtSOHTsyHdOt292se/fuVhfHM2bMULdu3WziiYyM1Jw5czR16lTt3r1b/fv3V6dOnbR27VqFhobqf//7nyRp//79io2N1aRJkyzbzp49Wz4+Ptq0aZM+/vhjffDBB5YL/sy8hh9++EHDhw/X6NGjtXXrVoWEhGjy5MmZeh/bt2+vsmXL6oMPPkh3/Y4dO7Rnzx4NGDBALi7Wl8DVq1dXRESEpTVw5cqVOnfunOXcuVV63T2zy8vLS2azWdevX89wvXT7lkdXV1eNHj1an332mf799990y3z//feqUKGCnn766XTX39yqm/aDRdoPEulZsmSJwsPD1atXLwUFBalKlSoaPXq0UlNTrcr16tVLzZs3V0RERIb7utmGDRtsyjZt2lQbNmyQJB09elRxcXFWZfz9/VW3bl1LmdziPNNYAADgRC4lXldcgv1fykMKeNos++9Kcqa2vZSY/oWWPQcPHpRhGKpUqVK66ytVqqQLFy7o7NmzCgwMzNQ+33//fcv/w8LCNGjQIEVFRanli/83rsZsNmvWrFny9fWVJL344ouKiYnRqFGj5O/vL3d3d3l7eys4ODjD4xQqVMjy/wkTJmj16tXatGmT5eLy5vETYWFhGjlypHr27KnJkyfL3d1d/v7+ll/8MxITE6Ndu3bp6NGjluRtzpw5euCBB7RlyxY99NBDdl9PmmXLlil//vxW+3/33Xf17rvvWp536NDBJoFJTk7WnDlzFBAQIEmKjo7OVEy3bnezTp06afDgwfrnn38kSevXr1dUVJTWrFljKZOUlKTRo0dr1apVCg8PlySVLl1av//+u6ZNm6YGDRpYPoPAwECbBKJatWoaNmyYJKlcuXL6/PPPFRMTo8aNG2fqfZ04caJ69OihHj16SJJGjhypVatWZarVKW2cT8uWLdW/f3+VKVPGan3auKTbnfe///67JFnGy1SsWNHuce/EwYMHNXXqVNWuXdtyHt3s6tWrev/99+Xq6qoGDRrcdl9t2rRRjRo1NGzYMH399dc2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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Plotting\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(all_activations_project, bins=100, alpha=0.5, label='Original')\n", + "for method, threshold in optimal_thresholds_project.items():\n", + " plt.axvline(threshold, linestyle='--', linewidth=2, label=f'{method}: {threshold:.2f}')\n", + "\n", + "plt.title('Activation Distribution with Optimal Quantization Thresholds Project BN layer')\n", + "plt.xlabel('Activation Value')\n", + "plt.ylabel('Frequency')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4c967d41-439d-405b-815f-be641f1768fe", + "metadata": { + "id": "4c967d41-439d-405b-815f-be641f1768fe" + }, + "source": [ + "## Model Evaluation\n", + "Finally, we can demonstrate the impact of these different thresholds on the model's overall accuracy.\n", + "In order to evaluate our models, we first need to load the validation dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "9199b59c4f10eca1", + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#val_dataset = get_dataset(batch_size=50, shuffle=False)\n", + "#val_dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", + "#val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "631780a79e2cedf0", + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [07:28<00:00, 1.15s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -5.2923, Accuracy: 33872/50000 (68%)\n", + "\n", + "Float model's Top 1 accuracy on the Imagenet validation set: 67.74%\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "import torch.nn.functional as F\n", + "\n", + "val_dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", + "val_loader = DataLoader(val_dataset, batch_size=128, shuffle=False)\n", + "\n", + "def evaluate(model, test_loader):\n", + " device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", + " model.to(device)\n", + " model.eval()\n", + " test_loss = 0\n", + " correct = 0\n", + " with torch.no_grad():\n", + " for data, target in tqdm(test_loader):\n", + " data, target = data.to(device), target.to(device)\n", + " output = model(data)\n", + " test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss\n", + " #print(\"test_loss\", test_loss)\n", + " pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability\n", + " correct += pred.eq(target.view_as(pred)).sum().item()\n", + "\n", + " test_loss /= len(test_loader.dataset)\n", + " accuracy = correct / len(test_loader.dataset)\n", + " \n", + " print('\\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format(\n", + " test_loss, correct, len(test_loader.dataset),\n", + " accuracy * 100.0))\n", + " \n", + " return test_loss, accuracy \n", + "\n", + "_, float_accuracy = evaluate(float_model, val_loader)\n", + "print(f\"Float model's Top 1 accuracy on the Imagenet validation set: {(float_accuracy * 100.0):.2f}%\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", + "metadata": { + "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [04:18<00:00, 1.51it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -6.0600, Accuracy: 35920/50000 (72%)\n", + "\n", + "Results for QuantizationErrorMethod.MSE: Loss = -6.05997125, Accuracy = 0.7184\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [05:56<00:00, 1.10it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -6.2230, Accuracy: 35963/50000 (72%)\n", + "\n", + "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2229875, Accuracy = 0.71926\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "evaluation_results = {}\n", + "\n", + "for error_method, data in quantized_models_dict.items():\n", + " quantized_model = data[\"quantized_model\"]\n", + "\n", + " results = evaluate(quantized_model, val_loader)\n", + "\n", + " evaluation_results[error_method] = results\n", + "\n", + " # Print the results\n", + " print(f\"Results for {error_method}: Loss = {results[0]}, Accuracy = {results[1]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "GpEZ2E1qzWl3", + "metadata": { + "id": "GpEZ2E1qzWl3" + }, + "source": [ + "These results are consistent across many models, which is why MSE is set as the default method.\n", + "\n", + "Each of MCT's error methods impacts models differently, so it is recommended to include this metric as part of hyperparameter tuning when optimizing quantized model accuracy.\n", + "\n", + "\n", + "## Conclusion\n", + "In this tutorial, we explored the process of finding optimal activation thresholds using different error metrics in MCT’s post-training quantization workflow. By comparing the **MSE** and **No Clipping** methods, we demonstrated how the choice of threshold can significantly affect the activation distributions and, ultimately, the quantized model’s performance. While **MSE** is commonly the best choice and is used by default, it is essential to consider other error metrics during hyperparameter tuning to achieve the best results for different models.\n", + "\n", + "Understanding the impact of these thresholds on data loss and resolution is critical when fine-tuning the quantization process for deployment, making this a valuable step in building high-performance quantized models.\n", + "\n", + "\n", + "## Appendix\n", + "Below is a code snippet that can be used to extract information from each layer in the MCT quantization output, assisting in analyzing the layer-wise quantization details." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "qml4LLmWZLP4", + "metadata": { + "id": "qml4LLmWZLP4" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: torchinfo in /mnt/share2/work/sss/ve310_1_pt/lib/python3.10/site-packages (1.8.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", + "odict_keys(['features_0_0_bn.weight', 'features_0_0_bn.layer.bias', 'features_1_conv_0_0_bn.weight', 'features_1_conv_0_0_bn.layer.bias', 'features_1_conv_1_bn.weight', 'features_1_conv_1_bn.layer.bias', 'features_2_conv_0_0_bn.weight', 'features_2_conv_0_0_bn.layer.bias', 'features_2_conv_1_0_bn.weight', 'features_2_conv_1_0_bn.layer.bias', 'features_2_conv_2_bn.weight', 'features_2_conv_2_bn.layer.bias', 'features_3_conv_0_0_bn.weight', 'features_3_conv_0_0_bn.layer.bias', 'features_3_conv_1_0_bn.weight', 'features_3_conv_1_0_bn.layer.bias', 'features_3_conv_2_bn.weight', 'features_3_conv_2_bn.layer.bias', 'features_4_conv_0_0_bn.weight', 'features_4_conv_0_0_bn.layer.bias', 'features_4_conv_1_0_bn.weight', 'features_4_conv_1_0_bn.layer.bias', 'features_4_conv_2_bn.weight', 'features_4_conv_2_bn.layer.bias', 'features_5_conv_0_0_bn.weight', 'features_5_conv_0_0_bn.layer.bias', 'features_5_conv_1_0_bn.weight', 'features_5_conv_1_0_bn.layer.bias', 'features_5_conv_2_bn.weight', 'features_5_conv_2_bn.layer.bias', 'features_6_conv_0_0_bn.weight', 'features_6_conv_0_0_bn.layer.bias', 'features_6_conv_1_0_bn.weight', 'features_6_conv_1_0_bn.layer.bias', 'features_6_conv_2_bn.weight', 'features_6_conv_2_bn.layer.bias', 'features_7_conv_0_0_bn.weight', 'features_7_conv_0_0_bn.layer.bias', 'features_7_conv_1_0_bn.weight', 'features_7_conv_1_0_bn.layer.bias', 'features_7_conv_2_bn.weight', 'features_7_conv_2_bn.layer.bias', 'features_8_conv_0_0_bn.weight', 'features_8_conv_0_0_bn.layer.bias', 'features_8_conv_1_0_bn.weight', 'features_8_conv_1_0_bn.layer.bias', 'features_8_conv_2_bn.weight', 'features_8_conv_2_bn.layer.bias', 'features_9_conv_0_0_bn.weight', 'features_9_conv_0_0_bn.layer.bias', 'features_9_conv_1_0_bn.weight', 'features_9_conv_1_0_bn.layer.bias', 'features_9_conv_2_bn.weight', 'features_9_conv_2_bn.layer.bias', 'features_10_conv_0_0_bn.weight', 'features_10_conv_0_0_bn.layer.bias', 'features_10_conv_1_0_bn.weight', 'features_10_conv_1_0_bn.layer.bias', 'features_10_conv_2_bn.weight', 'features_10_conv_2_bn.layer.bias', 'features_11_conv_0_0_bn.weight', 'features_11_conv_0_0_bn.layer.bias', 'features_11_conv_1_0_bn.weight', 'features_11_conv_1_0_bn.layer.bias', 'features_11_conv_2_bn.weight', 'features_11_conv_2_bn.layer.bias', 'features_12_conv_0_0_bn.weight', 'features_12_conv_0_0_bn.layer.bias', 'features_12_conv_1_0_bn.weight', 'features_12_conv_1_0_bn.layer.bias', 'features_12_conv_2_bn.weight', 'features_12_conv_2_bn.layer.bias', 'features_13_conv_0_0_bn.weight', 'features_13_conv_0_0_bn.layer.bias', 'features_13_conv_1_0_bn.weight', 'features_13_conv_1_0_bn.layer.bias', 'features_13_conv_2_bn.weight', 'features_13_conv_2_bn.layer.bias', 'features_14_conv_0_0_bn.weight', 'features_14_conv_0_0_bn.layer.bias', 'features_14_conv_1_0_bn.weight', 'features_14_conv_1_0_bn.layer.bias', 'features_14_conv_2_bn.weight', 'features_14_conv_2_bn.layer.bias', 'features_15_conv_0_0_bn.weight', 'features_15_conv_0_0_bn.layer.bias', 'features_15_conv_1_0_bn.weight', 'features_15_conv_1_0_bn.layer.bias', 'features_15_conv_2_bn.weight', 'features_15_conv_2_bn.layer.bias', 'features_16_conv_0_0_bn.weight', 'features_16_conv_0_0_bn.layer.bias', 'features_16_conv_1_0_bn.weight', 'features_16_conv_1_0_bn.layer.bias', 'features_16_conv_2_bn.weight', 'features_16_conv_2_bn.layer.bias', 'features_17_conv_0_0_bn.weight', 'features_17_conv_0_0_bn.layer.bias', 'features_17_conv_1_0_bn.weight', 'features_17_conv_1_0_bn.layer.bias', 'features_17_conv_2_bn.weight', 'features_17_conv_2_bn.layer.bias', 'features_18_0_bn.weight', 'features_18_0_bn.layer.bias', 'classifier_1.weight', 'classifier_1.layer.bias'])\n", + "====================================================================================================\n", + "Layer (type:depth-idx) Output Shape Param #\n", + "====================================================================================================\n", + "PytorchModel [1, 1000] --\n", + "├─DummyPlaceHolder: 1-1 [1, 3, 224, 224] --\n", + "├─PytorchActivationQuantizationHolder: 1-2 [1, 3, 224, 224] --\n", + "├─PytorchQuantizationWrapper: 1-3 [1, 32, 112, 112] 864\n", + "│ └─Conv2d: 2-1 [1, 32, 112, 112] 32\n", + "├─ReLU6: 1-4 [1, 32, 112, 112] --\n", + "├─PytorchActivationQuantizationHolder: 1-5 [1, 32, 112, 112] --\n", + "├─PytorchQuantizationWrapper: 1-6 [1, 32, 112, 112] 288\n", + "│ └─Conv2d: 2-2 [1, 32, 112, 112] 32\n", + "├─ReLU6: 1-7 [1, 32, 112, 112] --\n", + "├─PytorchActivationQuantizationHolder: 1-8 [1, 32, 112, 112] --\n", + "├─PytorchQuantizationWrapper: 1-9 [1, 16, 112, 112] 512\n", + "│ └─Conv2d: 2-3 [1, 16, 112, 112] 16\n", + "├─PytorchActivationQuantizationHolder: 1-10 [1, 16, 112, 112] --\n", + "├─PytorchQuantizationWrapper: 1-11 [1, 96, 112, 112] 1,536\n", + "│ └─Conv2d: 2-4 [1, 96, 112, 112] 96\n", + "├─ReLU6: 1-12 [1, 96, 112, 112] --\n", + "├─PytorchActivationQuantizationHolder: 1-13 [1, 96, 112, 112] --\n", + "├─PytorchQuantizationWrapper: 1-14 [1, 96, 56, 56] 864\n", + "│ └─Conv2d: 2-5 [1, 96, 56, 56] 96\n", + "├─ReLU6: 1-15 [1, 96, 56, 56] --\n", + "├─PytorchActivationQuantizationHolder: 1-16 [1, 96, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-17 [1, 24, 56, 56] 2,304\n", + "│ └─Conv2d: 2-6 [1, 24, 56, 56] 24\n", + "├─PytorchActivationQuantizationHolder: 1-18 [1, 24, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-19 [1, 144, 56, 56] 3,456\n", + "│ └─Conv2d: 2-7 [1, 144, 56, 56] 144\n", + "├─ReLU6: 1-20 [1, 144, 56, 56] --\n", + "├─PytorchActivationQuantizationHolder: 1-21 [1, 144, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-22 [1, 144, 56, 56] 1,296\n", + "│ └─Conv2d: 2-8 [1, 144, 56, 56] 144\n", + "├─ReLU6: 1-23 [1, 144, 56, 56] --\n", + "├─PytorchActivationQuantizationHolder: 1-24 [1, 144, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-25 [1, 24, 56, 56] 3,456\n", + "│ └─Conv2d: 2-9 [1, 24, 56, 56] 24\n", + "├─PytorchActivationQuantizationHolder: 1-26 [1, 24, 56, 56] --\n", + "├─PytorchActivationQuantizationHolder: 1-27 [1, 24, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-28 [1, 144, 56, 56] 3,456\n", + "│ └─Conv2d: 2-10 [1, 144, 56, 56] 144\n", + "├─ReLU6: 1-29 [1, 144, 56, 56] --\n", + "├─PytorchActivationQuantizationHolder: 1-30 [1, 144, 56, 56] --\n", + "├─PytorchQuantizationWrapper: 1-31 [1, 144, 28, 28] 1,296\n", + "│ └─Conv2d: 2-11 [1, 144, 28, 28] 144\n", + "├─ReLU6: 1-32 [1, 144, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-33 [1, 144, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-34 [1, 32, 28, 28] 4,608\n", + "│ └─Conv2d: 2-12 [1, 32, 28, 28] 32\n", + "├─PytorchActivationQuantizationHolder: 1-35 [1, 32, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-36 [1, 192, 28, 28] 6,144\n", + "│ └─Conv2d: 2-13 [1, 192, 28, 28] 192\n", + "├─ReLU6: 1-37 [1, 192, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-38 [1, 192, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-39 [1, 192, 28, 28] 1,728\n", + "│ └─Conv2d: 2-14 [1, 192, 28, 28] 192\n", + "├─ReLU6: 1-40 [1, 192, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-41 [1, 192, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-42 [1, 32, 28, 28] 6,144\n", + "│ └─Conv2d: 2-15 [1, 32, 28, 28] 32\n", + "├─PytorchActivationQuantizationHolder: 1-43 [1, 32, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-44 [1, 32, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-45 [1, 192, 28, 28] 6,144\n", + "│ └─Conv2d: 2-16 [1, 192, 28, 28] 192\n", + "├─ReLU6: 1-46 [1, 192, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-47 [1, 192, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-48 [1, 192, 28, 28] 1,728\n", + "│ └─Conv2d: 2-17 [1, 192, 28, 28] 192\n", + "├─ReLU6: 1-49 [1, 192, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-50 [1, 192, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-51 [1, 32, 28, 28] 6,144\n", + "│ └─Conv2d: 2-18 [1, 32, 28, 28] 32\n", + "├─PytorchActivationQuantizationHolder: 1-52 [1, 32, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-53 [1, 32, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-54 [1, 192, 28, 28] 6,144\n", + "│ └─Conv2d: 2-19 [1, 192, 28, 28] 192\n", + "├─ReLU6: 1-55 [1, 192, 28, 28] --\n", + "├─PytorchActivationQuantizationHolder: 1-56 [1, 192, 28, 28] --\n", + "├─PytorchQuantizationWrapper: 1-57 [1, 192, 14, 14] 1,728\n", + "│ └─Conv2d: 2-20 [1, 192, 14, 14] 192\n", + "├─ReLU6: 1-58 [1, 192, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-59 [1, 192, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-60 [1, 64, 14, 14] 12,288\n", + "│ └─Conv2d: 2-21 [1, 64, 14, 14] 64\n", + "├─PytorchActivationQuantizationHolder: 1-61 [1, 64, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-62 [1, 384, 14, 14] 24,576\n", + "│ └─Conv2d: 2-22 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-63 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-64 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-65 [1, 384, 14, 14] 3,456\n", + "│ └─Conv2d: 2-23 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-66 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-67 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-68 [1, 64, 14, 14] 24,576\n", + "│ └─Conv2d: 2-24 [1, 64, 14, 14] 64\n", + "├─PytorchActivationQuantizationHolder: 1-69 [1, 64, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-70 [1, 64, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-71 [1, 384, 14, 14] 24,576\n", + "│ └─Conv2d: 2-25 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-72 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-73 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-74 [1, 384, 14, 14] 3,456\n", + "│ └─Conv2d: 2-26 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-75 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-76 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-77 [1, 64, 14, 14] 24,576\n", + "│ └─Conv2d: 2-27 [1, 64, 14, 14] 64\n", + "├─PytorchActivationQuantizationHolder: 1-78 [1, 64, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-79 [1, 64, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-80 [1, 384, 14, 14] 24,576\n", + "│ └─Conv2d: 2-28 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-81 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-82 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-83 [1, 384, 14, 14] 3,456\n", + "│ └─Conv2d: 2-29 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-84 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-85 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-86 [1, 64, 14, 14] 24,576\n", + "│ └─Conv2d: 2-30 [1, 64, 14, 14] 64\n", + "├─PytorchActivationQuantizationHolder: 1-87 [1, 64, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-88 [1, 64, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-89 [1, 384, 14, 14] 24,576\n", + "│ └─Conv2d: 2-31 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-90 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-91 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-92 [1, 384, 14, 14] 3,456\n", + "│ └─Conv2d: 2-32 [1, 384, 14, 14] 384\n", + "├─ReLU6: 1-93 [1, 384, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-94 [1, 384, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-95 [1, 96, 14, 14] 36,864\n", + "│ └─Conv2d: 2-33 [1, 96, 14, 14] 96\n", + "├─PytorchActivationQuantizationHolder: 1-96 [1, 96, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-97 [1, 576, 14, 14] 55,296\n", + "│ └─Conv2d: 2-34 [1, 576, 14, 14] 576\n", + "├─ReLU6: 1-98 [1, 576, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-99 [1, 576, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-100 [1, 576, 14, 14] 5,184\n", + "│ └─Conv2d: 2-35 [1, 576, 14, 14] 576\n", + "├─ReLU6: 1-101 [1, 576, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-102 [1, 576, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-103 [1, 96, 14, 14] 55,296\n", + "│ └─Conv2d: 2-36 [1, 96, 14, 14] 96\n", + "├─PytorchActivationQuantizationHolder: 1-104 [1, 96, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-105 [1, 96, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-106 [1, 576, 14, 14] 55,296\n", + "│ └─Conv2d: 2-37 [1, 576, 14, 14] 576\n", + "├─ReLU6: 1-107 [1, 576, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-108 [1, 576, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-109 [1, 576, 14, 14] 5,184\n", + "│ └─Conv2d: 2-38 [1, 576, 14, 14] 576\n", + "├─ReLU6: 1-110 [1, 576, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-111 [1, 576, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-112 [1, 96, 14, 14] 55,296\n", + "│ └─Conv2d: 2-39 [1, 96, 14, 14] 96\n", + "├─PytorchActivationQuantizationHolder: 1-113 [1, 96, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-114 [1, 96, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-115 [1, 576, 14, 14] 55,296\n", + "│ └─Conv2d: 2-40 [1, 576, 14, 14] 576\n", + "├─ReLU6: 1-116 [1, 576, 14, 14] --\n", + "├─PytorchActivationQuantizationHolder: 1-117 [1, 576, 14, 14] --\n", + "├─PytorchQuantizationWrapper: 1-118 [1, 576, 7, 7] 5,184\n", + "│ └─Conv2d: 2-41 [1, 576, 7, 7] 576\n", + "├─ReLU6: 1-119 [1, 576, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-120 [1, 576, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-121 [1, 160, 7, 7] 92,160\n", + "│ └─Conv2d: 2-42 [1, 160, 7, 7] 160\n", + "├─PytorchActivationQuantizationHolder: 1-122 [1, 160, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-123 [1, 960, 7, 7] 153,600\n", + "│ └─Conv2d: 2-43 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-124 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-125 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-126 [1, 960, 7, 7] 8,640\n", + "│ └─Conv2d: 2-44 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-127 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-128 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-129 [1, 160, 7, 7] 153,600\n", + "│ └─Conv2d: 2-45 [1, 160, 7, 7] 160\n", + "├─PytorchActivationQuantizationHolder: 1-130 [1, 160, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-131 [1, 160, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-132 [1, 960, 7, 7] 153,600\n", + "│ └─Conv2d: 2-46 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-133 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-134 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-135 [1, 960, 7, 7] 8,640\n", + "│ └─Conv2d: 2-47 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-136 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-137 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-138 [1, 160, 7, 7] 153,600\n", + "│ └─Conv2d: 2-48 [1, 160, 7, 7] 160\n", + "├─PytorchActivationQuantizationHolder: 1-139 [1, 160, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-140 [1, 160, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-141 [1, 960, 7, 7] 153,600\n", + "│ └─Conv2d: 2-49 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-142 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-143 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-144 [1, 960, 7, 7] 8,640\n", + "│ └─Conv2d: 2-50 [1, 960, 7, 7] 960\n", + "├─ReLU6: 1-145 [1, 960, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-146 [1, 960, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-147 [1, 320, 7, 7] 307,200\n", + "│ └─Conv2d: 2-51 [1, 320, 7, 7] 320\n", + "├─PytorchActivationQuantizationHolder: 1-148 [1, 320, 7, 7] --\n", + "├─PytorchQuantizationWrapper: 1-149 [1, 1280, 7, 7] 409,600\n", + "│ └─Conv2d: 2-52 [1, 1280, 7, 7] 1,280\n", + "├─ReLU6: 1-150 [1, 1280, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-151 [1, 1280, 7, 7] --\n", + "├─PytorchActivationQuantizationHolder: 1-152 [1, 1280, 1, 1] --\n", + "├─Dropout: 1-153 [1, 1280] --\n", + "├─PytorchQuantizationWrapper: 1-154 [1, 1000] 1,280,000\n", + "│ └─Linear: 2-53 [1, 1000] 1,000\n", + "├─PytorchActivationQuantizationHolder: 1-155 [1, 1000] --\n", + "====================================================================================================\n", + "Total params: 3,487,816\n", + "Trainable params: 18,056\n", + "Non-trainable params: 3,469,760\n", + "Total mult-adds (M): 6.68\n", + "====================================================================================================\n", + "Input size (MB): 0.60\n", + "Forward/backward pass size (MB): 53.43\n", + "Params size (MB): 0.07\n", + "Estimated Total Size (MB): 54.11\n", + "====================================================================================================\n", + "layer features_0_0_bn.weight\n", + "layer features_0_0_bn.layer.bias\n", + "layer features_1_conv_0_0_bn.weight\n", + "layer features_1_conv_0_0_bn.layer.bias\n", + "layer features_1_conv_1_bn.weight\n", + "layer features_1_conv_1_bn.layer.bias\n", + "layer features_2_conv_0_0_bn.weight\n", + "layer features_2_conv_0_0_bn.layer.bias\n", + "layer features_2_conv_1_0_bn.weight\n", + "layer features_2_conv_1_0_bn.layer.bias\n", + "layer features_2_conv_2_bn.weight\n", + "layer features_2_conv_2_bn.layer.bias\n", + "layer features_3_conv_0_0_bn.weight\n", + "layer features_3_conv_0_0_bn.layer.bias\n", + "layer features_3_conv_1_0_bn.weight\n", + "layer features_3_conv_1_0_bn.layer.bias\n", + "layer features_3_conv_2_bn.weight\n", + "layer features_3_conv_2_bn.layer.bias\n", + "layer features_4_conv_0_0_bn.weight\n", + "layer features_4_conv_0_0_bn.layer.bias\n", + "layer features_4_conv_1_0_bn.weight\n", + "layer features_4_conv_1_0_bn.layer.bias\n", + "layer features_4_conv_2_bn.weight\n", + "layer features_4_conv_2_bn.layer.bias\n", + "layer features_5_conv_0_0_bn.weight\n", + "layer features_5_conv_0_0_bn.layer.bias\n", + "layer features_5_conv_1_0_bn.weight\n", + "layer features_5_conv_1_0_bn.layer.bias\n", + "layer features_5_conv_2_bn.weight\n", + "layer features_5_conv_2_bn.layer.bias\n", + "layer features_6_conv_0_0_bn.weight\n", + "layer features_6_conv_0_0_bn.layer.bias\n", + "layer features_6_conv_1_0_bn.weight\n", + "layer features_6_conv_1_0_bn.layer.bias\n", + "layer features_6_conv_2_bn.weight\n", + "layer features_6_conv_2_bn.layer.bias\n", + "layer features_7_conv_0_0_bn.weight\n", + "layer features_7_conv_0_0_bn.layer.bias\n", + "layer features_7_conv_1_0_bn.weight\n", + "layer features_7_conv_1_0_bn.layer.bias\n", + "layer features_7_conv_2_bn.weight\n", + "layer features_7_conv_2_bn.layer.bias\n", + "layer features_8_conv_0_0_bn.weight\n", + "layer features_8_conv_0_0_bn.layer.bias\n", + "layer features_8_conv_1_0_bn.weight\n", + "layer features_8_conv_1_0_bn.layer.bias\n", + "layer features_8_conv_2_bn.weight\n", + "layer features_8_conv_2_bn.layer.bias\n", + "layer features_9_conv_0_0_bn.weight\n", + "layer features_9_conv_0_0_bn.layer.bias\n", + "layer features_9_conv_1_0_bn.weight\n", + "layer features_9_conv_1_0_bn.layer.bias\n", + "layer features_9_conv_2_bn.weight\n", + "layer features_9_conv_2_bn.layer.bias\n", + "layer features_10_conv_0_0_bn.weight\n", + "layer features_10_conv_0_0_bn.layer.bias\n", + "layer features_10_conv_1_0_bn.weight\n", + "layer features_10_conv_1_0_bn.layer.bias\n", + "layer features_10_conv_2_bn.weight\n", + "layer features_10_conv_2_bn.layer.bias\n", + "layer features_11_conv_0_0_bn.weight\n", + "layer features_11_conv_0_0_bn.layer.bias\n", + "layer features_11_conv_1_0_bn.weight\n", + "layer features_11_conv_1_0_bn.layer.bias\n", + "layer features_11_conv_2_bn.weight\n", + "layer features_11_conv_2_bn.layer.bias\n", + "layer features_12_conv_0_0_bn.weight\n", + "layer features_12_conv_0_0_bn.layer.bias\n", + "layer features_12_conv_1_0_bn.weight\n", + "layer features_12_conv_1_0_bn.layer.bias\n", + "layer features_12_conv_2_bn.weight\n", + "layer features_12_conv_2_bn.layer.bias\n", + "layer features_13_conv_0_0_bn.weight\n", + "layer features_13_conv_0_0_bn.layer.bias\n", + "layer features_13_conv_1_0_bn.weight\n", + "layer features_13_conv_1_0_bn.layer.bias\n", + "layer features_13_conv_2_bn.weight\n", + "layer features_13_conv_2_bn.layer.bias\n", + "layer features_14_conv_0_0_bn.weight\n", + "layer features_14_conv_0_0_bn.layer.bias\n", + "layer features_14_conv_1_0_bn.weight\n", + "layer features_14_conv_1_0_bn.layer.bias\n", + "layer features_14_conv_2_bn.weight\n", + "layer features_14_conv_2_bn.layer.bias\n", + "layer features_15_conv_0_0_bn.weight\n", + "layer features_15_conv_0_0_bn.layer.bias\n", + "layer features_15_conv_1_0_bn.weight\n", + "layer features_15_conv_1_0_bn.layer.bias\n", + "layer features_15_conv_2_bn.weight\n", + "layer features_15_conv_2_bn.layer.bias\n", + "layer features_16_conv_0_0_bn.weight\n", + "layer features_16_conv_0_0_bn.layer.bias\n", + "layer features_16_conv_1_0_bn.weight\n", + "layer features_16_conv_1_0_bn.layer.bias\n", + "layer features_16_conv_2_bn.weight\n", + "layer features_16_conv_2_bn.layer.bias\n", + "layer features_17_conv_0_0_bn.weight\n", + "layer features_17_conv_0_0_bn.layer.bias\n", + "layer features_17_conv_1_0_bn.weight\n", + "layer features_17_conv_1_0_bn.layer.bias\n", + "layer features_17_conv_2_bn.weight\n", + "layer features_17_conv_2_bn.layer.bias\n", + "layer features_18_0_bn.weight\n", + "layer features_18_0_bn.layer.bias\n", + "layer classifier_1.weight\n", + "layer classifier_1.layer.bias\n" + ] + } + ], + "source": [ + "!pip install torchinfo\n", + "from torchinfo import summary\n", + "quantized_model = data[\"quantized_model\"]\n", + "quantizer_object = quantized_model.features_0_2_activation_holder_quantizer\n", + "\n", + "quantized_model = data[\"quantized_model\"]\n", + "\n", + "print(quantized_model.state_dict().keys())\n", + "\n", + "relu_layer_indices = []\n", + "\n", + "print(summary(\n", + " quantized_model,\n", + " input_size=(1, 3, 224, 224),\n", + " col_names=[\"output_size\", \"num_params\"],\n", + "))\n", + "\n", + "for i, layer in enumerate(quantized_model.state_dict().keys()):\n", + " print('layer', i, layer)\n", + " # Convert the layer's configuration to a string\n", + " #layer_config_str = str(layer.get_config())\n", + "\n", + " #layer_class_str = str(layer.__class__.__name__)\n", + "\n", + " # Check if \"relu\" is mentioned in the layer's configuration or class name\n", + " #if 'relu' in layer_config_str.lower() or 'relu' in layer_class_str.lower():\n", + " # relu_layer_indices.append(i)\n", + "\n", + "#print(\"Layer indices potentially using ReLU:\", relu_layer_indices)\n", + "#print(\"Number of relu layers \" + str(len(relu_layer_indices)))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", + "metadata": { + "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" + }, + "outputs": [], + "source": [ + "for error_method, data in quantized_models_dict.items():\n", + " quantized_model = data[\"quantized_model\"]\n", + " print(quantized_model.layers[1])" + ] + }, + { + "cell_type": "markdown", + "id": "01c1645e-205c-4d9a-8af3-e497b3addec1", + "metadata": { + "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" + }, + "source": [ + "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" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "ve310_1_pt", + "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.9.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 66a3bd965b64d31753c13d50176feb93cdf09931 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 29 Jan 2025 17:46:30 +0900 Subject: [PATCH 02/30] pytorch_activation_threshold_search(refine a little) --- ..._pytorch_activation_threshold_search.ipynb | 506 +++--------------- 1 file changed, 88 insertions(+), 418 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index 74cfe2b7f..bf52a77ac 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -98,7 +98,6 @@ }, "outputs": [], "source": [ - "\n", "if not importlib.util.find_spec('model_compression_toolkit'):\n", " !pip install model_compression_toolkit" ] @@ -127,7 +126,7 @@ "collapsed": false }, "source": [ - "Load a pre-trained MobileNetV2 model from Keras, in 32-bits floating-point precision format." + "Load a pre-trained MobileNetV2 model from pytorch, in 32-bits floating-point precision format." ] }, { @@ -215,7 +214,7 @@ "dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", "print(type(dataset))\n", "print(len(dataset))\n", - "print(dataset)\n" + "print(dataset)" ] }, { @@ -352,7 +351,7 @@ ], "source": [ "FOUND_TORCH = importlib.util.find_spec(\"torch\") is not None\n", - "print(\"FOUND_TORCH\", FOUND_TORCH)\n" + "print(\"FOUND_TORCH\", FOUND_TORCH)" ] }, { @@ -381,7 +380,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Statistics Collection: 10it [00:10, 1.05s/it]\n" + "Statistics Collection: 10it [00:10, 1.03s/it]\n" ] }, { @@ -397,7 +396,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.95it/s]\n" + "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.93it/s]\n" ] }, { @@ -429,7 +428,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Statistics Collection: 10it [00:09, 1.01it/s]\n" + "Statistics Collection: 10it [00:09, 1.02it/s]\n" ] }, { @@ -445,7 +444,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.15it/s]\n" + "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.17it/s]\n" ] }, { @@ -497,7 +496,7 @@ "PytorchActivationQuantizationHolder()\n", "\n", "\n", - "\n", + "\n", "8.0\n" ] } @@ -520,12 +519,6 @@ "print(type(wkm.features_1_conv_0_2_activation_holder_quantizer))\n", "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer)\n", "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer.threshold_np)\n", - "#print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer.get_config())\n", - "#print(wkc)\n", - "#print(wki)\n", - "\n", - "\n", - "\n", "\n", "for error_method, data in quantized_models_dict.items():\n", " ##print(error_method)\n", @@ -1205,42 +1198,27 @@ }, "outputs": [], "source": [ - "layer_name1 = 'features.0.2' # Conv1_reluに相当\n", - "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BNに相当\n", + "layer_name1 = 'features.0.2' # Conv1_relu\n", + "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BN\n", "\n", - "# 特定のレイヤーの出力を取得するためのフックを設定\n", + "# add hook\n", "def get_layer_output(model, layer_name):\n", " outputs = {}\n", "\n", " def hook(module, input, output):\n", " outputs[layer_name] = output\n", "\n", - " # 指定したレイヤーにフックを追加\n", - " layer = dict(model.named_modules())[layer_name] # モジュール名を辞書形式で取得\n", + " # add hook\n", + " layer = dict(model.named_modules())[layer_name]\n", " layer.register_forward_hook(hook)\n", "\n", " return outputs\n", "\n", - "\n", - "\n", - "# Conv1_reluの出力を取得\n", + "# get Conv1_relu's output\n", "output_dict_relu = get_layer_output(float_model, layer_name1)\n", "\n", - "# expanded_conv_project_BNの出力を取得(features.1.conv.1 は活性化関数の次)\n", - "output_dict_project = get_layer_output(float_model, layer_name2)\n", - "\n", - "# モデルに入力データを通す\n", - "#input_tensor = torch.randn(1, 3, 224, 224) # バッチサイズ1の入力データ\n", - "#float_model(input_tensor)\n", - "\n", - "# それぞれの出力を取得\n", - "#output_relu = output_dict_relu[layer_name1]\n", - "#output_project = output_dict_project[layer_name2]\n", - "\n", - "#print(f\"Output from {layer_name1}:\", output_relu.shape)\n", - "#print(f\"Output from {layer_name2}:\", output_project.shape)\n", - "\n", - "\n" + "# get expanded_conv_project_BN's output(features.1.conv.1 is next to activation-layer)\n", + "output_dict_project = get_layer_output(float_model, layer_name2)\n" ] }, { @@ -1275,6 +1253,7 @@ } ], "source": [ + "# explore\n", "import torch\n", "print(torch.__version__)\n", "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", @@ -1295,7 +1274,7 @@ "name": "stderr", "output_type": "stream", "text": [ - " 0%| | 10/6250 [00:00<06:27, 16.10it/s]\n" + " 0%| | 10/6250 [00:00<05:49, 17.85it/s]\n" ] }, { @@ -1659,7 +1638,7 @@ } ], "source": [ - "print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'])\n" + "print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'])" ] }, { @@ -1795,20 +1774,6 @@ { "cell_type": "code", "execution_count": 22, - "id": "9199b59c4f10eca1", - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "#val_dataset = get_dataset(batch_size=50, shuffle=False)\n", - "#val_dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", - "#val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 23, "id": "631780a79e2cedf0", "metadata": { "collapsed": false @@ -1818,7 +1783,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [07:28<00:00, 1.15s/it]" + "100%|██████████| 391/391 [05:48<00:00, 1.12it/s]" ] }, { @@ -1876,7 +1841,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" @@ -1886,7 +1851,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [04:18<00:00, 1.51it/s]\n" + "100%|██████████| 391/391 [04:49<00:00, 1.35it/s]\n" ] }, { @@ -1903,7 +1868,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [05:56<00:00, 1.10it/s]" + "100%|██████████| 391/391 [04:42<00:00, 1.38it/s]" ] }, { @@ -1935,7 +1900,7 @@ " evaluation_results[error_method] = results\n", "\n", " # Print the results\n", - " print(f\"Results for {error_method}: Loss = {results[0]}, Accuracy = {results[1]}\")" + " print(f\"Results for {error_method}: Loss = {results[0]}, Accuracy = {results[1]}\")\n" ] }, { @@ -1962,7 +1927,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 59, "id": "qml4LLmWZLP4", "metadata": { "id": "qml4LLmWZLP4" @@ -1972,388 +1937,93 @@ "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: torchinfo in /mnt/share2/work/sss/ve310_1_pt/lib/python3.10/site-packages (1.8.0)\n", - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", - "odict_keys(['features_0_0_bn.weight', 'features_0_0_bn.layer.bias', 'features_1_conv_0_0_bn.weight', 'features_1_conv_0_0_bn.layer.bias', 'features_1_conv_1_bn.weight', 'features_1_conv_1_bn.layer.bias', 'features_2_conv_0_0_bn.weight', 'features_2_conv_0_0_bn.layer.bias', 'features_2_conv_1_0_bn.weight', 'features_2_conv_1_0_bn.layer.bias', 'features_2_conv_2_bn.weight', 'features_2_conv_2_bn.layer.bias', 'features_3_conv_0_0_bn.weight', 'features_3_conv_0_0_bn.layer.bias', 'features_3_conv_1_0_bn.weight', 'features_3_conv_1_0_bn.layer.bias', 'features_3_conv_2_bn.weight', 'features_3_conv_2_bn.layer.bias', 'features_4_conv_0_0_bn.weight', 'features_4_conv_0_0_bn.layer.bias', 'features_4_conv_1_0_bn.weight', 'features_4_conv_1_0_bn.layer.bias', 'features_4_conv_2_bn.weight', 'features_4_conv_2_bn.layer.bias', 'features_5_conv_0_0_bn.weight', 'features_5_conv_0_0_bn.layer.bias', 'features_5_conv_1_0_bn.weight', 'features_5_conv_1_0_bn.layer.bias', 'features_5_conv_2_bn.weight', 'features_5_conv_2_bn.layer.bias', 'features_6_conv_0_0_bn.weight', 'features_6_conv_0_0_bn.layer.bias', 'features_6_conv_1_0_bn.weight', 'features_6_conv_1_0_bn.layer.bias', 'features_6_conv_2_bn.weight', 'features_6_conv_2_bn.layer.bias', 'features_7_conv_0_0_bn.weight', 'features_7_conv_0_0_bn.layer.bias', 'features_7_conv_1_0_bn.weight', 'features_7_conv_1_0_bn.layer.bias', 'features_7_conv_2_bn.weight', 'features_7_conv_2_bn.layer.bias', 'features_8_conv_0_0_bn.weight', 'features_8_conv_0_0_bn.layer.bias', 'features_8_conv_1_0_bn.weight', 'features_8_conv_1_0_bn.layer.bias', 'features_8_conv_2_bn.weight', 'features_8_conv_2_bn.layer.bias', 'features_9_conv_0_0_bn.weight', 'features_9_conv_0_0_bn.layer.bias', 'features_9_conv_1_0_bn.weight', 'features_9_conv_1_0_bn.layer.bias', 'features_9_conv_2_bn.weight', 'features_9_conv_2_bn.layer.bias', 'features_10_conv_0_0_bn.weight', 'features_10_conv_0_0_bn.layer.bias', 'features_10_conv_1_0_bn.weight', 'features_10_conv_1_0_bn.layer.bias', 'features_10_conv_2_bn.weight', 'features_10_conv_2_bn.layer.bias', 'features_11_conv_0_0_bn.weight', 'features_11_conv_0_0_bn.layer.bias', 'features_11_conv_1_0_bn.weight', 'features_11_conv_1_0_bn.layer.bias', 'features_11_conv_2_bn.weight', 'features_11_conv_2_bn.layer.bias', 'features_12_conv_0_0_bn.weight', 'features_12_conv_0_0_bn.layer.bias', 'features_12_conv_1_0_bn.weight', 'features_12_conv_1_0_bn.layer.bias', 'features_12_conv_2_bn.weight', 'features_12_conv_2_bn.layer.bias', 'features_13_conv_0_0_bn.weight', 'features_13_conv_0_0_bn.layer.bias', 'features_13_conv_1_0_bn.weight', 'features_13_conv_1_0_bn.layer.bias', 'features_13_conv_2_bn.weight', 'features_13_conv_2_bn.layer.bias', 'features_14_conv_0_0_bn.weight', 'features_14_conv_0_0_bn.layer.bias', 'features_14_conv_1_0_bn.weight', 'features_14_conv_1_0_bn.layer.bias', 'features_14_conv_2_bn.weight', 'features_14_conv_2_bn.layer.bias', 'features_15_conv_0_0_bn.weight', 'features_15_conv_0_0_bn.layer.bias', 'features_15_conv_1_0_bn.weight', 'features_15_conv_1_0_bn.layer.bias', 'features_15_conv_2_bn.weight', 'features_15_conv_2_bn.layer.bias', 'features_16_conv_0_0_bn.weight', 'features_16_conv_0_0_bn.layer.bias', 'features_16_conv_1_0_bn.weight', 'features_16_conv_1_0_bn.layer.bias', 'features_16_conv_2_bn.weight', 'features_16_conv_2_bn.layer.bias', 'features_17_conv_0_0_bn.weight', 'features_17_conv_0_0_bn.layer.bias', 'features_17_conv_1_0_bn.weight', 'features_17_conv_1_0_bn.layer.bias', 'features_17_conv_2_bn.weight', 'features_17_conv_2_bn.layer.bias', 'features_18_0_bn.weight', 'features_18_0_bn.layer.bias', 'classifier_1.weight', 'classifier_1.layer.bias'])\n", - "====================================================================================================\n", - "Layer (type:depth-idx) Output Shape Param #\n", - "====================================================================================================\n", - "PytorchModel [1, 1000] --\n", - "├─DummyPlaceHolder: 1-1 [1, 3, 224, 224] --\n", - "├─PytorchActivationQuantizationHolder: 1-2 [1, 3, 224, 224] --\n", - "├─PytorchQuantizationWrapper: 1-3 [1, 32, 112, 112] 864\n", - "│ └─Conv2d: 2-1 [1, 32, 112, 112] 32\n", - "├─ReLU6: 1-4 [1, 32, 112, 112] --\n", - "├─PytorchActivationQuantizationHolder: 1-5 [1, 32, 112, 112] --\n", - "├─PytorchQuantizationWrapper: 1-6 [1, 32, 112, 112] 288\n", - "│ └─Conv2d: 2-2 [1, 32, 112, 112] 32\n", - "├─ReLU6: 1-7 [1, 32, 112, 112] --\n", - "├─PytorchActivationQuantizationHolder: 1-8 [1, 32, 112, 112] --\n", - "├─PytorchQuantizationWrapper: 1-9 [1, 16, 112, 112] 512\n", - "│ └─Conv2d: 2-3 [1, 16, 112, 112] 16\n", - "├─PytorchActivationQuantizationHolder: 1-10 [1, 16, 112, 112] --\n", - "├─PytorchQuantizationWrapper: 1-11 [1, 96, 112, 112] 1,536\n", - "│ └─Conv2d: 2-4 [1, 96, 112, 112] 96\n", - "├─ReLU6: 1-12 [1, 96, 112, 112] --\n", - "├─PytorchActivationQuantizationHolder: 1-13 [1, 96, 112, 112] --\n", - "├─PytorchQuantizationWrapper: 1-14 [1, 96, 56, 56] 864\n", - "│ └─Conv2d: 2-5 [1, 96, 56, 56] 96\n", - "├─ReLU6: 1-15 [1, 96, 56, 56] --\n", - "├─PytorchActivationQuantizationHolder: 1-16 [1, 96, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-17 [1, 24, 56, 56] 2,304\n", - "│ └─Conv2d: 2-6 [1, 24, 56, 56] 24\n", - "├─PytorchActivationQuantizationHolder: 1-18 [1, 24, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-19 [1, 144, 56, 56] 3,456\n", - "│ └─Conv2d: 2-7 [1, 144, 56, 56] 144\n", - "├─ReLU6: 1-20 [1, 144, 56, 56] --\n", - "├─PytorchActivationQuantizationHolder: 1-21 [1, 144, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-22 [1, 144, 56, 56] 1,296\n", - "│ └─Conv2d: 2-8 [1, 144, 56, 56] 144\n", - "├─ReLU6: 1-23 [1, 144, 56, 56] --\n", - "├─PytorchActivationQuantizationHolder: 1-24 [1, 144, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-25 [1, 24, 56, 56] 3,456\n", - "│ └─Conv2d: 2-9 [1, 24, 56, 56] 24\n", - "├─PytorchActivationQuantizationHolder: 1-26 [1, 24, 56, 56] --\n", - "├─PytorchActivationQuantizationHolder: 1-27 [1, 24, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-28 [1, 144, 56, 56] 3,456\n", - "│ └─Conv2d: 2-10 [1, 144, 56, 56] 144\n", - "├─ReLU6: 1-29 [1, 144, 56, 56] --\n", - "├─PytorchActivationQuantizationHolder: 1-30 [1, 144, 56, 56] --\n", - "├─PytorchQuantizationWrapper: 1-31 [1, 144, 28, 28] 1,296\n", - "│ └─Conv2d: 2-11 [1, 144, 28, 28] 144\n", - "├─ReLU6: 1-32 [1, 144, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-33 [1, 144, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-34 [1, 32, 28, 28] 4,608\n", - "│ └─Conv2d: 2-12 [1, 32, 28, 28] 32\n", - "├─PytorchActivationQuantizationHolder: 1-35 [1, 32, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-36 [1, 192, 28, 28] 6,144\n", - "│ └─Conv2d: 2-13 [1, 192, 28, 28] 192\n", - "├─ReLU6: 1-37 [1, 192, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-38 [1, 192, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-39 [1, 192, 28, 28] 1,728\n", - "│ └─Conv2d: 2-14 [1, 192, 28, 28] 192\n", - "├─ReLU6: 1-40 [1, 192, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-41 [1, 192, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-42 [1, 32, 28, 28] 6,144\n", - "│ └─Conv2d: 2-15 [1, 32, 28, 28] 32\n", - "├─PytorchActivationQuantizationHolder: 1-43 [1, 32, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-44 [1, 32, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-45 [1, 192, 28, 28] 6,144\n", - "│ └─Conv2d: 2-16 [1, 192, 28, 28] 192\n", - "├─ReLU6: 1-46 [1, 192, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-47 [1, 192, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-48 [1, 192, 28, 28] 1,728\n", - "│ └─Conv2d: 2-17 [1, 192, 28, 28] 192\n", - "├─ReLU6: 1-49 [1, 192, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-50 [1, 192, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-51 [1, 32, 28, 28] 6,144\n", - "│ └─Conv2d: 2-18 [1, 32, 28, 28] 32\n", - "├─PytorchActivationQuantizationHolder: 1-52 [1, 32, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-53 [1, 32, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-54 [1, 192, 28, 28] 6,144\n", - "│ └─Conv2d: 2-19 [1, 192, 28, 28] 192\n", - "├─ReLU6: 1-55 [1, 192, 28, 28] --\n", - "├─PytorchActivationQuantizationHolder: 1-56 [1, 192, 28, 28] --\n", - "├─PytorchQuantizationWrapper: 1-57 [1, 192, 14, 14] 1,728\n", - "│ └─Conv2d: 2-20 [1, 192, 14, 14] 192\n", - "├─ReLU6: 1-58 [1, 192, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-59 [1, 192, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-60 [1, 64, 14, 14] 12,288\n", - "│ └─Conv2d: 2-21 [1, 64, 14, 14] 64\n", - "├─PytorchActivationQuantizationHolder: 1-61 [1, 64, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-62 [1, 384, 14, 14] 24,576\n", - "│ └─Conv2d: 2-22 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-63 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-64 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-65 [1, 384, 14, 14] 3,456\n", - "│ └─Conv2d: 2-23 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-66 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-67 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-68 [1, 64, 14, 14] 24,576\n", - "│ └─Conv2d: 2-24 [1, 64, 14, 14] 64\n", - "├─PytorchActivationQuantizationHolder: 1-69 [1, 64, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-70 [1, 64, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-71 [1, 384, 14, 14] 24,576\n", - "│ └─Conv2d: 2-25 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-72 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-73 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-74 [1, 384, 14, 14] 3,456\n", - "│ └─Conv2d: 2-26 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-75 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-76 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-77 [1, 64, 14, 14] 24,576\n", - "│ └─Conv2d: 2-27 [1, 64, 14, 14] 64\n", - "├─PytorchActivationQuantizationHolder: 1-78 [1, 64, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-79 [1, 64, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-80 [1, 384, 14, 14] 24,576\n", - "│ └─Conv2d: 2-28 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-81 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-82 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-83 [1, 384, 14, 14] 3,456\n", - "│ └─Conv2d: 2-29 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-84 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-85 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-86 [1, 64, 14, 14] 24,576\n", - "│ └─Conv2d: 2-30 [1, 64, 14, 14] 64\n", - "├─PytorchActivationQuantizationHolder: 1-87 [1, 64, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-88 [1, 64, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-89 [1, 384, 14, 14] 24,576\n", - "│ └─Conv2d: 2-31 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-90 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-91 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-92 [1, 384, 14, 14] 3,456\n", - "│ └─Conv2d: 2-32 [1, 384, 14, 14] 384\n", - "├─ReLU6: 1-93 [1, 384, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-94 [1, 384, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-95 [1, 96, 14, 14] 36,864\n", - "│ └─Conv2d: 2-33 [1, 96, 14, 14] 96\n", - "├─PytorchActivationQuantizationHolder: 1-96 [1, 96, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-97 [1, 576, 14, 14] 55,296\n", - "│ └─Conv2d: 2-34 [1, 576, 14, 14] 576\n", - "├─ReLU6: 1-98 [1, 576, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-99 [1, 576, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-100 [1, 576, 14, 14] 5,184\n", - "│ └─Conv2d: 2-35 [1, 576, 14, 14] 576\n", - "├─ReLU6: 1-101 [1, 576, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-102 [1, 576, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-103 [1, 96, 14, 14] 55,296\n", - "│ └─Conv2d: 2-36 [1, 96, 14, 14] 96\n", - "├─PytorchActivationQuantizationHolder: 1-104 [1, 96, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-105 [1, 96, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-106 [1, 576, 14, 14] 55,296\n", - "│ └─Conv2d: 2-37 [1, 576, 14, 14] 576\n", - "├─ReLU6: 1-107 [1, 576, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-108 [1, 576, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-109 [1, 576, 14, 14] 5,184\n", - "│ └─Conv2d: 2-38 [1, 576, 14, 14] 576\n", - "├─ReLU6: 1-110 [1, 576, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-111 [1, 576, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-112 [1, 96, 14, 14] 55,296\n", - "│ └─Conv2d: 2-39 [1, 96, 14, 14] 96\n", - "├─PytorchActivationQuantizationHolder: 1-113 [1, 96, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-114 [1, 96, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-115 [1, 576, 14, 14] 55,296\n", - "│ └─Conv2d: 2-40 [1, 576, 14, 14] 576\n", - "├─ReLU6: 1-116 [1, 576, 14, 14] --\n", - "├─PytorchActivationQuantizationHolder: 1-117 [1, 576, 14, 14] --\n", - "├─PytorchQuantizationWrapper: 1-118 [1, 576, 7, 7] 5,184\n", - "│ └─Conv2d: 2-41 [1, 576, 7, 7] 576\n", - "├─ReLU6: 1-119 [1, 576, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-120 [1, 576, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-121 [1, 160, 7, 7] 92,160\n", - "│ └─Conv2d: 2-42 [1, 160, 7, 7] 160\n", - "├─PytorchActivationQuantizationHolder: 1-122 [1, 160, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-123 [1, 960, 7, 7] 153,600\n", - "│ └─Conv2d: 2-43 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-124 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-125 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-126 [1, 960, 7, 7] 8,640\n", - "│ └─Conv2d: 2-44 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-127 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-128 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-129 [1, 160, 7, 7] 153,600\n", - "│ └─Conv2d: 2-45 [1, 160, 7, 7] 160\n", - "├─PytorchActivationQuantizationHolder: 1-130 [1, 160, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-131 [1, 160, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-132 [1, 960, 7, 7] 153,600\n", - "│ └─Conv2d: 2-46 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-133 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-134 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-135 [1, 960, 7, 7] 8,640\n", - "│ └─Conv2d: 2-47 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-136 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-137 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-138 [1, 160, 7, 7] 153,600\n", - "│ └─Conv2d: 2-48 [1, 160, 7, 7] 160\n", - "├─PytorchActivationQuantizationHolder: 1-139 [1, 160, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-140 [1, 160, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-141 [1, 960, 7, 7] 153,600\n", - "│ └─Conv2d: 2-49 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-142 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-143 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-144 [1, 960, 7, 7] 8,640\n", - "│ └─Conv2d: 2-50 [1, 960, 7, 7] 960\n", - "├─ReLU6: 1-145 [1, 960, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-146 [1, 960, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-147 [1, 320, 7, 7] 307,200\n", - "│ └─Conv2d: 2-51 [1, 320, 7, 7] 320\n", - "├─PytorchActivationQuantizationHolder: 1-148 [1, 320, 7, 7] --\n", - "├─PytorchQuantizationWrapper: 1-149 [1, 1280, 7, 7] 409,600\n", - "│ └─Conv2d: 2-52 [1, 1280, 7, 7] 1,280\n", - "├─ReLU6: 1-150 [1, 1280, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-151 [1, 1280, 7, 7] --\n", - "├─PytorchActivationQuantizationHolder: 1-152 [1, 1280, 1, 1] --\n", - "├─Dropout: 1-153 [1, 1280] --\n", - "├─PytorchQuantizationWrapper: 1-154 [1, 1000] 1,280,000\n", - "│ └─Linear: 2-53 [1, 1000] 1,000\n", - "├─PytorchActivationQuantizationHolder: 1-155 [1, 1000] --\n", - "====================================================================================================\n", - "Total params: 3,487,816\n", - "Trainable params: 18,056\n", - "Non-trainable params: 3,469,760\n", - "Total mult-adds (M): 6.68\n", - "====================================================================================================\n", - "Input size (MB): 0.60\n", - "Forward/backward pass size (MB): 53.43\n", - "Params size (MB): 0.07\n", - "Estimated Total Size (MB): 54.11\n", - "====================================================================================================\n", - "layer features_0_0_bn.weight\n", - "layer features_0_0_bn.layer.bias\n", - "layer features_1_conv_0_0_bn.weight\n", - "layer features_1_conv_0_0_bn.layer.bias\n", - "layer features_1_conv_1_bn.weight\n", - "layer features_1_conv_1_bn.layer.bias\n", - "layer features_2_conv_0_0_bn.weight\n", - "layer features_2_conv_0_0_bn.layer.bias\n", - "layer features_2_conv_1_0_bn.weight\n", - "layer features_2_conv_1_0_bn.layer.bias\n", - "layer features_2_conv_2_bn.weight\n", - "layer features_2_conv_2_bn.layer.bias\n", - "layer features_3_conv_0_0_bn.weight\n", - "layer features_3_conv_0_0_bn.layer.bias\n", - "layer features_3_conv_1_0_bn.weight\n", - "layer features_3_conv_1_0_bn.layer.bias\n", - "layer features_3_conv_2_bn.weight\n", - "layer features_3_conv_2_bn.layer.bias\n", - "layer features_4_conv_0_0_bn.weight\n", - "layer features_4_conv_0_0_bn.layer.bias\n", - "layer features_4_conv_1_0_bn.weight\n", - "layer features_4_conv_1_0_bn.layer.bias\n", - "layer features_4_conv_2_bn.weight\n", - "layer features_4_conv_2_bn.layer.bias\n", - "layer features_5_conv_0_0_bn.weight\n", - "layer features_5_conv_0_0_bn.layer.bias\n", - "layer features_5_conv_1_0_bn.weight\n", - "layer features_5_conv_1_0_bn.layer.bias\n", - "layer features_5_conv_2_bn.weight\n", - "layer features_5_conv_2_bn.layer.bias\n", - "layer features_6_conv_0_0_bn.weight\n", - "layer features_6_conv_0_0_bn.layer.bias\n", - "layer features_6_conv_1_0_bn.weight\n", - "layer features_6_conv_1_0_bn.layer.bias\n", - "layer features_6_conv_2_bn.weight\n", - "layer features_6_conv_2_bn.layer.bias\n", - "layer features_7_conv_0_0_bn.weight\n", - "layer features_7_conv_0_0_bn.layer.bias\n", - "layer features_7_conv_1_0_bn.weight\n", - "layer features_7_conv_1_0_bn.layer.bias\n", - "layer features_7_conv_2_bn.weight\n", - "layer features_7_conv_2_bn.layer.bias\n", - "layer features_8_conv_0_0_bn.weight\n", - "layer features_8_conv_0_0_bn.layer.bias\n", - "layer features_8_conv_1_0_bn.weight\n", - "layer features_8_conv_1_0_bn.layer.bias\n", - "layer features_8_conv_2_bn.weight\n", - "layer features_8_conv_2_bn.layer.bias\n", - "layer features_9_conv_0_0_bn.weight\n", - "layer features_9_conv_0_0_bn.layer.bias\n", - "layer features_9_conv_1_0_bn.weight\n", - "layer features_9_conv_1_0_bn.layer.bias\n", - "layer features_9_conv_2_bn.weight\n", - "layer features_9_conv_2_bn.layer.bias\n", - "layer features_10_conv_0_0_bn.weight\n", - "layer features_10_conv_0_0_bn.layer.bias\n", - "layer features_10_conv_1_0_bn.weight\n", - "layer features_10_conv_1_0_bn.layer.bias\n", - "layer features_10_conv_2_bn.weight\n", - "layer features_10_conv_2_bn.layer.bias\n", - "layer features_11_conv_0_0_bn.weight\n", - "layer features_11_conv_0_0_bn.layer.bias\n", - "layer features_11_conv_1_0_bn.weight\n", - "layer features_11_conv_1_0_bn.layer.bias\n", - "layer features_11_conv_2_bn.weight\n", - "layer features_11_conv_2_bn.layer.bias\n", - "layer features_12_conv_0_0_bn.weight\n", - "layer features_12_conv_0_0_bn.layer.bias\n", - "layer features_12_conv_1_0_bn.weight\n", - "layer features_12_conv_1_0_bn.layer.bias\n", - "layer features_12_conv_2_bn.weight\n", - "layer features_12_conv_2_bn.layer.bias\n", - "layer features_13_conv_0_0_bn.weight\n", - "layer features_13_conv_0_0_bn.layer.bias\n", - "layer features_13_conv_1_0_bn.weight\n", - "layer features_13_conv_1_0_bn.layer.bias\n", - "layer features_13_conv_2_bn.weight\n", - "layer features_13_conv_2_bn.layer.bias\n", - "layer features_14_conv_0_0_bn.weight\n", - "layer features_14_conv_0_0_bn.layer.bias\n", - "layer features_14_conv_1_0_bn.weight\n", - "layer features_14_conv_1_0_bn.layer.bias\n", - "layer features_14_conv_2_bn.weight\n", - "layer features_14_conv_2_bn.layer.bias\n", - "layer features_15_conv_0_0_bn.weight\n", - "layer features_15_conv_0_0_bn.layer.bias\n", - "layer features_15_conv_1_0_bn.weight\n", - "layer features_15_conv_1_0_bn.layer.bias\n", - "layer features_15_conv_2_bn.weight\n", - "layer features_15_conv_2_bn.layer.bias\n", - "layer features_16_conv_0_0_bn.weight\n", - "layer features_16_conv_0_0_bn.layer.bias\n", - "layer features_16_conv_1_0_bn.weight\n", - "layer features_16_conv_1_0_bn.layer.bias\n", - "layer features_16_conv_2_bn.weight\n", - "layer features_16_conv_2_bn.layer.bias\n", - "layer features_17_conv_0_0_bn.weight\n", - "layer features_17_conv_0_0_bn.layer.bias\n", - "layer features_17_conv_1_0_bn.weight\n", - "layer features_17_conv_1_0_bn.layer.bias\n", - "layer features_17_conv_2_bn.weight\n", - "layer features_17_conv_2_bn.layer.bias\n", - "layer features_18_0_bn.weight\n", - "layer features_18_0_bn.layer.bias\n", - "layer classifier_1.weight\n", - "layer classifier_1.layer.bias\n" + "Layer indices potentially using ReLU: [5, 9, 16, 20, 27, 31, 39, 43, 50, 54, 62, 66, 74, 78, 85, 89, 97, 101, 109, 113, 121, 125, 132, 136, 144, 148, 156, 160, 167, 171, 179, 183, 191, 195, 202]\n", + "Number of relu layers 35\n" ] } ], "source": [ - "!pip install torchinfo\n", - "from torchinfo import summary\n", + "import torch.nn as nn\n", + "\n", + "def flatten_model(modules):\n", + " def flatten_list(_2d_list):\n", + " flat_list = []\n", + " # Iterate through the outer list\n", + " for element in _2d_list:\n", + " if type(element) is list:\n", + " # If the element is of type list, iterate through the sublist\n", + " for item in element:\n", + " flat_list.append(item)\n", + " else:\n", + " flat_list.append(element)\n", + " return flat_list\n", + "\n", + " ret = []\n", + " try:\n", + " for _, n in modules:\n", + " ret.append(flatten_model(n))\n", + " except:\n", + " try:\n", + " if str(modules._modules.items()) == \"odict_items([])\":\n", + " ret.append(modules)\n", + " else:\n", + " for _, n in modules._modules.items():\n", + " ret.append(flatten_model(n))\n", + " except:\n", + " ret.append(modules)\n", + " return flatten_list(ret)\n", + "\n", "quantized_model = data[\"quantized_model\"]\n", "quantizer_object = quantized_model.features_0_2_activation_holder_quantizer\n", "\n", "quantized_model = data[\"quantized_model\"]\n", "\n", - "print(quantized_model.state_dict().keys())\n", + "#print(quantized_model.state_dict().keys())\n", "\n", "relu_layer_indices = []\n", "\n", - "print(summary(\n", - " quantized_model,\n", - " input_size=(1, 3, 224, 224),\n", - " col_names=[\"output_size\", \"num_params\"],\n", - "))\n", + "target_layers =[]\n", + "module_list =[module for module in quantized_model.modules()]\n", + "flatted_list= flatten_model(module_list)\n", "\n", - "for i, layer in enumerate(quantized_model.state_dict().keys()):\n", - " print('layer', i, layer)\n", - " # Convert the layer's configuration to a string\n", - " #layer_config_str = str(layer.get_config())\n", + "for count, value in enumerate(flatted_list):\n", + " if count == 0: continue\n", + " #print(count, value, type(value))\n", + " if isinstance(value, (nn.ReLU6)):\n", + " #if isinstance(value, (nn.Conv2d)):\n", + " ##print(count, value)\n", + " relu_layer_indices.append(count)\n", "\n", - " #layer_class_str = str(layer.__class__.__name__)\n", - "\n", - " # Check if \"relu\" is mentioned in the layer's configuration or class name\n", - " #if 'relu' in layer_config_str.lower() or 'relu' in layer_class_str.lower():\n", - " # relu_layer_indices.append(i)\n", - "\n", - "#print(\"Layer indices potentially using ReLU:\", relu_layer_indices)\n", - "#print(\"Number of relu layers \" + str(len(relu_layer_indices)))\n" + "print(\"Layer indices potentially using ReLU:\", relu_layer_indices)\n", + "print(\"Number of relu layers \" + str(len(relu_layer_indices)))\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", "metadata": { "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + ")\n", + "PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + ")\n" + ] + } + ], "source": [ "for error_method, data in quantized_models_dict.items():\n", " quantized_model = data[\"quantized_model\"]\n", - " print(quantized_model.layers[1])" + " layer = quantized_model.features_0_0_bn\n", + " print(layer)" ] }, { @@ -2398,7 +2068,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.5" + "version": "3.10.16" } }, "nbformat": 4, From 746cb573ad3fba37d191e61c7d39328ec3f3236e Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 31 Jan 2025 10:18:29 +0900 Subject: [PATCH 03/30] Fixed ofirgo-san's review comment. --- ..._pytorch_activation_threshold_search.ipynb | 682 ++---------------- 1 file changed, 63 insertions(+), 619 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index bf52a77ac..813d75716 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -9,7 +9,7 @@ "source": [ "# A Practical Guide to Activation Threshold Search in Post-Training Quantization\n", "\n", - "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/mct_features_notebooks/keras/example_keras_activation_threshold_search.ipynb)\n", + "[Run this tutorial in Google Colab](https://colab.research.google.com/github/sony/model_optimization/blob/main/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb)\n", "\n", "## Overview\n", "This tutorial demonstrates how to find the optimal activation threshold, a key component in MCT's post-training quantization workflow.\n", @@ -30,7 +30,7 @@ "$$\n", "\n", "- $ERR(t)$ : The quantization error function dependent on the threshold $t$.\n", - "- \n", + "\n", "- $n_s$: The size of the representative dataset.\n", "\n", "- $\\sum$: Summation over all elements $X$ in the flattened dataset $F_l(D)$.\n", @@ -67,44 +67,43 @@ { "cell_type": "code", "execution_count": 1, - "id": "cb67cee7", - "metadata": {}, - "outputs": [], - "source": [ - "import importlib" - ] - }, - { - "cell_type": "code", - "execution_count": 2, "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "metadata": { "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.0\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], "source": [ - "TORCH_VER = \"2.4.0\"\n", - "if not importlib.util.find_spec('torch'):\n", - " !pip install -q torch=={TORCH_VER} torchvision" + "!pip install -q torch torchvision" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "7837babf2112542b", "metadata": { "collapsed": false }, "outputs": [], "source": [ + "import importlib\n", "if not importlib.util.find_spec('model_compression_toolkit'):\n", " !pip install model_compression_toolkit" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19", "metadata": { "id": "b3f0acc8-281c-4bca-b0b9-3d7677105f19" @@ -114,9 +113,7 @@ "import torch\n", "from torch.utils.data import DataLoader\n", "from torchvision.models import mobilenet_v2, MobileNet_V2_Weights\n", - "from torchvision.datasets import ImageNet\n", - "import numpy as np\n", - "import random" + "from torchvision.datasets import ImageNet" ] }, { @@ -131,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "468d67cd5f25886e", "metadata": { "collapsed": false @@ -160,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "_ztv72uM6-UT", "metadata": { "id": "_ztv72uM6-UT" @@ -185,36 +182,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "9e5658c2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "50000\n", - "Dataset ImageNet\n", - " Number of datapoints: 50000\n", - " Root location: ./imagenet\n", - " Split: val\n", - " StandardTransform\n", - "Transform: ImageClassification(\n", - " crop_size=[224]\n", - " resize_size=[232]\n", - " mean=[0.485, 0.456, 0.406]\n", - " std=[0.229, 0.224, 0.225]\n", - " interpolation=InterpolationMode.BILINEAR\n", - " )\n" - ] - } - ], + "outputs": [], "source": [ - "dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())\n", - "print(type(dataset))\n", - "print(len(dataset))\n", - "print(dataset)" + "dataset = ImageNet(root='./imagenet', split='val', transform=weights.transforms())" ] }, { @@ -230,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "1bdb4144e4ce2ab6", "metadata": { "collapsed": false @@ -240,8 +213,7 @@ "batch_size = 8\n", "n_iter = 10\n", "\n", - "#dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n", - "dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=False)\n", + "dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n", "\n", "def representative_dataset_gen():\n", " dataloader_iter = iter(dataloader)\n", @@ -262,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "554719effaf90250", "metadata": { "collapsed": false @@ -295,7 +267,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "jtiZzXmTjxuI", "metadata": { "id": "jtiZzXmTjxuI" @@ -337,26 +309,7 @@ }, { "cell_type": "code", - "execution_count": 11, - "id": "b609660e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FOUND_TORCH True\n" - ] - } - ], - "source": [ - "FOUND_TORCH = importlib.util.find_spec(\"torch\") is not None\n", - "print(\"FOUND_TORCH\", FOUND_TORCH)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", "metadata": { "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" @@ -366,96 +319,12 @@ "name": "stderr", "output_type": "stream", "text": [ - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DepthwiseConv2D is not in model.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Statistics Collection: 10it [00:10, 1.03s/it]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Running quantization parameters search. This process might take some time, depending on the model size and the selected quantization methods.\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.93it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Please run your accuracy evaluation on the exported quantized model to verify it's accuracy.\n", - "Checkout the FAQ and Troubleshooting pages for resolving common issues and improving the quantized model accuracy:\n", - "FAQ: https://github.com/sony/model_optimization/tree/main/FAQ.md\n", - "Quantization Troubleshooting: https://github.com/sony/model_optimization/tree/main/quantization_troubleshooting.md\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DepthwiseConv2D is not in model.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Statistics Collection: 10it [00:09, 1.02it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Running quantization parameters search. This process might take some time, depending on the model size and the selected quantization methods.\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.17it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Please run your accuracy evaluation on the exported quantized model to verify it's accuracy.\n", - "Checkout the FAQ and Troubleshooting pages for resolving common issues and improving the quantized model accuracy:\n", - "FAQ: https://github.com/sony/model_optimization/tree/main/FAQ.md\n", - "Quantization Troubleshooting: https://github.com/sony/model_optimization/tree/main/quantization_troubleshooting.md\n" + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", + "Statistics Collection: 10it [00:10, 1.01s/it]\n", + "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.03it/s]\n", + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", + "Statistics Collection: 10it [00:10, 1.01s/it]\n", + "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.06it/s]\n" ] } ], @@ -482,52 +351,6 @@ " }\n" ] }, - { - "cell_type": "code", - "execution_count": 13, - "id": "24ae88fa", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "PytorchActivationQuantizationHolder()\n", - "\n", - "\n", - "\n", - "8.0\n" - ] - } - ], - "source": [ - "import mct_quantizers\n", - "\n", - "import mct_quantizers.pytorch.quantizers.activation_inferable_quantizers.activation_pot_inferable_quantizer\n", - "\n", - "#print(quantized_models_dict)\n", - "print(type(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model']))\n", - "#print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'].features_0_2.activation_holder_quantizer)\n", - "\n", - "wkm = quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model']\n", - "wkc = quantized_models_dict[QuantizationErrorMethod.MSE]['quantization_config']\n", - "wki = quantized_models_dict[QuantizationErrorMethod.MSE]['quantization_info']\n", - "\n", - "print(wkm.features_1_conv_0_2_activation_holder_quantizer)\n", - "print(type(wkm))\n", - "print(type(wkm.features_1_conv_0_2_activation_holder_quantizer))\n", - "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer)\n", - "print(wkm.features_1_conv_0_2_activation_holder_quantizer.activation_holder_quantizer.threshold_np)\n", - "\n", - "for error_method, data in quantized_models_dict.items():\n", - " ##print(error_method)\n", - " #print(data['quantized_model'].features_0_0_bn.layer)\n", - " #print(type(data['quantized_model'].features_0_0_bn))\n", - " #print(error_method, data)\n", - " pass\n" - ] - }, { "cell_type": "markdown", "id": "A8UHRsh2khM4", @@ -545,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", "metadata": { "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" @@ -555,7 +378,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "### 0\n", " MobileNetV2(\n", " (features): Sequential(\n", " (0): Conv2dNormActivation(\n", @@ -841,7 +663,6 @@ " (1): Linear(in_features=1280, out_features=1000, bias=True)\n", " )\n", ")\n", - "### 1\n", "features Sequential(\n", " (0): Conv2dNormActivation(\n", " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", @@ -1121,19 +942,14 @@ " (2): ReLU6(inplace=True)\n", " )\n", ")\n", - "### 2\n", "features.0 Conv2dNormActivation(\n", " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (2): ReLU6(inplace=True)\n", ")\n", - "### 3\n", "features.0.0 Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - "### 4\n", "features.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - "### 5\n", "features.0.2 ReLU6(inplace=True)\n", - "### 6\n", "features.1 InvertedResidual(\n", " (conv): Sequential(\n", " (0): Conv2dNormActivation(\n", @@ -1145,7 +961,6 @@ " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " )\n", ")\n", - "### 7\n", "features.1.conv Sequential(\n", " (0): Conv2dNormActivation(\n", " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", @@ -1155,13 +970,11 @@ " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", ")\n", - "### 8\n", "features.1.conv.0 Conv2dNormActivation(\n", " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", " (2): ReLU6(inplace=True)\n", ")\n", - "### 9\n", "features.1.conv.0.0 Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n" ] } @@ -1169,7 +982,6 @@ "source": [ "for index, (name, layer) in enumerate(float_model.named_modules()):\n", " if index < 10:\n", - " print(\"###\", index)\n", " print(name, layer)\n", " else:\n", " break" @@ -1191,14 +1003,14 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d", "metadata": { "id": "1f9dd3f3-6e22-4be9-9beb-29568ff14c9d" }, "outputs": [], "source": [ - "layer_name1 = 'features.0.2' # Conv1_relu\n", + "layer_name1 = 'features.0.2' # Conv1_relu\n", "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BN\n", "\n", "# add hook\n", @@ -1233,40 +1045,7 @@ }, { "cell_type": "code", - "execution_count": 16, - "id": "eaeb9888-5d67-4979-af50-80781a811b4b", - "metadata": { - "id": "eaeb9888-5d67-4979-af50-80781a811b4b" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2.4.0+cu121\n", - "cuda:0\n", - "NVIDIA GeForce RTX 3090\n", - "(8, 6)\n", - "0\n", - "1\n" - ] - } - ], - "source": [ - "# explore\n", - "import torch\n", - "print(torch.__version__)\n", - "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", - "print(device)\n", - "print(torch.cuda.get_device_name())\n", - "print(torch.cuda.get_device_capability())\n", - "print(torch.cuda.current_device())\n", - "print(torch.cuda.device_count())" - ] - }, - { - "cell_type": "code", - "execution_count": 17, + "execution_count": 13, "id": "4a5bf3dd", "metadata": {}, "outputs": [ @@ -1274,34 +1053,7 @@ "name": "stderr", "output_type": "stream", "text": [ - " 0%| | 10/6250 [00:00<05:49, 17.85it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0. 0. 0. ... 0.24012035 1.2019204 1.4022245 ] 35323904\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.25255647\n", - "0.6007481\n", - "0.6139736\n", - "0.25284094\n", - "0.0\n", - "0.0\n", - "0.25791883\n", - "0.15303095\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n", - "0.0\n" + "10it [00:00, 13.41it/s]\n" ] } ], @@ -1311,42 +1063,21 @@ "activation_batches_relu = []\n", "activation_batches_project = []\n", "with torch.no_grad():\n", + " device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", " float_model = float_model.to(device)\n", - " for index, data in enumerate(tqdm(dataloader)):\n", - " images, labels = data\n", - " images, labels = images.to(device), labels.to(device)\n", + " for data in tqdm(representative_dataset_gen()):\n", + " images = data[0]\n", + " images = images.to(device)\n", "\n", " float_model(images)\n", "\n", " activations_relu = output_dict_relu[layer_name1]\n", - " #activation_batches_relu.append(activations_relu.to('cpu'))\n", " activation_batches_relu.append(activations_relu.to('cpu').detach().numpy().copy())\n", " activations_project = output_dict_project[layer_name2]\n", - " #activation_batches_project.append(activations_project.to('cpu'))\n", " activation_batches_project.append(activations_project.to('cpu').detach().numpy().copy())\n", "\n", - " #print(\"images.shape\", images.shape, type(images))\n", - " #print(\"images0\", images[0])\n", - " #print(\"activations_relu\", type(activations_relu))\n", - " #print(\"activations_relu.shape\", activations_relu.shape)\n", - " #print(\"activations_project\", type(activations_project))\n", - " #print(\"activations_project.shape\", activations_project.shape)\n", - "\n", - " del(activations_relu)\n", - " del(activations_project)\n", - " del(data)\n", - " del(images)\n", - " del(labels)\n", - " \n", - " if index >= 10:\n", - " break\n", - "\n", " all_activations_relu = np.concatenate(activation_batches_relu, axis=0).flatten()\n", - " all_activations_project = np.concatenate(activation_batches_project, axis=0).flatten()\n", - "\n", - "print(all_activations_relu, len(all_activations_relu))\n", - "for i in range(20):\n", - " print(all_activations_relu[i])\n" + " all_activations_project = np.concatenate(activation_batches_project, axis=0).flatten()" ] }, { @@ -1363,287 +1094,7 @@ }, { "cell_type": "code", - "execution_count": 18, - "id": "20b6ad11", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PytorchModel(\n", - " (x): DummyPlaceHolder()\n", - " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " (features_0_2): ReLU6(inplace=True)\n", - " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", - " )\n", - " (features_1_conv_0_2): ReLU6(inplace=True)\n", - " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_0_2): ReLU6(inplace=True)\n", - " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", - " )\n", - " (features_2_conv_1_2): ReLU6(inplace=True)\n", - " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_0_2): ReLU6(inplace=True)\n", - " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", - " )\n", - " (features_3_conv_1_2): ReLU6(inplace=True)\n", - " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_0_2): ReLU6(inplace=True)\n", - " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", - " )\n", - " (features_4_conv_1_2): ReLU6(inplace=True)\n", - " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_0_2): ReLU6(inplace=True)\n", - " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_5_conv_1_2): ReLU6(inplace=True)\n", - " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_0_2): ReLU6(inplace=True)\n", - " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_6_conv_1_2): ReLU6(inplace=True)\n", - " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_0_2): ReLU6(inplace=True)\n", - " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", - " )\n", - " (features_7_conv_1_2): ReLU6(inplace=True)\n", - " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_0_2): ReLU6(inplace=True)\n", - " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_8_conv_1_2): ReLU6(inplace=True)\n", - " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_0_2): ReLU6(inplace=True)\n", - " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_9_conv_1_2): ReLU6(inplace=True)\n", - " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_0_2): ReLU6(inplace=True)\n", - " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_10_conv_1_2): ReLU6(inplace=True)\n", - " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_0_2): ReLU6(inplace=True)\n", - " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_11_conv_1_2): ReLU6(inplace=True)\n", - " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_0_2): ReLU6(inplace=True)\n", - " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_12_conv_1_2): ReLU6(inplace=True)\n", - " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_0_2): ReLU6(inplace=True)\n", - " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_13_conv_1_2): ReLU6(inplace=True)\n", - " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_0_2): ReLU6(inplace=True)\n", - " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", - " )\n", - " (features_14_conv_1_2): ReLU6(inplace=True)\n", - " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_0_2): ReLU6(inplace=True)\n", - " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_15_conv_1_2): ReLU6(inplace=True)\n", - " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_0_2): ReLU6(inplace=True)\n", - " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_16_conv_1_2): ReLU6(inplace=True)\n", - " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_0_2): ReLU6(inplace=True)\n", - " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_17_conv_1_2): ReLU6(inplace=True)\n", - " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_18_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_18_2): ReLU6(inplace=True)\n", - " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (classifier_0): Dropout(p=0.2, inplace=False)\n", - " (classifier_1): PytorchQuantizationWrapper(\n", - " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", - " )\n", - " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - ")\n" - ] - } - ], - "source": [ - "print(quantized_models_dict[QuantizationErrorMethod.MSE]['quantized_model'])" - ] - }, - { - "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "id": "NGnjrPD_uTd5", "metadata": { "id": "NGnjrPD_uTd5" @@ -1691,7 +1142,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 15, "id": "VPb8tBNGpJjo", "metadata": { "id": "VPb8tBNGpJjo" @@ -1699,7 +1150,7 @@ "outputs": [ { "data": { - "image/png": "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pD4nNWvWVIkSJXTgwAHVqFFD6dOnV4ECBfTTTz9JkjZs2KBKlSrJy8tLhQsX1urVq63iPXfunD766CMVLlxYXl5eypo1q5o3b273F90xY8bozp07+uabb6wSIEnKli2bpk+frsjISI0ZM8ZSntCcxPhex7NmzdIrr7wif39/eXh4qFixYpbXwMMS894QGhqq5s2bS5Jq1aplOd9x5/fRuTmPGzIWt09izmdS25WkK1euKDg4WNmzZ5enp6dKly6t2bNnW9V5eG6MPa9rezypnbj3glOnTqlBgwby8fFRmzZtJElms1kTJkxQ8eLF5enpqezZs6tTp05Wfw+StGvXLtWrV0/ZsmWTl5eXgoKC1LFjR7vikaS1a9eqevXq8vb2VqZMmdSkSRMdOXLkiY/VMAwNHz5cuXPnVvr06VWrVi0dOnTIpt79+/c1dOhQFSxYUJ6ensqaNauqVaumVatWPbGNJ3l0TlDc38jhw4fVunVrZc6cWdWqVZP0dJ9NSVG0aFFly5ZNp06dsiqPiorS4MGDVaBAAXl4eCggIEB9+/a1+Rx/VGI/v57G9evX1adPH5UsWVIZMmRQxowZVb9+fe3fv99SJzIyUt7e3urevbvN/v/8849cXV0VEhJiKbt586Z69OihgIAAeXh4qECBAho9erTVKJuH/0YnTJhgea0+b8OEUxN6gtK4uXPn6o033pC7u7tatWqlqVOnaufOnVbd5ZGRkapevbqOHDmijh076sUXX9S1a9f066+/6p9//lHRokX1+eefa9CgQfrggw9UvXp1SfEPbUiXLp2aNWumxYsXa/r06VZXJ5YuXaqoqCjL+OSIiAh99913atWqld5//33dunVLM2bMUL169bRjxw6VKVNGfn5+mjp1qj788EM1a9ZMb7zxhiSpVKlSCT7m9957T7Nnz9Zbb72l3r17a/v27QoJCdGRI0e0ZMkSq7onT57UW2+9peDgYLVr104zZ85U+/btVa5cORUvXtzu854nTx7VqFFD69atU0REhDJmzBhvvTfffFOHDh3Sxx9/rMDAQF25ckWrVq3S+fPnFRgYqAkTJujjjz9WhgwZNGDAAElS9uzZrY7x0Ucfyc/PT4MGDdLt27cfG9eJEyfUokULde7cWe3atdOsWbPUvHlz/fnnn6pTp06SHmNiYntYaGioOnTooAoVKigkJESXL1/W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2F7r1q117do1VatWTYZhKCYmRp07d37scLhLly4pKCjIqizuAsalS5eUOXNmXbp0yVL2cJ1Lly495tEDeJZIggAkyMfTTTky2iY4j8rq7R5vWWL29fF8urehJ/X0PHy19kkWLFigr776SqdOnVJkZKRiYmKUMWNGqzqBgYFWX/Ry5syZ4NClJ1m+fLn69eun3377TYUKFbKUr169WiEhITp69KgiIiIUExOje/fu6c6dO4me83PkyBEFBARY9UQVK1ZMmTJl0pEjRywJR2IeT61atTR16lSrsocTOUkqX768TQx58+a1SmQSG9Oj+z3Mz89PDRs2VGhoqAzDUMOGDZUtWzarOidPntSdO3dUp04dq/Lo6GibYXPxKVWqlNX9h89JYh7DkSNHbBZwqFy5statW/fEtqUHPQbVqlXTwIEDNW/evHjrJKaHMym9oEkVHh6uDBkyyGw26969e6pWrZolEYnTqlUrubq66u7du/Lz89OMGTNszm18Ro8erVdeeSXeHs74DBgwQF27dtXixYs1cuRIS3nFihV19OjRx+67fv16jRw5UlOmTFGlSpV08uRJde/eXcOGDXuqOVMAnn8kQQAS9F71fHrPzjX/v2tXIZmjsVagQAGZTCYdOXJEzZo1s9l+5MgR+fn5Wa7ym0wmmy+FD8/32bZtm9q0aaOhQ4eqXr168vX11fz58zV27FirfR69qmwymWQ2m5Mc/+HDh9WyZUuNGjVKdevWtZSfPXtWjRo10ocffqgRI0YoS5Ys2rx5s4KDgxUdHZ3sCx8k5vF4e3urQIECjz2Ot7d3osoS40n7dezYUV27dpUkff311zbb4+ZP/f7773rhhRestnl4eDyx/eR6jp/GqFGjVLlyZX3yySdW5XHJ8pEjR+JN6I4cOWKpE/fv0aNHLT1iycXHx0d79uyRi4uLcubMaTMkVXrQy1m7dm35+vommNTG5+WXX1a9evXUv39/mxX/ChYsqGPHjlmV+fn5yc/PL9E9vg8bOHCg3nnnHb333nuSpJIlS+r27dv64IMPNGDAAJshh9KD4baPzgGLux/XO5tQncf13gJ4tpgTBCBVypo1q+rUqaMpU6ZYTcaWHgxJmTt3rtUXKD8/P4WFhVnunzhxwmo54a1btypv3rwaMGCAypcvr4IFC1qGXCWFu7u7YmNjH1vn2rVraty4sd5880317NnTatvu3btlNps1duxYvfTSSypUqJAuXryY5DaKFi2qCxcu6MKFC5ayw4cP6+bNmypWrFgSH1XySK6YXnvtNUVHR+v+/fuqV6+ezfZixYrJw8ND58+fV4ECBaxucT04cT2ETzqP9jyGokWLavv27Vb7/fXXX0lqp2LFinrjjTfUr18/q/IyZcqoSJEiGj9+vE1itn//fq1evVqtWrWSJNWtW1fZsmWLd/lmSU+1PLiLi4sKFCigfPnyxZsASQ8SgQIFCiQpAYozatQoy0IPD2vVqpWOHTumX375xa64H3Xnzh2bRMfV1VVSwj1plStX1saNG60uoqxatUqFCxe2LBhSuXJlrVmzxmq/VatWJXsyCsB+JEEAUq3JkycrKipK9erV08aNG3XhwgX9+eefqlOnjuV3U+K88sormjx5svbu3atdu3apc+fOVlf8CxYsqPPnz2v+/Pk6deqUvvrqKy1ZsiTJMQUGBmr79u06e/asrl27Fm8Pwptvvqn06dNryJAhunTpkuUWGxurAgUK6P79+5o0aZJOnz6t77//XtOmTbNpIzIyUmvWrNG1a9fi/W2Y2rVrq2TJkmrTpo327NmjHTt26N1331WNGjXiHbr2OFFRUVZxXrp0SdeuXUvaiUnGmFxdXXXkyBEdPnzY8oX1YT4+PurTp4969uyp2bNn69SpU9qzZ48mTZqk2bNnS3ow5M5kMmnZsmW6evWqpfcoOR5D9+7dNXPmTM2aNUvHjx/X4MGDdejQIavjLFmy5IlzdUaMGKG1a9da9XyYTCbNmDFDhw8f1ptvvqkdO3bo/PnzWrRokRo3bqzKlStb5pR5e3vru+++0++//67XX39dq1ev1tmzZ7Vr1y717dvXZsjesWPHtG/fPqtbfMudPwtx5/irr76yKm/ZsqXeeusttWzZUp9//rnlb23Dhg1asGCB1ethx44dKlKkiP79998E22ncuLGmTp2q+fPn68yZM1q1apUGDhyoxo0bW441efJkvfrqq5Z9WrduLXd3dwUHB+vQoUNasGCBJk6cqF69elnqdO/eXX/++afGjh2ro0ePasiQIdq1a5elBxOA45EEAUi1ChYsqJ07dypfvnx6++23lTdvXtWvX1+FChXSli1brCbzjx07VgEBAapevbpat26tPn36WA0te/3119WzZ0917dpVZcqU0datW+2aE9Cn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lVde0f79+x97XABA0pEEAYCTWbhwoYoUKaLChQurbdu2mjlzpgzDsGz//fff1axZMzVo0EB79+7VmjVrVLFiRUnS4sWLlTt3bn3++ecKCwtTWFiYzfELFSqk8uXLa+7cuVblc+fOVevWrSVJZrNZuXPn1qJFi3T48GENGjRIn376qRYuXChJ6tOnj95++2299tprlnaqVKli09a///6rBg0aqEKFCtq/f7+mTp2qGTNmaPjw4Vb1Zs+eLW9vb23fvl1ffPGFPv/8c61atSrBcxQcHKyFCxfq9u3blrL169frzJkz6tix4xPjt1fz5s115coVLV++XLt379aLL76oV199VdevX3+q4wIAHmEAAJxKlSpVjAkTJhiGYRj37983smXLZqxbt86yvXLlykabNm0S3D9v3rzG+PHjrcpmzZpl+Pr6Wu6PHz/eyJ8/v+X+sWPHDEnGkSNHEjxuly5djDfffNNyv127dkaTJk2s6pw5c8aQZOzdu9cwDMP49NNPjcKFCxtms9lS5+uvvzYyZMhgxMbGGoZhGDVq1DCqVatmdZwKFSoY//vf/xKM5caNG4anp6cxa9YsS9k777xjc5ykxB/feStdurQxePBgwzAMY9OmTUbGjBmNe/fuWdXJnz+/MX369ATbBQAkHT1BAOBEjh07ph07dqhVq1aSJDc3N7Vo0UIzZsyw1Nm3b59effXVp2qnZcuWOnv2rP766y9JD3qBXnzxRRUpUsRS5+uvv1a5cuXk5+enDBky6JtvvtH58+eT1M6RI0dUuXJly7A8SapataoiIyP1zz//WMpKlSpltV/OnDl15cqVBI+bKVMmvfHGG5YhcREREfr5558VHBycrPE/bP/+/YqMjFTWrFmVIUMGy+3MmTM6deqU3ccFANhKuZmlAIDnzowZMxQTE6NcuXJZygzDkIeHhyZPnixfX195eXk9dTs5cuTQK6+8onnz5umll17SvHnz9OGHH1q2z58/X3369NHYsWNVuXJl+fj4aMyYMdq+fftTtx2fdOnSWd03mUwym82P3Sc4OFivvvqqTp48qXXr1snV1VXNmze3O34XFxerYYeSdP/+fcv/IyMjlTNnTq1fv95m32ex/DgAOBOSIABwEjExMZozZ47Gjh2runXrWm1r2rSpfvzxR3Xu3FmlSpXSmjVr1KFDh3iP4+7urtjY2Ce216ZNG/Xt21etWrXS6dOn1bJlS8u2LVu2qEqVKvroo48sZY/2diSmnaJFi+rnn3+WYRiW3qAtW7bIx8dHuXPnfmKMj1OrVi0FBQVp1qxZWrdunVq2bClvb+9Ex/8oPz8/qzlUEREROnPmjOX+iy++qEuXLsnNzU2BgYFPFTsA4PEYDgcATmLZsmW6ceOGgoODVaJECavbm2++aRkSN3jwYP34448aPHiwjhw5or///lujR4+2HCcwMFAbN27Uv//+q2vXriXY3htvvKFbt27pww8/VK1atax6nwoWLKhdu3ZpxYoVOn78uAYOHKidO3da7R8YGKgDBw7o2LFjunbtmlWvSZyPPvpIFy5c0Mcff6yjR4/ql19+0eDBg9WrVy+5uDzdR5zJZFLHjh01depUbdu2zWooXGLif9Qrr7yi77//Xps2bdLff/+tdu3aydXV1bK9du3aqly5spo2baqVK1fq7Nmz2rp1qwYMGKBdu3Y91WMBAFgjCQIAJzFjxgzVrl1bvr6+NtvefPNN7dq1SwcOHFDNmjW1aNEi/frrrypTpoxeeeUV7dixw1L3888/19mzZ5U/f375+fkl2J6Pj48aN26s/fv3q02bNlbbOnXqpDfeeEMtWrRQpUqV9N9//1n1qkjS+++/r8KFC6t8+fLy8/PTli1bbNp44YUX9Mcff2jHjh0qXbq0OnfurODgYH322WdJPT3xat++vcLDw1W8eHFVqlQpSfE/qn///qpRo4YaNWqkhg0bqmnTppYlw6UHSdcff/yhl19+WR06dFChQoXUsmVLnTt3TtmzZ0+WxwMAeMBkPDpAGQD+X3t2QAMAAIAgrH9re8jfggEAcMwJAgAAUkQQAACQIoIAAIAUEQQAAKSIIAAAIEUEAQAAKSIIAABIEUEAAECKCAIAAFJEEAAAkCKCAACAlAGd9q23/SDfIQAAAABJRU5ErkJggg==", + "image/png": "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hBOoR8Xo4+PzyBP4pBKCIkWKWD3PkiWL8uTJ81jvXb58+Ww+E35+fgoMDLQpk6xf3+3bt/Xhhx9a+rHnzJlT/v7+un79eor/hpOb3Ny4cUMmk8lq1rHk2rx5s+rVq2cZz+Hv76/3339fkmziTavvhqioKL388st65plnNHfuXKtjn5rHM8GZM2dUpEgRm+5QCd3nHvWdmNzPdUoldz9ubm6WsY4Jjh07psjISAUEBMjf39/qER0drUuXLkmSateurVdeeUUjRoxQzpw51bx5c82ePTvRcSWPiudh32ElSpTQlStXkuyOnLDug3+//v7+VomXJH300Ue6fv26ihYtqjJlyui9997Tvn37Et1uYsqUKaN69epZPR41IcKDvyGp9duUmObNm2vVqlX69ddfLeN3bt26ZfX5PHbsmA4cOGDz3ib8ViW8v45iNps1ceJEFSlSxOrvdN++fVZ/py4uLmrfvr2WLl2qW7duSbp3cdrT09Myrk+693p///13m9dbr149Sbav92mfFCY9yTBjgm7evKly5cqpa9eulnEjKdW7d2+tXLlSn376qcqUKaOrV6/q6tWrqRzpk7F27VpFRERowYIFWrBggc3yefPmqUGDBmm2/zZt2uiLL77Q8uXL1aJFC33//fcqXry4ypUrZ6nz7bffqnPnzmrRooXee+89BQQEWAYLPjhIMqWSe4U/qRYU476B9fbav3+/XF1dH/qF1adPHzVr1kxLly7VihUrNHToUIWFhWnt2rWqUKFCsvZz/9Xj1JDUsXvUpASp6XFmtqtZs6a++uornTx5Uhs3blStWrVkMplUs2ZNbdy4UXnz5pXZbH7sJMjez06JEiW0e/duxcTEJDmF+b59+5QpUyabk6a0kNTrSM7re/fddzV79mz16dNHISEh8vPzs9yXJKXT5vv5+Slv3ryPPOHbt2+f8uXLZ2lFTu7n9cSJE3rhhRdUvHhxTZgwQYGBgXJ3d9dvv/2miRMn2sSbVt8NnTt31vnz57V9+3absYKpeTztlZbfifbs5/6r7gnMZrMCAgKSnOgn4QKKyWTSDz/8oD///FO//PKLVqxYoa5du2r8+PH6888/La31KYknrT333HM6ceKEfvrpJ61cuVJff/21Jk6cqBkzZuiNN95Ik30m9huSGr9NicmXL5/l5L5x48bKmTOnevbsqbp161rO3cxm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ZXPsfX19bd67M2fOpCimsWPHavr06Zo6daoOHTqksWPH6pNPPtGUKVOSXOfUqVNq0qSJ6tatqz179qhPnz564403tGLFCkudhQsXql+/fho2bJh27dqlcuXKqWHDhrp06VKK4gPgWAveCtGvvWppwVshjg4lRRyeBJnNZs2ePVudOnWSm9tTM08DgHSgR48ecnd318qVK1W7dm3lz59fjRo10urVq/Xvv/9qyJAhlromk0lLly61Wj9r1qxWV6f/97//qWjRosqcObMKFiyooUOHWp1UJnRp+uabbxQUFCQ/Pz+1adNGN27ckHSvtWL9+vWaPHmy5ar76dOnbVod6tSpk+gV+oSr/BMmTFCZMmXk7e2twMBAvfPOO4qOjpZ07+p6ly5dFBkZaVlv+PDhkmy7w509e1bNmzdXlixZ5Ovrq9dee00XL15M9utJ4OHhody5c1s9smXLZnVsp0+frpdeekne3t4aNWqUZdtff/21goODLa0PyY3pwfUkqWLFigoMDNTixYstZYsXL1b+/PlVoUIFq5jNZrPCwsIsrRflypXTDz/8IOleq0vdunUlSdmyZbNpRTKbzRo4cKCyZ8+u3LlzW45vco+rdG+201y5csnHx0ehoaG6c+eOkqNTp06aPXu2VVnCb+T9fvnlF9WoUUPvvfeeihUrpqJFi6pFixb6/PPPreqZTCab9y5XrlzJiiXBli1b1Lx5czVp0kRBQUF69dVX1aBBg4e22syYMUPBwcEaP368SpQooZ49e+rVV1/VxIkTLXUmTJigN998U126dFHJkiU1Y8YMZc6cWbNmzUpRfABgD4cnQatXr9bZs2fVtWtXR4cC4AFfbzypZ0eveeTjjTk7bNZ9Y86OZK379caTdsV29epVrVixQu+88468vLysluXOnVvt27fXwoULH9ll534+Pj4KDw/XwYMHNXnyZH311VdWJ22SdOLECS1dulTLli3TsmXLtH79eo0ZM0aSNHnyZIWEhOjNN9+0XHVPbAKXxYsXW12Zf/nll1WsWDHLyamLi4s+++wzHThwQHPmzNHatWs1cOBASfe61k2aNMnqCv+AAQNs9mE2m9W8eXNdvXpV69ev16pVq3Ty5Em1bt062a8nJYYPH66WLVvq77//tnyfHz9+XD/++KMWL16sPXv2JDumB9e7X9euXa2ShFmzZqlLly428YSF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5REfH6/ChQvr7t27mjJlik6ePKlvvvlGM2bMsNlHdHS01qxZoytXrlglcwnq1aunMmXKqH379tq1a5e2b9+ujh07qnbt2ol2XXuYmJgYqzgvXLigK1eupOzApGJMrq6uOnTokA4ePChXV1eb5T4+PhowYID69u2rOXPm6MSJE9q1a5emTJmiOXPmSLrX5c5kMmnZsmW6fPmypfUoNV5D7969NWvWLM2ePVtHjx7VsGHDdODAAavtLFmyRMWLF09yP2+++aYuX76c5P3zhg8froEDB2rdunU6deqUdu/era5du+ru3btW3QANw7B57y5cuGD5XD4qDklq1qyZRo0apV9//VWnT5/WkiVLNGHCBKsW2MGDB6tjx46W5927d9fJkyc1cOBAHT58WNOmTdP3339vlfT369dPX331lebMmaNDhw7p7bff1s2bNxOd6AIAUhtJEIB0q0iRItqxY4cKFiyo1157TQUKFFCjRo1UtGhRbd682WpA+Pjx4xUYGKhatWqpXbt2GjBggFXXspdeekl9+/ZVz549Vb58eW3ZskVDhw5NcUwDBgyQq6urSpYsKX9/f509e9amzoYNG7R//34VKFBAefLksTzOnTuncuXKacKECRo7dqxKly6tefPmKSwszGr96tWrq3v37mrdurX8/f31ySef2OzDZDLpp59+UrZs2fTcc8+pXr16KliwoBYuXJji1/T7779bxZknTx7VrFkzxdtJzZgedcPsjz/+WEOHDlVYWJhKlCihF198Ub/++quCg4Ml3bsfz4gRIzRo0CDlypXL0r0uNV5D69atNXToUA0cOFCVKlXSmTNn9Pbbb1ttJzIyUkeOHElyP25ubsqZM2eSt46oXbu2Tp48qY4dO6p48eJq1KiRLly4oJUrV1pNdBAVFWXz3t0/xulRcUjSlClT9Oqrr+qdd95RiRIlNGDAAHXr1k0ff/yxpU5ERITVZz04OFi//vqrVq1apXLlymn8+PH6+uuvrVruWrdurU8//VQffvihypcvrz179uj3339P8RTeAGAPk5GOp2aLioqSn5+fIiMjH/pj+KRMXHU00fK+9YsmWg48Le7cuaNTp07Z3JclPRo2bJgmTJigVatW6dlnn3V0OACQLBnpexjO5WmaGCEluQETIwDIUEaMGKGgoCD9+eefqlq1qs1YBgAAAJIgABkOYwoAAMDDcIkUAAAAgFMhCQIAAADgVOgOBwAAAMAuQxqX0O278fLKZHvLgqcZSRAAAAAAu4TWKujoEOxCdzgAAAAAToUkCAAAAIBToTscAAAAALvM3HjSMiYoPXWNoyUIANLYunXrZDKZdP369TTdT1BQkCZNmpSm+8jo6tSpoz59+qT6docPH67y5cun+nYBwNFG/XZIn648qlG/HXJ0KClCEgQgXTt37py6du2qvHnzyt3dXQUKFFDv3r3133//OSSexE6iq1evroiICPn5+aXKPsLDw5U1a1ab8h07duitt95KlX0k6Ny5s0wmk83jxRdfTNX9JDeO7t272yzr0aOHTCaTOnfunOztPanENLlOnz4tk8mkgIAA3bhxw2pZ+fLlNXz4cKuyAwcO6LXXXpO/v788PDxUtGhRffjhh7p165bNtnfv3q1WrVopV65c8vT0VJEiRfTmm2/q6NGjVvves2dPorE9+HkLDw+3fA5cXFyUL18+denSRZcuXbLUuf+z4ufnpxo1amjt2rWW5Z07d1aLFi2snptMJo0ZM8Zq30uXLpXJZLIqMwxDX331lUJCQuTr66ssWbKoVKlS6t27t44fP57oa3iYSZMmqVixYvLy8lJgYKD69u2rO3fuPHSdffv2qVatWvL09FRgYKA++eQTmzqLFi1S8eLF5enpqTJlyui3335LcWwA0g5JEIB06+TJk6pcubKOHTum7777TsePH9eMGTO0Zs0ahYSE6OrVq44OUZLk7u6u3Llz25zMpTZ/f39lzpw51bf74osvKiIiwurx3XffJVn/7t27NmWxsbF27fv+9QIDA7VgwQLdvn3bUnbnzh3Nnz9f+fPnt2v7T5sbN27o008/fWidP//8U9WqVVNsbKx+/fVXHT16VKNGjVJ4eLjq169vdcyWLVumZ599VjExMZo3b54OHTqkb7/9Vn5+fho6dKjdcfr6+ioiIkL//POPvvrqKy1fvlyvv/66VZ3Zs2crIiJCmzdvVs6cOdW0aVOdPHkyyW16enpq7NixunbtWpJ1DMNQu3bt1KtXLzVu3FgrV67UwYMHNXPmTHl6emrkyJEpeh3z58/XoEGDNGzYMB06dEgzZ87UwoUL9f777ye5TlRUlBo0aKACBQror7/+0rhx4zR8+HB9+eWXljpbtmxR27ZtFRoaqt27d6tFixZq0aKF9u/fn6L4AKQdkiAA6VaPHj3k7u6ulStXqnbt2sqfP78aNWqk1atX699//9WQIUMsdU0mk5YuXWq1ftasWRUeHm55/r///U9FixZV5syZVbBgQQ0dOtTqhD6hS9M333yjoKAg+fn5qU2bNpYr9507d9b69es1efJky1Xw06dP27Q61KlTJ9HWldOnT0uSJkyYoDJlysjb21uBgYF65513FB0dLeleC0aXLl0UGRlpWS+hleDB7nBnz55V8+bNlSVLFvn6+uq1117TxYsXk/16Enh4eCh37txWj2zZslkd2+nTp+ull16St7e3Ro0aZdn2119/reDgYHl6eqYopgfXk6SKFSsqMDBQixcvtpQtXrxY+fPnV4UKFaxiNpvNCgsLU3BwsLy8vFSuXDn98MMPku61fNStW1eSlC1bNptWJLPZrIEDByp79uzKnTu3TSvMo16DJI0ZM0a5cuWSj4+PQkNDH9mykODdd9/VhAkTrFpV7mcYhkJDQ1WiRAktXrxYVatWVYECBdSqVSv98ssv2rp1qyZOnChJunXrlrp06aLGjRvr559/Vr169RQcHKxq1arp008/1RdffJGsmBJjMpmUO3du5c2bV40aNVKvXr20evVqqwQ1a9asyp07t0qXLq3p06fr9u3bWrVqVZLbrFevnnLnzq2wsLAk6yxcuFALFizQwoULNXToUD377LPKnz+/nn32WY0dO1azZ89O0evYsmWLatSooXbt2ikoKEgNGjRQ27ZttX379iTXmTdvnmJjYzVr1iyVKlVKbdq0Ua9evTRhwgRLncmTJ+vFF1/Ue++9pxIlSujjjz9WxYoVNXXq1BTFByDtkAQBSNLXG0/q2dFrHvl4Y84Om3XfmLMjWet+vTHpK8MPc/XqVa1YsULvvPOOvLy8rJblzp1b7du318KFC2UYRrK36ePjo/DwcB08eFCTJ0/WV199ZTmhTHDixAktXbpUy5Yt07Jly7R+/XpLF57JkycrJCREb775pqXFJDAw0GY/ixcvtmpVefnll1WsWDHlypVLkuTi4qLPPvtMBw4c0Jw5c7R27VoNHDhQ0r2udZMmTbJciY+IiNCAAQNs9mE2m9W8eXNdvXpV69ev16pVq3Ty5Em1bt062a8nJYYPH66WLVvq77//VteuXSVJx48f148//qjFixdrz549yY7pwfXu17VrV6sT3VmzZqlLly428YSF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", "text/plain": [ "
" ] @@ -1726,7 +1177,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 16, "id": "Df7eKzh4oj5X", "metadata": { "id": "Df7eKzh4oj5X" @@ -1734,7 +1185,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAIjCAYAAAA0vUuxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACM20lEQVR4nOzdd3gU1fv38c8mpJOElgYEQi9SBcEoCEoApQjYqFItKEi3oNIUCIo0C0WUpmgQvoAUBUMQUKQjgvQqCAlFIKGlkJ3nD57sj2UTNgkJm4X367r2gp05M3Pv7uzJ3HvKmAzDMAQAAAAAyJCLowMAAAAAgLyOxAkAAAAA7CBxAgAAAAA7SJwAAAAAwA4SJwAAAACwg8QJAAAAAOwgcQIAAAAAO0icAAAAAMAOEicAAAAAsIPECXdV165dFRYW5pBjDx8+XCaTySHHzqpjx47JZDJp1qxZuX6sWbNmyWQy6dixY5ZlYWFhatGiRa4fW5LWrFkjk8mkNWvW3JXj3amsfDZpZT/55JPcDywHOfIzcbbzISc0bNhQDRs2vG+Om1l57fuTG/GkV/9mJCwsTF27ds2xY98Nef0cuxuc8XNDxkicYGXy5MkymUyqW7dutvdx6tQpDR8+XDt27Mi5wDLp6tWrGj58eJ676DKZTJZHvnz5VKhQIdWqVUt9+/bVnj17cuw4kydPvivJVnbk5dju1E8//aThw4fn2v6PHz+unj17KiwsTB4eHgoMDFTr1q21fv36O9rvvfCZ7N69W506dVKxYsXk4eGhokWLqlOnTjn6vcoJe/bs0fDhwzN1gXwvHDcjYWFhVvVhRg9nPy/zsrQfJ9Iebm5uKl26tDp37qwjR444OrwsyWoddut55uPjo8qVK2vkyJG6evWqVdmuXbvKZDKpWrVqMgwj3X317t37Tl8CnEw+RweAvGXu3LkKCwvT5s2bdejQIZUtWzbL+zh16pRGjBihsLAw1ahRw2rd9OnTZTabcyhaW1evXtWIESMkyeZXrvfff1/vvPNOrh3bnsaNG6tz584yDEPx8fH666+/NHv2bE2ePFkfffSRBgwYYClbsmRJXbt2TW5ublk6xuTJk1WkSJEs/br14osvql27dvLw8MjSsbIqo9gee+wxXbt2Te7u7rl6/JyS3mfz008/6YsvvsiV5Gn9+vVq1qyZJOmll15S5cqVFRcXp1mzZql+/fqaNGmS3njjjWzt29k/k4ULF6p9+/YqVKiQevTooVKlSunYsWP6+uuvtWDBAs2bN0+tWrVydJiSbiQwI0aMUMOGDW1a3X/55Zd77rgZmThxoi5fvmx5/tNPP+n777/XhAkTVKRIEcvyRx555K7Hdr/p06ePHnroIaWkpGj79u368ssvtXz5cu3atUtFixbNkWPk9jmWnb95aX+LJeny5cv67bffNGTIEP3111+aP3++Tfldu3Zp4cKFevbZZ3MqbDgxEidYHD16VH/88YcWLlyoV199VXPnztWwYcNy9BhZTQRyUr58+ZQvn+NO+fLly6tTp05Wy8aMGaOWLVtq4MCBqlixouUC2WQyydPTM1fjuXLlinx8fOTq6ipXV9dcPdbtuLi45PprzUl347NJc+HCBT333HPy8vLS+vXrVaZMGcu6AQMGqGnTpurXr59q1aqVoxeazvCZHD58WC+++KJKly6tdevWKSAgwLKub9++ql+/vjp16qSdO3eqVKlSDozUPkclqI44buvWra2ex8XF6fvvv1fr1q1tErs7bSVLq+OQvvr16+u5556TJHXr1k3ly5dXnz59NHv2bA0ePDjdbbL6nubFH19u/Vvcs2dPJScna+HChUpMTLSq+7y8vBQaGqoPPvhAzzzzjNN098+s69evy2w258nPKa+iqx4s5s6dq4IFC6p58+Z67rnnNHfu3HTLXbx4Uf3797d0GypevLg6d+6sc+fOac2aNXrooYck3aiIb+12cfMYp5SUFBUqVEjdunWzOUZCQoI8PT01aNAgSVJycrKGDh2qWrVqyd/fXz4+Pqpfv75+/fVXyzbHjh2zXDyNGDHCcuy0VoD0xjhdv35dH374ocqUKSMPDw+FhYXp3XffVVJSklW5tDE/v//+u+rUqSNPT0+VLl1ac+bMydqbfIvChQsrKipK+fLl06hRo6xey63dVeLi4tStWzcVL15cHh4eCgkJUatWrSwXF2FhYdq9e7fWrl1ree1prW5p/ejXrl2r119/XYGBgSpevLjVuvQuUn755RfVqFFDnp6eqly5shYuXGi1PqNxY7fu83axZTSmZf78+apVq5a8vLxUpEgRderUSSdPnrQq07VrV+XPn18nT55U69atlT9/fgUEBGjQoEFKTU297Xs/YMAAFS5c2KoLxhtvvCGTyaRPP/3Usuz06dMymUyaMmWKJNvPpmvXrvriiy8kWXcDudWXX35pOc8eeughbdmy5bbxSdK0adMUFxensWPHWiVN0o0/6LNnz5bJZNIHH3xgWZ723q9bt06vvvqqChcuLD8/P3Xu3FkXLlywlMvqZ9KwYUNVqVJFO3fuVIMGDeTt7a2yZctqwYIFkqS1a9eqbt268vLyUoUKFbRq1SqreP/55x+9/vrrqlChgry8vFS4cGE9//zz2b44Hjt2rK5evaovv/zSKmmSpCJFimjatGm6fPmyxo4da1me0RjL9M7jmTNn6oknnlBgYKA8PDxUuXJlyzlws8zUDbNmzdLzzz8vSXr88cct73fa+3vrOJDbdWdL2yYz72dWjytJZ86cUY8ePRQUFCRPT09Vr15ds2fPtipz81if7JzX2WHvOGl1weHDh9WsWTP5+vqqY8eOkiSz2ayJEyfqgQcekKenp4KCgvTqq69afR8kaevWrWratKmKFCkiLy8vlSpVSt27d89WPJK0evVq1a9fXz4+PipQoIBatWqlvXv32n2thmFo5MiRKl68uLy9vfX4449r9+7dNuVSUlI0YsQIlStXTp6enipcuLDq1aun6Ohou8dIzxNPPCHpxo+o0v99L/bs2aMOHTqoYMGCqlevnqTM/+1M7xxLSkrSsGHDVLZsWXl4eCg0NFRvvfWWzbaS9O2336pOnTry9vZWwYIF9dhjj1lasW5Xh2VVcHCwpSv9zVxcXPT+++9r586dWrRoUbb2favz589r0KBBqlq1qvLnzy8/Pz899dRT+uuvvyxlLl++LB8fH/Xt29dm+3///Veurq6KjIy0LLt48aL69eun0NBQeXh4qGzZsvroo4+sevjc/L2dOHGi5bPLa92a8zpanGAxd+5cPfPMM3J3d1f79u01ZcoUbdmyxZIISTe+zPXr19fevXvVvXt3Pfjggzp37pyWLFmif//9V5UqVdIHH3ygoUOH6pVXXlH9+vUlpd/tws3NTW3atNHChQs1bdo0q188Fi9erKSkJLVr107SjUTqq6++Uvv27fXyyy/r0qVL+vrrr9W0aVNt3rxZNWrUUEBAgKZMmaLXXntNbdq00TPPPCNJqlatWoav+aWXXtLs2bP13HPPaeDAgdq0aZMiIyO1d+9em0ry0KFDeu6559SjRw916dJFM2bMUNeuXVWrVi098MAD2X7fS5QooQYNGujXX39VQkKC/Pz80i337LPPavfu3XrjjTcUFhamM2fOKDo6WsePH1dYWJgmTpyoN954Q/nz59d7770nSQoKCrLax+uvv66AgAANHTpUV65cuW1cBw8eVNu2bdWzZ0916dJFM2fO1PPPP68VK1aocePGWXqNmYntZrNmzVK3bt300EMPKTIyUqdPn9akSZO0fv16/fnnnypQoIClbGpqqpo2baq6devqk08+0apVqzRu3DiVKVNGr732WobHqF+/viZMmKDdu3erSpUqkqTffvtNLi4u+u2339SnTx/LMulG97X0vPrqqzp16pSio6P1zTffpFvmu+++06VLl/Tqq6/KZDLp448/1jPPPKMjR47cthV26dKl8vT01AsvvJDu+lKlSqlevXpavXq1rl27Ji8vL8u63r17q0CBAho+fLj279+vKVOm6J9//rEkRVn9TKQbLWAtWrRQu3bt9Pzzz2vKlClq166d5s6dq379+qlnz57q0KGDxo4dq+eee04nTpyQr6+vJGnLli36448/1K5dOxUvXlzHjh3TlClT1LBhQ+3Zs0fe3t63PXZ6701YWJiljrnVY489prCwMC1dulSTJ0/O0r4lacqUKXrggQf09NNPK1++fFq6dKlef/11mc1m9erVy6qsvbrhscceU58+ffTpp5/q3XffVaVKlSTJ8u+tbu3OJkkTJkzQjh07VLhwYUmZez+zetxr166pYcOGOnTokHr37q1SpUpp/vz56tq1qy5evGhzEZfd8zqrMnuc69evq2nTpqpXr54++eQTyzn16quvWuqUPn366OjRo/r888/1559/av369XJzc9OZM2fUpEkTBQQE6J133lGBAgV07Ngxmx+LMhvPqlWr9NRTT6l06dIaPny4rl27ps8++0yPPvqotm/ffttJkoYOHaqRI0eqWbNmatasmbZv364mTZooOTnZqtzw4cMVGRmpl156SXXq1FFCQoK2bt2q7du3Z7mOlm604kqynGNpnn/+eZUrV06jR4+2/NCUlb+dNzObzXr66af1+++/65VXXlGlSpW0a9cuTZgwQQcOHNDixYstZUeMGKHhw4frkUce0QcffCB3d3dt2rRJq1evVpMmTbJVh0lSYmKizp07J+lGC9r69es1e/ZsdejQId1eKR06dNCHH36oDz74QG3atLnjVqcjR45o8eLFev75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7bOsdzZlypTRqlWr9Oijj2bqgvHhhx/Www8/rFGjRum7775Tx44dFRUVlalEJM3Nddit9u3bpyJFimQ4pXfatgcPHlTp0qUty8+ePZtuS17azLTdunXT5cuX9dhjj2n48OFZijer7uRvZ5kyZfTXX3+pUaNGtz03y5QpI7PZrD179tjcD/JmOVWHXb9+XZJsJmW5WVqrU9euXfXjjz9m+1gLFizQ448/rq+//tpq+cWLF21ax6tUqaKaNWtq7ty5Kl68uI4fP67PPvvMqkyZMmV0+fLlTCXBuHP3dVc9e3r37q0NGzYoKipKO3fu1PPPP68nn3zS0p946dKlKl26tJYtW6ZSpUopLCxML730klO1OF27dk0LFy5UixYt9Nxzz9k8evfurUuXLmnJkiWSbvSN/euvv9KdNSft17a0PwiZvSBycXHRc889p6VLl+qbb77R9evXbbrppf3Cd/Mveps2bdKGDRusyqXNopSZY6ddQN4640zaL2HNmzfPVPx34vz582rfvr1SU1NtksWbXb16VYmJiVbLypQpI19fX6uuJD4+Ptm+EL3VqVOnrD7nhIQEzZkzRzVq1FBwcLAlBklWfajT+snfKrOx1a5dW4GBgZo6darVa/v555+1d+/eHP1cSpUqpWLFimnChAlKSUnRo48+KulGQnX48GEtWLBADz/8sN37f2X1nM+sV199VYGBgXrzzTdtxnMkJiaqW7duMgxDQ4cOtdn2yy+/tBqrMWXKFF2/ft3qx6KcPF/scXV1tflF/rPPPst26+SgQYPk7e2tV199Vf/995/VuvPnz6tnz57y8/NT7969LcvLlCmj+Ph4q5aq2NhYm/osvfomPj5eM2fOzFasUtbOkVWrVun999/Xe++9l+Ev25l9P7Ny3GbNmikuLs5qzOn169f12WefKX/+/GrQoIHdfeQ1L7zwglJTU/Xhhx/arLt+/brlfblw4YLN+5l2wZ5ed73bCQkJUY0aNTR79myr9/3vv//WL7/8YvXjxa0iIiLk5uamzz77zCqeW/9OSbI57/Pnz6+yZctmOd6supO/nS+88IJOnjyp6dOn26y7du2apQtj69at5eLiog8++MCmFebm9yWn6rClS5dKklVPl/R06tRJZcuW1YgRI7J9rPS+u/Pnz7e53UaaF198Ub/88osmTpyowoUL2/zg/8ILL2jDhg1auXKlzbYXL160JIXIGfd1i9PtHD9+XDNnztTx48ctd9AeNGiQVqxYoZkzZ2r06NE6cuSI/vnnH82fP19z5sxRamqq+vfvr+eee06rV6928CvInCVLlujSpUt6+umn013/8MMPKyAgQHPnzlXbtm315ptvasGCBXr++efVvXt31apVS+fPn9eSJUs0depUVa9eXWXKlFGBAgU0depU+fr6ysfHR3Xr1r1ta1zbtm312WefadiwYapatarNdLktWrTQwoUL1aZNGzVv3lxHjx7V1KlTVblyZatfiLy8vFS5cmXNmzdP5cuXV6FChVSlSpV0x2VVr15dXbp00ZdffqmLFy+qQYMG2rx5s2bPnq3WrVtb/VqfEw4cOKBvv/1WhmFYxs7Mnz9fly9f1vjx4/Xkk0/edttGjRrphRdeUOXKlZUvXz4tWrRIp0+fthqsX6tWLU2ZMkUjR45U2bJlFRgYaNNqk1nly5dXjx49tGXLFgUFBWnGjBk6ffq01cVjkyZNVKJECfXo0UNvvvmmXF1dNWPGDAUEBOj48eNW+8tsbG5ubvroo4/UrVs3NWjQQO3bt7dMRx4WFqb+/ftn6/VkpH79+oqKilLVqlUtLWgPPvigfHx8dODAAXXo0MHuPtJapPr06aOmTZvK1dXV6nPJrsKFC2vBggVq3ry5HnzwQb300kuqXLmy4uLiNGvWLB06dEiTJk1Kd7r/5ORkyzmzf/9+TZ48WfXq1bP6rufk+WJPixYt9M0338jf31+VK1fWhg0btGrVKpupjzOrbNmymjNnjtq3b6+qVauqR48eKlWqlI4dO6avv/5aFy5cUFRUlFW9065dO7399ttq06aN+vTpo6tXr2rKlCkqX7681aDsJk2ayN3dXS1bttSrr76qy5cva/r06QoMDFRsbGy24q1Ro4ZcXV310UcfKT4+Xh4eHpb7RN2qffv2CggIULly5SytyWkaN26soKCgTL+fWTnuK6+8omnTpqlr167atm2bwsLCtGDBAq1fv14TJ0602yKeFzVo0ECvvvqqIiMjtWPHDjVp0kRubm46ePCg5s+fr0mTJum5557T7NmzNXnyZLVp00ZlypTRpUuXNH36dPn5+d020cnI2LFj9dRTTyk8PFw9evSwTEfu7+9vubdgetLuQxcZGakWLVqoWbNm+vPPP/Xzzz/btEZUrlxZDRs2VK1atVSoUCFt3bpVCxYssPqxIDfcyd/OF198UT/88IN69uypX3/9VY8++qhSU1O1b98+/fDDD1q5cqVq166tsmXL6r333tOHH36o+vXr65lnnpGHh4e2bNmiokWLWu5hlJ06LO1vsXTjR8mNGzdq9uzZKlu2bLpjvG7m6uqq9957L937T2ZWixYt9MEHH6hbt2565JFHtGvXLs2dO9eqhfFmHTp00FtvvaVFixbptddes5nq/80339SSJUvUokULy20Qrly5ol27dmnBggU6duxYtsZ5IgN3cwq/vEySsWjRIsvztKmPfXx8rB758uUzXnjhBcMwDOPll182JBn79++3bLdt2zZDkrFv3767/RKypWXLloanp6dx5cqVDMt07drVcHNzM86dO2cYhmH8999/Ru/evY1ixYoZ7u7uRvHixY0uXbpY1hvGjelZK1eubOTLl89qWuKMpgM2m81GaGioIckYOXJkuutHjx5tlCxZ0vDw8DBq1qxpLFu2LN39/fHHH0atWrUMd3d3q6nJ05tyOCUlxRgxYoRRqlQpw83NzQgNDTUGDx5sMy1pyZIl05321d40wml007SnLi4uRoECBYyaNWsaffv2NXbv3m1T/tYpr8+dO2f06tXLqFixouHj42P4+/sbdevWNX744Qer7eLi4ozmzZsbvr6+hiRLbGlT3qY31W5G05E3b97cWLlypVGtWjXDw8PDqFixojF//nyb7bdt22bUrVvXcHd3N0qUKGGMHz8+3X1mFFtG00/PmzfPqFmzpuHh4WEUKlTI6Nixo/Hvv/9albl5utibZTRNenq++OILQ5Lx2muvWS2PiIgwJBkxMTFWy9Objvz69evGG2+8YQQEBBgmk8ly7LSy6U0jf/O5ac/Ro0eNl19+2ShRooTh5uZmFClSxHj66adtpvQ1jP/7PNeuXWu88sorRsGCBY38+fMbHTt2tJoa2TCy9pk0aNDAeOCBB2yOl9F3Q7dM1XvhwgWjW7duRpEiRYz8+fMbTZs2Nfbt22czVW9mpiO/2a5du4wOHToYwcHBhouLiyHJ8PT0TPd7ZRiG8csvvxhVqlQx3N3djQoVKhjffvttuufLkiVLjGrVqhmenp5GWFiY8dFHHxkzZszI8Ltyq/TqhunTpxulS5c2XF1drV7jrWVvri9ufaRtk9n3MyvHNQzDOH36tGW/7u7uRtWq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e1r9Il6uXDkdP35cUVFROnz4sD799FMtWrQoyzGFhYVp06ZNOnbsmM6dO5duS8Wzzz4rb29vDR8+XHFxcZZHamqqypYtq5SUFH322Wc6cuSIvvnmG02dOtXmGJcvX1ZMTIzOnTtnlQCmiYiIUNWqVdWxY0dt375dmzdvVufOndWgQYN0u9XdTlJSklWccXFxOnfuXNbemByMydXVVXv37tWePXvk6upqs97X11eDBg1S//79NXv2bB0+fFjbt2/XZ599ptmzZ0u60R3QZDJp2bJlOnv2bKbvDZSZ19C3b1/NmDFDM2fO1IEDBzRs2DDL1N1pFi1apIoVK2Z4nJdffllnz57VSy+9lO764cOH66233tKaNWt09OhR/fnnn+revbtSUlKsuigahmHz2cXFxVnOS3txSNLgwYO1bt06HTt2TLt27dLgwYO1Zs0adezYUdL/JU1XrlzR119/rYSEBKtzOk3FihUt3ymTyaR+/fpp5MiRWrJkiXbt2qXOnTuraNGiat26taQbCeiTTz6pl19+WZs3b9b69evVu3dvtWvXTkWLFr1tzMgYiRMAAPehcuXKacuWLSpdurReeOEFlSxZUk899ZTKly+v9evXWw2KHzdunEJDQ1W/fn116NBBgwYNsur29vTTT6t///7q3bu3atSooT/++ENDhgzJckyDBg2Sq6urKleurICAAB0/ftymzLp16/T333+rZMmSCgkJsTxOnDih6tWra/z48froo49UpUoVzZ07V5GRkVbbP/LII+rZs6fatm2rgIAAffzxxzbHMJlM+vHHH1WwYEE99thjioiIUOnSpTVv3rwsv6YVK1ZYxRkSEqJ69epleT85GVPazVgz8uGHH2rIkCGKjIy0XIAvX75cpUqVknTjfkkjRozQO++8o6CgIEvXv5x4DW3bttWQIUP01ltvqVatWvrnn3/02muvWe0nPj5e+/fvz/A4+fLlU5EiRTKcla5BgwY6cuSIOnfurIoVK+qpp55SXFycfvnlF6vJHhISEmw+u5vHbNmLQ5LOnDmjzp07q0KFCmrUqJG2bNmilStXWhK07du3a9OmTdq1a5fKli1rc06n2b9/v9VNcd966y298cYbeuWVV/TQQw/p8uXLWrFihVVr1dy5c1WxYkU1atRIzZo1U7169fTll1/eNl7cnsnIylyj94CEhAT5+/tbpnoEkLsmRB+47fr+jcvfdj2Q1yUmJuro0aMqVaqU3S42ed2wYcM0fvx4RUdH6+GHH86RfTrrIHAAuedu1wu3q6ezkhswOQQAAJAkjRgxQmFhYdq4caPq1KkjFxc6pgBAGhInAABgkd4NUQEAjHECAAAAALtInAAAAADADrrqAQCAXBNa0FuGDJlkcnQoAPIIZ60XSJwAAECuye/JpQYAa85aL9BVDwAAAADscM50D0CeZe++TQAAAM6IxAkAAOSay4nXLWMZnLV7DoCc5az1Al31AABArjlx4aqOnruiExeuZmv7NWvWyGQy6eLFizkb2C3CwsI0ceLEXD3Gva5hw4bq169fju93+PDhqlGjRo7vF45zp/WCo5A4AQBwnzpx4oS6d++uokWLyt3dXSVLllTfvn3133//OSSe9C68H3nkEcXGxsrf3z9HjjFr1iwVKFDAZvmWLVv0yiuv5Mgx0nTt2lUmk8nm8eSTT+bocTIbR8+ePW3W9erVSyaTSV27ds30/u5WMptZx44dk8lkUmBgoC5dumS1rkaNGho+fLjVst27d+uFF15QQECAPDw8VL58eQ0dOlRXr9pexP/55596/vnnFRQUJE9PT5UrV04vv/yyDhw4YHXsHTt2pBvbrefbrFmzLOeBi4uLihcvrm7duunMmTOWMjefK/7+/nr00Ue1evVqy/quXbuqdevWVs9NJpPGjBljdezFixfLZLKetc4wDE2fPl3h4eHy8/NT/vz59cADD6hv3746dOhQuq/hdi5evKhevXopJCTE8l7+9NNP6ZYdM2aMTCZTppLr+fPnq2LFivL09FTVqlVt9mkYhoYOHaqQkBB5eXkpIiJCBw8ezHL8WUXiBADAfejIkSOqXbu2Dh48qO+//16HDh3S1KlTFRMTo/DwcJ0/f97RIUqS3N3dFRwcbHMBmNMCAgLk7e2d4/t98sknFRsba/X4/vvvMyyfkpJisyw5OTlbx755u9DQUEVFRenatWuWZYmJifruu+9UokSJbO0/r7l06ZI++eST25bZuHGj6tatq+TkZC1fvlwHDhzQqFGjNGvWLDVu3NjqPVu2bJkefvhhJSUlae7cudq7d6++/fZb+fv7a8iQIdmO08/PT7Gxsfr33381ffp0/fzzz3rxxRetysycOVOxsbFav369ihQpohYtWujIkSMZ7tPT01MfffSRLly4kGEZwzDUoUMH9enTR82aNdMvv/yiPXv26Ouvv5anp6dGjhyZpdeRnJysxo0b69ixY1qwYIH279+v6dOnq1ixYjZlt2zZomnTpqlatWp29/vHH3+offv26tGjh/7880+1bt1arVu31t9//20p8/HHH+vTTz/V1KlTtWnTJvn4+Khp06ZKTEzM0mvIKhInAADuQ7169ZK7u7t++eUXNWjQQCVKlNBTTz2lVatW6eTJk3rvvfcsZU0mkxYvXmy1fYECBTRr1izL87ffflvly5eXt7e3SpcurSFDhlglAZ9/EqkaNWrom2++UVhYmPz9/dWuXTtLC0HXrl21du1aTZo0yfJr+7Fjx2xaNxo2bJhuK86xY8ckSePHj1fVqlXl4+Oj0NBQvf7667p8+bKkGy0l3bp1U3x8vGW7tNaIW7vqHT9+XK1atVL+/Pnl5+enF154QadPn7asT+s+ltHrSePh4aHg4GCrR8GCBa3e2ylTpujpp5+Wj4+PRo0aZdn3V199pVKlSsnT0zNLMd26nSQ9+OCDCg0N1cKFCy3LFi5cqBIlSqhmzZpWMZvNZkVGRqpUqVLy8vJS9erVtWDBAkk3Wlgef/xxSVLBggVtWqvMZrPeeustFSpUSMHBwTatPfZeg3SjZSIoKEi+vr7q0aNHpi+G33jjDY0fP96q9eZmhmGoR48eqlSpkhYuXKg6deqoZMmSev7557V06VJt2LBBEyZMkCRdvXpV3bp1U7NmzbRkyRJFRESoVKlSqlu3rj755BNNmzYtUzGlx2QyKTg4WEWLFtVTTz2lPn36aNWqVVZJbYECBRQcHKwqVapoypQpunbtmqKjozPcZ0REhIKDgxUZGZlhmXnz5ikqKkrz5s3TkCFD9PDDD6tEiRJ6+OGH9dFHH2nmzJlZeh0zZszQ+fPntXjxYj366KMKCwtTgwYNVL16datyly9fVseOHTV9+nSrcz8jkyZN0pNPPqk333xTlSpV0ocffqgHH3xQn3/+uaQbn+PEiRP1/vvvq1WrVqpWrZrmzJmjU6dO2dRTOY3ECQCAXPDVb0f08OgYu4+XZm+x2fal2Vsyte1Xv2X8C/TtnD9/XitXrtTrr78uLy8vq3XBwcHq2LGj5s2bJ8MwMr1PX19fzZo1S3v27NGkSZM0ffp0y0VomsOHD2vx4sVatmyZli1bprVr11q6F02aNEnh4eF6+eWXLS0zoaGhNsdZuHChVevNM888owoVKigoKEiS5OLiok8//VS7d+/W7NmztXr1ar311luSbnT7mzhxouUX/9jYWA0aNMjmGGazWa1atdL58+e1du1aRUdH68iRI2rbtm2mX09WDB8+XG3atNGuXbvUvXt3SdKhQ4f0v//9TwsXLtSOHTsyHdOt292se/fuVhfHM2bMULdu3WziiYyM1Jw5czR16lTt3r1b/fv3V6dOnbR27VqFhobqf//7nyRp//79io2N1aRJkyzbzp49Wz4+Ptq0aZM+/vhjffDBB5YL/sy8hh9++EHDhw/X6NGjtXXrVoWEhGjy5MmZeh/bt2+vsmXL6oMPPkh3/Y4dO7Rnzx4NGDBALi7Wl8DVq1dXRESEpTVw5cqVOnfunOXcuVV63T2zy8vLS2azWdevX89wvXT7lkdXV1eNHj1an332mf799990y3z//feqUKGCnn766XTX39yqm/aDRdoPEulZsmSJwsPD1atXLwUFBalKlSoaPXq0UlNTrcr16tVLzZs3V0RERIb7utmGDRtsyjZt2lQbNmyQJB09elRxcXFWZfz9/VW3bl1LmdziPNNYAADgRC4lXldcgv1fykMKeNos++9Kcqa2vZSY/oWWPQcPHpRhGKpUqVK66ytVqqQLFy7o7NmzCgwMzNQ+33//fcv/w8LCNGjQIEVFRanli/83rsZsNmvWrFny9fWVJL344ouKiYnRqFGj5O/vL3d3d3l7eys4ODjD4xQqVMjy/wkTJmj16tXatGmT5eLy5vETYWFhGjlypHr27KnJkyfL3d1d/v7+ll/8MxITE6Ndu3bp6NGjluRtzpw5euCBB7RlyxY99NBDdl9PmmXLlil//vxW+3/33Xf17rvvWp536NDBJoFJTk7WnDlzFBAQIEmKjo7OVEy3bnezTp06afDgwfrnn38kSevXr1dUVJTWrFljKZOUlKTRo0dr1apVCg8PlySVLl1av//+u6ZNm6YGDRpYPoPAwECbBKJatWoaNmyYJKlcuXL6/PPPFRMTo8aNG2fqfZ04caJ69OihHj16SJJGjhypVatWZarVKW2cT8uWLdW/f3+VKVPGan3auKTbnfe///67JFnGy1SsWNHuce/EwYMHNXXqVNWuXdtyHt3s6tWrev/99+Xq6qoGDRrcdl9t2rRRjRo1NGzYMH399dc2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", + "image/png": "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mjhxornszJkzslgsmjJliiTbz6Zdu3b6/PPPJVl3t7ndF198YV5nVatW1ebNm+8YnyRNmzZN0dHRGj16tFWyJt26kYiMjJTFYtGwYcPM5cnnfvXq1ercubPy588vb29vtWnTRhcvXjTLZfQzeeaZZ1SuXDnt3LlTtWrVUu7cuVW8eHH98MMPkqRVq1apWrVq8vDwUKlSpbR8+XKreP/++2+99dZbKlWqlDw8PJQ/f3698sormb4pHz16tK5du6YvvvjCKlmTpAIFCmjatGm6cuWKRo8ebS5PawxtatfxzJkz9dxzz8nf319ubm4qW7aseQ2klJ66YdasWXrllVckSc8++6x5vpPP7+3jfO7UbTB5m/Scz4weV5LOnj2rjh07KiAgQO7u7qpYsaIiIyOtyqQcy5WZ6zoz7nac5LrgyJEjqlevnry8vNSqVStJUlJSksaPH69HH31U7u7uCggIUOfOna1+HyRpy5YtioiIUIECBeTh4aHQ0FB16NAhU/FI0sqVK1WzZk15enrK19dXjRo10r59++76Xg3D0PDhw1WoUCHlzp1bzz77rPbs2WNT7ubNmxo6dKhKlCghd3d35c+fXzVq1FBUVNRdj5Ga5557TtKtL2+l//u92Lt3r1q2bKm8efOqRo0aktL/tzO1a+zGjRsaPHiwihcvLjc3NwUHB6t///4220rSN998oyeeeEK5c+dW3rx59fTTT5utdneqwzIqMDDQHLKQkpOTk95//33t3LlTCxcuzNS+b3fhwgX169dP5cuXV548eeTt7a0XXnhBf/31l1nmypUr8vT0VM+ePW22/+eff+Ts7KyRI0eayy5duqRevXopODhYbm5uKl68uD755BOrHk0pf2/Hjx9vfnaO1n3c0dHCBrubM2eOXnrpJbm6uqpFixaaMmWKNm/ebCZg0q1KpGbNmtq3b586dOigxx9/XOfPn9fixYv1zz//qEyZMho2bJgGDRqkTp06qWbNmpJS796SK1cuNWnSRAsWLNC0adOsvuFZtGiRbty4oebNm0u6lcB9+eWXatGihd544w1dvnxZX331lSIiIrRp0yZVqlRJfn5+mjJlit588001adJEL730kiSpQoUKab7n119/XZGRkXr55ZfVt29fbdy4USNHjtS+fftsKufDhw/r5ZdfVseOHdW2bVvNmDFD7dq1U+XKlfXoo49m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HipXrpxKlSqliRMn6plnnsnU/gAgsyxGekf84p7FxsbKx8dHMTEx8vb2tnc4wEPl9la17HBDC9xJXFycjh07ptDQULm7Z6y738Pgo48+0tSpU3Xy5El7hwLgIXanujq9uQEtbAAAINubPHmyqlatqvz582vt2rUaPXq0unXrphMXrikhMUkuzk4qnC+3vcMEYGfZsU4gYQMAANneoUOHNHz4cF24cEGFCxdW3759NXDgQB06d003E5OUy5lh+wCkqzcSsl2dQMIGAACyvXHjxmncuHH2DgMAslz2SS0BAAAA4CFDwgYAAAAADoqEDQAAAAAcFAkbAAAAADgoEjYAAAAAcFAkbAAAAADgoEjYAAAAUvHHH3/IYrHo0qVL9/U4ISEhGj9+/H09Rk73zDPPqFevXlm+3yFDhqhSpUpZvl8gI0jYAADAA3fy5El16NBBBQsWlKurq4oUKaKePXvqv//+y9Lj5PN0VYE8bsrn6XrHcqnd8D/55JM6ffq0fHx8siSWWbNmydfX12b55s2b1alTpyw5RrJ27drJYrHYvJ5//vksPU564+jSpYvNuq5du8pisahdu3bp3t+DSqLT6/jx47JYLHJ2dta///5rte706dNycXGRxWLR8ePHzeULFy5U9erV5ePjIy8vLz366KNW196sWbNS/ezc3d0zFNuUKVNUoUIFeXt7y9vbW2FhYfrll1/M9RcuXFD37t1VqlQpeXh4qHDhwurRo4diYmLuuF/DMDRo0CAFBQXJw8ND4eHhOnTokFWZCxcuqFWrVvL29pavr686duyoK1euZCj++yW9dYIjIWEDAAAP1NGjR1WlShUdOnRI3333nQ4fPqypU6dqxYoVCgsL04ULF7LsWAHe7iro66EA74zd7EqSq6urAgMDZbFYsiye1Pj5+Sl37txZvt/nn39ep0+ftnp99913aZa/efOmzbL4+PhMHTvldsHBwZo7d66uX79uLouLi9O3336rwoULZ2r/juaRRx7R7NmzrZZFRkbqkUcesVq2YsUKNWvWTE2bNtWmTZu0detWffTRRzbn3tvb2+az+/vvvzMUU6FChfTxxx9r69at2rJli5577jk1atRIe/bskSSdOnVKp06d0qeffqrdu3dr1qxZWrZsmTp27HjH/Y4aNUoTJ07U1KlTtXHjRnl6eioiIkJxcXFmmVatWmnPnj2KiorSkiVLtHr16iz/UiKz7qVOsBsDD0xMTIwhyYiJibF3KMBDZ+xvB6xeQHZ3/fp1Y+/evcb169ftHUqGPf/880ahQoWMa9euWS0/ffq0kTt3bqNLly7mMknGwoULrcr5+PgYM2fONH/u37+/UaJECcPDw8MIDQ013n//fSM+Pt5cP3jwYKNixYrG7NmzjSJFihje3t5Gs2bNjNjYWMMwDKNt27aGJKvXsWPHjN9//92QZFy8eNEwDMOoVauWTbnksoZhGGPGjDHKlStn5M6d2yhUqJDx5ptvGpcvXzYMwzD3lfI1ePBgwzAMo0iRIsa4cePMeP/++2/jxRdfNDw9PQ0vLy/jlVdeMaKjo9P9fpLfU6NGje74OUgyJk+ebDRs2NDInTu3MXjwYHPf06dPN0JCQgyLxZKhmG7fLjmOcuXKGd98841Zfs6cOUaFChWMRo0aGW3btjWXJyYmGiNGjDBCQkIMd3d3o0KFCsb8+fMNwzCMY8eO2ZzD5G1r1apldO/e3Xj77beNvHnzGgEBAeb5Te95NQzDGDlypOHv72/kyZPH6NChg/HOO+8YFStWTPMcJsf0/vvvGyVKlLBaV7JkSeODDz6wukZ69uxpPPPMM3f8XGbOnGn4+PjcsUxm5c2b1/jyyy/TXP/9998brq6uxs2bN1Ndn5SUZAQGBhqjR482l126dMlwc3MzvvvuO8MwDGPv3r2GJGPz5s1mmV9++cWwWCzGv//+m0XvJPu4U12d3tyAFjYAAHKYL9ccVfURK+76ej1ys822r0duTte2X645mqnYLly4oF9//VVvvfWWPDw8rNYFBgaqVatWmjdvngzDSPc+vby8NGvWLO3du1cTJkzQ9OnTNW7cOKsyR44c0aJFi7RkyRItWbJEq1at0scffyxJmjBhgsLCwvTGG2+YrRnBwcE2x1mwYIFVi8dLL72kUqVKKSAgQJLk5OSkiRMnas+ePYqMjNTKlSvVv39/Sbe6V44fP96q5aRfv342x0hKSlKjRo104cIFrVq1SlFRUTp69KiaNWuW7veTEUOGDFGTJk20a9cudejQQZJ0+PBh/e9//9OCBQu0Y8eOdMd0+3YpdejQQTNnzjR/njFjhtq3b28Tz8iRIzV79mxNnTpVe/bsUe/evdW6dWutWrVKwcHB+t///idJOnDggE6fPq0JEyaY20ZGRsrT01MbN27UqFGjNGzYMEVFRaX7vH7//fcaMmSIRowYoS1btigoKEiTJ09O13l88cUXdfHiRf3555+SpD///FMXL15Uw4YNrcoFBgZqz5492r17d7r2m5bkbpPplZiYqLlz5+rq1asKCwtLs1xMTIy8vb3l4uKS6vpjx44pOjpa4eHh5jIfHx9Vq1ZN69evlyStX79evr6+qlKlilkmPDxcTk5O2rhxY7pjxv9J/dMAAADZ1uW4BEXHxt21XJCvbZeg/67Gp2vby3EJmYrt0KFDMgxDZcqUSXV9mTJldPHiRZ07d07+/v7p2uf7779v/j8kJET9+vXT3LlzzWRJunXDPmvWLHl5eUmSXnvtNa1YsUIfffSRfHx85Orqqty5cyswMDDN4+TLl8/8/7hx47Ry5Upt3LjRTDxTjkMKCQnR8OHD1aVLF02ePFmurq7y8fGRxWK54zFWrFihXbt26dixY2bSOHv2bD366KPavHmzqlatetf3k2zJkiXKkyeP1f7fffddvfvuu+bPLVu2tEmc4uPjNXv2bPn5+UmSoqKi0hXT7dul1Lp1aw0cONDs1rd27VrNnTtXf/zxh1nmxo0bGjFihJYvX24mFUWLFtWff/6padOmqVatWuZn4O/vbzMesEKFCho8eLAkqUSJEpo0aZJWrFihOnXqpOu8jh8/Xh07djS7BA4fPlzLly+36uqXlly5cql169aaMWOGatSooRkzZqh169bKlSuXVbnu3btrzZo1Kl++vIoUKaLq1aurbt26atWqldzc3MxyMTExNp9dzZo1zTFoPj4+KlWq1F3j2rVrl8LCwhQXF6c8efJo4cKFKlu2bKplz58/rw8//PCOXRejo6MlyfySIllAQIC5Ljo62uZ318XFRfny5TPLIGNI2AAAyGG83F0UmI7xGflTGXSf39M1Xdt6ud/bLcTdWtBcXdM/IcC8efM0ceJEHTlyRFeuXFFCQoK8vb0lSftOx+pMbJyCChU2kxtJCgoK0tmzZzMV+y+//KIBAwbop59+UsmSJc3ly5cv18iRI7V//37FxsYqISFBcXFxunbtWrrHqO3bt0/BwcFWLXxly5aVr6+v9u3bZyZHISEhd30/zz77rKZMmWK1LGXSKcmqFSRZkSJFrJKu9MZ0+3Yp+fn5qX79+po1a5YMw1D9+vVVoEABqzKHDx/WtWvXVKdOHavl8fHxeuyxx1Ldb0oVKlSw+jnlOUnPe9i3b5/N5ChhYWH6/fff73ps6VYr4pNPPqkRI0Zo/vz5Wr9+vRISrL/Y8PT01NKlS3XkyBH9/vvv2rBhg/r27asJEyZo/fr15nXi5eWlbdu2WW2bskW6SZMmatKkyV1jKlWqlHbs2KGYmBj98MMPatu2rVatWmWTtMXGxqp+/foqW7ashgwZkq73m13tOx2rm4lJyuXspDJB3vYOJ11I2AAAyGFer1lUr9csmqltv2xbNYujsVa8eHFZLBbt27cv1RvOffv2yc/Pz2w9sVgsNsldygka1q9fr1atWmno0KGKiIiQj4+P5s6dqzFjxlht4+Ji3dJhsViUlJSU4fj37t2r5s2b6+OPP1bdunXN5cePH1eDBg305ptv6qOPPlK+fPn0559/qmPHjoqPj8/ySUVub7lJ7f14enqqePHid9yPp6dnupalx92269Chg7p16yZJ+vzzz23WJ88iuHTpUpvJOlK2PqUlPefkfipfvrxKly6tFi1aqEyZMipXrpxN19BkxYoVU7FixfT666/rvffeU8mSJTVv3jyztdPJyemun116uLq6mvupXLmyNm/erAkTJmjatGlmmcuXL+v555+Xl5eXFi5caHMeU0puHT5z5oyCgoLM5WfOnDEffxAYGGjz5UFCQoIuXLhwx9ZlpI0xbAAA4IHJnz+/6tSpo8mTJ1vNGijd6ko1Z84cq2ne/fz8dPr0afPnQ4cO6dq1a+bP69atU5EiRfTee++pSpUqKlGiRIZn05Nu3dgmJibescz58+fVsGFDNW3aVL1797Zat3XrViUlJWnMmDGqXr26SpYsqVOnTmX4GGXKlNHJkyd18uRJc9nevXt16dKlNLuy3W9ZFdPzzz+v+Ph43bx5UxERETbry5YtKzc3N504cULFixe3eiW3jCW3vN7tPGbmPZQpU8ZmjNWGDRsydJwOHTrojz/+MMcDpkdISIhy586tq1evZuhYmZGUlKQbN26YP8fGxqpu3bpydXXV4sWL7/rogNDQUAUGBmrFihVW+9i4caPZjTUsLEyXLl3S1q1bzTIrV65UUlKSqlWrlsXv6OFAwgYAAB6oSZMm6caNG4qIiNDq1at18uRJLVu2THXq1FHJkiU1aNAgs+xzzz2nSZMmafv27dqyZYu6dOli1QJQokQJnThxQnPnztWRI0c0ceJELVy4MMMxhYSEaOPGjTp+/LjOnz+fastM06ZNlTt3bg0ZMkTR0dHmKzExUcWLF9fNmzf12Wef6ejRo/r66681depUm2NcuXJFK1as0Pnz560Sz2Th4eEqX768WrVqpW3btmnTpk1q06aNatWqlWr3xTu5ceOGVZzR0dE6f/58xk5MFsbk7Oysffv2ae/evXJ2drZZ7+XlpX79+ql3796KjIzUkSNHtG3bNn322WeKjIyUdKvbpcVi0ZIlS3Tu3Ll0P9srPe+hZ8+emjFjhmbOnKmDBw9q8ODB5hT4yRYuXKjSpUuneZw33nhD586d0+uvv57q+iFDhqh///76448/dOzYMW3fvl0dOnTQzZs3rbqCGoZh89lFR0eb1+Xd4pCkgQMHavXq1Tp+/Lh27dqlgQMH6o8//lCrVq0k/V+ydvXqVX311VeKjY21uqaTlS5d2vydslgs6tWrl4YPH67Fixdr165datOmjQoWLKjGjRtLupX4Pv/883rjjTe0adMmrV27Vt26dVPz5s1VsGDBO8aM1JGwAQCAB6pEiRLavHmzihYtqldffVVFihTRCy+8oJIlS2rt2rVWky2MGTNGwcHBqlmzplq2bKl+/fpZdS988cUX1bt3b3Xr1k2VKlXSunXr9MEHH2Q4pn79+snZ2Vlly5aVn5+fTpw4YVNm9erV2r17t4oUKaKgoCDzdfLkSVWsWFFjx47VJ598onLlymnOnDkaOXKk1fZPPvmkunTpombNmsnPz0+jRo2yOYbFYtGPP/6ovHnz6umnn1Z4eLiKFi2qefPmZfg9LVu2zCrOoKAg1ahRI8P7ycqYkh/inJYPP/xQH3zwgUaOHGne+C9dulShoaGSbj3vbOjQoRowYIACAgLMLpZZ8R6aNWumDz74QP3791flypX1999/680337TaT0xMjA4cOJDmcVxcXFSgQIE0Z1msVauWjh49qjZt2qh06dJ64YUXFB0drd9++81qEpHY2Fibzy7lmLy7xSFJZ8+eVZs2bVSqVCnVrl1bmzdv1q+//momhtu2bdPGjRu1a9cuFS9e3OaaTnbgwAGrh2n3799f3bt3V6dOnVS1alVduXJFy5Yts2qdmzNnjkqXLq3atWurXr16qlGjhr744os7xou0WYyMzJuLexIbGysfHx9zylQAD864qINWP/euUzKNkkD2EBcXp2PHjik0NPSu3Ziyg8GDB2vs2LGKiopS9erVs2y/2XGCAQD3z4OuE+5UV6c3N2DSEQAAYHdDhw5VSEiINmzYoCeeeEJOTnQCAgCJhA0AADiI1B6kDAAPO76+AgAAAAAHRcIGAAAAAA6KLpEAHkq3T0IiMREJkBMF580tQ4Ysstg7FAAOIDvWCSRsAAAgx8rjzq0OgP+THesEukQCAAAAgIMiYQMAAAAAB5X92gQBAADS6UpcgjleJTt2hQKQtbJjnUALGwAAyLFOXrymY+ev6uTFaxne9o8//pDFYtGlS5eyPrAUQkJCNH78+Pt6jJzumWeeUa9evbJ8v0OGDFGlSpWyfL+wn3upE+yFhA0AADxwJ0+eVIcOHVSwYEG5urqqSJEi6tmzp/777z+7xJPaDf+TTz6p06dPy8fHJ0uOMWvWLPn6+tos37x5szp16pQlx0jWrl07WSwWm9fzzz+fpcdJbxxdunSxWde1a1dZLBa1a9cu3ft7UEl0eh0/flwWi0X+/v66fPmy1bpKlSppyJAhVsv27NmjV199VX5+fnJzc1PJkiU1aNAgXbtmmzxs375dr7zyigICAuTu7q4SJUrojTfe0MGDB62OvWPHjlRju/16mzVrlnkdODk5qVChQmrfvr3Onj1rlkl5rfj4+Oipp57SypUrzfXt2rVT48aNrX62WCz6+OOPrY69aNEiWSzWszAahqHp06crLCxM3t7eypMnjx599FH17NlThw8fTvU93MmlS5fUtWtXBQUFmefy559/TrXsxx9/LIvFkq6kfv78+SpdurTc3d1Vvnx5m30ahqFBgwYpKChIHh4eCg8P16FDhzIcf0aQsAEAgAfq6NGjqlKlig4dOqTvvvtOhw8f1tSpU7VixQqFhYXpwoUL9g5RkuTq6qrAwECbG8+s5ufnp9y5c2f5fp9//nmdPn3a6vXdd9+lWf7mzZs2y+Lj4zN17JTbBQcHa+7cubp+/bq5LC4uTt9++60KFy6cqf07msuXL+vTTz+9Y5kNGzaoWrVqio+P19KlS3Xw4EF99NFHmjVrlurUqWN1zpYsWaLq1avrxo0bmjNnjvbt26dvvvlGPj4++uCDDzIdp7e3t06fPq1//vlH06dP1y+//KLXXnvNqszMmTN1+vRprV27VgUKFFCDBg109OjRNPfp7u6uTz75RBcvXkyzjGEYatmypXr06KF69erpt99+0969e/XVV1/J3d1dw4cPz9D7iI+PV506dXT8+HH98MMPOnDggKZPn65HHnnEpuzmzZs1bdo0VahQ4a77XbdunVq0aKGOHTtq+/btaty4sRo3bqzdu3ebZUaNGqWJEydq6tSp2rhxozw9PRUREaG4uLgMvYeMIGEDAAAPVNeuXeXq6qrffvtNtWrVUuHChfXCCy9o+fLl+vfff/Xee++ZZS0WixYtWmS1va+vr2bNmmX+/M4776hkyZLKnTu3ihYtqg8++MAq+Zgy9mM1Ca+hr7/+WiEhIfLx8VHz5s3NFpF27dpp1apVmjBhgtm6cPz4cZvWnGeeeSbVVqvjx49LksaOHavy5cvL09NTwcHBeuutt3TlyhVJt1qG2rdvr5iYGHO75NaX27tEnjhxQo0aNVKePHnk7e2tV199VWfOnDHXJ3fTS+v9JHNzc1NgYKDVK2/evFbndsqUKXrxxRfl6empjz76yNz3l19+qdDQULm7u2coptu3k6THH39cwcHBWrBggblswYIFKly4sB577DGrmJOSkjRy5EiFhobKw8NDFStW1A8//CDpVovSs88+K0nKmzevTetcUlKS+vfvr3z58ikwMNCmdetu70G61RITEBAgLy8vdezYMd034d27d9fYsWOtWqtSMgxDHTt2VJkyZbRgwQI98cQTKlKkiF555RX99NNPWr9+vcaNGydJunbtmtq3b6969epp8eLFCg8PV2hoqKpVq6ZPP/1U06ZNS1dMqbFYLAoMDFTBggX1wgsvqEePHlq+fLlVMu3r66vAwECVK1dOU6ZM0fXr1xUVFZXmPsPDwxUYGKiRI0emWWbevHmaO3eu5s2bpw8++EDVq1dX4cKFVb16dX3yySeaOXNmht7HjBkzdOHCBS1atEhPPfWUQkJCVKtWLVWsWNGq3JUrV9SqVStNnz7d6tpPy4QJE/T888/r7bffVpkyZfThhx/q8ccf16RJkyTd+hzHjx+v999/X40aNVKFChU0e/ZsnTp1yqaeykokbAAA5DBfrjmq6iNW3PX1euRmm21fj9ycrm2/XJP2N+53cuHCBf36669666235OHhYbUuMDBQrVq10rx582QYRrr36eXlpVmzZmnv3r2aMGGCpk+fbt78Jjv59zEtWrRIS5Ys0ZIlS7Rq1SqzG9eECRMUFhamN954w2yJCg4O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mPz586tOnTqaPHmy1UQH0q3uRnPmzLG62fPz89Pp06fNnw8dOmQ1Bfq6detUpEgRvffee6pSpYpKlChhdrvLCFdXV5vxL7c7f/68GjZsqKZNm6p3795W67Zu3aqkpCSNGTNG1atXV8mSJXXq1KkMH6NMmTI6efKkTp48aS7bu3evLl26pLJly2bwXWWNrIrp+eefV3x8vG7evKmIiAib9WXLlpWbm5tOnDih4sWLW72SW8aSW17vdh4z8x7KlCmjjRs3Wm23YcOGDB3niSee0EsvvaQBAwZYLa9UqZJKly6tcePG2SSRf/31l5YvX64WLVpIkurWrasCBQpo1KhRqR7jXh5p4OTkpOLFi6to0aKpJmvSraSkePHiGUrWkn388cfmJCoptWjRQgcOHNCPP/6Yqbhv99RTT+nw4cNW5/LgwYMKCgqSq6urateurV27dmnHjh3mq0qVKmrVqpUWRP1pleilFBYWphUrVlgti4qKMhPn0NBQBQYGWpWJjY3Vxo0bszy5TomEDQAAPFCTJk3SjRs3FBERodWrV+vkyZNatmyZ6tSpYz6XKtlzzz2nSZMmafv27dqyZYu6dOli1ZJSokQJnThxQnPnztWRI0c0ceJELVy4MMMxhYSEaOPGjTp+/LjOnz+fastM06ZNlTt3bg0ZMkTR0dHmKzExUcWLF9fNmzf12Wef6ejRo/r66681depUm2NcuXJFK1as0Pnz51N99lZ4eLjKly+vVq1aadu2bdq0aZPatGmjWrVqpdp98U5u3LhhFWd0dLTOnz+fsROThTE5Oztr37592rt3b6o3zF5eXurXr5969+6tyMhIHTlyRNu2bdNnn32myMhISbe6XVosFi1ZskTnzp0zW+Wy4j307NlTM2bM0MyZM3Xw4EENHjxYe/bssdrPwoUL7zq27KOPPtLKlSutWpQsFou++uor7d27V02bNtWmTZt04sQJzZ8/Xw0bNlRYWJg5BtLT01Nffvmlli5dqhdffFHLly/X8ePHtWXLFvXv39+m2+aBAwesEpMdO3ak+oiGByH5HE+cONFqefPmzfXyyy+refPmGjZsmPm7tmrVKs2bN8/qeti0aZNKly6tf//9N83jvPnmm7pw4YJ69uypgwcPaunSpRoxYoS6du0q6da1VK5cOauXp6en8ufPrxKl/+9LhjZt2mjgwIHmzz179tSy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", "text/plain": [ "
" ] @@ -1773,7 +1224,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 17, "id": "631780a79e2cedf0", "metadata": { "collapsed": false @@ -1783,7 +1234,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [05:48<00:00, 1.12it/s]" + "100%|██████████| 391/391 [05:52<00:00, 1.11it/s]" ] }, { @@ -1791,9 +1242,9 @@ "output_type": "stream", "text": [ "\n", - "Test set: Average loss: -5.2923, Accuracy: 33872/50000 (68%)\n", + "Test set: Average loss: -6.4199, Accuracy: 35571/50000 (71%)\n", "\n", - "Float model's Top 1 accuracy on the Imagenet validation set: 67.74%\n" + "Float model's Top 1 accuracy on the Imagenet validation set: 71.14%\n" ] }, { @@ -1822,7 +1273,6 @@ " data, target = data.to(device), target.to(device)\n", " output = model(data)\n", " test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss\n", - " #print(\"test_loss\", test_loss)\n", " pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability\n", " correct += pred.eq(target.view_as(pred)).sum().item()\n", "\n", @@ -1841,7 +1291,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 18, "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" @@ -1851,7 +1301,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [04:49<00:00, 1.35it/s]\n" + "100%|██████████| 391/391 [04:52<00:00, 1.34it/s]\n" ] }, { @@ -1859,16 +1309,16 @@ "output_type": "stream", "text": [ "\n", - "Test set: Average loss: -6.0600, Accuracy: 35920/50000 (72%)\n", + "Test set: Average loss: -6.0591, Accuracy: 35955/50000 (72%)\n", "\n", - "Results for QuantizationErrorMethod.MSE: Loss = -6.05997125, Accuracy = 0.7184\n" + "Results for QuantizationErrorMethod.MSE: Loss = -6.05907375, Accuracy = 0.7191\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 391/391 [04:42<00:00, 1.38it/s]" + "100%|██████████| 391/391 [04:43<00:00, 1.38it/s]" ] }, { @@ -1876,9 +1326,9 @@ "output_type": "stream", "text": [ "\n", - "Test set: Average loss: -6.2230, Accuracy: 35963/50000 (72%)\n", + "Test set: Average loss: -6.2192, Accuracy: 35927/50000 (72%)\n", "\n", - "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2229875, Accuracy = 0.71926\n" + "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2191725, Accuracy = 0.71854\n" ] }, { @@ -1927,7 +1377,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 19, "id": "qml4LLmWZLP4", "metadata": { "id": "qml4LLmWZLP4" @@ -1978,8 +1428,6 @@ "\n", "quantized_model = data[\"quantized_model\"]\n", "\n", - "#print(quantized_model.state_dict().keys())\n", - "\n", "relu_layer_indices = []\n", "\n", "target_layers =[]\n", @@ -1987,11 +1435,7 @@ "flatted_list= flatten_model(module_list)\n", "\n", "for count, value in enumerate(flatted_list):\n", - " if count == 0: continue\n", - " #print(count, value, type(value))\n", " if isinstance(value, (nn.ReLU6)):\n", - " #if isinstance(value, (nn.Conv2d)):\n", - " ##print(count, value)\n", " relu_layer_indices.append(count)\n", "\n", "print(\"Layer indices potentially using ReLU:\", relu_layer_indices)\n", @@ -2000,7 +1444,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 20, "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", "metadata": { "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" @@ -2033,7 +1477,7 @@ "id": "01c1645e-205c-4d9a-8af3-e497b3addec1" }, "source": [ - "Copyright 2024 Sony Semiconductor Israel, Inc. All rights reserved.\n", + "Copyright 2025 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", From 81282646daf61a6d9cf4841fbedd029ddf4e23ca Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 31 Jan 2025 10:40:08 +0900 Subject: [PATCH 04/30] Fixed ofirgo-san's review comment.[2] --- ..._pytorch_activation_threshold_search.ipynb | 802 +----------------- 1 file changed, 23 insertions(+), 779 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index 813d75716..186605a7b 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -66,23 +66,13 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "metadata": { "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.0\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" - ] - } - ], + "outputs": [], "source": [ "!pip install -q torch torchvision" ] @@ -309,25 +299,12 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", "metadata": { "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", - "Statistics Collection: 10it [00:10, 1.01s/it]\n", - "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.03it/s]\n", - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", - "Statistics Collection: 10it [00:10, 1.01s/it]\n", - "Calculating quantization parameters: 100%|██████████| 102/102 [00:16<00:00, 6.06it/s]\n" - ] - } - ], + "outputs": [], "source": [ "quantized_models_dict = {}\n", "\n", @@ -368,617 +345,12 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", "metadata": { "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " MobileNetV2(\n", - " (features): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (2): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (3): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (4): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (5): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (6): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (7): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (8): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (9): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (10): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (11): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (12): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (13): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (14): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (15): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (16): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (17): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (18): Conv2dNormActivation(\n", - " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " )\n", - " (classifier): Sequential(\n", - " (0): Dropout(p=0.2, inplace=False)\n", - " (1): Linear(in_features=1280, out_features=1000, bias=True)\n", - " )\n", - ")\n", - "features Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (2): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (3): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (4): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (5): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (6): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (7): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (8): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (9): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (10): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (11): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (12): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (13): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (14): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (15): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (16): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (17): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (18): Conv2dNormActivation(\n", - " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - ")\n", - "features.0 Conv2dNormActivation(\n", - " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - ")\n", - "features.0.0 Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - "features.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - "features.0.2 ReLU6(inplace=True)\n", - "features.1 InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - ")\n", - "features.1.conv Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - ")\n", - "features.1.conv.0 Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - ")\n", - "features.1.conv.0.0 Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n" - ] - } - ], + "outputs": [], "source": [ "for index, (name, layer) in enumerate(float_model.named_modules()):\n", " if index < 10:\n", @@ -1045,18 +417,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "4a5bf3dd", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "10it [00:00, 13.41it/s]\n" - ] - } - ], + "outputs": [], "source": [ "from tqdm import tqdm\n", "import numpy as np\n", @@ -1094,21 +458,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "NGnjrPD_uTd5", "metadata": { "id": "NGnjrPD_uTd5" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{: np.float64(8.0), : np.float64(8.0)}\n", - "{: np.float64(32.0), : np.float64(64.0)}\n" - ] - } - ], + "outputs": [], "source": [ "# layer 4 is the first activation layer - Conv1_relu\n", "optimal_thresholds_relu = {\n", @@ -1142,23 +497,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "VPb8tBNGpJjo", "metadata": { "id": "VPb8tBNGpJjo" }, - "outputs": [ - { - "data": { - "image/png": "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hBOoR8Xo4+PzyBP4pBKCIkWKWD3PkiWL8uTJ81jvXb58+Ww+E35+fgoMDLQpk6xf3+3bt/Xhhx9a+rHnzJlT/v7+un79eor/hpOb3Ny4cUMmk8lq1rHk2rx5s+rVq2cZz+Hv76/3339fkmziTavvhqioKL388st65plnNHfuXKtjn5rHM8GZM2dUpEgRm+5QCd3nHvWdmNzPdUoldz9ubm6WsY4Jjh07psjISAUEBMjf39/qER0drUuXLkmSateurVdeeUUjRoxQzpw51bx5c82ePTvRcSWPiudh32ElSpTQlStXkuyOnLDug3+//v7+VomXJH300Ue6fv26ihYtqjJlyui9997Tvn37Et1uYsqUKaN69epZPR41IcKDvyGp9duUmObNm2vVqlX69ddfLeN3bt26ZfX5PHbsmA4cOGDz3ib8ViW8v45iNps1ceJEFSlSxOrvdN++fVZ/py4uLmrfvr2WLl2qW7duSbp3cdrT09Myrk+693p///13m9dbr149Sbav92mfFCY9yTBjgm7evKly5cqpa9eulnEjKdW7d2+tXLlSn376qcqUKaOrV6/q6tWrqRzpk7F27VpFRERowYIFWrBggc3yefPmqUGDBmm2/zZt2uiLL77Q8uXL1aJFC33//fcqXry4ypUrZ6nz7bffqnPnzmrRooXee+89BQQEWAYLPjhIMqWSe4U/qRYU476B9fbav3+/XF1dH/qF1adPHzVr1kxLly7VihUrNHToUIWFhWnt2rWqUKFCsvZz/9Xj1JDUsXvUpASp6XFmtqtZs6a++uornTx5Uhs3blStWrVkMplUs2ZNbdy4UXnz5pXZbH7sJMjez06JEiW0e/duxcTEJDmF+b59+5QpUyabk6a0kNTrSM7re/fddzV79mz16dNHISEh8vPzs9yXJKXT5vv5+Slv3ryPPOHbt2+f8uXLZ2lFTu7n9cSJE3rhhRdUvHhxTZgwQYGBgXJ3d9dvv/2miRMn2sSbVt8NnTt31vnz57V9+3absYKpeTztlZbfifbs5/6r7gnMZrMCAgKSnOgn4QKKyWTSDz/8oD///FO//PKLVqxYoa5du2r8+PH6888/La31KYknrT333HM6ceKEfvrpJ61cuVJff/21Jk6cqBkzZuiNN95Ik30m9huSGr9NicmXL5/l5L5x48bKmTOnevbsqbp161rO3cxm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ZXPsfX19bd67M2fOpCimsWPHavr06Zo6daoOHTqksWPH6pNPPtGUKVOSXOfUqVNq0qSJ6tatqz179qhPnz564403tGLFCkudhQsXql+/fho2bJh27dqlcuXKqWHDhrp06VKK4gPgWAveCtGvvWppwVshjg4lRRyeBJnNZs2ePVudOnWSm9tTM08DgHSgR48ecnd318qVK1W7dm3lz59fjRo10urVq/Xvv/9qyJAhlromk0lLly61Wj9r1qxWV6f/97//qWjRosqcObMKFiyooUOHWp1UJnRp+uabbxQUFCQ/Pz+1adNGN27ckHSvtWL9+vWaPHmy5ar76dOnbVod6tSpk+gV+oSr/BMmTFCZMmXk7e2twMBAvfPOO4qOjpZ07+p6ly5dFBkZaVlv+PDhkmy7w509e1bNmzdXlixZ5Ovrq9dee00XL15M9utJ4OHhody5c1s9smXLZnVsp0+frpdeekne3t4aNWqUZdtff/21goODLa0PyY3pwfUkqWLFigoMDNTixYstZYsXL1b+/PlVoUIFq5jNZrPCwsIsrRflypXTDz/8IOleq0vdunUlSdmyZbNpRTKbzRo4cKCyZ8+u3LlzW45vco+rdG+201y5csnHx0ehoaG6c+eOkqNTp06aPXu2VVnCb+T9fvnlF9WoUUPvvfeeihUrpqJFi6pFixb6/PPPreqZTCab9y5XrlzJiiXBli1b1Lx5czVp0kRBQUF69dVX1aBBg4e22syYMUPBwcEaP368SpQooZ49e+rVV1/VxIkTLXUmTJigN998U126dFHJkiU1Y8YMZc6cWbNmzUpRfABgD4cnQatXr9bZs2fVtWtXR4cC4AFfbzypZ0eveeTjjTk7bNZ9Y86OZK379caTdsV29epVrVixQu+88468vLysluXOnVvt27fXwoULH9ll534+Pj4KDw/XwYMHNXnyZH311VdWJ22SdOLECS1dulTLli3TsmXLtH79eo0ZM0aSNHnyZIWEhOjNN9+0XHVPbAKXxYsXW12Zf/nll1WsWDHLyamLi4s+++wzHThwQHPmzNHatWs1cOBASfe61k2aNMnqCv+AAQNs9mE2m9W8eXNdvXpV69ev16pVq3Ty5Em1bt062a8nJYYPH66WLVvq77//tnyfHz9+XD/++KMWL16sPXv2JDumB9e7X9euXa2ShFmzZqlLly428YSF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5REfH6/ChQvr7t27mjJlik6ePKlvvvlGM2bMsNlHdHS01qxZoytXrlglcwnq1aunMmXKqH379tq1a5e2b9+ujh07qnbt2ol2XXuYmJgYqzgvXLigK1eupOzApGJMrq6uOnTokA4ePChXV1eb5T4+PhowYID69u2rOXPm6MSJE9q1a5emTJmiOXPmSLrX5c5kMmnZsmW6fPmypfUoNV5D7969NWvWLM2ePVtHjx7VsGHDdODAAavtLFmyRMWLF09yP2+++aYuX76c5P3zhg8froEDB2rdunU6deqUdu/era5du+ru3btW3QANw7B57y5cuGD5XD4qDklq1qyZRo0apV9//VWnT5/WkiVLNGHCBKsW2MGDB6tjx46W5927d9fJkyc1cOBAHT58WNOmTdP3339vlfT369dPX331lebMmaNDhw7p7bff1s2bNxOd6AIAUhtJEIB0q0iRItqxY4cKFiyo1157TQUKFFCjRo1UtGhRbd682WpA+Pjx4xUYGKhatWqpXbt2GjBggFXXspdeekl9+/ZVz549Vb58eW3ZskVDhw5NcUwDBgyQq6urSpYsKX9/f509e9amzoYNG7R//34VKFBAefLksTzOnTuncuXKacKECRo7dqxKly6tefPmKSwszGr96tWrq3v37mrdurX8/f31ySef2OzDZDLpp59+UrZs2fTcc8+pXr16KliwoBYuXJji1/T7779bxZknTx7VrFkzxdtJzZgedcPsjz/+WEOHDlVYWJhKlCihF198Ub/++quCg4Ml3bsfz4gRIzRo0CDlypXL0r0uNV5D69atNXToUA0cOFCVKlXSmTNn9Pbbb1ttJzIyUkeOHElyP25ubsqZM2eSt46oXbu2Tp48qY4dO6p48eJq1KiRLly4oJUrV1pNdBAVFWXz3t0/xulRcUjSlClT9Oqrr+qdd95RiRIlNGDAAHXr1k0ff/yxpU5ERITVZz04OFi//vqrVq1apXLlymn8+PH6+uuvrVruWrdurU8//VQffvihypcvrz179uj3339P8RTeAGAPk5GOp2aLioqSn5+fIiMjH/pj+KRMXHU00fK+9YsmWg48Le7cuaNTp07Z3JclPRo2bJgmTJigVatW6dlnn3V0OACQLBnpexjO5WmaGCEluQETIwDIUEaMGKGgoCD9+eefqlq1qs1YBgAAAJIgABkOYwoAAMDDcIkUAAAAgFMhCQIAAADgVOgOBwAAAMAuQxqX0O278fLKZHvLgqcZSRAAAAAAu4TWKujoEOxCdzgAAAAAToUkCAAAAIBToTscAAAAALvM3HjSMiYoPXWNoyUIANLYunXrZDKZdP369TTdT1BQkCZNmpSm+8jo6tSpoz59+qT6docPH67y5cun+nYBwNFG/XZIn648qlG/HXJ0KClCEgQgXTt37py6du2qvHnzyt3dXQUKFFDv3r3133//OSSexE6iq1evroiICPn5+aXKPsLDw5U1a1ab8h07duitt95KlX0k6Ny5s0wmk83jxRdfTNX9JDeO7t272yzr0aOHTCaTOnfunOztPanENLlOnz4tk8mkgIAA3bhxw2pZ+fLlNXz4cKuyAwcO6LXXXpO/v788PDxUtGhRffjhh7p165bNtnfv3q1WrVopV65c8vT0VJEiRfTmm2/q6NGjVvves2dPorE9+HkLDw+3fA5cXFyUL18+denSRZcuXbLUuf+z4ufnpxo1amjt2rWW5Z07d1aLFi2snptMJo0ZM8Zq30uXLpXJZLIqMwxDX331lUJCQuTr66ssWbKoVKlS6t27t44fP57oa3iYSZMmqVixYvLy8lJgYKD69u2rO3fuPHSdffv2qVatWvL09FRgYKA++eQTmzqLFi1S8eLF5enpqTJlyui3335LcWwA0g5JEIB06+TJk6pcubKOHTum7777TsePH9eMGTO0Zs0ahYSE6OrVq44OUZLk7u6u3Llz25zMpTZ/f39lzpw51bf74osvKiIiwurx3XffJVn/7t27NmWxsbF27fv+9QIDA7VgwQLdvn3bUnbnzh3Nnz9f+fPnt2v7T5sbN27o008/fWidP//8U9WqVVNsbKx+/fVXHT16VKNGjVJ4eLjq169vdcyWLVumZ599VjExMZo3b54OHTqkb7/9Vn5+fho6dKjdcfr6+ioiIkL//POPvvrqKy1fvlyvv/66VZ3Zs2crIiJCmzdvVs6cOdW0aVOdPHkyyW16enpq7NixunbtWpJ1DMNQu3bt1KtXLzVu3FgrV67UwYMHNXPmTHl6emrkyJEpeh3z58/XoEGDNGzYMB06dEgzZ87UwoUL9f777ye5TlRUlBo0aKACBQror7/+0rhx4zR8+HB9+eWXljpbtmxR27ZtFRoaqt27d6tFixZq0aKF9u/fn6L4AKQdkiAA6VaPHj3k7u6ulStXqnbt2sqfP78aNWqk1atX699//9WQIUMsdU0mk5YuXWq1ftasWRUeHm55/r///U9FixZV5syZVbBgQQ0dOtTqhD6hS9M333yjoKAg+fn5qU2bNpYr9507d9b69es1efJky1Xw06dP27Q61KlTJ9HWldOnT0uSJkyYoDJlysjb21uBgYF65513FB0dLeleC0aXLl0UGRlpWS+hleDB7nBnz55V8+bNlSVLFvn6+uq1117TxYsXk/16Enh4eCh37txWj2zZslkd2+nTp+ull16St7e3Ro0aZdn2119/reDgYHl6eqYopgfXk6SKFSsqMDBQixcvtpQtXrxY+fPnV4UKFaxiNpvNCgsLU3BwsLy8vFSuXDn98MMPku61fNStW1eSlC1bNptWJLPZrIEDByp79uzKnTu3TSvMo16DJI0ZM0a5cuWSj4+PQkNDH9mykODdd9/VhAkTrFpV7mcYhkJDQ1WiRAktXrxYVatWVYECBdSqVSv98ssv2rp1qyZOnChJunXrlrp06aLGjRvr559/Vr169RQcHKxq1arp008/1RdffJGsmBJjMpmUO3du5c2bV40aNVKvXr20evVqqwQ1a9asyp07t0qXLq3p06fr9u3bWrVqVZLbrFevnnLnzq2wsLAk6yxcuFALFizQwoULNXToUD377LPKnz+/nn32WY0dO1azZ89O0evYsmWLatSooXbt2ikoKEgNGjRQ27ZttX379iTXmTdvnmJjYzVr1iyVKlVKbdq0Ua9evTRhwgRLncmTJ+vFF1/Ue++9pxIlSujjjz9WxYoVNXXq1BTFByDtkAQBSNLXG0/q2dFrHvl4Y84Om3XfmLMjWet+vTHpK8MPc/XqVa1YsULvvPOOvLy8rJblzp1b7du318KFC2UYRrK36ePjo/DwcB08eFCTJ0/WV199ZTmhTHDixAktXbpUy5Yt07Jly7R+/XpLF57JkycrJCREb775pqXFJDAw0GY/ixcvtmpVefnll1WsWDHlypVLkuTi4qLPPvtMBw4c0Jw5c7R27VoNHDhQ0r2udZMmTbJciY+IiNCAAQNs9mE2m9W8eXNdvXpV69ev16pVq3Ty5Em1bt062a8nJYYPH66WLVvq77//VteuXSVJx48f148//qjFixdrz549yY7pwfXu17VrV6sT3VmzZqlLly428YSF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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -1177,23 +521,12 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "Df7eKzh4oj5X", "metadata": { "id": "Df7eKzh4oj5X" }, - "outputs": [ - { - "data": { - "image/png": "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mjhxornszJkzslgsmjJliiTbz6Zdu3b6/PPPJVl3t7ndF198YV5nVatW1ebNm+8YnyRNmzZN0dHRGj16tFWyJt26kYiMjJTFYtGwYcPM5cnnfvXq1ercubPy588vb29vtWnTRhcvXjTLZfQzeeaZZ1SuXDnt3LlTtWrVUu7cuVW8eHH98MMPkqRVq1apWrVq8vDwUKlSpbR8+XKreP/++2+99dZbKlWqlDw8PJQ/f3698sormb4pHz16tK5du6YvvvjCKlmTpAIFCmjatGm6cuWKRo8ebS5PawxtatfxzJkz9dxzz8nf319ubm4qW7aseQ2klJ66YdasWXrllVckSc8++6x5vpPP7+3jfO7UbTB5m/Scz4weV5LOnj2rjh07KiAgQO7u7qpYsaIiIyOtyqQcy5WZ6zoz7nac5LrgyJEjqlevnry8vNSqVStJUlJSksaPH69HH31U7u7uCggIUOfOna1+HyRpy5YtioiIUIECBeTh4aHQ0FB16NAhU/FI0sqVK1WzZk15enrK19dXjRo10r59++76Xg3D0PDhw1WoUCHlzp1bzz77rPbs2WNT7ubNmxo6dKhKlCghd3d35c+fXzVq1FBUVNRdj5Ga5557TtKtL2+l//u92Lt3r1q2bKm8efOqRo0aktL/tzO1a+zGjRsaPHiwihcvLjc3NwUHB6t///4220rSN998oyeeeEK5c+dW3rx59fTTT5utdneqwzIqMDDQHLKQkpOTk95//33t3LlTCxcuzNS+b3fhwgX169dP5cuXV548eeTt7a0XXnhBf/31l1nmypUr8vT0VM+ePW22/+eff+Ts7KyRI0eayy5duqRevXopODhYbm5uKl68uD755BOrHk0pf2/Hjx9vfnaO1n3c0dHCBrubM2eOXnrpJbm6uqpFixaaMmWKNm/ebCZg0q1KpGbNmtq3b586dOigxx9/XOfPn9fixYv1zz//qEyZMho2bJgGDRqkTp06qWbNmpJS796SK1cuNWnSRAsWLNC0adOsvuFZtGiRbty4oebNm0u6lcB9+eWXatGihd544w1dvnxZX331lSIiIrRp0yZVqlRJfn5+mjJlit588001adJEL730kiSpQoUKab7n119/XZGRkXr55ZfVt29fbdy4USNHjtS+fftsKufDhw/r5ZdfVseOHdW2bVvNmDFD7dq1U+XKlfXoo49m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HipXrpxKlSqliRMn6plnnsnU/gAgsyxGekf84p7FxsbKx8dHMTEx8vb2tnc4wEPl9la17HBDC9xJXFycjh07ptDQULm7Z6y738Pgo48+0tSpU3Xy5El7hwLgIXanujq9uQEtbAAAINubPHmyqlatqvz582vt2rUaPXq0unXrphMXrikhMUkuzk4qnC+3vcMEYGfZsU4gYQMAANneoUOHNHz4cF24cEGFCxdW3759NXDgQB06d003E5OUy5lh+wCkqzcSsl2dQMIGAACyvXHjxmncuHH2DgMAslz2SS0BAAAA4CFDwgYAAAAADoqEDQAAAAAcFAkbAAAAADgoEjYAAAAAcFAkbAAAAADgoEjYAAAAUvHHH3/IYrHo0qVL9/U4ISEhGj9+/H09Rk73zDPPqFevXlm+3yFDhqhSpUpZvl8gI0jYAADAA3fy5El16NBBBQsWlKurq4oUKaKePXvqv//+y9Lj5PN0VYE8bsrn6XrHcqnd8D/55JM6ffq0fHx8siSWWbNmydfX12b55s2b1alTpyw5RrJ27drJYrHYvJ5//vksPU564+jSpYvNuq5du8pisahdu3bp3t+DSqLT6/jx47JYLHJ2dta///5rte706dNycXGRxWLR8ePHzeULFy5U9erV5ePjIy8vLz366KNW196sWbNS/ezc3d0zFNuUKVNUoUIFeXt7y9vbW2FhYfrll1/M9RcuXFD37t1VqlQpeXh4qHDhwurRo4diYmLuuF/DMDRo0CAFBQXJw8ND4eHhOnTokFWZCxcuqFWrVvL29pavr686duyoK1euZCj++yW9dYIjIWEDAAAP1NGjR1WlShUdOnRI3333nQ4fPqypU6dqxYoVCgsL04ULF7LsWAHe7iro66EA74zd7EqSq6urAgMDZbFYsiye1Pj5+Sl37txZvt/nn39ep0+ftnp99913aZa/efOmzbL4+PhMHTvldsHBwZo7d66uX79uLouLi9O3336rwoULZ2r/juaRRx7R7NmzrZZFRkbqkUcesVq2YsUKNWvWTE2bNtWmTZu0detWffTRRzbn3tvb2+az+/vvvzMUU6FChfTxxx9r69at2rJli5577jk1atRIe/bskSSdOnVKp06d0qeffqrdu3dr1qxZWrZsmTp27HjH/Y4aNUoTJ07U1KlTtXHjRnl6eioiIkJxcXFmmVatWmnPnj2K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6KF69erpt99+0969e/XVV1/J3d1dw4cPz9D7iI+PV506dXT8+HH98MMPOnDggKZPn65HHnnEpuzmzZs1bdo0VahQ4a77XbdunVq0aKGOHTtq+/btaty4sRo3bqzdu3ebZUaNGqWJEydq6tSp2rhxozw9PRUREaG4uLgMvYeMIGEDAAAPVNeuXeXq6qrffvtNtWrVUuHChfXCCy9o+fLl+vfff/Xee++ZZS0WixYtWmS1va+vr2bNmmX+/M4776hkyZLKnTu3ihYtqg8++MAq+Zgy9mM1Ca+hr7/+WiEhIfLx8VHz5s3NFpF27dpp1apVmjBhgtm6cPz4cZvWnGeeeSbVVqvjx49LksaOHavy5cvL09NTwcHBeuutt3TlyhVJt1qG2rdvr5iYGHO75NaX27tEnjhxQo0aNVKePHnk7e2tV199VWfOnDHXJ3fTS+v9JHNzc1NgYKDVK2/evFbndsqUKXrxxRfl6empjz76yNz3l19+qdDQULm7u2coptu3k6THH39cwcHBWrBggblswYIFKly4sB577DGrmJOSkjRy5EiFhobKw8NDFStW1A8//CDpVovSs88+K0nKmzevTetcUlKS+vfvr3z58ikwMNCmdetu70G61RITEBAgLy8vdezYMd034d27d9fYsWOtWqtSMgxDHTt2VJkyZbRgwQI98cQTKlKkiF555RX99NNPWr9+vcaNGydJunbtmtq3b6969epp8eLFCg8PV2hoqKpVq6ZPP/1U06ZNS1dMqbFYLAoMDFTBggX1wgsvqEePHlq+fLlVMu3r66vAwECVK1dOU6ZM0fXr1xUVFZXmPsPDwxUYGKiRI0emWWbevHmaO3eu5s2bpw8++EDVq1dX4cKFVb16dX3yySeaOXNmht7HjBkzdOHCBS1atEhPPfWUQkJCVKtWLVWsWNGq3JUrV9SqVStNnz7d6tpPy4QJE/T888/r7bffVpkyZfThhx/q8ccf16RJkyTd+hzHjx+v999/X40aNVKFChU0e/ZsnTp1yqaeykokbAAA5DBfrjmq6iNW3PX1euRmm21fj9ycrm2/XJP2N+53cuHCBf36669666235OHhYbUuMDBQrVq10rx582QYRrr36eXlpVmzZmnv3r2aMGGCpk+fbt78Jjv59zEtWrRIS5Ys0ZIlS7Rq1SqzG9eECRMUFhamN954w2yJCg4O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mPz586tOnTqaPHmy1UQH0q3uRnPmzLG62fPz89Pp06fNnw8dOmQ1Bfq6detUpEgRvffee6pSpYpKlChhdrvLCFdXV5vxL7c7f/68GjZsqKZNm6p3795W67Zu3aqkpCSNGTNG1atXV8mSJXXq1KkMH6NMmTI6efKkTp48aS7bu3evLl26pLJly2bwXWWNrIrp+eefV3x8vG7evKmIiAib9WXLlpWbm5tOnDih4sWLW72SW8aSW17vdh4z8x7KlCmjjRs3Wm23YcOGDB3niSee0EsvvaQBAwZYLa9UqZJKly6tcePG2SSRf/31l5YvX64WLVpIkurWrasCBQpo1KhRqR7jXh5p4OTkpOLFi6to0aKpJmvSraSkePHiGUrWkn388cfmJCoptWjRQgcOHNCPP/6Yqbhv99RTT+nw4cNW5/LgwYMKCgqSq6urateurV27dmnHjh3mq0qVKmrVqpUWRP1pleilFBYWphUrVlgti4qKMhPn0NBQBQYGWpWJjY3Vxo0bszy5TomEDQAAPFCTJk3SjRs3FBERodWrV+vkyZNatmyZ6tSpYz6XKtlzzz2nSZMmafv27dqyZYu6dOli1ZJSokQJnThxQnPnztWRI0c0ceJELVy4MMMxhYSEaOPGjTp+/LjOnz+fastM06ZNlTt3bg0ZMkTR0dHmKzExUcWLF9fNmzf12Wef6ejRo/r66681depUm2NcuXJFK1as0Pnz51N99lZ4eLjKly+vVq1aadu2bdq0aZPatGmjWrVqpdp98U5u3LhhFWd0dLTOnz+fsROThTE5Oztr37592rt3b6o3zF5eXurXr5969+6tyMhIHTlyRNu2bdNnn32myMhISbe6XVosFi1ZskTnzp0zW+Wy4j307NlTM2bM0MyZM3Xw4EENHjxYe/bssdrPwoUL7zq27KOPPtLKlSutWpQsFou++uor7d27V02bNtWmTZt04sQJzZ8/Xw0bNlRYWJg5BtLT01Nffvmlli5dqhdffFHLly/X8ePHtWXLFvXv39+m2+aBAwesEpMdO3ak+oiGByH5HE+cONFqefPmzfXyyy+refPmGjZsmPm7tmrVKs2bN8/qeti0aZNKly6tf//9N83jvPnmm7pw4YJ69uypgwcPaunSpRoxYoS6du0q6da1VK5cOauXp6en8ufPrxKl/+9LhjZt2mjgwIHmzz179tSyZcs0ZswY7d+/X0OGDNGWLVvMluHkZ7kNHz5cixcv1q5du9SmTRsVLFjQ6vl0WY2EDQAAPFAlSpTQ5s2bVbRoUb366qsqUqSIXnjhBZUsWVJr1661mihjzJgxCg4OVs2aNdWyZUv169fPqnvhiy++qN69e6tbt26qVKmS1q1bZ/WcqjJB3grwdpd7rjvf8vTr10/Ozs4qW7as/Pz8dOLECZsyq1ev1u7du1WkSBEFBQWZr5MnT6pixYoaO3asPvnkE5UrV05z5syxmeb8ySefVJcuXdSsWTP5+fml2oJisVj0448/Km/evHr66acVHh6uokWLat68eek+v8mWLVtmFWdQUJBq1KiR4f1kZUze3t424wtT+vDDD/XBBx9o5MiRKlOmjJ5//nktXbpUoaGhkqRHHnlEQ4cO1YABAxQQEGDeSGfFe2jWrJk++OAD9e/fX5UrV9bff/+tN99802o/MTExNl37bleyZEl16NDBZrKSJ598Uhs2bJCzs7NeeOEFFS9eXAMHDlTbtm0VFRVl1e2zUaNGWrdunXLlyqWWLVuqdOnSatGihWJiYmyeWda8eXM99thjVq/bu/U9SMOGDbP5wsNisWjevHkaP368fv75Z9WuXVulSpVShw4dFBwcbE64It2a7OTAgQN3TDqDg4P166+/avPmzapQoYJ69Oihnj172rRspqZMkLcqFPJVmSBvnThxwqoF/8knn9S3336rL774wnykxKJFi1SuXDmzTP/+/dW9e3d16tRJVatW1ZUrV7Rs2bL7OlbOYtzPkY2wEhsbKx8fH8XExNyxsgKQ9cZFHbxrmd51St61DOAo4uLidOzYMZtnXmVXgwcP1tixYxUVFaXq1avbOxwAyBJ3qqvTmxsw6QgAALC7oUOHKiQkRBs2bNATTzxh86wqAHhYkbABAACHkNqDlAHgYUfCBgAAcqwzsXFKTDLk7GRRQDqeLwcgZ8uOdQIJGwAAyLEuXI3XzcQk5XJ2yjY3ZwDun+xYJ5CwAciR0jPJCJATMHcYADiurKijGdELAEA2lPwsstSe5QUAcAzJdXTK50dmFC1sAABkQ87OzvL19dXZs2clSblz55bFYrFzVI4n6Wa8jKQkJSU52TwXC8DD50HVCYZh6Nq1azp79qx8fX1TfVh8epGwAQCQTQUGBkqSmbTB1pmY/5tgwOlq9hivAuD+edB1gq+vr1lXZxYJGwAA2ZTFYlFQUJD8/f118+ZNe4fjkAZMW6/zV26oQB43zescZu9wANjZg6wTcuXKdU8ta8lI2AAAyOacnZ2z5KYgJzpzNUnRlxOVaEmSuzstbMDDLjvWCUw6AgAAAAAOioQNAAAAABwUXSIBAECOVa1oPl24Gq98nq72DgWAA8iOdQIJGwAAyLEmNH/M3iEAcCDZsU6gSyQAAAAAOCgSNgAAAABwUCRsAAAAAOCgGMMGAAByrBZfbDAfkvtdp+r2DgeAnWXHOoGEDQAA5FjHzl9VdGycLscl2DsUAA4gO9YJdIkEAAAAAAdFwgYAAAAADsquCVtiYqI++OADhYaGysPDQ8WKFdOHH34owzDMMoZhaNCgQQoKCpKHh4fCw8N16NAhq/1cuHBBrVq1kre3t3x9fdWxY0dduXLFqszOnTtVs2ZNubu7Kzg4WKNGjbKJZ/78+SpdurTc3d1Vvnx5/fzzz1br0xMLAAAAAGQVuyZsn3zyiaZMmaJJkyZp3759+uSTTzRq1Ch99tlnZplRo0Zp4sSJmjp1qjZu3ChPT09FREQoLi7OLNOqVSvt2bNHUVFRWrJkiVavXq1OnTqZ62NjY1W3bl0VKVJEW7du1ejRozVkyBB98cUXZpl169apRYsW6tixo7Zv367GjRurcePG2r17d4ZiAQAAAICsYjFSNmc9YA0aNFBAQIC++uorc1nTpk3l4eGhb775RoZhqGDBgurbt6/69esnSYqJiVFAQIBmzZql5s2ba9++fSpbtqw2b96sKlWqSJKWLVumevXq6Z9//lHBggU1ZcoUvffee4qOjparq6skacCAAVq0aJH2798vSWrWrJmuXr2qJUuWmLFUr15dlSpV0tSpU9MVy93ExsbKx8dHMTEx8vb2zpqTCCBV46IOZnib3nVK3odIANhT9RErFB0bp0Bvd214t7a9wwFgZ45UJ6Q3N7BrC9uTTz6pFStW6ODBWzdWf/31l/7880+98MILkqRjx44pOjpa4eHh5jY+Pj6qVq2a1q9fL0lav369fH19zWRNksLDw+Xk5KSNGzeaZZ5++mkzWZOkiIgIHThwQBcvXjTLpDxOcpnk46QnltvduHFDsbGxVi8AAAAASC+7Tus/YMAAxcbGqnTp0nJ2dlZiYqI++ugjtWrVSpIUHR0tSQoICLDaLiAgwFwXHR0tf39/q/UuLi7Kly+fVZnQ0FCbfSSvy5s3r6Kjo+96nLvFcruRI0dq6NCh6TgTAAAAAGDLri1s33//vebMmaNvv/1W27ZtU2RkpD799FNFRkbaM6wsM3DgQMXExJivkydP2jskAAAAANmIXVvY3n77bQ0YMMAc/1W+fHn9/fffGjlypNq2bavAwEBJ0pkzZxQUFGRud+bMGVWqVEmSFBgYqLNnz1rtNyEhQRcuXDC3DwwM1JkzZ6zKJP98tzIp198tltu5ubnJzc0tfScDAABkuR61S+hafIJyu9r1lgeAg8iOdYJdW9iuXbsmJyfrEJydnZWUlCRJCg0NVWBgoFasWGGuj42N1caNGxUWFiZJCgsL06VLl7R161azzMqVK5WUlKRq1aqZZVavXq2bN2+aZaKiolSqVCnlzZvXLJPyOMllko+TnlgAAIBjaVmtsF6vWVQtqxW2dygAHEB2rBPsmrA1bNhQH330kZYuXarjx49r4cKFGjt2rJo0aSJJslgs6tWrl4YPH67Fixdr165datOmjQoWLKjGjRtLksqUKaPnn39eb7zxhjZt2qS1a9eqW7duat68uQoWLChJatmypVxdXdWxY0ft2bNH8+bN04QJE9SnTx8zlp49e2rZsmUaM2aM9u/fryFDhmjLli3q1q1bumMBAAAAgKxk17bAzz77TB988IHeeustnT17VgULFlTnzp01aNAgs0z//v119epVderUSZcuXVKNGjW0bNkyubu7m2XmzJmjbt26qXbt2nJyclLTpk01ceJEc72Pj49+++03de3aVZUrV1aBAgU0aNAgq2e1Pfnkk/r222/1/vvv691331WJEiW0aNEilStXLkOxAAAAAEBWsetz2B42PIcNeHB4DhsASTobG6dEw5CzxSJ/b75gBR52jlQnpDc3yD6j7QAAADLoxUlrHeYhuQDsLzvWCXYdwwYAAAAASBsJGwAAAAA4KBI2AAAAAHBQJGwAAAAA4KBI2AAAAADAQZGwAQAAAICDImEDAAAAAAfFc9gAIA2pPXybh2sDAIAHiRY2AAAAAHBQtLABAIAca84b1ZSYZMjZyWLvUAA4gOxYJ5CwAQCAHKuYXx57hwDAgWTHOoEukQAAAADgoEjYAAAAAMBB0SUSAADkWD/u+FfX4xPl4eqsRpUesXc4AOwsO9YJJGwAACDHGvnzfkXHxinQ2z3b3JwBuH+yY51Al0gAAAAAcFC0sAHA/5fag7IBAADsiRY2AAAAAHBQJGwAAAAA4KBI2AAAAADAQZGwAQAAAICDImEDAAAAAAdFwgYAAAAADopp/QEAQI7l5+Vm9S+Ah1t2rBNI2AAAQI71U/ca9g4BgAPJjnUCXSIBAAAAwEGRsAEAAACAgyJhAwAAAAAHxRg2AACQYw1csEsx1+Pl4+GqkS+Vt3c4AOwsO9YJJGwAACDH+n3/WUXHxinQ293eoQBwANmxTqBLJAAAAAA4KBI2AAAAAHBQdIkEkO2Nizpo7xAAAADuC1rYAAAAAMBBkbABAAAAgIMiYQMAAAAAB0XCBgAAAAAOioQNAAAAABwUs0QCAIAc68VKBRVz7aZ8cueydygAHEB2rBNI2AAAQI71br0y9g4BgAPJjnUCXSIBAAAAwEGRsAEAAACAgyJhAwAAAAAHxRg2AACQYz035g+djb0hf283rez7jL3DAWBn2bFOoIUNAADkWNduJOrKjQRdu5Fo71AAOIDsWCeQsAEAAACAgyJhAwAAAAAHRcIGAAAAAA6KhA0AAAAAHBQJGwAAAAA4KBI2AAAAAHBQJGwAAAAA4KBI2AAAAADAQbnYOwAAAID75aMm5RR3M0nuufiOGkD2rBNI2AAAQI5Vu0yAvUMA4ECyY52QfVJLAAAAAHjIkLABAAAAgIOiSyQAAMixdv0To/jEJLk6O6l8IR97hwPAzrJjnUDCBgAAcqw3Zm9RdGycAr3dteHd2vYOB4CdZcc6gS6RAAAAAOCgSNgAAAAAwEHRJRJAtjMu6qC9QwAAAHggaGEDAAAAAAdFwgYAAAAADoqEDQAAAAAcFAkbAAAAADgoEjYAAAAAcFAkbAAAAADgoJjWHwAA5FjL+9aSYRiyWCz2DgWAA8iOdQIJGwAAyLHyuHGrA+D/ZMc6gS6RAAAAAOCgSNgAAAAAwEFlvzZBAACAdPpyzVFdjkuQl7uLXq9Z1N7hALCz7FgnkLABAIAc68s1xxQdG6dAb/dsc3MG4P7JjnUCXSIBAAAAwEGRsAEAAACAgyJhAwAAAAAHRcIGAAAAAA6KhA0AAAAAHBQJGwAAAAA4KLsnbP/++69at26t/Pnzy8PDQ+XLl9eWLVvM9YZhaNCgQQoKCpKHh4fCw8N16NAhq31cuHBBrVq1kre3t3x9fdWxY0dduXLFqszOnTtVs2ZNubu7Kzg4WKNGjbKJZf78+SpdurTc3d1Vvnx5/fzzz1br0xMLAAAAAGQVuyZsFy9e1FNPPaVcuXLpl19+0d69ezVmzBjlzZvXLDNq1ChNnDhRU6dO1caNG+Xp6amIiAjFxcWZZVq1aqU9e/YoKipKS5Ys0erVq9WpUydzfWxsrOrWrasiRYpo69atGj16tIYMGaIvvvjCLLNu3Tq1aNFCHTt21Pbt29W4cWM1btxYu3fvzlAsALLeuKiDVi8AAICHhcUwDMNeBx8wYIDWrl2rNWvWpLreMAwVLFhQffv2Vb9+/SRJMTExCggI0KxZs9S8eXPt27dPZcuW1ebNm1WlShVJ0rJly1SvXj39888/KliwoKZMmaL33ntP0dHRcnV1NY+9aNEi7d+/X5LUrFkzXb16VUuWLDGPX716dVWqVElTp05NVyx3ExsbKx8fH8XExMjb2zvzJw54yDhSkta7Tkl7hwAgA16P3Kz/rsYrv6ervmxb1d7hALAzR6oT0psb2LWFbfHixapSpYpeeeUV+fv767HHHtP06dPN9ceOHVN0dLTCw8PNZT4+PqpWrZrWr18vSVq/fr18fX3NZE2SwsPD5eTkpI0bN5plnn76aTNZk6SIiAgdOHBAFy9eNMukPE5ymeTjpCeW2924cUOxsbFWLwAA8OB82baqFr71lN1vzAA4huxYJ9g1YTt69KimTJmiEiVK6Ndff9Wbb76pHj16KDIyUpIUHR0tSQoICLDaLiAgwFwXHR0tf39/q/UuLi7Kly+fVZnU9pHyGGmVSbn+brHcbuTIkfLx8TFfwcHBdzslAAAAAGCya8KWlJSkxx9/XCNGjNBjjz2mTp066Y033tDUqVPtGVaWGThwoGJiYszXyZMn7R0SAAAAgGzExZ4HDwoKUtmyZa2WlSlTRv/73/8kSYGBgZKkM2fOKCgoyCxz5swZVapUySxz9uxZq30kJCTowoUL5vaBgYE6c+aMVZnkn+9WJuX6u8VyOzc3N7m5ud3hDADIbm4fT8eYNgAAcD/ZtYXtqaee0oEDB6yWHTx4UEWKFJEkhYaGKjAwUCtWrDDXx8bGauPGjQoLC5MkhYWF6dKlS9q6datZZuXKlUpKSlK1atXMMqtXr9bNmzfNMlFRUSpVqpQ5I2VYWJjVcZLLJB8nPbEAAADH8nrkZjWZvFavR262dygAHEB2rBPsmrD17t1bGzZs0IgRI3T48GF9++23+uKLL9S1a1dJksViUa9evTR8+HAtXrxYu3btUps2bVSwYEE1btxY0q0Wueeff15vvPGGNm3apLVr16pbt25q3ry5ChYsKElq2bKlXF1d1bFjR+3Zs0fz5s3ThAkT1KdPHzOWnj17atmyZRozZoz279+vIUOGaMuWLerWrVu6YwEAAI5l97+x2n7iknb/y8RfALJnnWDXLpFVq1bVwoULNXDgQA0bNkyhoaEaP368WrVqZZbp37+/rl69qk6dOunSpUuqUaOGli1bJnd3d7PMnDlz1K1bN9WuXVtOTk5q2rSpJk6caK738fHRb7/9pq5du6py5coqUKCABg0aZPWstieffFLffvut3n//fb377rsqUaKEFi1apHLlymUoFgAAAADIKnZ9DtvDhuewAZnjSM9hux1j2ADHVn3ECkXHxinQ210b3q1t73AA2Jkj1QnZ4jlsAAAAAIC0kbABAAAAgIMiYQMAAAAAB0XCBgAAAAAOioQNAAAAABwUCRsAAAAAOCi7PocNAADgfnq9ZqguxyXIy51bHgDZs07IVKRHjx5V0aJFszoWAACALPV6Te5XAPyf7FgnZKpLZPHixfXss8/qm2++UVxcXFbHBAAAAABQJhO2bdu2qUKFCurTp48CAwPVuXNnbdq0KatjAwAAAICHWqYStkqVKmnChAk6deqUZsyYodOnT6tGjRoqV66cxo4dq3PnzmV1nAAAABl25UaCLsfd1JUbCfYOBYADyI51wj3NEuni4qKXXnpJ8+fP1yeffKLDhw+rX79+Cg4OVps2bXT69OmsihMAACDDwsesUvkhvyl8zCp7hwLAAWTHOuGeErYtW7borbfeUlBQkMaOHat+/frpyJEjioqK0qlTp9SoUaOsihMAAAAAHjqZmiVy7Nixmjlzpg4cOKB69epp9uzZqlevnpycbuV/oaGhmjVrlkJCQrIyVgAAAAB4qGQqYZsyZYo6dOigdu3aKSgoKNUy/v7++uqrr+4pOAAAAAB4mGUqYTt06NBdy7i6uqpt27aZ2T0AAAAAQJkcwzZz5kzNnz/fZvn8+fMVGRl5z0EBAAAAADKZsI0cOVIFChSwWe7v768RI0bcc1AAAAAAgEwmbCdOnFBoaKjN8iJFiujEiRP3HBQAAAAAIJMJm7+/v3bu3Gmz/K+//lL+/PnvOSgAAAAAQCYTthYtWqhHjx76/ffflZiYqMTERK1cuVI9e/ZU8+bNszpGAAAAAHgoZWqWyA8//FDHjx9X7dq15eJyaxdJSUlq06YNY9gAAIDDmN6miuITk+TqnKnvqAHkMNmxTshUwubq6qp58+bpww8/1F9//SUPDw+VL19eRYoUyer4AAAAMq18IR97hwDAgWTHOiFTCVuykiVLqmTJklkVCwAAAAAghUwlbImJiZo1a5ZWrFihs2fPKikpyWr9ypUrsyQ4AAAAAHiYZSph69mzp2bNmqX69eurXLlyslgsWR0XAADAPVux74zibibJPZeTapcJsHc4AOwsO9YJmUrY5s6dq++//1716tXL6ngAAACyzHsLdys6Nk6B3u7Z5uYMwP2THeuETE2P4urqquLFi2d1LAAAAACAFDKVsPXt21cTJkyQYRhZHQ8AAAAA4P/LVJfIP//8U7///rt++eUXPfroo8qVK5fV+gULFmRJcAAAAADwMMtUwubr66smTZpkdSwAAAAAgBQylbDNnDkzq+MAAAAAANwmU2PYJCkhIUHLly/XtGnTdPnyZUnSqVOndOXKlSwLDgAAAAAeZplqYfv777/1/PPP68SJE7px44bq1KkjLy8vffLJJ7px44amTp2a1XECAAAAwEMnUy1sPXv2VJUqVXTx4kV5eHiYy5s0aaIVK1ZkWXAAAAAA8DDLVAvbmjVrtG7dOrm6ulotDwkJ0b///pslgQEAANyr3G7OyuPmotxuzvYOBYADyI51QqYStqSkJCUmJtos/+eff+Tl5XXPQQEAAGSFlX2fsXcIABxIdqwTMtUlsm7duho/frz5s8Vi0ZUrVzR48GDVq1cvq2IDAAAAgIdaplrYxowZo4iICJUtW1ZxcXFq2bKlDh06pAIFCui7777L6hgBAAAA4KGUqYStUKFC+uuvvzR37lzt3LlTV65cUceOHdWqVSurSUgAAAAAAJmXqYRNklxcXNS6deusjAUAACBLjfh5n2Ku3ZRP7lx6t14Ze4cDwM6yY52QqYRt9uzZd1zfpk2bTAUDAACQlRbvOKXo2DgFertnm5szAPdPdqwTMpWw9ezZ0+rnmzdv6tq1a3J1dVXu3LlJ2AAAAAAgC2QqYbt48aLNskOHDunNN9/U22+/fc9BAcDDblzUQaufe9cpaadIAACAPWV6DNvtSpQooY8//litW7fW/v37s2q3AODQSKwAAMD9lKnnsKXFxcVFp06dyspdAgAAAMBDK1MtbIsXL7b62TAMnT59WpMmTdJTTz2VJYEBAAAAwMMuUwlb48aNrX62WCzy8/PTc889pzFjxmRFXAAAAADw0MtUwpaUlJTVcQAAAAAAbpOlY9gAAAAAAFknUy1sffr0SXfZsWPHZuYQAAAA9+zZ0v6KuR4vHw9Xe4cCwAFkxzohUwnb9u3btX37dt28eVOlSpWSJB08eFDOzs56/PHHzXIWiyVrogQAAMiEkS+Vt3cIABxIdqwTMpWwNWzYUF5eXoqMjFTevHkl3XqYdvv27VWzZk317ds3S4MEAAAAgIdRpsawjRkzRiNHjjSTNUnKmzevhg8fziyRAAAAAJBFMpWwxcbG6ty5czbLz507p8uXL99zUAAAAACATHaJbNKkidq3b68xY8boiSeekCRt3LhRb7/9tl566aUsDRDAw2Vc1EF7hwAgB2n42Z86d/mG/Lzc9FP3GvYOB4CdZcc6IVMJ29SpU9WvXz+1bNlSN2/evLUjFxd17NhRo0ePztIAAQAAMuvc5RuKjo2zdxgAHER2rBMylbDlzp1bkydP1ujRo3XkyBFJUrFixeTp6ZmlwQEAAADAw+yeHpx9+vRpnT59WiVKlJCnp6cMw8iquAAAAADgoZephO2///5T7dq1VbJkSdWrV0+nT5+WJHXs2JEp/QEAAAAgi2QqYevdu7dy5cqlEydOKHfu3ObyZs2aadmyZVkWHAAAAAA8zDI1hu23337Tr7/+qkKFClktL1GihP7+++8sCQwAAAAAHnaZamG7evWqVctasgsXLsjNze2egwIAAAAAZLKFrWbNmpo9e7Y+/PBDSZLFYlFSUpJGjRqlZ599NksDBICc5vZnzfWuU9JOkQAAAEeXqYRt1KhRql27trZs2aL4+Hj1799fe/bs0YULF7R27dqsjhEAAAAAHkqZStjKlSungwcPatKkSfLy8tKVK1f00ksvqWvXrgoKCsrqGAEAADJlYL3Suh6fKA9XZ3uHAsABZMc6IcMJ282bN/X8889r6tSpeu+99+5HTAAAAFmiUaVH7B0CAAeSHeuEDE86kitXLu3cufN+xAIAAAAASCFTs0S2bt1aX331VVbHAgAAAABIIVNj2BISEjRjxgwtX75clStXlqenp9X6sWPHZklwAPAwuH3WSABZ58i5K0pMMuTsZFExvzz2DgeAnWXHOiFDCdvRo0cVEhKi3bt36/HHH5ckHTxofaNhsViyLjoAAIB70Gr6RkXHxinQ210b3q1t73AA2Fl2rBMylLCVKFFCp0+f1u+//y5JatasmSZOnKiAgID7EhwAAAAAPMwyNIbNMAyrn3/55RddvXo1SwMCAAAAANySqUlHkt2ewAEAAAAAsk6GEjaLxWIzRo0xawAAAABwf2RoDJthGGrXrp3c3NwkSXFxcerSpYvNLJELFizIuggBAAAA4CGVoYStbdu2Vj+3bt06S4MBAAAAAPyfDCVsM2fOvF9xAAAAAABuk6kHZwMAHqzUHq7du05JO0QCAAAepHuaJRIAAAAAcP/QwgYAAHKsxd2eUqJhyJlZrQEoe9YJJGwAACDH8vd2t3cIABxIdqwTHKZL5McffyyLxaJevXqZy+Li4tS1a1flz59fefLkUdOmTXXmzBmr7U6cOKH69esrd+7c8vf319tvv62EhASrMn/88Ycef/xxubm5qXjx4po1a5bN8T///HOFhITI3d1d1apV06ZNm6zWpycWAAAAAMhKDpGwbd68WdOmTVOFChWslvfu3Vs//fST5s+fr1WrVunUqVN66aWXzPWJiYmqX7++4uPjtW7dOkVGRmrWrFkaNGiQWebYsWOqX7++nn32We3YsUO9evXS66+/rl9//dUsM2/ePPXp00eDBw/Wtm3bVLFiRUVEROjs2bPpjgUAAAAAsprFMAzDngFcuXJFjz/+uCZPnqzhw4erUqVKGj9+vGJiYuTn56dvv/1WL7/8siRp//79KlOmjNavX6/q1avrl19+UYMGDXTq1CkFBARIkqZOnap33nlH586dk6urq9555x0tXbpUu3fvNo/ZvHlzXbp0ScuWLZMkVatWTVWrVtWkSZMkSUlJSQoODlb37t01YMCAdMWSHrGxsfLx8VFMTIy8vb2z7BwCOUlqsyFmJ+mZuTGr3iOzRAJ39+3GE7oWn6Dcri5qWa2wvcMBYGeOVCekNzewewtb165dVb9+fYWHh1st37p1q27evGm1vHTp0ipcuLDWr18vSVq/fr3Kly9vJmuSFBERodjYWO3Zs8csc/u+IyIizH3Ex8dr69atVmWcnJwUHh5ulklPLKm5ceOGYmNjrV4AcrZxUQdtXgDsZ+KKQxq+dJ8mrjhk71AAOIDsWCfYddKRuXPnatu2bdq8ebPNuujoaLm6usrX19dqeUBAgKKjo80yKZO15PXJ6+5UJjY2VtevX9fFixeVmJiYapn9+/enO5bUjBw5UkOHDk1zPQAAAADcid1a2E6ePKmePXtqzpw5cnfPfrO1pMfAgQMVExNjvk6ePGnvkAAAAABkI3ZL2LZu3aqzZ8/q8ccfl4uLi1xcXLRq1SpNnDhRLi4uCggIUHx8vC5dumS13ZkzZxQYGChJCgwMtJmpMfnnu5Xx9vaWh4eHChQoIGdn51TLpNzH3WJJjZubm7y9va1eAAAAAJBedkvYateurV27dmnHjh3mq0qVKmrVqpX5/1y5cmnFihXmNgcOHNCJEycUFhYmSQoLC9OuXbusZnOMioqSt7e3ypYta5ZJuY/kMsn7cHV1VeXKla3KJCUlacWKFWaZypUr3zUWAAAAAMhqdhvD5uXlpXLlylkt8/T0VP78+c3lHTt2VJ8+fZQvXz55e3ure/fuCgsLM2dlrFu3rsqWLavXXntNo0aNUnR0tN5//3117dpVbm5ukqQuXbpo0qRJ6t+/vzp06KCVK1fq+++/19KlS83j9unTR23btlWVKlX0xBNPaPz48bp69arat28vSfLx8blrLAAAAACQ1ew66cjdjBs3Tk5OTmratKlu3LihiIgITZ482Vzv7OysJUuW6M0331RYWJg8PT3Vtm1bDRs2zCwTGhqqpUuXqnfv3powYYIKFSqkL7/8UhEREWaZZs2a6dy5cxo0aJCio6NVqVIlLVu2zGoikrvFAgBpYaZIAACQWXZ/DtvDhOewAXdHcpN+PIcNuLvqI1YoOjZOgd7u2vBubXuHA8DOHKlOyDbPYQMAAAAApM6hu0QCAADci9ACnvJyd1GBPG72DgWAA8iOdQIJGwAAyLG+68TkYAD+T3asE+gSCQAAAAAOioQNAAAAABwUCRsAAAAAOCjGsAEAgByr59ztunA1Xvk8XTWh+WP2DgeAnWXHOoGEDQAA5Fgbj14wn7kEANmxTqBLJAAAAAA4KBI2AAAAAHBQdIkEYFfjog7aOwQAAACHRQsbAAAAADgoEjYAAAAAcFAkbAAAAADgoEjYAAAAAMBBkbABAAAAgINilkgAAJBjNX8iWJfjEuTlzi0PgOxZJ2SfSAEAADKoV3hJe4cAwIFkxzqBLpEAAAAA4KBI2AAAAADAQdElEkC6jIs6aPVz7zrZr0tBTsNnAgBAzkfCBiBVtycDAJAdVR+xQtGxcQr0dteGd2vbOxwAdpYd6wS6RAIAAACAgyJhAwAAAAAHRcIGAAAAAA6KhA0AAAAAHBQJGwAAAAA4KBI2AAAAAHBQJGwAAAAA4KBI2AAAAADAQZGwAQAAAICDcrF3AAAAAPfLuGaVFJ+YJFdnvqMGkD3rBBI2AACQY4UVy2/vEAA4kOxYJ2Sf1BIAAAAAHjK0sAF4YMZFHbR3CAAAANkKCRsAAMix1h/5zxyvkh27QgHIWtmxTiBhAwAAOVbveTsUHRunQG93bXi3tr3DAWBn2bFOYAwbAAAAADgoEjYAAAAAcFAkbAAAAADgoEjYAAAAAMBBkbABAAAAgIMiYQMAAAAAB8W0/sBD4PYHVveuU9JOkQAAACAjaGEDAAAAAAdFwgYAAAAADooukQAAIMfa8G5te4cAwIFkxzqBhA3AfXP72DkAAABkDF0iAQAAAMBBkbABAAAAgIOiSySATEmtuyOPCwDgaMYvP6jLcQnycndRr3DqKOBhlx3rBBI2AACQY83ddFLRsXEK9HbPNjdnAO6f7Fgn0CUSAAAAABwUCRsAAAAAOCgSNgAAAABwUIxhA5BleO4aAABA1qKFDQAAAAAcFAkbAAAAADgoEjYAAAAAcFAkbAAAAADgoJh0BAAA5FjViubThavxyufpau9QADiA7FgnkLABD6HbZ3PsXaeknSIBgPtrQvPH7B0CAAeSHesEukQCAAAAgIMiYQMAAAAAB0XCBgAAAAAOijFsAGzGtAFATtHiiw06f+WGCuRx03edqts7HAB2lh3rBBI2AACQYx07f1XRsXG6HJdg71AAOIDsWCfQJRIAAAAAHBQJGwAAAAA4KBI2AAAAAHBQJGwAAAAA4KCYdAQAcojbZ/vsXaeknSIBAABZhRY2AAAAAHBQJGwAAAAA4KBI2AAAAADAQTGGDQAA5Fg9apfQtfgE5XbllgdA9qwTsk+kAAAAGdSyWmF7hwDAgWTHOoEukQAAAADgoEjYAAAAAMBB0SUSAADkWGdj45RoGHK2WOTv7W7vcADYWXasE0jYAABAjvXipLWKjo1ToLe7Nrxb297hALCz7Fgn2LVL5MiRI1W1alV5eXnJ399fjRs31oEDB6zKxMXFqWvXrsqfP7/y5Mmjpk2b6syZM1ZlTpw4ofr16yt37tzy9/fX22+/rYSEBKsyf/zxhx5//HG5ubmpePHimjVrlk08n3/+uUJCQuTu7q5q1app06ZNGY4FAJA546IOWr0AAICdE7ZVq1apa9eu2rBhg6KionTz5k3VrVtXV69eNcv07t1bP/30k+bPn69Vq1bp1KlTeumll8z1iYmJql+/vuLj47Vu3TpFRkZq1qxZGjRokFnm2LFjql+/vp599lnt2LFDvXr10uuvv65ff/3VLDNv3jz16dNHgwcP1rZt21SxYkVFRETo7Nmz6Y4FAAAAALKSxTAMw95BJDt37pz8/f21atUqPf3004qJiZGfn5++/fZbvfzyy5Kk/fv3q0yZMlq/fr2qV6+uX375RQ0aNNCpU6cUEBAgSZo6dareeecdnTt3Tq6urnrnnXe0dOlS7d692zxW8+bNdenSJS1btkySVK1aNVWtWlWTJk2SJCUlJSk4OFjdu3fXgAED0hXL3cTGxsrHx0cxMTHy9vbO0nMH3AmtFQ+n3nVK2juEDLn9Os1u8cMxVR+xItt1fwJw/zhSnZDe3MChZomMiYmRJOXLl0+StHXrVt28eVPh4eFmmdKlS6tw4cJav369JGn9+vUqX768maxJUkREhGJjY7Vnzx6zTMp9JJdJ3kd8fLy2bt1qVcbJyUnh4eFmmfTEcrsbN24oNjbW6gUAAAAA6eUwCVtSUpJ69eqlp556SuXKlZMkRUdHy9XVVb6+vlZlAwICFB0dbZZJmawlr09ed6cysbGxun79us6fP6/ExMRUy6Tcx91iud3IkSPl4+NjvoKDg9N5NgAAAADAgRK2rl27avfu3Zo7d669Q8kyAwcOVExMjPk6efKkvUMCAAAAkI04xLT+3bp105IlS7R69WoVKlTIXB4YGKj4+HhdunTJqmXrzJkzCgwMNMvcPptj8syNKcvcPpvjmTNn5O3tLQ8PDzk7O8vZ2TnVMin3cbdYbufm5iY3N7cMnAkAAAAA+D92bWEzDEPdunXTwoULtXLlSoWGhlqtr1y5snLlyqUVK1aYyw4cOKATJ04oLCxMkhQWFqZdu3ZZzeYYFRUlb29vlS1b1iyTch/JZZL34erqqsqVK1uVSUpK0ooVK8wy6YkFAAAAALKSXVvYunbtqm+//VY//vijvLy8zLFgPj4+8vDwkI+Pjzp27Kg+ffooX7588vb2Vvfu3RUWFmbOyli3bl2VLVtWr732mkaNGqXo6Gi9//776tq1q9m61aVLF02aNEn9+/dXhw4dtHLlSn3//fdaunSpGUufPn3Utm1bValSRU888YTGjx+vq1evqn379mZMd4sFcATMCAkAAJBz2DVhmzJliiTpmWeesVo+c+ZMtWvXTpI0btw4OTk5qWnTprpx44YiIiI0efJks6yzs7OWLFmiN998U2FhYfL09FTbtm01bNgws0xoaKiWLl2q3r17a8KECSpUqJC+/PJLRUREmGWaNWumc+fOadCgQYqOjlalSpW0bNkyq4lI7hYLAABwLHPeqKbEJEPOThZ7hwLAAWTHOsGhnsOW0/EcNjwItLAhWXZ7jhnPYQMAPEyy5XPYAAAAAAD/h4QNAAAAAByUQ0zrDwAAcD/8uONfXY9PlIersxpVesTe4QCws+xYJ5CwAQCAHGvkz/sVHRunQG/3bHNzBuD+yY51AgkbAMAumCAHAIC7YwwbAAAAADgoEjYAAAAAcFAkbAAAAADgoEjYAAAAAMBBkbABAAAAgINilkgAyKFSm4Wxd52SdogEAABkFi1sAAAAAOCgaGEDAAA5lp+Xm9W/AB5u2bFOIGEDAAA51k/da9g7BAAOJDvWCXSJBAAAAAAHRcIGAAAAAA6KhA0AAAAAHBRj2AAAQI41cMEuxVyPl4+Hq0a+VN7e4QCws+xYJ5CwAQCAHOv3/WcVHRunQG93e4cCwAFkxzqBLpEAAAAA4KBI2AAAAADAQZGwAQAAAICDImEDAAAAAAdFwgYAAAAADoqEDQAAAAAcFAkbAAAAADgoEjYAAAAAcFA8OBsAAORYL1YqqJhrN+WTO5e9QwHgALJjnUDCBmRz46IO2jsEAHBY79YrY+8QADiQ7FgnkLABABxSal9G9K5T0g6RAABgP4xhAwAAAAAHRQsbADxEbm+1osUKAADHRsIGAAByrOfG/KGzsTfk7+2mlX2fsXc4AOwsO9YJdIkEAAA51rUbibpyI0HXbiTaOxQADiA71gkkbAAAAADgoEjYAAAAAMBBkbABAAAAgIMiYQMAAAAAB8UskQCA+y61h2ADAIC7o4UNAAAAABwUCRsAAAAAOCgSNgAAAABwUIxhAwAAOdZHTcop7maS3HPxHTWA7FknkLABAIAcq3aZAHuHAMCBZMc6gYQNyEaYaQ9Z7fZrqnedknaKBAAApCb7tAUCAAAAwEOGFjYAAJBj7fonRvGJSXJ1dlL5Qj72DgeAnWXHOoGEDQAA5FhvzN6i6Ng4BXq7a8O7te0dDgA7y451Al0iAQAAAMBBkbABAAAAgIMiYQMAAAAAB8UYNgBAluMRFAAAZA0SNgCAKbVEi2ezAQBgPyRsAIBsgwd9AwAeNoxhAwAAAAAHRQsb4MAYBwRHQKsWAAD2QwsbAAAAADgoWtgAAPeElmA4suV9a8kwDFksFnuHAsABZMc6gYQNAJAhJGjITvK4casD4P9kxzqBLpEAAAAA4KBI2AAAAADAQWW/NkEAAIB0+nLNUV2OS5CXu4ter1nU3uEAsLPsWCeQsAEAgBzryzXHFB0bp0Bv92xzcwbg/smOdQIJG+BAmMwBAAAAKZGwAQCyLR7qDQDI6Zh0BAAAAAAcFAkbAAAAADgoEjYAAAAAcFAkbAAAAADgoJh0BLATZoQEAADA3dDCBgAAAAAOihY2AECOkVrLNVP9P9zKPeKtIF935fd0tXcoABxAdqwTSNgAAECO9WXbqvYOAYADyY51Agkb8IAwZg0AAAAZxRg2AAAAAHBQtLABAHK021u3GdMGAMhOSNiA+4QukABgf69HbtZ/V+OV39M1W45dAZC1smOdQMIGAAByrN3/xio6Nk6B3u72DgWAA8iOdQIJGwDgocLU/wCA7ISEDcgCdH8EAADA/UDCBgB46DExCQDAUZGwAZlAixqQs5HAAQAcBQlbBn3++ecaPXq0oqOjVbFiRX322Wd64okn7B0W7iOSMwAAANgLCVsGzJs3T3369NHUqVNVrVo1jR8/XhERETpw4ID8/f3tHR6yCAkagNulp16gFQ4AcD+QsGXA2LFj9cYbb6h9+/aSpKlTp2rp0qWaMWOGBgwYYOfokB4kYwDuF5I6AMD9QMKWTvHx8dq6dasGDhxoLnNyclJ4eLjWr1+f6jY3btzQjRs3zJ9jYmIkSbGxsfc32Bzq85WH7R0CANyTkYu2PZDjdH2u+AM5TnaQEHdVSTduKCEukb+/AByqTkg+vmEYdyxHwpZO58+fV2JiogICAqyWBwQEaP/+/aluM3LkSA0dOtRmeXBw8H2JEQAASXrX3gE4oJOSfD60dxQAHIUj1QmXL1+Wj49PmutJ2O6jgQMHqk+fPubPSUlJunDhgvLnzy+LxWLHyLJebGysgoODdfLkSXl7e9s7HDzEuBbhKLgW4Si4FuEouBatGYahy5cvq2DBgncsR8KWTgUKFJCzs7POnDljtfzMmTMKDAxMdRs3Nze5ublZLfP19b1fIToEb29vfgHhELgW4Si4FuEouBbhKLgW/8+dWtaSOT2AOHIEV1dXVa5cWStWrDCXJSUlacWKFQoLC7NjZAAAAAByKlrYMqBPnz5q27atqlSpoieeeELjx4/X1atXzVkjAQAAACArkbBlQLNmzXTu3DkNGjRI0dHRqlSpkpYtW2YzEcnDyM3NTYMHD7bpAgo8aFyLcBRci3AUXItwFFyLmWMx7jaPJAAAAADALhjDBgAAAAAOioQNAAAAABwUCRsAAAAAOCgSNgAAAABwUCRsuCfHjx9Xx44dFRoaKg8PDxUrVkyDBw9WfHy8VbmdO3eqZs2acnd3V3BwsEaNGmWniJGTff755woJCZG7u7uqVaumTZs22Tsk5HAjR45U1apV5eXlJX9/fzVu3FgHDhywKhMXF6euXbsqf/78ypMnj5o2baozZ87YKWI8LD7++GNZLBb16tXLXMa1iAfl33//VevWrZU/f355eHiofPny2rJli7neMAwNGjRIQUFB8vDwUHh4uA4dOmTHiB0bCRvuyf79+5WUlKRp06Zpz549GjdunKZOnap3333XLBMbG6u6devq/7V35zFRXW0YwJ9hG3fQigxKFUoVbIkiIHbAKFvUSBsRQ8VS4zJ1QVyqNWptLca01WqMtmpdGgTboiixjUs1lqCAUQRBRY0stYILgsQWVKxWZN7vD+P9emVpLctM8fklN5l7zrnnvEzeDLycOzN9+vRBbm4u1qxZg+XLl2Pbtm0mjJzamt27d2PBggWIjY3FmTNnMHDgQIwcORIVFRWmDo3asPT0dMTExODUqVNISUlBTU0NRowYgfv37ytj5s+fjwMHDiA5ORnp6em4efMmwsPDTRg1tXWnT5/G1q1bMWDAAFU7c5FaQ2VlJfz9/WFtbY3Dhw/j0qVLWLt2Lbp27aqMWb16Nb766its2bIFWVlZ6NixI0aOHImHDx+aMHIzJkTNbPXq1eLi4qKcf/3119K1a1f5888/lbbFixeLm5ubKcKjNsrX11diYmKU89raWunZs6esXLnShFHRi6aiokIASHp6uoiIVFVVibW1tSQnJytj8vPzBYBkZmaaKkxqw+7duyd9+/aVlJQUGT58uMybN09EmIvUehYvXixDhw5tsN9oNIpOp5M1a9YobVVVVaLVamXXrl2tEeJ/DnfYqNnduXMH3bp1U84zMzMxbNgw2NjYKG0jR45EYWEhKisrTREitTGPHj1Cbm4uQkJClDYLCwuEhIQgMzPThJHRi+bOnTsAoLwG5ubmoqamRpWb7u7u6N27N3OTWkRMTAxCQ0NVOQcwF6n17N+/Hz4+PoiIiECPHj0waNAgfPPNN0p/cXExysvLVbloa2uLIUOGMBcbwIKNmtXly5exYcMGzJgxQ2krLy+Hg4ODatzT8/Ly8laNj9qm27dvo7a2tt48Y45RazEajXj//ffh7+8PDw8PAE9e42xsbGBnZ6cay9yklpCUlIQzZ85g5cqVdfqYi9Rarly5gs2bN6Nv3744cuQIoqOjMXfuXOzYsQPA///24+/sf44FG9VryZIl0Gg0jR4FBQWqa0pLSzFq1ChERERg2rRpJoqciMg0YmJicPHiRSQlJZk6FHoBXb9+HfPmzUNiYiLatWtn6nDoBWY0GuHl5YXPP/8cgwYNwvTp0zFt2jRs2bLF1KH9Z1mZOgAyTx988AEmT57c6JhXXnlFeXzz5k0EBgbCz8+vzoeJ6HS6Op9C9fRcp9M1T8D0QuvevTssLS3rzTPmGLWG2bNn4+DBg8jIyICTk5PSrtPp8OjRI1RVVal2Npib1Nxyc3NRUVEBLy8vpa22thYZGRnYuHEjjhw5wlykVuHo6IjXXntN1da/f3/s3bsXwP//9rt16xYcHR2VMbdu3YKnp2erxflfwh02qpe9vT3c3d0bPZ6+J620tBQBAQHw9vZGfHw8LCzUaaXX65GRkYGamhqlLSUlBW5ubqpPDCL6t2xsbODt7Y3U1FSlzWg0IjU1FXq93oSRUVsnIpg9ezZ+/PFHHD16FC4uLqp+b29vWFtbq3KzsLAQ165dY25SswoODsaFCxdw7tw55fDx8UFUVJTymLlIrcHf37/O15sUFRWhT58+AAAXFxfodDpVLt69exdZWVnMxYaY+lNP6L/txo0b8uqrr0pwcLDcuHFDysrKlOOpqqoqcXBwkIkTJ8rFixclKSlJOnToIFu3bjVh5NTWJCUliVarlYSEBLl06ZJMnz5d7OzspLy83NShURsWHR0ttra2kpaWpnr9++OPP5QxM2fOlN69e8vRo0clJydH9Hq96PV6E0ZNL4q/fkqkCHORWkd2drZYWVnJZ599Jr/88oskJiZKhw4d5Pvvv1fGrFq1Suzs7GTfvn1y/vx5GTNmjLi4uMiDBw9MGLn5YsFGTRIfHy8A6j3+Ki8vT4YOHSparVZ69eolq1atMlHE1JZt2LBBevfuLTY2NuLr6yunTp0ydUjUxjX0+hcfH6+MefDggcyaNUu6du0qHTp0kLFjx6r+qUXUUp4t2JiL1FoOHDggHh4eotVqxd3dXbZt26bqNxqNsmzZMnFwcBCtVivBwcFSWFhoomjNn0ZExDR7e0RERERERNQYvoeNiIiIiIjITLFgIyIiIiIiMlMs2IiIiIiIiMwUCzYiIiIiIiIzxYKNiIiIiIjITLFgIyIiIiIiMlMs2IiIiIiIiMwUCzYiIiIiIiIzxYKNiIheKAkJCbCzs2vxdUpKSqDRaHDu3LkWX6upJk+ejLCwMFOHQURE9WDBRkREZi0zMxOWlpYIDQ197mudnZ2xfv16Vdv48eNRVFTUTNE9UV/B8/LLL6OsrAweHh7NutZfzZkzB/3796+379q1a7C0tMT+/ftbbH0iImp5LNiIiMisxcXFYc6cOcjIyMDNmzebPF/79u3Ro0ePZoiscZaWltDpdLCysmqxNQwGAwoKCnDy5Mk6fQkJCejRowdGjx7dYusTEVHLY8FGRERmq7q6Grt370Z0dDRCQ0ORkJBQZ8yBAwcwePBgtGvXDt27d8fYsWMBAAEBAbh69Srmz58PjUYDjUYDQH1LZFFRETQaDQoKClRzrlu3Dq6urgCA2tpaGAwGuLi4oH379nBzc8OXX36pjF2+fDl27NiBffv2KeukpaXVe0tkeno6fH19odVq4ejoiCVLluDx48dKf0BAAObOnYtFixahW7du0Ol0WL58eYPPj6enJ7y8vLB9+3ZVu4ggISEBkyZNgkajaTT++tS3M+np6amKpaqqCu+99x7s7e3RpUsXBAUFIS8vr9F5iYjo+bFgIyIis7Vnzx64u7vDzc0N7777LrZv3w4RUfp/+uknjB07FqNHj8bZs2eRmpoKX19fAMAPP/wAJycnrFixAmVlZSgrK6szf79+/eDj44PExERVe2JiIt555x0AgNFohJOTE5KTk3Hp0iV88sknWLp0Kfbs2QMAWLhwId5++22MGjVKWcfPz6/OWqWlpRg9ejQGDx6MvLw8bN68GXFxcfj0009V43bs2IGOHTsiKysLq1evxooVK5CSktLgc2QwGLBnzx7cv39faUtLS0NxcTGmTp36t/H/WxEREaioqMDhw4eRm5sLLy8vBAcH4/fff2/SvERE9AwhIiIyU35+frJ+/XoREampqZHu3bvLsWPHlH69Xi9RUVENXt+nTx9Zt26dqi0+Pl5sbW2V83Xr1omrq6tyXlhYKAAkPz+/wXljYmJk3LhxyvmkSZNkzJgxqjHFxcUCQM6ePSsiIkuXLhU3NzcxGo3KmE2bNkmnTp2ktrZWRESGDx8uQ4cOVc0zePBgWbx4cYOxVFZWSrt27SQ+Pl5pmzhxYp15nif++p63gQMHSmxsrIiIHD9+XLp06SIPHz5UjXF1dZWtW7c2uC4RET0/7rAREZFZKiwsRHZ2NiZMmAAAsLKywvjx4xEXF6eMOXfuHIKDg5u0TmRkJEpKSnDq1CkAT3bXvLy84O7urozZtGkTvL29YW9vj06dOmHbtm24du3ac62Tn58PvV6v3JoJAP7+/qiursaNGzeUtgEDBqiuc3R0REVFRYPz2tnZITw8XLkt8u7du9i7dy8MBkOzxv9XeXl5qK6uxksvvYROnTopR3FxMX799dd/PS8REdXVcu+EJiIiaoK4uDg8fvwYPXv2VNpEBFqtFhs3boStrS3at2/f5HV0Oh2CgoKwc+dOvPHGG9i5cyeio6OV/qSkJCxcuBBr166FXq9H586dsWbNGmRlZTV57fpYW1urzjUaDYxGY6PXGAwGBAcH4/Llyzh27BgsLS0RERHxr+O3sLBQ3XoKADU1Ncrj6upqODo6Ii0trc61rfGVCURELxIWbEREZHYeP36Mb7/9FmvXrsWIESNUfWFhYdi1axdmzpyJAQMGIDU1FVOmTKl3HhsbG9TW1v7telFRUVi0aBEmTJiAK1euIDIyUuk7ceIE/Pz8MGvWLKXt2V2kf7JO//79sXfvXoiIsst24sQJdO7cGU5OTn8bY2MCAwPh4uKC+Ph4HDt2DJGRkejYseM/jv9Z9vb2qvf83b17F8XFxcq5l5cXysvLYWVlBWdn5ybFTkREjeMtkUREZHYOHjyIyspKGAwGeHh4qI5x48Ypt0XGxsZi165diI2NRX5+Pi5cuIAvvvhCmcfZ2RkZGRkoLS3F7du3G1wvPDwc9+7dQ3R0NAIDA1W7en379kVOTg6OHDmCoqIiLFu2DKdPn1Zd7+zsjPPnz6OwsBC3b99W7UY9NWvWLFy/fh1z5sxBQUEB9u3bh9jYWCxYsAAWFk37dazRaDB16lRs3rwZmZmZqtsh/0n8zwoKCsJ3332H48eP48KFC5g0aRIsLS2V/pCQEOj1eoSFheHnn39GSUkJTp48iY8++gg5OTlN+lmIiEiNBRsREZmduLg4hISEwNbWtk7fuHHjkJOTg/PnzyMgIADJycnYv38/PD09ERQUhOzsbGXsihUrUFJSAldXV9jb2ze4XufOnfHWW28hLy8PUVFRqr4ZM2YgPDwc48ePx5AhQ/Dbb7+pdqsAYNq0aXBzc4OPjw/s7e1x4sSJOmv06tULhw4dQnZ2NgYOHIiZM2fCYDDg448/ft6np16TJ0/GnTt38Prrr2PIkCHPFf+zPvzwQwwfPhxvvvkmQkNDERYWpnzNAfCkQDx06BCGDRuGKVOmoF+/foiMjMTVq1fh4ODQLD8PERE9oZFnb1InIiIiIiIis8AdNiIiIiIiIjPFgo2IiIiIiMhMsWAjIiIiIiIyUyzYiIiIiIiIzBQLNiIiIiIiIjPFgo2IiIiIiMhMsWAjIiIiIiIyUyzYiIiIiIiIzBQLNiIiIiIiIjPFgo2IiIiIiMhMsWAjIiIiIiIyU/8DJQ3RYICxdscAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -1224,37 +557,12 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "631780a79e2cedf0", "metadata": { "collapsed": false }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [05:52<00:00, 1.11it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.4199, Accuracy: 35571/50000 (71%)\n", - "\n", - "Float model's Top 1 accuracy on the Imagenet validation set: 71.14%\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "from tqdm import tqdm\n", "import torch.nn.functional as F\n", @@ -1291,54 +599,12 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [04:52<00:00, 1.34it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.0591, Accuracy: 35955/50000 (72%)\n", - "\n", - "Results for QuantizationErrorMethod.MSE: Loss = -6.05907375, Accuracy = 0.7191\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [04:43<00:00, 1.38it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.2192, Accuracy: 35927/50000 (72%)\n", - "\n", - "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2191725, Accuracy = 0.71854\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "evaluation_results = {}\n", "\n", @@ -1377,21 +643,12 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "qml4LLmWZLP4", "metadata": { "id": "qml4LLmWZLP4" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Layer indices potentially using ReLU: [5, 9, 16, 20, 27, 31, 39, 43, 50, 54, 62, 66, 74, 78, 85, 89, 97, 101, 109, 113, 121, 125, 132, 136, 144, 148, 156, 160, 167, 171, 179, 183, 191, 195, 202]\n", - "Number of relu layers 35\n" - ] - } - ], + "outputs": [], "source": [ "import torch.nn as nn\n", "\n", @@ -1444,25 +701,12 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", "metadata": { "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - ")\n", - "PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - ")\n" - ] - } - ], + "outputs": [], "source": [ "for error_method, data in quantized_models_dict.items():\n", " quantized_model = data[\"quantized_model\"]\n", @@ -1512,7 +756,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.16" + "version": "3.9.5" } }, "nbformat": 4, From 67b78b5ff99a6527e90e834646fcbbb0f8a73486 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 31 Jan 2025 11:22:45 +0900 Subject: [PATCH 05/30] Fixed ofirgo-san's review comment.[3] --- ...ple_pytorch_activation_threshold_search.ipynb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index 186605a7b..9d0a50a17 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -506,11 +506,14 @@ "source": [ "import matplotlib.pyplot as plt\n", "\n", + "ls_table = ['--', ':']\n", + "lc_table = ['blue', 'red']\n", + "\n", "# Plotting\n", "plt.figure(figsize=(10, 6))\n", "plt.hist(all_activations_relu, bins=100, alpha=0.5, label='Original')\n", - "for method, threshold in optimal_thresholds_relu.items():\n", - " plt.axvline(threshold, linestyle='--', linewidth=2, label=f'{method}: {threshold:.2f}')\n", + "for index, (method, threshold) in enumerate(optimal_thresholds_relu.items()):\n", + " plt.axvline(threshold, linestyle=ls_table[index], color=lc_table[index], linewidth=2, label=f'{method}: {threshold:.2f}')\n", "\n", "plt.title('Activation Distribution with Optimal Quantization Thresholds First Relu Layer')\n", "plt.xlabel('Activation Value')\n", @@ -530,11 +533,14 @@ "source": [ "import matplotlib.pyplot as plt\n", "\n", + "ls_table = ['--', ':']\n", + "lc_table = ['blue', 'red']\n", + "\n", "# Plotting\n", "plt.figure(figsize=(10, 6))\n", "plt.hist(all_activations_project, bins=100, alpha=0.5, label='Original')\n", - "for method, threshold in optimal_thresholds_project.items():\n", - " plt.axvline(threshold, linestyle='--', linewidth=2, label=f'{method}: {threshold:.2f}')\n", + "for index, (method, threshold) in enumerate(optimal_thresholds_project.items()):\n", + " plt.axvline(threshold, linestyle=ls_table[index], color=lc_table[index], linewidth=2, label=f'{method}: {threshold:.2f}')\n", "\n", "plt.title('Activation Distribution with Optimal Quantization Thresholds Project BN layer')\n", "plt.xlabel('Activation Value')\n", @@ -756,7 +762,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.5" + "version": "3.10.16" } }, "nbformat": 4, From 722380b00dc97e29ab41eb2198d5de284bef8efd Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Mon, 3 Feb 2025 11:01:06 +0900 Subject: [PATCH 06/30] PR comment correction (in progress) --- .../example_pytorch_activation_threshold_search.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index 9d0a50a17..ddaa4a05a 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -340,7 +340,7 @@ "\n", "MCT’s `quantization_info` stores the threshold values for each layer. However, to view the actual activation distributions, the model needs to be reconstructed up to and including the target layer selected for visualization.\n", "\n", - "To do this, we first need to identify the layer names. In Keras, this can be easily done for the first 10 layers using the following code snippet." + "To do this, we first need to identify the layer names. " ] }, { @@ -353,8 +353,8 @@ "outputs": [], "source": [ "for index, (name, layer) in enumerate(float_model.named_modules()):\n", - " if index < 10:\n", - " print(name, layer)\n", + " if index < 14:\n", + " print(name)\n", " else:\n", " break" ] From 5564182033566802415c1cdea4fc5b06f6939bcb Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Tue, 4 Feb 2025 08:29:41 +0900 Subject: [PATCH 07/30] [DEBUG]about printout layer_type --- ..._pytorch_activation_threshold_search.ipynb | 1122 ++++++++++++++++- 1 file changed, 1095 insertions(+), 27 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index ddaa4a05a..5585ced37 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -66,13 +66,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "metadata": { "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.0\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], "source": [ "!pip install -q torch torchvision" ] @@ -299,12 +309,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", "metadata": { "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", + "Statistics Collection: 10it [00:10, 1.00s/it]\n", + "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.98it/s]\n", + "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", + "Statistics Collection: 10it [00:10, 1.00s/it]\n", + "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.99it/s]\n" + ] + } + ], "source": [ "quantized_models_dict = {}\n", "\n", @@ -345,16 +368,406 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", "metadata": { "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "---------------------------\n", + "MobileNetV2(\n", + " (features): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (2): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (3): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (4): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", + " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (5): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (6): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (7): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", + " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (8): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (9): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (10): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (11): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", + " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (12): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (13): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (14): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", + " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (15): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (16): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (17): InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", + " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + " )\n", + " (18): Conv2dNormActivation(\n", + " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " )\n", + " (classifier): Sequential(\n", + " (0): Dropout(p=0.2, inplace=False)\n", + " (1): Linear(in_features=1280, out_features=1000, bias=True)\n", + " )\n", + ")\n", + "---------------------------\n", + "# 2 features.0 Conv2dNormActivation(\n", + " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + ")\n", + "# 3 features.0.0 Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", + "# 4 features.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + "# 5 features.0.2 ReLU6(inplace=True)\n", + "# 6 features.1 InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + ")\n", + "# 7 features.1.conv Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + ")\n", + "# 8 features.1.conv.0 Conv2dNormActivation(\n", + " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + ")\n", + "# 9 features.1.conv.0.0 Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", + "# 10 features.1.conv.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + "# 11 features.1.conv.0.2 ReLU6(inplace=True)\n", + "# 12 features.1.conv.1 Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + "# 13 features.1.conv.2 BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + "# 14 features.2 InvertedResidual(\n", + " (conv): Sequential(\n", + " (0): Conv2dNormActivation(\n", + " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (1): Conv2dNormActivation(\n", + " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", + " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU6(inplace=True)\n", + " )\n", + " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", + " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " )\n", + ")\n", + "Name: features.0.0, Type: Conv2d\n", + "Name: features.0.1, Type: BatchNorm2d\n", + "Name: features.0.2, Type: ReLU6\n", + "Name: features.1.conv.0.0, Type: Conv2d\n", + "Name: features.1.conv.0.1, Type: BatchNorm2d\n", + "Name: features.1.conv.0.2, Type: ReLU6\n", + "Name: features.1.conv.1, Type: Conv2d\n", + "Name: features.1.conv.2, Type: BatchNorm2d\n", + "Name: features.2.conv.0.0, Type: Conv2d\n", + "Name: features.2.conv.0.1, Type: BatchNorm2d\n" + ] + } + ], "source": [ + "import torch\n", + "import torch.nn as nn\n", + "def get_leaf_layer_info(module, parent_name=''):\n", + " leaf_layer_info = []\n", + " \n", + " # Iterate through all child modules with their names\n", + " for name, child in module.named_children():\n", + " # Construct the full name of the child layer\n", + " full_name = f\"{parent_name}.{name}\" if parent_name else name\n", + " \n", + " # Recurse into the child module\n", + " leaf_layer_info.extend(get_leaf_layer_info(child, full_name))\n", + " \n", + " # If it has no children, it's a leaf layer; add its name and type\n", + " if not isinstance(module, nn.Sequential) and not isinstance(module, nn.ModuleList) and not list(module.children()):\n", + " leaf_layer_info.append((parent_name, type(module).__name__))\n", + "\n", + " return leaf_layer_info\n", + "\n", + "print('---------------------------')\n", + "print(float_model)\n", + "print('---------------------------')\n", + "\n", + "leaf_layer_info = get_leaf_layer_info(float_model)\n", "for index, (name, layer) in enumerate(float_model.named_modules()):\n", - " if index < 14:\n", - " print(name)\n", + " if index <= 1: continue\n", + " if index < 15:\n", + " print(\"#\", index, name, layer)\n", + " else:\n", + " break\n", + "\n", + "for index, (layer_name, layer_type) in enumerate(leaf_layer_info):\n", + " #print(f\"Layer Name: {layer_name}, Layer Type: {layer_type}\")\n", + " if index < 10:\n", + " print(f\"Name: {layer_name}, Type: {layer_type}\")\n", " else:\n", " break" ] @@ -382,8 +795,8 @@ }, "outputs": [], "source": [ - "layer_name1 = 'features.0.2' # Conv1_relu\n", - "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BN\n", + "layer_name1 = 'features.0.2' # Conv1_relu\n", + "layer_name2 = 'features.1.conv.1' # expanded_conv_project_BN\n", "\n", "# add hook\n", "def get_layer_output(model, layer_name):\n", @@ -417,10 +830,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "4a5bf3dd", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "10it [00:00, 13.84it/s]\n" + ] + } + ], "source": [ "from tqdm import tqdm\n", "import numpy as np\n", @@ -458,12 +879,546 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "NGnjrPD_uTd5", "metadata": { "id": "NGnjrPD_uTd5" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{: np.float64(8.0), : np.float64(8.0)}\n", + "dict_items([(, {'quantization_config': QuantizationConfig(activation_error_method=, weights_error_method=, relu_bound_to_power_of_2=False, weights_bias_correction=True, weights_second_moment_correction=False, input_scaling=False, softmax_shift=False, shift_negative_activation_correction=True, activation_channel_equalization=False, z_threshold=inf, min_threshold=1.52587890625e-05, l_p_value=2, linear_collapsing=True, residual_collapsing=True, shift_negative_ratio=0.05, shift_negative_threshold_recalculation=False, shift_negative_params_search=False, concat_threshold_update=False, activation_bias_correction=False, activation_bias_correction_threshold=0.0, custom_tpc_opset_to_layer=None), 'quantized_model': PytorchModel(\n", + " (x): DummyPlaceHolder()\n", + " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " (features_0_2): ReLU6(inplace=True)\n", + " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", + " )\n", + " (features_1_conv_0_2): ReLU6(inplace=True)\n", + " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_0_2): ReLU6(inplace=True)\n", + " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", + " )\n", + " (features_2_conv_1_2): ReLU6(inplace=True)\n", + " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_0_2): ReLU6(inplace=True)\n", + " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", + " )\n", + " (features_3_conv_1_2): ReLU6(inplace=True)\n", + " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_0_2): ReLU6(inplace=True)\n", + " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", + " )\n", + " (features_4_conv_1_2): ReLU6(inplace=True)\n", + " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_0_2): ReLU6(inplace=True)\n", + " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_5_conv_1_2): ReLU6(inplace=True)\n", + " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_0_2): ReLU6(inplace=True)\n", + " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_6_conv_1_2): ReLU6(inplace=True)\n", + " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_0_2): ReLU6(inplace=True)\n", + " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", + " )\n", + " (features_7_conv_1_2): ReLU6(inplace=True)\n", + " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_0_2): ReLU6(inplace=True)\n", + " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_8_conv_1_2): ReLU6(inplace=True)\n", + " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_0_2): ReLU6(inplace=True)\n", + " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_9_conv_1_2): ReLU6(inplace=True)\n", + " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_0_2): ReLU6(inplace=True)\n", + " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_10_conv_1_2): ReLU6(inplace=True)\n", + " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_0_2): ReLU6(inplace=True)\n", + " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_11_conv_1_2): ReLU6(inplace=True)\n", + " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_0_2): ReLU6(inplace=True)\n", + " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_12_conv_1_2): ReLU6(inplace=True)\n", + " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_0_2): ReLU6(inplace=True)\n", + " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_13_conv_1_2): ReLU6(inplace=True)\n", + " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_0_2): ReLU6(inplace=True)\n", + " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", + " )\n", + " (features_14_conv_1_2): ReLU6(inplace=True)\n", + " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_0_2): ReLU6(inplace=True)\n", + " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_15_conv_1_2): ReLU6(inplace=True)\n", + " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_0_2): ReLU6(inplace=True)\n", + " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_16_conv_1_2): ReLU6(inplace=True)\n", + " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_0_2): ReLU6(inplace=True)\n", + " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_17_conv_1_2): ReLU6(inplace=True)\n", + " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_18_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_18_2): ReLU6(inplace=True)\n", + " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (classifier_0): Dropout(p=0.2, inplace=False)\n", + " (classifier_1): PytorchQuantizationWrapper(\n", + " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", + " )\n", + " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + "), 'quantization_info': }), (, {'quantization_config': QuantizationConfig(activation_error_method=, weights_error_method=, relu_bound_to_power_of_2=False, weights_bias_correction=True, weights_second_moment_correction=False, input_scaling=False, softmax_shift=False, shift_negative_activation_correction=True, activation_channel_equalization=False, z_threshold=inf, min_threshold=1.52587890625e-05, l_p_value=2, linear_collapsing=True, residual_collapsing=True, shift_negative_ratio=0.05, shift_negative_threshold_recalculation=False, shift_negative_params_search=False, concat_threshold_update=False, activation_bias_correction=False, activation_bias_correction_threshold=0.0, custom_tpc_opset_to_layer=None), 'quantized_model': PytorchModel(\n", + " (x): DummyPlaceHolder()\n", + " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + " )\n", + " (features_0_2): ReLU6(inplace=True)\n", + " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", + " )\n", + " (features_1_conv_0_2): ReLU6(inplace=True)\n", + " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_0_2): ReLU6(inplace=True)\n", + " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", + " )\n", + " (features_2_conv_1_2): ReLU6(inplace=True)\n", + " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_0_2): ReLU6(inplace=True)\n", + " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", + " )\n", + " (features_3_conv_1_2): ReLU6(inplace=True)\n", + " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_0_2): ReLU6(inplace=True)\n", + " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", + " )\n", + " (features_4_conv_1_2): ReLU6(inplace=True)\n", + " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_0_2): ReLU6(inplace=True)\n", + " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_5_conv_1_2): ReLU6(inplace=True)\n", + " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_0_2): ReLU6(inplace=True)\n", + " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", + " )\n", + " (features_6_conv_1_2): ReLU6(inplace=True)\n", + " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_0_2): ReLU6(inplace=True)\n", + " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", + " )\n", + " (features_7_conv_1_2): ReLU6(inplace=True)\n", + " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_0_2): ReLU6(inplace=True)\n", + " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_8_conv_1_2): ReLU6(inplace=True)\n", + " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_0_2): ReLU6(inplace=True)\n", + " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_9_conv_1_2): ReLU6(inplace=True)\n", + " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_0_2): ReLU6(inplace=True)\n", + " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_10_conv_1_2): ReLU6(inplace=True)\n", + " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_0_2): ReLU6(inplace=True)\n", + " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", + " )\n", + " (features_11_conv_1_2): ReLU6(inplace=True)\n", + " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_0_2): ReLU6(inplace=True)\n", + " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_12_conv_1_2): ReLU6(inplace=True)\n", + " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_0_2): ReLU6(inplace=True)\n", + " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", + " )\n", + " (features_13_conv_1_2): ReLU6(inplace=True)\n", + " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_0_2): ReLU6(inplace=True)\n", + " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", + " )\n", + " (features_14_conv_1_2): ReLU6(inplace=True)\n", + " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_0_2): ReLU6(inplace=True)\n", + " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_15_conv_1_2): ReLU6(inplace=True)\n", + " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_0_2): ReLU6(inplace=True)\n", + " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_16_conv_1_2): ReLU6(inplace=True)\n", + " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_0_2): ReLU6(inplace=True)\n", + " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", + " )\n", + " (features_17_conv_1_2): ReLU6(inplace=True)\n", + " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (features_18_0_bn): PytorchQuantizationWrapper(\n", + " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", + " )\n", + " (features_18_2): ReLU6(inplace=True)\n", + " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + " (classifier_0): Dropout(p=0.2, inplace=False)\n", + " (classifier_1): PytorchQuantizationWrapper(\n", + " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", + " )\n", + " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", + "), 'quantization_info': })])\n", + "{: np.float64(64.0), : np.float64(128.0)}\n" + ] + } + ], "source": [ "# layer 4 is the first activation layer - Conv1_relu\n", "optimal_thresholds_relu = {\n", @@ -473,9 +1428,11 @@ "\n", "print(optimal_thresholds_relu)\n", "\n", + "print(quantized_models_dict.items())\n", + "\n", "# layer 9 is the batch normalisation projection layer - Expanded_conv_project_BN\n", "optimal_thresholds_project = {\n", - " error_method: data[\"quantized_model\"].features_2_conv_2_bn_activation_holder_quantizer.activation_holder_quantizer.threshold_np\n", + " error_method: data[\"quantized_model\"].features_1_conv_1_bn_activation_holder_quantizer.activation_holder_quantizer.threshold_np\n", " for error_method, data in quantized_models_dict.items()\n", "}\n", "\n", @@ -497,12 +1454,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "VPb8tBNGpJjo", "metadata": { "id": "VPb8tBNGpJjo" }, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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unjxom09V5+TOnXqqEyZMvrnn39Uu3ZtZcuWTUWKFNGCBQskSevXr1eVKlXk6+ur4sWLa/Xq1XZ5jx49qrfeekvFixeXr6+vcuXKpZYtW7r9RXf06NG6evWqvvjiC7sCSJJy586tadOmKT4+XqNHj7YtT21MYkqv45kzZ+rpp59WSEiIvL29VapUKdtr4HbOvDdERUWpZcuWkqSnnnrKdryTj++dY3Pu1mUs+T7OHE9X9ytJZ86cUefOnZUnTx75+PiofPnymjVrlt06t4+Nced17Y577Sf5veDgwYNq1KiRAgIC1LZtW0mS1WrV+PHjVbp0afn4+ChPnjzq0qWL3d+DJG3ZskUNGjRQ7ty55evrq4IFC6pTp05u5ZGktWvXqmbNmvLz81P27NnVrFkz7d69+56P1TAMDR06VPnz51e2bNn01FNPaefOnQ7r3bhxQ0OGDFHRokXl4+OjXLlyqUaNGlq1atU993Evd44JSv4b2bVrl15++WXlyJFDNWrUkHR/n02uKFmypHLnzq2DBw/aLU9ISNCgQYNUpEgReXt7Kzw8XO+++67D5/idnP38uh8XLlxQ3759VbZsWfn7+yswMFANGzbU33//bVsnPj5efn5+6tGjh8P9T5w4IU9PT40YMcK27NKlS+rZs6fCw8Pl7e2tIkWKaNSoUXa9bG7/Gx0/frzttfqodRNOT2gJyuDmzJmj559/Xl5eXmrTpo2mTJmizZs32zWXx8fHq2bNmtq9e7c6deqkxx57TOfOndOPP/6oEydOqGTJkvroo480cOBAvfHGG6pZs6aklLs2ZM2aVS1atNDChQs1bdo0u7MTixcvVkJCgq1/clxcnL788ku1adNGr7/+ui5fvqwZM2aoQYMG+vPPP1WhQgUFBwdrypQpevPNN9WiRQs9//zzkqRy5cql+phfe+01zZo1Sy+++KL69OmjP/74QyNGjNDu3bu1aNEiu3UPHDigF198UZ07d1b79u311VdfqUOHDqpUqZJKly7t9nEvUKCAateurZ9//llxcXEKDAxMcb0XXnhBO3fu1Ntvv63IyEidOXNGq1at0rFjxxQZGanx48fr7bfflr+/v95//31JUp48eey28dZbbyk4OFgDBw7UlStX7ppr//79atWqlbp27ar27dtr5syZatmypZYvX6569eq59BidyXa7qKgodezYUY8//rhGjBih06dPa8KECdq4caP++usvZc+e3bZuUlKSGjRooCpVqujTTz/V6tWrNWbMGBUuXFhvvvlmqvuoWbOmxo0bp507d6pMmTKSpF9++UUeHh765Zdf9M4779iWSbe6iKWkS5cuOnnypFatWqX//ve/Ka7zzTff6PLly+rSpYssFos++eQTPf/88zp06NBdW0d/+ukn+fj46KWXXkrx9oIFC6pGjRpau3atrl27Jl9fX9tt3bt3V/bs2TV48GDt3btXU6ZM0dGjR20FjqvPiXSrZapJkyZq3bq1WrZsqSlTpqh169aaM2eOevbsqa5du+rll1/W6NGj9eKLL+r48eMKCAiQJG3evFm//fabWrdurfz58+vIkSOaMmWK6tSpo127dilbtmx33XdKxyYyMtL2HnOnWrVqKTIyUj/99JMmT57s0rYlacqUKSpdurSee+45ZcmSRT/99JPeeustWa1WdevWzW7de7031KpVS++8844+++wzvffeeypZsqQk2f69051dxiRp3Lhx2r59u3LlyiXJuePp6n6vXbumOnXq6MCBA+revbsKFiyo+fPnq0OHDrp06ZLDlzR3X9eucnY/N2/eVIMGDVSjRg19+umnttdUly5dbO8p77zzjg4fPqxJkybpr7/+0saNG5U1a1adOXNG9evXV3BwsPr376/s2bPryJEjDid+nM2zevVqNWzYUIUKFdLgwYN17do1TZw4UdWrV9e2bdvuOkHQwIEDNXToUDVq1EiNGjXStm3bVL9+fSUmJtqtN3jwYI0YMUKvvfaannjiCcXFxWnLli3atm2bU+/Rly9fdhjPkjNnTnl4pH7Ou2XLlipatKiGDx9uO4GUFp9NzoiNjdXFixftTghZrVY999xz+vXXX/XGG2+oZMmS+vfffzVu3Djt27dPixcvdnk/aenQoUNavHixWrZsqYIFC+r06dOaNm2aateurV27dilfvnzy9/dXixYtNG/ePI0dO9auZ8a3334rwzBsBf3Vq1dVu3ZtRUdHq0uXLipQoIB+++03DRgwQDExMRo/frzd/mfOnKnr16/rjTfekLe3t3LmzPkwH37GYiDD2rJliyHJWLVqlWEYhmG1Wo38+fMbPXr0sFtv4MCBhiRj4cKFDtuwWq2GYRjG5s2bDUnGzJkzHdZp3769ERERYbu+YsUKQ5Lx008/2a3XqFEj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UVRUlMM6n3/+uSIjI+Xj46MqVarozz//dCkbAPPtrfW61j3WS3trvW52FJdQBAFItw4dOqTKlStr//79+vbbb3XgwAFNnTpVa9asUdWqVXXhwgWzI0qSvLy8lDdvXlkslge6n+DgYGXLli3Nt/vss88qJibG7vLtt9+muv6NGzccliUmJrq179vvFx4errlz5+ratWu2ZdevX9c333yjAgUKuLX9R01YWJhmz55tt2zWrFkKCwuzW7ZmzRq1atVKL7zwgv78809t3bpVw4YNczj2gYGBDs/d0aNHXco0atQoTZkyRZMmTdLu3bs1atQoffLJJ5o4cWKq9zl8+LAaN26sp556Stu3b1fPnj312muvacWKFbZ15s2bp969e2vQoEHatm2bypcvrwYNGujMmTMu5QNgrjo/D1KdrWNV5+dBZkdxCUUQgHSrW7du8vLy0sqVK1W7dm0VKFBADRs21OrVqxUdHa3333/ftq7FYtHixYvt7p89e3a7s9P/+c9/VKxYMWXLlk2FChXShx9+aPelMrlL03//+19FRkYqKChIrVu31uXLlyXdaq1Yv369JkyYYDvrfuTIEYdWhzp16qR4hj75LP/YsWNVtmxZ+fn5KTw8XG+99Zbi4+Ml3Tq73rFjR8XGxtruN3jwYEmO3eGOHTumZs2ayd/fX4GBgXrppZd0+vRppx9PMm9vb+XNm9fukiNHDrtjO2XKFD333HPy8/PTsGHDbNv+8ssvVbBgQVvrg7OZ7ryfJD322GMKDw/XwoULbcsWLlyoAgUKqGLFinaZrVarRowYYWu9KF++vBYsWCDpVqvLU089JUnKkSOHQyuS1WrVu+++q5w5cypv3ry24+vscZWkkSNHKk+ePAoICFDnzp11/fp1OaN9+/aaOXOm3bLk39K73U8//aTq1aurX79+Kl68uIoVK6bmzZvr888/t1vPYrE4PHd58uRxKkuy3377Tc2aNVPjxo0VGRmpF198UfXr179rq83UqVNVsGBBjRkzRiVLllT37t314osvaty4cbZ1xo4dq9dff10dO3ZUqVKlNHXqVGXLlk1fffWVS/kAwB0UQQBSNXaslD//vS/PPed43+eec+6+Y8e6l+3ChQtasWKF3nrrLfn6+trdljdvXrVt21bz5s1zaQr+gIAARUVFadeuXZowYYKmT59u96VNkg4ePKjFixdr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IiMjlS1bNl25csWlfd3L1atX5eFh/3XB09PzroVh1apVtWbNGrtlq1atsnVP9PLyUqVKlezWsVqttpkdAaQflzxy6qxy65JHznuv/AihJQhAujVp0iRVq1ZNDRo00NChQ1WwYEHt3LlT/fr1U7FixTRw4EDbuk8//bQmTZqkqlWrKikpSf/5z3/szvgXLVpUx44d09y5c/X4449r6dKlWrRokcuZIiMj9ccff+jIkSPy9/d36DYmSS+88IKyZcumwYMH69SpU7blwcHBKlKkiG7cuKGJEyeqadOm2rhxo6ZOneqwj/j4eK1Zs0bly5dXtmzZHFqI6tatq7Jly6pt27YaP368bt68qbfeeku1a9dOseva3SQkJNjllKQsWbI4dEO7l7TK5Onpqd27d9v+f6eAgAD17dtXvXr1ktVqVY0aNRQbG6uNGzcqMDBQ7du3V0REhCwWi5YsWaJGjRrJ19fXYeyMu4+hR48e6tChgypXrqzq1atrzpw52rlzpwoVKmTbzqJFizRgwIBUx7+8/vrratmyZYq/ByXdmoji6tWratSokSIiInTp0iV99tlnunHjhl03QMMwHJ476dZYKA8Pj3vmkKSmTZtq2LBhKlCggEqXLq2//vpLY8eOtSvQBgwYoOjoaNv03l27dtWkSZP07rvvqlOnTlq7dq2+++47LV261Haf3r17q3379qpcubKeeOIJjR8/XleuXElxogsAj656ef5RdLQUlkc6YXYYF9ASBCDdKlq0qDZv3qxChQrppZdeUkREhBo2bKhixYpp48aNdl9qx4wZo/DwcNWsWVMvv/yy+vbta1c4PPfcc+rVq5e6d++uChUq6LffftOHH37ocqa+ffvK09NTpUqVUnBwsI4dO+awzoYNG7Rjxw5FREQoNDTUdjl+/LjKly+vsWPHatSoUSpTpozmzJmjESNG2N2/WrVq6tq1q1q1aqXg4GB98sknDvuwWCz64YcflCNHDtWqVUt169ZVoUKFNG/ePJcf0/Lly+1yhoaGqkaNGi5vJy0zBQYGOozXut3HH3+sDz/8UCNGjFDJkiX17LPPaunSpSpYsKCkW7/HM2TIEPXv31958uSxda9Li8fQqlUrffjhh3r33XdVqVIlHT16VG+++abddmJjY7V3795U95NcZGbJkvK5ytq1a+vQoUN69dVXVaJECTVs2FCnTp3S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//nPf4zixYvbrr/00ktG48aN7e5XpUoVo0uXLqluN72+DwNhYYYh3frXbK7UBrQEAUjd2LFS/vy3LuvW2d92+PD/3fb22473fe65/7v9TlFR/3fbwoVuRbtw4YJWrFiht956S76+vna35c2bV23bttW8efNkGIbT2wwICFBUVJR27dqlCRMmaPr06Ro3bpzdOgcPHtTixYu1ZMkSLVmyROvXr7d14ZkwYYKqVq2q119/3dZiEh4e7rCfhQsX2rWqPP/88ypevLjy5MkjSfLw8NBnn32mnTt3atasWVq7dq3effddSbe61o0fP952Jj4mJkZ9+/Z12IfValWzZs104cIFrV+/XqtWrdKhQ4fUqlUrpx+PKwYPHqwWLVro33//VadOnSRJBw4c0Pfff6+FCxdq+/btTme6836369Spk2bOnGm7/tVXX6ljx44OeUaMGKHZs2dr6tSp2rlzp3r16qV27dpp/fr1Cg8P1/fffy9J2rt3r2JiYjRhwgTbfWfNmiU/Pz/98ccf+uSTT/TRRx9p1apVTh/X7777ToMHD9bw4cO1ZcsWhYaGavLkyU4dxzZt2qhIkSL66KOPUrx9+/bt2rVrl3r37i0PD/uP8PLly6tu3bq2VroVK1bo3LlzttfOnVLqUukuX19fWa1W3bx5M9Xbpbu3CHp6emr48OGaOHGiTpw4keI63377rYoXL67nnnsuxdtvb21N7vJ45MiRVPdZrVo1bd26VX/++aekW63L//vf/9SoUaNU77Np0ybVqlVLXl5etmUNGjTQ3r17ba1YmzZtUt26de3u16BBA23atCnV7QJ4uJgdDkDq4uKk6Ohb/09IsL8tKen/bkup+8rZs/93+52uXPm/21IYw+CM/fv3yzAMlSxZMsXbS5YsqYsXL+rs2bMKCQlxapsffPCB7f+RkZHq27ev5s6da/cl0mq1KioqSgEBAZKkV155RWvWrNGwYcMUFBQkLy8vZcuWTXnz5k11Pzlz5rT9f9y4cVq7dq3++OMP2xfF28cURUZGaujQoeratasmT54sLy8vBQUFyWKx3HUfa9as0b///qvDhw/bCrHZs2erdOnS2rx5sx5//PF7Pp5kS5Yskb+/v93233vvPbsuQy+//LJDMZKYmKjZs2crODhYkrRq1SqnMt15v9u1a9dOAwYM0NGjRyVJGzdu1Ny5c7XutiI9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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -524,12 +1492,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "Df7eKzh4oj5X", "metadata": { "id": "Df7eKzh4oj5X" }, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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hETdlDoMIiIiInoPmDgRERERERGpwMSJiIiIiIhIBSZOREREOq56dcDKKueZiKjEa9cO+OijnGcdwsEhiIiIdFxKCvDyZc4zEVGJd/nym/s46RDWOBERERERUfExMnrz0CGscSIiIiIiouJz967UEWiFNU5EREREREQqMHEiIiIiIiJSgYkTERERERGRCuzjRERERERExefHH3OGAbWwAIYPlzoatTFxIiIiIiKi4jN37pvhyHUocWJTPSIiIiIiIhVY40RERKTjwsKA168BU1OpIyEiUoOOXrSYOBEREem4jh2ljoCISAM6etFiUz0iIiIiIiIVmDgRERERERGpwKZ6REREOu7sWSAjAzAyAjw9pY6GiEiF//4D5HJATw+wtZU6GrUxcSIiItJxXbq8Gdn34UOpoyEiUqFePZ28aLGpHhERERERkQqscSIiIiIiouLTpk1Ocz0daqYHMHEiIiIiIqLitG6d1BFohU31iIiIiIiIVGDiREREREREpAITJyIiIiIiIhXYx4mIiIiIiIpP//7As2eAnR0QESF1NGpj4kRERERERMXn0KE393HSIWyqR0REREREpAJrnIiIiHTctWuAIAAymdSREBGpQUcvWkyciIiIdJylpdQREBFpQEcvWmyqR0REREREpAITJyIiIiIiIhXYVI8oj5Com1KHQESkseBgIDkZsLICAgKkjoaISIXISCA1FTAzA7p3lzoatTFxIiIi0nHBwW9G9mXiREQl3rhxby5aOpQ4sakeERERERGRCqxxIiIiIiKi4jN/PvDqFWBuLnUkGmHiRAT2bSIiIiIqNoMHSx2BVthUj4iIiIiISAUmTkRERERERCowcSIiIiIiIlKBiRMRERERERWfChUAmSznWYcwcSIiIiIiIlKBo+oRERHpuI8+AlxcAHt7qSMhIlKDjl60JK9xWrFiBVxdXWFiYoLGjRvj1KlThZZ/8eIFRo8eDWdnZxgbG8PDwwN//PFHMUVLRERU8uzcCRw/nvNMRFTi6ehFS9Iapy1btiAgIABhYWFo3LgxQkND4evrixs3bsDBwUGpfEZGBtq0aQMHBwds27YN5cuXx/3792FjY1P8wRMRERER0QdD0sQpODgYw4YNw5AhQwAAYWFh2LNnD9auXYtp06YplV+7di0SExNx7NgxGBoaAgBcXV2LM2QiIiIiIvoASdZU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C5iamkIQBA5BTkREREREBfv/ihddo1Ufp//++w+ffvopPDw80L59e8TFxQEAhg4dyqHIiYiIiIioYMbGbx46RKvEaeLEiTA0NERsbCzMzMzE6b1798bevXuLLDgiIiIiIqKSQKumevv27cNff/2FChUqKEyvWrUq7t+/XySBEZV2IVE3xb8ntvGQMBIiIiIiUkWrxOnVq1cKNU25EhMTYaxjVW5ERERERFSMNm4EUlMBMzOgXz+po1GbVk31mjdvjg0bNoivZTIZ5HI5vv32W7Ru3brIgiMiIiIiolJm6lRg2LCcZx2iVY3Tt99+i08//RRnzpxBRkYGpk6diitXriAxMRFHjx4t6hiJiIiIiIgkpVXiVLt2bdy8eRPLly+HpaUlUlJS0L17d4wePRrOzs5FHSMREREV4ttv37R6ISIq8XT0oqVx4pSZmYl27dohLCwM06dPfx8xERERkQZ0qIsAEZHOXrQ07uNkaGiIS5cuvY9YiIiIiIiISiStBocYMGAAfv7556KOhYiIiIiIqETSqo9TVlYW1q5di/3798PT0xPm5uYK84ODg4skOCIiIlLtxg0gKwswMACqVZM6GiIiFdLT3/ytQ7cy0ihxunv3LlxdXXH58mV89NFHAICbN28qlJHJZEUXHREREan06afAo0dA+fLAw4dSR0NEpEKVKjp50dIocapatSri4uLw999/AwB69+6N77//Ho6Oju8lOCIiIiIiopJAo8RJEASF13/++SdevXpVpAEREREREVEp5u0NPH0K2NtLHYlGtOrjlOvtRIqIiIiIiKhQW7dKHYFWNBpVTyaTKfVhYp8mIiIiIiIq7TRuqjd48GAY///oF2lpaRg5cqTSqHqRkZFFFyEREREREZHENEqc/Pz8FF4PGDCgSIMhIiIiIiIqiTRKnNatW/e+4iAiIiIiog/BiBFAYiJQtiywerXU0ajtnQaHICIiIiIi0siePW/u46RDNBocgoiIiIiI6EPEGiciIiIdd/o0kJ0N6OtLHQkRkRp09KLFxImIiEjHOTtLHQERkQZ09KLFxImoBAiJuil1CERERERUCCZO9MFhkkJEREREmioRg0OsWLECrq6uMDExQePGjXHq1Cm1ltu8eTNkMhm6du36fgMkIiIqwX78EQgOznkmIirx9u0Ddu7MedYhktc4bdmyBQEBAQgLC0Pjxo0RGhoKX19f3LhxAw4ODgUuFxMTg8mTJ6N58+bFGC0REVHJM3fum5F9hw+XOhoiIhX8/d9ctB4+lDoatUle4xQcHIxhw4ZhyJAhqFmzJsLCwmBmZoa1a9cWuEx2djb69++POXPmoHLlysUYLRERERERfYgkrXHKyMjA2bNnERgYKE7T09ODj48Pjh8/XuByc+fOhYODA4YOHYrDhw8Xuo309HSkp6eLr5OTk989cCIiIiIi0s60acDLl4ClpdSRaETSxOnZs2fIzs6Go6OjwnRHR0dcv34932WOHDmCn3/+GRcuXFBrG0FBQZgzZ867hkpEREREREVhzBipI9CK5E31NPHy5UsMHDgQa9asgZ2dnVrLBAYGIikpSXw8ePDgPUdJRERERESljaQ1TnZ2dtDX10dCQoLC9ISEBDg5OSmVv3PnDmJiYtCpUydxmlwuBwAYGBjgxo0bqFKlisIyxsbGMDY2fg/RExERERHRh0LSGicjIyN4enoiOjpanCaXyxEdHQ1vb2+l8tWrV8e///6LCxcuiI/OnTujdevWuHDhAlxcXIozfCKVQqJu8r5RRERERKWA5MORBwQEwM/PD15eXmjUqBFCQ0Px6tUrDBkyBAAwaNAglC9fHkFBQTAxMUHt2rUVlrexsQEApelERERERFQCVa8OPH4MlCsHFDCuQUkkeeLUu3dvPH36FDNnzkR8fDzq16+PvXv3igNGxMbGQk9Pp7piERERERFRQVJSckbVS0mROhKNyARBEKQOojglJyfD2toaSUlJsLKykjockoAUTecmtvHQevt5lyUiys8nnwAJCYCjI3DggNTREBGpUIIuWprkBpLXOBEREdG7YbJERDpFRy9abANHRERERESkAhMnIiIiIiIiFZg4ERERERERqcA+TkRERDquf3/g2TPAzg6IiJA6GiIiFaZPB168AGxsgAULpI5GbUyciIiIdNyhQ8CjR0D58lJHQkSkhvXr31y0dChxYlM9IiIiIiIiFVjjRERERERExefPP4HMTMDQUOpINMLEiYiIiIiIik+dOlJHoBU21SMiIiIiIlKBiRMREREREZEKbKpHRERERETF5+xZICMDMDICPD2ljkZtTJyIikFI1E2pQyAiIiIqGbp0eTMc+cOHUkejNjbVIyIiIiIiUoE1TkRERDpu2DAgKQmwtpY6EiIiNejoRYuJExERkY6bNUvqCIiINKCjFy021SMiIiIiIlKBiRMREREREZEKbKpHOinvKHUT23hIGAkRERERfQhY40RERKTjKlQAZLKcZyKiEq9ZM8DdPedZh7DGiYiIiIiIik9MTM59nNLSpI5EI0yciIiIiIio+JQtC6Sn5zzrECZORERERERUfC5dkjoCrbCPExERERERkQqscSKdknc0PSIiIiKi4sLEiT4YTLqIiIiISFtMnIiIiIiIqPgEBwPJyYCVFRAQIHU0amPiRFTC5daU8Ua/REREVCoEB+cMR16+vE4lThwcgoiIiIiISAXWOBEREem4X37JuSWKsbHUkRARqUFHL1pMnIiIiHRcq1ZSR0BEpAEdvWixqR4REREREZEKTJyIiIiIiIhUYFM9IiIiHXfw4JvuAjraAoaIPiT37gHZ2YC+PuDmJnU0amPiREREpOMGDHgzsu/Dh1JHQ0SkQvPmOnnRYlM9IiIiIiIiFVjjRERERERExadbN+D5c6BMGakj0QgTJyIiIiIiKj4//CB1BFphUz0iIiIiIiIVmDhRiRMSdRMhUTelDoOIiIiISMTEiYiIiIiISAX2cSIiIiIiouLTuTPw9Clgbw/s3Cl1NGpj4kRERERERMXn3Lk393HSIWyqR0REREREpAJrnIiIiHTcw4dSR0BEpAEdvWgxcaJSjaPzEREREVFRYFM9IiIiIiIiFZg4ERERERERqcCmekRERDpuzhwgKQmwtgZmzZI6GiIiFcLDgVevAHNzYPBgqaNRGxMnIiIiHbdmzZuRfZk4EVGJ9803by5aOpQ4sakeERERERGRCqxxIiIiIiKi4vP990BqKmBmJnUkGmHiRERERERExad7d6kj0Aqb6hEREREREanAxImIiIiIiEgFNtUjnRcSdRMAMLGNh8SREBEREZFKL18CggDIZIClpdTRqI2JE5VKuckUEREREZUwNWq8GY784UOpo1Ebm+oRERERERGpwBonIiIiHdeyJfDsGWBnJ3UkRERq0NGLFhMnIiIiHRcRIXUEREQa0NGLFhMnKjXYr4mIiIiI3hf2cSIiIiIiIlKBiRMREREREZEKbKpHRESk4z75BEhIABwdgQMHpI6GiEiFIUOA//4DbG2BdeukjkZtTJyIiIh03M2bObdESUqSOhIiIjVERb25j5MOYVM9IiIiIiIiFVjjRERERERExefiRUAuB/R0qw6HiRMRERERERUfW1upI9AKEyfSCbxHExERERFJiYkTlVhMloiIiIiopGDiRERERERExWf3buD1a8DUFOjYUepo1MbEiYiIiIiIis/IkW+GI3/4UOpo1KZbQ1kQERERERFJgDVOREREOm7mTCAlBbCwkDoSIiI16OhFi4kTERGRjhs+XOoIiIg0oKMXLTbVIyIiIiIiUoE1TkSkJO9Q8BPbeEgYCREREVHJwMSJSEcwmSGigsTFAdnZgL4+4OwsdTRERKUTm+oRERHpuIYNAReXnGciohKvcmXA2DjnWYcwcSIiIiIiouKTkfHmoUNKROK0YsUKuLq6wsTEBI0bN8apU6cKLLtmzRo0b94cZcqUQZkyZeDj41NoeSIiIiIiKkFq1wYaNMh51iGSJ05btmxBQEAAZs2ahXPnzqFevXrw9fXFkydP8i1/8OBB9O3bF3///TeOHz8OFxcXtG3bFo8ePSrmyImIiIiISGN79wLnzuU86xDJE6fg4GAMGzYMQ4YMQc2aNREWFgYzMzOsXbs23/IRERH48ssvUb9+fVSvXh0//fQT5HI5oqOjizlyIiIiIiL6UEiaOGVkZODs2bPw8fERp+np6cHHxwfHjx9Xax2pqanIzMxE2bJl852fnp6O5ORkhQcREREREZEmJE2cnj17huzsbDg6OipMd3R0RHx8vFrr+Oqrr1CuXDmF5CuvoKAgWFtbiw8XF5d3jpuIiIiIiD4skjfVexeLFi3C5s2bsX37dpiYmORbJjAwEElJSeLjwYMHxRwlERERERGJpkwBvvgi51mHSHoDXDs7O+jr6yMhIUFhekJCApycnApd9rvvvsOiRYuwf/9+1K1bt8ByxsbGMDY2LpJ4iYiIiIjoHW3aBDx6BJQvDyxZInU0apO0xsnIyAienp4KAzvkDvTg7e1d4HLffvst5s2bh71798LLy6s4QiUiIiIiog+YpDVOABAQEAA/Pz94eXmhUaNGCA0NxatXrzBkyBAAwKBBg1C+fHkEBQUBABYvXoyZM2di48aNcHV1FftCWVhYwMLCQrL9ICIikkp0NJCVBRhI/l+diEgNOnrRkjza3r174+nTp5g5cybi4+NRv3597N27VxwwIjY2Fnp6byrGVq1ahYyMDHz++ecK65k1axZmz55dnKFTEQqJuil1CEREOqtaNakjICLSgI5etCRPnABgzJgxGDNmTL7zDh48qPA6Jibm/QdERERERESUh06PqkdERERERFQcSkSNExEREWlv40YgNRUwMwP69ZM6GiIiFY4fB9LTAWNjoJAB4UoaJk5EREQ6burUNyP7MnEiohKvZ883F62HD6WORm1sqkdERERERKQCa5yIiIiIiKj4jB4NJCcDVlZSR6IRJk5ERERERFR8AgOljkArbKpHRERERESkAhMnIiIiIiIiFZg4ERERERERqcDEiYiIiIiIio+XF1ChQs6zDuHgEEREREREVHzi43Pu46RjmDgRERHpOCcnxWciohJNRy9aTJyIiIh03JkzUkdARKQBHb1osY8TERERERGRCqxxIiJRSNRNqUMgIiIiKpFY40RERERERKQCa5yIdFBuzdDENh4SR0JEJcGIEUBiIlC2LLB6tdTREBGpEBQEJCcDVlZAYKDU0aiNiRMREZGO27MnZ2Tf8uWljoSISA0rVry5aOlQ4sSmekRERERERCqwxomICsVmgURERFSktm4F0tMBY2OpI9EIEyciIiIiIio+3t5SR6AVNtUjIiIiIiJSgYkTERERERGRCmyqR0RERERExefGDSArCzAwAKpVkzoatTFxIiIiIiKi4vPpp2+GI3/4UOpo1MamekRERERERCqwxomIiEjH9e0LPH8OlCkjdSRERGrQ0YsWEyciIiIdt2SJ1BEQEWlARy9abKpHRERERESkAmucSFIhUTelDoGIiIiISCXWOBEREREREanAxImIiEjHVa8OWFnlPBMRlXjt2gEffZTzrEPYVI+IiEjHpaQAL1/mPBMRlXiXL7+5j5MOYY0TEREREREVHyOjNw8dwhonIiIiIiIqPnfvSh2BVpg4EemwvKMSTmzjIWEkRERERKUbm+oRERERERGpwMSJiIiIiIhIBTbVIyIiIiKi4vPjjznDgFpYAMOHSx2N2pg4ERERERFR8Zk7981w5DqUOLGpHhERERERkQqscSIiItJxYWHA69eAqanUkRARqUFHL1pMnIiIiHRcx45SR0BEpAEdvWgxcSL6wOW9FxQRERER5Y+JExU7flEnIiIiIl3DxImIiEjHnT0LZGQARkaAp6fU0RARqfDff4BcDujpAba2UkejNiZOREREOq5Llzcj+z58KHU0REQq1KunkxctDkdOVEqERN1kM0giIiKi94Q1TkRE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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -563,12 +1542,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "631780a79e2cedf0", "metadata": { "collapsed": false }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [05:47<00:00, 1.13it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -6.5173, Accuracy: 35494/50000 (71%)\n", + "\n", + "Float model's Top 1 accuracy on the Imagenet validation set: 70.99%\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "from tqdm import tqdm\n", "import torch.nn.functional as F\n", @@ -605,12 +1609,54 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [03:47<00:00, 1.72it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -6.0622, Accuracy: 35966/50000 (72%)\n", + "\n", + "Results for QuantizationErrorMethod.MSE: Loss = -6.06215625, Accuracy = 0.71932\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 391/391 [05:44<00:00, 1.14it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Test set: Average loss: -6.2207, Accuracy: 35962/50000 (72%)\n", + "\n", + "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2207425, Accuracy = 0.71924\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], "source": [ "evaluation_results = {}\n", "\n", @@ -649,12 +1695,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "qml4LLmWZLP4", "metadata": { "id": "qml4LLmWZLP4" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Layer indices potentially using ReLU: [5, 9, 16, 20, 27, 31, 39, 43, 50, 54, 62, 66, 74, 78, 85, 89, 97, 101, 109, 113, 121, 125, 132, 136, 144, 148, 156, 160, 167, 171, 179, 183, 191, 195, 202]\n", + "Number of relu layers 35\n" + ] + } + ], "source": [ "import torch.nn as nn\n", "\n", @@ -707,12 +1762,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", "metadata": { "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + ")\n", + "PytorchQuantizationWrapper(\n", + " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", + ")\n" + ] + } + ], "source": [ "for error_method, data in quantized_models_dict.items():\n", " quantized_model = data[\"quantized_model\"]\n", From d5481a05bbb0469b6060282cce1f7b0cb2035a9b Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Tue, 4 Feb 2025 15:34:17 +0900 Subject: [PATCH 08/30] PR comment correction(fixed) --- ..._pytorch_activation_threshold_search.ipynb | 1116 +---------------- 1 file changed, 24 insertions(+), 1092 deletions(-) diff --git a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb index 5585ced37..7720601ff 100644 --- a/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb +++ b/tutorials/notebooks/mct_features_notebooks/pytorch/example_pytorch_activation_threshold_search.ipynb @@ -66,23 +66,13 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "metadata": { "id": "324685b9-5dcc-4d22-80f4-dec9a93d3324", "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.0\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" - ] - } - ], + "outputs": [], "source": [ "!pip install -q torch torchvision" ] @@ -309,25 +299,12 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "ba0c6e55-d474-4dc3-9a43-44b736635998", "metadata": { "id": "ba0c6e55-d474-4dc3-9a43-44b736635998" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", - "Statistics Collection: 10it [00:10, 1.00s/it]\n", - "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.98it/s]\n", - "WARNING:Model Compression Toolkit:DepthwiseConv2D is not in model.\n", - "Statistics Collection: 10it [00:10, 1.00s/it]\n", - "Calculating quantization parameters: 100%|██████████| 102/102 [00:17<00:00, 5.99it/s]\n" - ] - } - ], + "outputs": [], "source": [ "quantized_models_dict = {}\n", "\n", @@ -368,406 +345,16 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac", "metadata": { "id": "a22e6d68-c40f-40bf-ab74-ff453011aeac" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "---------------------------\n", - "MobileNetV2(\n", - " (features): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (2): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (3): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (4): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)\n", - " (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (5): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (6): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (7): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)\n", - " (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (8): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (9): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (10): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (11): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)\n", - " (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (12): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (13): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (14): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)\n", - " (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (15): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (16): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (17): InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)\n", - " (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - " )\n", - " (18): Conv2dNormActivation(\n", - " (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " )\n", - " (classifier): Sequential(\n", - " (0): Dropout(p=0.2, inplace=False)\n", - " (1): Linear(in_features=1280, out_features=1000, bias=True)\n", - " )\n", - ")\n", - "---------------------------\n", - "# 2 features.0 Conv2dNormActivation(\n", - " (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - ")\n", - "# 3 features.0.0 Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)\n", - "# 4 features.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - "# 5 features.0.2 ReLU6(inplace=True)\n", - "# 6 features.1 InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - ")\n", - "# 7 features.1.conv Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - ")\n", - "# 8 features.1.conv.0 Conv2dNormActivation(\n", - " (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - " (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - ")\n", - "# 9 features.1.conv.0.0 Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)\n", - "# 10 features.1.conv.0.1 BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - "# 11 features.1.conv.0.2 ReLU6(inplace=True)\n", - "# 12 features.1.conv.1 Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - "# 13 features.1.conv.2 BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - "# 14 features.2 InvertedResidual(\n", - " (conv): Sequential(\n", - " (0): Conv2dNormActivation(\n", - " (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (1): Conv2dNormActivation(\n", - " (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)\n", - " (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " (2): ReLU6(inplace=True)\n", - " )\n", - " (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)\n", - " (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", - " )\n", - ")\n", - "Name: features.0.0, Type: Conv2d\n", - "Name: features.0.1, Type: BatchNorm2d\n", - "Name: features.0.2, Type: ReLU6\n", - "Name: features.1.conv.0.0, Type: Conv2d\n", - "Name: features.1.conv.0.1, Type: BatchNorm2d\n", - "Name: features.1.conv.0.2, Type: ReLU6\n", - "Name: features.1.conv.1, Type: Conv2d\n", - "Name: features.1.conv.2, Type: BatchNorm2d\n", - "Name: features.2.conv.0.0, Type: Conv2d\n", - "Name: features.2.conv.0.1, Type: BatchNorm2d\n" - ] - } - ], + "outputs": [], "source": [ - "import torch\n", - "import torch.nn as nn\n", - "def get_leaf_layer_info(module, parent_name=''):\n", - " leaf_layer_info = []\n", - " \n", - " # Iterate through all child modules with their names\n", - " for name, child in module.named_children():\n", - " # Construct the full name of the child layer\n", - " full_name = f\"{parent_name}.{name}\" if parent_name else name\n", - " \n", - " # Recurse into the child module\n", - " leaf_layer_info.extend(get_leaf_layer_info(child, full_name))\n", - " \n", - " # If it has no children, it's a leaf layer; add its name and type\n", - " if not isinstance(module, nn.Sequential) and not isinstance(module, nn.ModuleList) and not list(module.children()):\n", - " leaf_layer_info.append((parent_name, type(module).__name__))\n", - "\n", - " return leaf_layer_info\n", - "\n", - "print('---------------------------')\n", - "print(float_model)\n", - "print('---------------------------')\n", - "\n", - "leaf_layer_info = get_leaf_layer_info(float_model)\n", - "for index, (name, layer) in enumerate(float_model.named_modules()):\n", - " if index <= 1: continue\n", + "for index, (name, _) in enumerate(float_model.named_modules()):\n", " if index < 15:\n", - " print(\"#\", index, name, layer)\n", - " else:\n", - " break\n", - "\n", - "for index, (layer_name, layer_type) in enumerate(leaf_layer_info):\n", - " #print(f\"Layer Name: {layer_name}, Layer Type: {layer_type}\")\n", - " if index < 10:\n", - " print(f\"Name: {layer_name}, Type: {layer_type}\")\n", + " print(name)\n", " else:\n", " break" ] @@ -830,18 +417,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "4a5bf3dd", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "10it [00:00, 13.84it/s]\n" - ] - } - ], + "outputs": [], "source": [ "from tqdm import tqdm\n", "import numpy as np\n", @@ -879,546 +458,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "NGnjrPD_uTd5", "metadata": { "id": "NGnjrPD_uTd5" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{: np.float64(8.0), : np.float64(8.0)}\n", - "dict_items([(, {'quantization_config': QuantizationConfig(activation_error_method=, weights_error_method=, relu_bound_to_power_of_2=False, weights_bias_correction=True, weights_second_moment_correction=False, input_scaling=False, softmax_shift=False, shift_negative_activation_correction=True, activation_channel_equalization=False, z_threshold=inf, min_threshold=1.52587890625e-05, l_p_value=2, linear_collapsing=True, residual_collapsing=True, shift_negative_ratio=0.05, shift_negative_threshold_recalculation=False, shift_negative_params_search=False, concat_threshold_update=False, activation_bias_correction=False, activation_bias_correction_threshold=0.0, custom_tpc_opset_to_layer=None), 'quantized_model': PytorchModel(\n", - " (x): DummyPlaceHolder()\n", - " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " (features_0_2): ReLU6(inplace=True)\n", - " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", - " )\n", - " (features_1_conv_0_2): ReLU6(inplace=True)\n", - " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_0_2): ReLU6(inplace=True)\n", - " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", - " )\n", - " (features_2_conv_1_2): ReLU6(inplace=True)\n", - " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_0_2): ReLU6(inplace=True)\n", - " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", - " )\n", - " (features_3_conv_1_2): ReLU6(inplace=True)\n", - " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_0_2): ReLU6(inplace=True)\n", - " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", - " )\n", - " (features_4_conv_1_2): ReLU6(inplace=True)\n", - " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_0_2): ReLU6(inplace=True)\n", - " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_5_conv_1_2): ReLU6(inplace=True)\n", - " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_0_2): ReLU6(inplace=True)\n", - " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_6_conv_1_2): ReLU6(inplace=True)\n", - " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_0_2): ReLU6(inplace=True)\n", - " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", - " )\n", - " (features_7_conv_1_2): ReLU6(inplace=True)\n", - " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_0_2): ReLU6(inplace=True)\n", - " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_8_conv_1_2): ReLU6(inplace=True)\n", - " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_0_2): ReLU6(inplace=True)\n", - " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_9_conv_1_2): ReLU6(inplace=True)\n", - " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_0_2): ReLU6(inplace=True)\n", - " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_10_conv_1_2): ReLU6(inplace=True)\n", - " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_0_2): ReLU6(inplace=True)\n", - " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_11_conv_1_2): ReLU6(inplace=True)\n", - " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_0_2): ReLU6(inplace=True)\n", - " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_12_conv_1_2): ReLU6(inplace=True)\n", - " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_0_2): ReLU6(inplace=True)\n", - " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_13_conv_1_2): ReLU6(inplace=True)\n", - " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_0_2): ReLU6(inplace=True)\n", - " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", - " )\n", - " (features_14_conv_1_2): ReLU6(inplace=True)\n", - " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_0_2): ReLU6(inplace=True)\n", - " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_15_conv_1_2): ReLU6(inplace=True)\n", - " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_0_2): ReLU6(inplace=True)\n", - " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_16_conv_1_2): ReLU6(inplace=True)\n", - " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_0_2): ReLU6(inplace=True)\n", - " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_17_conv_1_2): ReLU6(inplace=True)\n", - " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_18_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_18_2): ReLU6(inplace=True)\n", - " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (classifier_0): Dropout(p=0.2, inplace=False)\n", - " (classifier_1): PytorchQuantizationWrapper(\n", - " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", - " )\n", - " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - "), 'quantization_info': }), (, {'quantization_config': QuantizationConfig(activation_error_method=, weights_error_method=, relu_bound_to_power_of_2=False, weights_bias_correction=True, weights_second_moment_correction=False, input_scaling=False, softmax_shift=False, shift_negative_activation_correction=True, activation_channel_equalization=False, z_threshold=inf, min_threshold=1.52587890625e-05, l_p_value=2, linear_collapsing=True, residual_collapsing=True, shift_negative_ratio=0.05, shift_negative_threshold_recalculation=False, shift_negative_params_search=False, concat_threshold_update=False, activation_bias_correction=False, activation_bias_correction_threshold=0.0, custom_tpc_opset_to_layer=None), 'quantized_model': PytorchModel(\n", - " (x): DummyPlaceHolder()\n", - " (x_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - " )\n", - " (features_0_2): ReLU6(inplace=True)\n", - " (features_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32)\n", - " )\n", - " (features_1_conv_0_2): ReLU6(inplace=True)\n", - " (features_1_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_1_conv_1_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_1_conv_1_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_0_2): ReLU6(inplace=True)\n", - " (features_2_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96)\n", - " )\n", - " (features_2_conv_1_2): ReLU6(inplace=True)\n", - " (features_2_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_2_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_2_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_0_2): ReLU6(inplace=True)\n", - " (features_3_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144)\n", - " )\n", - " (features_3_conv_1_2): ReLU6(inplace=True)\n", - " (features_3_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_3_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_3_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_0_2): ReLU6(inplace=True)\n", - " (features_4_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144)\n", - " )\n", - " (features_4_conv_1_2): ReLU6(inplace=True)\n", - " (features_4_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_4_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_4_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_0_2): ReLU6(inplace=True)\n", - " (features_5_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_5_conv_1_2): ReLU6(inplace=True)\n", - " (features_5_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_5_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_5_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_0_2): ReLU6(inplace=True)\n", - " (features_6_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192)\n", - " )\n", - " (features_6_conv_1_2): ReLU6(inplace=True)\n", - " (features_6_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_6_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_6_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_0_2): ReLU6(inplace=True)\n", - " (features_7_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192)\n", - " )\n", - " (features_7_conv_1_2): ReLU6(inplace=True)\n", - " (features_7_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_7_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_7_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_0_2): ReLU6(inplace=True)\n", - " (features_8_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_8_conv_1_2): ReLU6(inplace=True)\n", - " (features_8_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_8_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_8_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_3_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_0_2): ReLU6(inplace=True)\n", - " (features_9_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_9_conv_1_2): ReLU6(inplace=True)\n", - " (features_9_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_9_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_9_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_4_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_0_2): ReLU6(inplace=True)\n", - " (features_10_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_10_conv_1_2): ReLU6(inplace=True)\n", - " (features_10_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_10_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_10_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_5_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_0_2): ReLU6(inplace=True)\n", - " (features_11_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384)\n", - " )\n", - " (features_11_conv_1_2): ReLU6(inplace=True)\n", - " (features_11_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_11_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_11_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_0_2): ReLU6(inplace=True)\n", - " (features_12_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_12_conv_1_2): ReLU6(inplace=True)\n", - " (features_12_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_12_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_12_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_6_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_0_2): ReLU6(inplace=True)\n", - " (features_13_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576)\n", - " )\n", - " (features_13_conv_1_2): ReLU6(inplace=True)\n", - " (features_13_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_13_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_13_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_7_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_0_2): ReLU6(inplace=True)\n", - " (features_14_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576)\n", - " )\n", - " (features_14_conv_1_2): ReLU6(inplace=True)\n", - " (features_14_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_14_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_14_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_0_2): ReLU6(inplace=True)\n", - " (features_15_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_15_conv_1_2): ReLU6(inplace=True)\n", - " (features_15_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_15_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_15_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_8_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_0_2): ReLU6(inplace=True)\n", - " (features_16_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_16_conv_1_2): ReLU6(inplace=True)\n", - " (features_16_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_16_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_16_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (add_9_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_0_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_0_2): ReLU6(inplace=True)\n", - " (features_17_conv_0_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_1_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960)\n", - " )\n", - " (features_17_conv_1_2): ReLU6(inplace=True)\n", - " (features_17_conv_1_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_17_conv_2_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_17_conv_2_bn_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (features_18_0_bn): PytorchQuantizationWrapper(\n", - " (layer): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1))\n", - " )\n", - " (features_18_2): ReLU6(inplace=True)\n", - " (features_18_2_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (adaptive_avg_pool2d_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - " (classifier_0): Dropout(p=0.2, inplace=False)\n", - " (classifier_1): PytorchQuantizationWrapper(\n", - " (layer): Linear(in_features=1280, out_features=1000, bias=True)\n", - " )\n", - " (classifier_1_activation_holder_quantizer): PytorchActivationQuantizationHolder()\n", - "), 'quantization_info': })])\n", - "{: np.float64(64.0), : np.float64(128.0)}\n" - ] - } - ], + "outputs": [], "source": [ "# layer 4 is the first activation layer - Conv1_relu\n", "optimal_thresholds_relu = {\n", @@ -1428,8 +473,6 @@ "\n", "print(optimal_thresholds_relu)\n", "\n", - "print(quantized_models_dict.items())\n", - "\n", "# layer 9 is the batch normalisation projection layer - Expanded_conv_project_BN\n", "optimal_thresholds_project = {\n", " error_method: data[\"quantized_model\"].features_1_conv_1_bn_activation_holder_quantizer.activation_holder_quantizer.threshold_np\n", @@ -1454,23 +497,12 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "VPb8tBNGpJjo", "metadata": { "id": "VPb8tBNGpJjo" }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAIjCAYAAADFthA8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8ekN5oAAAACXBIWXMAAA9hAAAPYQGoP6dpAACLGElEQVR4nOzdd3hTdf//8Vda6KCL1UIppWXvJSiyURmyBFQEBGWpoKBsb3AwlCmyBBkiUrhFQRBQ4WYjoIjKEJW9VykbWspooTm/P/g1X0JaSELh0Pb5uK5ckJOTc145SZO8z2fEYhiGIQAAAADIJDzMDgAAAAAADxNFEAAAAIBMhSIIAAAAQKZCEQQAAAAgU6EIAgAAAJCpUAQBAAAAyFQoggAAAABkKhRBAAAAADIViiAAAAAAmQpFEO5bhw4dFBkZacq+Bw8eLIvFYsq+XXXkyBFZLBZFRUU98H1FRUXJYrHoyJEjtmWRkZFq0qTJA9+3JK1bt04Wi0Xr1q17KPu7X648N8nrfvrppw8+WBoy8zlJb6+HtFCnTh3VqVMn0+zXWY/a38+DyJPS+29qIiMj1aFDhzTb991YLBYNHjz4oezrYXmYxw8ZD0VQJjB58mRZLBZVqVLF7W2cPHlSgwcP1vbt29MumJOuXr2qwYMHP3JfoCwWi+2SJUsW5cyZU5UqVVKPHj20a9euNNvP5MmTH0rh5I5HOdv9+t///vdAvzAcO3ZMXbt2VWRkpLy9vRUSEqLmzZtr48aN97XdjPCc7Ny5U+3atVNYWJi8vb2VL18+tWvXLk3/rtLCrl27NHjwYKe+7GaE/aYmMjLS7v0wtUt6f10+ypJPNKR0ad269QPZp6vvNXfmCgwMVO3atbV06dIHks8dySdWz507Z3YUPARZzA6AB2/OnDmKjIzUn3/+qQMHDqhIkSIub+PkyZMaMmSIIiMjVaFCBbvbpk+fLqvVmkZpHV29elVDhgyRJIcznB988IH69+//wPZ9L/Xq1dOrr74qwzAUGxurv//+W7NmzdLkyZM1atQo9e7d27ZuRESErl27pqxZs7q0j8mTJyt37twune165ZVX1Lp1a3l7e7u0L1ellq1WrVq6du2avLy8Huj+00pKz83//vc/ff755w+kENq4caMaNWokSXrttddUqlQpnTp1SlFRUapZs6YmTJigt99+261tp/fnZOHChWrTpo1y5sypzp07q2DBgjpy5IhmzJihBQsWaN68eWrWrJnZMSXdKkaGDBmiOnXqOLSGr1y5MsPtNzXjx49XfHy87fr//vc/ffvttxo3bpxy585tW16tWrWHni2zeeedd/T444/bLUt+jVy7dk1ZsqTd1z53Pptu/8w8evSopkyZoqZNm2rZsmVq0KBBmmUDnEERlMEdPnxYv/32mxYuXKguXbpozpw5GjRoUJruw9Uv9WkpS5Ysafqm7qpixYqpXbt2dstGjhyppk2bqk+fPipRooTty67FYpGPj88DzXPlyhX5+fnJ09NTnp6eD3Rfd+Ph4fHAH2taehjPTbKLFy/qxRdflK+vrzZu3KjChQvbbuvdu7caNGignj17qlKlSmn6pTE9PCcHDx7UK6+8okKFCmnDhg0KDg623dajRw/VrFlT7dq10z///KOCBQuamPTezCo2zdhv8+bN7a6fOnVK3377rZo3b+5QpN1v61XyexxSVrNmTb344osp3ubM3/+DPr53fma+8MILKlWqlCZMmEAR5IabN2/KarU+8ie3HlV0h8vg5syZoxw5cqhx48Z68cUXNWfOnBTXu3Tpknr16mXrmpM/f369+uqrOnfunNatW2c7s9SxY0eHrg23jwm6ceOGcubMqY4dOzrsIy4uTj4+Purbt68kKTExUQMHDlSlSpUUFBQkPz8/1axZUz///LPtPkeOHLF9ERoyZIht38ln51MaE3Tz5k19/PHHKly4sLy9vRUZGan33ntPCQkJduslj5H59ddf9cQTT8jHx0eFChXS7NmzXTvId8iVK5fmzp2rLFmyaNiwYXaP5c4uIadOnVLHjh2VP39+eXt7KzQ0VM2aNbN9UYiMjNTOnTu1fv1622NPbg1L7ne+fv16vfXWWwoJCVH+/PntbkvpC8fKlStVoUIF+fj4qFSpUlq4cKHd7amNs7pzm3fLltoYkPnz56tSpUry9fVV7ty51a5dO0VHR9ut06FDB/n7+ys6OlrNmzeXv7+/goOD1bdvXyUlJd312Pfu3Vu5cuWSYRi2ZW+//bYsFos+++wz27LTp0/LYrFoypQpkhyfmw4dOujzzz+XZN+F405ffPGF7XX2+OOPa/PmzXfNJ0nTpk3TqVOnNHr0aLsCSJJ8fX01a9YsWSwWffTRR7blycd+w4YN6tKli3LlyqXAwEC9+uqrunjxom09V5+TOnXqqEyZMvrnn39Uu3ZtZcuWTUWKFNGCBQskSevXr1eVKlXk6+ur4sWLa/Xq1XZ5jx49qrfeekvFixeXr6+vcuXKpZYtW7r9RXf06NG6evWqvvjiC7sCSJJy586tadOmKT4+XqNHj7YtT21MYkqv45kzZ+rpp59WSEiIvL29VapUKdtr4HbOvDdERUWpZcuWkqSnnnrKdryTj++dY3Pu1mUs+T7OHE9X9ytJZ86cUefOnZUnTx75+PiofPnymjVrlt06t4+Nced17Y577Sf5veDgwYNq1KiRAgIC1LZtW0mS1WrV+PHjVbp0afn4+ChPnjzq0qWL3d+DJG3ZskUNGjRQ7ty55evrq4IFC6pTp05u5ZGktWvXqmbNmvLz81P27NnVrFkz7d69+56P1TAMDR06VPnz51e2bNn01FNPaefOnQ7r3bhxQ0OGDFHRokXl4+OjXLlyqUaNGlq1atU993Evd44JSv4b2bVrl15++WXlyJFDNWrUkHR/n02uKFmypHLnzq2DBw/aLU9ISNCgQYNUpEgReXt7Kzw8XO+++67D5/idnP38uh8XLlxQ3759VbZsWfn7+yswMFANGzbU33//bVsnPj5efn5+6tGjh8P9T5w4IU9PT40YMcK27NKlS+rZs6fCw8Pl7e2tIkWKaNSoUXa9bG7/Gx0/frzttfqodRNOT2gJyuDmzJmj559/Xl5eXmrTpo2mTJmizZs32zWXx8fHq2bNmtq9e7c6deqkxx57TOfOndOPP/6oEydOqGTJkvroo480cOBAvfHGG6pZs6aklLs2ZM2aVS1atNDChQs1bdo0u7MTixcvVkJCgq1/clxcnL788ku1adNGr7/+ui5fvqwZM2aoQYMG+vPPP1WhQgUFBwdrypQpevPNN9WiRQs9//zzkqRy5cql+phfe+01zZo1Sy+++KL69OmjP/74QyNGjNDu3bu1aNEiu3UPHDigF198UZ07d1b79u311VdfqUOHDqpUqZJKly7t9nEvUKCAateurZ9//llxcXEKDAxMcb0XXnhBO3fu1Ntvv63IyEidOXNGq1at0rFjxxQZGanx48fr7bfflr+/v95//31JUp48eey28dZbbyk4OFgDBw7UlStX7ppr//79atWqlbp27ar27dtr5syZatmypZYvX6569eq59BidyXa7qKgodezYUY8//rhGjBih06dPa8KECdq4caP++usvZc+e3bZuUlKSGjRooCpVqujTTz/V6tWrNWbMGBUuXFhvvvlmqvuoWbOmxo0bp507d6pMmTKSpF9++UUeHh765Zdf9M4779iWSbe6iKWkS5cuOnnypFatWqX//ve/Ka7zzTff6PLly+rSpYssFos++eQTPf/88zp06NBdW0d/+ukn+fj46KWXXkrx9oIFC6pGjRpau3atrl27Jl9fX9tt3bt3V/bs2TV48GDt3btXU6ZM0dGjR20FjqvPiXSrZapJkyZq3bq1WrZsqSlTpqh169aaM2eOevbsqa5du+rll1/W6NGj9eKLL+r48eMKCAiQJG3evFm//fabWrdurfz58+vIkSOaMmWK6tSpo127dilbtmx33XdKxyYyMtL2HnOnWrVqKTIyUj/99JMmT57s0rYlacqUKSpdurSee+45ZcmSRT/99JPeeustWa1WdevWzW7de7031KpVS++8844+++wzvffeeypZsqQk2f69051dxiRp3Lhx2r59u3LlyiXJuePp6n6vXbumOnXq6MCBA+revbsKFiyo+fPnq0OHDrp06ZLDlzR3X9eucnY/N2/eVIMGDVSjRg19+umnttdUly5dbO8p77zzjg4fPqxJkybpr7/+0saNG5U1a1adOXNG9evXV3BwsPr376/s2bPryJEjDid+nM2zevVqNWzYUIUKFdLgwYN17do1TZw4UdWrV9e2bdvuOkHQwIEDNXToUDVq1EiNGjXStm3bVL9+fSUmJtqtN3jwYI0YMUKvvfaannjiCcXFxWnLli3atm2bU+/Rly9fdhjPkjNnTnl4pH7Ou2XLlipatKiGDx9uO4GUFp9NzoiNjdXFixftTghZrVY999xz+vXXX/XGG2+oZMmS+vfffzVu3Djt27dPixcvdnk/aenQoUNavHixWrZsqYIFC+r06dOaNm2aateurV27dilfvnzy9/dXixYtNG/ePI0dO9auZ8a3334rwzBsBf3Vq1dVu3ZtRUdHq0uXLipQoIB+++03DRgwQDExMRo/frzd/mfOnKnr16/rjTfekLe3t3LmzPkwH37GYiDD2rJliyHJWLVqlWEYhmG1Wo38+fMbPXr0sFtv4MCBhiRj4cKFDtuwWq2GYRjG5s2bDUnGzJkzHdZp3769ERERYbu+YsUKQ5Lx008/2a3XqFEj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UVRUlMM6n3/+uSIjI+Xj46MqVarozz//dCkbAPPtrfW61j3WS3trvW52FJdQBAFItw4dOqTKlStr//79+vbbb3XgwAFNnTpVa9asUdWqVXXhwgWzI0qSvLy8lDdvXlkslge6n+DgYGXLli3Nt/vss88qJibG7vLtt9+muv6NGzccliUmJrq179vvFx4errlz5+ratWu2ZdevX9c333yjAgUKuLX9R01YWJhmz55tt2zWrFkKCwuzW7ZmzRq1atVKL7zwgv78809t3bpVw4YNczj2gYGBDs/d0aNHXco0atQoTZkyRZMmTdLu3bs1atQoffLJJ5o4cWKq9zl8+LAaN26sp556Stu3b1fPnj312muvacWKFbZ15s2bp969e2vQoEHatm2bypcvrwYNGujMmTMu5QNgrjo/D1KdrWNV5+dBZkdxCUUQgHSrW7du8vLy0sqVK1W7dm0VKFBADRs21OrVqxUdHa3333/ftq7FYtHixYvt7p89e3a7s9P/+c9/VKxYMWXLlk2FChXShx9+aPelMrlL03//+19FRkYqKChIrVu31uXLlyXdaq1Yv369JkyYYDvrfuTIEYdWhzp16qR4hj75LP/YsWNVtmxZ+fn5KTw8XG+99Zbi4+Ml3Tq73rFjR8XGxtruN3jwYEmO3eGOHTumZs2ayd/fX4GBgXrppZd0+vRppx9PMm9vb+XNm9fukiNHDrtjO2XKFD333HPy8/PTsGHDbNv+8ssvVbBgQVvrg7OZ7ryfJD322GMKDw/XwoULbcsWLlyoAgUKqGLFinaZrVarRowYYWu9KF++vBYsWCDpVqvLU089JUnKkSOHQyuS1WrVu+++q5w5cypv3ry24+vscZWkkSNHKk+ePAoICFDnzp11/fp1OaN9+/aaOXOm3bLk39K73U8//aTq1aurX79+Kl68uIoVK6bmzZvr888/t1vPYrE4PHd58uRxKkuy3377Tc2aNVPjxo0VGRmpF198UfXr179rq83UqVNVsGBBjRkzRiVLllT37t314osvaty4cbZ1xo4dq9dff10dO3ZUqVKlNHXqVGXLlk1fffWVS/kAwB0UQQBSNXaslD//vS/PPed43+eec+6+Y8e6l+3ChQtasWKF3nrrLfn6+trdljdvXrVt21bz5s1zaQr+gIAARUVFadeuXZowYYKmT59u96VNkg4ePKjFixdr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IiMjlS1bNl25csWlfd3L1atX5eFh/3XB09PzroVh1apVtWbNGrtlq1atsnVP9PLyUqVKlezWsVqttpkdAaQflzxy6qxy65JHznuv/AihJQhAujVp0iRVq1ZNDRo00NChQ1WwYEHt3LlT/fr1U7FixTRw4EDbuk8//bQmTZqkqlWrKikpSf/5z3/szvgXLVpUx44d09y5c/X4449r6dKlWrRokcuZIiMj9ccff+jIkSPy9/d36DYmSS+88IKyZcumwYMH69SpU7blwcHBKlKkiG7cuKGJEyeqadOm2rhxo6ZOneqwj/j4eK1Zs0bly5dXtmzZHFqI6tatq7Jly6pt27YaP368bt68qbfeeku1a9dOseva3SQkJNjllKQsWbI4dEO7l7TK5Onpqd27d9v+f6eAgAD17dtXvXr1ktVqVY0aNRQbG6uNGzcqMDBQ7du3V0REhCwWi5YsWaJGjRrJ19fXYeyMu4+hR48e6tChgypXrqzq1atrzpw52rlzpwoVKmTbzqJFizRgwIBUx7+8/vrratmyZYq/ByXdmoji6tWratSokSIiInTp0iV99tlnunHjhl03QMMwHJ476dZYKA8Pj3vmkKSmTZtq2LBhKlCggEqXLq2//vpLY8eOtSvQBgwYoOjoaNv03l27dtWkSZP07rvvqlOnTlq7dq2+++47LV261Haf3r17q3379qpcubKeeOIJjR8/XleuXElxogsAj656ef5RdLQUlkc6YXYYF9ASBCDdKlq0qDZv3qxChQrppZdeUkREhBo2bKhixYpp48aNdl9qx4wZo/DwcNWsWVMvv/yy+vbta1c4PPfcc+rVq5e6d++uChUq6LffftOHH37ocqa+ffvK09NTpUqVUnBwsI4dO+awzoYNG7Rjxw5FREQoNDTUdjl+/LjKly+vsWPHatSoUSpTpozmzJmjESNG2N2/WrVq6tq1q1q1aqXg4GB98sknDvuwWCz64YcflCNHDtWqVUt169ZVoUKFNG/ePJcf0/Lly+1yhoaGqkaNGi5vJy0zBQYGOozXut3HH3+sDz/8UCNGjFDJkiX17LPPaunSpSpYsKCkW7/HM2TIEPXv31958uSxda9Li8fQqlUrffjhh3r33XdVqVIlHT16VG+++abddmJjY7V3795U95NcZGbJkvK5ytq1a+vQoUN69dVXVaJECTVs2FCnTp3S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//nPf4zixYvbrr/00ktG48aN7e5XpUoVo0uXLqluN72+DwNhYYYh3frXbK7UBrQEAUjd2LFS/vy3LuvW2d92+PD/3fb22473fe65/7v9TlFR/3fbwoVuRbtw4YJWrFiht956S76+vna35c2bV23bttW8efNkGIbT2wwICFBUVJR27dqlCRMmaPr06Ro3bpzdOgcPHtTixYu1ZMkSLVmyROvXr7d14ZkwYYKqVq2q119/3dZiEh4e7rCfhQsX2rWqPP/88ypevLjy5MkjSfLw8NBnn32mnTt3atasWVq7dq3effddSbe61o0fP952Jj4mJkZ9+/Z12IfValWzZs104cIFrV+/XqtWrdKhQ4fUqlUrpx+PKwYPHqwWLVro33//VadOnSRJBw4c0Pfff6+FCxdq+/btTme6836369Spk2bOnGm7/tVXX6ljx44OeUaMGKHZs2dr6tSp2rlzp3r16qV27dpp/fr1Cg8P1/fffy9J2rt3r2JiYjRhwgTbfWfNmiU/Pz/98ccf+uSTT/TRRx9p1apVTh/X7777ToMHD9bw4cO1ZcsWhYaGavLkyU4dxzZt2qhIkSL66KOPUrx9+/bt2rVrl3r37i0PD/uP8PLly6tu3bq2VroVK1bo3LlzttfOnVLqUukuX19fWa1W3bx5M9Xbpbu3CHp6emr48OGaOHGiTpw4keI63377rYoXL67nnnsuxdtvb21N7vJ45MiRVPdZrVo1bd26VX/++aekW63L//vf/9SoUaNU77Np0ybVqlVLXl5etmUNGjTQ3r17ba1YmzZtUt26de3u16BBA23atCnV7QJ4uJgdDkDq4uKk6Ohb/09IsL8tKen/bkup+8rZs/93+52uXPm/21IYw+CM/fv3yzAMlSxZMsXbS5YsqYsXL+rs2bMKCQlxapsffPCB7f+RkZHq27ev5s6da/cl0mq1KioqSgEBAZKkV155RWvWrNGwYcMUFBQkLy8vZcuWTXnz5k11Pzlz5rT9f9y4cVq7dq3++OMP2xfF28cURUZGaujQoeratasmT54sLy8vBQUFyWKx3HUfa9as0b///qvDhw/bCrHZs2erdOnS2rx5sx5//PF7Pp5kS5Yskb+/v93233vvPbsuQy+//LJDMZKYmKjZs2crODhYkrRq1SqnMt15v9u1a9dOAwYM0NGjRyVJGzdu1Ny5c7XutiI9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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -1492,23 +524,12 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "Df7eKzh4oj5X", "metadata": { "id": "Df7eKzh4oj5X" }, - "outputs": [ - { - "data": { - "image/png": "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hETdlDoMIiIiInoPmDgRERERERGpwMSJiIiIiIhIBSZOREREOq56dcDKKueZiKjEa9cO+OijnGcdwsEhiIiIdFxKCvDyZc4zEVGJd/nym/s46RDWOBERERERUfExMnrz0CGscSIiIiIiouJz967UEWiFNU5EREREREQqMHEiIiIiIiJSgYkTERERERGRCuzjRERERERExefHH3OGAbWwAIYPlzoatTFxIiIiIiKi4jN37pvhyHUocWJTPSIiIiIiIhVY40RERKTjwsKA168BU1OpIyEiUoOOXrSYOBEREem4jh2ljoCISAM6etFiUz0iIiIiIiIVmDgRERERERGpwKZ6REREOu7sWSAjAzAyAjw9pY6GiEiF//4D5HJATw+wtZU6GrUxcSIiItJxXbq8Gdn34UOpoyEiUqFePZ28aLGpHhERERERkQqscSIiIiIiouLTpk1Ocz0daqYHMHEiIiIiIqLitG6d1BFohU31iIiIiIiIVGDiREREREREpAITJyIiIiIiIhXYx4mIiIiIiIpP//7As2eAnR0QESF1NGpj4kRERERERMXn0KE393HSIWyqR0REREREpAJrnIiIiHTctWuAIAAymdSREBGpQUcvWkyciIiIdJylpdQREBFpQEcvWmyqR0REREREpAITJyIiIiIiIhXYVI8oj5Com1KHQESkseBgIDkZsLICAgKkjoaISIXISCA1FTAzA7p3lzoatTFxIiIi0nHBwW9G9mXiREQl3rhxby5aOpQ4sakeERERERGRCqxxIiIiIiKi4jN/PvDqFWBuLnUkGmHiRAT2bSIiIiIqNoMHSx2BVthUj4iIiIiISAUmTkRERERERCowcSIiIiIiIlKBiRMRERERERWfChUAmSznWYcwcSIiIiIiIlKBo+oRERHpuI8+AlxcAHt7qSMhIlKDjl60JK9xWrFiBVxdXWFiYoLGjRvj1KlThZZ/8eIFRo8eDWdnZxgbG8PDwwN//PFHMUVLRERU8uzcCRw/nvNMRFTi6ehFS9Iapy1btiAgIABhYWFo3LgxQkND4evrixs3bsDBwUGpfEZGBtq0aQMHBwds27YN5cuXx/3792FjY1P8wRMRERER0QdD0sQpODgYw4YNw5AhQwAAYWFh2LNnD9auXYtp06YplV+7di0SExNx7NgxGBoaAgBcXV2LM2QiIiIiIvoASdZU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C5iamkIQBA5BTkREREREBfv/ihddo1Ufp//++w+ffvopPDw80L59e8TFxQEAhg4dyqHIiYiIiIioYMbGbx46RKvEaeLEiTA0NERsbCzMzMzE6b1798bevXuLLDgiIiIiIqKSQKumevv27cNff/2FChUqKEyvWrUq7t+/XySBEZV2IVE3xb8ntvGQMBIiIiIiUkWrxOnVq1cKNU25EhMTYaxjVW5ERERERFSMNm4EUlMBMzOgXz+po1GbVk31mjdvjg0bNoivZTIZ5HI5vv32W7Ru3brIgiMiIiIiolJm6lRg2LCcZx2iVY3Tt99+i08//RRnzpxBRkYGpk6diitXriAxMRFHjx4t6hiJiIiIiIgkpVXiVLt2bdy8eRPLly+HpaUlUlJS0L17d4wePRrOzs5FHSMREREV4ttv37R6ISIq8XT0oqVx4pSZmYl27dohLCwM06dPfx8xERERkQZ0qIsAEZHOXrQ07uNkaGiIS5cuvY9YiIiIiIiISiStBocYMGAAfv7556KOhYiIiIiIqETSqo9TVlYW1q5di/3798PT0xPm5uYK84ODg4skOCIiIlLtxg0gKwswMACqVZM6GiIiFdLT3/ytQ7cy0ihxunv3LlxdXXH58mV89NFHAICbN28qlJHJZEUXHREREan06afAo0dA+fLAw4dSR0NEpEKVKjp50dIocapatSri4uLw999/AwB69+6N77//Ho6Oju8lOCIiIiIiopJAo8RJEASF13/++SdevXpVpAEREREREVEp5u0NPH0K2NtLHYlGtOrjlOvtRIqIiIiIiKhQW7dKHYFWNBpVTyaTKfVhYp8mIiIiIiIq7TRuqjd48GAY///oF2lpaRg5cqTSqHqRkZFFFyEREREREZHENEqc/Pz8FF4PGDCgSIMhIiIiIiIqiTRKnNatW/e+4iAiIiIiog/BiBFAYiJQtiywerXU0ajtnQaHICIiIiIi0siePW/u46RDNBocgoiIiIiI6EPEGiciIiIdd/o0kJ0N6OtLHQkRkRp09KLFxImIiEjHOTtLHQERkQZ09KLFxImoBAiJuil1CERERERUCCZO9MFhkkJEREREmioRg0OsWLECrq6uMDExQePGjXHq1Cm1ltu8eTNkMhm6du36fgMkIiIqwX78EQgOznkmIirx9u0Ddu7MedYhktc4bdmyBQEBAQgLC0Pjxo0RGhoKX19f3LhxAw4ODgUuFxMTg8mTJ6N58+bFGC0REVHJM3fum5F9hw+XOhoiIhX8/d9ctB4+lDoatUle4xQcHIxhw4ZhyJAhqFmzJsLCwmBmZoa1a9cWuEx2djb69++POXPmoHLlysUYLRERERERfYgkrXHKyMjA2bNnERgYKE7T09ODj48Pjh8/XuByc+fOhYODA4YOHYrDhw8Xuo309HSkp6eLr5OTk989cCIiIiIi0s60acDLl4ClpdSRaETSxOnZs2fIzs6Go6OjwnRHR0dcv34932WOHDmCn3/+GRcuXFBrG0FBQZgzZ867hkpEREREREVhzBipI9CK5E31NPHy5UsMHDgQa9asgZ2dnVrLBAYGIikpSXw8ePDgPUdJRERERESljaQ1TnZ2dtDX10dCQoLC9ISEBDg5OSmVv3PnDmJiYtCpUydxmlwuBwAYGBjgxo0bqFKlisIyxsbGMDY2fg/RExERERHRh0LSGicjIyN4enoiOjpanCaXyxEdHQ1vb2+l8tWrV8e///6LCxcuiI/OnTujdevWuHDhAlxcXIozfCKVQqJu8r5RRERERKWA5MORBwQEwM/PD15eXmjUqBFCQ0Px6tUrDBkyBAAwaNAglC9fHkFBQTAxMUHt2rUVlrexsQEApelERERERFQCVa8OPH4MlCsHFDCuQUkkeeLUu3dvPH36FDNnzkR8fDzq16+PvXv3igNGxMbGQk9Pp7piERERERFRQVJSckbVS0mROhKNyARBEKQOojglJyfD2toaSUlJsLKykjockoAUTecmtvHQevt5lyUiys8nnwAJCYCjI3DggNTREBGpUIIuWprkBpLXOBEREdG7YbJERDpFRy9abANHRERERESkAhMnIiIiIiIiFZg4ERERERERqcA+TkRERDquf3/g2TPAzg6IiJA6GiIiFaZPB168AGxsgAULpI5GbUyciIiIdNyhQ8CjR0D58lJHQkSkhvXr31y0dChxYlM9IiIiIiIiFVjjRERERERExefPP4HMTMDQUOpINMLEiYiIiIiIik+dOlJHoBU21SMiIiIiIlKBiRMREREREZEKbKpHRERERETF5+xZICMDMDICPD2ljkZtTJyIikFI1E2pQyAiIiIqGbp0eTMc+cOHUkejNjbVIyIiIiIiUoE1TkRERDpu2DAgKQmwtpY6EiIiNejoRYuJExERkY6bNUvqCIiINKCjFy021SMiIiIiIlKBiRMREREREZEKbKpHOinvKHUT23hIGAkRERERfQhY40RERKTjKlQAZLKcZyKiEq9ZM8DdPedZh7DGiYiIiIiIik9MTM59nNLSpI5EI0yciIiIiIio+JQtC6Sn5zzrECZORERERERUfC5dkjoCrbCPExERERERkQqscSKdknc0PSIiIiKi4sLEiT4YTLqIiIiISFtMnIiIiIiIqPgEBwPJyYCVFRAQIHU0amPiRFTC5daU8Ua/REREVCoEB+cMR16+vE4lThwcgoiIiIiISAXWOBEREem4X37JuSWKsbHUkRARqUFHL1pMnIiIiHRcq1ZSR0BEpAEdvWixqR4REREREZEKTJyIiIiIiIhUYFM9IiIiHXfw4JvuAjraAoaIPiT37gHZ2YC+PuDmJnU0amPiREREpOMGDHgzsu/Dh1JHQ0SkQvPmOnnRYlM9IiIiIiIiFVjjRERERERExadbN+D5c6BMGakj0QgTJyIiIiIiKj4//CB1BFphUz0iIiIiIiIVmDhRiRMSdRMhUTelDoOIiIiISMTEiYiIiIiISAX2cSIiIiIiouLTuTPw9Clgbw/s3Cl1NGpj4kRERERERMXn3Lk393HSIWyqR0REREREpAJrnIiIiHTcw4dSR0BEpAEdvWgxcaJSjaPzEREREVFRYFM9IiIiIiIiFZg4ERERERERqcCmekRERDpuzhwgKQmwtgZmzZI6GiIiFcLDgVevAHNzYPBgqaNRGxMnIiIiHbdmzZuRfZk4EVGJ9803by5aOpQ4sakeERERERGRCqxxIiIiIiKi4vP990BqKmBmJnUkGmHiRERERERExad7d6kj0Aqb6hEREREREanAxImIiIiIiEgFNtUjnRcSdRMAMLGNh8SREBEREZFKL18CggDIZIClpdTRqI2JE5VKuckUEREREZUwNWq8GY784UOpo1Ebm+oRERERERGpwBonIiIiHdeyJfDsGWBnJ3UkRERq0NGLFhMnIiIiHRcRIXUEREQa0NGLFhMnKjXYr4mIiIiI3hf2cSIiIiIiIlKBiRMREREREZEKbKpHRESk4z75BEhIABwdgQMHpI6GiEiFIUOA//4DbG2BdeukjkZtTJyIiIh03M2bObdESUqSOhIiIjVERb25j5MOYVM9IiIiIiIiFVjjRERERERExefiRUAuB/R0qw6HiRMRERERERUfW1upI9AKEyfSCbxHExERERFJiYkTlVhMloiIiIiopGDiRERERERExWf3buD1a8DUFOjYUepo1MbEiYiIiIiIis/IkW+GI3/4UOpo1KZbQ1kQERERERFJgDVOREREOm7mTCAlBbCwkDoSIiI16OhFi4kTERGRjhs+XOoIiIg0oKMXLTbVIyIiIiIiUoE1TkSkJO9Q8BPbeEgYCREREVHJwMSJSEcwmSGigsTFAdnZgL4+4OwsdTRERKUTm+oRERHpuIYNAReXnGciohKvcmXA2DjnWYcwcSIiIiIiouKTkfHmoUNKROK0YsUKuLq6wsTEBI0bN8apU6cKLLtmzRo0b94cZcqUQZkyZeDj41NoeSIiIiIiKkFq1wYaNMh51iGSJ05btmxBQEAAZs2ahXPnzqFevXrw9fXFkydP8i1/8OBB9O3bF3///TeOHz8OFxcXtG3bFo8ePSrmyImIiIiISGN79wLnzuU86xDJE6fg4GAMGzYMQ4YMQc2aNREWFgYzMzOsXbs23/IRERH48ssvUb9+fVSvXh0//fQT5HI5oqOjizlyIiIiIiL6UEiaOGVkZODs2bPw8fERp+np6cHHxwfHjx9Xax2pqanIzMxE2bJl852fnp6O5ORkhQcREREREZEmJE2cnj17huzsbDg6OipMd3R0RHx8vFrr+Oqrr1CuXDmF5CuvoKAgWFtbiw8XF5d3jpuIiIiIiD4skjfVexeLFi3C5s2bsX37dpiYmORbJjAwEElJSeLjwYMHxRwlERERERGJpkwBvvgi51mHSHoDXDs7O+jr6yMhIUFhekJCApycnApd9rvvvsOiRYuwf/9+1K1bt8ByxsbGMDY2LpJ4iYiIiIjoHW3aBDx6BJQvDyxZInU0apO0xsnIyAienp4KAzvkDvTg7e1d4HLffvst5s2bh71798LLy6s4QiUiIiIiog+YpDVOABAQEAA/Pz94eXmhUaNGCA0NxatXrzBkyBAAwKBBg1C+fHkEBQUBABYvXoyZM2di48aNcHV1FftCWVhYwMLCQrL9ICIikkp0NJCVBRhI/l+diEgNOnrRkjza3r174+nTp5g5cybi4+NRv3597N27VxwwIjY2Fnp6byrGVq1ahYyMDHz++ecK65k1axZmz55dnKFTEQqJuil1CEREOqtaNakjICLSgI5etCRPnABgzJgxGDNmTL7zDh48qPA6Jibm/QdERERERESUh06PqkdERERERFQcSkSNExEREWlv40YgNRUwMwP69ZM6GiIiFY4fB9LTAWNjoJAB4UoaJk5EREQ6burUNyP7MnEiohKvZ883F62HD6WORm1sqkdERERERKQCa5yIiIiIiKj4jB4NJCcDVlZSR6IRJk5ERERERFR8AgOljkArbKpHRERERESkAhMnIiIiIiIiFZg4ERERERERqcDEiYiIiIiIio+XF1ChQs6zDuHgEEREREREVHzi43Pu46RjmDgRERHpOCcnxWciohJNRy9aTJyIiIh03JkzUkdARKQBHb1osY8TERERERGRCqxxIiJRSNRNqUMgIiIiKpFY40RERERERKQCa5yIdFBuzdDENh4SR0JEJcGIEUBiIlC2LLB6tdTREBGpEBQEJCcDVlZAYKDU0aiNiRMREZGO27MnZ2Tf8uWljoSISA0rVry5aOlQ4sSmekRERERERCqwxomICsVmgURERFSktm4F0tMBY2OpI9EIEyciIiIiIio+3t5SR6AVNtUjIiIiIiJSgYkTERERERGRCmyqR0RERERExefGDSArCzAwAKpVkzoatTFxIiIiIiKi4vPpp2+GI3/4UOpo1MamekRERERERCqwxomIiEjH9e0LPH8OlCkjdSRERGrQ0YsWEyciIiIdt2SJ1BEQEWlARy9abKpHRERERESkAmucSFIhUTelDoGIiIiISCXWOBEREREREanAxImIiEjHVa8OWFnlPBMRlXjt2gEffZTzrEPYVI+IiEjHpaQAL1/mPBMRlXiXL7+5j5MOYY0TEREREREVHyOjNw8dwhonIiIiIiIqPnfvSh2BVpg4EemwvKMSTmzjIWEkRERERKUbm+oRERERERGpwMSJiIiIiIhIBTbVIyIiIiKi4vPjjznDgFpYAMOHSx2N2pg4ERERERFR8Zk7981w5DqUOLGpHhERERERkQqscSIiItJxYWHA69eAqanUkRARqUFHL1pMnIiIiHRcx45SR0BEpAEdvWgxcSL6wOW9FxQRERER5Y+JExU7flEnIiIiIl3DxImIiEjHnT0LZGQARkaAp6fU0RARqfDff4BcDujpAba2UkejNiZOREREOq5Llzcj+z58KHU0REQq1KunkxctDkdOVEqERN1kM0giIiKi94Q1TkRE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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", @@ -1542,37 +563,12 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "631780a79e2cedf0", "metadata": { "collapsed": false }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [05:47<00:00, 1.13it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.5173, Accuracy: 35494/50000 (71%)\n", - "\n", - "Float model's Top 1 accuracy on the Imagenet validation set: 70.99%\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "from tqdm import tqdm\n", "import torch.nn.functional as F\n", @@ -1609,54 +605,12 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613", "metadata": { "id": "07a22d28-56ff-46de-8ed0-1163c3b7a613" }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [03:47<00:00, 1.72it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.0622, Accuracy: 35966/50000 (72%)\n", - "\n", - "Results for QuantizationErrorMethod.MSE: Loss = -6.06215625, Accuracy = 0.71932\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 391/391 [05:44<00:00, 1.14it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Test set: Average loss: -6.2207, Accuracy: 35962/50000 (72%)\n", - "\n", - "Results for QuantizationErrorMethod.NOCLIPPING: Loss = -6.2207425, Accuracy = 0.71924\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], + "outputs": [], "source": [ "evaluation_results = {}\n", "\n", @@ -1695,21 +649,12 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "qml4LLmWZLP4", "metadata": { "id": "qml4LLmWZLP4" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Layer indices potentially using ReLU: [5, 9, 16, 20, 27, 31, 39, 43, 50, 54, 62, 66, 74, 78, 85, 89, 97, 101, 109, 113, 121, 125, 132, 136, 144, 148, 156, 160, 167, 171, 179, 183, 191, 195, 202]\n", - "Number of relu layers 35\n" - ] - } - ], + "outputs": [], "source": [ "import torch.nn as nn\n", "\n", @@ -1762,25 +707,12 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207", "metadata": { "id": "43f34133-8ed4-429a-a225-6fb6a6f5b207" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - ")\n", - "PytorchQuantizationWrapper(\n", - " (layer): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))\n", - ")\n" - ] - } - ], + "outputs": [], "source": [ "for error_method, data in quantized_models_dict.items():\n", " quantized_model = data[\"quantized_model\"]\n", From 7d4ba382d8546c8fc54020dc4269376da10f38cd Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 28 Feb 2025 16:13:10 +0900 Subject: [PATCH 09/30] add set_manual_weights_bit_width to bit_width_config.py --- .../common/quantization/bit_width_config.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 887d828e1..f05f93801 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -42,6 +42,7 @@ class BitWidthConfig: manual_activation_bit_width_selection_list (List[ManualBitWidthSelection]): A list of ManualBitWidthSelection objects defining manual bit-width configurations. """ manual_activation_bit_width_selection_list: List[ManualBitWidthSelection] = field(default_factory=list) + manual_weights_bit_width_selection_list: List[ManualBitWidthSelection] = field(default_factory=list) def set_manual_activation_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], @@ -65,6 +66,28 @@ def set_manual_activation_bit_width(self, for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] + def set_manual_weights_bit_width(self, + filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], + bit_widths: Union[List[int], int]): + """ + Add a manual bit-width selection to the configuration. + + Args: + filter (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_width (Union[List[int], int]): The bit widths to be applied to the selected nodes. + If a single value is given it will be applied to all the filters + """ + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths + if len(bit_widths) > 1 and len(bit_widths) != len(filters): + Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " + f"must match the number of filters {len(filters)}, or a single bit_width value " + f"should be provided for all filters.") + elif len(bit_widths) == 1 and len(filters) > 1: + bit_widths = [bit_widths[0] for f in filters] + for bit_width, filter in zip (bit_widths, filters): + self.manual_weights_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] + def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: """ Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. From 6eec34bc738ea6e52402d4d8f06330cc44bfbeec Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 28 Feb 2025 19:43:48 +0900 Subject: [PATCH 10/30] modified get_nodes_to_manipulate_bit_widths and etc --- .../common/quantization/bit_width_config.py | 33 ++++++++++++------- .../set_node_quantization_config.py | 3 +- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index f05f93801..f53f09e68 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -98,16 +98,25 @@ def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - nodes_to_change_bit_width = {} - for manual_bit_width_selection in self.manual_activation_bit_width_selection_list: - filtered_nodes = graph.filter(manual_bit_width_selection.filter) - if len(filtered_nodes) == 0: - Logger.critical(f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " - f"to change their bit width to {manual_bit_width_selection.bit_width}.") - for n in filtered_nodes: - # check if a manual configuration exists for this node - if n in nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + def make_nodes_to_change_bit_width(manual_bit_width_selection_list): + unit_nodes_to_change_bit_width = {} + for manual_bit_width_selection in manual_bit_width_selection_list: + filtered_nodes = graph.filter(manual_bit_width_selection.filter) + if len(filtered_nodes) == 0: + Logger.critical( + f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " + f"to change their bit width to {manual_bit_width_selection.bit_width}.") + for n in filtered_nodes: + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + + return unit_nodes_to_change_bit_width + + a_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_activation_bit_width_selection_list) + w_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_weights_bit_width_selection_list) + + nodes_to_change_bit_width = {'activation': a_nodes_to_change_bit_width, 'weights': w_nodes_to_change_bit_width} return nodes_to_change_bit_width \ No newline at end of file diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 7359cdf1c..719f67e64 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -68,13 +68,14 @@ def set_quantization_configuration_to_graph(graph: Graph, nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) for n in graph.nodes: + print(n, nodes_to_manipulate_bit_widths['activation'].get(n)) set_quantization_configs_to_node(node=n, graph=graph, quant_config=quant_config, fw_info=graph.fw_info, fqc=graph.fqc, mixed_precision_enable=mixed_precision_enable, - manual_bit_width_override=nodes_to_manipulate_bit_widths.get(n)) + manual_bit_width_override=nodes_to_manipulate_bit_widths['activation'].get(n)) return graph From 6c3c262f07a2afd9bf2296eff50b0dc21032ad5c Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 5 Mar 2025 07:56:53 +0900 Subject: [PATCH 11/30] All implementations completed. Not yet tested. --- model_compression_toolkit/constants.py | 2 + .../core/common/graph/graph_matchers.py | 1 - .../common/quantization/bit_width_config.py | 7 +- .../set_node_quantization_config.py | 103 ++++++++++++++---- .../tpc_models/imx500_tpc/v1/tpc.py | 58 +++++++--- 5 files changed, 132 insertions(+), 39 deletions(-) diff --git a/model_compression_toolkit/constants.py b/model_compression_toolkit/constants.py index ec335a608..7dc7ca366 100644 --- a/model_compression_toolkit/constants.py +++ b/model_compression_toolkit/constants.py @@ -57,6 +57,8 @@ # In Mixed-Precision, a node can have multiple candidates for weights and activations quantization configuration. # In order to display a single view of a node (for example, for logging in TensorBoard) we need to track the attributes # that are shared among different candidates: +WEIGHTS_ATTRIBUTE = 'weights' +ACTIVATION_ATTRIBUTE = 'activation' WEIGHTS_NBITS_ATTRIBUTE = 'weights_n_bits' CORRECTED_BIAS_ATTRIBUTE = 'corrected_bias' ACTIVATION_N_BITS_ATTRIBUTE = 'activation_n_bits' diff --git a/model_compression_toolkit/core/common/graph/graph_matchers.py b/model_compression_toolkit/core/common/graph/graph_matchers.py index c24adc5f7..b486bae14 100755 --- a/model_compression_toolkit/core/common/graph/graph_matchers.py +++ b/model_compression_toolkit/core/common/graph/graph_matchers.py @@ -50,7 +50,6 @@ def apply(self, input_node_object: BaseNode) -> bool: if input_node_object.is_match_type(self.operation): return True - class NodeFrameworkAttrMatcher(node_matcher.BaseNodeMatcher): """ Class NodeFrameworkAttrMatcher to check if a node's attribute has a specific value. diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index f53f09e68..018357f2a 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -15,6 +15,7 @@ from dataclasses import dataclass, field from typing import List, Union, Dict +from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE from model_compression_toolkit.core.common import Graph from model_compression_toolkit.core.common.matchers.node_matcher import BaseNodeMatcher from model_compression_toolkit.logger import Logger @@ -115,8 +116,8 @@ def make_nodes_to_change_bit_width(manual_bit_width_selection_list): return unit_nodes_to_change_bit_width - a_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_activation_bit_width_selection_list) - w_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_weights_bit_width_selection_list) + activation_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_activation_bit_width_selection_list) + weights_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_weights_bit_width_selection_list) - nodes_to_change_bit_width = {'activation': a_nodes_to_change_bit_width, 'weights': w_nodes_to_change_bit_width} + nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} return nodes_to_change_bit_width \ No newline at end of file diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 719f67e64..6fa3ecd08 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -15,9 +15,10 @@ import copy -from typing import List, Tuple, Optional +from typing import List, Tuple, Dict, Optional -from mct_quantizers.common.constants import ACTIVATION_N_BITS +from mct_quantizers.common.constants import WEIGHTS_N_BITS, ACTIVATION_N_BITS +from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE from model_compression_toolkit.core.common import BaseNode from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig from model_compression_toolkit.logger import Logger @@ -32,6 +33,7 @@ get_activation_quantization_params_fn, get_weights_quantization_params_fn from model_compression_toolkit.core.common.quantization.quantization_fn_selection import \ get_weights_quantization_fn +from model_compression_toolkit.target_platform_capabilities.constants import BIAS, KERNEL_ATTR from model_compression_toolkit.target_platform_capabilities.schema.schema_functions import max_input_activation_n_bits from model_compression_toolkit.target_platform_capabilities.schema.mct_current_schema import OpQuantizationConfig, \ QuantizationConfigOptions @@ -68,14 +70,15 @@ def set_quantization_configuration_to_graph(graph: Graph, nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) for n in graph.nodes: - print(n, nodes_to_manipulate_bit_widths['activation'].get(n)) + manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(ACTIVATION_ATTRIBUTE).get(n), + WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(WEIGHTS_ATTRIBUTE).get(n)} set_quantization_configs_to_node(node=n, graph=graph, quant_config=quant_config, fw_info=graph.fw_info, fqc=graph.fqc, mixed_precision_enable=mixed_precision_enable, - manual_bit_width_override=nodes_to_manipulate_bit_widths['activation'].get(n)) + manual_bit_width_override=manual_bit_width_override) return graph @@ -151,7 +154,7 @@ def set_quantization_configs_to_node(node: BaseNode, fw_info: FrameworkInfo, fqc: FrameworkQuantizationCapabilities, mixed_precision_enable: bool = False, - manual_bit_width_override: Optional[int] = None): + manual_bit_width_override: Optional[Dict] = None): """ Create and set quantization configurations to a node (for both weights and activation). @@ -321,7 +324,7 @@ def filter_qc_options_with_manual_bit_width( node: BaseNode, node_qc_options_list: List[OpQuantizationConfig], base_config: OpQuantizationConfig, - manual_bit_width_override: Optional[int], + manual_bit_width_override: Optional[Dict], mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: """ Update the quantization configurations for a node, allowing manual bit-width overrides if specified. @@ -330,29 +333,48 @@ def filter_qc_options_with_manual_bit_width( node (BaseNode): A node to set quantization configuration candidates to. node_qc_options_list (List[OpQuantizationConfig]): List of quantization configs for the node. base_config (OpQuantizationConfig): Base quantization config for the node. - manual_bit_width_override (Optional[int]): Specifies a custom bit-width to override the node's activation bit-width. + manual_bit_width_override (Optional[Dict]): Specifies a custom bit-width to override the node's activation and weights bit-width. mixed_precision_enable (bool): Whether mixed precision is enabled. Returns: Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: The updated base configuration and the filtered list of quantization configs. """ - if manual_bit_width_override is None: + base_config, node_qc_options_list = activation_qc_options_with_manual_bit_width(node, + node_qc_options_list, + base_config, + manual_bit_width_override.get(ACTIVATION_ATTRIBUTE), + mixed_precision_enable) + base_config, node_qc_options_list = weights_qc_options_with_manual_bit_width(node, + node_qc_options_list, + base_config, + manual_bit_width_override.get(WEIGHTS_ATTRIBUTE), + mixed_precision_enable) + return base_config, node_qc_options_list + + +def activation_qc_options_with_manual_bit_width( + node: BaseNode, + node_qc_options_list: List[OpQuantizationConfig], + base_config: OpQuantizationConfig, + activation_manual_bit_width_override: Optional[int], + mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: + + if activation_manual_bit_width_override is None: return base_config, node_qc_options_list - # Filter node_qc_options_list to retain only the options with activation bits equal to manual_bit_width_override. + # Filter node_qc_options_list to retain only the options with activation bits equal to activation_manual_bit_width_override. node_qc_options_list = [op_cfg for op_cfg in node_qc_options_list if - manual_bit_width_override == op_cfg.activation_n_bits] - + activation_manual_bit_width_override == op_cfg.activation_n_bits] if len(node_qc_options_list) == 0: - Logger.critical(f"Manually selected activation bit-width {manual_bit_width_override} is invalid for node {node}.") + Logger.critical(f"Manually selected activation bit-width {activation_manual_bit_width_override} is invalid for node {node}.") else: # Update the base_config to one of the values from the filtered node_qc_options_list. - # First, check if a configuration similar to the original base_config but with activation bits equal to manual_bit_width_override exists. + # First, check if a configuration similar to the original base_config but with activation bits equal to activation_manual_bit_width_override exists. # If it does, use it as the base_config. If not, choose a different configuration from node_qc_options_list. - Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {manual_bit_width_override} bits.") - updated_base_config = base_config.clone_and_edit({ACTIVATION_N_BITS, manual_bit_width_override}) + Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {activation_manual_bit_width_override} bits.") + updated_base_config = base_config.clone_and_edit({ACTIVATION_N_BITS, activation_manual_bit_width_override}) if updated_base_config in node_qc_options_list: - # If a base_config with the specified manual_bit_width_override exists in the node_qc_options_list, + # If a base_config with the specified activation_manual_bit_width_override exists in the node_qc_options_list, # point the base_config to this option. base_config = node_qc_options_list[node_qc_options_list.index(updated_base_config)] else: @@ -360,6 +382,49 @@ def filter_qc_options_with_manual_bit_width( base_config = node_qc_options_list[0] if len(node_qc_options_list) > 0 and not mixed_precision_enable: Logger.info( - f"Request received to select {manual_bit_width_override} activation bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." - f" Overriding base_config with an option that uses {manual_bit_width_override} bit activations.") # pragma: no cover - return base_config, node_qc_options_list \ No newline at end of file + f"Request received to select {activation_manual_bit_width_override} activation bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." + f" Overriding base_config with an option that uses {activation_manual_bit_width_override} bit activations.") # pragma: no cover + + return base_config, node_qc_options_list + + +def weights_qc_options_with_manual_bit_width( + node: BaseNode, + node_qc_options_list: List[OpQuantizationConfig], + base_config: OpQuantizationConfig, + weights_manual_bit_width_override: Optional[int], + mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: + + if weights_manual_bit_width_override is None: + return base_config, node_qc_options_list + + # Filter node_qc_options_list to retain only the options with weights bits equal to weights_manual_bit_width_override. + node_qc_options_weights_list, target_key_list = [], [] + for op_cfg in node_qc_options_list: + for weights_attrs in op_cfg.attr_weights_configs_mapping.keys(): + if weights_manual_bit_width_override == op_cfg.attr_weights_configs_mapping.get(weights_attrs).weights_n_bits: + node_qc_options_weights_list.append(op_cfg) + target_key_list.append(weights_attrs) + + if len(node_qc_options_weights_list) == 0 or len(target_key_list) == 0: + Logger.critical(f"Manually selected weights bit-width {weights_manual_bit_width_override} is invalid for node {node}.") + else: + # Update the base_config to one of the values from the filtered node_qc_options_list. + # First, check if a configuration similar to the original base_config but with activation bits equal to weights_manual_bit_width_override exists. + # If it does, use it as the base_config. If not, choose a different configuration from node_qc_options_list. + for target_key in target_key_list: + Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {weights_manual_bit_width_override} bits.") + updated_base_config = base_config.clone_and_edit(attr_to_edit={target_key : {WEIGHTS_N_BITS: weights_manual_bit_width_override}}) + if updated_base_config in node_qc_options_weights_list: + # If a base_config with the specified weights_manual_bit_width_override exists in the node_qc_options_list, + # point the base_config to this option. + base_config = node_qc_options_weights_list[node_qc_options_weights_list.index(updated_base_config)] + else: + # Choose a different configuration from node_qc_options_list. If multiple options exist, issue a warning. + base_config = node_qc_options_weights_list[0] + if len(node_qc_options_weights_list) > 0 and not mixed_precision_enable: + Logger.info( + f"Request received to select {weights_manual_bit_width_override} weights bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." + f" Overriding base_config with an option that uses {weights_manual_bit_width_override} bit widths.") # pragma: no cover + + return base_config, node_qc_options_weights_list diff --git a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py index de6e953de..041ca89e2 100644 --- a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py +++ b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py @@ -68,7 +68,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza # define a quantization config to quantize the kernel (for layers where there is a kernel attribute). kernel_base_config = AttributeQuantizationConfig( - weights_quantization_method=QuantizationMethod.SYMMETRIC, + weights_quantization_method=QuantizationMethod.POWER_OF_TWO, weights_n_bits=8, weights_per_channel_threshold=True, enable_weights_quantization=True, @@ -93,7 +93,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza attr_weights_configs_mapping={}, activation_quantization_method=QuantizationMethod.POWER_OF_TWO, activation_n_bits=8, - supported_input_activation_n_bits=8, + supported_input_activation_n_bits=(8,16), enable_activation_quantization=True, quantization_preserving=False, fixed_scale=None, @@ -107,7 +107,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza attr_weights_configs_mapping={KERNEL_ATTR: kernel_base_config, BIAS_ATTR: bias_config}, activation_quantization_method=QuantizationMethod.POWER_OF_TWO, activation_n_bits=8, - supported_input_activation_n_bits=8, + supported_input_activation_n_bits=(8,16), enable_activation_quantization=True, quantization_preserving=False, fixed_scale=None, @@ -120,12 +120,25 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza # In this example, we quantize some operations' weights # using 2, 4 or 8 bits, and when using 2 or 4 bits, it's possible # to quantize the operations' activations using LUT. - four_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, - simd_size=linear_eight_bits.simd_size * 2) - two_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, - simd_size=linear_eight_bits.simd_size * 4) - - mixed_precision_cfg_list = [linear_eight_bits, four_bits, two_bits] + weights_four_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, + simd_size=linear_eight_bits.simd_size * 2) + weights_two_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, + simd_size=linear_eight_bits.simd_size * 4) + weights_sixteen_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 16}}, + simd_size=linear_eight_bits.simd_size / 2) + + weights_linear_eight_bits_activation_sixteen_bit = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 8}}, + activation_n_bits=16) + weights_four_bits_activation_sixteen_bit = weights_four_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, + activation_n_bits=16) + weights_two_eight_bits_activation_sixteen_bit = weights_two_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, + activation_n_bits=16) + weights_sixteen_bits_activation_sixteen_bit = weights_sixteen_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 16}}, + activation_n_bits=16) + + mixed_precision_cfg_list = [linear_eight_bits, weights_four_bits, weights_two_bits, weights_sixteen_bits, + weights_linear_eight_bits_activation_sixteen_bit, weights_four_bits_activation_sixteen_bit, + weights_two_eight_bits_activation_sixteen_bit, weights_sixteen_bits_activation_sixteen_bit] return linear_eight_bits, mixed_precision_cfg_list, eight_bits_default @@ -166,9 +179,22 @@ def generate_tpc(default_config: OpQuantizationConfig, operator_set = [] fusing_patterns = [] - no_quantization_config = (default_configuration_options.clone_and_edit(enable_activation_quantization=False) + no_quantization_config = (default_configuration_options.clone_and_edit(enable_activation_quantization=False, + supported_input_activation_n_bits=(8, 16)) .clone_and_edit_weight_attribute(enable_weights_quantization=False)) + const_config = default_config.clone_and_edit( + default_weight_attr_config=default_config.default_weight_attr_config.clone_and_edit( + enable_weights_quantization=True, weights_per_channel_threshold=True, + weights_quantization_method=QuantizationMethod.POWER_OF_TWO)) + const_activation_config_input16 = const_config.clone_and_edit(supported_input_activation_n_bits=(8, 16)) + const_activation_config_input16_output16 = const_activation_config_input16.clone_and_edit(activation_n_bits=16, + signedness=Signedness.SIGNED) + const_activation_configuration_options_inout16 = schema.QuantizationConfigOptions( + quantization_configurations=tuple([const_activation_config_input16_output16, + const_activation_config_input16]), + base_config=const_activation_config_input16) + operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.STACK, qc_options=no_quantization_config)) operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.UNSTACK, qc_options=no_quantization_config)) operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.DROPOUT, qc_options=no_quantization_config)) @@ -201,14 +227,14 @@ def generate_tpc(default_config: OpQuantizationConfig, # Define operations sets without quantization configuration # options (useful for creating fusing patterns, for example): - relu = schema.OperatorsSet(name=schema.OperatorSetNames.RELU) - relu6 = schema.OperatorsSet(name=schema.OperatorSetNames.RELU6) + relu = schema.OperatorsSet(name=schema.OperatorSetNames.RELU, qc_options=const_activation_configuration_options_inout16) + relu6 = schema.OperatorsSet(name=schema.OperatorSetNames.RELU6, qc_options=const_activation_configuration_options_inout16) leaky_relu = schema.OperatorsSet(name=schema.OperatorSetNames.LEAKY_RELU) prelu = schema.OperatorsSet(name=schema.OperatorSetNames.PRELU) - add = schema.OperatorsSet(name=schema.OperatorSetNames.ADD) - sub = schema.OperatorsSet(name=schema.OperatorSetNames.SUB) - mul = schema.OperatorsSet(name=schema.OperatorSetNames.MUL) - div = schema.OperatorsSet(name=schema.OperatorSetNames.DIV) + add = schema.OperatorsSet(name=schema.OperatorSetNames.ADD, qc_options=const_activation_configuration_options_inout16) + sub = schema.OperatorsSet(name=schema.OperatorSetNames.SUB, qc_options=const_activation_configuration_options_inout16) + mul = schema.OperatorsSet(name=schema.OperatorSetNames.MUL, qc_options=const_activation_configuration_options_inout16) + div = schema.OperatorsSet(name=schema.OperatorSetNames.DIV, qc_options=const_activation_configuration_options_inout16) swish = schema.OperatorsSet(name=schema.OperatorSetNames.SWISH) hard_swish = schema.OperatorsSet(name=schema.OperatorSetNames.HARDSWISH) sigmoid = schema.OperatorsSet(name=schema.OperatorSetNames.SIGMOID) From 94110426617a469f512ddd80e9386f46dad9141a Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Thu, 6 Mar 2025 12:44:08 +0900 Subject: [PATCH 12/30] revert original setting for tpc. --- .../tpc_models/imx500_tpc/v1/tpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py index 041ca89e2..3111e3e04 100644 --- a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py +++ b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py @@ -68,7 +68,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza # define a quantization config to quantize the kernel (for layers where there is a kernel attribute). kernel_base_config = AttributeQuantizationConfig( - weights_quantization_method=QuantizationMethod.POWER_OF_TWO, + weights_quantization_method=QuantizationMethod.SYMMETRIC, weights_n_bits=8, weights_per_channel_threshold=True, enable_weights_quantization=True, From b283c8198e9f0e47de1735de6458c8fdcb8f1fdf Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Thu, 6 Mar 2025 15:36:23 +0900 Subject: [PATCH 13/30] add test for weights_manual_selection_bitwidth --- .../test_manual_weights_bitwidth_selection.py | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100755 tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py new file mode 100755 index 000000000..fa0d524ca --- /dev/null +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -0,0 +1,203 @@ +import pytest +import numpy as np +import tensorflow as tf + +import model_compression_toolkit as mct + +from model_compression_toolkit.core.common.network_editors import NodeTypeFilter, NodeNameFilter +from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection +from model_compression_toolkit.core import BitWidthConfig + + +tf.config.set_visible_devices([], 'GPU') + +### test model +def get_test_model(): + ### Note: This test model is ref for mct + ### path: model_optimization/tests/keras_tests/feature_networks_tests/feature_networks/manual_bit_selection.py + + input_tensor = tf.keras.layers.Input(shape=(224,224,3), name='input') + x1 = tf.keras.layers.Conv2D(filters=32, kernel_size=(1, 1), padding='same', name='conv1')(input_tensor) + x1 = tf.keras.layers.Add(name='add1')([x1, np.ones((3,), dtype=np.float32)]) + + # Second convolutional block + x2 = tf.keras.layers.Conv2D(filters=32, kernel_size=(1, 1), padding='same', name='conv2')(x1) + x2 = tf.keras.layers.BatchNormalization(name='bn1')(x2) + x2 = tf.keras.layers.ReLU(name='relu1')(x2) + + # Addition + x = tf.keras.layers.Add(name='add2')([x1, x2]) + + # Flatten and fully connected layer + x = tf.keras.layers.Flatten()(x) + output_tensor = tf.keras.layers.Dense(units=10, activation='softmax', name='fc')(x) + + return tf.keras.Model(inputs=input_tensor, outputs=output_tensor) + +def get_test_graph(model): + + from model_compression_toolkit.core.graph_prep_runner import read_model_to_graph + from model_compression_toolkit.core.keras.default_framework_info import DEFAULT_KERAS_INFO + from model_compression_toolkit.core.keras.keras_implementation import KerasImplementation + + from model_compression_toolkit.target_platform_capabilities.targetplatform2framework.attach2keras import AttachTpcToKeras + from model_compression_toolkit.target_platform_capabilities.tpc_io_handler import load_target_platform_capabilities + + + fw_info = DEFAULT_KERAS_INFO + fw_impl = KerasImplementation() + + target_platform_capabilities = mct.get_target_platform_capabilities(fw_name='tensorflow', + target_platform_name='imx500', + target_platform_version='v1') + + attach2keras = AttachTpcToKeras() + target_platform_capabilities = load_target_platform_capabilities(target_platform_capabilities) + framework_platform_capabilities = attach2keras.attach(target_platform_capabilities) + + graph = read_model_to_graph(model, + None, + framework_platform_capabilities, + fw_info, fw_impl) + + return graph + +class TestBitWidthConfig: + + ####################################################################################################### + ### test case + setter_test_input_0 = {"activation": (None, None), + "weights": (None, None)} + setter_test_input_1 = {"activation": (NodeTypeFilter(tf.keras.layers.ReLU), [16]), + "weights": (None, None)} + setter_test_input_2 = {"activation": (None, None), + "weights": (NodeNameFilter("conv2"), [8])} + setter_test_input_3 = {"activation": (NodeTypeFilter(tf.keras.layers.ReLU), [16]), + "weights": (NodeNameFilter("conv2"), [8])} + setter_test_input_4 = {"activation": ([NodeTypeFilter(tf.keras.layers.ReLU), NodeNameFilter("conv1")], [16, 8]), + "weights": ([NodeTypeFilter(tf.keras.layers.Conv2D), NodeNameFilter("fc")], [16, 2])} + + setter_test_expected_0 = {"activation": (None, None), + "weights": (None, None)} + setter_test_expected_1 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16]), + "weights": (None, None)} + setter_test_expected_2 = {"activation": (None, None), + "weights": ([NodeNameFilter, "conv2", 8]) } + setter_test_expected_3 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16]), + "weights": ([NodeNameFilter, "conv2", 8])} + setter_test_expected_4 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16], [NodeNameFilter, "conv1", 8]), + "weights": ([NodeTypeFilter, tf.keras.layers.Conv2D, 16], [NodeNameFilter, "fc", 2])} + + + # test : BitWidthConfig set_manual_activation_bit_width, set_manual_weights_bit_width + @pytest.mark.parametrize(("inputs", "expected"), [ + (setter_test_input_0, setter_test_expected_0), + (setter_test_input_1, setter_test_expected_1), + (setter_test_input_2, setter_test_expected_2), + (setter_test_input_3, setter_test_expected_3), + (setter_test_input_4, setter_test_expected_4), + ]) + def test_BitWidthConfig_setter(self, inputs, expected): + + def check_param(mb_cfg, exp): + ### check setting config class (expected ManualBitWidthSelection) + assert type(mb_cfg) == ManualBitWidthSelection + + ### check setting filter for NodeFilter and NodeInfo + if mb_cfg.filter is not None: + assert isinstance(mb_cfg.filter, exp[0]) + if isinstance(mb_cfg.filter, NodeTypeFilter): + assert mb_cfg.filter.node_type == exp[1] + elif isinstance(mb_cfg.filter, NodeNameFilter): + assert mb_cfg.filter.node_name == exp[1] + + ### check setting bit_width + assert mb_cfg.bit_width == exp[2] + else: + assert mb_cfg.filter is None + + + activation = inputs["activation"] + weights = inputs["weights"] + + activation_expected = expected["activation"] + weights_expected = expected["weights"] + + manual_bit_cfg = BitWidthConfig() + + manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) + manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1]) + + ### check got object instance + assert isinstance(manual_bit_cfg, BitWidthConfig) + + ### check Activation + if len(manual_bit_cfg.manual_activation_bit_width_selection_list) == 1: + for a_mb_cfg in manual_bit_cfg.manual_activation_bit_width_selection_list: + check_param(a_mb_cfg, activation_expected) + else: + for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_activation_bit_width_selection_list): + check_param(a_mb_cfg, activation_expected[idx]) + + ### check Weights + if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: + for w_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: + check_param(w_mb_cfg, weights_expected) + else: + for idx, w_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): + check_param(w_mb_cfg, weights_expected[idx]) + + + ####################################################################################################### + ### test case + ### Note: setter inputs reuse getters test inputs + getter_test_expected_0 = {"activation":{}, + "weights": {}} + getter_test_expected_1 = {"activation":{"ReLU:relu1": 16}, + "weights": {}} + getter_test_expected_2 = {"activation":{}, + "weights": {"Conv2D:conv2": 8}} + getter_test_expected_3 = {"activation": {"ReLU:relu1": 16}, + "weights": {"Conv2D:conv2": 8}} + getter_test_expected_4 = {"activation": {"ReLU:relu1": 16, "Conv2D:conv1": 8}, + "weights": {"Conv2D:conv2": 16, "Conv2D:conv1": 16, "Dense:fc": 2}} + + # test : BitWidthConfig get_nodes_to_manipulate_bit_widths + @pytest.mark.parametrize(("model", "inputs", "expected"), [ + (get_test_model(), setter_test_input_0, getter_test_expected_0), + (get_test_model(), setter_test_input_1, getter_test_expected_1), + (get_test_model(), setter_test_input_2, getter_test_expected_2), + (get_test_model(), setter_test_input_3, getter_test_expected_3), + (get_test_model(), setter_test_input_4, getter_test_expected_4), + ]) + def test_BitWidthConfig_getter(self, model, inputs, expected): + + graph = get_test_graph(model) + + activation = inputs["activation"] + weights = inputs["weights"] + + activation_expected = expected["activation"] + weights_expected = expected["weights"] + + manual_bit_cfg = BitWidthConfig() + if activation[0] is not None: + manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) + if weights[0] is not None: + manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1]) + + get_manual_bit_dict = manual_bit_cfg.get_nodes_to_manipulate_bit_widths(graph) + + if activation[0] is not None: + for idx, (key, val) in enumerate(get_manual_bit_dict["activation"].items()): + assert str(key) == list(activation_expected.keys())[idx] + assert val == list(activation_expected.values())[idx] + else: + assert get_manual_bit_dict["activation"] == activation_expected + + if weights[0] is not None: + for idx, (key, val) in enumerate(get_manual_bit_dict["weights"].items()): + assert str(key) == list(weights_expected.keys())[idx] + assert val == list(weights_expected.values())[idx] + else: + assert get_manual_bit_dict["weights"] == weights_expected From a776f4c378ebb0365f7242943e85cc7bfb60f73d Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Tue, 11 Mar 2025 13:24:58 +0900 Subject: [PATCH 14/30] modified test_manual_weights_bitwidth_selection.py --- .../test_manual_weights_bitwidth_selection.py | 132 ++++++++---------- 1 file changed, 61 insertions(+), 71 deletions(-) diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index fa0d524ca..95d1d895b 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -1,65 +1,55 @@ import pytest -import numpy as np -import tensorflow as tf - -import model_compression_toolkit as mct from model_compression_toolkit.core.common.network_editors import NodeTypeFilter, NodeNameFilter from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection from model_compression_toolkit.core import BitWidthConfig - -tf.config.set_visible_devices([], 'GPU') +from model_compression_toolkit.core.common import Graph +from model_compression_toolkit.core.common.graph.edge import Edge +from tests_pytest.test_util.graph_builder_utils import build_node + +### dummy layer classes +class Conv2D: + pass +class InputLayer: + pass +class Add: + pass +class BatchNormalization: + pass +class ReLU: + pass +class Flatten: + pass +class Dense: + pass ### test model -def get_test_model(): - ### Note: This test model is ref for mct - ### path: model_optimization/tests/keras_tests/feature_networks_tests/feature_networks/manual_bit_selection.py - - input_tensor = tf.keras.layers.Input(shape=(224,224,3), name='input') - x1 = tf.keras.layers.Conv2D(filters=32, kernel_size=(1, 1), padding='same', name='conv1')(input_tensor) - x1 = tf.keras.layers.Add(name='add1')([x1, np.ones((3,), dtype=np.float32)]) - - # Second convolutional block - x2 = tf.keras.layers.Conv2D(filters=32, kernel_size=(1, 1), padding='same', name='conv2')(x1) - x2 = tf.keras.layers.BatchNormalization(name='bn1')(x2) - x2 = tf.keras.layers.ReLU(name='relu1')(x2) - - # Addition - x = tf.keras.layers.Add(name='add2')([x1, x2]) - - # Flatten and fully connected layer - x = tf.keras.layers.Flatten()(x) - output_tensor = tf.keras.layers.Dense(units=10, activation='softmax', name='fc')(x) - - return tf.keras.Model(inputs=input_tensor, outputs=output_tensor) - -def get_test_graph(model): - - from model_compression_toolkit.core.graph_prep_runner import read_model_to_graph - from model_compression_toolkit.core.keras.default_framework_info import DEFAULT_KERAS_INFO - from model_compression_toolkit.core.keras.keras_implementation import KerasImplementation - - from model_compression_toolkit.target_platform_capabilities.targetplatform2framework.attach2keras import AttachTpcToKeras - from model_compression_toolkit.target_platform_capabilities.tpc_io_handler import load_target_platform_capabilities - - - fw_info = DEFAULT_KERAS_INFO - fw_impl = KerasImplementation() - - target_platform_capabilities = mct.get_target_platform_capabilities(fw_name='tensorflow', - target_platform_name='imx500', - target_platform_version='v1') - - attach2keras = AttachTpcToKeras() - target_platform_capabilities = load_target_platform_capabilities(target_platform_capabilities) - framework_platform_capabilities = attach2keras.attach(target_platform_capabilities) - - graph = read_model_to_graph(model, - None, - framework_platform_capabilities, - fw_info, fw_impl) - +def get_test_graph(): + n1 = build_node('input', layer_class=InputLayer) + conv1 = build_node('conv1', layer_class=Conv2D) + add1 = build_node('add1', layer_class=Add) + conv2 = build_node('conv2', layer_class=Conv2D) + bn1 = build_node('bn1', layer_class=BatchNormalization) + relu = build_node('relu1', layer_class=ReLU) + add2 = build_node('add2', layer_class=Add) + flatten = build_node('flatten', layer_class=Flatten) + fc = build_node('fc', layer_class=Dense) + + graph = Graph('g', input_nodes=[n1], + nodes=[conv1,add1, conv2, bn1, relu, add2, flatten], + output_nodes=[fc], + edge_list=[Edge(n1, conv1, 0, 0), + Edge(conv1, add1, 0, 0), + Edge(add1, conv2, 0, 0), + Edge(conv2, bn1, 0, 0), + Edge(bn1, relu, 0, 0), + Edge(relu, add2, 0, 0), + Edge(add1, add2, 0, 0), + Edge(add2, flatten, 0, 0), + Edge(flatten, fc, 0, 0), + ] + ) return graph class TestBitWidthConfig: @@ -68,25 +58,25 @@ class TestBitWidthConfig: ### test case setter_test_input_0 = {"activation": (None, None), "weights": (None, None)} - setter_test_input_1 = {"activation": (NodeTypeFilter(tf.keras.layers.ReLU), [16]), + setter_test_input_1 = {"activation": (NodeTypeFilter(ReLU), [16]), "weights": (None, None)} setter_test_input_2 = {"activation": (None, None), "weights": (NodeNameFilter("conv2"), [8])} - setter_test_input_3 = {"activation": (NodeTypeFilter(tf.keras.layers.ReLU), [16]), + setter_test_input_3 = {"activation": (NodeTypeFilter(ReLU), [16]), "weights": (NodeNameFilter("conv2"), [8])} - setter_test_input_4 = {"activation": ([NodeTypeFilter(tf.keras.layers.ReLU), NodeNameFilter("conv1")], [16, 8]), - "weights": ([NodeTypeFilter(tf.keras.layers.Conv2D), NodeNameFilter("fc")], [16, 2])} + setter_test_input_4 = {"activation": ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]), + "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2])} setter_test_expected_0 = {"activation": (None, None), "weights": (None, None)} - setter_test_expected_1 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16]), + setter_test_expected_1 = {"activation": ([NodeTypeFilter, ReLU, 16]), "weights": (None, None)} setter_test_expected_2 = {"activation": (None, None), "weights": ([NodeNameFilter, "conv2", 8]) } - setter_test_expected_3 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16]), + setter_test_expected_3 = {"activation": ([NodeTypeFilter, ReLU, 16]), "weights": ([NodeNameFilter, "conv2", 8])} - setter_test_expected_4 = {"activation": ([NodeTypeFilter, tf.keras.layers.ReLU, 16], [NodeNameFilter, "conv1", 8]), - "weights": ([NodeTypeFilter, tf.keras.layers.Conv2D, 16], [NodeNameFilter, "fc", 2])} + setter_test_expected_4 = {"activation": ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]), + "weights": ([NodeTypeFilter, Conv2D, 16], [NodeNameFilter, "fc", 2])} # test : BitWidthConfig set_manual_activation_bit_width, set_manual_weights_bit_width @@ -160,19 +150,19 @@ def check_param(mb_cfg, exp): getter_test_expected_3 = {"activation": {"ReLU:relu1": 16}, "weights": {"Conv2D:conv2": 8}} getter_test_expected_4 = {"activation": {"ReLU:relu1": 16, "Conv2D:conv1": 8}, - "weights": {"Conv2D:conv2": 16, "Conv2D:conv1": 16, "Dense:fc": 2}} + "weights": {"Conv2D:conv1": 16, "Conv2D:conv2": 16, "Dense:fc": 2}} # test : BitWidthConfig get_nodes_to_manipulate_bit_widths - @pytest.mark.parametrize(("model", "inputs", "expected"), [ - (get_test_model(), setter_test_input_0, getter_test_expected_0), - (get_test_model(), setter_test_input_1, getter_test_expected_1), - (get_test_model(), setter_test_input_2, getter_test_expected_2), - (get_test_model(), setter_test_input_3, getter_test_expected_3), - (get_test_model(), setter_test_input_4, getter_test_expected_4), + @pytest.mark.parametrize(("inputs", "expected"), [ + (setter_test_input_0, getter_test_expected_0), + (setter_test_input_1, getter_test_expected_1), + (setter_test_input_2, getter_test_expected_2), + (setter_test_input_3, getter_test_expected_3), + (setter_test_input_4, getter_test_expected_4), ]) - def test_BitWidthConfig_getter(self, model, inputs, expected): + def test_BitWidthConfig_getter(self, inputs, expected): - graph = get_test_graph(model) + graph = get_test_graph() activation = inputs["activation"] weights = inputs["weights"] From 424dde638ebd0b4a579de2c8b7da116f154c6641 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 12 Mar 2025 10:24:31 +0900 Subject: [PATCH 15/30] revert tpc.py --- .../tpc_models/imx500_tpc/v1/tpc.py | 56 +++++-------------- 1 file changed, 15 insertions(+), 41 deletions(-) diff --git a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py index 3111e3e04..de6e953de 100644 --- a/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py +++ b/model_compression_toolkit/target_platform_capabilities/tpc_models/imx500_tpc/v1/tpc.py @@ -93,7 +93,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza attr_weights_configs_mapping={}, activation_quantization_method=QuantizationMethod.POWER_OF_TWO, activation_n_bits=8, - supported_input_activation_n_bits=(8,16), + supported_input_activation_n_bits=8, enable_activation_quantization=True, quantization_preserving=False, fixed_scale=None, @@ -107,7 +107,7 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza attr_weights_configs_mapping={KERNEL_ATTR: kernel_base_config, BIAS_ATTR: bias_config}, activation_quantization_method=QuantizationMethod.POWER_OF_TWO, activation_n_bits=8, - supported_input_activation_n_bits=(8,16), + supported_input_activation_n_bits=8, enable_activation_quantization=True, quantization_preserving=False, fixed_scale=None, @@ -120,25 +120,12 @@ def get_op_quantization_configs() -> Tuple[OpQuantizationConfig, List[OpQuantiza # In this example, we quantize some operations' weights # using 2, 4 or 8 bits, and when using 2 or 4 bits, it's possible # to quantize the operations' activations using LUT. - weights_four_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, - simd_size=linear_eight_bits.simd_size * 2) - weights_two_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, - simd_size=linear_eight_bits.simd_size * 4) - weights_sixteen_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 16}}, - simd_size=linear_eight_bits.simd_size / 2) - - weights_linear_eight_bits_activation_sixteen_bit = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 8}}, - activation_n_bits=16) - weights_four_bits_activation_sixteen_bit = weights_four_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, - activation_n_bits=16) - weights_two_eight_bits_activation_sixteen_bit = weights_two_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, - activation_n_bits=16) - weights_sixteen_bits_activation_sixteen_bit = weights_sixteen_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 16}}, - activation_n_bits=16) - - mixed_precision_cfg_list = [linear_eight_bits, weights_four_bits, weights_two_bits, weights_sixteen_bits, - weights_linear_eight_bits_activation_sixteen_bit, weights_four_bits_activation_sixteen_bit, - weights_two_eight_bits_activation_sixteen_bit, weights_sixteen_bits_activation_sixteen_bit] + four_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 4}}, + simd_size=linear_eight_bits.simd_size * 2) + two_bits = linear_eight_bits.clone_and_edit(attr_to_edit={KERNEL_ATTR: {WEIGHTS_N_BITS: 2}}, + simd_size=linear_eight_bits.simd_size * 4) + + mixed_precision_cfg_list = [linear_eight_bits, four_bits, two_bits] return linear_eight_bits, mixed_precision_cfg_list, eight_bits_default @@ -179,22 +166,9 @@ def generate_tpc(default_config: OpQuantizationConfig, operator_set = [] fusing_patterns = [] - no_quantization_config = (default_configuration_options.clone_and_edit(enable_activation_quantization=False, - supported_input_activation_n_bits=(8, 16)) + no_quantization_config = (default_configuration_options.clone_and_edit(enable_activation_quantization=False) .clone_and_edit_weight_attribute(enable_weights_quantization=False)) - const_config = default_config.clone_and_edit( - default_weight_attr_config=default_config.default_weight_attr_config.clone_and_edit( - enable_weights_quantization=True, weights_per_channel_threshold=True, - weights_quantization_method=QuantizationMethod.POWER_OF_TWO)) - const_activation_config_input16 = const_config.clone_and_edit(supported_input_activation_n_bits=(8, 16)) - const_activation_config_input16_output16 = const_activation_config_input16.clone_and_edit(activation_n_bits=16, - signedness=Signedness.SIGNED) - const_activation_configuration_options_inout16 = schema.QuantizationConfigOptions( - quantization_configurations=tuple([const_activation_config_input16_output16, - const_activation_config_input16]), - base_config=const_activation_config_input16) - operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.STACK, qc_options=no_quantization_config)) operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.UNSTACK, qc_options=no_quantization_config)) operator_set.append(schema.OperatorsSet(name=schema.OperatorSetNames.DROPOUT, qc_options=no_quantization_config)) @@ -227,14 +201,14 @@ def generate_tpc(default_config: OpQuantizationConfig, # Define operations sets without quantization configuration # options (useful for creating fusing patterns, for example): - relu = schema.OperatorsSet(name=schema.OperatorSetNames.RELU, qc_options=const_activation_configuration_options_inout16) - relu6 = schema.OperatorsSet(name=schema.OperatorSetNames.RELU6, qc_options=const_activation_configuration_options_inout16) + relu = schema.OperatorsSet(name=schema.OperatorSetNames.RELU) + relu6 = schema.OperatorsSet(name=schema.OperatorSetNames.RELU6) leaky_relu = schema.OperatorsSet(name=schema.OperatorSetNames.LEAKY_RELU) prelu = schema.OperatorsSet(name=schema.OperatorSetNames.PRELU) - add = schema.OperatorsSet(name=schema.OperatorSetNames.ADD, qc_options=const_activation_configuration_options_inout16) - sub = schema.OperatorsSet(name=schema.OperatorSetNames.SUB, qc_options=const_activation_configuration_options_inout16) - mul = schema.OperatorsSet(name=schema.OperatorSetNames.MUL, qc_options=const_activation_configuration_options_inout16) - div = schema.OperatorsSet(name=schema.OperatorSetNames.DIV, qc_options=const_activation_configuration_options_inout16) + add = schema.OperatorsSet(name=schema.OperatorSetNames.ADD) + sub = schema.OperatorsSet(name=schema.OperatorSetNames.SUB) + mul = schema.OperatorsSet(name=schema.OperatorSetNames.MUL) + div = schema.OperatorsSet(name=schema.OperatorSetNames.DIV) swish = schema.OperatorsSet(name=schema.OperatorSetNames.SWISH) hard_swish = schema.OperatorsSet(name=schema.OperatorSetNames.HARDSWISH) sigmoid = schema.OperatorsSet(name=schema.OperatorSetNames.SIGMOID) From b07fc5d88170f3566942691b8a04a9aebe13fd69 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 12 Mar 2025 10:29:43 +0900 Subject: [PATCH 16/30] correcting accrding to the feedback comments --- .../common/quantization/bit_width_config.py | 106 ++++++++++-------- .../set_node_quantization_config.py | 10 +- .../test_manual_weights_bitwidth_selection.py | 8 +- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 018357f2a..800566c41 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -33,6 +33,54 @@ class ManualBitWidthSelection: filter: BaseNodeMatcher bit_width: int +@dataclass +class ManualWeightsBitWidthSelection(ManualBitWidthSelection): + """ + Class to encapsulate the manual weights bit width selection configuration for a specific filter. + + Attributes: + filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. + bit_width (int): The bit width to be applied to the selected nodes. + """ + val: int + + +def _set_manual_bit_width(filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], + bit_widths: Union[List[int], int]): + + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths + if len(bit_widths) > 1 and len(bit_widths) != len(filters): + Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " + f"must match the number of filters {len(filters)}, or a single bit_width value " + f"should be provided for all filters.") + elif len(bit_widths) == 1 and len(filters) > 1: + bit_widths = [bit_widths[0] for f in filters] + + return bit_widths, filters + + +def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): + unit_nodes_to_change_bit_width = {} + for manual_bit_width_selection in manual_bit_width_selection_list: + filtered_nodes = graph.filter(manual_bit_width_selection.filter) + if len(filtered_nodes) == 0: + Logger.critical( + f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " + f"to change their bit width to {manual_bit_width_selection.bit_width}.") + for n in filtered_nodes: + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + + return unit_nodes_to_change_bit_width + +@dataclass +class NodesToChangeBitWidth: + activation_nodes_to_change_bit_width: Dict + weights_nodes_to_change_bit_width: Dict @dataclass class BitWidthConfig: @@ -49,21 +97,14 @@ def set_manual_activation_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int]): """ - Add a manual bit-width selection to the configuration. + Add a manual bit-width selection for activation to the configuration. Args: - filter (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_width (Union[List[int], int]): The bit widths to be applied to the selected nodes. + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths - if len(bit_widths) > 1 and len(bit_widths) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " - f"must match the number of filters {len(filters)}, or a single bit_width value " - f"should be provided for all filters.") - elif len(bit_widths) == 1 and len(filters) > 1: - bit_widths = [bit_widths[0] for f in filters] + bit_widths, filters = _set_manual_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] @@ -71,25 +112,18 @@ def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int]): """ - Add a manual bit-width selection to the configuration. + Add a manual bit-width selection for weights to the configuration. Args: - filter (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_width (Union[List[int], int]): The bit widths to be applied to the selected nodes. + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths - if len(bit_widths) > 1 and len(bit_widths) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " - f"must match the number of filters {len(filters)}, or a single bit_width value " - f"should be provided for all filters.") - elif len(bit_widths) == 1 and len(filters) > 1: - bit_widths = [bit_widths[0] for f in filters] + bit_widths, filters = _set_manual_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_weights_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] - def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: + def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWidth: """ Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. @@ -99,25 +133,9 @@ def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - def make_nodes_to_change_bit_width(manual_bit_width_selection_list): - unit_nodes_to_change_bit_width = {} - for manual_bit_width_selection in manual_bit_width_selection_list: - filtered_nodes = graph.filter(manual_bit_width_selection.filter) - if len(filtered_nodes) == 0: - Logger.critical( - f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " - f"to change their bit width to {manual_bit_width_selection.bit_width}.") - for n in filtered_nodes: - # check if a manual configuration exists for this node - if n in unit_nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) - - return unit_nodes_to_change_bit_width - - activation_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_activation_bit_width_selection_list) - weights_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_weights_bit_width_selection_list) - - nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} + activation_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_activation_bit_width_selection_list) + weights_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_weights_bit_width_selection_list) + + #nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} + nodes_to_change_bit_width = NodesToChangeBitWidth(activation_nodes_to_change_bit_width, weights_nodes_to_change_bit_width) return nodes_to_change_bit_width \ No newline at end of file diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 6fa3ecd08..5d7b6ad3f 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -20,7 +20,7 @@ from mct_quantizers.common.constants import WEIGHTS_N_BITS, ACTIVATION_N_BITS from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE from model_compression_toolkit.core.common import BaseNode -from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig +from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig, NodesToChangeBitWidth from model_compression_toolkit.logger import Logger from model_compression_toolkit.core.common.framework_info import FrameworkInfo from model_compression_toolkit.core.common.graph.base_graph import Graph @@ -67,11 +67,13 @@ def set_quantization_configuration_to_graph(graph: Graph, Logger.warning("Using the HMSE error method for weights quantization parameters search. " "Note: This method may significantly increase runtime during the parameter search process.") - nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) + nodes_to_manipulate_bit_widths = NodesToChangeBitWidth() if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) + print('nodes_to_manipulate_bit_widths', nodes_to_manipulate_bit_widths) + print('zzz', nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width) for n in graph.nodes: - manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(ACTIVATION_ATTRIBUTE).get(n), - WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(WEIGHTS_ATTRIBUTE).get(n)} + manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width.get(n), + WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.weights_nodes_to_change_bit_width.get(n)} set_quantization_configs_to_node(node=n, graph=graph, quant_config=quant_config, diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index 95d1d895b..37ef95fc9 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -179,15 +179,15 @@ def test_BitWidthConfig_getter(self, inputs, expected): get_manual_bit_dict = manual_bit_cfg.get_nodes_to_manipulate_bit_widths(graph) if activation[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict["activation"].items()): + for idx, (key, val) in enumerate(get_manual_bit_dict.activation_nodes_to_change_bit_width.items()): assert str(key) == list(activation_expected.keys())[idx] assert val == list(activation_expected.values())[idx] else: - assert get_manual_bit_dict["activation"] == activation_expected + assert get_manual_bit_dict.activation_nodes_to_change_bit_width == activation_expected if weights[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict["weights"].items()): + for idx, (key, val) in enumerate(get_manual_bit_dict.weights_nodes_to_change_bit_width.items()): assert str(key) == list(weights_expected.keys())[idx] assert val == list(weights_expected.values())[idx] else: - assert get_manual_bit_dict["weights"] == weights_expected + assert get_manual_bit_dict.weights_nodes_to_change_bit_width == weights_expected From 02dce0c1ad4b315e518c2fd981fd6d1a4c747451 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 12 Mar 2025 10:29:43 +0900 Subject: [PATCH 17/30] correcting according to the feedback comments --- .../common/quantization/bit_width_config.py | 106 ++++++++++-------- .../set_node_quantization_config.py | 10 +- .../test_manual_weights_bitwidth_selection.py | 8 +- 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 018357f2a..800566c41 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -33,6 +33,54 @@ class ManualBitWidthSelection: filter: BaseNodeMatcher bit_width: int +@dataclass +class ManualWeightsBitWidthSelection(ManualBitWidthSelection): + """ + Class to encapsulate the manual weights bit width selection configuration for a specific filter. + + Attributes: + filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. + bit_width (int): The bit width to be applied to the selected nodes. + """ + val: int + + +def _set_manual_bit_width(filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], + bit_widths: Union[List[int], int]): + + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths + if len(bit_widths) > 1 and len(bit_widths) != len(filters): + Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " + f"must match the number of filters {len(filters)}, or a single bit_width value " + f"should be provided for all filters.") + elif len(bit_widths) == 1 and len(filters) > 1: + bit_widths = [bit_widths[0] for f in filters] + + return bit_widths, filters + + +def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): + unit_nodes_to_change_bit_width = {} + for manual_bit_width_selection in manual_bit_width_selection_list: + filtered_nodes = graph.filter(manual_bit_width_selection.filter) + if len(filtered_nodes) == 0: + Logger.critical( + f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " + f"to change their bit width to {manual_bit_width_selection.bit_width}.") + for n in filtered_nodes: + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + + return unit_nodes_to_change_bit_width + +@dataclass +class NodesToChangeBitWidth: + activation_nodes_to_change_bit_width: Dict + weights_nodes_to_change_bit_width: Dict @dataclass class BitWidthConfig: @@ -49,21 +97,14 @@ def set_manual_activation_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int]): """ - Add a manual bit-width selection to the configuration. + Add a manual bit-width selection for activation to the configuration. Args: - filter (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_width (Union[List[int], int]): The bit widths to be applied to the selected nodes. + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths - if len(bit_widths) > 1 and len(bit_widths) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " - f"must match the number of filters {len(filters)}, or a single bit_width value " - f"should be provided for all filters.") - elif len(bit_widths) == 1 and len(filters) > 1: - bit_widths = [bit_widths[0] for f in filters] + bit_widths, filters = _set_manual_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] @@ -71,25 +112,18 @@ def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int]): """ - Add a manual bit-width selection to the configuration. + Add a manual bit-width selection for weights to the configuration. Args: - filter (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_width (Union[List[int], int]): The bit widths to be applied to the selected nodes. + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths - if len(bit_widths) > 1 and len(bit_widths) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " - f"must match the number of filters {len(filters)}, or a single bit_width value " - f"should be provided for all filters.") - elif len(bit_widths) == 1 and len(filters) > 1: - bit_widths = [bit_widths[0] for f in filters] + bit_widths, filters = _set_manual_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_weights_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] - def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: + def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWidth: """ Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. @@ -99,25 +133,9 @@ def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> Dict: Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - def make_nodes_to_change_bit_width(manual_bit_width_selection_list): - unit_nodes_to_change_bit_width = {} - for manual_bit_width_selection in manual_bit_width_selection_list: - filtered_nodes = graph.filter(manual_bit_width_selection.filter) - if len(filtered_nodes) == 0: - Logger.critical( - f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " - f"to change their bit width to {manual_bit_width_selection.bit_width}.") - for n in filtered_nodes: - # check if a manual configuration exists for this node - if n in unit_nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) - - return unit_nodes_to_change_bit_width - - activation_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_activation_bit_width_selection_list) - weights_nodes_to_change_bit_width = make_nodes_to_change_bit_width(self.manual_weights_bit_width_selection_list) - - nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} + activation_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_activation_bit_width_selection_list) + weights_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_weights_bit_width_selection_list) + + #nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} + nodes_to_change_bit_width = NodesToChangeBitWidth(activation_nodes_to_change_bit_width, weights_nodes_to_change_bit_width) return nodes_to_change_bit_width \ No newline at end of file diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 6fa3ecd08..5d7b6ad3f 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -20,7 +20,7 @@ from mct_quantizers.common.constants import WEIGHTS_N_BITS, ACTIVATION_N_BITS from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE from model_compression_toolkit.core.common import BaseNode -from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig +from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig, NodesToChangeBitWidth from model_compression_toolkit.logger import Logger from model_compression_toolkit.core.common.framework_info import FrameworkInfo from model_compression_toolkit.core.common.graph.base_graph import Graph @@ -67,11 +67,13 @@ def set_quantization_configuration_to_graph(graph: Graph, Logger.warning("Using the HMSE error method for weights quantization parameters search. " "Note: This method may significantly increase runtime during the parameter search process.") - nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) + nodes_to_manipulate_bit_widths = NodesToChangeBitWidth() if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) + print('nodes_to_manipulate_bit_widths', nodes_to_manipulate_bit_widths) + print('zzz', nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width) for n in graph.nodes: - manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(ACTIVATION_ATTRIBUTE).get(n), - WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.get(WEIGHTS_ATTRIBUTE).get(n)} + manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width.get(n), + WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.weights_nodes_to_change_bit_width.get(n)} set_quantization_configs_to_node(node=n, graph=graph, quant_config=quant_config, diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index 95d1d895b..37ef95fc9 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -179,15 +179,15 @@ def test_BitWidthConfig_getter(self, inputs, expected): get_manual_bit_dict = manual_bit_cfg.get_nodes_to_manipulate_bit_widths(graph) if activation[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict["activation"].items()): + for idx, (key, val) in enumerate(get_manual_bit_dict.activation_nodes_to_change_bit_width.items()): assert str(key) == list(activation_expected.keys())[idx] assert val == list(activation_expected.values())[idx] else: - assert get_manual_bit_dict["activation"] == activation_expected + assert get_manual_bit_dict.activation_nodes_to_change_bit_width == activation_expected if weights[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict["weights"].items()): + for idx, (key, val) in enumerate(get_manual_bit_dict.weights_nodes_to_change_bit_width.items()): assert str(key) == list(weights_expected.keys())[idx] assert val == list(weights_expected.values())[idx] else: - assert get_manual_bit_dict["weights"] == weights_expected + assert get_manual_bit_dict.weights_nodes_to_change_bit_width == weights_expected From bc0cf98cb0bfec9891f05f3bae3b4616c8f810fa Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 12 Mar 2025 11:35:25 +0900 Subject: [PATCH 18/30] Revert unnecessary modifications --- model_compression_toolkit/core/common/graph/graph_matchers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/model_compression_toolkit/core/common/graph/graph_matchers.py b/model_compression_toolkit/core/common/graph/graph_matchers.py index b486bae14..c24adc5f7 100755 --- a/model_compression_toolkit/core/common/graph/graph_matchers.py +++ b/model_compression_toolkit/core/common/graph/graph_matchers.py @@ -50,6 +50,7 @@ def apply(self, input_node_object: BaseNode) -> bool: if input_node_object.is_match_type(self.operation): return True + class NodeFrameworkAttrMatcher(node_matcher.BaseNodeMatcher): """ Class NodeFrameworkAttrMatcher to check if a node's attribute has a specific value. From 70db8e898dcd38a6a09c565427e3f33dd1c6804d Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 12 Mar 2025 16:42:47 +0900 Subject: [PATCH 19/30] Revert set_node_quantization_config.py --- .../set_node_quantization_config.py | 108 ++++-------------- 1 file changed, 20 insertions(+), 88 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 5d7b6ad3f..7359cdf1c 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -15,12 +15,11 @@ import copy -from typing import List, Tuple, Dict, Optional +from typing import List, Tuple, Optional -from mct_quantizers.common.constants import WEIGHTS_N_BITS, ACTIVATION_N_BITS -from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE +from mct_quantizers.common.constants import ACTIVATION_N_BITS from model_compression_toolkit.core.common import BaseNode -from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig, NodesToChangeBitWidth +from model_compression_toolkit.core.common.quantization.bit_width_config import BitWidthConfig from model_compression_toolkit.logger import Logger from model_compression_toolkit.core.common.framework_info import FrameworkInfo from model_compression_toolkit.core.common.graph.base_graph import Graph @@ -33,7 +32,6 @@ get_activation_quantization_params_fn, get_weights_quantization_params_fn from model_compression_toolkit.core.common.quantization.quantization_fn_selection import \ get_weights_quantization_fn -from model_compression_toolkit.target_platform_capabilities.constants import BIAS, KERNEL_ATTR from model_compression_toolkit.target_platform_capabilities.schema.schema_functions import max_input_activation_n_bits from model_compression_toolkit.target_platform_capabilities.schema.mct_current_schema import OpQuantizationConfig, \ QuantizationConfigOptions @@ -67,20 +65,16 @@ def set_quantization_configuration_to_graph(graph: Graph, Logger.warning("Using the HMSE error method for weights quantization parameters search. " "Note: This method may significantly increase runtime during the parameter search process.") - nodes_to_manipulate_bit_widths = NodesToChangeBitWidth() if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) - print('nodes_to_manipulate_bit_widths', nodes_to_manipulate_bit_widths) - print('zzz', nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width) + nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) for n in graph.nodes: - manual_bit_width_override = {ACTIVATION_ATTRIBUTE: nodes_to_manipulate_bit_widths.activation_nodes_to_change_bit_width.get(n), - WEIGHTS_ATTRIBUTE: nodes_to_manipulate_bit_widths.weights_nodes_to_change_bit_width.get(n)} set_quantization_configs_to_node(node=n, graph=graph, quant_config=quant_config, fw_info=graph.fw_info, fqc=graph.fqc, mixed_precision_enable=mixed_precision_enable, - manual_bit_width_override=manual_bit_width_override) + manual_bit_width_override=nodes_to_manipulate_bit_widths.get(n)) return graph @@ -156,7 +150,7 @@ def set_quantization_configs_to_node(node: BaseNode, fw_info: FrameworkInfo, fqc: FrameworkQuantizationCapabilities, mixed_precision_enable: bool = False, - manual_bit_width_override: Optional[Dict] = None): + manual_bit_width_override: Optional[int] = None): """ Create and set quantization configurations to a node (for both weights and activation). @@ -326,7 +320,7 @@ def filter_qc_options_with_manual_bit_width( node: BaseNode, node_qc_options_list: List[OpQuantizationConfig], base_config: OpQuantizationConfig, - manual_bit_width_override: Optional[Dict], + manual_bit_width_override: Optional[int], mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: """ Update the quantization configurations for a node, allowing manual bit-width overrides if specified. @@ -335,48 +329,29 @@ def filter_qc_options_with_manual_bit_width( node (BaseNode): A node to set quantization configuration candidates to. node_qc_options_list (List[OpQuantizationConfig]): List of quantization configs for the node. base_config (OpQuantizationConfig): Base quantization config for the node. - manual_bit_width_override (Optional[Dict]): Specifies a custom bit-width to override the node's activation and weights bit-width. + manual_bit_width_override (Optional[int]): Specifies a custom bit-width to override the node's activation bit-width. mixed_precision_enable (bool): Whether mixed precision is enabled. Returns: Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: The updated base configuration and the filtered list of quantization configs. """ - base_config, node_qc_options_list = activation_qc_options_with_manual_bit_width(node, - node_qc_options_list, - base_config, - manual_bit_width_override.get(ACTIVATION_ATTRIBUTE), - mixed_precision_enable) - base_config, node_qc_options_list = weights_qc_options_with_manual_bit_width(node, - node_qc_options_list, - base_config, - manual_bit_width_override.get(WEIGHTS_ATTRIBUTE), - mixed_precision_enable) - return base_config, node_qc_options_list - - -def activation_qc_options_with_manual_bit_width( - node: BaseNode, - node_qc_options_list: List[OpQuantizationConfig], - base_config: OpQuantizationConfig, - activation_manual_bit_width_override: Optional[int], - mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: - - if activation_manual_bit_width_override is None: + if manual_bit_width_override is None: return base_config, node_qc_options_list - # Filter node_qc_options_list to retain only the options with activation bits equal to activation_manual_bit_width_override. + # Filter node_qc_options_list to retain only the options with activation bits equal to manual_bit_width_override. node_qc_options_list = [op_cfg for op_cfg in node_qc_options_list if - activation_manual_bit_width_override == op_cfg.activation_n_bits] + manual_bit_width_override == op_cfg.activation_n_bits] + if len(node_qc_options_list) == 0: - Logger.critical(f"Manually selected activation bit-width {activation_manual_bit_width_override} is invalid for node {node}.") + Logger.critical(f"Manually selected activation bit-width {manual_bit_width_override} is invalid for node {node}.") else: # Update the base_config to one of the values from the filtered node_qc_options_list. - # First, check if a configuration similar to the original base_config but with activation bits equal to activation_manual_bit_width_override exists. + # First, check if a configuration similar to the original base_config but with activation bits equal to manual_bit_width_override exists. # If it does, use it as the base_config. If not, choose a different configuration from node_qc_options_list. - Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {activation_manual_bit_width_override} bits.") - updated_base_config = base_config.clone_and_edit({ACTIVATION_N_BITS, activation_manual_bit_width_override}) + Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {manual_bit_width_override} bits.") + updated_base_config = base_config.clone_and_edit({ACTIVATION_N_BITS, manual_bit_width_override}) if updated_base_config in node_qc_options_list: - # If a base_config with the specified activation_manual_bit_width_override exists in the node_qc_options_list, + # If a base_config with the specified manual_bit_width_override exists in the node_qc_options_list, # point the base_config to this option. base_config = node_qc_options_list[node_qc_options_list.index(updated_base_config)] else: @@ -384,49 +359,6 @@ def activation_qc_options_with_manual_bit_width( base_config = node_qc_options_list[0] if len(node_qc_options_list) > 0 and not mixed_precision_enable: Logger.info( - f"Request received to select {activation_manual_bit_width_override} activation bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." - f" Overriding base_config with an option that uses {activation_manual_bit_width_override} bit activations.") # pragma: no cover - - return base_config, node_qc_options_list - - -def weights_qc_options_with_manual_bit_width( - node: BaseNode, - node_qc_options_list: List[OpQuantizationConfig], - base_config: OpQuantizationConfig, - weights_manual_bit_width_override: Optional[int], - mixed_precision_enable: bool) -> Tuple[OpQuantizationConfig, List[OpQuantizationConfig]]: - - if weights_manual_bit_width_override is None: - return base_config, node_qc_options_list - - # Filter node_qc_options_list to retain only the options with weights bits equal to weights_manual_bit_width_override. - node_qc_options_weights_list, target_key_list = [], [] - for op_cfg in node_qc_options_list: - for weights_attrs in op_cfg.attr_weights_configs_mapping.keys(): - if weights_manual_bit_width_override == op_cfg.attr_weights_configs_mapping.get(weights_attrs).weights_n_bits: - node_qc_options_weights_list.append(op_cfg) - target_key_list.append(weights_attrs) - - if len(node_qc_options_weights_list) == 0 or len(target_key_list) == 0: - Logger.critical(f"Manually selected weights bit-width {weights_manual_bit_width_override} is invalid for node {node}.") - else: - # Update the base_config to one of the values from the filtered node_qc_options_list. - # First, check if a configuration similar to the original base_config but with activation bits equal to weights_manual_bit_width_override exists. - # If it does, use it as the base_config. If not, choose a different configuration from node_qc_options_list. - for target_key in target_key_list: - Logger.info(f"Setting node {node} bit-width to manually selected bit-width: {weights_manual_bit_width_override} bits.") - updated_base_config = base_config.clone_and_edit(attr_to_edit={target_key : {WEIGHTS_N_BITS: weights_manual_bit_width_override}}) - if updated_base_config in node_qc_options_weights_list: - # If a base_config with the specified weights_manual_bit_width_override exists in the node_qc_options_list, - # point the base_config to this option. - base_config = node_qc_options_weights_list[node_qc_options_weights_list.index(updated_base_config)] - else: - # Choose a different configuration from node_qc_options_list. If multiple options exist, issue a warning. - base_config = node_qc_options_weights_list[0] - if len(node_qc_options_weights_list) > 0 and not mixed_precision_enable: - Logger.info( - f"Request received to select {weights_manual_bit_width_override} weights bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." - f" Overriding base_config with an option that uses {weights_manual_bit_width_override} bit widths.") # pragma: no cover - - return base_config, node_qc_options_weights_list + f"Request received to select {manual_bit_width_override} activation bits. However, the base configuration for layer type {node.type} is missing in the node_qc_options_list." + f" Overriding base_config with an option that uses {manual_bit_width_override} bit activations.") # pragma: no cover + return base_config, node_qc_options_list \ No newline at end of file From c51a200ce2d1e0047600238f08207703b64cc7a3 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Thu, 13 Mar 2025 11:29:39 +0900 Subject: [PATCH 20/30] fixing for manual weights selection bitwidth(kernel,bias) --- .../common/quantization/bit_width_config.py | 38 ++++++++----- .../test_manual_weights_bitwidth_selection.py | 56 ++++++++++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 800566c41..0400090fb 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -31,7 +31,7 @@ class ManualBitWidthSelection: bit_width (int): The bit width to be applied to the selected nodes. """ filter: BaseNodeMatcher - bit_width: int + bit_width: int = 0 @dataclass class ManualWeightsBitWidthSelection(ManualBitWidthSelection): @@ -42,12 +42,13 @@ class ManualWeightsBitWidthSelection(ManualBitWidthSelection): filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. bit_width (int): The bit width to be applied to the selected nodes. """ - val: int - - -def _set_manual_bit_width(filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], - bit_widths: Union[List[int], int]): + kernel_bit_width: int = 0 + bias_bit_width: int = 0 +def _expand_to_list_filter_and_bit_width( + filters: Union[List[BaseNodeMatcher]], + bit_widths: Union[List[int], int] +): filters = [filters] if not isinstance(filters, list) else filters bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths if len(bit_widths) > 1 and len(bit_widths) != len(filters): @@ -73,7 +74,11 @@ def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): if n in unit_nodes_to_change_bit_width: Logger.info( f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + if isinstance(manual_bit_width_selection_list, ManualBitWidthSelection): + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + elif isinstance(manual_bit_width_selection_list, ManualWeightsBitWidthSelection): + unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.kernel_bit_width, manual_bit_width_selection.bias_bit_width]}) + return unit_nodes_to_change_bit_width @@ -104,24 +109,30 @@ def set_manual_activation_bit_width(self, bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - bit_widths, filters = _set_manual_bit_width(filters, bit_widths) + bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], - bit_widths: Union[List[int], int]): + kernel_bit_widths: Union[List[int], int], + bias_bit_widths: Union[List[int], int] + ): """ Add a manual bit-width selection for weights to the configuration. Args: filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. + kernel_bit_widths (Union[List[int], int]): The bit widths for kernel to be applied to the selected nodes. + bias_bit_widths (Union[List[int], int]): The bit widths for bias to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - bit_widths, filters = _set_manual_bit_width(filters, bit_widths) - for bit_width, filter in zip (bit_widths, filters): - self.manual_weights_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] + kernel_bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, kernel_bit_widths) + bias_bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bias_bit_widths) + print("kernel_bit_widths", kernel_bit_widths) + print("bias_bit_widths", bias_bit_widths) + for kernel_bit_width, bias_bit_width, filter in zip (kernel_bit_widths, bias_bit_widths, filters): + self.manual_weights_bit_width_selection_list += [ManualWeightsBitWidthSelection(filter, kernel_bit_width=kernel_bit_width, bias_bit_width=bias_bit_width)] def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWidth: """ @@ -136,6 +147,5 @@ def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWi activation_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_activation_bit_width_selection_list) weights_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_weights_bit_width_selection_list) - #nodes_to_change_bit_width = {ACTIVATION_ATTRIBUTE: activation_nodes_to_change_bit_width, WEIGHTS_ATTRIBUTE: weights_nodes_to_change_bit_width} nodes_to_change_bit_width = NodesToChangeBitWidth(activation_nodes_to_change_bit_width, weights_nodes_to_change_bit_width) return nodes_to_change_bit_width \ No newline at end of file diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index 37ef95fc9..700ad92c6 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -1,7 +1,7 @@ import pytest from model_compression_toolkit.core.common.network_editors import NodeTypeFilter, NodeNameFilter -from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection +from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection, ManualWeightsBitWidthSelection from model_compression_toolkit.core import BitWidthConfig from model_compression_toolkit.core.common import Graph @@ -57,26 +57,26 @@ class TestBitWidthConfig: ####################################################################################################### ### test case setter_test_input_0 = {"activation": (None, None), - "weights": (None, None)} + "weights": (None, None, None)} setter_test_input_1 = {"activation": (NodeTypeFilter(ReLU), [16]), - "weights": (None, None)} + "weights": (None, None, None)} setter_test_input_2 = {"activation": (None, None), - "weights": (NodeNameFilter("conv2"), [8])} + "weights": (NodeNameFilter("conv2"), [8], 16)} setter_test_input_3 = {"activation": (NodeTypeFilter(ReLU), [16]), - "weights": (NodeNameFilter("conv2"), [8])} + "weights": (NodeNameFilter("conv2"), [8], 16)} setter_test_input_4 = {"activation": ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]), - "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2])} + "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2], [8, 4])} setter_test_expected_0 = {"activation": (None, None), - "weights": (None, None)} + "weights": (None, None, None)} setter_test_expected_1 = {"activation": ([NodeTypeFilter, ReLU, 16]), - "weights": (None, None)} + "weights": (None, None, None)} setter_test_expected_2 = {"activation": (None, None), - "weights": ([NodeNameFilter, "conv2", 8]) } + "weights": ([NodeNameFilter, "conv2", 8, 16]) } setter_test_expected_3 = {"activation": ([NodeTypeFilter, ReLU, 16]), - "weights": ([NodeNameFilter, "conv2", 8])} + "weights": ([NodeNameFilter, "conv2", 8, 16])} setter_test_expected_4 = {"activation": ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]), - "weights": ([NodeTypeFilter, Conv2D, 16], [NodeNameFilter, "fc", 2])} + "weights": ([NodeTypeFilter, Conv2D, 16, 8], [NodeNameFilter, "fc", 2, 4])} # test : BitWidthConfig set_manual_activation_bit_width, set_manual_weights_bit_width @@ -106,6 +106,23 @@ def check_param(mb_cfg, exp): else: assert mb_cfg.filter is None + def check_param_for_weights(mb_cfg, exp): + ### check setting config class (expected ManualBitWidthSelection) + assert type(mb_cfg) == ManualWeightsBitWidthSelection + + ### check setting filter for NodeFilter and NodeInfo + if mb_cfg.filter is not None: + assert isinstance(mb_cfg.filter, exp[0]) + if isinstance(mb_cfg.filter, NodeTypeFilter): + assert mb_cfg.filter.node_type == exp[1] + elif isinstance(mb_cfg.filter, NodeNameFilter): + assert mb_cfg.filter.node_name == exp[1] + + ### check setting bit_width + assert mb_cfg.kernel_bit_width == exp[2] + assert mb_cfg.bias_bit_width == exp[3] + else: + assert mb_cfg.filter is None activation = inputs["activation"] weights = inputs["weights"] @@ -116,7 +133,7 @@ def check_param(mb_cfg, exp): manual_bit_cfg = BitWidthConfig() manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) - manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1]) + manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1], weights[2]) ### check got object instance assert isinstance(manual_bit_cfg, BitWidthConfig) @@ -132,10 +149,10 @@ def check_param(mb_cfg, exp): ### check Weights if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: for w_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: - check_param(w_mb_cfg, weights_expected) + check_param_for_weights(w_mb_cfg, weights_expected) else: for idx, w_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): - check_param(w_mb_cfg, weights_expected[idx]) + check_param_for_weights(w_mb_cfg, weights_expected[idx]) ####################################################################################################### @@ -146,11 +163,11 @@ def check_param(mb_cfg, exp): getter_test_expected_1 = {"activation":{"ReLU:relu1": 16}, "weights": {}} getter_test_expected_2 = {"activation":{}, - "weights": {"Conv2D:conv2": 8}} + "weights": {"Conv2D:conv2": [8, 16]}} getter_test_expected_3 = {"activation": {"ReLU:relu1": 16}, - "weights": {"Conv2D:conv2": 8}} + "weights": {"Conv2D:conv2": [8, 16]}} getter_test_expected_4 = {"activation": {"ReLU:relu1": 16, "Conv2D:conv1": 8}, - "weights": {"Conv2D:conv1": 16, "Conv2D:conv2": 16, "Dense:fc": 2}} + "weights": {"Conv2D:conv1": [16, 8], "Conv2D:conv2": [16, 8], "Dense:fc": [2, 4]}} # test : BitWidthConfig get_nodes_to_manipulate_bit_widths @pytest.mark.parametrize(("inputs", "expected"), [ @@ -174,7 +191,7 @@ def test_BitWidthConfig_getter(self, inputs, expected): if activation[0] is not None: manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) if weights[0] is not None: - manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1]) + manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1], weights[2]) get_manual_bit_dict = manual_bit_cfg.get_nodes_to_manipulate_bit_widths(graph) @@ -188,6 +205,7 @@ def test_BitWidthConfig_getter(self, inputs, expected): if weights[0] is not None: for idx, (key, val) in enumerate(get_manual_bit_dict.weights_nodes_to_change_bit_width.items()): assert str(key) == list(weights_expected.keys())[idx] - assert val == list(weights_expected.values())[idx] + assert val[0] == list(weights_expected.values())[idx][0] + assert val[1] == list(weights_expected.values())[idx][1] else: assert get_manual_bit_dict.weights_nodes_to_change_bit_width == weights_expected From 37d2dcfa339445e3533cd0c7de25235180196676 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Thu, 13 Mar 2025 14:37:53 +0900 Subject: [PATCH 21/30] fixing for manual weights selection bitwidth(kernel,bias) --- .../common/quantization/bit_width_config.py | 54 ++++++++++--------- .../test_manual_weights_bitwidth_selection.py | 24 +++++---- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 0400090fb..92f3f573c 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -31,7 +31,7 @@ class ManualBitWidthSelection: bit_width (int): The bit width to be applied to the selected nodes. """ filter: BaseNodeMatcher - bit_width: int = 0 + bit_width: int @dataclass class ManualWeightsBitWidthSelection(ManualBitWidthSelection): @@ -40,26 +40,31 @@ class ManualWeightsBitWidthSelection(ManualBitWidthSelection): Attributes: filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. - bit_width (int): The bit width to be applied to the selected nodes. + attr (str): The attribute used to select nodes for bit width manipulation. [KERNEL_ATTR|BIAS_ATTR] """ - kernel_bit_width: int = 0 - bias_bit_width: int = 0 + attr: str -def _expand_to_list_filter_and_bit_width( +def _expand_to_list_core( filters: Union[List[BaseNodeMatcher]], - bit_widths: Union[List[int], int] -): - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = [bit_widths] if not isinstance(bit_widths, list) else bit_widths - if len(bit_widths) > 1 and len(bit_widths) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(bit_widths)} " + vals: Union[List[any], any]): + vals = [vals] if not isinstance(vals, list) else vals + if len(vals) > 1 and len(vals) != len(filters): + Logger.critical(f"Configuration Error: The number of provided bit_width values {len(vals)} " f"must match the number of filters {len(filters)}, or a single bit_width value " f"should be provided for all filters.") - elif len(bit_widths) == 1 and len(filters) > 1: - bit_widths = [bit_widths[0] for f in filters] + elif len(vals) == 1 and len(filters) > 1: + vals = [vals[0] for f in filters] + return vals - return bit_widths, filters +def _expand_to_list_filter_and_bit_width( + filters: Union[List[BaseNodeMatcher]], + bit_widths: Union[List[int], int], + attrs: Union[List[str], str] = None): + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = _expand_to_list_core(filters, bit_widths) + attrs = _expand_to_list_core(filters, attrs) + return attrs, bit_widths, filters def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): unit_nodes_to_change_bit_width = {} @@ -77,7 +82,7 @@ def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): if isinstance(manual_bit_width_selection_list, ManualBitWidthSelection): unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) elif isinstance(manual_bit_width_selection_list, ManualWeightsBitWidthSelection): - unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.kernel_bit_width, manual_bit_width_selection.bias_bit_width]}) + unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]}) return unit_nodes_to_change_bit_width @@ -109,30 +114,27 @@ def set_manual_activation_bit_width(self, bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths) + _, bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], - kernel_bit_widths: Union[List[int], int], - bias_bit_widths: Union[List[int], int] + bit_widths: Union[List[int], int], + attrs: Union[List[str], str] ): """ Add a manual bit-width selection for weights to the configuration. Args: filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - kernel_bit_widths (Union[List[int], int]): The bit widths for kernel to be applied to the selected nodes. - bias_bit_widths (Union[List[int], int]): The bit widths for bias to be applied to the selected nodes. + bit_widths (Union[List[int], int]): The bit widths for kernel to be applied to the selected nodes. + attrs (Union[List[str], str]): The attributes used to select nodes for bit width manipulation. [KERNEL_ATTR|BIAS_ATTR] If a single value is given it will be applied to all the filters """ - kernel_bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, kernel_bit_widths) - bias_bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bias_bit_widths) - print("kernel_bit_widths", kernel_bit_widths) - print("bias_bit_widths", bias_bit_widths) - for kernel_bit_width, bias_bit_width, filter in zip (kernel_bit_widths, bias_bit_widths, filters): - self.manual_weights_bit_width_selection_list += [ManualWeightsBitWidthSelection(filter, kernel_bit_width=kernel_bit_width, bias_bit_width=bias_bit_width)] + attrs, bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths, attrs) + for attr, bit_width, filter in zip (attrs, bit_widths, filters): + self.manual_weights_bit_width_selection_list += [ManualWeightsBitWidthSelection(filter, bit_width, attr)] def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWidth: """ diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index 700ad92c6..7fa2b8899 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -8,6 +8,8 @@ from model_compression_toolkit.core.common.graph.edge import Edge from tests_pytest.test_util.graph_builder_utils import build_node +from model_compression_toolkit.target_platform_capabilities.constants import KERNEL_ATTR, BIAS_ATTR + ### dummy layer classes class Conv2D: pass @@ -61,22 +63,22 @@ class TestBitWidthConfig: setter_test_input_1 = {"activation": (NodeTypeFilter(ReLU), [16]), "weights": (None, None, None)} setter_test_input_2 = {"activation": (None, None), - "weights": (NodeNameFilter("conv2"), [8], 16)} + "weights": (NodeNameFilter("conv2"), [8], KERNEL_ATTR)} setter_test_input_3 = {"activation": (NodeTypeFilter(ReLU), [16]), - "weights": (NodeNameFilter("conv2"), [8], 16)} + "weights": (NodeNameFilter("conv2"), [8], KERNEL_ATTR)} setter_test_input_4 = {"activation": ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]), - "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2], [8, 4])} + "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2], [KERNEL_ATTR, BIAS_ATTR])} setter_test_expected_0 = {"activation": (None, None), "weights": (None, None, None)} setter_test_expected_1 = {"activation": ([NodeTypeFilter, ReLU, 16]), "weights": (None, None, None)} setter_test_expected_2 = {"activation": (None, None), - "weights": ([NodeNameFilter, "conv2", 8, 16]) } + "weights": ([NodeNameFilter, "conv2", 8, KERNEL_ATTR]) } setter_test_expected_3 = {"activation": ([NodeTypeFilter, ReLU, 16]), - "weights": ([NodeNameFilter, "conv2", 8, 16])} + "weights": ([NodeNameFilter, "conv2", 8, KERNEL_ATTR])} setter_test_expected_4 = {"activation": ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]), - "weights": ([NodeTypeFilter, Conv2D, 16, 8], [NodeNameFilter, "fc", 2, 4])} + "weights": ([NodeTypeFilter, Conv2D, 16, KERNEL_ATTR], [NodeNameFilter, "fc", 2, BIAS_ATTR])} # test : BitWidthConfig set_manual_activation_bit_width, set_manual_weights_bit_width @@ -119,8 +121,8 @@ def check_param_for_weights(mb_cfg, exp): assert mb_cfg.filter.node_name == exp[1] ### check setting bit_width - assert mb_cfg.kernel_bit_width == exp[2] - assert mb_cfg.bias_bit_width == exp[3] + assert mb_cfg.bit_width == exp[2] + assert mb_cfg.attr == exp[3] else: assert mb_cfg.filter is None @@ -163,11 +165,11 @@ def check_param_for_weights(mb_cfg, exp): getter_test_expected_1 = {"activation":{"ReLU:relu1": 16}, "weights": {}} getter_test_expected_2 = {"activation":{}, - "weights": {"Conv2D:conv2": [8, 16]}} + "weights": {"Conv2D:conv2": [8, KERNEL_ATTR]}} getter_test_expected_3 = {"activation": {"ReLU:relu1": 16}, - "weights": {"Conv2D:conv2": [8, 16]}} + "weights": {"Conv2D:conv2": [8, KERNEL_ATTR]}} getter_test_expected_4 = {"activation": {"ReLU:relu1": 16, "Conv2D:conv1": 8}, - "weights": {"Conv2D:conv1": [16, 8], "Conv2D:conv2": [16, 8], "Dense:fc": [2, 4]}} + "weights": {"Conv2D:conv1": [16, KERNEL_ATTR], "Conv2D:conv2": [16, KERNEL_ATTR], "Dense:fc": [2, BIAS_ATTR]}} # test : BitWidthConfig get_nodes_to_manipulate_bit_widths @pytest.mark.parametrize(("inputs", "expected"), [ From d411b8d255c800d81c860a5eba90131ae56698c4 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 14 Mar 2025 13:47:05 +0900 Subject: [PATCH 22/30] fixed PR-FB for manual weights selection bitwidth --- .../common/quantization/bit_width_config.py | 137 ++++++++++-------- .../test_manual_weights_bitwidth_selection.py | 20 ++- 2 files changed, 85 insertions(+), 72 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 92f3f573c..7a70ac2b5 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -20,6 +20,7 @@ from model_compression_toolkit.core.common.matchers.node_matcher import BaseNodeMatcher from model_compression_toolkit.logger import Logger +from model_compression_toolkit.core.common.graph.base_node import WeightAttrT @dataclass class ManualBitWidthSelection: @@ -40,57 +41,10 @@ class ManualWeightsBitWidthSelection(ManualBitWidthSelection): Attributes: filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. - attr (str): The attribute used to select nodes for bit width manipulation. [KERNEL_ATTR|BIAS_ATTR] + bit_width (int): The bit width to be applied to the selected nodes. + attr (str): The filtered node's attributes to apply bit-width manipulation to. """ - attr: str - -def _expand_to_list_core( - filters: Union[List[BaseNodeMatcher]], - vals: Union[List[any], any]): - vals = [vals] if not isinstance(vals, list) else vals - if len(vals) > 1 and len(vals) != len(filters): - Logger.critical(f"Configuration Error: The number of provided bit_width values {len(vals)} " - f"must match the number of filters {len(filters)}, or a single bit_width value " - f"should be provided for all filters.") - elif len(vals) == 1 and len(filters) > 1: - vals = [vals[0] for f in filters] - return vals - -def _expand_to_list_filter_and_bit_width( - filters: Union[List[BaseNodeMatcher]], - bit_widths: Union[List[int], int], - attrs: Union[List[str], str] = None): - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = _expand_to_list_core(filters, bit_widths) - attrs = _expand_to_list_core(filters, attrs) - - return attrs, bit_widths, filters - -def _make_nodes_to_change_bit_width(graph, manual_bit_width_selection_list): - unit_nodes_to_change_bit_width = {} - for manual_bit_width_selection in manual_bit_width_selection_list: - filtered_nodes = graph.filter(manual_bit_width_selection.filter) - if len(filtered_nodes) == 0: - Logger.critical( - f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " - f"to change their bit width to {manual_bit_width_selection.bit_width}.") - for n in filtered_nodes: - # check if a manual configuration exists for this node - if n in unit_nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - if isinstance(manual_bit_width_selection_list, ManualBitWidthSelection): - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) - elif isinstance(manual_bit_width_selection_list, ManualWeightsBitWidthSelection): - unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]}) - - - return unit_nodes_to_change_bit_width - -@dataclass -class NodesToChangeBitWidth: - activation_nodes_to_change_bit_width: Dict - weights_nodes_to_change_bit_width: Dict + attr: WeightAttrT @dataclass class BitWidthConfig: @@ -98,10 +52,11 @@ class BitWidthConfig: Class to manage manual bit-width configurations. Attributes: - manual_activation_bit_width_selection_list (List[ManualBitWidthSelection]): A list of ManualBitWidthSelection objects defining manual bit-width configurations. + manual_activation_bit_width_selection_list (List[ManualBitWidthSelection]): A list of ManualBitWidthSelection objects for activation defining manual bit-width configurations. + manual_activation_bit_width_selection_list (List[ManualWeightsBitWidthSelection]): A list of ManualWeightsBitWidthSelection for weights objects defining manual bit-width configurations. """ manual_activation_bit_width_selection_list: List[ManualBitWidthSelection] = field(default_factory=list) - manual_weights_bit_width_selection_list: List[ManualBitWidthSelection] = field(default_factory=list) + manual_weights_bit_width_selection_list: List[ManualWeightsBitWidthSelection] = field(default_factory=list) def set_manual_activation_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], @@ -114,7 +69,7 @@ def set_manual_activation_bit_width(self, bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - _, bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths) + _, bit_widths, filters = self._expand_to_list_filter_and_bit_width(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] @@ -128,15 +83,15 @@ def set_manual_weights_bit_width(self, Args: filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. - bit_widths (Union[List[int], int]): The bit widths for kernel to be applied to the selected nodes. - attrs (Union[List[str], str]): The attributes used to select nodes for bit width manipulation. [KERNEL_ATTR|BIAS_ATTR] + bit_widths (Union[List[int], int]): The bit widths for specified by attrs to be applied to the selected nodes. + attrs (Union[List[str], str]): The filtered node's attributes to apply bit-width manipulation to. If a single value is given it will be applied to all the filters """ - attrs, bit_widths, filters = _expand_to_list_filter_and_bit_width(filters, bit_widths, attrs) + attrs, bit_widths, filters = self._expand_to_list_filter_and_bit_width(filters, bit_widths, attrs) for attr, bit_width, filter in zip (attrs, bit_widths, filters): self.manual_weights_bit_width_selection_list += [ManualWeightsBitWidthSelection(filter, bit_width, attr)] - def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWidth: + def get_nodes_to_manipulate_activation_bit_widths(self, graph: Graph) -> Dict: """ Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. @@ -146,8 +101,68 @@ def get_nodes_to_manipulate_bit_widths(self, graph: Graph) -> NodesToChangeBitWi Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - activation_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_activation_bit_width_selection_list) - weights_nodes_to_change_bit_width = _make_nodes_to_change_bit_width(graph, self.manual_weights_bit_width_selection_list) + activation_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_activation_bit_width_selection_list) + + return activation_nodes_to_change_bit_width + + def get_nodes_to_manipulate_weights_bit_widths(self, graph: Graph) -> Dict: + """ + Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. - nodes_to_change_bit_width = NodesToChangeBitWidth(activation_nodes_to_change_bit_width, weights_nodes_to_change_bit_width) - return nodes_to_change_bit_width \ No newline at end of file + Args: + graph (Graph): The graph containing the nodes to be filtered and manipulated. + + Returns: + Dict: A dictionary mapping nodes to their new bit-widths. + """ + weights_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_weights_bit_width_selection_list) + + return weights_nodes_to_change_bit_width + + + + def _expand_to_list_core( + self, + filters: Union[List[BaseNodeMatcher]], + vals: Union[List[any], any]): + vals = [vals] if not isinstance(vals, list) else vals + if len(vals) > 1 and len(vals) != len(filters): + Logger.critical(f"Configuration Error: The number of provided bit_width values {len(vals)} " + f"must match the number of filters {len(filters)}, or a single bit_width value " + f"should be provided for all filters.") + elif len(vals) == 1 and len(filters) > 1: + vals = [vals[0] for f in filters] + return vals + + def _expand_to_list_filter_and_bit_width( + self, + filters: Union[List[BaseNodeMatcher]], + bit_widths: Union[List[int], int], + attrs: Union[List[str], str] = None): + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = self._expand_to_list_core(filters, bit_widths) + attrs = self._expand_to_list_core(filters, attrs) + + return attrs, bit_widths, filters + + def _construct_node_to_new_bit_mapping(self, graph, manual_bit_width_selection_list): + unit_nodes_to_change_bit_width = {} + for manual_bit_width_selection in manual_bit_width_selection_list: + filtered_nodes = graph.filter(manual_bit_width_selection.filter) + if len(filtered_nodes) == 0: + Logger.critical( + f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " + f"to change their bit width to {manual_bit_width_selection.bit_width}.") + for n in filtered_nodes: + if n.get_node_weights_attributes() is False: + Logger.critical(f'The requested attribute to change the bit width for {n} is not existing.') + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") + if isinstance(manual_bit_width_selection_list, ManualBitWidthSelection): + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + elif isinstance(manual_bit_width_selection_list, ManualWeightsBitWidthSelection): + unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]}) + + return unit_nodes_to_change_bit_width \ No newline at end of file diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py index 7fa2b8899..4ae6e88fe 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py @@ -55,9 +55,7 @@ def get_test_graph(): return graph class TestBitWidthConfig: - - ####################################################################################################### - ### test case + # test case setter_test_input_0 = {"activation": (None, None), "weights": (None, None, None)} setter_test_input_1 = {"activation": (NodeTypeFilter(ReLU), [16]), @@ -89,7 +87,7 @@ class TestBitWidthConfig: (setter_test_input_3, setter_test_expected_3), (setter_test_input_4, setter_test_expected_4), ]) - def test_BitWidthConfig_setter(self, inputs, expected): + def test_bit_width_config_setter(self, inputs, expected): def check_param(mb_cfg, exp): ### check setting config class (expected ManualBitWidthSelection) @@ -157,7 +155,6 @@ def check_param_for_weights(mb_cfg, exp): check_param_for_weights(w_mb_cfg, weights_expected[idx]) - ####################################################################################################### ### test case ### Note: setter inputs reuse getters test inputs getter_test_expected_0 = {"activation":{}, @@ -179,7 +176,7 @@ def check_param_for_weights(mb_cfg, exp): (setter_test_input_3, getter_test_expected_3), (setter_test_input_4, getter_test_expected_4), ]) - def test_BitWidthConfig_getter(self, inputs, expected): + def test_bit_width_config_getter(self, inputs, expected): graph = get_test_graph() @@ -195,19 +192,20 @@ def test_BitWidthConfig_getter(self, inputs, expected): if weights[0] is not None: manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1], weights[2]) - get_manual_bit_dict = manual_bit_cfg.get_nodes_to_manipulate_bit_widths(graph) + get_manual_bit_dict_activation = manual_bit_cfg.get_nodes_to_manipulate_activation_bit_widths(graph) + get_manual_bit_dict_weights = manual_bit_cfg.get_nodes_to_manipulate_weights_bit_widths(graph) if activation[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict.activation_nodes_to_change_bit_width.items()): + for idx, (key, val) in enumerate(get_manual_bit_dict_activation.items()): assert str(key) == list(activation_expected.keys())[idx] assert val == list(activation_expected.values())[idx] else: - assert get_manual_bit_dict.activation_nodes_to_change_bit_width == activation_expected + assert get_manual_bit_dict_activation == activation_expected if weights[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict.weights_nodes_to_change_bit_width.items()): + for idx, (key, val) in enumerate(get_manual_bit_dict_weights.items()): assert str(key) == list(weights_expected.keys())[idx] assert val[0] == list(weights_expected.values())[idx][0] assert val[1] == list(weights_expected.values())[idx][1] else: - assert get_manual_bit_dict.weights_nodes_to_change_bit_width == weights_expected + assert get_manual_bit_dict_weights == weights_expected From 5b88fe2551a186e4256a27bed4f831567f515624 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Fri, 14 Mar 2025 15:08:14 +0900 Subject: [PATCH 23/30] rename test script --- ...y => test_manual_activation_and_weights_bitwidth_selection.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests_pytest/common_tests/core/common/quantization/{test_manual_weights_bitwidth_selection.py => test_manual_activation_and_weights_bitwidth_selection.py} (100%) diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_activation_and_weights_bitwidth_selection.py similarity index 100% rename from tests_pytest/common_tests/core/common/quantization/test_manual_weights_bitwidth_selection.py rename to tests_pytest/common_tests/core/common/quantization/test_manual_activation_and_weights_bitwidth_selection.py From 1f30c992a0abca97361038785998546a2fea9bcd Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 19 Mar 2025 16:28:40 +0900 Subject: [PATCH 24/30] Correcting comments on pull requests --- .../common/quantization/bit_width_config.py | 110 ++++++++++++------ ...n.py => test_manual_bitwidth_selection.py} | 0 2 files changed, 74 insertions(+), 36 deletions(-) rename tests_pytest/common_tests/core/common/quantization/{test_manual_activation_and_weights_bitwidth_selection.py => test_manual_bitwidth_selection.py} (100%) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 7a70ac2b5..0aaa0d7fd 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -15,7 +15,6 @@ from dataclasses import dataclass, field from typing import List, Union, Dict -from model_compression_toolkit.constants import WEIGHTS_ATTRIBUTE, ACTIVATION_ATTRIBUTE from model_compression_toolkit.core.common import Graph from model_compression_toolkit.core.common.matchers.node_matcher import BaseNodeMatcher from model_compression_toolkit.logger import Logger @@ -25,25 +24,25 @@ @dataclass class ManualBitWidthSelection: """ - Class to encapsulate the manual bit width selection configuration for a specific filter. + Class to encapsulate the manual bit width selection configuration for a specific filter. - Attributes: + Attributes: filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. bit_width (int): The bit width to be applied to the selected nodes. - """ + """ filter: BaseNodeMatcher bit_width: int @dataclass class ManualWeightsBitWidthSelection(ManualBitWidthSelection): """ - Class to encapsulate the manual weights bit width selection configuration for a specific filter. + Class to encapsulate the manual weights bit width selection configuration for a specific filter. - Attributes: + Attributes: filter (BaseNodeMatcher): The filter used to select nodes for bit width manipulation. bit_width (int): The bit width to be applied to the selected nodes. attr (str): The filtered node's attributes to apply bit-width manipulation to. - """ + """ attr: WeightAttrT @dataclass @@ -53,7 +52,7 @@ class BitWidthConfig: Attributes: manual_activation_bit_width_selection_list (List[ManualBitWidthSelection]): A list of ManualBitWidthSelection objects for activation defining manual bit-width configurations. - manual_activation_bit_width_selection_list (List[ManualWeightsBitWidthSelection]): A list of ManualWeightsBitWidthSelection for weights objects defining manual bit-width configurations. + manual_weights_bit_width_selection_list (List[ManualWeightsBitWidthSelection]): A list of ManualWeightsBitWidthSelection for weights objects defining manual bit-width configurations. """ manual_activation_bit_width_selection_list: List[ManualBitWidthSelection] = field(default_factory=list) manual_weights_bit_width_selection_list: List[ManualWeightsBitWidthSelection] = field(default_factory=list) @@ -69,14 +68,16 @@ def set_manual_activation_bit_width(self, bit_widths (Union[List[int], int]): The bit widths to be applied to the selected nodes. If a single value is given it will be applied to all the filters """ - _, bit_widths, filters = self._expand_to_list_filter_and_bit_width(filters, bit_widths) + if filters is None: + Logger.critical(f"The filters cannot be None.") + _, bit_widths, filters = self._expand_to_list(filters, bit_widths) for bit_width, filter in zip (bit_widths, filters): self.manual_activation_bit_width_selection_list += [ManualBitWidthSelection(filter, bit_width)] def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int], - attrs: Union[List[str], str] + attrs: Union[List[WeightAttrT], WeightAttrT] ): """ Add a manual bit-width selection for weights to the configuration. @@ -84,16 +85,18 @@ def set_manual_weights_bit_width(self, Args: filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. bit_widths (Union[List[int], int]): The bit widths for specified by attrs to be applied to the selected nodes. - attrs (Union[List[str], str]): The filtered node's attributes to apply bit-width manipulation to. + attrs (Union[List[WeightAttrT], WeightAttrT]): The filtered node's attributes to apply bit-width manipulation to. If a single value is given it will be applied to all the filters """ - attrs, bit_widths, filters = self._expand_to_list_filter_and_bit_width(filters, bit_widths, attrs) + if filters is None: + Logger.critical(f"The filters cannot be None.") + attrs, bit_widths, filters = self._expand_to_list(filters, bit_widths, attrs) for attr, bit_width, filter in zip (attrs, bit_widths, filters): self.manual_weights_bit_width_selection_list += [ManualWeightsBitWidthSelection(filter, bit_width, attr)] def get_nodes_to_manipulate_activation_bit_widths(self, graph: Graph) -> Dict: """ - Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. + Retrieve nodes from the graph that need their bit-widths for activation changed according to the manual bit-width selections. Args: graph (Graph): The graph containing the nodes to be filtered and manipulated. @@ -102,12 +105,11 @@ def get_nodes_to_manipulate_activation_bit_widths(self, graph: Graph) -> Dict: Dict: A dictionary mapping nodes to their new bit-widths. """ activation_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_activation_bit_width_selection_list) - return activation_nodes_to_change_bit_width def get_nodes_to_manipulate_weights_bit_widths(self, graph: Graph) -> Dict: """ - Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. + Retrieve nodes from the graph that need their bit-widths for weights changed according to the manual bit-width selections. Args: graph (Graph): The graph containing the nodes to be filtered and manipulated. @@ -116,15 +118,22 @@ def get_nodes_to_manipulate_weights_bit_widths(self, graph: Graph) -> Dict: Dict: A dictionary mapping nodes to their new bit-widths. """ weights_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_weights_bit_width_selection_list) - return weights_nodes_to_change_bit_width + @staticmethod + def _expand_to_list_core( + filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], + vals: Union[List[Union[WeightAttrT, int]], Union[WeightAttrT, int]]) -> list: + """ + Extend the length of vals to match the length of filters. + Args: + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + vals Union[List[Union[WeightAttrT, int], Union[WeightAttrT, int]]]): The bit widths or The filtered node's attributes. - def _expand_to_list_core( - self, - filters: Union[List[BaseNodeMatcher]], - vals: Union[List[any], any]): + Returns: + list: Extended vals to match the length of filters. + """ vals = [vals] if not isinstance(vals, list) else vals if len(vals) > 1 and len(vals) != len(filters): Logger.critical(f"Configuration Error: The number of provided bit_width values {len(vals)} " @@ -134,18 +143,39 @@ def _expand_to_list_core( vals = [vals[0] for f in filters] return vals - def _expand_to_list_filter_and_bit_width( - self, + @staticmethod + def _expand_to_list( filters: Union[List[BaseNodeMatcher]], bit_widths: Union[List[int], int], - attrs: Union[List[str], str] = None): - filters = [filters] if not isinstance(filters, list) else filters - bit_widths = self._expand_to_list_core(filters, bit_widths) - attrs = self._expand_to_list_core(filters, attrs) + attrs: Union[List[WeightAttrT], WeightAttrT] = None) -> [List]: + """ + Extend the length of filters, bit-widths and The filtered node's attributes to match the length of filters. + + Args: + filters (Union[List[BaseNodeMatcher], BaseNodeMatcher]): The filters used to select nodes for bit-width manipulation. + bit_widths (Union[List[int], int]): The bit widths for specified by attrs to be applied to the selected nodes. + attrs (Union[List[WeightAttrT], WeightAttrT]): The filtered node's attributes to apply bit-width manipulation to. + Returns: + [List]: A List of extended input arguments. + """ + filters = [filters] if not isinstance(filters, list) else filters + bit_widths = BitWidthConfig._expand_to_list_core(filters, bit_widths) + if attrs is not None: + attrs = BitWidthConfig._expand_to_list_core(filters, attrs) return attrs, bit_widths, filters - def _construct_node_to_new_bit_mapping(self, graph, manual_bit_width_selection_list): + @staticmethod + def _construct_node_to_new_bit_mapping(graph, manual_bit_width_selection_list) -> Dict: + """ + Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. + + Args: + graph (Graph): The graph containing the nodes to be filtered and manipulated. + + Returns: + Dict: A dictionary retrieved nodes from the graph. + """ unit_nodes_to_change_bit_width = {} for manual_bit_width_selection in manual_bit_width_selection_list: filtered_nodes = graph.filter(manual_bit_width_selection.filter) @@ -154,15 +184,23 @@ def _construct_node_to_new_bit_mapping(self, graph, manual_bit_width_selection_l f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " f"to change their bit width to {manual_bit_width_selection.bit_width}.") for n in filtered_nodes: - if n.get_node_weights_attributes() is False: - Logger.critical(f'The requested attribute to change the bit width for {n} is not existing.') - # check if a manual configuration exists for this node - if n in unit_nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}. A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - if isinstance(manual_bit_width_selection_list, ManualBitWidthSelection): + if type(manual_bit_width_selection) is ManualBitWidthSelection: + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}." + f"A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) - elif isinstance(manual_bit_width_selection_list, ManualWeightsBitWidthSelection): + elif type(manual_bit_width_selection) is ManualWeightsBitWidthSelection: + if len(n.get_node_weights_attributes()) == 0: + Logger.critical(f'The requested attribute to change the bit width for {n} is not existing.') + + if n in unit_nodes_to_change_bit_width: + if unit_nodes_to_change_bit_width[n][1] == manual_bit_width_selection.attr: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}." + f"A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]}) - + else: + Logger.critical(f'The type of manual_bit_width_selection_list must be ManualBitWidthSelection or ManualWeightsBitWidthSelection.') return unit_nodes_to_change_bit_width \ No newline at end of file diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_activation_and_weights_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py similarity index 100% rename from tests_pytest/common_tests/core/common/quantization/test_manual_activation_and_weights_bitwidth_selection.py rename to tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py From 8ee3002ff711e09c84c75f9d66ee7aeef26264d9 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 19 Mar 2025 17:46:57 +0900 Subject: [PATCH 25/30] Correcting comments on pull requests_2 --- .../test_manual_bitwidth_selection.py | 228 +++++++++--------- 1 file changed, 116 insertions(+), 112 deletions(-) diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py index 4ae6e88fe..b312d1e8c 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py @@ -8,7 +8,8 @@ from model_compression_toolkit.core.common.graph.edge import Edge from tests_pytest.test_util.graph_builder_utils import build_node -from model_compression_toolkit.target_platform_capabilities.constants import KERNEL_ATTR, BIAS_ATTR +TEST_KERNEL = 'kernel' +TEST_BIAS = 'bias' ### dummy layer classes class Conv2D: @@ -29,11 +30,11 @@ class Dense: ### test model def get_test_graph(): n1 = build_node('input', layer_class=InputLayer) - conv1 = build_node('conv1', layer_class=Conv2D) + conv1 = build_node('conv1', layer_class=Conv2D, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) add1 = build_node('add1', layer_class=Add) conv2 = build_node('conv2', layer_class=Conv2D) bn1 = build_node('bn1', layer_class=BatchNormalization) - relu = build_node('relu1', layer_class=ReLU) + relu = build_node('relu1', layer_class=ReLU, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) add2 = build_node('add2', layer_class=Add) flatten = build_node('flatten', layer_class=Flatten) fc = build_node('fc', layer_class=Dense) @@ -55,41 +56,25 @@ def get_test_graph(): return graph class TestBitWidthConfig: - # test case - setter_test_input_0 = {"activation": (None, None), - "weights": (None, None, None)} - setter_test_input_1 = {"activation": (NodeTypeFilter(ReLU), [16]), - "weights": (None, None, None)} - setter_test_input_2 = {"activation": (None, None), - "weights": (NodeNameFilter("conv2"), [8], KERNEL_ATTR)} - setter_test_input_3 = {"activation": (NodeTypeFilter(ReLU), [16]), - "weights": (NodeNameFilter("conv2"), [8], KERNEL_ATTR)} - setter_test_input_4 = {"activation": ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]), - "weights": ([NodeTypeFilter(Conv2D), NodeNameFilter("fc")], [16, 2], [KERNEL_ATTR, BIAS_ATTR])} - - setter_test_expected_0 = {"activation": (None, None), - "weights": (None, None, None)} - setter_test_expected_1 = {"activation": ([NodeTypeFilter, ReLU, 16]), - "weights": (None, None, None)} - setter_test_expected_2 = {"activation": (None, None), - "weights": ([NodeNameFilter, "conv2", 8, KERNEL_ATTR]) } - setter_test_expected_3 = {"activation": ([NodeTypeFilter, ReLU, 16]), - "weights": ([NodeNameFilter, "conv2", 8, KERNEL_ATTR])} - setter_test_expected_4 = {"activation": ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]), - "weights": ([NodeTypeFilter, Conv2D, 16, KERNEL_ATTR], [NodeNameFilter, "fc", 2, BIAS_ATTR])} - - - # test : BitWidthConfig set_manual_activation_bit_width, set_manual_weights_bit_width + # test case for set_manual_activation_bit_width + test_input_0 = (None, None) + test_input_1 = (NodeTypeFilter(ReLU), 16) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16]) + test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) + + test_expected_0 = ("The filters cannot be None.", None) + test_expected_1 = (NodeTypeFilter, ReLU, 16) + test_expected_2 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 16]) + test_expected_3 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]) + @pytest.mark.parametrize(("inputs", "expected"), [ - (setter_test_input_0, setter_test_expected_0), - (setter_test_input_1, setter_test_expected_1), - (setter_test_input_2, setter_test_expected_2), - (setter_test_input_3, setter_test_expected_3), - (setter_test_input_4, setter_test_expected_4), + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + (test_input_3, test_expected_3), ]) - def test_bit_width_config_setter(self, inputs, expected): - - def check_param(mb_cfg, exp): + def test_set_manual_activation_bit_width(self, inputs, expected): + def check_param_for_activation(mb_cfg, exp): ### check setting config class (expected ManualBitWidthSelection) assert type(mb_cfg) == ManualBitWidthSelection @@ -106,8 +91,40 @@ def check_param(mb_cfg, exp): else: assert mb_cfg.filter is None - def check_param_for_weights(mb_cfg, exp): - ### check setting config class (expected ManualBitWidthSelection) + manual_bit_cfg = BitWidthConfig() + try: + manual_bit_cfg.set_manual_activation_bit_width(inputs[0], inputs[1]) + ### check Activation + if len(manual_bit_cfg.manual_activation_bit_width_selection_list) == 1: + for a_mb_cfg in manual_bit_cfg.manual_activation_bit_width_selection_list: + print(a_mb_cfg, expected) + check_param_for_activation(a_mb_cfg, expected) + else: + for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_activation_bit_width_selection_list): + check_param_for_activation(a_mb_cfg, expected[idx]) + except Exception as e: + assert str(e) == expected[0] + + # test case for set_manual_weights_bit_width + test_input_0 = (None, None, None) + test_input_1 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16], [TEST_KERNEL]) + test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) + + test_expected_0 = ("The filters cannot be None.", None, None) + test_expected_1 = (NodeTypeFilter, ReLU, 16, TEST_KERNEL) + test_expected_2 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 16, TEST_KERNEL]) + test_expected_3 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 8, TEST_BIAS]) + + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + (test_input_3, test_expected_3), + ]) + def test_set_manual_weights_bit_width(self, inputs, expected): + def check_param_weights(mb_cfg, exp): + ### check setting config class (expected ManualWeightsBitWidthSelection) assert type(mb_cfg) == ManualWeightsBitWidthSelection ### check setting filter for NodeFilter and NodeInfo @@ -118,94 +135,81 @@ def check_param_for_weights(mb_cfg, exp): elif isinstance(mb_cfg.filter, NodeNameFilter): assert mb_cfg.filter.node_name == exp[1] - ### check setting bit_width + ### check setting bit_width and attr assert mb_cfg.bit_width == exp[2] assert mb_cfg.attr == exp[3] else: assert mb_cfg.filter is None - activation = inputs["activation"] - weights = inputs["weights"] + manual_bit_cfg = BitWidthConfig() + try: + manual_bit_cfg.set_manual_weights_bit_width(inputs[0], inputs[1], inputs[2]) + ### check weights + if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: + for a_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: + print(a_mb_cfg, expected) + check_param_weights(a_mb_cfg, expected) + else: + for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): + check_param_weights(a_mb_cfg, expected[idx]) + except Exception as e: + assert str(e) == expected[0] - activation_expected = expected["activation"] - weights_expected = expected["weights"] + # test case for get_nodes_to_manipulate_activation_bit_widths + test_input_0 = (NodeTypeFilter(ReLU), 16) + test_input_1 = (NodeNameFilter('relu1'), 16) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) - manual_bit_cfg = BitWidthConfig() + test_expected_0 = ({"ReLU:relu1": 16}) + test_expected_1 = ({"ReLU:relu1": 16}) + test_expected_2 = ({"ReLU:relu1": 16, "Conv2D:conv1": 8}) - manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) - manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1], weights[2]) - - ### check got object instance - assert isinstance(manual_bit_cfg, BitWidthConfig) - - ### check Activation - if len(manual_bit_cfg.manual_activation_bit_width_selection_list) == 1: - for a_mb_cfg in manual_bit_cfg.manual_activation_bit_width_selection_list: - check_param(a_mb_cfg, activation_expected) - else: - for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_activation_bit_width_selection_list): - check_param(a_mb_cfg, activation_expected[idx]) - - ### check Weights - if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: - for w_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: - check_param_for_weights(w_mb_cfg, weights_expected) - else: - for idx, w_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): - check_param_for_weights(w_mb_cfg, weights_expected[idx]) - - - ### test case - ### Note: setter inputs reuse getters test inputs - getter_test_expected_0 = {"activation":{}, - "weights": {}} - getter_test_expected_1 = {"activation":{"ReLU:relu1": 16}, - "weights": {}} - getter_test_expected_2 = {"activation":{}, - "weights": {"Conv2D:conv2": [8, KERNEL_ATTR]}} - getter_test_expected_3 = {"activation": {"ReLU:relu1": 16}, - "weights": {"Conv2D:conv2": [8, KERNEL_ATTR]}} - getter_test_expected_4 = {"activation": {"ReLU:relu1": 16, "Conv2D:conv1": 8}, - "weights": {"Conv2D:conv1": [16, KERNEL_ATTR], "Conv2D:conv2": [16, KERNEL_ATTR], "Dense:fc": [2, BIAS_ATTR]}} - - # test : BitWidthConfig get_nodes_to_manipulate_bit_widths @pytest.mark.parametrize(("inputs", "expected"), [ - (setter_test_input_0, getter_test_expected_0), - (setter_test_input_1, getter_test_expected_1), - (setter_test_input_2, getter_test_expected_2), - (setter_test_input_3, getter_test_expected_3), - (setter_test_input_4, getter_test_expected_4), + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), ]) - def test_bit_width_config_getter(self, inputs, expected): + def test_get_nodes_to_manipulate_activation_bit_widths(self, inputs, expected): + fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] + bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] + + mbws_config = [] + for fl, bw in zip(fl_list, bw_list): + mbws_config.append(ManualBitWidthSelection(fl, bw)) + manual_bit_cfg = BitWidthConfig(manual_activation_bit_width_selection_list=mbws_config) graph = get_test_graph() + get_manual_bit_dict_activation = manual_bit_cfg.get_nodes_to_manipulate_activation_bit_widths(graph) + for idx, (key, val) in enumerate(get_manual_bit_dict_activation.items()): + assert str(key) == list(expected.keys())[idx] + assert val == list(expected.values())[idx] - activation = inputs["activation"] - weights = inputs["weights"] + # test case for get_nodes_to_manipulate_weights_bit_widths + test_input_0 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) + test_input_1 = (NodeNameFilter('relu1'), 16, TEST_BIAS) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) - activation_expected = expected["activation"] - weights_expected = expected["weights"] + test_expected_0 = ({"ReLU:relu1": [16, TEST_KERNEL]}) + test_expected_1 = ({"ReLU:relu1": [16, TEST_BIAS]}) + test_expected_2 = ({"ReLU:relu1": [16, TEST_KERNEL], "Conv2D:conv1": [8, TEST_BIAS]}) - manual_bit_cfg = BitWidthConfig() - if activation[0] is not None: - manual_bit_cfg.set_manual_activation_bit_width(activation[0], activation[1]) - if weights[0] is not None: - manual_bit_cfg.set_manual_weights_bit_width(weights[0], weights[1], weights[2]) + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + ]) + def test_get_nodes_to_manipulate_weights_bit_widths(self, inputs, expected): + fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] + bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] + at_list = inputs[2] if isinstance(inputs[2], list) else [inputs[2]] - get_manual_bit_dict_activation = manual_bit_cfg.get_nodes_to_manipulate_activation_bit_widths(graph) - get_manual_bit_dict_weights = manual_bit_cfg.get_nodes_to_manipulate_weights_bit_widths(graph) + manual_weights_bit_width_config = [] + for fl, bw, at in zip(fl_list, bw_list, at_list): + manual_weights_bit_width_config.append(ManualWeightsBitWidthSelection(fl, bw, at)) + manual_bit_cfg = BitWidthConfig(manual_weights_bit_width_selection_list=manual_weights_bit_width_config) - if activation[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict_activation.items()): - assert str(key) == list(activation_expected.keys())[idx] - assert val == list(activation_expected.values())[idx] - else: - assert get_manual_bit_dict_activation == activation_expected - - if weights[0] is not None: - for idx, (key, val) in enumerate(get_manual_bit_dict_weights.items()): - assert str(key) == list(weights_expected.keys())[idx] - assert val[0] == list(weights_expected.values())[idx][0] - assert val[1] == list(weights_expected.values())[idx][1] - else: - assert get_manual_bit_dict_weights == weights_expected + graph = get_test_graph() + get_manual_bit_dict_weights = manual_bit_cfg.get_nodes_to_manipulate_weights_bit_widths(graph) + for idx, (key, val) in enumerate(get_manual_bit_dict_weights.items()): + assert str(key) == list(expected.keys())[idx] + assert val == list(expected.values())[idx] From 2a3c40cd9f171e1fef57fcea789a65edf7d9da46 Mon Sep 17 00:00:00 2001 From: gouda-youichi Date: Wed, 19 Mar 2025 18:39:21 +0900 Subject: [PATCH 26/30] Fixed comments on pull requests. --- .../core/common/quantization/bit_width_config.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 0aaa0d7fd..66fc39223 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -58,8 +58,8 @@ class BitWidthConfig: manual_weights_bit_width_selection_list: List[ManualWeightsBitWidthSelection] = field(default_factory=list) def set_manual_activation_bit_width(self, - filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], - bit_widths: Union[List[int], int]): + filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], + bit_widths: Union[List[int], int]): """ Add a manual bit-width selection for activation to the configuration. @@ -77,8 +77,7 @@ def set_manual_activation_bit_width(self, def set_manual_weights_bit_width(self, filters: Union[List[BaseNodeMatcher], BaseNodeMatcher], bit_widths: Union[List[int], int], - attrs: Union[List[WeightAttrT], WeightAttrT] - ): + attrs: Union[List[WeightAttrT], WeightAttrT]): """ Add a manual bit-width selection for weights to the configuration. @@ -193,7 +192,7 @@ def _construct_node_to_new_bit_mapping(graph, manual_bit_width_selection_list) - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) elif type(manual_bit_width_selection) is ManualWeightsBitWidthSelection: if len(n.get_node_weights_attributes()) == 0: - Logger.critical(f'The requested attribute to change the bit width for {n} is not existing.') + Logger.critical(f'The requested attribute to change the bit width for {n} does not exist.') if n in unit_nodes_to_change_bit_width: if unit_nodes_to_change_bit_width[n][1] == manual_bit_width_selection.attr: From 7ba5a179f0e1b83982e89f49f73bbfb194160ecd Mon Sep 17 00:00:00 2001 From: kawakami-masaki0 Date: Mon, 24 Mar 2025 17:26:35 +0900 Subject: [PATCH 27/30] fixing for manual weights selection bitwidth --- .../common/quantization/bit_width_config.py | 76 ++- .../set_node_quantization_config.py | 2 +- .../test_manual_bitwidth_selection.py | 440 +++++++++--------- 3 files changed, 279 insertions(+), 239 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index 66fc39223..f148de0d4 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -103,7 +103,7 @@ def get_nodes_to_manipulate_activation_bit_widths(self, graph: Graph) -> Dict: Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - activation_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_activation_bit_width_selection_list) + activation_nodes_to_change_bit_width = self._construct_node_to_new_activation_bit_mapping(graph) return activation_nodes_to_change_bit_width def get_nodes_to_manipulate_weights_bit_widths(self, graph: Graph) -> Dict: @@ -116,7 +116,7 @@ def get_nodes_to_manipulate_weights_bit_widths(self, graph: Graph) -> Dict: Returns: Dict: A dictionary mapping nodes to their new bit-widths. """ - weights_nodes_to_change_bit_width = self._construct_node_to_new_bit_mapping(graph, self.manual_weights_bit_width_selection_list) + weights_nodes_to_change_bit_width = self._construct_node_to_new_weights_bit_mapping(graph) return weights_nodes_to_change_bit_width @staticmethod @@ -164,10 +164,9 @@ def _expand_to_list( attrs = BitWidthConfig._expand_to_list_core(filters, attrs) return attrs, bit_widths, filters - @staticmethod - def _construct_node_to_new_bit_mapping(graph, manual_bit_width_selection_list) -> Dict: + def _construct_node_to_new_activation_bit_mapping(self, graph) -> Dict: """ - Retrieve nodes from the graph that need their bit-widths changed according to the manual bit-width selections. + Retrieve nodes from the graph that need their activation bit-widths changed according to the manual bit-width selections. Args: graph (Graph): The graph containing the nodes to be filtered and manipulated. @@ -176,30 +175,61 @@ def _construct_node_to_new_bit_mapping(graph, manual_bit_width_selection_list) - Dict: A dictionary retrieved nodes from the graph. """ unit_nodes_to_change_bit_width = {} - for manual_bit_width_selection in manual_bit_width_selection_list: + for manual_bit_width_selection in self.manual_activation_bit_width_selection_list: filtered_nodes = graph.filter(manual_bit_width_selection.filter) if len(filtered_nodes) == 0: Logger.critical( f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " f"to change their bit width to {manual_bit_width_selection.bit_width}.") for n in filtered_nodes: - if type(manual_bit_width_selection) is ManualBitWidthSelection: - # check if a manual configuration exists for this node - if n in unit_nodes_to_change_bit_width: - Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}." - f"A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) - elif type(manual_bit_width_selection) is ManualWeightsBitWidthSelection: - if len(n.get_node_weights_attributes()) == 0: - Logger.critical(f'The requested attribute to change the bit width for {n} does not exist.') - - if n in unit_nodes_to_change_bit_width: - if unit_nodes_to_change_bit_width[n][1] == manual_bit_width_selection.attr: + # check if a manual configuration exists for this node + if n in unit_nodes_to_change_bit_width: + Logger.info( + f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}." + f"A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") + unit_nodes_to_change_bit_width.update({n: manual_bit_width_selection.bit_width}) + return unit_nodes_to_change_bit_width + + def _construct_node_to_new_weights_bit_mapping(self, graph) -> Dict: + """ + Retrieve nodes from the graph that need their weights bit-widths changed according to the manual bit-width selections. + + Args: + graph (Graph): The graph containing the nodes to be filtered and manipulated. + + Returns: + Dict: A dictionary retrieved nodes from the graph. + """ + unit_nodes_to_change_bit_width = {} + + for manual_bit_width_selection in self.manual_weights_bit_width_selection_list: + filtered_nodes = graph.filter(manual_bit_width_selection.filter) + if len(filtered_nodes) == 0: + Logger.critical( + f"Node Filtering Error: No nodes found in the graph for filter {manual_bit_width_selection.filter.__dict__} " + f"to change their bit width to {manual_bit_width_selection.bit_width}.") + + for n in filtered_nodes: + attr_to_change_bit_width = [] + + attrs_str = n.get_node_weights_attributes() + if len(attrs_str) == 0: + Logger.critical(f'The requested attribute {manual_bit_width_selection.attr} to change the bit width for {n} does not exist.') + + attr = [attr_str for attr_str in attrs_str if attr_str.find(manual_bit_width_selection.attr) != -1] + if len(attr) == 0: + Logger.critical(f'The requested attribute {manual_bit_width_selection.attr} to change the bit width for {n} does not exist.') + + if n in unit_nodes_to_change_bit_width: + attr_to_change_bit_width = unit_nodes_to_change_bit_width[n] + for i, attr_to_bitwidth in enumerate(attr_to_change_bit_width): + if attr_to_bitwidth[1] == manual_bit_width_selection.attr: + del attr_to_change_bit_width[i] Logger.info( - f"Node {n} has an existing manual bit width configuration of {unit_nodes_to_change_bit_width.get(n)}." + f"Node {n} has an existing manual bit width configuration of {manual_bit_width_selection.attr}." f"A new manual configuration request of {manual_bit_width_selection.bit_width} has been received, and the previous value is being overridden.") - unit_nodes_to_change_bit_width.update({n: [manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]}) - else: - Logger.critical(f'The type of manual_bit_width_selection_list must be ManualBitWidthSelection or ManualWeightsBitWidthSelection.') + + attr_to_change_bit_width.append([manual_bit_width_selection.bit_width, manual_bit_width_selection.attr]) + unit_nodes_to_change_bit_width.update({n: attr_to_change_bit_width}) + return unit_nodes_to_change_bit_width \ No newline at end of file diff --git a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py index 7359cdf1c..5c79fe9a3 100644 --- a/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py +++ b/model_compression_toolkit/core/common/quantization/set_node_quantization_config.py @@ -65,7 +65,7 @@ def set_quantization_configuration_to_graph(graph: Graph, Logger.warning("Using the HMSE error method for weights quantization parameters search. " "Note: This method may significantly increase runtime during the parameter search process.") - nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_bit_widths(graph) + nodes_to_manipulate_bit_widths = {} if bit_width_config is None else bit_width_config.get_nodes_to_manipulate_activation_bit_widths(graph) for n in graph.nodes: set_quantization_configs_to_node(node=n, diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py index b312d1e8c..0dc04d6d7 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py @@ -1,215 +1,225 @@ -import pytest - -from model_compression_toolkit.core.common.network_editors import NodeTypeFilter, NodeNameFilter -from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection, ManualWeightsBitWidthSelection -from model_compression_toolkit.core import BitWidthConfig - -from model_compression_toolkit.core.common import Graph -from model_compression_toolkit.core.common.graph.edge import Edge -from tests_pytest.test_util.graph_builder_utils import build_node - -TEST_KERNEL = 'kernel' -TEST_BIAS = 'bias' - -### dummy layer classes -class Conv2D: - pass -class InputLayer: - pass -class Add: - pass -class BatchNormalization: - pass -class ReLU: - pass -class Flatten: - pass -class Dense: - pass - -### test model -def get_test_graph(): - n1 = build_node('input', layer_class=InputLayer) - conv1 = build_node('conv1', layer_class=Conv2D, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) - add1 = build_node('add1', layer_class=Add) - conv2 = build_node('conv2', layer_class=Conv2D) - bn1 = build_node('bn1', layer_class=BatchNormalization) - relu = build_node('relu1', layer_class=ReLU, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) - add2 = build_node('add2', layer_class=Add) - flatten = build_node('flatten', layer_class=Flatten) - fc = build_node('fc', layer_class=Dense) - - graph = Graph('g', input_nodes=[n1], - nodes=[conv1,add1, conv2, bn1, relu, add2, flatten], - output_nodes=[fc], - edge_list=[Edge(n1, conv1, 0, 0), - Edge(conv1, add1, 0, 0), - Edge(add1, conv2, 0, 0), - Edge(conv2, bn1, 0, 0), - Edge(bn1, relu, 0, 0), - Edge(relu, add2, 0, 0), - Edge(add1, add2, 0, 0), - Edge(add2, flatten, 0, 0), - Edge(flatten, fc, 0, 0), - ] - ) - return graph - -class TestBitWidthConfig: - # test case for set_manual_activation_bit_width - test_input_0 = (None, None) - test_input_1 = (NodeTypeFilter(ReLU), 16) - test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16]) - test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) - - test_expected_0 = ("The filters cannot be None.", None) - test_expected_1 = (NodeTypeFilter, ReLU, 16) - test_expected_2 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 16]) - test_expected_3 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]) - - @pytest.mark.parametrize(("inputs", "expected"), [ - (test_input_0, test_expected_0), - (test_input_1, test_expected_1), - (test_input_2, test_expected_2), - (test_input_3, test_expected_3), - ]) - def test_set_manual_activation_bit_width(self, inputs, expected): - def check_param_for_activation(mb_cfg, exp): - ### check setting config class (expected ManualBitWidthSelection) - assert type(mb_cfg) == ManualBitWidthSelection - - ### check setting filter for NodeFilter and NodeInfo - if mb_cfg.filter is not None: - assert isinstance(mb_cfg.filter, exp[0]) - if isinstance(mb_cfg.filter, NodeTypeFilter): - assert mb_cfg.filter.node_type == exp[1] - elif isinstance(mb_cfg.filter, NodeNameFilter): - assert mb_cfg.filter.node_name == exp[1] - - ### check setting bit_width - assert mb_cfg.bit_width == exp[2] - else: - assert mb_cfg.filter is None - - manual_bit_cfg = BitWidthConfig() - try: - manual_bit_cfg.set_manual_activation_bit_width(inputs[0], inputs[1]) - ### check Activation - if len(manual_bit_cfg.manual_activation_bit_width_selection_list) == 1: - for a_mb_cfg in manual_bit_cfg.manual_activation_bit_width_selection_list: - print(a_mb_cfg, expected) - check_param_for_activation(a_mb_cfg, expected) - else: - for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_activation_bit_width_selection_list): - check_param_for_activation(a_mb_cfg, expected[idx]) - except Exception as e: - assert str(e) == expected[0] - - # test case for set_manual_weights_bit_width - test_input_0 = (None, None, None) - test_input_1 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) - test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16], [TEST_KERNEL]) - test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) - - test_expected_0 = ("The filters cannot be None.", None, None) - test_expected_1 = (NodeTypeFilter, ReLU, 16, TEST_KERNEL) - test_expected_2 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 16, TEST_KERNEL]) - test_expected_3 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 8, TEST_BIAS]) - - @pytest.mark.parametrize(("inputs", "expected"), [ - (test_input_0, test_expected_0), - (test_input_1, test_expected_1), - (test_input_2, test_expected_2), - (test_input_3, test_expected_3), - ]) - def test_set_manual_weights_bit_width(self, inputs, expected): - def check_param_weights(mb_cfg, exp): - ### check setting config class (expected ManualWeightsBitWidthSelection) - assert type(mb_cfg) == ManualWeightsBitWidthSelection - - ### check setting filter for NodeFilter and NodeInfo - if mb_cfg.filter is not None: - assert isinstance(mb_cfg.filter, exp[0]) - if isinstance(mb_cfg.filter, NodeTypeFilter): - assert mb_cfg.filter.node_type == exp[1] - elif isinstance(mb_cfg.filter, NodeNameFilter): - assert mb_cfg.filter.node_name == exp[1] - - ### check setting bit_width and attr - assert mb_cfg.bit_width == exp[2] - assert mb_cfg.attr == exp[3] - else: - assert mb_cfg.filter is None - - manual_bit_cfg = BitWidthConfig() - try: - manual_bit_cfg.set_manual_weights_bit_width(inputs[0], inputs[1], inputs[2]) - ### check weights - if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: - for a_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: - print(a_mb_cfg, expected) - check_param_weights(a_mb_cfg, expected) - else: - for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): - check_param_weights(a_mb_cfg, expected[idx]) - except Exception as e: - assert str(e) == expected[0] - - # test case for get_nodes_to_manipulate_activation_bit_widths - test_input_0 = (NodeTypeFilter(ReLU), 16) - test_input_1 = (NodeNameFilter('relu1'), 16) - test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) - - test_expected_0 = ({"ReLU:relu1": 16}) - test_expected_1 = ({"ReLU:relu1": 16}) - test_expected_2 = ({"ReLU:relu1": 16, "Conv2D:conv1": 8}) - - @pytest.mark.parametrize(("inputs", "expected"), [ - (test_input_0, test_expected_0), - (test_input_1, test_expected_1), - (test_input_2, test_expected_2), - ]) - def test_get_nodes_to_manipulate_activation_bit_widths(self, inputs, expected): - fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] - bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] - - mbws_config = [] - for fl, bw in zip(fl_list, bw_list): - mbws_config.append(ManualBitWidthSelection(fl, bw)) - manual_bit_cfg = BitWidthConfig(manual_activation_bit_width_selection_list=mbws_config) - - graph = get_test_graph() - get_manual_bit_dict_activation = manual_bit_cfg.get_nodes_to_manipulate_activation_bit_widths(graph) - for idx, (key, val) in enumerate(get_manual_bit_dict_activation.items()): - assert str(key) == list(expected.keys())[idx] - assert val == list(expected.values())[idx] - - # test case for get_nodes_to_manipulate_weights_bit_widths - test_input_0 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) - test_input_1 = (NodeNameFilter('relu1'), 16, TEST_BIAS) - test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) - - test_expected_0 = ({"ReLU:relu1": [16, TEST_KERNEL]}) - test_expected_1 = ({"ReLU:relu1": [16, TEST_BIAS]}) - test_expected_2 = ({"ReLU:relu1": [16, TEST_KERNEL], "Conv2D:conv1": [8, TEST_BIAS]}) - - @pytest.mark.parametrize(("inputs", "expected"), [ - (test_input_0, test_expected_0), - (test_input_1, test_expected_1), - (test_input_2, test_expected_2), - ]) - def test_get_nodes_to_manipulate_weights_bit_widths(self, inputs, expected): - fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] - bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] - at_list = inputs[2] if isinstance(inputs[2], list) else [inputs[2]] - - manual_weights_bit_width_config = [] - for fl, bw, at in zip(fl_list, bw_list, at_list): - manual_weights_bit_width_config.append(ManualWeightsBitWidthSelection(fl, bw, at)) - manual_bit_cfg = BitWidthConfig(manual_weights_bit_width_selection_list=manual_weights_bit_width_config) - - graph = get_test_graph() - get_manual_bit_dict_weights = manual_bit_cfg.get_nodes_to_manipulate_weights_bit_widths(graph) - for idx, (key, val) in enumerate(get_manual_bit_dict_weights.items()): - assert str(key) == list(expected.keys())[idx] - assert val == list(expected.values())[idx] +import pytest + +from model_compression_toolkit.core.common.network_editors import NodeTypeFilter, NodeNameFilter +from model_compression_toolkit.core.common.quantization.bit_width_config import ManualBitWidthSelection, ManualWeightsBitWidthSelection +from model_compression_toolkit.core import BitWidthConfig + +from model_compression_toolkit.core.common import Graph +from model_compression_toolkit.core.common.graph.edge import Edge +from tests_pytest.test_util.graph_builder_utils import build_node + + +TEST_KERNEL = 'kernel' +TEST_BIAS = 'bias' + +### dummy layer classes +class Conv2D: + pass +class InputLayer: + pass +class Add: + pass +class BatchNormalization: + pass +class ReLU: + pass +class Flatten: + pass +class Dense: + pass + + +### test model +def get_test_graph(): + n1 = build_node('input', layer_class=InputLayer) + conv1 = build_node('conv1', layer_class=Conv2D, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) + add1 = build_node('add1', layer_class=Add) + conv2 = build_node('conv2', layer_class=Conv2D) + bn1 = build_node('bn1', layer_class=BatchNormalization) + relu = build_node('relu1', layer_class=ReLU, canonical_weights={TEST_KERNEL: [1,2], TEST_BIAS: [3,4]}) + add2 = build_node('add2', layer_class=Add) + flatten = build_node('flatten', layer_class=Flatten) + fc = build_node('fc', layer_class=Dense) + + graph = Graph('g', input_nodes=[n1], + nodes=[conv1,add1, conv2, bn1, relu, add2, flatten], + output_nodes=[fc], + edge_list=[Edge(n1, conv1, 0, 0), + Edge(conv1, add1, 0, 0), + Edge(add1, conv2, 0, 0), + Edge(conv2, bn1, 0, 0), + Edge(bn1, relu, 0, 0), + Edge(relu, add2, 0, 0), + Edge(add1, add2, 0, 0), + Edge(add2, flatten, 0, 0), + Edge(flatten, fc, 0, 0), + ] + ) + return graph + + +class TestBitWidthConfig: + # test case for set_manual_activation_bit_width + test_input_0 = (None, None) + test_input_1 = (NodeTypeFilter(ReLU), 16) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16]) + test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) + + test_expected_0 = ("The filters cannot be None.", None) + test_expected_1 = (NodeTypeFilter, ReLU, 16) + test_expected_2 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 16]) + test_expected_3 = ([NodeTypeFilter, ReLU, 16], [NodeNameFilter, "conv1", 8]) + + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + (test_input_3, test_expected_3), + ]) + def test_set_manual_activation_bit_width(self, inputs, expected): + def check_param_for_activation(mb_cfg, exp): + ### check setting config class (expected ManualBitWidthSelection) + assert type(mb_cfg) == ManualBitWidthSelection + + ### check setting filter for NodeFilter and NodeInfo + if mb_cfg.filter is not None: + assert isinstance(mb_cfg.filter, exp[0]) + + if isinstance(mb_cfg.filter, NodeTypeFilter): + assert mb_cfg.filter.node_type == exp[1] + elif isinstance(mb_cfg.filter, NodeNameFilter): + assert mb_cfg.filter.node_name == exp[1] + + ### check setting bit_width + assert mb_cfg.bit_width == exp[2] + else: + assert mb_cfg.filter is None + + manual_bit_cfg = BitWidthConfig() + try: + manual_bit_cfg.set_manual_activation_bit_width(inputs[0], inputs[1]) + ### check Activation + if len(manual_bit_cfg.manual_activation_bit_width_selection_list) == 1: + for a_mb_cfg in manual_bit_cfg.manual_activation_bit_width_selection_list: + print(a_mb_cfg, expected) + check_param_for_activation(a_mb_cfg, expected) + else: + for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_activation_bit_width_selection_list): + check_param_for_activation(a_mb_cfg, expected[idx]) + except Exception as e: + assert str(e) == expected[0] + + + # test case for set_manual_weights_bit_width + test_input_0 = (None, None, None) + test_input_1 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16], [TEST_KERNEL]) + test_input_3 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) + + test_expected_0 = ("The filters cannot be None.", None, None) + test_expected_1 = (NodeTypeFilter, ReLU, 16, TEST_KERNEL) + test_expected_2 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 16, TEST_KERNEL]) + test_expected_3 = ([NodeTypeFilter, ReLU, 16, TEST_KERNEL], [NodeNameFilter, "conv1", 8, TEST_BIAS]) + + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + (test_input_3, test_expected_3), + ]) + def test_set_manual_weights_bit_width(self, inputs, expected): + def check_param_weights(mb_cfg, exp): + ### check setting config class (expected ManualWeightsBitWidthSelection) + assert type(mb_cfg) == ManualWeightsBitWidthSelection + + ### check setting filter for NodeFilter and NodeInfo + if mb_cfg.filter is not None: + assert isinstance(mb_cfg.filter, exp[0]) + if isinstance(mb_cfg.filter, NodeTypeFilter): + assert mb_cfg.filter.node_type == exp[1] + elif isinstance(mb_cfg.filter, NodeNameFilter): + assert mb_cfg.filter.node_name == exp[1] + + ### check setting bit_width and attr + assert mb_cfg.bit_width == exp[2] + assert mb_cfg.attr == exp[3] + else: + assert mb_cfg.filter is None + + manual_bit_cfg = BitWidthConfig() + try: + manual_bit_cfg.set_manual_weights_bit_width(inputs[0], inputs[1], inputs[2]) + ### check weights + if len(manual_bit_cfg.manual_weights_bit_width_selection_list) == 1: + for a_mb_cfg in manual_bit_cfg.manual_weights_bit_width_selection_list: + print(a_mb_cfg, expected) + check_param_weights(a_mb_cfg, expected) + else: + for idx, a_mb_cfg in enumerate(manual_bit_cfg.manual_weights_bit_width_selection_list): + check_param_weights(a_mb_cfg, expected[idx]) + except Exception as e: + assert str(e) == expected[0] + + + # test case for get_nodes_to_manipulate_activation_bit_widths + test_input_0 = (NodeTypeFilter(ReLU), 16) + test_input_1 = (NodeNameFilter('relu1'), 16) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8]) + + test_expected_0 = ({"ReLU:relu1": 16}) + test_expected_1 = ({"ReLU:relu1": 16}) + test_expected_2 = ({"ReLU:relu1": 16, "Conv2D:conv1": 8}) + + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + ]) + def test_get_nodes_to_manipulate_activation_bit_widths(self, inputs, expected): + fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] + bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] + + mbws_config = [] + for fl, bw in zip(fl_list, bw_list): + mbws_config.append(ManualBitWidthSelection(fl, bw)) + manual_bit_cfg = BitWidthConfig(manual_activation_bit_width_selection_list=mbws_config) + + graph = get_test_graph() + get_manual_bit_dict_activation = manual_bit_cfg.get_nodes_to_manipulate_activation_bit_widths(graph) + for idx, (key, val) in enumerate(get_manual_bit_dict_activation.items()): + assert str(key) == list(expected.keys())[idx] + assert val == list(expected.values())[idx] + + + # test case for get_nodes_to_manipulate_weights_bit_widths + test_input_0 = (NodeTypeFilter(ReLU), 16, TEST_KERNEL) + test_input_1 = (NodeNameFilter('relu1'), 16, TEST_BIAS) + test_input_2 = ([NodeTypeFilter(ReLU), NodeNameFilter("conv1")], [16, 8], [TEST_KERNEL, TEST_BIAS]) + test_input_3 = ([NodeNameFilter("conv1"), NodeNameFilter("conv1")], [4, 8], [TEST_KERNEL, TEST_BIAS]) + + test_expected_0 = ({"ReLU:relu1": [[16, TEST_KERNEL]]}) + test_expected_1 = ({"ReLU:relu1": [[16, TEST_BIAS]]}) + test_expected_2 = ({"ReLU:relu1": [[16, TEST_KERNEL]], "Conv2D:conv1": [[8, TEST_BIAS]]}) + test_expected_3 = ({"Conv2D:conv1": [[4, TEST_KERNEL], [8, TEST_BIAS]]}) + + @pytest.mark.parametrize(("inputs", "expected"), [ + (test_input_0, test_expected_0), + (test_input_1, test_expected_1), + (test_input_2, test_expected_2), + (test_input_3, test_expected_3), + ]) + def test_get_nodes_to_manipulate_weights_bit_widths(self, inputs, expected): + fl_list = inputs[0] if isinstance(inputs[0], list) else [inputs[0]] + bw_list = inputs[1] if isinstance(inputs[1], list) else [inputs[1]] + at_list = inputs[2] if isinstance(inputs[2], list) else [inputs[2]] + + manual_weights_bit_width_config = [] + for fl, bw, at in zip(fl_list, bw_list, at_list): + manual_weights_bit_width_config.append(ManualWeightsBitWidthSelection(fl, bw, at)) + manual_bit_cfg = BitWidthConfig(manual_weights_bit_width_selection_list=manual_weights_bit_width_config) + + graph = get_test_graph() + get_manual_bit_dict_weights = manual_bit_cfg.get_nodes_to_manipulate_weights_bit_widths(graph) + for idx, (key, val) in enumerate(get_manual_bit_dict_weights.items()): + assert str(key) == list(expected.keys())[idx] + assert val == list(expected.values())[idx] From afe57b46dff4f97f797ce85e47b940b171fe2533 Mon Sep 17 00:00:00 2001 From: kawakami-masaki0 Date: Mon, 24 Mar 2025 18:20:34 +0900 Subject: [PATCH 28/30] add __init__.py --- tests_pytest/common_tests/core/__init__.py | 14 ++++++++++++++ tests_pytest/common_tests/core/common/__init__.py | 14 ++++++++++++++ .../core/common/quantization/__init__.py | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests_pytest/common_tests/core/__init__.py create mode 100644 tests_pytest/common_tests/core/common/__init__.py create mode 100644 tests_pytest/common_tests/core/common/quantization/__init__.py diff --git a/tests_pytest/common_tests/core/__init__.py b/tests_pytest/common_tests/core/__init__.py new file mode 100644 index 000000000..5397dea24 --- /dev/null +++ b/tests_pytest/common_tests/core/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2025 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. +# ============================================================================== diff --git a/tests_pytest/common_tests/core/common/__init__.py b/tests_pytest/common_tests/core/common/__init__.py new file mode 100644 index 000000000..5397dea24 --- /dev/null +++ b/tests_pytest/common_tests/core/common/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2025 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. +# ============================================================================== diff --git a/tests_pytest/common_tests/core/common/quantization/__init__.py b/tests_pytest/common_tests/core/common/quantization/__init__.py new file mode 100644 index 000000000..5397dea24 --- /dev/null +++ b/tests_pytest/common_tests/core/common/quantization/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2025 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. +# ============================================================================== From c49ba74677bbdb3caa2007b7edd0f0f02001a8cd Mon Sep 17 00:00:00 2001 From: kawakami-masaki0 Date: Mon, 24 Mar 2025 18:27:06 +0900 Subject: [PATCH 29/30] fix imported module --- .../core/common/quantization/test_manual_bitwidth_selection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py index 0dc04d6d7..d5f97c3f3 100755 --- a/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py +++ b/tests_pytest/common_tests/core/common/quantization/test_manual_bitwidth_selection.py @@ -6,7 +6,7 @@ from model_compression_toolkit.core.common import Graph from model_compression_toolkit.core.common.graph.edge import Edge -from tests_pytest.test_util.graph_builder_utils import build_node +from tests_pytest._test_util.graph_builder_utils import build_node TEST_KERNEL = 'kernel' From e7737d619efb4f2e884a039fc69ae1864e25e474 Mon Sep 17 00:00:00 2001 From: kawakami-masaki0 Date: Wed, 26 Mar 2025 10:36:37 +0900 Subject: [PATCH 30/30] fixed to check for integer or string --- .../core/common/quantization/bit_width_config.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/model_compression_toolkit/core/common/quantization/bit_width_config.py b/model_compression_toolkit/core/common/quantization/bit_width_config.py index f148de0d4..77af61ce1 100644 --- a/model_compression_toolkit/core/common/quantization/bit_width_config.py +++ b/model_compression_toolkit/core/common/quantization/bit_width_config.py @@ -211,12 +211,19 @@ def _construct_node_to_new_weights_bit_mapping(self, graph) -> Dict: for n in filtered_nodes: attr_to_change_bit_width = [] - + attrs_str = n.get_node_weights_attributes() if len(attrs_str) == 0: Logger.critical(f'The requested attribute {manual_bit_width_selection.attr} to change the bit width for {n} does not exist.') - attr = [attr_str for attr_str in attrs_str if attr_str.find(manual_bit_width_selection.attr) != -1] + attr = [] + for attr_str in attrs_str: + if isinstance(attr_str, str) and isinstance(manual_bit_width_selection.attr, str): + if attr_str.find(manual_bit_width_selection.attr) != -1: + attr.append(attr_str) + elif isinstance(attr_str, int) and isinstance(manual_bit_width_selection.attr, int): + if attr_str == manual_bit_width_selection.attr: + attr.append(attr_str) if len(attr) == 0: Logger.critical(f'The requested attribute {manual_bit_width_selection.attr} to change the bit width for {n} does not exist.')