<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>anwaar-khalid - Compression</title><link href="https://hello-fri-end.github.io/" rel="alternate"/><link href="https://hello-fri-end.github.io/feeds/compression.atom.xml" rel="self"/><id>https://hello-fri-end.github.io/</id><updated>2024-08-10T00:00:00+05:30</updated><entry><title>Model Compression: A Survey of Techniques</title><link href="https://hello-fri-end.github.io/2024/08/model-compression-a-survey-of-techniques/" rel="alternate"/><published>2024-08-10T00:00:00+05:30</published><updated>2024-08-10T00:00:00+05:30</updated><author><name>Anwaar Khalid</name></author><id>tag:hello-fri-end.github.io,2024-08-10:/2024/08/model-compression-a-survey-of-techniques/</id><summary type="html">&lt;p&gt;Machine Learning (ML) has witnessed a surge in interest in recent years driven by the availability of large-scale datasets, advances in ML frameworks such as PyTorch and TensorFlow, rise of hardware accelerators (e.g., GPUs and TPUs) that enable large-scale training, and the development of increasingly powerful neural network architectures …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Machine Learning (ML) has witnessed a surge in interest in recent years driven by the availability of large-scale datasets, advances in ML frameworks such as PyTorch and TensorFlow, rise of hardware accelerators (e.g., GPUs and TPUs) that enable large-scale training, and the development of increasingly powerful neural network architectures. Together, these factors have enabled the creation of highly capable models with wide ranges of applications.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://arxiv.org/abs/2001.08361"&gt;Scaling Laws&lt;/a&gt; have demonstrated model performance improves predictably with the increase in model size, dataset size and compute. This has led companies to train even larger models on massive datasets, requiring a vast amount of computational resources. As a consequence, it is expected that the cost of training models will &lt;a href="https://mpost.io/ai-model-training-costs-are-expected-to-rise-from-100-million-to-500-million-by-2030/"&gt;rise from $100 Million to $500 Million by 2030&lt;/a&gt;. At the same time, &lt;a href="https://www.iea.org/"&gt;International Energy Agency&lt;/a&gt; projects that the energy requirement for AI could &lt;a href="https://www.iea.org/reports/energy-and-ai/energy-demand-from-ai?utm_source=chatgpt.com"&gt;double&lt;/a&gt; over the same period.  &lt;/p&gt;
&lt;p&gt;Beyond training costs, deployment introduces another critical constraint for these models: user experience. Models must operate with low latency &amp;amp; high throughput to be practical in real-world applications. Therfore, it has become increasingly important to develop techniques to optimize these models and make them faster, greener and more cost efficient.&lt;/p&gt;
&lt;p&gt;Enter Model Compression.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Model compression&lt;/strong&gt; refers to a set of algorithms that aim to reduce the size, memory and power requirements of neural networks without significantly impacting their accuracy. These include pruning redundant connections in the model, representing the parameters through lower precision datatypes, exploiting the structure of data to represent the layers more efficiently, or training smaller and more efficient models.&lt;/p&gt;
&lt;p&gt;The goal of this blogpost is to provide a high-level overview of these techniques.&lt;/p&gt;
&lt;h2 id="quantization-making-the-most-of-approximations"&gt;&lt;strong&gt;Quantization: Making the most of approximations&lt;/strong&gt;&lt;a class="headerlink" href="#quantization-making-the-most-of-approximations" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="what-is-quantization"&gt;&lt;strong&gt;What is Quantization?&lt;/strong&gt;&lt;a class="headerlink" href="#what-is-quantization" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modern models are typically trained using mixed precision, i.e  some operations in float16 or bfloat16, and others in float32. As a rule of thumb, a model with N billion parameters requires roughly 2 × N GB of memory when stored in 16 bits. For instance, a 3B parameter model needs about 6 GB of memory, a 7B model needs 14 GBs and so on.&lt;/p&gt;
&lt;p&gt;Quantization reduces this footprint by representing weights and activations using lower-precision data types such as int8, effectively halving memory requirements. Beyond memory savings, prior work (e.g., Horowitz, 2014) has showed that integer arithmetic is generally faster and more energy-efficient than floating-point computation.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Integer Quantization" src="/assets/images/quant_white_paper_formula.png"&gt;
&lt;em&gt;Quantization Steps: Source: &lt;a href="https://arxiv.org/pdf/2106.08295"&gt;A White Paper On Neural Network Quantization&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Quantization is therefore a key model compression technique for efficient deployment of neural networks on mobile phones and other resource-constrained edge devices.&lt;/p&gt;
&lt;h3 id="what-are-the-different-types-of-quantization-algorithms"&gt;&lt;strong&gt;What are the different types of quantization algorithms?&lt;/strong&gt;&lt;a class="headerlink" href="#what-are-the-different-types-of-quantization-algorithms" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quantization can be categorized along several axes, depending on when it is applied, how parameters are represented, and how computations are performed.&lt;/p&gt;
&lt;h4 id="based-on-when-it-is-applied-during-training-or-inference"&gt;&lt;strong&gt;Based on when it is applied: During Training or Inference?&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-when-it-is-applied-during-training-or-inference" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Quantization aware training (QAT)&lt;/strong&gt; simlutes inference-time quantization duering training by using quantized weights and activations in the forward pass, while using full precision weights in the backward pass. The idea is to emulate the inference time quantization error by using quantized values in the forward pass, and allowing the backward pass to update the full-precision weights such that they become more robust to quantization. This approach leads to better accuracy preservation as the model learns to adapt to the precision loss during training. &lt;/p&gt;
&lt;p&gt;&lt;img alt="An illustration of forward and backward pass for QAT" src="/assets/images/qat.webp"&gt;&lt;br&gt;
&lt;em&gt; Quantization Aware Training. Source: &lt;a href="https://www.researchgate.net/publication/351925867_Quantization_and_Deployment_of_Deep_Neural_Networks_on_Microcontrollers"&gt;Quantization and Deployment of Deep Neural Networks on Microcontrollers&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Unlike quantization aware training, where the model learns to adjust to lower precision during training, &lt;strong&gt;post-training quantization (PTQ)&lt;/strong&gt; directly applies this reduction in bit width after the model has been trained. While it may not offer the same level of accuracy preservation as quantization aware training, it is widely used in practice since it doesn’t require retraining the model.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PTQ Flow" src="https://pytorch.org/wp-content/uploads/2022/02/ptq-flowchart.svg"&gt;&lt;br&gt;
&lt;em&gt;Post Training Quantization. Source: &lt;a href="https://pytorch.org/blog/quantization-in-practice/#post-training-dynamicweight-only-quantization"&gt;Practical Quantization in PyTorch&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="based-on-how-quantization-parameters-are-applied"&gt;&lt;strong&gt;Based on how quantization parameters are applied:&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-how-quantization-parameters-are-applied" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Per-tensor quantization&lt;/strong&gt;: a single scale and offset are used for the entire tensor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-channel quantization&lt;/strong&gt;: each channel has its own scale and offset.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Block quantization&lt;/strong&gt;: groups of elements share quantization parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, finer-grained schemes (more parameters) yield better accuracy but introduce additional storage and compute overhead.&lt;/p&gt;
&lt;h4 id="based-on-how-activations-are-quantized"&gt;&lt;strong&gt;Based on how activations are quantized:&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-how-activations-are-quantized" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since weights are always known before inference they can be quantized offline, however, activations depend on the input to the model, and there are two ways of quantizing them.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;static quantization&lt;/strong&gt;, the min and max ranges of activations are calculated using a small subset of the training data called calibration dataset which typically contains around 300-500 samples. This technique is typically used when both memory bandwidth and compute savings are important with CNNs being a typical use case.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;dynamic quantization,&lt;/strong&gt; the min-max ranges are calculated on the fly during inference. This is useful in situations where the model execution time is dominated by loading weights from memory rather than computing the matrix multiplications. For example, this is true for LSTM and Transformer type models with small batch size.&lt;/p&gt;
&lt;h4 id="based-on-quantization-mapping"&gt;&lt;strong&gt;Based on quantization mapping:&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-quantization-mapping" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Affine quantization&lt;/strong&gt; incorporates scaling (Scale S) and a &lt;strong&gt;non zero shifting(Zero Point Z)&lt;/strong&gt; allowing for a flexible representation of a wide range of values by adjusting magnitude and position before rounding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Symmetric quantization&lt;/strong&gt; is a special case of affine quantization where-in the values are mapped to a symmetric range of values i.e [-a, a].  In this case, the integer space is usually [-127, 127], meaning that the -128 is opted out of the regular [-128, 127] signed int8 range. The reason is that having both ranges symmetric allows to have Z \= 0. While one value out of the 256 representable values is lost, it can provide a speedup since a lot of additional operations can be skipped.&lt;/p&gt;
&lt;h2 id="pruning-keeping-what-matters"&gt;&lt;strong&gt;Pruning: Keeping what matters&lt;/strong&gt;&lt;a class="headerlink" href="#pruning-keeping-what-matters" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="what-is-pruning"&gt;&lt;strong&gt;What is Pruning?&lt;/strong&gt;&lt;a class="headerlink" href="#what-is-pruning" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pruning is a model compression technique that involves removing redundant or less important weights from a neural network to reduce model size and compute while preserving accuracy. Typically, weights are pruned based on critera such as magnitude or their estimated impact on the model&amp;rsquo;s output.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pruning: before and after" src="/assets/images/pruning.png"&gt;&lt;br&gt;
&lt;em&gt;A neural network structure, before and after pruning. Source: &lt;a href="https://arxiv.org/abs/1506.02626"&gt;Learning both Weights and Connections for Efficient Neural Networks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="what-are-the-different-types-of-pruning-algorithms"&gt;&lt;strong&gt;What are the different types of pruning algorithms?&lt;/strong&gt;&lt;a class="headerlink" href="#what-are-the-different-types-of-pruning-algorithms" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pruning can also be categorized along several dimensions:&lt;/p&gt;
&lt;h4 id="based-on-how-weights-are-removed"&gt;&lt;strong&gt;Based on how weights are removed&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-how-weights-are-removed" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unstructured pruning&lt;/strong&gt;: removes individual weights without constraints, resulting in sparse weight matrices. While this can achieve high compression, it often requires specialized hardware or libraries to realize speedups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured pruning&lt;/strong&gt;: removes entire structures such as neurons, channels, or filters. This produces dense, smaller models that are more compatible with standard hardware and typically lead to practical speedups.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Semi-structured pruning&lt;/strong&gt; : imposes regular sparsity i.e  N:M patterns. For example, 2:4 sparsity, where for every group of 4 weights 2 are zeroed out. It&amp;rsquo;s a middle ground between structured and unstructured pruning and provides real speed ups (e.g on Nvidia sparse tensor cores.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="based-on-how-pruning-is-performed"&gt;&lt;strong&gt;Based on how pruning is performed?&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-how-pruning-is-performed" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One-shot pruning&lt;/strong&gt;: removes weights in a single step. It is simple but can lead to significant accuracy degradation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterative pruning&lt;/strong&gt; : gradually removes weights over multiple steps, often interleaved with fine-tuning. This approach generally preserves accuracy better.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="based-on-scope"&gt;&lt;strong&gt;Based on scope&lt;/strong&gt;&lt;a class="headerlink" href="#based-on-scope" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local pruning&lt;/strong&gt;: applies pruning independently within each layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global pruning&lt;/strong&gt;: selects weights to prune across the entire network based on a global criterion, often leading to better overall allocation of sparsity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tensorization-factorization-higher-ranking-doesnt-mean-better"&gt;&lt;strong&gt;Tensorization / Factorization: Higher ranking doesn’t mean better&lt;/strong&gt;&lt;a class="headerlink" href="#tensorization-factorization-higher-ranking-doesnt-mean-better" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="what-is-tensorization-factorization"&gt;&lt;strong&gt;What is Tensorization / Factorization?&lt;/strong&gt;&lt;a class="headerlink" href="#what-is-tensorization-factorization" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tensorization (or factorization) decomposes weight tensors into lower-rank components, reducing parameter count while preserving the most important structure in the data.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tucker Decomposition" src="/assets/images/tucker_decomposition.png"&gt;&lt;br&gt;
&lt;em&gt;Visual representation of the tensorization process applied to image data. Source: &lt;a href="https://aps.arxiv.org/pdf/1707.08308v2.pdf"&gt;Tensor Contraction and Regression Networks&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="what-are-the-different-types-of-tensorizationfactorization-algorithms"&gt;&lt;strong&gt;What are the different types of tensorization/factorization algorithms?&lt;/strong&gt;&lt;a class="headerlink" href="#what-are-the-different-types-of-tensorizationfactorization-algorithms" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are four popular tensor decomposition algorithms which are used extensively:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Singular Value Decomposition&lt;/strong&gt;: Decomposes a matrix A into UΣV⊤, where a low-rank approximation retains only the top singular values. This provides the most optimal rank-r approximation of the matrix (Eckart-Young theorem). It is directly applicable to 2D weights (e.g., linear layers); higher-dimensional tensors must be reshaped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canonical Polyadic Decomposition (CP or PARAFAC)&lt;/strong&gt;: Factorizes a tensor into a sum of rank-1 tensors. Each component captures a separable pattern across all modes, enabling direct decomposition of multi-dimensional weights without flattening.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tensor Train Decomposition&lt;/strong&gt; : Represents a high-dimensional tensor as a sequence of smaller, interconnected tensors (“cores”). This factorization scales linearly with the number of dimensions, making it efficient for very large tensors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tucker Decomposition&lt;/strong&gt;: Decomposes a tensor into a smaller core tensor multiplied by factor matrices along each mode. It can be seen as a higher-order generalization of SVD, allowing different ranks per dimension.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="CP and Tucker Decomposition" src="/assets/images/cp_and_tucker.png"&gt;
&lt;em&gt;Visual representation of CP &amp;amp; Tucker Decomposition. Source: &lt;a href="https://www.researchgate.net/figure/Illustration-of-a-the-CP-decomposition-and-b-the-Tucker-decomposition-of-a_fig1_316632383"&gt;Research Gate&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="how-is-tensorization-used-in-practice"&gt;&lt;strong&gt;How is Tensorization used in practice?&lt;/strong&gt;&lt;a class="headerlink" href="#how-is-tensorization-used-in-practice" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tensorly is the most popular library for working with tensor decompositions. It works with various frameworks such as Numpy, Pytorch and TensorFlow &amp;amp; provides off-the-shelf API for all the algorithms we discussed. Extending the core features of TensorLy, TensorLy-Torch provides out-of-the-box tensor layers that can be used to implement and train tensorized networks from scratch or fine-tuning existing models by replacing the layers with their tensorized counterparts. Some examples are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tensorly.org/torch/dev/user_guide/factorized_conv.html"&gt;&lt;strong&gt;Factorized Convolutions&lt;/strong&gt;&lt;/a&gt;: which decompose the convolution filter into two or more smaller filters, reducing parameter count and compute. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://tensorly.org/torch/dev/user_guide/tensorized_linear.html"&gt;&lt;strong&gt;Tensorized Linear Layers&lt;/strong&gt;&lt;/a&gt;: where the 2D weight matrix of a linear layer is first tensorized (reshaped into a higher dimensional tensor) and then factored using a high-dimensional decomposition / tensorization method.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tensorly.org/torch/dev/user_guide/factorized_embeddings.html"&gt;&lt;strong&gt;Factorized Embedding Layers&lt;/strong&gt;&lt;/a&gt;: which can act as a drop-in replacement for Pytorch’s embeddings but using efficient tensor parametrization that doesn’t need to reconstruct the table. &lt;/li&gt;
&lt;li&gt;&lt;a href="https://tensorly.org/torch/dev/user_guide/trl.html"&gt;&lt;strong&gt;Tensor Regression Layers&lt;/strong&gt;&lt;/a&gt;: eliminate fully connected layers by regressing directly on convolutional activations using low-rank tensor factorization. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;insert tensor regression image here&gt;&lt;/p&gt;
&lt;h2 id="knowledge-distillation-why-reinvent-the-wheel"&gt;&lt;strong&gt;Knowledge Distillation: Why reinvent the wheel?&lt;/strong&gt;&lt;a class="headerlink" href="#knowledge-distillation-why-reinvent-the-wheel" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="what-is-knowledge-distillation"&gt;&lt;strong&gt;What is Knowledge Distillation?&lt;/strong&gt;&lt;a class="headerlink" href="#what-is-knowledge-distillation" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Knowledge distillation involves transferring knowledge from a complex, large model (teacher network) to a smaller, simpler neural network (student model). The goal here is to leverage the knowledge learned by a large model to train a small model to mimic it&amp;rsquo;s behavior. &lt;/p&gt;
&lt;p&gt;At a high-level, knowledge distillation involves two main steps: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Training the teacher model&lt;/strong&gt;: Involves training the teacher network to a desired level of accuracy   &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Training the student model&lt;/strong&gt;: Using the outputs of the teacher network as soft targets i.e probability distributions over the classes instead of hard labels&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="Knowledge Distillation" src="/assets/images/kd.png"&gt;
&lt;em&gt;An illustration of the knowledge distillation process. Source: &lt;a href="https://arxiv.org/pdf/2006.05525.pdf"&gt;Knowledge Distillation: A Survey&lt;/a&gt;&lt;/em&gt;  &lt;/p&gt;
&lt;h3 id="what-are-the-different-types-of-knowledge-distillation-algorithms"&gt;&lt;strong&gt;What are the different types of knowledge distillation algorithms?&lt;/strong&gt;&lt;a class="headerlink" href="#what-are-the-different-types-of-knowledge-distillation-algorithms" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Knowledge Distillation techniques generally pertains to one of the following categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offline Distillation: is the most common distillation technique and involves using a trained model (teacher) to guide another smaller model (student). &lt;/li&gt;
&lt;li&gt;Online Distillation: is used when a trained teacher model isn’t available. In online distillation, both teacher and student models are updated simultaneously in a single end-to-end training process.&lt;/li&gt;
&lt;li&gt;Self Distillation: is a special case of online distillation and involves using the same model as the teacher as well as the student. In self-distillation, knowledge from deeper layers of the network is used to guide the shallow layers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Models may keep scaling, but so do the bills. Model compression is how we push back.
Hope this gave you a useful overview. Thank you for reading!&lt;/p&gt;</content><category term="Compression"/></entry><entry><title>Case Study: Compressing DeepMind’s RepNet for Edge Deployment</title><link href="https://hello-fri-end.github.io/2024/06/case-study-compressing-deepminds-repnet-for-edge-deployment/" rel="alternate"/><published>2024-06-10T00:00:00+05:30</published><updated>2024-06-10T00:00:00+05:30</updated><author><name>Anwaar Khalid</name></author><id>tag:hello-fri-end.github.io,2024-06-10:/2024/06/case-study-compressing-deepminds-repnet-for-edge-deployment/</id><summary type="html">&lt;p&gt;In this case study, we explore compressing neural networks for efficient deployment on edge devices with limited resources. We explore practical techniques like quantization, pruning, and tensorization using off-the-shelf open-source tools. &lt;/p&gt;
&lt;p&gt;Our aim is to illustrate a typical model compression workflow, highlighting the approaches and techniques used to analyse a …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In this case study, we explore compressing neural networks for efficient deployment on edge devices with limited resources. We explore practical techniques like quantization, pruning, and tensorization using off-the-shelf open-source tools. &lt;/p&gt;
&lt;p&gt;Our aim is to illustrate a typical model compression workflow, highlighting the approaches and techniques used to analyse a model from a compression perspective, and how to select and apply the appropriate compression techniques expected to yield the best results. We begin our experiment by understanding the target model and how it behaves, before diving into sequentially applying several state-of-the-art techniques and explaining the thought process at each step. &lt;/p&gt;
&lt;p&gt;Join us as we go through this process to learn useful insights and optimization strategies to use when deploying models on mobile and edge devices!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;NB: If you&amp;rsquo;re interested in trying out these techniques, check out our demo notebook &lt;a href="https://colab.research.google.com/drive/1oUzrLxEICgdWWuc4PnS3SoD9edmwHHi_#scrollTo=rh74CTuW2-cV"&gt;here&lt;/a&gt; which you can use to follow along. It provides all the code used to reproduce and experiment with the results presented here.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="understanding-the-model"&gt;&lt;strong&gt;Understanding the Model&lt;/strong&gt;&lt;a class="headerlink" href="#understanding-the-model" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this experiment, we’ll attempt to compress Google DeepMind&amp;rsquo;s &lt;a href="https://sites.google.com/view/repnet"&gt;RepNet&lt;/a&gt; model. RepNet is a model that predicts the count of repeating actions in a video. Because the model is class agnostic, it can be used to detect various repetitive actions in different contexts. &lt;/p&gt;
&lt;p&gt;We chose RepNet for this demonstration because the base model performance is not suitable for running on mobile (as we’ll see below) making it a good candidate for compression. The architecture includes a diverse set of layers that we use to illustrate the use of different compression techniques. &lt;/p&gt;
&lt;p&gt;&lt;img alt="RepNet" src="/assets/images/Repnet-arch.png"&gt;
Figure 2. RepNet Model Architecture. Source &lt;a href="https://sites.google.com/view/repnet"&gt;RepNet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;RepNet is composed of three fundamental building blocks, namely:
* An encoder layer that generates per-frame embeddings from the input videos.
* A temporal self-similarity layer that computes all pairwise similarities (distances) between the calculated frame embeddings.
* A period predictor block that outputs the (a) per-frame binary periodicity classification (whether the frame relates to a repeated action or not) and (b) per-frame period length estimation (how many times the action was carried until the frame).&lt;/p&gt;
&lt;h2 id="building-intuition-for-compression"&gt;&lt;strong&gt;Building Intuition for Compression&lt;/strong&gt;&lt;a class="headerlink" href="#building-intuition-for-compression" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="profiling"&gt;&lt;strong&gt;Profiling&lt;/strong&gt;&lt;a class="headerlink" href="#profiling" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Instead of blindly applying model compression techniques to the model, it is common practice to identify the latency and memory bottlenecks within the network to perform a more targeted and efficient compression. This involves running and timing inference on each major block making up the architecture as well as their trainable parameters. &lt;/p&gt;
&lt;p&gt;To measure this, we run inference on a sample of inputs while making sure to separately time each submodule’s latency and measure the number of trainable parameters at each step. This allows us to track how the overall inference runtime and parameters count evolves from one layer to the next, and assess the contribution of each layer to the total inference time and model weight. We start with a warm-up of 10 initial runs and follow up with 32 runs to assess the speed of each component, with results averaged for accuracy. Using PyTorch on a Tesla V100-PCIE-16GB GPU, the script focuses on specific elements such as the encoder, temporal convolutional layers, TSM convolutional layer, period length head, and periodicity head, as the main building blocks of the architecture.&lt;/p&gt;
&lt;p&gt;To ensure accuracy, we explicitly define the input sizes for these components based on the expected sequence of input transformation events during inference. For example, the encoder begins with an input size of 64 which is subsequently transformed to an input size of 1 at the temporal convolution layer, and so on. Consequently, we assume an input size of 1 for the temporal convolution layer and 64 for the encoder, precisely aligning with the actual input transformation process.&lt;/p&gt;
&lt;p&gt;The analysis reveals that the &lt;strong&gt;temporal convolution submodule makes up half of the model’s weights &lt;/strong&gt;but the &lt;strong&gt;encoder submodule is responsible for more than two-thirds of the overall latency. &lt;/strong&gt;It is thus natural to focus our efforts on these two submodules.&lt;/p&gt;
&lt;p&gt;&lt;img alt="RepNet" src="/assets/images/parameter-contribution.png"&gt;
Fig 3. Parameter and Latency Contribution of each Submodule&lt;/p&gt;
&lt;h3 id="baseline-benchmarks"&gt;&lt;strong&gt;Baseline Benchmarks&lt;/strong&gt;&lt;a class="headerlink" href="#baseline-benchmarks" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To assess the impact of applying model compression techniques, we track three key metrics namely &lt;strong&gt;Model Size, Accuracy, &lt;/strong&gt;and &lt;strong&gt;Latency.&lt;/strong&gt; For Accuracy, we look at the average OBO (Off by One) error, which shows the percentage of times predictions were off by just one count. Additionally, we use the Average MAE (Mean Absolute Error) to get the average normalised absolute error of predictions. Tracking these metrics helps us understand how the model performs in terms of size, accuracy, and speed, and how these values change when we apply compression techniques. &lt;/p&gt;
&lt;p&gt;We use the &lt;a href="https://tomrunia.github.io/projects/repetition/"&gt;QUVARepetetions Dataset&lt;/a&gt; for benchmarking. The dataset consists of 100 short videos of various repeating actions and is also used by the authors of the model to initially benchmark the performance against comparable architectures. &lt;/p&gt;
&lt;p&gt;Since compression is highly hardware-dependent, we begin by running the benchmarks on the target device early on. For this, we use Pytorch&amp;rsquo;s &lt;a href="https://pytorch.org/tutorials/recipes/mobile_perf.html#android-benchmarking-setup"&gt;benchmarking binary&lt;/a&gt; to run a _scripted _pytorch model on the device without having to learn anything about mobile development. &lt;/p&gt;
&lt;p&gt;The table below shows benchmark results for the baseline model on a Samsung S22 Plus phone with batch sizes of 1, 8, and 16. Each frame has dimensions 112x112 and the model processes 64 frames per iteration.  The model size is 97.97 MB, with OBO at 0.32 and MAE at 0.17.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Input Dimensions&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Seconds Per Iteration&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Iteration Per Second&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;2.77
   &lt;/td&gt;
   &lt;td&gt;0.36
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;22.5
   &lt;/td&gt;
   &lt;td&gt;0.04
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;16,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;49.3
   &lt;/td&gt;
   &lt;td&gt;0.02
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id="compressing-the-model"&gt;&lt;strong&gt;Compressing the Model&lt;/strong&gt;&lt;a class="headerlink" href="#compressing-the-model" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We can begin our compression experiments now that we have a better idea of various memory and latency bottlenecks in our model and how well it runs on the target device. We first explore pruning, tensorization, and quantization in isolation before experimenting with various combinations of these techniques.&lt;/p&gt;
&lt;h3 id="tensorization"&gt;&lt;strong&gt;Tensorization&lt;/strong&gt;&lt;a class="headerlink" href="#tensorization" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tensorization involves representing the weights of layers of neural networks in a factorized form using tensor decomposition methods like Tucker, CP, etc. &lt;a href="https://tensorly.org/torch/dev/"&gt;Tensorly-Torch&lt;/a&gt; provides off-the-shelf factorized layers that can directly be replaced with their standard pytorch counterparts where the pre-trained weights are used to initialise the layers. However, Tensorly-Torch is not compatible with _torch.jit.script _as highlighted in this &lt;a href="https://github.com/tensorly/torch/issues/33"&gt;issue&lt;/a&gt;, so we use TensorLy to perform the decompositions and implement custom factorized layers.&lt;/p&gt;
&lt;p&gt;Tensorization works especially well with convolutional layers where kernels of any order ( 2D, 3D, or more!) can be parametrized efficiently using tensor decomposition algorithms. For example, using a CP decomposition results in a separable convolution that can replace your original convolution with a series of small efficient ones that work better on mobile phones.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tensorizing the Convolution Layer&lt;/strong&gt;. Because the Temporal Convolution layer consists of the maximum number of parameters, we begin by tensorizing this layer first. Running experiments using different decomposition ranks shows that a rank of 0.1 yields the best results in this case. With this rank, the model size decreases to 62.85MB, and the inference time is cut in half. Notably, prediction accuracy improves with OBO decreasing to 0.29 and MAE to 0.167, without fine-tuning the model. While this is not a common result, we believe the model’s original performance may be impacted by inherent noise, and we attribute the increase in prediction accuracy to the regularisation effect of tensorization which reduces the noise in the network, allowing it to generalise better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tensorizing the Feature Extractor&lt;/strong&gt;. Tensorizing this part was challenging as it includes numerous convolutional layers, making grid searching for optimal ranks impractical. Rank selection for tensor decompositions is a hard problem because determining the optimal rank involves finding the right balance between capturing the essential structure of the underlying data and avoiding overfitting. Selecting a rank that is too low may result in a loss of important information, leading to a poor approximation of the original tensor. On the other hand, selecting a rank that is too high can lead to overfitting, where the model captures noise or irrelevant details, hindering generalisation to new data. Additionally, the computational complexity of searching for the optimal rank increases as the number of possible ranks grows exponentially.&lt;/p&gt;
&lt;p&gt;Practitioners generally rely on heuristics or meta-learning for determining the optimal ranks. The authors of &lt;a href="https://arxiv.org/abs/1511.06530"&gt;Compression of Deep Convolutional Neural Networks for Fast and Low Power Mobile&lt;/a&gt; have used Variational Bayesian Matrix Factorization (VBMF) successfully on various networks, including VGG-16 and AlexNet. VBMF is a complicated technique and out of the scope of this post, but on a high level, it involves approximating a matrix V&lt;sub&gt;LxM&lt;/sub&gt; as the sum of a lower-ranking matrix B&lt;sub&gt;LxH&lt;/sub&gt;A&lt;sup&gt;T&lt;sub&gt;HxM&lt;/sub&gt;&lt;/sup&gt; and Gaussian noise. After A and B are found, H acts as an upper bound on the rank. VBMF is a very promising tool because it can automatically find noise variance, and rank and even provide theoretical condition for perfect rank recovery (&lt;a href="https://www.jmlr.org/papers/volume14/nakajima13a/nakajima13a.pdf"&gt;Nakajima et al., 2012&lt;/a&gt;). We determined the rank R3 and R4 by applying global analytic VBMF on mode-3 and mode-4 matricization of the convolutional kernel tensors K and managed to reduce the model size to 84.19 MB. In this case, MAE error increased to 0.27 while OBO decreased to 0.29. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tensorizing the Entire Encoder&lt;/strong&gt;. Finally, we experiment with tensorizing the entire encoder as it makes up for the bulk of the latency and memory footprint of the model. We use VMBF ranks for the Resnet50 part and a set of different ranks for the Conv3D layer and get the best results with a rank of 0.1. We can see an average of 1.7x speedup across all the batch sizes. This is mainly due to the reduction in arithmetic intensity as a result of tensorization. The model size is reduced to 51.8MB, OBO decreases to 0.30 while MAE increases to 0.21. The results are summarised in the following table:&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Input Dimensions&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Seconds Per Iter&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Iter Per Second&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;1.51
   &lt;/td&gt;
   &lt;td&gt;0.66
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;12.9
   &lt;/td&gt;
   &lt;td&gt;0.08
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;16,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;32.4
   &lt;/td&gt;
   &lt;td&gt;0.03
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h3 id="pruning"&gt;&lt;strong&gt;Pruning&lt;/strong&gt;&lt;a class="headerlink" href="#pruning" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pruning is a technique aimed at reducing the size of a deep neural network by eliminating non-essential parameters, or weights. &lt;strong&gt;Structured Pruning&lt;/strong&gt; involves removing specific structures like neurons in a fully connected layer, channels or filters in a convolutional layer, or self-attention heads in transformers. Conversely, &lt;strong&gt;Unstructured Pruning&lt;/strong&gt; targets and removes individual weights based on criteria such as magnitude or their contribution to the overall loss function during training, without adhering to a specific structural pattern.&lt;/p&gt;
&lt;p&gt;Given the intended scope of our solution, where we want to deploy on as wide a range of devices as possible, we focus on structured pruning as it is a more hardware-agnostic technique. Because unstructured pruning introduces sparsity in the model, it is generally not suitable for all hardware devices, for example, GPUs are not well-suited for processing sparse matrices, and using unstructured pruning on devices with embedded GPU won’t yield significant performance improvements. Conversely, structural pruning involves removing entire neurons, layers, or other structured components from the neural network to reduce size and complexity and is thus a viable solution regardless of the device. For our experiments, we use the &lt;a href="https://github.com/VainF/Torch-Pruning"&gt;Torch-Pruner&lt;/a&gt; library which provides off-the-shelf utilities for structured pruning. &lt;/p&gt;
&lt;p&gt;In the following table, benchmark results demonstrate an average 2.19x latency reduction achieved by the pruned model compared to the baseline. Notably, we reduced the model size to approximately 58% of the original model size by pruning 35% of the ResNet encoder layer and 64% of the Temporal Convolution layer. There is a minimal drop in accuracy with MAE at 0.22 and OBO at 0.34.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Input Dimensions&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Seconds Per Iter&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Iter Per Second&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;1.25
   &lt;/td&gt;
   &lt;td&gt;0.79
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;10.8
   &lt;/td&gt;
   &lt;td&gt;0.08
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;16,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;22.2
   &lt;/td&gt;
   &lt;td&gt;0.05
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h3 id="quantization"&gt;&lt;strong&gt;Quantization&lt;/strong&gt;&lt;a class="headerlink" href="#quantization" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quantization refers to the process of converting the weights of a neural network from a high-precision bidwidth (e.g. float32) to a lower precision bidwidth (e.g. float16 or int8). We use Pytorch&amp;rsquo;s built-in Quantization API for all of our quantization experiments. &lt;/p&gt;
&lt;p&gt;To determine which parts of the model to target for quantization, we first test-quantize the whole model and measure the &lt;a href="https://en.wikipedia.org/wiki/Signal-to-quantization-noise_ratio"&gt;Signal-To-Quantization-Noise-Ratio (SQNR)&lt;/a&gt; of each layer of the quantized model to quantify the sensitivity of the weights and activations to quantization. SQNR measures the amount of signal power (or information in this context) lost due to quantization, relative to the amount of noise introduced by quantization. A higher SQNR indicates that the layer is less affected by quantization noise, and therefore should be targeted for quantization to minimise accuracy loss. Running this sensitivity reveals that, consistent with the inference sensitivity we conducted above, both the temporal and the encoder layers are better fit for quantization.&lt;/p&gt;
&lt;p&gt;We run the final quantization experiments using post-training static quantization which involves calibrating the quantization parameters ahead of time and allows us to compress the model more than we would using dynamic quantization which defines the quantization values during inference. With post-training static quantization, we reduce the model size by 12% and improve model inference by 1.2x speedup compared to the baseline model. Accuracy drop is minimal with OBO at 0.38 and MAE at 0.31. The results are summarised in the following table:&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Input Dimensions&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Seconds Per Iteration&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Iterations Per Second&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;2.64
   &lt;/td&gt;
   &lt;td&gt;0.380
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;16.3
   &lt;/td&gt;
   &lt;td&gt;0.060
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;16,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;46.9
   &lt;/td&gt;
   &lt;td&gt;0.022
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id="combining-compression-techniques"&gt;&lt;strong&gt;Combining Compression Techniques&lt;/strong&gt;&lt;a class="headerlink" href="#combining-compression-techniques" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having studied the impacts on the performance of each group of compression techniques, we move into combining them to achieve the best overall performance improvement.&lt;/p&gt;
&lt;p&gt;We begin with pruning the model since the use of structured pruning allows us to contain the impact on the model architecture to the removal of layers and blocks of neurons, and doesn’t introduce new components that may be incompatible with the other techniques.&lt;/p&gt;
&lt;p&gt;We proceed to apply tensorization on the pruned model, leaving quantization at the end since quantizing the model replaces all the target layers with quantized versions that are not supported by the tensorization operations we use. Combining tensorization with pruning only minimally impacts accuracy but already allows us to achieve almost double the speed-up obtained from pruning only. Interestingly, we also separately tried tensorizing the model first before pruning it and this resulted in a significant dip in accuracy, which highlights the importance of conducting thorough tests when designing optimization pipelines.&lt;/p&gt;
&lt;p&gt;Finally, applying SQNR-guided post-training static quantization to the pruned and tensorized model allows us to achieve an overall speed-up of 3.5x compared to the baseline model and a 70.5% decrease in model size. Impact on accuracy compounds but remains within acceptable boundaries with OBO at 0.40 and MAE at 0.21. This lost accuracy can be recovered by subsequent fine-tuning.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Input Dimensions&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Seconds Per Iteration&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Iterations Per Second&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;0.85
   &lt;/td&gt;
   &lt;td&gt;1.17
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;8,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;6.3
   &lt;/td&gt;
   &lt;td&gt;0.142
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;16,3,64,112,112
   &lt;/td&gt;
   &lt;td&gt;13.5
   &lt;/td&gt;
   &lt;td&gt;0.073
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id="conclusion"&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;a class="headerlink" href="#conclusion" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Through this blog post, we have outlined the overall process for compressing a model and optimising it for edge deployment by using various techniques and libraries. The following table summarises the results achieved with all the techniques:&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Setup&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Model Size (MB)&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;Speedup&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;OBO&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;&lt;strong&gt;MAE&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Baseline&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;97.97
   &lt;/td&gt;
   &lt;td&gt;-
   &lt;/td&gt;
   &lt;td&gt;0.32
   &lt;/td&gt;
   &lt;td&gt;0.17
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Tensorized&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;51.80
   &lt;/td&gt;
   &lt;td&gt;1.7x
   &lt;/td&gt;
   &lt;td&gt;0.30
   &lt;/td&gt;
   &lt;td&gt;0.21
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Pruned&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;57.56
   &lt;/td&gt;
   &lt;td&gt;2.19x
   &lt;/td&gt;
   &lt;td&gt;0.34
   &lt;/td&gt;
   &lt;td&gt;0.22
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Quantized&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;86.14
   &lt;/td&gt;
   &lt;td&gt;1.2x
   &lt;/td&gt;
   &lt;td&gt;0.38
   &lt;/td&gt;
   &lt;td&gt;0.31
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;&lt;strong&gt;Pruning + Tensorization + Quantization&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;28.92
   &lt;/td&gt;
   &lt;td&gt;3.5x
   &lt;/td&gt;
   &lt;td&gt;0.40
   &lt;/td&gt;
   &lt;td&gt;0.21
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Of course, the proposed solution isn&amp;rsquo;t necessarily the best depending on the use case, and selecting the appropriate hyperparameters/compression modes often boils down to running several tests to find the combination that strikes the best balance in terms of performance vs accuracy, taking into account one&amp;rsquo;s personal objectives that may involve putting more emphasis on either metrics.&lt;/p&gt;
&lt;p&gt;The libraries and tools we used are also not the only ones offering the utilities we needed, and we encourage you to explore using other tools as well! A good place to get started would be our blog post series on model compression where we outline the various tools available and what they uniquely offer, feel free to give it a read to learn more on the topic!&lt;/p&gt;
&lt;p&gt;We hope this was informational and that it gave you some insights regarding model optimization, see you in the next one!&lt;/p&gt;
&lt;h2 id="acknowledgements"&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;a class="headerlink" href="#acknowledgements" title="Permanent link"&gt;&amp;para;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This case study was conducted at Unify with the collaborative efforts of Muhammad Elaseyad, Nassim Berrada, and Guillermo Brizeula.
I extend my gratitude to their valuable contributions and insights throughout the project.&lt;/p&gt;</content><category term="Compression"/></entry></feed>