未验证 提交 0ba185ef 编写于 作者: W whs 提交者: GitHub

Add document for slim demo. (#1967) (#2126)

* Add README.md

* Update url of images.

* Add images.

* Add usage.md

* Add demo.md

* Fix demo.md

* Add model_zoo.md

* Fix pretrain models download in model zoo.

* Refine documents.
1. Rename python to Python
2. Fix README.md: vgg->VGG, resnet->ResNet, mobilenet->MobileNet
3. Rename flops to FLOPS

* Move docs of PaddleSlim from models.fluid.PaddleSlim to models.PaddleSlim

* 1. Introduce model format in README.md
2. Merge results of combined strategies into one table.

* Refine README.md

* Refine README
1. Fix some terms in README.md
2. Fix description of model format in README.md
3. Add Channel-Wise into feature list
4. Rename 'paddle mobile' to 'Paddle Mobile'
5. Add comments for dynamic quantization and static quantization
6. Add entry url for Model Zoo in README.md

* Refine README.
上级 fef56494
<div align="center">
<h3>
<a href="docs/tutorial.md">
算法原理介绍
</a>
<span> | </span>
<a href="docs/usage.md">
使用文档
</a>
<span> | </span>
<a href="docs/demo.md">
示例文档
</a>
<span> | </span>
<a href="docs/model_zoo.md">
Model Zoo
</a>
</h3>
</div>
---
# PaddleSlim模型压缩工具库
PaddleSlim是PaddlePaddle框架的一个子模块。PaddleSlim首次在PaddlePaddle 1.4版本中发布。在PaddleSlim中,实现了目前主流的网络剪枝、量化、蒸馏三种压缩策略,主要用于压缩图像领域模型。在后续版本中,会添加更多的压缩策略,以及完善对NLP领域模型的支持。
## 目录
- [特色](#特色)
- [架构介绍](#架构介绍)
- [功能列表](#功能列表)
- [实验结果与ModelZoo](#简要实验结果)
- [模型导出格式](#模型导出格式)
## 主要特点
Paddle-Slim工具库有以下特点:
### 接口简单
- 以配置文件方式集中管理可配参数,方便实验管理
- 在普通模型训练脚本上,添加极少代码即可完成模型压缩
详见:[使用示例](docs/demo.md)
### 效果好
- 对于冗余信息较少的MobileNetV1模型,卷积核剪切策略依然可缩减模型大小,并保持尽量少的精度损失。
- 蒸馏压缩策略可明显提升原始模型的精度。
- 量化训练与蒸馏的组合使用,可同时做到缩减模型大小和提升模型精度。
详见:[效果数据与ModelZoo](docs/model_zoo.md)
### 功能更强更灵活
- 剪切压缩过程自动化
- 剪切压缩策略支持更多网络结构
- 蒸馏支持多种方式,用户可自定义组合loss
- 支持快速配置多种压缩策略组合使用
详见:[使用说明](docs/usage.md)
## 架构介绍
这里简要介绍模型压缩工具实现的整体原理,便于理解使用流程。
**图 1**为模型压缩工具的架构图,从上到下为API依赖关系。蒸馏模块、量化模块和剪切模块都间接依赖底层的paddle框架。目前,模型压缩工具作为了PaddlePaddle框架的一部分,所以已经安装普通版本paddle的用户需要重新下载安装支持模型压缩功能的paddle,才能使用压缩功能。
<p align="center">
<img src="docs/images/framework_0.png" height=452 width=706 hspace='10'/> <br />
<strong>图 1</strong>
</p>
**图 1**所示,最上层的紫色模块为用户接口,在Python脚本中调用模型压缩功能时,只需要构造一个Compressor对象即可,在[使用文档](docs/usage.md)中会有详细说明。
我们将每个压缩算法称为压缩策略,在迭代训练模型的过程中调用用户注册的压缩策略完成模型压缩,如**图2**所示。其中,模型压缩工具封装好了模型训练逻辑,用户只需要提供训练模型需要的网络结构、数据、优化策略(optimizer)等,在[使用文档](docs/usage.md)会对此详细介绍。
<p align="center">
<img src="docs/images/framework_1.png" height=255 width=646 hspace='10'/> <br />
<strong>图 2</strong>
</p>
## 功能列表
### 剪切
- 支持敏感度和uniform两种方式
- 支持VGG、ResNet、MobileNet等各种类型的网络
- 支持用户自定义剪切范围
### 量化训练
- 支持动态和静态两种量化训练方式
- 动态策略: 在推理过程中,动态统计激活的量化参数。
- 静态策略: 在推理过程中,对不同的输入,采用相同的从训练数据中统计得到的量化参数。
- 支持对权重全局量化和Channel-Wise量化
- 支持以兼容Paddle Mobile的格式保存模型
### 蒸馏
- 支持在teacher网络和student网络任意层添加组合loss
- 支持FSP loss
- 支持L2 loss
- 支持softmax with cross-entropy loss
### 其它功能
- 支持配置文件管理压缩任务超参数
- 支持多种压缩策略组合使用
- 蒸馏和剪切压缩过程支持checkpoints功能
## 简要实验结果
本节列出了PaddleSlim模型压缩工具库的一些实验结果,更多实验数据和预训练模型的下载,请参考:[详细实验结果与ModelZoo](docs/model_zoo.md)
### 量化训练
评估实验所使用数据集为ImageNet1000类数据,且以top-1准确率为衡量指标:
| Model | FP32| int8(X:abs_max, W:abs_max) | int8, (X:moving_average_abs_max, W:abs_max) |int8, (X:abs_max, W:channel_wise_abs_max) |
|:---|:---:|:---:|:---:|:---:|
|MobileNetV1|89.54%/70.91%|89.64%/71.01%|89.58%/70.86%|89.75%/71.13%|
|ResNet50|92.80%/76.35%|93.12%/76.77%|93.07%/76.65%|93.15%/76.80%|
### 卷积核剪切
数据:ImageNet 1000类
模型:MobileNetV1
原始模型大小:17M
原始精度(top5/top1): 89.54% / 70.91%
#### Uniform剪切
| FLOPS |model size| 精度损失(top5/top1)|精度(top5/top1) |
|---|---|---|---|
| -50%|-47.0%(9.0M)|-0.41% / -1.08%|89.13% / 69.83%|
| -60%|-55.9%(7.5M)|-1.34% / -2.67%|88.22% / 68.24%|
| -70%|-65.3%(5.9M)|-2.55% / -4.34%|86.99% / 66.57%|
#### 基于敏感度迭代剪切
| FLOPS |精度(top5/top1)|
|---|---|
| -0% |89.54% / 70.91% |
| -20% |90.08% / 71.48% |
| -36% |89.62% / 70.83%|
| -50% |88.77% / 69.31%|
### 蒸馏
数据:ImageNet 1000类
模型:MobileNetV1
|- |精度(top5/top1) |收益(top5/top1)|
|---|---|---|
| 单独训| 89.54% / 70.91%| - |
| ResNet50蒸馏训| 90.92% / 71.97%| +1.28% / +1.06%|
### 组合实验
数据:ImageNet 1000类
模型:MobileNetV1
|压缩策略 |精度(top5/top1) |模型大小|
|---|---|---|
| Baseline|89.54% / 70.91%|17.0M|
| ResNet50蒸馏|90.92% / 71.97%|17.0M|
| ResNet50蒸馏训 + 量化|90.94% / 72.08%|4.2M|
| 剪切-50% FLOPS|89.13% / 69.83%|9.0M|
| 剪切-50% FLOPS + 量化|89.11% / 69.70%|2.3M|
## 模型导出格式
压缩框架支持导出以下格式的模型:
- **Paddle Fluid模型格式:** Paddle Fluid模型格式,可通过Paddle框架加载使用。
- **Paddle Mobile模型格式:** 仅在量化训练策略时使用,兼容[Paddle Mobile](https://github.com/PaddlePaddle/paddle-mobile)的模型格式。
<div align="center">
<h3>
<a href="usage.md">
使用文档
</a>
<span> | </span>
<a href="tutorial.md">
算法原理介绍
</a>
<span> | </span>
<a href="model_zoo.md">
Model Zoo
</a>
</h3>
</div>
---
# Paddle模型压缩工具库使用示例
## 目录
- [概述](#0-概述)
- [数据准备](#1-数据准备)
- [压缩脚本准备](#2-压缩脚本介绍)
- [蒸馏示例](#31-蒸馏)
- [剪切示例](#32-uniform剪切)
- [量化示例](#34-int8量化训练)
- [蒸馏后量化示例](#35-蒸馏后int8量化)
- [剪切后量化示例](#36-剪切后int8量化)
## 0. 概述
该示例参考[PaddlePaddle/models/fluid/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleCV/image_classification)下代码,分别实现了以下策略:
1. <a href="#31-蒸馏">蒸馏</a>:用ResNet50对MobileNetV1的在ImageNet 1000数据上的蒸馏训练。
2. <a href="#32-uniform剪切">剪切</a>:对预训练好的MobileNetV1进行剪切
3. <a href="#34-int8量化训练">量化</a>:对预训练好的MobileNetV1进行int8量化训练
4. <a href="#35-蒸馏后int8量化">蒸馏量化组合</a>:先用ResNet50对MobileNetV1进行蒸馏,再对蒸馏后得到的模型进行int8量化训练。
5. <a href="#36-剪切后int8量化">剪切量化组合</a>:先用Uniform剪切策略对MobileNetV1进行剪切,再对剪切后的模型进行int8量化训练
本示例完整代码链接:https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleSlim
使用方式:
克隆[PaddlePaddle/models](https://github.com/PaddlePaddle/models)到本地,并进入models/fluid/PaddleSlim路径。
**文件结构**
```
/.
|-configs # 压缩任务的配置文件,包括:蒸馏、int8量化量化、filter剪切和组合策略的配置文件。
|-data # 存放训练数据和pretrain model
|-models # MobileNetV1和ResNet50网络结构的定义
|-quant_low_level_api # 量化训练的底层API, 用于处理特殊情况,用户可暂时忽略该内容
|-compress.py # 模型压缩任务主脚本,示例中多个压缩策略共用这一个脚本。定义了压缩任务需要的模型相关的信息。
|-reader.py # 定义数据处理逻辑
|-run.sh # 模型压缩任务启动脚本
|-utility.py # 定义了常用的工具方法
```
本示例中的五个压缩策略使用相同的训练数据和压缩Python脚本`compress.py`,每种策略对应独立的配置文件。
第1章介绍数据准备,第2章介绍脚本compress.py中几个关键步骤。第3章分别介绍了如何执行各种压缩策略的示例。
## 1. 数据准备
### 1.1 训练数据准备
参考[models/fluid/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleCV/image_classification#data-preparation)下的数据准备教程准备训练数据,并放入PaddleSlim/data路径下。
### 1.2 预训练模型准备
脚本run.sh会自动从[models/fluid/PaddleCV/image_classification](https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleCV/image_classification#supported-models-and-performances)下载ResNet50和MobileNetV1的预训练模型,并放入PaddleSlim/pretrain路径下。
## 2. 压缩脚本介绍
`compress.py`中定义了执行压缩任务需要的所有模型相关的信息,这里对几个关键的步骤进行简要介绍:
### 2.1 目标网络的定义
compress.py的以下代码片段定义了train program, 这里train program只有前向计算操作。
```
out = model.net(input=image, class_dim=args.class_dim)
cost = fluid.layers.cross_entropy(input=out, label=label)
avg_cost = fluid.layers.mean(x=cost)
acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1)
acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5)
```
然后,通过clone方法得到eval_program, 用来在压缩过程中评估模型精度,如下:
```
val_program = fluid.default_main_program().clone()
```
定义完目标网络结构,需要对其初始化,并根据需要加载预训练模型。
### 2.2 定义feed_list和fetch_list
对于train program, 定义train_feed_list用于指定从train data reader中取的数据feed给哪些variable。定义train_fetch_list用于指定在训练时,需要在log中展示的结果。如果需要在训练过程中在log中打印accuracy信心,则将('acc_top1', acc_top1.name)添加到train_fetch_list中即可。
```
train_feed_list = [('image', image.name), ('label', label.name)]
train_fetch_list = [('loss', avg_cost.name)]
```
>注意: 在train_fetch_list里必须有loss这一项。
对于eval program. 同上定义eval_feed_list和train_fetch_list:
```
val_feed_list = [('image', image.name), ('label', label.name)]
val_fetch_list = [('acc_top1', acc_top1.name), ('acc_top5', acc_top5.name)]
```
### 2.3 定义teacher网络
以下代码片段定义了teacher网络,并对其进行了初始化操作。
```
teacher_program = fluid.Program()
startup_program = fluid.Program()
with fluid.program_guard(teacher_program, startup_program):
img = teacher_program.global_block()._clone_variable(image, force_persistable=False)
predict = teacher_model.net(img, class_dim=args.class_dim)
exe.run(startup_program)
```
需要注意的是:
- teacher网络只有一个输入,直接clone在train program(fluid.default_main_program) 中定义的image变量即可。
- teacher网络的输出只需要到predict即可,不用加loss和accuracy等操作
- teacher网络需要初始化并加载预训练模型。
>注意: ResNet50和MobileNetV1的fc layer的weight parameter的名称都为‘fc_1.weight’,所以需要到PaddleSlim/models/resnet.py中修改一下ResNet fc layer的名称, 同时,修改ResNet50 pretrain model中响应weight的文件名,使其与resnet.py中的名称保持一致。
## 3. 执行压缩策略示例
所有示例的执行命令都放在`run.sh`文件中,用户可以修改run.sh后,执行不同的压缩策略示例。
### 3.1 蒸馏
在该示例中,用预训练好的ResNet50模型监督训练MobileNetV1模型。
修改run.sh, 执行以下命令,执行蒸馏压缩示例:
```
# for distillation
#--------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--model "MobileNet" \
--teacher_model "ResNet50" \
--teacher_pretrained_model ./data/pretrain/ResNet50_pretrained \
--compress_config ./configs/mobilenetv1_resnet50_distillation.yaml
```
该示例在评估数据集上的准确率结果如下:
|- |精度(top5/top1) |
|---|---|
| ResNet50蒸馏训| 90.92% / 71.97%|
<p align="center">
<img src="images/demo/distillation_result.png" height=300 width=400 hspace='10'/> <br />
<strong>图1</strong>
</p>
### 3.2 Uniform剪切
在该示例中,将MobileNetV1模型剪掉50%的FLOPS.
修改run.sh, 执行以下命令,执行Uniform卷积核剪切模型压缩示例:
```
# for uniform filter pruning
#---------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--model "MobileNet" \
--pretrained_model ./data/pretrain/MobileNetV1_pretrained \
--compress_config ./configs/filter_pruning_uniform.yaml
```
该示例在评估数据集上的准确率结果如下:
| FLOPS |模型大小|精度(top5/top1) |
|---|---|---|
| -50%|-47.0%(9.0M) |89.13% / 69.83%|
<p align="center">
<img src="images/demo/pruning_uni_result.png" height=300 width=400 hspace='10'/> <br />
<strong>图2</strong>
</p>
### 3.3 敏感度剪切
在该示例中,将MobileNetV1模型剪掉50%的FLOPS.
修改run.sh, 执行以下命令,执行敏感度卷积核剪切压缩示例:
```
# for sensitivity filter pruning
#---------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--model "MobileNet" \
--pretrained_model ./data/pretrain/MobileNetV1_pretrained \
--compress_config ./configs/filter_pruning_sen.yaml
```
该示例在评估数据集上的准确率结果如下:
| FLOPS |模型大小| 精度(top5/top1) |
|---|---|---|
| -50%|-61.2%(6.6M) |88.47% / 68.68%|
<p align="center">
<img src="images/demo/pruning_sen_result.png" height=300 width=400 hspace='10'/> <br />
<strong>图3</strong>
</p>
### 3.4 int8量化训练
修改run.sh, 执行以下命令,执行int8量化训练示例:
```
# for quantization
#---------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--batch_size 64 \
--model "MobileNet" \
--pretrained_model ./pretrain/MobileNetV1_pretrained \
--compress_config ./configs/quantization.yaml
```
该示例结果如下:
| Model | int8量化(top1_acc)|
|---|---|
|MobileNetV1|71.00%|
### 3.5 蒸馏后int8量化
本示例先用ResNet50模型对MobileNetV1蒸馏训练120个epochs,然后再对MobileNetV1模型进行动态int8量化训练。
修改run.sh, 执行以下命令,执行蒸馏与int8量化训练结合的模型压缩示例:
```
# for distillation with quantization
#-----------------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--model "MobileNet" \
--teacher_model "ResNet50" \
--teacher_pretrained_model ./data/pretrain/ResNet50_pretrained \
--compress_config ./configs/quantization_dist.yaml
```
该示例结果如下:
|- |精度(top5/top1) |
|---|---|
| ResNet50蒸馏训+量化|90.94% / 72.08%|
### 3.6 剪切后int8量化
本示例先将预训练好的MobileNetV1模型剪掉50% FLOPS, 让后再对其进行动态int8量化训练。
修改run.sh, 执行以下命令,执行剪切与int8量化训练结合的模型压缩示例:
```
# for uniform filter pruning with quantization
#---------------------------------------------
export CUDA_VISIBLE_DEVICES=0
python compress.py \
--model "MobileNet" \
--pretrained_model ./data/pretrain/MobileNetV1_pretrained \
--compress_config ./configs/quantization_pruning.yaml
```
该示例结果如下:
| 剪切FLOPS |剪切+量化(dynamic)
|---|---|
| -50%|89.11% / 69.70%|
<div align="center">
<h3>
<a href="usage.md">
使用文档
</a>
<span> | </span>
<a href="demo.md">
示例文档
</a>
<span> | </span>
<a href="tutorial.md">
算法原理介绍
</a>
</h3>
</div>
---
# Paddle模型压缩工具实验与模型库
## 目录
- [量化实验](#int8量化训练)
- [剪切实验](#剪切实验)
- [蒸馏实验](蒸馏实验)
- [组合实验](组合实验)
## 1. int8量化训练
评估实验所使用数据集为ImageNet 1000类,且以top-1准确率为衡量指标:
| Model | FP32| int8(A:abs_max, W:abs_max) | int8, (A:moving_average_abs_max, W:abs_max) |int8, (A:abs_max, W:channel_wise_abs_max) |
|:---|:---:|:---:|:---:|:---:|
|MobileNetV1|[70.916%]()|[71.008%]()|[70.84%]()|[71.00%]()|
|ResNet50|[76.352%]()|[76.612%]()|[76.456%]()|[76.73%]()|
点击表中超链接即可下载预训练模型。
模型大小变化:
| Model | FP32| int8(A:abs_max, W:abs_max) | int8, (A:moving_average_abs_max, W:abs_max) |int8, (A:abs_max, W:channel_wise_abs_max) |
|:---|:---:|:---:|:---:|:---:|
|MobileNetV1|17M|4.8M(1/3.54)|5.1M(1/3.33)|4.9M(1/3.47)|
|ResNet50|99M|26M(1/3.81)|27M(1/3.67)|27M(1/3.67)|
## 2. 剪切实验
数据: ImageNet 1000类
模型:MobileNetV1
原始模型大小:17M
原始精度(top5/top1): 89.54% / 70.91%
### 2.1 基于敏感度迭代剪切
#### 实验说明
分步剪切,每步剪掉模型7%的FLOPS.
optimizer配置如下:
```
epoch_size=5000
boundaries = [30, 60, 90, 120] * epoch_size # for -50% FLOPS
#boundaries = [35, 65, 95, 125] * epoch_size # for -60% FLOPS
#boundaries = [50, 80, 110, 140] * epoch_size # for -70% FLOPS
values = [0.01, 0.1, 0.01, 0.001, 0.0001]
optimizer = fluid.optimizer.Momentum(
momentum=0.9,
learning_rate=fluid.layers.piecewise_decay(boundaries=boundaries, values=values),
regularization=fluid.regularizer.L2Decay(1e-4))
```
#### 实验结果
| FLOPS |model size| 精度(top5/top1) |下载模型|
|---|---|---|---|
| -50%|-59.4%(6.9M) |88.22% / 68.41% |[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_0.5_model.tar.gz)|
| -60%|-70.6%(5.0M)|87.01% / 66.31% |[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_0.6_model.tar.gz)|
| -70%|-78.8%(3.6M)|85.30% / 63.41% |[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_0.7_model.tar.gz)|
### 2.2 基于敏感度一次性剪切
#### 实验说明
一步剪切掉50%FLOPS, 然后fine-tune 120个epoch.
optimizer配置如下:
```
epoch_size=5000
boundaries = [30, 60, 90] * epoch_size
values = [0.1, 0.01, 0.001, 0.0001]
optimizer = fluid.optimizer.Momentum(
momentum=0.9,
learning_rate=fluid.layers.piecewise_decay(boundaries=boundaries, values=values),
regularization=fluid.regularizer.L2Decay(1e-4))
```
#### 实验结果
| FLOPS |model size|精度(top5/top1) |模型下载|
|---|---|---|---|
| -50%|-61.2%(6.6M)| 88.47% / 68.68% |[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_0.5-1step.tar.gz)|
### 2.3 基于敏感度分步剪切
#### 实验说明
1. 一次剪掉20%FLOPS, fine-tune 120个epoch
2. 在上一步基础上,一次剪掉20%FLOPS, fine-tune 120个epoch
3. 在上一步基础上,一次剪掉20%FLOPS, fine-tune 120个epoch
optimizer配置如下:
```
epoch_size=5000
boundaries = [30, 60, 90] * epoch_size
values = [0.1, 0.01, 0.001, 0.0001]
optimizer = fluid.optimizer.Momentum(
momentum=0.9,
learning_rate=fluid.layers.piecewise_decay(boundaries=boundaries, values=values),
regularization=fluid.regularizer.L2Decay(1e-4))
```
#### 实验结果
| FLOPS |精度(top5/top1)|模型下载 |
|---|---|---|
| -20%|90.08% / 71.48% |[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_3step_0.2_model.tar.gz)|
| -36%|89.62% / 70.83%|[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_3step_0.36_model.tar.gz)|
| -50%|88.77% / 69.31%|[点击下载](https://paddle-slim-models.bj.bcebos.com/sensitive_filter_pruning_3step_0.5_model.tar.gz)|
### 2.4 Uniform剪切
#### 实验说明
一次剪掉指定比例的FLOPS,然后fine-tune 120个epoch.
optimizer配置如下:
```
epoch_size=5000
boundaries = [30, 60, 90] * epoch_size
values = [0.1, 0.01, 0.001, 0.0001]
optimizer = fluid.optimizer.Momentum(
momentum=0.9,
learning_rate=fluid.layers.piecewise_decay(boundaries=boundaries, values=values),
regularization=fluid.regularizer.L2Decay(1e-4))
```
#### 实验结果
| FLOPS |model size|精度(top5/top1) |模型下载 |
|---|---|---|---|
| -50%|-47.0%(9.0M) | 89.13% / 69.83%|[点击下载](https://paddle-slim-models.bj.bcebos.com/uniform_filter_pruning_0.5_model.tar.gz)|
| -60%|-55.9%(7.5M)|88.22% / 68.24%| [点击下载](https://paddle-slim-models.bj.bcebos.com/uniform_filter_pruning_0.6_model.tar.gz)|
| -70%|-65.3%(5.9M)|86.99% / 66.57%| [点击下载](https://paddle-slim-models.bj.bcebos.com/uniform_filter_pruning_0.7_model.tar.gz)|
## 3. 蒸馏
数据: ImageNet 1000类
模型:MobileNetV1
原始模型大小:17M
原始精度(top5/top1): 89.54% / 70.91%
#### 实验说明
用训练好的ResNet50蒸馏训练MobileNetV1, 训练120个epoch. 对第一个block加FSP loss; 对softmax layer的输入加L2-loss.
optimizer配置如下:
```
epoch_size=5000
boundaries = [30, 60, 90] * epoch_size
values = [0.1, 0.01, 0.001, 0.0001]
optimizer = fluid.optimizer.Momentum(
momentum=0.9,
learning_rate=fluid.layers.piecewise_decay(boundaries=boundaries, values=values),
regularization=fluid.regularizer.L2Decay(1e-4))
```
#### 实验结果
|- |精度(top5/top1) |收益(top5/top1)|模型下载 |
|---|---|---|---|
| ResNet50蒸馏训| 90.92% / 71.97%| +1.28% / +1.06%| [点击下载](https://paddle-slim-models.bj.bcebos.com/mobilenetv1_resnet50_distillation_model.tar.gz)|
## 4. 组合实验
### 4.1 蒸馏后量化
#### 实验说明
#### 实验结果
|- |精度(top5/top1) |模型下载 |
|---|---|---|
| ResNet50蒸馏训+量化|90.94% / 72.08%| [点击下载]()|
### 4.2 剪切后量化
#### 实验说明
#### 实验结果
| 剪切FLOPS |剪切+量化(dynamic)|模型下载 |
|---|---|---|
| -50%|89.11% / 69.70%| [点击下载]()|
<div align="center">
<h3>
<a href="usage.md">
使用文档
</a>
<span> | </span>
<a href="demo.md">
示例文档
</a>
<span> | </span>
<a href="model_zoo.md">
Model Zoo
</a>
</h3>
</div>
---
# Paddle模型压缩工具库算法原理介绍
## 目录
- [量化原理介绍](#1-quantization-aware-training量化介绍)
- [剪切原理介绍](#2-卷积核剪切原理)
- [蒸馏原理介绍](#3-蒸馏)
## 1. Quantization Aware Training量化介绍
### 1.1 背景
近年来,定点量化使用更少的比特数(如8-bit、3-bit、2-bit等)表示神经网络的权重和激活已被验证是有效的。定点量化的优点包括低内存带宽、低功耗、低计算资源占用以及低模型存储需求等。
<p align="center">
<img src="images/tutorial/quan_table_0.png" height=258 width=600 hspace='10'/> <br />
<strong>表1: 不同类型操作的开销对比</strong>
</p>
由表1可知,低精度定点数操作的硬件面积大小及能耗比高精度浮点数要少几个数量级。 使用定点量化可带来4倍的模型压缩、4倍的内存带宽提升,以及更高效的cache利用(很多硬件设备,内存访问是主要能耗)。除此之外,计算速度也会更快(通常具有2x-3x的性能提升)。由表2可知,在很多场景下,定点量化操作对精度并不会造成损失。另外,定点量化对神经网络于嵌入式设备上的推断来说是极其重要的。
<p align="center">
<img src="images/tutorial/quan_table_1.png" height=155 width=500 hspace='10'/> <br />
<strong>表2:模型量化前后精度对比</strong>
</p>
目前,学术界主要将量化分为两大类:`Post Training Quantization``Quantization Aware Training``Post Training Quantization`是指使用KL散度、滑动平均等方法确定量化参数且不需要重新训练的定点量化方法。`Quantization Aware Training`是在训练过程中对量化进行建模以确定量化参数,它与`Post Training Quantization`模式相比可以提供更高的预测精度。本文主要针对`Quantization Aware Training`量化模式进行阐述说明。
### 1.2 量化原理
#### 1.2.1 量化方式
目前,存在着许多方法可以将浮点数量化成定点数。例如:
$$ r = min(max(x, a), b)$$ $$ s = \frac{b - a}{n - 1} $$ $$ q = \left \lfloor \frac{r - a}{s} \right \rceil $$
式中,$x$是待量化的浮点值,$[a, b]$是量化范围,$a$是待量化浮点数中的最小值, $b$ 是待量化浮点数中的最大值。$\left \lfloor \right \rceil$ 表示将结果四舍五入到最近的整数。如果量化级别为$k$,则$n$为$2^k$。例如,若$k$为8,则$n$为256。$q$是量化得到的整数。
PaddleSlim框架中选择的量化方法为最大绝对值量化(`max-abs`),具体描述如下:
$$ M = max(abs(x)) $$ $$ q = \left \lfloor \frac{x}{M} * (n - 1) \right \rceil $$
式中,$x$是待被量化的浮点值,$M$是待量化浮点数中的绝对值最大值。$\left \lfloor \right \rceil$表示将结果四舍五入到最近的整数。对于8bit量化,PaddleSlim采用`int8_t`,即$n=2^7=128$。$q$是量化得到的整数。
无论是`min-max量化`还是`max-abs量化`,他们都可以表示为如下形式:
$q = scale * r + b$
其中`min-max``max-abs`被称为量化参数或者量化比例或者量化范围。
#### 1.2.2 量化训练框架
##### 1.2.2.1 前向传播
前向传播过程采用模拟量化的方式,具体描述如下:
<p align="center">
<img src="images/tutorial/quan_forward.png" height=433 width=335 hspace='10'/> <br />
<strong>图1:基于模拟量化训练的前向过程</strong>
</p>
由图1可知,基于模拟量化训练的前向过程可被描述为以下四个部分:
1) 输入和权重均被量化成8-bit整数。
2) 在8-bit整数上执行矩阵乘法或者卷积操作。
3) 反量化矩阵乘法或者卷积操作的输出结果为32-bit浮点型数据。
4) 在32-bit浮点型数据上执行偏置加法操作。此处,偏置并未被量化。
对于通用矩阵乘法(`GEMM`),输入$X$和权重$W$的量化操作可被表述为如下过程:
$$ X_q = \left \lfloor \frac{X}{X_m} * (n - 1) \right \rceil $$ $$ W_q = \left \lfloor \frac{W}{W_m} * (n - 1) \right \rceil $$
执行通用矩阵乘法:
$$ Y = X_q * W_q $$
反量化$Y$:
$$
\begin{align}
Y_{dq} = \frac{Y}{(n - 1) * (n - 1)} * X_m * W_m \
=\frac{X_q * W_q}{(n - 1) * (n - 1)} * X_m * W_m \
=(\frac{X_q}{n - 1} * X_m) * (\frac{W_q}{n - 1} * W_m) \
\end{align}
$$
上述公式表明反量化操作可以被移动到`GEMM`之前,即先对$Xq$和$Wq$执行反量化操作再做`GEMM`操作。因此,前向传播的工作流亦可表示为如下方式:
<p align="center">
<img src="images/tutorial/quan_fwd_1.png" height=435 width=341 hspace='10'/> <br />
<strong>图2:基于模拟量化训练前向过程的等价工作流</strong>
</p>
训练过程中,PaddleSlim使用图2中所示的等价工作流。在设计中,量化Pass在IrGraph中插入量化op和反量化op。因为在连续的量化、反量化操作之后输入仍然为32-bit浮点型数据。因此,PaddleSlim量化训练框架所采用的量化方式被称为模拟量化。
##### 1.2.2.2 反向传播
由图3可知,权重更新所需的梯度值可以由量化后的权重和量化后的激活求得。反向传播过程中的所有输入和输出均为32-bit浮点型数据。注意,梯度更新操作需要在原始权重上进行,即计算出的梯度将被加到原始权重上而非量化后或反量化后的权重上。
<p align="center">
<img src="images/tutorial/quan_bwd.png" height=300 width=650 hspace='10'/> <br />
<strong>图3:基于模拟量化训练的反向传播和权重更新过程</strong>
</p>
因此,量化Pass也会改变相应反向算子的某些输入。
#### 1.2.3 确定量化参数
存在着两种策略可以计算求取量化比例系数,即动态策略和静态策略。动态策略会在每次迭代过程中计算量化比例系数的值。静态策略则对不同的输入采用相同的量化比例系数。
对于权重而言,在训练过程中采用动态策略。换句话说,在每次迭代过程中量化比例系数均会被重新计算得到直至训练过程结束。
对于激活而言,可以选择动态策略也可以选择静态策略。若选择使用静态策略,则量化比例系数会在训练过程中被评估求得,且在推断过程中被使用(不同的输入均保持不变)。静态策略中的量化比例系数可于训练过程中通过如下三种方式进行评估:
1. 在一个窗口中计算激活最大绝对值的平均值。
2. 在一个窗口中计算激活最大绝对值的最大值。
3. 在一个窗口中计算激活最大绝对值的滑动平均值,计算公式如下:
$$ Vt = (1 - k) * V + k * V_{t-1} $$
式中,$V$ 是当前batch的最大绝对值, $Vt$是滑动平均值。$k$是一个因子,例如其值可取为0.9。
## 2. 卷积核剪切原理
该策略参考paper: [Pruning Filters for Efficient ConvNets](https://arxiv.org/pdf/1608.08710.pdf)
该策略通过减少卷积层中卷积核的数量,来减小模型大小和降低模型计算复杂度。
### 2.1 剪切卷积核
**剪切注意事项1**
剪切一个conv layer的filter,需要修改后续conv layer的filter. 如**图4**所示,剪掉Xi的一个filter,会导致$X_{i+1}$少一个channel, $X_{i+1}$对应的filter在input_channel纬度上也要减1.
<p align="center">
<img src="images/tutorial/pruning_0.png" height=200 width=600 hspace='10'/> <br />
<strong>图4</strong>
</p>
**剪切注意事项2**
**图5**所示,剪切完$X_i$之后,根据注意事项1我们从$X_{i+1}$的filter中删除了一行(图中蓝色行),在计算$X_{i+1}$的filters的l1_norm(图中绿色一列)的时候,有两种选择:
算上被删除的一行:independent pruning
减去被删除的一行:greedy pruning
<p align="center">
<img src="images/tutorial/pruning_1.png" height=200 width=450 hspace='10'/> <br />
<strong>图5</strong>
</p>
**剪切注意事项3**
在对ResNet等复杂网络剪切的时候,还要考虑到后当前卷积层的修改对上一层卷积层的影响。
**图6**所示,在对residual block剪切时,$X_{i+1}$层如何剪切取决于project shortcut的剪切结果,因为我们要保证project shortcut的output和$X_{i+1}$的output能被正确的concat.
<p align="center">
<img src="images/tutorial/pruning_2.png" height=240 width=600 hspace='10'/> <br />
<strong>图6</strong>
</p>
### 2.2 Uniform剪切卷积网络
每层剪切一样比例的卷积核。
在剪切一个卷积核之前,按l1_norm对filter从高到低排序,越靠后的filter越不重要,优先剪掉靠后的filter.
### 2.3 基于敏感度剪切卷积网络
根据每个卷积层敏感度的不同,剪掉不同比例的卷积核。
#### 两个假设
- 在一个conv layer的parameter内部,按l1_norm对filter从高到低排序,越靠后的filter越不重要。
- 两个layer剪切相同的比例的filters,我们称对模型精度影响更大的layer的敏感度相对高。
#### 剪切filter的指导原则
- layer的剪切比例与其敏感度成反比
- 优先剪切layer内l1_norm相对低的filter
#### 敏感度的理解
<p align="center">
<img src="images/tutorial/pruning_3.png" height=200 width=400 hspace='10'/> <br />
<strong>图7</strong>
</p>
**图7**所示,横坐标是将filter剪切掉的比例,竖坐标是精度的损失,每条彩色虚线表示的是网络中的一个卷积层。
以不同的剪切比例**单独**剪切一个卷积层,并观察其在验证数据集上的精度损失,并绘出**图7**中的虚线。虚线上升较慢的,对应的卷积层相对不敏感,我们优先剪不敏感的卷积层的filter.
#### 选择最优的剪切率组合
我们将**图7**中的折线拟合为**图8**中的曲线,每在竖坐标轴上选取一个精度损失值,就在横坐标轴上对应着一组剪切率,如**图8**中黑色实线所示。
用户给定一个模型整体的剪切率,我们通过移动**图5**中的黑色实线来找到一组满足条件的且合法的剪切率。
<p align="center">
<img src="images/tutorial/pruning_4.png" height=200 width=400 hspace='10'/> <br />
<strong>图8</strong>
</p>
#### 迭代剪切
考虑到多个卷积层间的相关性,一个卷积层的修改可能会影响其它卷积层的敏感度,我们采取了多次剪切的策略,步骤如下:
- step1: 统计各卷积层的敏感度信息
- step2: 根据当前统计的敏感度信息,对每个卷积层剪掉少量filter, 并统计FLOPS,如果FLOPS已满足要求,进入step4,否则进行step3。
- step3: 对网络进行简单的fine-tune,进入step1
- step4: fine-tune训练至收敛
## 3. 蒸馏
一般情况下,模型参数量越多,结构越复杂,其性能越好,但参数也越允余,运算量和资源消耗也越大;模型蒸馏是将复杂网络中的有用信息将复杂网络中的有用信息提取出来提取出来,迁移到一个更小的网络中去,在我们的工具包中,支持两种蒸馏的方法。
第一种是传统的蒸馏方法(参考论文:[Distilling the Knowledge in a Neural Network](https://arxiv.org/pdf/1503.02531.pdf)
使用复杂的网络作为teacher模型去监督训练一个参数量和运算量更少的student模型。teacher模型可以是一个或者多个提前训练好的高性能模型。student模型的训练有两个目标:一个是原始的目标函数,为student模型输出的类别概率和label的交叉熵,记为hard-target;另一个是student模型输出的类别概率和teacher模型输出的类别概率的交叉熵,记为soft target,这两个loss加权后得到最终的训练loss,共同监督studuent模型的训练。
第二种是基于FSP的蒸馏方法(参考论文:[A Gift from Knowledge Distillation:
Fast Optimization, Network Minimization and Transfer Learning](http://openaccess.thecvf.com/content_cvpr_2017/papers/Yim_A_Gift_From_CVPR_2017_paper.pdf)
相比传统的蒸馏方法直接用小模型去拟合大模型的输出,该方法用小模型去拟合大模型不同层特征之间的转换关系,其用一个FSP矩阵(特征的内积)来表示不同层特征之间的关系,大模型和小模型不同层之间分别获得多个FSP矩阵,然后使用L2 loss让小模型的对应层FSP矩阵和大模型对应层的FSP矩阵尽量一致,具体如下图所示。这种方法的优势,通俗的解释是,比如将蒸馏类比成teacher(大模型)教student(小模型)解决一个问题,传统的蒸馏是直接告诉小模型问题的答案,让小模型学习,而学习FSP矩阵是让小模型学习解决问题的中间过程和方法,因此其学到的信息更多。
<p align="center">
<img src="images/tutorial/distillation_0.png" height=300 width=600 hspace='10'/> <br />
<strong>图9</strong>
</p>
由于小模型和大模型之间通过L2 loss进行监督,必须保证两个FSP矩阵的维度必须相同,而FSP矩阵的维度为M*N,其中M、N分别为输入和输出特征的channel数,因此大模型和小模型的FSP矩阵需要一一对应。
## 4. 参考文献
1. [High-Performance Hardware for Machine Learning](https://media.nips.cc/Conferences/2015/tutorialslides/Dally-NIPS-Tutorial-2015.pdf)
2. [Quantizing deep convolutional networks for efficient inference: A whitepaper](https://arxiv.org/pdf/1806.08342.pdf)
3. [Pruning Filters for Efficient ConvNets](https://arxiv.org/pdf/1608.08710.pdf)
4. [Distilling the Knowledge in a Neural Network](https://arxiv.org/pdf/1503.02531.pdf)
5. [A Gift from Knowledge Distillation: Fast Optimization, Network Minimization and Transfer Learning](http://openaccess.thecvf.com/content_cvpr_2017/papers/Yim_A_Gift_From_CVPR_2017_paper.pdf)
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册