COE 592Lecture 4.1Part 03
Pruning granularity: from irregular to regular
Fine-grained versus coarse-grained pruning on a 2D weight matrix, the four dimensions of a convolution weight tensor, and the five commonly used granularities from fine-grained to channel-level.
- Concepts
- 3
- Slides
- 14-18
- Reading
- 18 min
Why this part matters
Sooner or later your embedded project will hit this moment: a network that is 90% zeros runs no faster on the target MCU or GPU than the dense one did. Nothing is wrong with the pruning. What is wrong is the shape of the holes, and that shape is what this part is about.
Part 02 showed that pruning can remove most of a network's weights without hurting accuracy. The next question on the lecture's outline is in what pattern to remove them. We start with a single 8 x 8 matrix and two ways of cutting it, then open up a convolution weight to see that it has four dimensions and therefore many more ways to be cut, and finish with a ladder of five granularities adapted from Mao et al., who studied four. Exams ask you to order that ladder, to count the weights a given unit removes and to argue the trade between flexibility and acceleration. The same ladder returns in the next part as NVIDIA 2:4 sparsity, channel pruning and AMC, and again in lecture 04-2.
By the end you can
- Define pruning granularity and place fine-grained and coarse-grained pruning at its two ends.
- Explain why coarse-grained choices are a subset of fine-grained ones and what that does to attainable sparsity at equal accuracy.
- Read a convolution weight as [c_o, c_i, k_h, k_w] and count the weights in a filter, an input channel, a kernel and a row.
- Order fine-grained, pattern-based, vector-level, kernel-level and channel-level from irregular to regular and say what hardware each maps onto.
- Compute the sparsity and the surviving shape after removing a given unit.
Take a weight matrix with eight rows and eight columns, 64 weights in all, and decide to remove 24 of them. One way is to cross out rows 3, 4 and 7 completely. Another is to cross out 24 individual entries wherever the magnitude criterion of part 05 says they matter least. Both leave 40 weights and both reach a Sparsity of 24 / 64 = 37.5%. Everything else about them is different.
The unit you delete is the Pruning granularity. Deleting single weights is fine-grained, or unstructured, pruning. Deleting whole rows, columns, kernels or channels is coarse-grained, or structured, pruning. Two consequences pull in opposite directions as the unit grows. The number of masks you may choose from falls, which costs accuracy at a given Pruning ratio. The regularity of what survives rises, which is what lets ordinary hardware run the pruned layer faster.
Counting the choices
The slide calls fine-grained pruning "more flexible" and coarse-grained pruning "a subset of the fine-grained case". Both statements can be made exact by counting masks. A mask is the set of positions you zero, and the pruning problem of part 01 searches over masks for the one with the lowest loss.
Worked example
How many masks remove 24 of 64 weights
Fine-grained
Any 24 of the 64 positions may go, so there are C(64, 24) = 250,649,105,469,666,120 masks, about 2.5 x 10^17.Row-structured
Any 3 of the 8 rows may go, so there are C(8, 3) = 56 masks.Nest one inside the other
Every one of the 56 row masks is also a set of 24 individual positions, so it is one of the 2.5 x 10^17 fine-grained masks. The structured menu sits strictly inside the unstructured menu.Consequence for accuracy
The best row mask can at most tie the best fine-grained mask on the loss. It can never beat it. At a fixed accuracy target, structured pruning therefore reaches equal or lower sparsity, never higher, for the best achievable mask under the same retraining.
Same sparsity, different search spaces
- Weights removed
- 24 of 64 (37.5%)
- Fine-grained masks
- C(64, 24) ≈ 2.5 x 10^17
- Row-structured masks
- C(8, 3) = 56
- Surviving shape, fine-grained
- 8 x 8 with 24 holes
- Surviving shape, row-structured
- 5 x 8 dense
Why irregular means hard to accelerate
Multiply a vector by the 5 x 8 survivor and nothing special happens. It is just a smaller dense matrix, and every BLAS routine, every tensor core and every microcontroller loop handles it at full speed. That is the slide's "easy to accelerate (just a smaller matrix!)". Now multiply by the 8 x 8 matrix with 24 holes. A dense routine still performs all 64 multiplies, 24 of them by zero, and gains nothing. To gain, you must store the 40 surviving values together with their positions, in a compressed sparse format, and let the hardware skip the zeros one by one. Mao et al. draw the same line: fine-grained sparsity scatters the tensor into isolated weights and needs custom accelerators such as EIE or SCNN to exploit it, while filter and channel sparsity is simple to accelerate on general-purpose processors because it "is equivalent to obtaining a smaller dense model" (Mao et al., 2017, section 2).
The gap is measurable. Wen et al. trained AlexNet with structured sparsity and reported average layer-wise speedups of the convolutional layers of 5.1x on CPU and 3.1x on GPU with off-the-shelf libraries, against 3.0x and 0.9x for non-structured l1 sparsity (Wen et al., 2016, Table 4). The fine-grained model had more zeros. The structured model ran faster.
What the accuracy price looks like
Because coarse units search a smaller space, they lose a little accuracy at the same density (the fraction of weights kept). Mao et al. measured this on ImageNet with the same pipeline at every granularity. The differences are small between fine and vector grains and grow as the grain reaches whole kernels; pruning entire filters "loses nearly 1% validation accuracy at the very first pruning stage" on AlexNet (Mao et al., 2017).
| Network | Density kept | Fine-grained | Vector-level | Kernel-level |
|---|---|---|---|---|
| AlexNet | 24.8% | 80.41% | 79.94% | 79.20% |
| VGG-16 | 23.5% | 90.56% | 90.48% | 89.70% |
| ResNet-50 | 40.0% | 92.34% | 92.26% | 92.07% |
| Granularity | Density kept | Storage relative to dense |
|---|---|---|
| Fine-grained | 22.1% | 33.0% |
| Vector-level | 29.9% | 34.5% |
| Kernel-level | 37.8% | 39.7% |
Recall
Rows 3, 4 and 7 of an 8 x 8 weight matrix are pruned. What is the sparsity, and what shape survives?
Recall
Why does the slide call coarse-grained choices a subset of the fine-grained case, and what does that imply for compression?
Quick check
Why is fine-grained pruning hard to accelerate on an ordinary GPU?
Quick check
Which statement about coarse-grained pruning matches slide 15?
A linear layer's weight is a matrix, so its only structured units are rows and columns. A convolution weight is richer. Slide 17 draws one: three rows of kernels stacked vertically, two columns of kernels side by side, and each kernel a 3 x 3 grid. Count the cells and you get 3 x 2 x 3 x 3 = 54 weights.
Those four factors are the four dimensions of a convolution weight. The tensor has shape [c_o, c_i, k_h, k_w]: c_o output channels, also called filters; c_i input channels; and a kernel of height k_h and width k_w. Goodfellow, Bengio and Courville write the same object as a 4-D kernel tensor K whose element K[i, j, k, l] is the connection strength between output channel i and input channel j at a row offset of k and a column offset of l (Goodfellow et al., 2016, section 9.5). PyTorch stores Conv2d.weight with exactly the slide's ordering, (out_channels, in_channels / groups, kernel_size[0], kernel_size[1]) (PyTorch documentation).
Slicing the tensor into units
The reason the lecture stops to name the dimensions is that each way of slicing the tensor is a candidate pruning unit. Mao et al. name the slices with array notation, and reading them that way makes the counts automatic. Fix the output channel and you have a filter, a 3-D slab. Fix the output and input channel and you have one kernel, a 2-D grid. Fix a row inside that kernel and you have a 1-D vector. Fix everything and you have a single scalar weight (Mao et al., 2017, section 3.2).
| Unit | Slice | Weights | Share of 54 |
|---|---|---|---|
| Single weight | W[o, i, r, c] | 1 | 1.9% |
| Row inside a kernel | W[o, i, r, :] | k_w = 3 | 5.6% |
| Kernel | W[o, i, :, :] | k_h x k_w = 9 | 16.7% |
| Filter (output channel) | W[o, :, :, :] | c_i x k_h x k_w = 18 | 33.3% |
| Input channel | W[:, i, :, :] | c_o x k_h x k_w = 27 | 50.0% |
Two formulas do all the work. A filter holds c_i · k_h · k_w weights, because it has one kernel per input channel. An input channel holds c_o · k_h · k_w weights, because every filter has a kernel that reads it. In the slide these are 18 and 27, and it is easy to mix them up, so picture the grid: a filter is a row of kernels, an input channel is a column of kernels.
The same counts at a realistic scale
Slide 17 is a toy so the picture fits. A VGG-style layer with 64 input and 64 output channels and 3 x 3 kernels has 64 x 64 x 9 = 36,864 weights, and the same slicing rules apply.
| Unit removed | Weights removed | Share | Surviving shape |
|---|---|---|---|
| One kernel | 9 | 0.024% | Still [64, 64, 3, 3] with holes |
| One filter | 64 x 9 = 576 | 1.56% | [63, 64, 3, 3] |
| One input channel | 64 x 9 = 576 | 1.56% | [64, 63, 3, 3] |
| Sixteen input channels | 16 x 576 = 9,216 | 25% | [64, 48, 3, 3] |
Removing a whole filter has a second effect that Li et al. spell out. The filter's output feature map disappears, so the next layer no longer receives that channel, and the corresponding kernels in the next layer's weight tensor disappear too. Pruning m of the n filters of a layer removes m / n of the compute in that layer and in the one after it (Li et al., 2017).
Recall
Name the four dimensions of a convolution weight in the slide's order, and give the weight count for c_o = 3, c_i = 2 and 3 x 3 kernels.
Recall
How many weights go when you remove one kernel, one input channel and one filter from the 54-weight tensor?
Quick check
A convolution layer has c_o = 3, c_i = 2 and 3 x 3 kernels. Channel-level pruning removes one input channel. How many weights are removed?
Put the two previous ideas together and the lecture's central figure appears. Slide 18 takes the same 54-weight tensor and prunes it five times, once per unit, arranging the results on an axis from irregular to regular. Read from left to right, the holes grow from single cells, to fixed shapes inside kernels, to whole rows, to whole kernels, to an entire column of kernels.
Every panel can be counted, and counting them shows something the picture hides: every panel removes between a third and two thirds of the weights, so the real difference between them is not how much Sparsity they reach but how the zeros are arranged. The fine-grained panel keeps 23 scattered weights. The pattern-based panel keeps exactly four cells in every kernel, and each kept shape is a Tetris piece (a T or an L of four cells). The vector-level panel keeps seven of the eighteen kernel rows. The kernel-level panel drops the first filter's right kernel and the second filter's left kernel. The channel-level panel drops the whole second input channel across all three filters, and what survives is a dense [3, 1, 3, 3] tensor.
| Panel | What was removed | Weights removed | Sparsity | Survivor |
|---|---|---|---|---|
| Fine-grained | 31 single weights | 31 | 57.4% | 54 slots with holes |
| Pattern-based | 5 of 9 cells in each of 6 kernels | 6 x 5 = 30 | 55.6% | 54 slots, one shape per kernel |
| Vector-level | 11 of 18 kernel rows | 11 x 3 = 33 | 61.1% | 54 slots with empty rows |
| Kernel-level | 2 of 6 kernels | 2 x 9 = 18 | 33.3% | 54 slots with empty kernels |
| Channel-level | 1 of 2 input channels | 3 x 9 = 27 | 50.0% | dense [3, 1, 3, 3] |
The ladder, rung by rung
Mao et al. organise four grains (fine, vector, kernel, filter) by how many dimensions each unit spans. The slide keeps their order, inserts pattern-based pruning between fine-grained and vector-level, and shows the 3-D rung as an input channel instead of Mao's filter (Mao et al., 2017, section 3.2). At the bottom, fine-grained pruning removes 0-D scalars anywhere. Pattern-based pruning keeps a small catalogue of fixed shapes inside each kernel, which the slide calls "like Tetris": the hardware knows the finite set of masks in advance instead of facing arbitrary holes. Niu et al. describe it as "fine-grained pruning patterns inside the coarse-grained structures" and, by compiling specialised code per pattern, ran networks on mobile CPUs and GPUs up to 44.5x faster than TensorFlow Lite and up to 11.4x faster than TVM with no accuracy loss (Niu et al., 2020). Vector-level pruning removes 1-D rows, W[o, i, r, :], of a kernel. Kernel-level pruning removes 2-D kernels, W[o, i, :, :]. At the top, channel pruning removes a 3-D slab: on the slide, an input channel W[:, i, :, :] across every filter.
| Granularity | Unit | Weights per unit | Index cost | Runs efficiently on |
|---|---|---|---|---|
| Fine-grained | Any single weight (0-D) | 1 | One index per surviving weight | Custom sparse engines such as EIE or SCNN |
| Pattern-based | A fixed mask inside each kernel | 5 per kernel | One pattern id per kernel | Compilers that specialize code per pattern (PatDNN) |
| Vector-level | A row of a kernel (1-D) | k_w = 3 | One index per surviving row | Custom 1-D convolution hardware such as Eyeriss |
| Kernel-level | A whole k_h x k_w kernel (2-D) | 9 | One index per surviving kernel | Custom 2-D convolution hardware (not stock dense libraries) |
| Channel-level | An input channel across all filters (3-D) | 27 | None: the tensor shrinks | Any dense CPU or GPU library |
The right-hand column is the practical half of the story. Only the channel and filter rungs run faster on unmodified dense libraries, because the tensor simply shrinks. Kernel and vector sparsity line up with 2-D and 1-D convolution primitives (Winograd, Eyeriss-style 1-D units), which makes custom hardware simpler, but Mao et al. note they are still hard to accelerate on general-purpose processors. Pattern-based pruning needs compiler support (PatDNN) or dedicated support such as NVIDIA's 2:4 sparse tensor cores, and fine-grained sparsity needs accelerators such as EIE (Mao et al., 2017, section 6).
Try every rung on one tensor
The simulator below holds a random [3, 2, 3, 3] tensor and lets you prune it with any of the six units, including the filter unit that Mao et al. list alongside channels. It groups the weights by the chosen unit, scores each group with the L1 or L2 magnitude that part 05 develops, zeros the weakest groups until the target is met, and reports what survives. Watch three things as you switch units: the achieved sparsity snaps to coarser steps, the index count collapses as units grow, and only the channel and filter units ever produce a smaller dense tensor.
- Unit
- Individual weight
- Weights per unit
- 1
- Slice
- W[o, i, r, c]
- Units removed
- 19 of 54
Units are ranked by the L1 norm of the weights they would remove, lowest first, and pruned until at least 19 of 54 weights are zero. Achieved sparsity snaps to whole units.
The energy argument from part 01 also runs along this axis. Mao et al. simulated the SCNN sparse accelerator on VGG-16 and found that, at the same density, vector-level sparsity needs 30 to 35% fewer output memory references than fine-grained sparsity. Neighbouring surviving weights in a row write to the same output address, so SCNN can skip the repeated read and write of that address (Mao et al., 2017, section 6 and Table 4). Since a DRAM access costs about two hundred times a multiply, fewer memory references is where the energy goes, not fewer multiplies.
Recall
Order fine-grained, kernel-level, pattern-based, channel-level and vector-level pruning from irregular to regular.
Quick check
Which granularity turns the weight tensor into a smaller dense tensor that needs no index storage at all?
Recap
If you remember nothing else
- Granularity is the unit you delete. Smaller units give more index choices, larger units give regular structure.
- Coarse-grained masks are a subset of fine-grained masks, so at equal accuracy structured pruning reaches equal or lower sparsity, never higher, for the best achievable mask under the same retraining.
- Fine-grained pruning is hard to accelerate because zeros sit anywhere. Channel or filter pruning yields a smaller dense tensor that any library runs.
- Convolution weights are [c_o, c_i, k_h, k_w]. Slide 17's example holds 3 x 2 x 3 x 3 = 54 weights.
- From irregular to regular: fine-grained, pattern-based, vector-level, kernel-level, channel-level.
- Removing one kernel drops k_h x k_w weights, one input channel drops c_o x k_h x k_w, one filter drops c_i x k_h x k_w.
- Coarser grains share indices, so their storage penalty is smaller than their sparsity penalty (Mao et al.).
Sources
- Exploring the Granularity of Sparsity in Convolutional Neural NetworksPaperCVPR Workshops 2017, Mao, Han, Pool, Li, Liu, Wang and DallySource of the slide's figure, which adds a pattern-based rung and uses channel-level where the paper uses filter-level. Grain definitions in 3.2, accuracy at equal density in Table 1, storage with 4-bit indices in Table 2, hardware mapping and SCNN memory reference savings in section 6.(opens in a new tab)
- MIT 6.5940 EfficientML.ai, Lecture 3: Pruning and Sparsity (Part I)DocsMIT HAN LabThe course from which this lecture's slides are adapted.(opens in a new tab)
- Learning Structured Sparsity in Deep Neural NetworksPaperNIPS 2016, Wen, Wu, Wang, Chen and LiStructured sparsity gives average layer-wise convolution speedups of 5.1x on CPU and 3.1x on GPU with standard libraries, against 3.0x and 0.9x for non-structured l1 sparsity (Table 4).(opens in a new tab)
- Pruning Filters for Efficient ConvNetsPaperICLR 2017, Li, Kadav, Durdanovic, Samet and GrafFilter pruning removes the feature map and the next layer's kernels; pruning m of n filters cuts m/n of the compute in both layers.(opens in a new tab)
- PatDNN: Achieving Real-Time DNN Execution on Mobile Devices with Pattern-based Weight PruningPaperASPLOS 2020, Niu et al.Pattern-based pruning as fine-grained patterns inside coarse-grained structures, up to 44.5x over TensorFlow Lite and up to 11.4x over TVM.(opens in a new tab)
- Structured Pruning of Deep Convolutional Neural NetworksPaperAnwar, Hwang and Sung, 2015Channel-wise, kernel-wise and intra-kernel strided sparsity, an early version of the ladder.(opens in a new tab)
- Deep Learning, chapter 9: Convolutional NetworksBookGoodfellow, Bengio and Courville, MIT Press 2016Section 9.5 defines the 4-D kernel tensor K with output channel, input channel, row offset and column offset.(opens in a new tab)
- torch.nn.Conv2dDocsPyTorch documentationWeight shape (out_channels, in_channels / groups, kernel_size[0], kernel_size[1]).(opens in a new tab)
- Learning both Weights and Connections for Efficient Neural NetworksPaperNIPS 2015, Han, Pool, Tran and DallyFine-grained magnitude pruning, the left end of the ladder.(opens in a new tab)