Pruner.md 22.1 KB
Newer Older
Chi Song's avatar
Chi Song committed
1
# NNI 支持的剪枝算法
Chi Song's avatar
Chi Song committed
2

Chi Song's avatar
Chi Song committed
3
4
5
6
NNI 提供了一些支持细粒度权重剪枝和结构化的滤波器剪枝算法。 **细粒度的剪枝**通常会导致非结构化的模型,这需要特定的硬件或软件来加速这样的稀疏网络。 **滤波器剪枝**通过删除整个滤波器来实现加速。  NNI 还提供了算法来进行**剪枝规划**


**细粒度剪枝**
Chi Song's avatar
Chi Song committed
7
* [Level Pruner](#level-pruner)
Chi Song's avatar
Chi Song committed
8
9
10
11
12
13

**滤波器剪枝**
* [Slim Pruner](#slim-pruner)
* [FPGM Pruner](#fpgm-pruner)
* [L1Filter Pruner](#l1filter-pruner)
* [L2Filter Pruner](#l2filter-pruner)
14
15
16
* [Activation APoZ Rank Filter Pruner](#activationAPoZRankFilter-pruner)
* [Activation Mean Rank Filter Pruner](#activationmeanrankfilter-pruner)
* [Taylor FO On Weight Pruner](#taylorfoweightfilter-pruner)
Chi Song's avatar
Chi Song committed
17
18

**剪枝计划**
Chi Song's avatar
Chi Song committed
19
* [AGP Pruner](#agp-pruner)
Chi Song's avatar
Chi Song committed
20
21
22
* [NetAdapt Pruner](#netadapt-pruner)
* [SimulatedAnnealing Pruner](#simulatedannealing-pruner)
* [AutoCompress Pruner](#autocompress-pruner)
23
24
* [AutoML for Model Compression Pruner](#automl-for-model-compression-pruner)
* [Sensitivity Pruner](#sensitivity-pruner)
Chi Song's avatar
Chi Song committed
25
26
27

**其它**
* [ADMM Pruner](#admm-pruner)
28
* [Lottery Ticket 假设](#Lottery-Ticket-假设)
Chi Song's avatar
Chi Song committed
29

Chi Song's avatar
Chi Song committed
30
31
## Level Pruner

Chi Song's avatar
Chi Song committed
32
这是个基本的一次性 Pruner:可设置目标稀疏度(以分数表示,0.6 表示会剪除 60%)。
Chi Song's avatar
Chi Song committed
33
34
35
36
37
38

首先按照绝对值对指定层的权重排序。 然后按照所需的稀疏度,将值最小的权重屏蔽为 0。

### 用法

TensorFlow 代码
Chi Song's avatar
Chi Song committed
39
```python
Chi Song's avatar
Chi Song committed
40
41
from nni.compression.tensorflow import LevelPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['default'] }]
liuzhe-lz's avatar
liuzhe-lz committed
42
pruner = LevelPruner(model, config_list)
Chi Song's avatar
Chi Song committed
43
pruner.compress()
Chi Song's avatar
Chi Song committed
44
45
46
```

PyTorch 代码
Chi Song's avatar
Chi Song committed
47
```python
Chi Song's avatar
Chi Song committed
48
49
from nni.compression.torch import LevelPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['default'] }]
Chi Song's avatar
Chi Song committed
50
51
pruner = LevelPruner(model, config_list)
pruner.compress()
Chi Song's avatar
Chi Song committed
52
53
54
55
```

#### Level Pruner 的用户配置

56
57
58
59
60
61
62
63
64
65
66
67
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.LevelPruner
```

##### Tensorflow

```eval_rst
..  autoclass:: nni.compression.tensorflow.LevelPruner
```

Chi Song's avatar
Chi Song committed
68

Chi Song's avatar
Chi Song committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
## Slim Pruner

这是一次性的 Pruner,在 ['Learning Efficient Convolutional Networks through Network Slimming'](https://arxiv.org/pdf/1708.06519.pdf) 中提出,作者 Zhuang Liu, Jianguo Li, Zhiqiang Shen, Gao Huang, Shoumeng Yan 以及 Changshui Zhang。

![](../../img/slim_pruner.png)

> Slim Pruner **会遮盖卷据层通道之后 BN 层对应的缩放因子**,训练时在缩放因子上的 L1 正规化应在批量正规化 (BN) 层之后来做。BN 层的缩放因子在修剪时,是**全局排序的**,因此稀疏模型能自动找到给定的稀疏度。

### 用法

PyTorch 代码

```python
from nni.compression.torch import SlimPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['BatchNorm2d'] }]
pruner = SlimPruner(model, config_list)
pruner.compress()
```

#### Slim Pruner 的用户配置

90
91
92
93
94
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.SlimPruner
```
Chi Song's avatar
Chi Song committed
95

Chi Song's avatar
Chi Song committed
96
97
98
99
100
101
102
103
104
105
### 重现实验

我们实现了 ['Learning Efficient Convolutional Networks through Network Slimming'](https://arxiv.org/pdf/1708.06519.pdf) 中的一项实验。根据论文,对 CIFAR-10 上的 **VGGNet** 剪除了 $70\%$ 的通道,即约 $88.5\%$ 的参数。 实验结果如下:

| 模型            | 错误率(论文/我们的) | 参数量    | 剪除率   |
| ------------- | ----------- | ------ | ----- |
| VGGNet        | 6.34/6.40   | 20.04M |       |
| Pruned-VGGNet | 6.20/6.26   | 2.03M  | 88.5% |

实验代码在 [examples/model_compress](https://github.com/microsoft/nni/tree/master/examples/model_compress/)
Chi Song's avatar
Chi Song committed
106

Chi Song's avatar
Chi Song committed
107
***
Chi Song's avatar
Chi Song committed
108

Chi Song's avatar
Chi Song committed
109
## FPGM Pruner
Chi Song's avatar
Chi Song committed
110

Chi Song's avatar
Chi Song committed
111
这是一种一次性的 Pruner,FPGM Pruner 是论文 [Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration](https://arxiv.org/pdf/1811.00250.pdf) 的实现
Chi Song's avatar
Chi Song committed
112

113
具有最小几何中位数的 FPGMPruner 修剪过滤器。
Chi Song's avatar
Chi Song committed
114
115

 ![](../../img/fpgm_fig1.png)
Chi Song's avatar
Chi Song committed
116
> 以前的方法使用 “smaller-norm-less-important” 准则来修剪卷积神经网络中规范值较小的。 本文中,分析了基于规范的准则,并指出其所依赖的两个条件不能总是满足:(1) 滤波器的规范偏差应该较大;(2) 滤波器的最小规范化值应该很小。 为了解决此问题,提出了新的滤波器修剪方法,即 Filter Pruning via Geometric Median (FPGM),可不考虑这两个要求来压缩模型。 与以前的方法不同,FPGM 通过修剪冗余的,而不是相关性更小的部分来压缩 CNN 模型。
Chi Song's avatar
Chi Song committed
117

Chi Song's avatar
Chi Song committed
118
### 用法
Chi Song's avatar
Chi Song committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132

PyTorch 代码
```python
from nni.compression.torch import FPGMPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
pruner = FPGMPruner(model, config_list)
pruner.compress()
```

#### FPGM Pruner 的用户配置

133
134
135
136
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.FPGMPruner
```
Chi Song's avatar
Chi Song committed
137

Chi Song's avatar
Chi Song committed
138
## L1Filter Pruner
Chi Song's avatar
Chi Song committed
139

Chi Song's avatar
Chi Song committed
140
这是一种一次性的 Pruner,由 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 提出,作者 Hao Li, Asim Kadav, Igor Durdanovic, Hanan Samet 和 Hans Peter Graf。
Chi Song's avatar
Chi Song committed
141
142
143

![](../../img/l1filter_pruner.png)

Chi Song's avatar
Chi Song committed
144
> L1Filter Pruner 修剪**卷积层**中的滤波器
Chi Song's avatar
Chi Song committed
145
> 
Chi Song's avatar
Chi Song committed
146
> 从第 i 个卷积层修剪 m 个滤波器的过程如下:
Chi Song's avatar
Chi Song committed
147
> 
Chi Song's avatar
Chi Song committed
148
149
150
> 1. 对于每个滤波器 ![](http://latex.codecogs.com/gif.latex?F_{i,j}),计算其绝对内核权重之和![](http://latex.codecogs.com/gif.latex?s_j=\sum_{l=1}^{n_i}\sum|K_l|)
> 2. 将滤波器按 ![](http://latex.codecogs.com/gif.latex?s_j) 排序。
> 3. 修剪 ![](http://latex.codecogs.com/gif.latex?m) 具有最小求和值及其相应特征图的滤波器。 在 下一个卷积层中,被剪除的特征图所对应的内核也被移除。
Chi Song's avatar
Chi Song committed
151
152
> 4. 为第 ![](http://latex.codecogs.com/gif.latex?i) 和 ![](http://latex.codecogs.com/gif.latex?i+1) 层创建新的内核举证,并保留剩余的内核 权重,并复制到新模型中。

Chi Song's avatar
Chi Song committed
153
### 用法
Chi Song's avatar
Chi Song committed
154
155
156
157

PyTorch 代码

```python
Chi Song's avatar
Chi Song committed
158
159
160
161
162
163
164
165
from nni.compression.torch import L1FilterPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['Conv2d'] }]
pruner = L1FilterPruner(model, config_list)
pruner.compress()
```

#### L1Filter Pruner 的用户配置

166
167
168
169
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.L1FilterPruner
```
Chi Song's avatar
Chi Song committed
170
171
172
173
174
175
176
177
178
179
180

### 重现实验

我们通过 **L1FilterPruner** 实现了 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 中的一项实验, 即论文中,在 CIFAR-10 数据集上修剪 **VGG-16****VGG-16-pruned-A**,其中大约剪除了 $64\%$ 的参数。 实验结果如下:

| 模型              | 错误率(论文/我们的) | 参数量      | 剪除率   |
| --------------- | ----------- | -------- | ----- |
| VGG-16          | 6.75/6.49   | 1.5x10^7 |       |
| VGG-16-pruned-A | 6.60/6.47   | 5.4x10^6 | 64.0% |

实验代码在 [examples/model_compress](https://github.com/microsoft/nni/tree/master/examples/model_compress/)
Chi Song's avatar
Chi Song committed
181

Chi Song's avatar
Chi Song committed
182
***
Chi Song's avatar
Chi Song committed
183

Chi Song's avatar
Chi Song committed
184
## L2Filter Pruner
Chi Song's avatar
Chi Song committed
185

Chi Song's avatar
Chi Song committed
186
这是一种结构化剪枝算法,用于修剪权重的最小 L2 规范滤波器。 它被实现为一次性修剪器。
Chi Song's avatar
Chi Song committed
187

Chi Song's avatar
Chi Song committed
188
### 用法
Chi Song's avatar
Chi Song committed
189

Chi Song's avatar
Chi Song committed
190
191
192
193
194
195
196
197
198
PyTorch 代码

```python
from nni.compression.torch import L2FilterPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['Conv2d'] }]
pruner = L2FilterPruner(model, config_list)
pruner.compress()
```

Chi Song's avatar
Chi Song committed
199
### L2Filter Pruner 的用户配置
Chi Song's avatar
Chi Song committed
200

201
202
203
204
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.L2FilterPruner
```
Chi Song's avatar
Chi Song committed
205
***
Chi Song's avatar
Chi Song committed
206

207
## ActivationAPoZRankFilter Pruner
Chi Song's avatar
Chi Song committed
208

209
ActivationAPoZRankFilter Pruner 是从卷积层激活的输出,用最小的重要性标准 `APoZ` 修剪滤波器,来达到预设的网络稀疏度。 剪枝标准 `APoZ` 的解释在论文 [Network Trimming: A Data-Driven Neuron Pruning Approach towards Efficient Deep Architectures](https://arxiv.org/abs/1607.03250) 中。
Chi Song's avatar
Chi Song committed
210
211
212
213
214

APoZ 定义为:

![](../../img/apoz.png)

Chi Song's avatar
Chi Song committed
215
### 用法
Chi Song's avatar
Chi Song committed
216
217
218

PyTorch 代码

Chi Song's avatar
Chi Song committed
219
220
221
222
223
224
225
226
```python
from nni.compression.torch import ActivationAPoZRankFilterPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
pruner = ActivationAPoZRankFilterPruner(model, config_list, statistics_batch_num=1)
pruner.compress()
Chi Song's avatar
Chi Song committed
227
```
Chi Song's avatar
Chi Song committed
228
229
230

注意:ActivationAPoZRankFilterPruner 用于修剪深度神经网络中的卷积层,因此 `op_types` 字段仅支持卷积层。

231
参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/model_prune_torch.py)了解更多信息。
Chi Song's avatar
Chi Song committed
232

Chi Song's avatar
Chi Song committed
233
### ActivationAPoZRankFilterPruner 的用户配置
Chi Song's avatar
Chi Song committed
234

235
236
237
238
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.ActivationAPoZRankFilterPruner
```
Chi Song's avatar
Chi Song committed
239
240
241
***


242
243
244
## ActivationMeanRankFilter Pruner

ActivationMeanRankFilter Pruner 是从卷积层激活的输出,用最小的重要性标准`平均激活`来修剪滤波器,来达到预设的网络稀疏度。 剪枝标准`平均激活`,在论文 [Pruning Convolutional Neural Networks for Resource Efficient Inference](https://arxiv.org/abs/1611.06440) 的 2.2 节中进行了介绍。 本文中提到的其他修剪标准将在以后的版本中支持。
Chi Song's avatar
Chi Song committed
245

Chi Song's avatar
Chi Song committed
246
### 用法
Chi Song's avatar
Chi Song committed
247
248
249
250
251
252
253
254
255

PyTorch 代码

```python
from nni.compression.torch import ActivationMeanRankFilterPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
Chi Song's avatar
Chi Song committed
256
pruner = ActivationMeanRankFilterPruner(model, config_list, statistics_batch_num=1)
Chi Song's avatar
Chi Song committed
257
258
259
pruner.compress()
```

Chi Song's avatar
Chi Song committed
260
注意:ActivationMeanRankFilterPruner 用于修剪深度神经网络中的卷积层,因此 `op_types` 字段仅支持卷积层。
Chi Song's avatar
Chi Song committed
261

262
参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/model_prune_torch.py)了解更多信息。
Chi Song's avatar
Chi Song committed
263

Chi Song's avatar
Chi Song committed
264
### ActivationMeanRankFilterPruner 的用户配置
Chi Song's avatar
Chi Song committed
265

266
267
268
269
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.ActivationMeanRankFilterPruner
```
Chi Song's avatar
Chi Song committed
270
***
Chi Song's avatar
Chi Song committed
271

272
## TaylorFOWeightFilter Pruner
Chi Song's avatar
Chi Song committed
273

274
TaylorFOWeightFilter Pruner 根据权重上的一阶泰勒展开式,来估计重要性并进行剪枝,从而达到预设的网络稀疏度。 过滤器的估计重要性在论文 [Importance Estimation for Neural Network Pruning](http://jankautz.com/publications/Importance4NNPruning_CVPR19.pdf) 中有定义。 本文中提到的其他修剪标准将在以后的版本中支持。
Chi Song's avatar
Chi Song committed
275
276
277
278
>

![](../../img/importance_estimation_sum.png)

Chi Song's avatar
Chi Song committed
279
### 用法
Chi Song's avatar
Chi Song committed
280
281
282
283
284
285
286
287
288

PyTorch 代码

```python
from nni.compression.torch import TaylorFOWeightFilterPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
Chi Song's avatar
Chi Song committed
289
pruner = TaylorFOWeightFilterPruner(model, config_list, statistics_batch_num=1)
Chi Song's avatar
Chi Song committed
290
291
292
293
pruner.compress()
```


294
#### TaylorFOWeightFilter Pruner 的用户配置
Chi Song's avatar
Chi Song committed
295

296
297
298
299
##### PyTorch
```eval_rst
..  autoclass:: nni.compression.torch.TaylorFOWeightFilterPruner
```
Chi Song's avatar
Chi Song committed
300
301
***

302

Chi Song's avatar
Chi Song committed
303
## AGP Pruner
304

Chi Song's avatar
Chi Song committed
305
这是一种迭代的 Pruner,在 [To prune, or not to prune: exploring the efficacy of pruning for model compression](https://arxiv.org/abs/1710.01878)中,作者 Michael Zhu 和 Suyog Gupta 提出了一种逐渐修建权重的算法。
306
307
> 引入了一种新的自动逐步剪枝算法,在 n 个剪枝步骤中,稀疏度从初始的稀疏度值 si(通常为 0)增加到最终的稀疏度值 sf,从训练步骤 t0 开始,剪枝频率 ∆t:  ![](../../img/agp_pruner.png)
> 在训练网络时,每隔 ∆t 步更新二值权重掩码,以逐渐增加网络的稀疏性,同时允许网络训练步骤从任何剪枝导致的精度损失中恢复。 根据我们的经验,∆t 设为 100 到 1000 个训练步骤之间时,对于模型最终精度的影响可忽略不计。 一旦模型达到了稀疏度目标 sf,权重掩码将不再更新。 背后的稀疏函数直觉在公式(1)。
Chi Song's avatar
Chi Song committed
308
309

### 用法
310

Chi Song's avatar
Chi Song committed
311
312
313
314
通过下列代码,可以在 10 个 Epoch 中将权重稀疏度从 0% 剪枝到 80%。

PyTorch 代码
```python
315
from nni.compression.torch import AGPPruner
Chi Song's avatar
Chi Song committed
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
config_list = [{
    'initial_sparsity': 0,
    'final_sparsity': 0.8,
    'start_epoch': 0,
    'end_epoch': 10,
    'frequency': 1,
    'op_types': ['default']
}]

# 使用 Pruner 前,加载预训练模型、或训练模型。
# model = MyModel()
# model.load_state_dict(torch.load('mycheckpoint.pth'))

# AGP Pruner 会在 optimizer.step() 上回调,在微调模型时剪枝,
# 因此,必须要有 optimizer 才能完成模型剪枝。
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)

333
pruner = AGPPruner(model, config_list, optimizer, pruning_algorithm='level')
Chi Song's avatar
Chi Song committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
pruner.compress()
```

AGP Pruner 默认使用 `LevelPruner` 算法来修建权重,还可以设置 `pruning_algorithm` 参数来使用其它剪枝算法:
* `level`: LevelPruner
* `slim`: SlimPruner
* `l1`: L1FilterPruner
* `l2`: L2FilterPruner
* `fpgm`: FPGMPruner
* `taylorfo`: TaylorFOWeightFilterPruner
* `apoz`: ActivationAPoZRankFilterPruner
* `mean_activation`: ActivationMeanRankFilterPruner

在训练代码中每完成一个 Epoch,需要更新一下 Epoch 的值。

PyTorch 代码
```python
pruner.update_epoch(epoch)
```
353
参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/model_prune_torch.py)了解更多信息。
Chi Song's avatar
Chi Song committed
354
355

#### AGP Pruner 的用户配置
356
357
358
359
360
361

##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.AGPPruner
```
Chi Song's avatar
Chi Song committed
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

***

## NetAdapt Pruner
NetAdapt 在满足资源预算的情况下,自动简化预训练的网络。 给定整体稀疏度,NetAdapt 可通过迭代剪枝自动为不同层生成不同的稀疏分布。

参考 [NetAdapt: Platform-Aware Neural Network Adaptation for Mobile Applications](https://arxiv.org/abs/1804.03230) 了解详细信息。

![](../../img/algo_NetAdapt.png)

#### 用法

PyTorch 代码

```python
from nni.compression.torch import NetAdaptPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
pruner = NetAdaptPruner(model, config_list, short_term_fine_tuner=short_term_fine_tuner, evaluator=evaluator,base_algo='l1', experiment_data_dir='./')
pruner.compress()
```

参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/auto_pruners_torch.py)了解更多信息。

#### NetAdapt Pruner 的用户配置

390
391
392
393
394
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.NetAdaptPruner
```
Chi Song's avatar
Chi Song committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427


## SimulatedAnnealing Pruner

此 Pruner 基于先验经验,实现了引导式的启发搜索方法,模拟退火(SA)算法。 增强的模拟退火算法基于以下发现:具有更多权重的深度神经网络层通常具有较高的可压缩度,对整体精度的影响更小。

- 随机初始化剪枝率的分布(稀疏度)。
- 当 current_temperature < stop_temperature 时:
    1. 对当前分布生成扰动
    2. 对扰动的分布进行快速评估
    3. 根据性能和概率来决定是否接受扰动,如果不接受,返回步骤 1
    4. 冷却,current_temperature = current_temperature * cool_down_rate

更多详细信息,参考 [AutoCompress: An Automatic DNN Structured Pruning Framework for Ultra-High Compression Rates](https://arxiv.org/abs/1907.03141)

#### 用法

PyTorch 代码

```python
from nni.compression.torch import SimulatedAnnealingPruner
config_list = [{
    'sparsity': 0.5,
    'op_types': ['Conv2d']
}]
pruner = SimulatedAnnealingPruner(model, config_list, evaluator=evaluator, base_algo='l1', cool_down_rate=0.9, experiment_data_dir='./')
pruner.compress()
```

参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/auto_pruners_torch.py)了解更多信息。

#### SimulatedAnnealing Pruner 的用户配置

428
429
430
431
432
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.SimulatedAnnealingPruner
```
Chi Song's avatar
Chi Song committed
433
434
435
436
437


## AutoCompress Pruner
每一轮中,AutoCompressPruner 会用相同的稀疏度对模型进行剪枝,从而达到总体的稀疏度:

438
        1. 使用 SimulatedAnnealingPruner 生成稀疏度分布
Chi Song's avatar
Chi Song committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
        2. 执行基于 ADMM 的结构化剪枝,为下一轮生成剪枝结果。
           这里会使用 `speedup` 来执行真正的剪枝。

更多详细信息,参考 [AutoCompress: An Automatic DNN Structured Pruning Framework for Ultra-High Compression Rates](https://arxiv.org/abs/1907.03141)

#### 用法

PyTorch 代码

```python
from nni.compression.torch import ADMMPruner
config_list = [{
        'sparsity': 0.5,
        'op_types': ['Conv2d']
    }]
pruner = AutoCompressPruner(
            model, config_list, trainer=trainer, evaluator=evaluator,
            dummy_input=dummy_input, num_iterations=3, optimize_mode='maximize', base_algo='l1',
            cool_down_rate=0.9, admm_num_iterations=30, admm_training_epochs=5, experiment_data_dir='./')
pruner.compress()
```

参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/auto_pruners_torch.py)了解更多信息。

#### AutoCompress Pruner 的用户配置

465
466
467
468
469
470
471
472
473
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.AutoCompressPruner
```

## AutoML for Model Compression Pruner

自动机器学习用于模型压缩剪枝器(AMCPruner)借助强化学习来提供模型压缩策略。 这种基于学习的压缩策略比传统的基于规则的压缩策略有更高的压缩比,更好地保存了精度,节省了人力。
Chi Song's avatar
Chi Song committed
474

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
![](../../img/amc_pruner.jpg)

更多详细信息,参考 [AMC: AutoML for Model Compression and Acceleration on Mobile Devices](https://arxiv.org/pdf/1802.03494.pdf)


#### 用法

PyTorch 代码

```python
from nni.compression.torch import AMCPruner
config_list = [{
        'op_types': ['Conv2d', 'Linear']
    }]
pruner = AMCPruner(model, config_list, evaluator, val_loader, flops_ratio=0.5)
pruner.compress()
```

参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/amc/)了解更多信息。

#### AutoCompress Pruner 的用户配置

##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.AMCPruner
```
Chi Song's avatar
Chi Song committed
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534

## ADMM Pruner
Alternating Direction Method of Multipliers (ADMM) 是一种数学优化技术,它将原始的非凸问题分解为两个可以迭代解决的子问题。 在权重修剪问题中,这两个子问题分别通过 1) 梯度下降算法和 2) 欧几里得投影来解决。

在解决这两个子问题的过程中,原始模型的权重会被改变。 One-Shot Pruner 会根据给定的配置对模型剪枝。

此解决方案框架既适用于非结构化剪枝也适用于结构化剪枝的变体。

更多详细信息,参考 [A Systematic DNN Weight Pruning Framework using Alternating Direction Method of Multipliers](https://arxiv.org/abs/1804.03294)

#### 用法

PyTorch 代码

```python
from nni.compression.torch import ADMMPruner
config_list = [{
            'sparsity': 0.8,
            'op_types': ['Conv2d'],
            'op_names': ['conv1']
        }, {
            'sparsity': 0.92,
            'op_types': ['Conv2d'],
            'op_names': ['conv2']
        }]
pruner = ADMMPruner(model, config_list, trainer=trainer, num_iterations=30, epochs=5)
pruner.compress()
```

参考[示例](https://github.com/microsoft/nni/blob/master/examples/model_compress/auto_pruners_torch.py)了解更多信息。

#### ADMM Pruner 的用户配置

535
536
537
538
539
##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.ADMMPruner
```
Chi Song's avatar
Chi Song committed
540
541
542
543
544


## Lottery Ticket 假设
[The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks](https://arxiv.org/abs/1803.03635), 作者 Jonathan Frankle 和 Michael Carbin,提供了全面的测量和分析,并阐明了 *lottery ticket 假设*: 密集的、随机初始化的、包含子网络的前馈网络 (*winning tickets*) -- 在单独训练时 -- 在相似的迭代次数后达到了与原始网络相似的准确度。

545
本文中,作者使用叫做*迭代*修剪的方法:
Chi Song's avatar
Chi Song committed
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
> 1. 随机初始化一个神经网络 f(x;theta_0) (其中 theta_0 为 D_{theta}).
> 2. 将网络训练 j 次,得出参数 theta_j。
> 3. 在 theta_j 修剪参数的 p%,创建掩码 m。
> 4. 将其余参数重置为 theta_0 的值,创建获胜彩票 f(x;m*theta_0)。
> 5. 重复步骤 2、3 和 4。

如果配置的最终稀疏度为 P (e.g., 0.8) 并且有 n 次修建迭代,每次迭代修剪前一轮中剩余权重的 1-(1-P)^(1/n)。

### 用法

PyTorch 代码
```python
from nni.compression.torch import LotteryTicketPruner
config_list = [{
    'prune_iterations': 5,
    'sparsity': 0.8,
    'op_types': ['default']
}]
pruner = LotteryTicketPruner(model, config_list, optimizer)
pruner.compress()
for _ in pruner.get_prune_iterations():
    pruner.prune_iteration_start()
    for epoch in range(epoch_num):
        ...
```

上述配置意味着有 5 次迭代修剪。 由于在同一次运行中执行了 5 次修剪,LotteryTicketPruner 需要 `model``optimizer` (**注意,如果使用 `lr_scheduler`,也需要添加**) 来在每次开始新的修剪迭代时,将其状态重置为初始值。 使用 `get_prune_iterations` 来获取修建迭代,并在每次迭代开始时调用 `prune_iteration_start`。 为了模型能较好收敛,`epoch_num` 最好足够大。因为假设是在后几轮中具有较高稀疏度的性能(准确度)可与第一轮获得的相当。


*稍后支持 TensorFlow 版本。*

577
578
579
#### LotteryTicket Pruner 的用户配置

##### PyTorch
Chi Song's avatar
Chi Song committed
580

581
582
583
```eval_rst
..  autoclass:: nni.compression.torch.LotteryTicketPruner
```
Chi Song's avatar
Chi Song committed
584
585
586

### 重现实验

587
在重现时,在 MNIST 使用了与论文相同的配置。 [此处](https://github.com/microsoft/nni/tree/master/examples/model_compress/lottery_torch_mnist_fc.py)为实现代码。 在次实验中,修剪了10次,在每次修剪后,训练了 50 个 epoch。
Chi Song's avatar
Chi Song committed
588

Chi Song's avatar
Chi Song committed
589
![](../../img/lottery_ticket_mnist_fc.png)
Chi Song's avatar
Chi Song committed
590

Chi Song's avatar
Chi Song committed
591
上图展示了全连接网络的结果。 `round0-sparsity-0.0` 是没有剪枝的性能。 与论文一致,修剪约 80% 也能获得与不修剪时相似的性能,收敛速度也会更快。 如果修剪过多(例如,大于 94%),则精度会降低,收敛速度会稍慢。 与本文稍有不同,论文中数据的趋势比较明显。
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624


## Sensitivity Pruner
在每一轮,SensitivityPruner 根据对每一层准确率的敏感度对模型进行剪枝,直到满足整个模型最终配置的稀疏度:

        1. 分析模型当前状态下各层的敏感度。
        2. 根据敏感度对每一层剪枝。

更多详细信息,参考 [Learning both Weights and Connections for Efficient Neural Networks ](https://arxiv.org/abs/1506.02626)

#### 用法

PyTorch 代码

```python
from nni.compression.torch import SensitivityPruner
config_list = [{
        'sparsity': 0.5,
        'op_types': ['Conv2d']
    }]
pruner = SensitivityPruner(model, config_list, finetuner=fine_tuner, evaluator=evaluator)
# eval_args and finetune_args are the parameters passed to the evaluator and finetuner respectively
pruner.compress(eval_args=[model], finetune_args=[model])
```


#### Sensitivity Pruner 的用户配置

##### PyTorch

```eval_rst
..  autoclass:: nni.compression.torch.SensitivityPruner
```