--- title: Sparsifier keywords: fastai sidebar: home_sidebar summary: "Make your neural network sparse" description: "Make your neural network sparse" nb_path: "nbs/01a_sparsifier.ipynb" ---
{% raw %}
{% endraw %} {% raw %}
{% endraw %}

A sparse vector, as opposed to a dense one, is a vector which contains a lot of zeroes. When we speak about making a neural network sparse, we thus mean that the network's weight are mostly zeroes.

With fasterai, you can do that thanks to the Sparsifier class.

Let's start by creating a model

{% raw %}
model = resnet18()
{% endraw %}

As you probably know, weights in a convolutional neural network have 4 dimensions ($ c_{out} \times c_{in} \times k_h \times k_w$)

{% raw %}
model.conv1.weight.ndim
4
{% endraw %}

In the case of ResNet18, the dimension of the first layer weights is $64 \times 3 \times 7 \times 7$. We thus can plot each of the $64$ filter as a $7 \times 7$ color image (because they contains $3$ channels).

{% raw %}
plot_kernels(model.conv1)
{% endraw %} {% raw %}

class Sparsifier[source]

Sparsifier(model, granularity, method, criteria, layer_type=Conv2d)

{% endraw %} {% raw %}
{% endraw %}

The Sparsifier class allows us to remove some (part of) the filters, that are considered to be less useful than others. This can be done by first creating an instance of the class, specifying:

  • The granularity, i.e. the part of filters that you want to remove. Typically, we usually remove weights, vectors, kernels or even complete filters.
  • The method, i.e. if you want to consider each layer independently (local), or compare the parameters to remove across the whole network (global).
  • The criteria, i.e. the way to assess the usefulness of a parameter. Common methods compare parameters using their magnitude, the lowest magnitude ones considered to be less useful.

User can pass a single layer to prune by using the Sparsifier.prune_layer method.

{% raw %}

Sparsifier.prune_layer[source]

Sparsifier.prune_layer(m, sparsity, round_to=None)

{% endraw %} {% raw %}
model = resnet18()
pruner = Sparsifier(model, 'filter', 'local', large_final)
pruner.prune_layer(model.conv1, 70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 70.31%
Sparsity in Conv2d 7: 0.00%
Sparsity in Conv2d 10: 0.00%
Sparsity in Conv2d 13: 0.00%
Sparsity in Conv2d 16: 0.00%
Sparsity in Conv2d 20: 0.00%
Sparsity in Conv2d 23: 0.00%
Sparsity in Conv2d 26: 0.00%
Sparsity in Conv2d 29: 0.00%
Sparsity in Conv2d 32: 0.00%
Sparsity in Conv2d 36: 0.00%
Sparsity in Conv2d 39: 0.00%
Sparsity in Conv2d 42: 0.00%
Sparsity in Conv2d 45: 0.00%
Sparsity in Conv2d 48: 0.00%
Sparsity in Conv2d 52: 0.00%
Sparsity in Conv2d 55: 0.00%
Sparsity in Conv2d 58: 0.00%
Sparsity in Conv2d 61: 0.00%
Sparsity in Conv2d 64: 0.00%
{% endraw %}

Most of the time, we may want to prune the whole model at once, using the Sparsifier.prune_model method, indicating the percentage of sparsity to you want to apply.

{% raw %}

Sparsifier.prune_model[source]

Sparsifier.prune_model(sparsity, round_to=None)

{% endraw %}

There are several ways in which we can make that first layer sparse. You will find the most important below:

{% raw %}
model = resnet18()
pruner = Sparsifier(model, 'weight', 'local', large_final)
pruner.prune_model(70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 69.99%
Sparsity in Conv2d 7: 70.00%
Sparsity in Conv2d 10: 70.00%
Sparsity in Conv2d 13: 70.00%
Sparsity in Conv2d 16: 70.00%
Sparsity in Conv2d 20: 70.00%
Sparsity in Conv2d 23: 70.00%
Sparsity in Conv2d 26: 70.00%
Sparsity in Conv2d 29: 70.00%
Sparsity in Conv2d 32: 70.00%
Sparsity in Conv2d 36: 70.00%
Sparsity in Conv2d 39: 70.00%
Sparsity in Conv2d 42: 70.00%
Sparsity in Conv2d 45: 70.00%
Sparsity in Conv2d 48: 70.00%
Sparsity in Conv2d 52: 70.00%
Sparsity in Conv2d 55: 70.00%
Sparsity in Conv2d 58: 70.00%
Sparsity in Conv2d 61: 70.00%
Sparsity in Conv2d 64: 70.00%
{% endraw %}

You now have a model that is $70\%$ sparse !

Granularity

As we said earlier, the granularity defines the structure of parameter that you will remove.

In the example below, we removed weight from each convolutional filter, meaning that we now have sparse filters, as can be seen in the image below:

{% raw %}
plot_kernels(model.conv1)
{% endraw %}

Another granularity is, for example, removing column vectors from the filters. To do so, just change the granularity parameter accordingly.

{% raw %}
model = resnet18()
pruner = Sparsifier(model, 'column', 'local', large_final)
pruner.prune_layer(model.conv1, 70)
{% endraw %} {% raw %}
plot_kernels(model.conv1)
{% endraw %}

For more information and examples about the pruning granularities, I suggest you to take a look at the corresponding section.

Method

The method defines where to look in the model, i.e. from where do we compare weight. The two basic methods are:

  • local, i.e. we compare weight from each layer individually. This will lead to layers with similar levels of sparsity.
  • global, i.e. we compare weight from the whole model. This will lead to layers with different levels of sparsity
{% raw %}
model = resnet18()
pruner = Sparsifier(model, 'weight', 'local', large_final)
pruner.prune_model(70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 69.99%
Sparsity in Conv2d 7: 70.00%
Sparsity in Conv2d 10: 70.00%
Sparsity in Conv2d 13: 70.00%
Sparsity in Conv2d 16: 70.00%
Sparsity in Conv2d 20: 70.00%
Sparsity in Conv2d 23: 70.00%
Sparsity in Conv2d 26: 70.00%
Sparsity in Conv2d 29: 70.00%
Sparsity in Conv2d 32: 70.00%
Sparsity in Conv2d 36: 70.00%
Sparsity in Conv2d 39: 70.00%
Sparsity in Conv2d 42: 70.00%
Sparsity in Conv2d 45: 70.00%
Sparsity in Conv2d 48: 70.00%
Sparsity in Conv2d 52: 70.00%
Sparsity in Conv2d 55: 70.00%
Sparsity in Conv2d 58: 70.00%
Sparsity in Conv2d 61: 70.00%
Sparsity in Conv2d 64: 70.00%
{% endraw %} {% raw %}
model = resnet18()
pruner = Sparsifier(model, 'weight', 'global', large_final)
pruner.prune_model(70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 66.36%
Sparsity in Conv2d 7: 32.06%
Sparsity in Conv2d 10: 32.03%
Sparsity in Conv2d 13: 32.00%
Sparsity in Conv2d 16: 32.12%
Sparsity in Conv2d 20: 44.17%
Sparsity in Conv2d 23: 44.28%
Sparsity in Conv2d 26: 15.48%
Sparsity in Conv2d 29: 44.34%
Sparsity in Conv2d 32: 44.06%
Sparsity in Conv2d 36: 59.31%
Sparsity in Conv2d 39: 59.21%
Sparsity in Conv2d 42: 21.94%
Sparsity in Conv2d 45: 59.33%
Sparsity in Conv2d 48: 59.27%
Sparsity in Conv2d 52: 75.85%
Sparsity in Conv2d 55: 75.84%
Sparsity in Conv2d 58: 30.34%
Sparsity in Conv2d 61: 75.91%
Sparsity in Conv2d 64: 75.79%
{% endraw %}

Criteria

The criteria defines how we select the parameters to remove. It is usually given by a scoring method. The most common one is the large_final, i.e. select parameters with the highest absolute value as they are supposed to contribute the most to the final results of the model.

{% raw %}
model = resnet18()
pruner = Sparsifier(model, 'filter', 'global', large_final)
pruner.prune_model(70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 98.44%
Sparsity in Conv2d 7: 0.00%
Sparsity in Conv2d 10: 0.00%
Sparsity in Conv2d 13: 0.00%
Sparsity in Conv2d 16: 0.00%
Sparsity in Conv2d 20: 48.44%
Sparsity in Conv2d 23: 42.97%
Sparsity in Conv2d 26: 0.00%
Sparsity in Conv2d 29: 35.94%
Sparsity in Conv2d 32: 47.66%
Sparsity in Conv2d 36: 99.61%
Sparsity in Conv2d 39: 99.61%
Sparsity in Conv2d 42: 0.00%
Sparsity in Conv2d 45: 99.61%
Sparsity in Conv2d 48: 99.61%
Sparsity in Conv2d 52: 99.80%
Sparsity in Conv2d 55: 99.80%
Sparsity in Conv2d 58: 0.00%
Sparsity in Conv2d 61: 99.80%
Sparsity in Conv2d 64: 99.80%
{% endraw %} {% raw %}
model = resnet18()
pruner = Sparsifier(model, 'filter', 'global', small_final)
pruner.prune_model(70)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 98.44%
Sparsity in Conv2d 7: 98.44%
Sparsity in Conv2d 10: 98.44%
Sparsity in Conv2d 13: 98.44%
Sparsity in Conv2d 16: 98.44%
Sparsity in Conv2d 20: 99.22%
Sparsity in Conv2d 23: 99.22%
Sparsity in Conv2d 26: 99.22%
Sparsity in Conv2d 29: 99.22%
Sparsity in Conv2d 32: 99.22%
Sparsity in Conv2d 36: 99.61%
Sparsity in Conv2d 39: 99.61%
Sparsity in Conv2d 42: 99.61%
Sparsity in Conv2d 45: 99.61%
Sparsity in Conv2d 48: 99.61%
Sparsity in Conv2d 52: 34.77%
Sparsity in Conv2d 55: 29.30%
Sparsity in Conv2d 58: 99.80%
Sparsity in Conv2d 61: 26.95%
Sparsity in Conv2d 64: 27.73%
{% endraw %}

For more information and examples about the pruning criteria, I suggest you to take a look at the corresponding section.

Remark

In some case, you may want to impose the remaining amount of parameters to be a multiple of 8, this can be done by passing the round_to parameter.

{% raw %}
model = resnet18()
pruner = Sparsifier(model, 'filter', 'local', large_final)
pruner.prune_model(70, round_to=8)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 62.50%
Sparsity in Conv2d 7: 62.50%
Sparsity in Conv2d 10: 62.50%
Sparsity in Conv2d 13: 62.50%
Sparsity in Conv2d 16: 62.50%
Sparsity in Conv2d 20: 68.75%
Sparsity in Conv2d 23: 68.75%
Sparsity in Conv2d 26: 68.75%
Sparsity in Conv2d 29: 68.75%
Sparsity in Conv2d 32: 68.75%
Sparsity in Conv2d 36: 68.75%
Sparsity in Conv2d 39: 68.75%
Sparsity in Conv2d 42: 68.75%
Sparsity in Conv2d 45: 68.75%
Sparsity in Conv2d 48: 68.75%
Sparsity in Conv2d 52: 68.75%
Sparsity in Conv2d 55: 68.75%
Sparsity in Conv2d 58: 68.75%
Sparsity in Conv2d 61: 68.75%
Sparsity in Conv2d 64: 68.75%
{% endraw %} {% raw %}
model = resnet18()
pruner = Sparsifier(model, 'filter', 'global', large_final)
pruner.prune_model(70, round_to=8)
{% endraw %} {% raw %}
pruner.print_sparsity()
Sparsity in Conv2d 1: 87.50%
Sparsity in Conv2d 7: 0.00%
Sparsity in Conv2d 10: 0.00%
Sparsity in Conv2d 13: 0.00%
Sparsity in Conv2d 16: 0.00%
Sparsity in Conv2d 20: 37.50%
Sparsity in Conv2d 23: 43.75%
Sparsity in Conv2d 26: 0.00%
Sparsity in Conv2d 29: 43.75%
Sparsity in Conv2d 32: 37.50%
Sparsity in Conv2d 36: 96.88%
Sparsity in Conv2d 39: 96.88%
Sparsity in Conv2d 42: 0.00%
Sparsity in Conv2d 45: 96.88%
Sparsity in Conv2d 48: 96.88%
Sparsity in Conv2d 52: 98.44%
Sparsity in Conv2d 55: 98.44%
Sparsity in Conv2d 58: 0.00%
Sparsity in Conv2d 61: 98.44%
Sparsity in Conv2d 64: 98.44%
{% endraw %}

For more information about granularities at which you can operate, please check the related page.