From 0b9684360c6ee93085a0057952fdc1624de4ff39 Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Mon, 4 Dec 2023 13:57:22 -0800 Subject: [PATCH 1/9] added notebook and rst --- .DS_Store | Bin 0 -> 6148 bytes docs/.DS_Store | Bin 0 -> 6148 bytes .../sconce_model_compression_pruning.rst | 375 +++++++++++ docs/tutorials/tutorials.rst | 5 + examples/.DS_Store | Bin 0 -> 6148 bytes .../sconce_model_compression_pruning.ipynb | 623 ++++++++++++++++++ 6 files changed, 1003 insertions(+) create mode 100644 .DS_Store create mode 100644 docs/.DS_Store create mode 100644 docs/examples/sconce_model_compression_pruning.rst create mode 100644 examples/.DS_Store create mode 100644 examples/sconce_model_compression_pruning.ipynb diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0=1.20, but you have numpy 1.26.2 which is incompatible. + imageio 2.31.6 requires pillow<10.1.0,>=8.3.2, but you have pillow 10.1.0 which is incompatible. + tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.8.0 which is incompatible. +  + +Import Libraries +================ + +.. code:: ipython3 + + from collections import defaultdict, OrderedDict + + import numpy as np + import torch + from torch import nn + from torch.optim import * + from torch.optim.lr_scheduler import * + from torch.utils.data import DataLoader + from torchvision.datasets import * + from torchvision.transforms import * + import torch.optim as optim + from sconce import sconce + + assert torch.cuda.is_available(), \ + "The current runtime does not have CUDA support." \ + "Please go to menu bar (Runtime - Change runtime type) and select GPU" + +.. code:: ipython3 + + from google.colab import drive + drive.mount('/content/drive') + + + +.. parsed-literal:: + + Mounted at /content/drive + + +**Spiking Neural Network Compression** +====================================== + +.. code:: ipython3 + + # Import snntorch libraries + import snntorch as snn + from snntorch import surrogate + from snntorch import backprop + from snntorch import functional as SF + from snntorch import utils + from snntorch import spikeplot as splt + from torch import optim + + import torch + import torch.nn as nn + from torch.utils.data import DataLoader + from torchvision import datasets, transforms + import torch.nn.functional as F + + import matplotlib.pyplot as plt + import numpy as np + import itertools + + + + +.. parsed-literal:: + + :4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance. + from snntorch import backprop + + +Dataset +======= + +.. code:: ipython3 + + + # Event Drive Data + + # dataloader arguments + batch_size = 128 + data_path = "./data/mnist" + + dtype = torch.float + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + + # Define a transform + transform = transforms.Compose( + [ + transforms.Resize((28, 28)), + transforms.Grayscale(), + transforms.ToTensor(), + transforms.Normalize((0,), (1,)), + ] + ) + + mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform) + mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform) + + # Create DataLoaders + train_loader = DataLoader( + mnist_train, batch_size=batch_size, shuffle=True, drop_last=True + ) + test_loader = DataLoader( + mnist_test, batch_size=batch_size, shuffle=True, drop_last=True + ) + + + +.. parsed-literal:: + + Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 9912422/9912422 [00:00<00:00, 82101508.40it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 28881/28881 [00:00<00:00, 111748795.04it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 1648877/1648877 [00:00<00:00, 26490461.97it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 4542/4542 [00:00<00:00, 6970555.71it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw + + + +Instantiate an Object of sconce +=============================== + +.. code:: ipython3 + + + sconces = sconce() + + +Set you Dataloader +================== + +.. code:: ipython3 + + + dataloader = {} + dataloader["train"] = train_loader + dataloader["test"] = test_loader + sconces.dataloader = dataloader + +#Enable snn in sconce + +.. code:: ipython3 + + + sconces.snn = True + + +Load your snn Model +=================== + +.. code:: ipython3 + + spike_grad = surrogate.fast_sigmoid(slope=25) + beta = 0.5 + snn_model = nn.Sequential( + nn.Conv2d(1, 12, 5), + nn.MaxPool2d(2), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), + nn.Conv2d(12, 64, 5), + nn.MaxPool2d(2), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), + nn.Flatten(), + nn.Linear(64 * 4 * 4, 10), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True), + ).to('cuda') + + + +Load the pretrained weights +=========================== + +.. code:: ipython3 + + snn_pretrained_model_path = "drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth" + snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition + sconces.model = snn_model + +.. code:: ipython3 + + + sconces.optimizer = optim.Adam(sconces.model.parameters(), lr=1e-4) + sconces.scheduler = optim.lr_scheduler.CosineAnnealingLR(sconces.optimizer, T_max=200) + + sconces.criterion = SF.ce_rate_loss() + + sconces.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + sconces.experiment_name = "snn-gmp" # Define your experiment name here + sconces.prune_mode = "GMP" + sconces.num_finetune_epochs = 1 + + +.. code:: ipython3 + + sconces.evaluate() + + +.. parsed-literal:: + + + + + +.. parsed-literal:: + + 97.11538461538461 + + + +.. code:: ipython3 + + sconces.compress() + + +.. parsed-literal:: + + + Original Dense Model Size Model=0.11 MiB + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Original Model Validation Accuracy: 97.11538461538461 % + Granular-Magnitude Pruning + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Sensitivity Scan Time(secs): 204.14258646965027 + Sparsity for each Layer: {'0.weight': 0.6500000000000001, '3.weight': 0.5000000000000001, '7.weight': 0.7000000000000002} + Pruning Time Consumed (mins): 2.8362054 + Total Pruning Time Consumed (mins): 3.402399698893229 + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + Pruned Model has size=0.05 MiB(non-zeros) = 43.13% of Original model size + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + Pruned Model has Accuracy=95.94 MiB(non-zeros) = -1.17% of Original model Accuracy + + + ========== Fine-Tuning ========== + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Epoch:1 Train Loss: 0.00000 Validation Accuracy: 95.96354 + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + ................. Comparison Table ................. + Original Pruned Reduction Ratio + Latency (ms) 16.7 15.6 1.1 + MACs (M) 160 160 1.0 + Param (M) 0.01 0.01 1.0 + Accuracies (%) 97.115 95.964 -1.152 + Fine-Tuned Sparse model has size=0.05 MiB = 43.13% of Original model size + Fine-Tuned Pruned Model Validation Accuracy: 95.96354166666667 + + +.. parsed-literal:: + + /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. + warnings.warn('No handlers found: "{}". Skipped.'.format( + /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. + warnings.warn('No handlers found: "{}". Skipped.'.format( + diff --git a/docs/tutorials/tutorials.rst b/docs/tutorials/tutorials.rst index 9043367b..2da9b623 100644 --- a/docs/tutorials/tutorials.rst +++ b/docs/tutorials/tutorials.rst @@ -78,9 +78,14 @@ The tutorial consists of a series of Google Colab notebooks. Static non-editable :alt: Open In Colab :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/tutorial_regression_2.ipynb + * - `Model Pruning `_ + - .. image:: https://colab.research.google.com/assets/colab-badge.svg + :alt: Open In Colab + :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/examples/sconce_model_compression_pruning.ipynb.ipynb * - `Accelerating snnTorch on IPUs `_ - — + Future tutorials on spiking neurons and training are under development. \ No newline at end of file diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0=1.20, but you have numpy 1.26.2 which is incompatible.\n", + "imageio 2.31.6 requires pillow<10.1.0,>=8.3.2, but you have pillow 10.1.0 which is incompatible.\n", + "tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.8.0 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0m" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Import Libraries" + ], + "metadata": { + "id": "l9M2kqJwn0Uc" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xEDfNQnMIW9H", + "ExecuteTime": { + "end_time": "2023-12-01T07:32:53.170470032Z", + "start_time": "2023-12-01T07:32:50.766056935Z" + } + }, + "outputs": [], + "source": [ + "from collections import defaultdict, OrderedDict\n", + "\n", + "import numpy as np\n", + "import torch\n", + "from torch import nn\n", + "from torch.optim import *\n", + "from torch.optim.lr_scheduler import *\n", + "from torch.utils.data import DataLoader\n", + "from torchvision.datasets import *\n", + "from torchvision.transforms import *\n", + "import torch.optim as optim\n", + "from sconce import sconce\n", + "\n", + "assert torch.cuda.is_available(), \\\n", + "\"The current runtime does not have CUDA support.\" \\\n", + "\"Please go to menu bar (Runtime - Change runtime type) and select GPU\"" + ] + }, + { + "cell_type": "code", + "source": [ + "from google.colab import drive\n", + "drive.mount('/content/drive')\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YI-g2PwnGf_s", + "outputId": "ee2d27fa-c526-471e-86b2-4c394a62f5ff" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mounted at /content/drive\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# **Spiking Neural Network Compression**\n" + ], + "metadata": { + "id": "oJjAdrRK0aRD" + } + }, + { + "cell_type": "code", + "source": [ + "# Import snntorch libraries\n", + "import snntorch as snn\n", + "from snntorch import surrogate\n", + "from snntorch import backprop\n", + "from snntorch import functional as SF\n", + "from snntorch import utils\n", + "from snntorch import spikeplot as splt\n", + "from torch import optim\n", + "\n", + "import torch\n", + "import torch.nn as nn\n", + "from torch.utils.data import DataLoader\n", + "from torchvision import datasets, transforms\n", + "import torch.nn.functional as F\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import itertools\n", + "\n" + ], + "metadata": { + "id": "V9qFLeGl0y24", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "3ced031a-a68f-4d7a-9be2-81fdd17f0654" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + ":4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance.\n", + " from snntorch import backprop\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Dataset" + ], + "metadata": { + "id": "0iqMEfCgnufM" + } + }, + { + "cell_type": "code", + "source": [ + "\n", + "# Event Drive Data\n", + "\n", + "# dataloader arguments\n", + "batch_size = 128\n", + "data_path = \"./data/mnist\"\n", + "\n", + "dtype = torch.float\n", + "device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", + "\n", + "# Define a transform\n", + "transform = transforms.Compose(\n", + " [\n", + " transforms.Resize((28, 28)),\n", + " transforms.Grayscale(),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0,), (1,)),\n", + " ]\n", + ")\n", + "\n", + "mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)\n", + "mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform)\n", + "\n", + "# Create DataLoaders\n", + "train_loader = DataLoader(\n", + " mnist_train, batch_size=batch_size, shuffle=True, drop_last=True\n", + ")\n", + "test_loader = DataLoader(\n", + " mnist_test, batch_size=batch_size, shuffle=True, drop_last=True\n", + ")\n" + ], + "metadata": { + "id": "zk0TCGvRKOWh", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "78c643ab-1d99-4f2e-d64c-ddc6aedc9940" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n", + "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 9912422/9912422 [00:00<00:00, 82101508.40it/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Extracting ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw\n", + "\n", + "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\n", + "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 28881/28881 [00:00<00:00, 111748795.04it/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Extracting ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw\n", + "\n", + "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz\n", + "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 1648877/1648877 [00:00<00:00, 26490461.97it/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Extracting ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw\n", + "\n", + "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz\n", + "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "100%|██████████| 4542/4542 [00:00<00:00, 6970555.71it/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Extracting ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw\n", + "\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Instantiate an Object of sconce" + ], + "metadata": { + "id": "khyN02zynLkO" + } + }, + { + "cell_type": "code", + "source": [ + "\n", + "sconces = sconce()\n" + ], + "metadata": { + "id": "cu7BrvWvnKjm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Set you Dataloader" + ], + "metadata": { + "id": "-hTGOBb-nsBo" + } + }, + { + "cell_type": "code", + "source": [ + "\n", + "dataloader = {}\n", + "dataloader[\"train\"] = train_loader\n", + "dataloader[\"test\"] = test_loader\n", + "sconces.dataloader = dataloader" + ], + "metadata": { + "id": "9pj-ZzMQU9_Q" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "#Enable snn in sconce" + ], + "metadata": { + "id": "AV4QDhgznnHQ" + } + }, + { + "cell_type": "code", + "source": [ + "\n", + "sconces.snn = True\n" + ], + "metadata": { + "id": "YnqX4AxNnlwG" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Load your snn Model" + ], + "metadata": { + "id": "F1umQlu1nptt" + } + }, + { + "cell_type": "code", + "source": [ + "spike_grad = surrogate.fast_sigmoid(slope=25)\n", + "beta = 0.5\n", + "snn_model = nn.Sequential(\n", + " nn.Conv2d(1, 12, 5),\n", + " nn.MaxPool2d(2),\n", + " snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True),\n", + " nn.Conv2d(12, 64, 5),\n", + " nn.MaxPool2d(2),\n", + " snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True),\n", + " nn.Flatten(),\n", + " nn.Linear(64 * 4 * 4, 10),\n", + " snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True),\n", + ").to('cuda')\n", + "\n" + ], + "metadata": { + "id": "LzEHUCSx0hN6" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Load the pretrained weights" + ], + "metadata": { + "id": "Suw2PXg6nc6i" + } + }, + { + "cell_type": "code", + "source": [ + "snn_pretrained_model_path = \"drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth\"\n", + "snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition\n", + "sconces.model = snn_model" + ], + "metadata": { + "id": "7Hkv8zdJncZ4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "\n", + "sconces.optimizer = optim.Adam(sconces.model.parameters(), lr=1e-4)\n", + "sconces.scheduler = optim.lr_scheduler.CosineAnnealingLR(sconces.optimizer, T_max=200)\n", + "\n", + "sconces.criterion = SF.ce_rate_loss()\n", + "\n", + "sconces.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "sconces.experiment_name = \"snn-gmp\" # Define your experiment name here\n", + "sconces.prune_mode = \"GMP\"\n", + "sconces.num_finetune_epochs = 1\n" + ], + "metadata": { + "id": "qZLTwjV0VdJc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "sconces.evaluate()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lRlavnVPbKUT", + "outputId": "8becb541-1e43-4d75-9f84-3d45340d65e4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "97.11538461538461" + ] + }, + "metadata": {}, + "execution_count": 11 + } + ] + }, + { + "cell_type": "code", + "source": [ + "sconces.compress()" + ], + "metadata": { + "id": "K6kWCtSk0kVq", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "53bebd3c-898e-4635-cb9e-d6d67603b451" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Original Dense Model Size Model=0.11 MiB\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Original Model Validation Accuracy: 97.11538461538461 %\n", + "Granular-Magnitude Pruning\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Sensitivity Scan Time(secs): 204.14258646965027\n", + "Sparsity for each Layer: {'0.weight': 0.6500000000000001, '3.weight': 0.5000000000000001, '7.weight': 0.7000000000000002}\n", + "Pruning Time Consumed (mins): 2.8362054\n", + "Total Pruning Time Consumed (mins): 3.402399698893229\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Pruned Model has size=0.05 MiB(non-zeros) = 43.13% of Original model size\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "Pruned Model has Accuracy=95.94 MiB(non-zeros) = -1.17% of Original model Accuracy\n", + "\n", + " \n", + "========== Fine-Tuning ==========\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Epoch:1 Train Loss: 0.00000 Validation Accuracy: 95.96354\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + " ................. Comparison Table .................\n", + " Original Pruned Reduction Ratio\n", + "Latency (ms) 16.7 15.6 1.1 \n", + "MACs (M) 160 160 1.0 \n", + "Param (M) 0.01 0.01 1.0 \n", + "Accuracies (%) 97.115 95.964 -1.152 \n", + "Fine-Tuned Sparse model has size=0.05 MiB = 43.13% of Original model size\n", + "Fine-Tuned Pruned Model Validation Accuracy: 95.96354166666667\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: \"prim::pythonop\". Skipped.\n", + " warnings.warn('No handlers found: \"{}\". Skipped.'.format(\n", + "/usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: \"prim::pythonop\". Skipped.\n", + " warnings.warn('No handlers found: \"{}\". Skipped.'.format(\n" + ] + } + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "machine_shape": "hm", + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "language": "python", + "display_name": "Python 3 (ipykernel)" + }, + "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.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From 82f9de8537a6432e5e054a39d59551913a3e966f Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Mon, 4 Dec 2023 14:01:52 -0800 Subject: [PATCH 2/9] Update tutorials.rst --- docs/tutorials/tutorials.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/tutorials.rst b/docs/tutorials/tutorials.rst index 2da9b623..5a543c3a 100644 --- a/docs/tutorials/tutorials.rst +++ b/docs/tutorials/tutorials.rst @@ -54,6 +54,12 @@ The tutorial consists of a series of Google Colab notebooks. Static non-editable :alt: Open In Colab :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/tutorial_7_neuromorphic_datasets.ipynb + * - `Tutorial 8 `_ + - Model Compresion: sconce + snnTorch + - .. image:: https://colab.research.google.com/assets/colab-badge.svg + :alt: Open In Colab + :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/sconce_model_compression_pruning.ipynb + .. list-table:: @@ -81,11 +87,11 @@ The tutorial consists of a series of Google Colab notebooks. Static non-editable * - `Model Pruning `_ - .. image:: https://colab.research.google.com/assets/colab-badge.svg :alt: Open In Colab - :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/examples/sconce_model_compression_pruning.ipynb.ipynb + :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/examples/sconce_model_compression_pruning.ipynb * - `Accelerating snnTorch on IPUs `_ - — -Future tutorials on spiking neurons and training are under development. \ No newline at end of file +Future tutorials on spiking neurons and training are under development. From 9bdd8cbe6fdc2bf0307113e1dc4b12363973b26c Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Mon, 4 Dec 2023 14:04:10 -0800 Subject: [PATCH 3/9] Update tutorials.rst --- docs/tutorials/tutorials.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/tutorials.rst b/docs/tutorials/tutorials.rst index 5a543c3a..0a355a30 100644 --- a/docs/tutorials/tutorials.rst +++ b/docs/tutorials/tutorials.rst @@ -54,7 +54,7 @@ The tutorial consists of a series of Google Colab notebooks. Static non-editable :alt: Open In Colab :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/tutorial_7_neuromorphic_datasets.ipynb - * - `Tutorial 8 `_ + * - `Tutorial 8 `_ - Model Compresion: sconce + snnTorch - .. image:: https://colab.research.google.com/assets/colab-badge.svg :alt: Open In Colab @@ -84,10 +84,10 @@ The tutorial consists of a series of Google Colab notebooks. Static non-editable :alt: Open In Colab :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/tutorial_regression_2.ipynb - * - `Model Pruning `_ + * - `Model Pruning `_ - .. image:: https://colab.research.google.com/assets/colab-badge.svg :alt: Open In Colab - :target: https://colab.research.google.com/github/jeshraghian/snntorch/blob/master/examples/examples/sconce_model_compression_pruning.ipynb + :target: https://github.com/satabios/snntorch/blob/master/examples/sconce_model_compression_pruning.ipynb * - `Accelerating snnTorch on IPUs `_ - — From 6c8bafe2c968af4012953972d15a91a3744e8c14 Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Tue, 5 Dec 2023 10:48:05 -0800 Subject: [PATCH 4/9] updated rst of model pruning --- docs/examples/.DS_Store | Bin 0 -> 6148 bytes .../sconce_model_compression_pruning.rst | 375 ++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 docs/examples/.DS_Store create mode 100644 docs/tutorials/sconce_model_compression_pruning.rst diff --git a/docs/examples/.DS_Store b/docs/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0=1.20, but you have numpy 1.26.2 which is incompatible. + imageio 2.31.6 requires pillow<10.1.0,>=8.3.2, but you have pillow 10.1.0 which is incompatible. + tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.8.0 which is incompatible. +  + +Import Libraries +================ + +.. code:: ipython3 + + from collections import defaultdict, OrderedDict + + import numpy as np + import torch + from torch import nn + from torch.optim import * + from torch.optim.lr_scheduler import * + from torch.utils.data import DataLoader + from torchvision.datasets import * + from torchvision.transforms import * + import torch.optim as optim + from sconce import sconce + + assert torch.cuda.is_available(), \ + "The current runtime does not have CUDA support." \ + "Please go to menu bar (Runtime - Change runtime type) and select GPU" + +.. code:: ipython3 + + from google.colab import drive + drive.mount('/content/drive') + + + +.. parsed-literal:: + + Mounted at /content/drive + + +**Spiking Neural Network Compression** +====================================== + +.. code:: ipython3 + + # Import snntorch libraries + import snntorch as snn + from snntorch import surrogate + from snntorch import backprop + from snntorch import functional as SF + from snntorch import utils + from snntorch import spikeplot as splt + from torch import optim + + import torch + import torch.nn as nn + from torch.utils.data import DataLoader + from torchvision import datasets, transforms + import torch.nn.functional as F + + import matplotlib.pyplot as plt + import numpy as np + import itertools + + + + +.. parsed-literal:: + + :4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance. + from snntorch import backprop + + +Dataset +======= + +.. code:: ipython3 + + + # Event Drive Data + + # dataloader arguments + batch_size = 128 + data_path = "./data/mnist" + + dtype = torch.float + device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") + + # Define a transform + transform = transforms.Compose( + [ + transforms.Resize((28, 28)), + transforms.Grayscale(), + transforms.ToTensor(), + transforms.Normalize((0,), (1,)), + ] + ) + + mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform) + mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform) + + # Create DataLoaders + train_loader = DataLoader( + mnist_train, batch_size=batch_size, shuffle=True, drop_last=True + ) + test_loader = DataLoader( + mnist_test, batch_size=batch_size, shuffle=True, drop_last=True + ) + + + +.. parsed-literal:: + + Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 9912422/9912422 [00:00<00:00, 82101508.40it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 28881/28881 [00:00<00:00, 111748795.04it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 1648877/1648877 [00:00<00:00, 26490461.97it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw + + Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz + Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz + + +.. parsed-literal:: + + 100%|██████████| 4542/4542 [00:00<00:00, 6970555.71it/s] + + +.. parsed-literal:: + + Extracting ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw + + + +Instantiate an Object of sconce +=============================== + +.. code:: ipython3 + + + sconces = sconce() + + +Set you Dataloader +================== + +.. code:: ipython3 + + + dataloader = {} + dataloader["train"] = train_loader + dataloader["test"] = test_loader + sconces.dataloader = dataloader + +#Enable snn in sconce + +.. code:: ipython3 + + + sconces.snn = True + + +Load your snn Model +=================== + +.. code:: ipython3 + + spike_grad = surrogate.fast_sigmoid(slope=25) + beta = 0.5 + snn_model = nn.Sequential( + nn.Conv2d(1, 12, 5), + nn.MaxPool2d(2), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), + nn.Conv2d(12, 64, 5), + nn.MaxPool2d(2), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), + nn.Flatten(), + nn.Linear(64 * 4 * 4, 10), + snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True), + ).to('cuda') + + + +Load the pretrained weights +=========================== + +.. code:: ipython3 + + snn_pretrained_model_path = "drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth" + snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition + sconces.model = snn_model + +.. code:: ipython3 + + + sconces.optimizer = optim.Adam(sconces.model.parameters(), lr=1e-4) + sconces.scheduler = optim.lr_scheduler.CosineAnnealingLR(sconces.optimizer, T_max=200) + + sconces.criterion = SF.ce_rate_loss() + + sconces.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + sconces.experiment_name = "snn-gmp" # Define your experiment name here + sconces.prune_mode = "GMP" + sconces.num_finetune_epochs = 1 + + +.. code:: ipython3 + + sconces.evaluate() + + +.. parsed-literal:: + + + + + +.. parsed-literal:: + + 97.11538461538461 + + + +.. code:: ipython3 + + sconces.compress() + + +.. parsed-literal:: + + + Original Dense Model Size Model=0.11 MiB + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Original Model Validation Accuracy: 97.11538461538461 % + Granular-Magnitude Pruning + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Sensitivity Scan Time(secs): 204.14258646965027 + Sparsity for each Layer: {'0.weight': 0.6500000000000001, '3.weight': 0.5000000000000001, '7.weight': 0.7000000000000002} + Pruning Time Consumed (mins): 2.8362054 + Total Pruning Time Consumed (mins): 3.402399698893229 + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + Pruned Model has size=0.05 MiB(non-zeros) = 43.13% of Original model size + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + Pruned Model has Accuracy=95.94 MiB(non-zeros) = -1.17% of Original model Accuracy + + + ========== Fine-Tuning ========== + + +.. parsed-literal:: + + + +.. parsed-literal:: + + Epoch:1 Train Loss: 0.00000 Validation Accuracy: 95.96354 + + +.. parsed-literal:: + + + +.. parsed-literal:: + + + ................. Comparison Table ................. + Original Pruned Reduction Ratio + Latency (ms) 16.7 15.6 1.1 + MACs (M) 160 160 1.0 + Param (M) 0.01 0.01 1.0 + Accuracies (%) 97.115 95.964 -1.152 + Fine-Tuned Sparse model has size=0.05 MiB = 43.13% of Original model size + Fine-Tuned Pruned Model Validation Accuracy: 95.96354166666667 + + +.. parsed-literal:: + + /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. + warnings.warn('No handlers found: "{}". Skipped.'.format( + /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. + warnings.warn('No handlers found: "{}". Skipped.'.format( + From 593c13ec1c555e2803bac9b7eed72de584bfcc07 Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Tue, 5 Dec 2023 12:01:15 -0800 Subject: [PATCH 5/9] added extensive comments --- examples/.DS_Store | Bin 6148 -> 10244 bytes .../sconce_model_compression_pruning.ipynb | 394 +++++++++--------- examples/snn_model.pth | Bin 0 -> 124335 bytes 3 files changed, 205 insertions(+), 189 deletions(-) create mode 100644 examples/snn_model.pth diff --git a/examples/.DS_Store b/examples/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0260a2393792a4f0e962493f393955165a8469f4 100644 GIT binary patch literal 10244 zcmeHMON$dh5UyTVh^$I{99%>gL4p^9SzW!xH40w5ES|HQWb(iyGiEZr4wAbE|AGHN zJbTlF;L-o$Nj&IR-Lu*5$;`wR5h2x~yC$8gue!c|3|%22wG(%0L>>{9P+68wqRA-y zJJ*G>mh^a#3id=p3TU4?Z1dVz5+n5qgxj|;~t=GyhPR$IWtn50@K_cRAQ=)9R$^MoKFT2m^}@c<9=|jeVb{sP_FmdN2&ytzpoD25+i6 zsMVjk;3?N?*sJZfgHfn;RHKrwISLw$1i!TX@q8Owm+oRay!IbAw zM#xW0sfqSEVjfY5xP3~;`yMuSyi4uf$iJQY9iKJ_pT5pHSnA7W<0y6xFXjj>(-Vqm zKy7+VkzNH8v{m$k^n%9Ja@T>nl;Y(Cay`Dz`ivl};Iosmc%8DSz)u^QqA^+@^Dx3Q z=ZuDU=6;?L#*FY8XuYQHiD5B7?G=pfd&<|9zu;>EjA<$+u%KR9yWtdc#`xILR59u) z^<7KeM~>wtbYj#1F%R?}Y*Gz45v&K86Cd&VK+c_!wO%Ubiy4SpKpX-tqU|C^Vii8JVOK%eB<#mANlyazij-=8TdDW!}-LgXw7P2WPAKk zz6ZThZ4c)&JZuB+KHa5<1vtEZm*Lz34x<#|@Y=L-DjDOs?Zn4BV}fX5#cX|9=k`ee-)K=ue0QW=XxS%MxqDh WpZ^(fm}!ywzufw59N^D?X%+A3f$P5w! kif{u7S0Gm1SoocJGQWx:4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance.\n", + " from snntorch import backprop\n" ] } - ] - }, - { - "cell_type": "markdown", - "source": [ - "# **Spiking Neural Network Compression**\n" ], - "metadata": { - "id": "oJjAdrRK0aRD" - } - }, - { - "cell_type": "code", "source": [ "# Import snntorch libraries\n", "import snntorch as snn\n", @@ -150,96 +144,46 @@ "import numpy as np\n", "import itertools\n", "\n" - ], - "metadata": { - "id": "V9qFLeGl0y24", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "3ced031a-a68f-4d7a-9be2-81fdd17f0654" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - ":4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance.\n", - " from snntorch import backprop\n" - ] - } ] }, { "cell_type": "markdown", - "source": [ - "# Dataset" - ], "metadata": { "id": "0iqMEfCgnufM" - } + }, + "source": [ + "# Dataset" + ] }, { "cell_type": "code", - "source": [ - "\n", - "# Event Drive Data\n", - "\n", - "# dataloader arguments\n", - "batch_size = 128\n", - "data_path = \"./data/mnist\"\n", - "\n", - "dtype = torch.float\n", - "device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", - "\n", - "# Define a transform\n", - "transform = transforms.Compose(\n", - " [\n", - " transforms.Resize((28, 28)),\n", - " transforms.Grayscale(),\n", - " transforms.ToTensor(),\n", - " transforms.Normalize((0,), (1,)),\n", - " ]\n", - ")\n", - "\n", - "mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)\n", - "mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform)\n", - "\n", - "# Create DataLoaders\n", - "train_loader = DataLoader(\n", - " mnist_train, batch_size=batch_size, shuffle=True, drop_last=True\n", - ")\n", - "test_loader = DataLoader(\n", - " mnist_test, batch_size=batch_size, shuffle=True, drop_last=True\n", - ")\n" - ], + "execution_count": null, "metadata": { - "id": "zk0TCGvRKOWh", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "zk0TCGvRKOWh", "outputId": "78c643ab-1d99-4f2e-d64c-ddc6aedc9940" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n", "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "100%|██████████| 9912422/9912422 [00:00<00:00, 82101508.40it/s]\n" ] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Extracting ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw\n", "\n", @@ -248,15 +192,15 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "100%|██████████| 28881/28881 [00:00<00:00, 111748795.04it/s]\n" ] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Extracting ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw\n", "\n", @@ -265,15 +209,15 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "100%|██████████| 1648877/1648877 [00:00<00:00, 26490461.97it/s]\n" ] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Extracting ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw\n", "\n", @@ -282,99 +226,136 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "100%|██████████| 4542/4542 [00:00<00:00, 6970555.71it/s]\n" ] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Extracting ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw\n", "\n" ] } + ], + "source": [ + "\n", + "# Event Drive Data\n", + "\n", + "# dataloader arguments\n", + "batch_size = 128\n", + "data_path = \"./data/mnist\"\n", + "\n", + "dtype = torch.float\n", + "device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", + "\n", + "# Define a transform\n", + "transform = transforms.Compose(\n", + " [\n", + " transforms.Resize((28, 28)),\n", + " transforms.Grayscale(),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0,), (1,)),\n", + " ]\n", + ")\n", + "\n", + "mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform)\n", + "mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform)\n", + "\n", + "# Create DataLoaders\n", + "train_loader = DataLoader(\n", + " mnist_train, batch_size=batch_size, shuffle=True, drop_last=True\n", + ")\n", + "test_loader = DataLoader(\n", + " mnist_test, batch_size=batch_size, shuffle=True, drop_last=True\n", + ")\n" ] }, { "cell_type": "markdown", - "source": [ - "# Instantiate an Object of sconce" - ], "metadata": { "id": "khyN02zynLkO" - } + }, + "source": [ + "# Instantiate an Object of sconce" + ] }, { "cell_type": "code", - "source": [ - "\n", - "sconces = sconce()\n" - ], + "execution_count": null, "metadata": { "id": "cu7BrvWvnKjm" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "\n", + "sconces = sconce()\n" + ] }, { "cell_type": "markdown", - "source": [ - "# Set you Dataloader" - ], "metadata": { "id": "-hTGOBb-nsBo" - } + }, + "source": [ + "# Set you Dataloader" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9pj-ZzMQU9_Q" + }, + "outputs": [], "source": [ "\n", "dataloader = {}\n", "dataloader[\"train\"] = train_loader\n", "dataloader[\"test\"] = test_loader\n", "sconces.dataloader = dataloader" - ], - "metadata": { - "id": "9pj-ZzMQU9_Q" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "#Enable snn in sconce" - ], "metadata": { "id": "AV4QDhgznnHQ" - } + }, + "source": [ + "#Enable snn in sconce" + ] }, { "cell_type": "code", - "source": [ - "\n", - "sconces.snn = True\n" - ], + "execution_count": null, "metadata": { "id": "YnqX4AxNnlwG" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "\n", + "sconces.snn = True\n" + ] }, { "cell_type": "markdown", - "source": [ - "# Load your snn Model" - ], "metadata": { "id": "F1umQlu1nptt" - } + }, + "source": [ + "# Load your snn Model" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LzEHUCSx0hN6" + }, + "outputs": [], "source": [ "spike_grad = surrogate.fast_sigmoid(slope=25)\n", "beta = 0.5\n", @@ -390,37 +371,44 @@ " snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True),\n", ").to('cuda')\n", "\n" - ], - "metadata": { - "id": "LzEHUCSx0hN6" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "# Load the pretrained weights" - ], "metadata": { "id": "Suw2PXg6nc6i" - } + }, + "source": [ + "# Load the pretrained weights" + ] }, { "cell_type": "code", - "source": [ - "snn_pretrained_model_path = \"drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth\"\n", - "snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition\n", - "sconces.model = snn_model" - ], + "execution_count": null, "metadata": { "id": "7Hkv8zdJncZ4" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "snn_pretrained_model_path = \"./snn_model.pth\"\n", + "snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition\n", + "sconces.model = snn_model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Set the Optimizizer and Type of Pruning Operation to Perform on the model" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qZLTwjV0VdJc" + }, + "outputs": [], "source": [ "\n", "sconces.optimizer = optim.Adam(sconces.model.parameters(), lr=1e-4)\n", @@ -432,18 +420,18 @@ "sconces.experiment_name = \"snn-gmp\" # Define your experiment name here\n", "sconces.prune_mode = \"GMP\"\n", "sconces.num_finetune_epochs = 1\n" - ], - "metadata": { - "id": "qZLTwjV0VdJc" - }, - "execution_count": null, - "outputs": [] + ] }, { - "cell_type": "code", + "cell_type": "markdown", + "metadata": {}, "source": [ - "sconces.evaluate()" - ], + "# Test the Pre-Trained Model Accuracy" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -451,68 +439,83 @@ "id": "lRlavnVPbKUT", "outputId": "8becb541-1e43-4d75-9f84-3d45340d65e4" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "execute_result", "data": { "text/plain": [ "97.11538461538461" ] }, + "execution_count": 11, "metadata": {}, - "execution_count": 11 + "output_type": "execute_result" } + ], + "source": [ + "sconces.evaluate()" ] }, { - "cell_type": "code", + "cell_type": "markdown", + "metadata": {}, "source": [ - "sconces.compress()" - ], + "# Prune the Model\n", + "\n", + "The Compression does a series of steps as explained below:\n", + "\n", + "1. It evaluates the dense model accuracy\n", + "2. Given the model, the package finds the best parameters for pruning such that the accuracy degradation is minimal.\n", + "3. The retreived optimal parameters from the above steps are used to prune the model.\n", + "4. At times, certain pruning techniques might require a fine-tuning on the dataset. For which the pruned model is fine-tuned on the dataset.\n", + "5. Pruned Model is saved and Compared for Latency, Paramater, MAC and model size.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { - "id": "K6kWCtSk0kVq", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "K6kWCtSk0kVq", "outputId": "53bebd3c-898e-4635-cb9e-d6d67603b451" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "\n", "Original Dense Model Size Model=0.11 MiB\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Original Model Validation Accuracy: 97.11538461538461 %\n", "Granular-Magnitude Pruning\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Sensitivity Scan Time(secs): 204.14258646965027\n", "Sparsity for each Layer: {'0.weight': 0.6500000000000001, '3.weight': 0.5000000000000001, '7.weight': 0.7000000000000002}\n", @@ -521,26 +524,26 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "\n", "Pruned Model has size=0.05 MiB(non-zeros) = 43.13% of Original model size\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "\n", "Pruned Model has Accuracy=95.94 MiB(non-zeros) = -1.17% of Original model Accuracy\n", @@ -550,25 +553,25 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Epoch:1 Train Loss: 0.00000 Validation Accuracy: 95.96354\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "\n", " ................. Comparison Table .................\n", @@ -582,8 +585,8 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "/usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: \"prim::pythonop\". Skipped.\n", " warnings.warn('No handlers found: \"{}\". Skipped.'.format(\n", @@ -591,6 +594,19 @@ " warnings.warn('No handlers found: \"{}\". Skipped.'.format(\n" ] } + ], + "source": [ + "sconces.compress()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Note:\n", + "* The Latency is reduced and Parameters will be reduced(the numbers are rounded to .2f hence we cannnot see the parameter pruning here, large model will be able to showcase this deliberately),\n", + "* The MAC is remains the same, sicne the Conv operation used here is Conv2d and the MAC are calculated Channel Wise and **not Element Wise**. \n", + "* If specialised sparsity aware, Software/Hardware is used then we reach the ultimate goal of compressing the model and leveraging the inherent sparsity in the model\n" ] } ], @@ -601,9 +617,9 @@ "provenance": [] }, "kernelspec": { - "name": "python3", + "display_name": "Python 3 (ipykernel)", "language": "python", - "display_name": "Python 3 (ipykernel)" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -620,4 +636,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/examples/snn_model.pth b/examples/snn_model.pth new file mode 100644 index 0000000000000000000000000000000000000000..dc3b018e957887f1bb7eeeee8e165b3418e941a4 GIT binary patch literal 124335 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 Date: Tue, 5 Dec 2023 12:01:45 -0800 Subject: [PATCH 6/9] added extensive comments --- .DS_Store | Bin 6148 -> 6148 bytes docs/.DS_Store | Bin 6148 -> 6148 bytes .../sconce_model_compression_pruning.rst | 375 ------------------ 3 files changed, 375 deletions(-) delete mode 100644 docs/examples/sconce_model_compression_pruning.rst diff --git a/.DS_Store b/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..fdc236bed171a429ab259358e1fd97d66b9db481 100644 GIT binary patch literal 6148 zcmeHK!HU#C5Ut*w)gIS{IVcQ!YVf+?L>EEy5XbCZ1tac3WlYSl8#YM~lZ+z_13Bwq zKS2Bsf52bxLXGRn=Ynpu1B5AjXTt0~i2+#75Y=f!zinJGG8% zxshE&Vdj`Y22&{GWjX73^8hoz4E%2l(AkxEWCU*lPpmepwVU2JXl zcCKBQQtrw(;hA2BRa7m?aWsELUoW)I;!8b>U#8RLWN_=LE~+RkrgIaJCR22IeUcW5 zUXJymNJhPdv&Ncx3a9Gk z^*)>znJ!MzM*G=;if&Qt%T~tl<#F5E%=Pohe&E|&8}YgRmE~rSR(_VvM*MG?;qok3 zh5>hWB;5fl%m6dM3^W;_`$1tN^c~g~&DDXODFG0BIBW!UswF5!IrJUY7SVztY$&1) z6|Te(HXO&5^Yb0n7Hv2PS9}P!vTzlOFstMI%D98@EppEcFavD{w(aMD-v6UN*Z=Jx z{$d80fh)y;=mlXg#x1$GwX->TYZbP)*ht8)wrE1IbI-9+=qVmzGlDin3Zn0@wumDr P{3Bp!;EoyirwsfASvrvx delta 66 zcmZoMXfc=|#>AjHu~2NHo+1YW5HK<@2y7PQ5M$Y_z%h$?Gdl-A2T%b}u84%9H(hI5x+KtY8KJ$3PAT diff --git a/docs/.DS_Store b/docs/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..43040c5ee73a1a562915f494893d077261467efa 100644 GIT binary patch delta 336 zcmZoMXfc=|#>B)qF;Q%yo}wrd0|Nsi1A_nqLlHwJLn1>CLvd31#>C}}^&lBeh7yKS zAkIgYEiOpP$xi~x?no-g$t*50Fu2CZ#LU9V#?HaP!OamHoRME1T#{H)TI`fq6b<5q zAjHu~2NHo+1YW5HK<@2y7N){>HLdfq56xW_AvK4xj>{$am(+{342+ UKzW7)kiy9(Jj$D6L{=~Z01@sEU;qFB diff --git a/docs/examples/sconce_model_compression_pruning.rst b/docs/examples/sconce_model_compression_pruning.rst deleted file mode 100644 index 6ef3a9f9..00000000 --- a/docs/examples/sconce_model_compression_pruning.rst +++ /dev/null @@ -1,375 +0,0 @@ -Install Packages -================ - -.. code:: ipython3 - - !pip install snntorch -q - !pip install sconce -q - %pip show sconce - - -.. parsed-literal:: - -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 109.0/109.0 kB 1.1 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 153.0/153.0 kB 2.0 MB/s eta 0:00:00 - [?25h Installing build dependencies ... [?25l[?25hdone - Getting requirements to build wheel ... [?25l[?25hdone - Installing backend dependencies ... [?25l[?25hdone - Preparing metadata (pyproject.toml) ... [?25l[?25hdone -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.6/11.6 MB 70.2 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.2/18.2 MB 69.5 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.6/3.6 MB 114.1 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.8/2.8 MB 113.5 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 53.1/53.1 kB 5.4 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 121.1/121.1 kB 16.0 MB/s eta 0:00:00 -  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.6/1.6 MB 69.3 MB/s eta 0:00:00 - [?25h Building wheel for lit (pyproject.toml) ... [?25l[?25hdone - ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. - lida 0.0.10 requires fastapi, which is not installed. - lida 0.0.10 requires kaleido, which is not installed. - lida 0.0.10 requires python-multipart, which is not installed. - lida 0.0.10 requires uvicorn, which is not installed. - cupy-cuda11x 11.0.0 requires numpy<1.26,>=1.20, but you have numpy 1.26.2 which is incompatible. - imageio 2.31.6 requires pillow<10.1.0,>=8.3.2, but you have pillow 10.1.0 which is incompatible. - tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.8.0 which is incompatible. -  - -Import Libraries -================ - -.. code:: ipython3 - - from collections import defaultdict, OrderedDict - - import numpy as np - import torch - from torch import nn - from torch.optim import * - from torch.optim.lr_scheduler import * - from torch.utils.data import DataLoader - from torchvision.datasets import * - from torchvision.transforms import * - import torch.optim as optim - from sconce import sconce - - assert torch.cuda.is_available(), \ - "The current runtime does not have CUDA support." \ - "Please go to menu bar (Runtime - Change runtime type) and select GPU" - -.. code:: ipython3 - - from google.colab import drive - drive.mount('/content/drive') - - - -.. parsed-literal:: - - Mounted at /content/drive - - -**Spiking Neural Network Compression** -====================================== - -.. code:: ipython3 - - # Import snntorch libraries - import snntorch as snn - from snntorch import surrogate - from snntorch import backprop - from snntorch import functional as SF - from snntorch import utils - from snntorch import spikeplot as splt - from torch import optim - - import torch - import torch.nn as nn - from torch.utils.data import DataLoader - from torchvision import datasets, transforms - import torch.nn.functional as F - - import matplotlib.pyplot as plt - import numpy as np - import itertools - - - - -.. parsed-literal:: - - :4: DeprecationWarning: The module snntorch.backprop will be deprecated in a future release. Writing out your own training loop will lead to substantially faster performance. - from snntorch import backprop - - -Dataset -======= - -.. code:: ipython3 - - - # Event Drive Data - - # dataloader arguments - batch_size = 128 - data_path = "./data/mnist" - - dtype = torch.float - device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") - - # Define a transform - transform = transforms.Compose( - [ - transforms.Resize((28, 28)), - transforms.Grayscale(), - transforms.ToTensor(), - transforms.Normalize((0,), (1,)), - ] - ) - - mnist_train = datasets.MNIST(data_path, train=True, download=True, transform=transform) - mnist_test = datasets.MNIST(data_path, train=False, download=True, transform=transform) - - # Create DataLoaders - train_loader = DataLoader( - mnist_train, batch_size=batch_size, shuffle=True, drop_last=True - ) - test_loader = DataLoader( - mnist_test, batch_size=batch_size, shuffle=True, drop_last=True - ) - - - -.. parsed-literal:: - - Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz - Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz - - -.. parsed-literal:: - - 100%|██████████| 9912422/9912422 [00:00<00:00, 82101508.40it/s] - - -.. parsed-literal:: - - Extracting ./data/mnist/MNIST/raw/train-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw - - Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz - Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz - - -.. parsed-literal:: - - 100%|██████████| 28881/28881 [00:00<00:00, 111748795.04it/s] - - -.. parsed-literal:: - - Extracting ./data/mnist/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw - - Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz - Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz - - -.. parsed-literal:: - - 100%|██████████| 1648877/1648877 [00:00<00:00, 26490461.97it/s] - - -.. parsed-literal:: - - Extracting ./data/mnist/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/mnist/MNIST/raw - - Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz - Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz - - -.. parsed-literal:: - - 100%|██████████| 4542/4542 [00:00<00:00, 6970555.71it/s] - - -.. parsed-literal:: - - Extracting ./data/mnist/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/mnist/MNIST/raw - - - -Instantiate an Object of sconce -=============================== - -.. code:: ipython3 - - - sconces = sconce() - - -Set you Dataloader -================== - -.. code:: ipython3 - - - dataloader = {} - dataloader["train"] = train_loader - dataloader["test"] = test_loader - sconces.dataloader = dataloader - -#Enable snn in sconce - -.. code:: ipython3 - - - sconces.snn = True - - -Load your snn Model -=================== - -.. code:: ipython3 - - spike_grad = surrogate.fast_sigmoid(slope=25) - beta = 0.5 - snn_model = nn.Sequential( - nn.Conv2d(1, 12, 5), - nn.MaxPool2d(2), - snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), - nn.Conv2d(12, 64, 5), - nn.MaxPool2d(2), - snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True), - nn.Flatten(), - nn.Linear(64 * 4 * 4, 10), - snn.Leaky(beta=beta, spike_grad=spike_grad, init_hidden=True, output=True), - ).to('cuda') - - - -Load the pretrained weights -=========================== - -.. code:: ipython3 - - snn_pretrained_model_path = "drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth" - snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition - sconces.model = snn_model - -.. code:: ipython3 - - - sconces.optimizer = optim.Adam(sconces.model.parameters(), lr=1e-4) - sconces.scheduler = optim.lr_scheduler.CosineAnnealingLR(sconces.optimizer, T_max=200) - - sconces.criterion = SF.ce_rate_loss() - - sconces.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") - sconces.experiment_name = "snn-gmp" # Define your experiment name here - sconces.prune_mode = "GMP" - sconces.num_finetune_epochs = 1 - - -.. code:: ipython3 - - sconces.evaluate() - - -.. parsed-literal:: - - - - - -.. parsed-literal:: - - 97.11538461538461 - - - -.. code:: ipython3 - - sconces.compress() - - -.. parsed-literal:: - - - Original Dense Model Size Model=0.11 MiB - - -.. parsed-literal:: - - - -.. parsed-literal:: - - Original Model Validation Accuracy: 97.11538461538461 % - Granular-Magnitude Pruning - - -.. parsed-literal:: - - - -.. parsed-literal:: - - Sensitivity Scan Time(secs): 204.14258646965027 - Sparsity for each Layer: {'0.weight': 0.6500000000000001, '3.weight': 0.5000000000000001, '7.weight': 0.7000000000000002} - Pruning Time Consumed (mins): 2.8362054 - Total Pruning Time Consumed (mins): 3.402399698893229 - - -.. parsed-literal:: - - - -.. parsed-literal:: - - - Pruned Model has size=0.05 MiB(non-zeros) = 43.13% of Original model size - - -.. parsed-literal:: - - - -.. parsed-literal:: - - - Pruned Model has Accuracy=95.94 MiB(non-zeros) = -1.17% of Original model Accuracy - - - ========== Fine-Tuning ========== - - -.. parsed-literal:: - - - -.. parsed-literal:: - - Epoch:1 Train Loss: 0.00000 Validation Accuracy: 95.96354 - - -.. parsed-literal:: - - - -.. parsed-literal:: - - - ................. Comparison Table ................. - Original Pruned Reduction Ratio - Latency (ms) 16.7 15.6 1.1 - MACs (M) 160 160 1.0 - Param (M) 0.01 0.01 1.0 - Accuracies (%) 97.115 95.964 -1.152 - Fine-Tuned Sparse model has size=0.05 MiB = 43.13% of Original model size - Fine-Tuned Pruned Model Validation Accuracy: 95.96354166666667 - - -.. parsed-literal:: - - /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. - warnings.warn('No handlers found: "{}". Skipped.'.format( - /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. - warnings.warn('No handlers found: "{}". Skipped.'.format( - From 0bcf803f840a065471f7955e3f3077a123f1c300 Mon Sep 17 00:00:00 2001 From: Sathyaprakash Narayanan Date: Tue, 5 Dec 2023 12:04:47 -0800 Subject: [PATCH 7/9] pushed with comments --- .DS_Store | Bin 6148 -> 6148 bytes .../sconce_model_compression_pruning.rst | 48 +++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/.DS_Store b/.DS_Store index fdc236bed171a429ab259358e1fd97d66b9db481..faeb406419f9794a9ff7c96ecf33229a87196d48 100644 GIT binary patch delta 52 zcmZoMXffEp#>!OAG}(YvY_c3H7fXEZ#rnxQtST^u#N-@SKBfc)paPN26Ie@x0Dl(_ AUjP6A delta 52 zcmZoMXffEp#>(X8GTDGtY_c3H7mGx`fA(Y@RuvdSVsZ{EACm$DP=Uzi39KbT08qLN AdH?_b diff --git a/docs/tutorials/sconce_model_compression_pruning.rst b/docs/tutorials/sconce_model_compression_pruning.rst index 6ef3a9f9..3c30d561 100644 --- a/docs/tutorials/sconce_model_compression_pruning.rst +++ b/docs/tutorials/sconce_model_compression_pruning.rst @@ -56,18 +56,6 @@ Import Libraries "The current runtime does not have CUDA support." \ "Please go to menu bar (Runtime - Change runtime type) and select GPU" -.. code:: ipython3 - - from google.colab import drive - drive.mount('/content/drive') - - - -.. parsed-literal:: - - Mounted at /content/drive - - **Spiking Neural Network Compression** ====================================== @@ -249,10 +237,13 @@ Load the pretrained weights .. code:: ipython3 - snn_pretrained_model_path = "drive/MyDrive/Efficientml/Efficientml.ai/snn_model.pth" + snn_pretrained_model_path = "./snn_model.pth" snn_model.load_state_dict(torch.load(snn_pretrained_model_path)) # Model Definition sconces.model = snn_model +Set the Optimizizer and Type of Pruning Operation to Perform on the model +========================================================================= + .. code:: ipython3 @@ -267,6 +258,9 @@ Load the pretrained weights sconces.num_finetune_epochs = 1 +Test the Pre-Trained Model Accuracy +=================================== + .. code:: ipython3 sconces.evaluate() @@ -284,6 +278,21 @@ Load the pretrained weights +Prune the Model +=============== + +The Compression does a series of steps as explained below: + +1. It evaluates the dense model accuracy +2. Given the model, the package finds the best parameters for pruning + such that the accuracy degradation is minimal. +3. The retreived optimal parameters from the above steps are used to + prune the model. +4. At times, certain pruning techniques might require a fine-tuning on + the dataset. For which the pruned model is fine-tuned on the dataset. +5. Pruned Model is saved and Compared for Latency, Paramater, MAC and + model size. + .. code:: ipython3 sconces.compress() @@ -373,3 +382,16 @@ Load the pretrained weights /usr/local/lib/python3.10/dist-packages/torchprofile/profile.py:22: UserWarning: No handlers found: "prim::pythonop". Skipped. warnings.warn('No handlers found: "{}". Skipped.'.format( + +Note: +===== + +- The Latency is reduced and Parameters will be reduced(the numbers are + rounded to .2f hence we cannnot see the parameter pruning here, large + model will be able to showcase this deliberately), +- The MAC is remains the same, sicne the Conv operation used here is + Conv2d and the MAC are calculated Channel Wise and **not Element + Wise**. +- If specialised sparsity aware, Software/Hardware is used then we + reach the ultimate goal of compressing the model and leveraging the + inherent sparsity in the model From 5502dd51af125abe1f1368c7ac01329fc69ee5ac Mon Sep 17 00:00:00 2001 From: satabios Date: Wed, 13 Dec 2023 16:37:48 -0800 Subject: [PATCH 8/9] updated purning docs --- examples/sconce_model_compression_pruning.ipynb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/examples/sconce_model_compression_pruning.ipynb b/examples/sconce_model_compression_pruning.ipynb index 58a285b4..3bbc2a69 100644 --- a/examples/sconce_model_compression_pruning.ipynb +++ b/examples/sconce_model_compression_pruning.ipynb @@ -1,5 +1,21 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Model Pruning\n", + "\n", + "A Detialed Explanation/Tutorial for Pruning on Deep Learning can be found here:[Pruning Basics and DL implementation walkthrough](https://github.com/satabios/sconce/blob/main/tutorials/Pruning.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# snn Model Pruning" + ] + }, { "cell_type": "markdown", "metadata": { From 7dd86b184ebf3dab51cd5f5af891fa061b6b5aaa Mon Sep 17 00:00:00 2001 From: satabios Date: Wed, 13 Dec 2023 16:40:46 -0800 Subject: [PATCH 9/9] added rst --- docs/tutorials/sconce_model_compression_pruning.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/tutorials/sconce_model_compression_pruning.rst b/docs/tutorials/sconce_model_compression_pruning.rst index 3c30d561..a636392b 100644 --- a/docs/tutorials/sconce_model_compression_pruning.rst +++ b/docs/tutorials/sconce_model_compression_pruning.rst @@ -1,3 +1,13 @@ +Model Pruning +============= + +A Detialed Explanation/Tutorial for Pruning on Deep Learning can be +found here:`Pruning Basics and DL implementation +walkthrough `__ + +snn Model Pruning +================= + Install Packages ================