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

Refine index of docs (#658)

上级 95160fc1
# PaddleSlim
中文 | [English](README_en.md)
[![Documentation Status](https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat)](https://paddleslim.readthedocs.io/en/latest/)
[![Documentation Status](https://img.shields.io/badge/中文文档-最新-brightgreen.svg)](https://paddleslim.readthedocs.io/zh_CN/latest/)
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE)
......@@ -16,7 +14,7 @@ PaddleSlim是一个专注于深度学习模型压缩的工具库,提供**剪
| :-----------: | :------------: | :------------:| :----------:|
| 1.0.1 | <=1.7 | 2.7 | 支持静态图 |
| 1.1.1 | 1.8 | 2.7 | 支持静态图 |
| 1.2.0 | 2.0Beta/RC | 2.8 | 支持静态图; 新增CPU预测 |
| 1.2.0 | 2.0Beta/RC | 2.8 | 支持静态图 |
| 2.0.0 | 2.0 | 2.8 | 支持动态图和静态图 |
......@@ -29,12 +27,15 @@ pip install paddleslim -i https://pypi.tuna.tsinghua.edu.cn/simple
安装指定版本:
```bash
pip install paddleslim=1.2.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install paddleslim=2.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
## 最近更新
2021.2.5: 发布V2.0.0版本,新增支持动态图,新增OFA压缩功能,优化剪枝功能。
2020.9.16: 发布V1.2.0版本,新增PACT量化训练功能,新增DML(互蒸馏功能),修复部分剪裁bug,加强对depthwise_conv2d的剪裁能力,优化剪裁和量化API的易用性和灵活性。
更多信息请参考:[release note](https://github.com/PaddlePaddle/PaddleSlim/releases)
## 功能概览
......@@ -105,52 +106,92 @@ PaddleSlim在典型视觉和自然语言处理任务上做了模型压缩,并
## 文档教程
### 快速上手
### 快速开始
- 量化训练 - [动态图]() | [静态图]()
- 离线量化 - [动态图]() | [静态图]()
- 剪裁 - [动态图]() | [静态图]()
- 蒸馏 - [动态图]() | [静态图]()
- NAS - [动态图]() | [静态图]()
- 量化训练 - [动态图](docs/zh_cn/quick_start/dygraph/dygraph_quant_aware_training_tutorial.md) | [静态图](docs/zh_cn/quick_start/static/quant_aware_tutorial.md)
- 离线量化 - [动态图](docs/zh_cn/quick_start/dygraph/dygraph_quant_post_tutorial.md) | [静态图](docs/zh_cn/quick_start/static/quant_post_static_tutorial.md)
- 剪裁 - [动态图](docs/zh_cn/quick_start/dygraph/dygraph_pruning_tutorial.md) | [静态图](docs/zh_cn/quick_start/static/pruning_tutorial.md)
- 蒸馏 - [静态图](docs/zh_cn/quick_start/static/distillation_tutorial.md)
- NAS - [静态图](docs/zh_cn/quick_start/static/nas_tutorial.md)
### 进阶教程
#### 压缩功能详解
- 通道剪裁
- [四种剪裁策略效果对比与应用方法](docs/zh_cn/tutorials/pruning/overview.md)
- [L1NormFilterPruner](docs/zh_cn/tutorials/pruning/overview.md#l1normfilterpruner)
- [FPGMFilterPruner](docs/zh_cn/tutorials/pruning/overview.md#fpgmfilterpruner)
- [SlimFilterFilterPruner](docs/zh_cn/tutorials/pruning/overview.md#slimfilterpruner)
- [OptSlimFilterPruner](docs/zh_cn/tutorials/pruning/overview.md#optslimfilterpruner)
- 剪裁功能详解: [动态图](docs/zh_cn/tutorials/pruning/dygraph/filter_pruning.md) | [静态图](docs/zh_cn/tutorials/pruning/static/image_classification_sensitivity_analysis_tutorial.md)
- 自定义剪裁策略:[动态图](docs/zh_cn/tutorials/pruning/dygraph/self_defined_filter_pruning.md)
- 低比特量化
- [三种量化方法介绍与应用](docs/zh_cn/tutorials/quant/overview.md)
- 量化训练:[动态图](docs/zh_cn/tutorials/quant/dygraph/quant_aware_training_tutorial.md) | [静态图](docs/zh_cn/quick_start/static/quant_aware_tutorial.md)
- 离线量化:[动态图](docs/zh_cn/tutorials/quant/dygraph/dygraph_quant_post_tutorial.md) | [静态图](docs/zh_cn/tutorials/quant/static/quant_post_tutorial.md)
- embedding量化:[静态图](docs/zh_cn/tutorials/quant/static/embedding_quant_tutorial.md)
- NAS
- [四种NAS策略介绍和应用](docs/zh_cn/tutorials/nas/overview.md)
- 蒸馏
- [知识蒸馏示例](demo/distillation)
[量化训练]() | [离线量化]() | [剪裁]() | [蒸馏]() | [NAS]()
#### 推理部署
- [概述]()
- [PaddleInference量化部署]()
- [Intel CPU量化部署]()
- [GPU量化部署]()
- [PaddleLite量化部署]()
- [Intel CPU量化部署](demo/mkldnn_quant/README.md)
- [Nvidia GPU量化部署](demo/quant/deploy/TensorRT/README.md)
- [PaddleLite量化部署](docs/zh_cn/deploy/deploy_cls_model_on_mobile_device.md)
### CV模型压缩
- [检测模型压缩(基于PaddleDetection)]()
- YOLOv3 3.5倍加速方案
- 检测模型压缩(基于PaddleDetection)
- 压缩方案
- YOLOv3 3.5倍加速方案: 文档整理中...
- 方法应用-静态图
- [在COCO和VOC上蒸馏MobileNetV1-YOLOv3](docs/zh_cn/cv/detection/static/paddledetection_slim_distillation_tutorial.md)
- [MobileNetV1-YOLOv3低比特量化训练](docs/zh_cn/cv/detection/static/paddledetection_slim_quantization_tutorial.md)
- [人脸检测模型小模型结构搜索](docs/zh_cn/cv/detection/static/paddledetection_slim_nas_tutorial.md)
- [剪枝](docs/zh_cn/cv/detection/static/paddledetection_slim_pruing_tutorial.md)
- [剪枝与蒸馏的结合使用](docs/zh_cn/cv/detection/static/paddledetection_slim_prune_dist_tutorial.md)
- [卷积层敏感度分析](docs/zh_cn/cv/detection/static/paddledetection_slim_sensitivy_tutorial.md)
- 方法应用-动态图
- 文档整理中...
- 分割模型压缩(基于PaddleSeg)
- 压缩方案
- 方案建设中...
- [分割模型压缩(基于PaddleSeg)]()
- 方法应用-静态图
- 文档整理中...
- 方法应用-动态图
- 文档整理中...
- [OCR模型压缩(基于PaddleOCR)]()
- [3.5M模型压缩方案]()
### NLP模型压缩
- 压缩方案
- 3.5M模型压缩方案: 文档整理中...
- 方法应用-静态图
- [低比特量化训练](https://github.com/PaddlePaddle/PaddleOCR/tree/release/1.1/deploy/slim/quantization)
- [剪枝](https://github.com/PaddlePaddle/PaddleOCR/tree/release/1.1/deploy/slim/prune)
- [BERT]()
- [ERNIE]()
- 方法应用-动态图
- 文档整理中...
### 通用轻量级模型
- 人脸模型(SlimfaceNet)
- 图像分类模型(SlimMobileNet)
### NLP模型压缩
- [BERT](docs/zh_cn/nlp/paddlenlp_slim_ofa_tutorial.md)
- [ERNIE](docs/zh_cn/nlp/ernie_slim_ofa_tutorial.md)
### API文档
- 动态图
- 静态图
- [动态图](docs/zh_cn/api_cn/dygraph)
- [静态图](docs/zh_cn/api_cn/static)
### [FAQ]()
......@@ -163,3 +204,5 @@ PaddleSlim在典型视觉和自然语言处理任务上做了模型压缩,并
我们非常欢迎你可以为PaddleSlim提供代码,也十分感谢你的反馈。
## 欢迎加入PaddleSlim技术交流群
请添加微信公众号"AIDigest",备注“压缩”,飞桨同学会拉您进入微信交流群。
# PaddleSlim INT8量化模型在CPU上的部署和预测
# Intel CPU量化部署
## 概述
......
# PaddleSlim INT8量化模型使用TensorRT的部署和预测
# Nvidia GPU量化部署
## 概述
NVIDIA TensorRT 是一个高性能的深度学习预测库,适用于Nvidia GPU,可为深度学习推理应用程序提供低延迟和高吞吐量。PaddlePaddle 采用子图的形式对TensorRT进行了集成,即我们可以使用该模块来提升Paddle模型的预测性能。本教程将介绍如何使用TensortRT部署PaddleSlim量化得到的模型,无论是量化训练(QAT)还是离线量化(PTQ)模型均可支持。对于常见图像分类模型,INT8模型的推理速度通常是FP32模型的3.2-6.7倍。
......
......@@ -2,6 +2,22 @@
## 最新版本信息
### v2.0.0(02/2020)
- 新增支持动态图剪枝、量化
- 新增OFA压缩策略
- 修复若干剪枝相关问题
## 历史版本信息
### v1.2.0
- 新增PACT量化训练功能,提升PaddleSlim在图像目标检测、图像语义分割、OCR等模型上的量化训练效果。
- 新增DML(互蒸馏功能),提升部分模型的蒸馏效果。
- 修复部分剪裁bug,加强对depthwise_conv2d的剪裁能力。
- 优化剪裁和量化API的易用性和灵活性。
### v1.1.0(05/2020)
- 量化
......@@ -17,8 +33,6 @@
- 新增一种基于强化学习的模型结构搜索策略,并提供扩展接口,为用户调研实现新策略提供参考。
## 历史版本信息
### v1.0.1
- 拆分PaddleSlim为独立repo。
......
# 算法原理
## 目录
- [量化原理介绍](#1-quantization-aware-training量化介绍)
- [剪裁原理介绍](#2-卷积核剪裁原理)
- [蒸馏原理介绍](#3-蒸馏)
- [轻量级模型结构搜索原理介绍](#4-轻量级模型结构搜索)
## 1. Quantization Aware Training量化介绍
### 1.1 背景
近年来,定点量化使用更少的比特数(如8-bit、3-bit、2-bit等)表示神经网络的权重和激活已被验证是有效的。定点量化的优点包括低内存带宽、低功耗、低计算资源占用以及低模型存储需求等。
<p align="center">
<img src="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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`模式相比可以提供更高的预测精度。
### 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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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_q = X_q * W_q $$
对量化乘积结果$Yq$进行反量化:
$$
\begin{align}
Y_{dq} = \frac{Y_q}{(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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/quan_bwd.png" height=300 width=650 hspace='10'/> <br />
<strong>图3:基于模拟量化训练的反向传播和权重更新过程</strong>
</p>
因此,量化Pass也会改变相应反向算子的某些输入。
##### 1.2.2.3 确定量化比例系数
存在着两种策略可以计算求取量化比例系数,即动态策略和静态策略。动态策略会在每次迭代过程中计算量化比例系数的值。静态策略则对不同的输入采用相同的量化比例系数。
对于权重而言,在训练过程中采用动态策略。换句话说,在每次迭代过程中量化比例系数均会被重新计算得到直至训练过程结束。
对于激活而言,可以选择动态策略也可以选择静态策略。若选择使用静态策略,则量化比例系数会在训练过程中被评估求得,且在推断过程中被使用(不同的输入均保持不变)。静态策略中的量化比例系数可于训练过程中通过如下三种方式进行评估:
1. 在一个窗口中计算激活最大绝对值的平均值。
2. 在一个窗口中计算激活最大绝对值的最大值。
3. 在一个窗口中计算激活最大绝对值的滑动平均值,计算公式如下:
$$ Vt = (1 - k) * V + k * V_{t-1} $$
式中,$V$ 是当前batch的最大绝对值, $Vt$是滑动平均值。$k$是一个因子,例如其值可取为0.9。
#### 1.2.4 训练后量化
训练后量化是基于采样数据,采用KL散度等方法计算量化比例因子的方法。相比量化训练,训练后量化不需要重新训练,可以快速得到量化模型。
训练后量化的目标是求取量化比例因子,主要有两种方法:非饱和量化方法 ( No Saturation) 和饱和量化方法 (Saturation)。非饱和量化方法计算FP32类型Tensor中绝对值的最大值`abs_max`,将其映射为127,则量化比例因子等于`abs_max/127`。饱和量化方法使用KL散度计算一个合适的阈值`T` (`0<T<mab_max`),将其映射为127,则量化比例因子等于`T/127`。一般而言,对于待量化op的权重Tensor,采用非饱和量化方法,对于待量化op的激活Tensor(包括输入和输出),采用饱和量化方法 。
训练后量化的实现步骤如下:
* 加载预训练的FP32模型,配置`DataLoader`
* 读取样本数据,执行模型的前向推理,保存待量化op激活Tensor的数值;
* 基于激活Tensor的采样数据,使用饱和量化方法计算它的量化比例因子;
* 模型权重Tensor数据一直保持不变,使用非饱和方法计算它每个通道的绝对值最大值,作为每个通道的量化比例因子;
* 将FP32模型转成INT8模型,进行保存。
## 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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/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. 轻量级模型结构搜索
深度学习模型在很多任务上都取得了不错的效果,网络结构的好坏对最终模型的效果有非常重要的影响。手工设计网络需要非常丰富的经验和众多尝试,并且众多的超参数和网络结构参数会产生爆炸性的组合,常规的random search几乎不可行,因此最近几年自动模型搜索技术(Neural Architecture Search)成为研究热点。区别于传统NAS,我们专注在搜索精度高并且速度快的模型结构,我们将该功能统称为Light-NAS.
### 4.1 搜索策略
搜索策略定义了使用怎样的算法可以快速、准确找到最优的网络结构参数配置。常见的搜索方法包括:强化学习、贝叶斯优化、进化算法、基于梯度的算法。我们当前的实现以模拟退火算法为主。
#### 4.1.1 模拟退火
模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
鉴于物理中固体物质的退火过程与一般组合优化问题之间的相似性,我们将其用于网络结构的搜索。
使用模拟退火算法搜索模型的过程如下:
$$
T_k = T_0*\theta^k
$$
$$
\begin{equation}
P(r_k) =
\begin{cases}
e^{\frac{(r_k-r)}{T_k}} & r_k < r\\
1 & r_k>=r
\end{cases}
\end{equation}
$$
在第k次迭代,搜到的网络为$N_k$, 对$N_k$训练若干epoch后,在测试集上得到reward为$r_k$, 以概率$P(r_k)$接受$r_k$,即执行$r=r_k$。$r$在搜索过程起始时被初始化为0. $T_0$为初始化温度,$\theta$为温度衰减系数,$T_k$为第k次迭代的温度。
在我们的NAS任务中,区别于RL每次重新生成一个完整的网络,我们将网络结构映射成一段编码,第一次随机初始化,然后每次随机修改编码中的一部分(对应于网络结构的一部分)生成一个新的编码,然后将这个编码再映射回网络结构,通过在训练集上训练一定的epochs后的精度以及网络延时融合获得reward,来指导退火算法的收敛。
### 4.2 搜索空间
搜索空间定义了优化问题的变量,变量规模决定了搜索算法的难度和搜索时间。因此为了加快搜索速度,定义一个合理的搜索空间至关重要。在Light-NAS中,为了加速搜索速度,我们将一个网络划分为多个block,先手动按链状层级结构堆叠c,再 使用搜索算法自动搜索每个block内部的结构。
因为要搜索出在移动端运行速度快的模型,我们参考了MobileNetV2中的Linear Bottlenecks和Inverted residuals结构,搜索每一个Inverted residuals中的具体参数,包括kernelsize、channel扩张倍数、重复次数、channels number。如图10所示:
<p align="center">
<img src="https://raw.githubusercontent.com/PaddlePaddle/PaddleSlim/release/1.0.1/docs/images/algo/light-nas-block.png" height=300 width=600 hspace='10'/> <br />
<strong>图10</strong>
</p>
### 4.3 模型延时评估
搜索过程支持 FLOPS 约束和模型延时约束。而基于 Android/iOS 移动端、开发板等硬件平台,迭代搜索过程中不断测试模型的延时不仅消耗时间而且非常不方便,因此我们开发了模型延时评估器来评估搜索得到模型的延时。通过延时评估器评估得到的延时与模型实际测试的延时波动偏差小于 10%。
延时评估器分为配置硬件延时评估器和评估模型延时两个阶段,配置硬件延时评估器只需要执行一次,而评估模型延时则在搜索过程中不断评估搜索得到的模型延时。
- 配置硬件延时评估器
1. 获取搜索空间中所有不重复的 op 及其参数
2. 获取每组 op 及其参数的延时
- 评估模型延时
1. 获取给定模型的所有 op 及其参数
2. 根据给定模型的所有 op 及参数,利用延时评估器去估计模型的延时
## 5. 参考文献
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)
目标检测(PaddleDetection)
=========================
.. toctree::
:maxdepth: 1
dygraph/index
static/index
静态图压缩教程
==============
静态图
=========================
.. toctree::
:maxdepth: 1
image_classification_sensitivity_analysis_tutorial.md
darts_nas_turorial.md
paddledetection_slim_distillation_tutorial.md
paddledetection_slim_nas_tutorial.md
paddledetection_slim_pruing_tutorial.md
paddledetection_slim_prune_dist_tutorial.md
paddledetection_slim_quantization_tutorial.md
image_classification_mkldnn_quant_tutorial.md
paddledetection_slim_sensitivy_tutorial.md
CV模型压缩
==============
.. toctree::
:maxdepth: 1
detection/index
../../../demo/quant/deploy/TensorRT/README.md
\ No newline at end of file
../../../demo/mkldnn_quant/README.md
\ No newline at end of file
推理部署
==============
该部分介绍如何将PaddleSlim产出的量化模型部署到移动端、Intel CPU和Nvidia GPU上。
.. toctree::
:maxdepth: 1
deploy_cls_model_on_mobile_device.md
deploy_cls_model_on_x86_cpu.md
deploy_cls_model_on_nvidia_gpu.md
......@@ -7,15 +7,19 @@
==================
.. toctree::
:maxdepth: 1
:maxdepth: 2
intro.md
install.md
quick_start/index
tutorials/index
deploy/index
cv/index
nlp/index
models/index
api_cn/index
FAQ/index
model_zoo/index
model_zoo.md
algo/algo.md
CHANGELOG.md
......
# 安装
安装PaddleSlim前,请确认已正确安装Paddle1.6版本或更新版本。Paddle安装请参考:[Paddle安装教程](https://www.paddlepaddle.org.cn/install/quick)
- 安装develop版本
# 安装
安装最新版本:
```bash
git clone https://github.com/PaddlePaddle/PaddleSlim.git
cd PaddleSlim
python setup.py install
pip install paddleslim -i https://pypi.tuna.tsinghua.edu.cn/simple
```
- 安装官方发布的最新版本
安装指定版本:
```bash
pip install paddleslim -i https://pypi.org/simple
pip install paddleslim=2.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
- 安装历史版本
## 版本对齐
请点击[pypi.org](https://pypi.org/project/paddleslim/#history)查看可安装历史版本。
| PaddleSlim | PaddlePaddle | PaddleLite | 备注 |
| :-----------: | :------------: | :------------:| :----------:|
| 1.0.1 | <=1.7 | 2.7 | 支持静态图 |
| 1.1.1 | 1.8 | 2.7 | 支持静态图 |
| 1.2.0 | 2.0Beta/RC | 2.8 | 支持静态图 |
| 2.0.0 | 2.0 | 2.8 | 支持动态图和静态图 |
# 介绍
PaddleSlim是一个模型压缩工具库,包含模型剪裁、定点量化、知识蒸馏、超参搜索和模型结构搜索等一系列模型压缩策略。
对于业务用户,PaddleSlim提供完整的模型压缩解决方案,可用于图像分类、检测、分割等各种类型的视觉场景。
同时也在持续探索NLP领域模型的压缩方案。另外,PaddleSlim提供且在不断完善各种压缩策略在经典开源任务的benchmark,
以便业务用户参考。
对于模型压缩算法研究者或开发者,PaddleSlim提供各种压缩策略的底层辅助接口,方便用户复现、调研和使用最新论文方法。
PaddleSlim会从底层能力、技术咨询合作和业务场景等角度支持开发者进行模型压缩策略相关的创新工作。
## 功能
- 模型剪裁
- 卷积通道均匀剪裁
- 基于敏感度的卷积通道剪裁
- 基于进化算法的自动剪裁
- 定点量化
- 在线量化训练(training aware)
- 静态离线量化(static post training)
- 动态离线量化(dynamic post training)
- 知识蒸馏
- 支持单进程知识蒸馏
- 支持多进程分布式知识蒸馏
- 神经网络结构自动搜索(NAS)
- 支持基于进化算法的轻量神经网络结构自动搜索
- 支持One-Shot网络结构自动搜索
- 支持基于梯度的DARTS网络结构自动搜索
- 支持 FLOPS / 硬件延时约束
- 支持多平台模型延时评估
- 支持用户自定义搜索算法和搜索空间
## 部分压缩策略效果
### 分类模型
数据: ImageNet2012; 模型: MobileNetV1;
|压缩策略 |精度收益(baseline: 70.91%) |模型大小(baseline: 17.0M)|
|:---:|:---:|:---:|
| 知识蒸馏(ResNet50)| **+1.06%** | |
| 知识蒸馏(ResNet50) + int8量化训练 |**+1.10%**| **-71.76%**|
| 剪裁(FLOPs-50%) + int8量化训练|**-1.71%**|**-86.47%**|
### 图像检测模型
#### 数据:Pascal VOC;模型:MobileNet-V1-YOLOv3
| 压缩方法 | mAP(baseline: 76.2%) | 模型大小(baseline: 94MB) |
| :---------------------: | :------------: | :------------:|
| 知识蒸馏(ResNet34-YOLOv3) | **+2.8%** | |
| 剪裁 FLOPs -52.88% | **+1.4%** | **-67.76%** |
|知识蒸馏(ResNet34-YOLOv3)+剪裁(FLOPs-69.57%)| **+2.6%**|**-67.00%**|
#### 数据:COCO;模型:MobileNet-V1-YOLOv3
| 压缩方法 | mAP(baseline: 29.3%) | 模型大小|
| :---------------------: | :------------: | :------:|
| 知识蒸馏(ResNet34-YOLOv3) | **+2.1%** | |
| 知识蒸馏(ResNet34-YOLOv3)+剪裁(FLOPs-67.56%) | **-0.3%** | **-66.90%**|
### 搜索
数据:ImageNet2012; 模型:MobileNetV2
|硬件环境 | 推理耗时 | Top1准确率(baseline:71.90%) |
|:---------------:|:---------:|:--------------------:|
| RK3288 | **-23%** | +0.07% |
| Android cellphone | **-20%** | +0.16% |
| iPhone 6s | **-17%** | +0.32% |
# 简介
PaddleSlim是一个专注于深度学习模型压缩的工具库,提供**剪裁、量化、蒸馏、和模型结构搜索**等模型压缩策略,帮助用户快速实现模型的小型化。
## 版本对齐
| PaddleSlim | PaddlePaddle | PaddleLite | 备注 |
| :-----------: | :------------: | :------------:| :----------:|
| 1.0.1 | <=1.7 | 2.7 | 支持静态图 |
| 1.1.1 | 1.8 | 2.7 | 支持静态图 |
| 1.2.0 | 2.0Beta/RC | 2.8 | 支持静态图 |
| 2.0.0 | 2.0 | 2.8 | 支持动态图和静态图 |
## 最近更新
- 2021.2.5: 发布V2.0.0版本,新增支持动态图,新增OFA压缩功能,优化剪枝功能。
- 2020.9.16: 发布V1.2.0版本,新增PACT量化训练功能,新增DML(互蒸馏功能),修复部分剪裁bug,加强对depthwise_conv2d的剪裁能力,优化剪裁和量化API的易用性和灵活性。
## 功能概览
PaddleSlim支持以下功能,也支持自定义量化、裁剪等功能。
<table border=1>
<tr align="center" valign="bottom">
<th>Quantization</th>
<th>Pruning</th>
<th>NAS</th>
<th>Distilling</th>
</tr>
<tr valign="top">
<td>
<ul>
<li>QAT</li>
<li>PACT</li>
<li>PTQ-Static</li>
<li>PTQ-Dynamic</li>
<li>Embedding Quant</li>
</ul>
</td>
<td>
<ul>
<li>SensitivityPruner</li>
<li>FPGMFilterPruner</li>
<li>L1NormFilterPruner</li>
<li>L2NormFilterPruner</li>
<li>*SlimFilterPruner</li>
<li>*OptSlimFilterPruner</li>
</ul>
</td>
<td>
<ul>
<li>*Simulate Anneal based NAS</li>
<li>*Reinforcement Learning based NAS</li>
<li>**DARTS</li>
<li>**PC-DARTS</li>
<li>**Once-for-All</li>
<li>*Hardware-aware Search</li>
</ul>
</td>
<td>
<ul>
<li>*FSP</li>
<li>*DML</li>
<li>*DK for YOLOv3</li>
</ul>
</td>
</tr>
</table>
注:*表示仅支持静态图,**表示仅支持动态图
### 效果展示
PaddleSlim在典型视觉和自然语言处理任务上做了模型压缩,并且测试了Nvidia GPU、ARM等设备上的加速情况,这里展示部分模型的压缩效果,详细方案可以参考下面CV和NLP模型压缩方案:
<p align="center">
<img src="https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/images/benchmark.png?raw=true" height=185 width=849 hspace='10'/> <br />
<strong>表1: 部分模型压缩加速情况</strong>
</p>
注:
- YOLOv3: 在移动端SD855上加速3.55倍。
- PP-OCR: 体积由8.9M减少到2.9M, 在SD855上加速1.27倍。
- BERT: 模型参数由110M减少到80M,精度提升的情况下,Tesla T4 GPU FP16计算加速1.47倍。
## 许可证书
本项目的发布受[Apache 2.0 license](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/LICENSE)许可认证。
模型库
========
.. toctree::
:maxdepth: 1
model_zoo.md
quant_model_zoo.md
distillation_model_zoo.md
prune_model_zoo.md
nas_model_zoo.md
## 模型库概览
### 量化主要结论
| 任务 | 模型 | 数据集 | 结论 | 更多细节和模型下载 |
|:--:|:---:|:--:|:--:|:--:|
| 分类| MobileNetV1 | ImageNet | top1 -0.39% <br> 高通835 <font color='red'>1.6-2倍</font>加速<br> 高通855 armv8 <font color='red'>2倍</font>加速<br> 麒麟970 <font color='red'>1.6-2倍</font>加速 |[详细数据和模型下载]() |
### 蒸馏主要结论
| 任务 | 模型 | 数据集 | 结论 | 更多细节和模型下载 |
|:--:|:---:|:--:|:--:|:--:|
| 分类| MobileNetV1 | ImageNet | top1 -0.39% <br> 高通835 <font color='red'>1.6-2倍</font>加速<br> 高通855 armv8 <font color='red'>2倍</font>加速<br> 麒麟970 <font color='red'>1.6-2倍</font>加速 |[详细数据和模型下载]() |
### 剪裁主要结论
| 任务 | 模型 | 数据集 | 结论 | 更多细节和模型下载 |
|:--:|:---:|:--:|:--:|:--:|
| 分类| MobileNetV1 | ImageNet | top1 -0.39% <br> 高通835 <font color='red'>1.6-2倍</font>加速<br> 高通855 armv8 <font color='red'>2倍</font>加速<br> 麒麟970 <font color='red'>1.6-2倍</font>加速 |[详细数据和模型下载]() |
### nas 主要结论
| 任务 | 模型 | 数据集 | 结论 | 更多细节和模型下载 |
|:--:|:---:|:--:|:--:|:--:|
| 分类| MobileNetV1 | ImageNet | top1 -0.39% <br> 高通835 <font color='red'>1.6-2倍</font>加速<br> 高通855 armv8 <font color='red'>2倍</font>加速<br> 麒麟970 <font color='red'>1.6-2倍</font>加速 |[详细数据和模型下载]() |
通用轻量级模型
==============
.. toctree::
:maxdepth: 1
slimfacenet.md
../../../demo/slimfacenet/README.md
\ No newline at end of file
......@@ -13,10 +13,8 @@
4. 每个batch数据在训练前首先中会选择当前要训练的子网络配置(子网络配置目前仅包括对整个模型的宽度的选择),参数更新时仅会更新当前子网络计算中用到的那部分参数。
5. 通过以上的方式来优化整个超网络参数,训练完成后选择满足加速要求和精度要求的子模型。
<p align="center">
<img src="https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/images/algo/ofa_bert.jpg" width="950"/><br />
整体流程图
</p>
![](./images/ofa_bert.png)
<center>整体流程</center>
## 基于ERNIE repo代码进行压缩
本教程基于PaddleSlim2.0及之后版本、Paddle1.8.5和ERNIE 0.0.4dev及之后版本,请确认已正确安装Paddle、PaddleSlim和ERNIE。
......
NLP模型压缩
==============
.. toctree::
:maxdepth: 1
ernie_slim_ofa_tutorial.md
paddlenlp_slim_ofa_tutorial.md
......@@ -13,10 +13,9 @@
4. 每个batch数据在训练前首先中会选择当前要训练的子网络配置(子网络配置目前仅包括对整个模型的宽度的选择),参数更新时仅会更新当前子网络计算中用到的那部分参数。
5. 通过以上的方式来优化整个超网络参数,训练完成后选择满足加速要求和精度要求的子模型。
<p align="center">
<img src="https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/images/algo/ofa_bert.jpg" width="950"/><br />
整体流程图
</p>
![](./images/ofa_bert.png)
<center>整体流程</center>
## 基于PaddleNLP repo代码进行压缩
本教程基于PaddleSlim2.0及之后版本、Paddle2.0rc1及之后版本和PaddleNLP2.0beta及之后版本,请确认已正确安装Paddle、PaddleSlim和PaddleNLP。
......
# 图像分类模型通道剪裁-快速开始
# 卷积Filter剪裁
该教程以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的卷积通道剪裁接口](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/dygraph_docs)
该教程以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的卷积通道剪裁接口]()。
该示例包含以下步骤:
1. 导入依赖
......@@ -27,7 +27,7 @@ from paddleslim.dygraph import L1NormFilterPruner
## 2. 构建网络和数据集
该章节构造一个用于对CIFAR10数据进行分类的分类模型,选用`MobileNetV1`,并将输入大小设置为`[3, 32, 32]`,输出类别数为10。
为了方便展示示例,我们使用Paddle提供的[预定义分类模型](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/vision/models/mobilenetv1/MobileNetV1_cn.html#mobilenetv1)[高层API](https://www.paddlepaddle.org.cn/documentation/docs/zh/2.0-rc1/tutorial/quick_start/high_level_api/high_level_api.html),执行以下代码构建分类模型:
为了方便展示示例,我们使用Paddle提供的[预定义分类模型](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api/paddle/vision/models/mobilenetv1/MobileNetV1_cn.html#mobilenetv1)[高层API](),执行以下代码构建分类模型:
```
net = models.mobilenet_v1(pretrained=False, scale=1.0, num_classes=10)
......
# 图像分类模型量化训练-快速开始
# 量化训练
量化训练要解决的问题是将FP32浮点数量化成INT8整数进行存储和计算,通过在训练中建模量化对模型的影响,降低量化误差。
......
# 图像分类模型离线量化-快速开始
# 离线量化
离线量化又称为训练后量化,仅需要使用少量校准数据,确定最佳的量化参数降低量化误差。这种方法需要的数据量较少,但量化模型精度相比在线量化稍逊。
......
动态图
==============
.. toctree::
:maxdepth: 1
dygraph_pruning_tutorial.md
dygraph_quant_aware_training_tutorial.md
dygraph_quant_post_tutorial.md
......@@ -4,8 +4,7 @@
.. toctree::
:maxdepth: 1
static/index.rst
dygraph_pruning_tutorial.md
dygraph_quant_aware_training_tutorial.md
dygraph_quant_post_tutorial.md
dygraph/index.rst
# 图像分类模型知识蒸馏-快速开始
# 知识蒸馏
该教程以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的知识蒸馏接口](https://paddlepaddle.github.io/PaddleSlim/api/single_distiller_api/)
该示例包含以下步骤:
......
静态图快速开始
静态图
==============
.. toctree::
......
# 图像分类网络结构搜索-快速开始
# 网络结构搜索
该教程以图像分类模型MobileNetV2为例,说明如何在cifar10数据集上快速使用[网络结构搜索接口](../api/nas_api.md)
该示例包含以下步骤:
......
# 图像分类模型通道剪裁-快速开始
# 卷积Filter剪裁
该教程以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的卷积通道剪裁接口]()。
该示例包含以下步骤:
......
# 图像分类模型量化训练-快速开始
# 量化训练
该教程以图像分类模型MobileNetV1为例,说明如何快速使用PaddleSlim的[量化训练接口](../api_cn/quantization_api.html)。 该示例包含以下步骤:
该教程以图像分类模型MobileNetV1为例,说明如何快速使用PaddleSlim的量化训练接口。 该示例包含以下步骤:
1. 导入依赖
2. 构建模型
......@@ -128,7 +128,7 @@ test(val_program)
## 4. 量化
按照[默认配置](https://paddlepaddle.github.io/PaddleSlim/api_cn/quantization_api.html#id2)``train_program````val_program``中加入量化和反量化op.
按照[默认配置]()在``train_program``和``val_program``中加入量化和反量化op.
```python
......@@ -156,7 +156,7 @@ test(val_quant_program)
## 6. 保存量化后的模型
``4. 量化``中使用接口``slim.quant.quant_aware``接口得到的模型只适合训练时使用,为了得到最终使用时的模型,需要使用[slim.quant.convert](https://paddlepaddle.github.io/PaddleSlim/api_cn/quantization_api.html#convert)接口,然后使用[fluid.io.save_inference_model](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/api_cn/io_cn/save_inference_model_cn.html#save-inference-model)保存模型。``float_prog``的参数数据类型是float32,但是数据范围是int8, 保存之后可使用fluid或者paddle-lite加载使用,paddle-lite在使用时,会先将类型转换为int8。``int8_prog``的参数数据类型是int8, 保存后可看到量化后模型大小,不可加载使用。
``4. 量化``中使用接口``slim.quant.quant_aware``接口得到的模型只适合训练时使用,为了得到最终使用时的模型,需要使用[slim.quant.convert]()接口,然后使用[fluid.io.save_inference_model]()保存模型。``float_prog``的参数数据类型是float32,但是数据范围是int8, 保存之后可使用fluid或者paddle-lite加载使用,paddle-lite在使用时,会先将类型转换为int8。``int8_prog``的参数数据类型是int8, 保存后可看到量化后模型大小,不可加载使用。
```python
......
# 图像分类模型静态离线量化-快速开始
# 离线量化
该教程以图像分类模型MobileNetV1为例,说明如何快速使用PaddleSlim的[静态离线量化接口](../api_cn/quantization_api.html#quant-post-static)。 该示例包含以下步骤:
该教程以图像分类模型MobileNetV1为例,说明如何快速使用PaddleSlim的[静态离线量化接口]()。 该示例包含以下步骤:
1. 导入依赖
2. 构建模型
......
../../../../demo/quant/deploy/TensorRT/README.md
\ No newline at end of file
../../../../demo/mkldnn_quant/README.md
\ No newline at end of file
压缩教程
高阶教程
==============
该部分包括各个策略的综述与详细教程。
.. toctree::
:maxdepth: 1
static/index.rst
pruning/index
nas/index
quant/index
动态图
==============
.. toctree::
:maxdepth: 1
nas_ofa.md
# Once-For-All
# OFA详细教程
&emsp;&emsp;[Once-For-All(以下简称OFA)](https://arxiv.org/abs/1908.09791)主要的目的是训练一个超网络,根据不同的硬件从超网络中选择满足时延要求和精度要求的小模型。可以基于已有的预训练模型进行压缩也是OFA一个很大的优势。
&emsp;&emsp;为了防止子网络之间互相干扰,本论文提出了一种Progressive Shrinking(PS)的模式进行超网络训练,逐步从大型子网络到小型子网络进行训练。首先是从最大的子网络开始训练,例如:超网络包含可变的卷积核大小 kernel_size = {3, 5, 7},可变的网络结构深度 depth = {2, 3, 4} 和可变的网络的宽度 expand_ratio = {2, 4, 6},则训练卷积核为7、深度为4,宽度为6的网络。之后逐步将其添加到搜索空间中来逐步调整网络以支持较小的子网络。具体来说,在训练了最大的网络之后,我们首先支持可变卷积核大小,可以在{3,5,7}中进行选择,而深度和宽度则保持最大值。然后,我们依次支持可变深度和可变宽度。
......
NAS
==============
.. toctree::
:maxdepth: 1
overview.md
dygraph/index
static/index
# PaddleSlim模型结构搜索总览
# Overview
PaddleSlim提供了4种网络结构搜索的方法:基于模拟退火进行网络结构搜索、基于强化学习进行网络结构搜索、基于梯度进行网络结构搜索和Once-For-All。
......@@ -10,7 +10,7 @@ PaddleSlim提供了4种网络结构搜索的方法:基于模拟退火进行网
| [DARTS/PCDARTS](https://github.com/PaddlePaddle/PaddleSlim/blob/develop/docs/zh_cn/api_cn/dygraph/nas/darts.rst) | DARTS是基于梯度进行网络结构搜索,这种方式比较高效,大大减少了搜索时间和所需要的机器资源。 |DARTS、PCDARTS、ProxylessNAS|
# 参考文献
## 参考文献
[1] H. Cai, C. Gan, T. Wang, Z. Zhang, and S. Han. Once for all: Train one network and specialize it for efficient deployment. In International Conference on Learning Representations, 2020.
[2] Pham, H.; Guan, M. Y.; Zoph, B.; Le, Q. V.; and Dean, J. 2018. Efficient neural architecture search via parameter sharing. arXiv preprint arXiv:1802.03268.
[3] Zoph B, Vasudevan V, Shlens J, et al. Learning transferable architectures for scalable image recognition[J]. arXiv preprint arXiv:1707.07012, 2017, 2(6).
......
静态图
==============
.. toctree::
:maxdepth: 1
sanas_darts_space.md
# Paddle动态图卷积模型Filter剪裁教程
# Filter剪裁详细教程
该教程以MobileNetV1模型和Cifar10分类任务为例,介绍如何使用PaddleSlim对动态图卷积模型进行filter剪裁。
......
动态图
==============
.. toctree::
:maxdepth: 1
filter_pruning.md
self_defined_filter_pruning.md
剪裁
==============
.. toctree::
:maxdepth: 1
overview.md
dygraph/index
static/index
# PaddleSlim模型裁剪方法总览
PaddleSlim裁剪模块针对动态图目前实现了两种裁剪相关算法,并提供对应的接口给用户来实现自身的裁剪算法。
## 1. 基于敏感度的模型分析算法
本节内容分为两部分:卷积层敏感度分析和基于敏感度的`Filters`剪裁,其中『卷积层敏感度分析』也可以被称作『卷积层重要度分析』。我们定义越重要的卷积层越敏感。
PaddleSlim提供了工具类`Pruner`来进行重要性分析和剪裁操作,不同的`Pruner`的子类对应不同的分析和剪裁策略,本示例以`L1NormFilterPruner`为例说明。首先我们声明一个`L1NormFilterPruner`对象,如下所示:
```python
from paddleslim.dygraph import L1NormFilterPruner
pruner = L1NormFilterPruner(net, [1, 3, 224, 224])
```
如果本地文件系统已有一个存储敏感度信息(见1.1节)的文件,声明`L1NormFilterPruner`对象时,可以通过指定`sen_file`选项加载计算好的敏感度信息,如下:
```python
#pruner = L1NormFilterPruner(net, [1, 3, 224, 224]), sen_file="./sen.pickle")
```
### 1.1 卷积重要性分析
在对卷积网络中的filters进行剪裁时,我们需要判断哪些`filters`不重要,然后优先剪掉不重要的`filters`
在一个卷积内,我们使用`filter``L1 Norm`来代表重要性,`L1 Norm`越大的`filters`越重要。在多个卷积间,我们通过敏感度代表卷积的重要性,越敏感的卷积越重要,重要的卷积会被剪掉相对较少的`filters`
单个卷积内的filters重要性计算会在剪裁时进行,无需用户关注。本小节,我们只介绍多个卷积间如何分析重要性,即『敏感度分析』。
#### 敏感度定义
如图4-1所示,某个卷积网络包含K个卷积层,每个卷积层有4个`filters`,原始网络精度为90。
第一步:从『卷积1』中剪裁掉25%的filters,也就是『卷积1』中第2个Filters,然后直接在测试集上评估精度结果为85,得到左边坐标图的第二个红点。恢复模型到初始状态。
第二步:从『卷积1』中裁掉2个卷积,然后在测试集上评估精度为70,得到坐标图的第3个红点。恢复模型到初始状态。
第三步:同理得到第4个红点。把『卷积1』对应的4个红点链接成曲线,即为『卷积1』的敏感度曲线。
第四步:同理得到『卷积K』的敏感度曲线。
<div align="center">
<img src="filter_pruning/4-1.png" width="600" height="300">
</div>
<div align="center">
图1-1 敏感度计算过程示意图
</div>
如图1-2所示,为VGG-16在CIFAR10分类任务上的敏感度曲线示意图:
<div align="center">
<img src="filter_pruning/4-2.png" width="600" height="500">
</div>
<div align="center">
图1-2 VGG-16敏感度示例
</div>
考虑到不同的模型在不同的任务上的精度数值差别较大,甚至不在同一个量级,所以,PaddleSlim在计算和存储卷积层敏感度时,使用的是精度的损失比例。如图1-3所示,为PaddleSlim计算出的MobileNetV1-YOLOv3在VOC检测任务上的敏感度示意图,其中,纵轴为精度损失:
<div align="center">
<img src="filter_pruning/4-3.png" width="600" height="600">
</div>
<div align="center">
图1-3 用精度损失表示的敏感度
</div>
#### 敏感度计算
调用`pruner`对象的`sensitive`方法进行敏感度分析,在调用`sensitive`之前,我们简单对`model.evaluate`进行包装,使其符合`sensitive`接口的规范。执行如下代码,会进行敏感度计算,并将计算结果存入本地文件系统:
```python
def eval_fn():
result = model.evaluate(
val_dataset,
batch_size=128)
return result['acc_top1']
pruner.sensitive(eval_func=eval_fn, sen_file="./sen.pickle")
```
上述代码执行完毕后,敏感度信息会存放在pruner对象中,可以通过以下方式查看敏感度信息内容:
```python
print(pruner.sensitive())
```
### 1.2 根据敏感度数据对模型进行裁剪
选定模型的裁剪比例和需要跳过不裁剪的网络层,根据敏感度分析的数据对模型进行裁剪。
```python
plan = pruner.sensitive_prune(pruned_flops=0.4, skip_vars=["conv2d_26.w_0"])
```
### 2 基于几何中心(FPGM)的剪裁算法
在第1节中,我们采用了L1NormFilterPruner,在对一个卷积层进行裁剪的时候通过不同`filters``L1 Norm`来判定`filter`的重要性。然而这种方法基于两个前提:
1. 处于同一卷积网络层中的不同卷积核L1范数的标准差足够大,从而容易找到划分重要卷积核和非重要卷积核的阈值。
2. 每一个卷积层中均存在部分卷积核的L1范数足够小,使得这些卷积核可以被安全的移除而不损害模型的表现。
然而在很多情况下,网络模型的参数分布往往不满足上述两个前提:
<div align="center">
<img src="FPGM/weight_dist.png" width="600" height="235">
</div>
<div align="center">
卷积层中卷积核的参数分布
</div>
针对这一问题PaddleSlim中提供了基于卷积层中卷积核的几何中心的判定方式(Filter Pruning via Geometric Median, FPGM)。通过同一卷积层中不同卷积核的权重向量与这些卷积核所构成高维空间几何中心的距离来判断其自身在所处卷积层中的重要程度。一个卷积核的权重向量与几何中心越近,说明其所含信息与其他卷积核的信息越近似,该卷积核的可替代性也就越强,因此可以被安全的从模型中移除。
基于FPGM的裁剪算法可以与第1节中的敏感度分析算法共同使用,只需将第一节中的
```python
from paddleslim.dygraph import L1NormFilterPruner
pruner = L1NormFilterPruner(net, [1, 3, 224, 224])
```
更改为
```python
from paddleslim.dygraph import FPGMFilterPruner
pruner = FPGMFilterPruner(net, [1, 3, 224, 224])
```
### 3 自定义裁剪方法
除了FPGM以外,PaddleSlim也提供了接口给用户来实现自己的裁剪算法。详细可参见:[自定义结构化裁剪](self_defined_filter_pruning.md)
## 裁剪结果
| 模型 | 压缩方法 | Top-1/Top-5 Acc | 模型体积(MB) | GFLOPs |PaddleLite推理耗时| 下载 |
|:--:|:---:|:--:|:--:|:--:|:--:|:--:|
| MobileNetV1 | Baseline | 70.99%/89.68% | 17 | 1.11 |66.052\35.8014\19.5762|[下载链接](http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar) |
| MobileNetV1 | uniform -50% | 69.4%/88.66% (-1.59%/-1.02%) | 9 | 0.56 | 33.5636\18.6834\10.5076|[下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/MobileNetV1_uniform-50.tar) |
| MobileNetV1 | sensitive -30% | 70.4%/89.3% (-0.59%/-0.38%) | 12 | 0.74 | 46.5958\25.3098\13.6982|[下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/MobileNetV1_sensitive-30.tar) |
| MobileNetV1 | sensitive -50% | 69.8% / 88.9% (-1.19%/-0.78%) | 9 | 0.56 |37.9892\20.7882\11.3144|[下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/MobileNetV1_sensitive-50.tar) |
| MobileNetV1 | uniform+FPGM -50% | 69.56% / 89.14% (-1.43%/-0.53%) | 9 | 0.56 |33.5636\18.6834\10.5076|[下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/MobileNetV1_sensitive-50.tar) |
# Overview
PaddleSlim提供以下内置剪裁方法。
| 序号 | 剪裁方法 | 支持静态图 | 支持动态图 | 支持敏感度分析 | 支持自定义各层剪裁率|
|:----:|:------------------:|:----------:|:----------:|:--------------:|:-------------------:|
|1 |FPGMFilterPruner |是 | 是 | 是 |是 |
|2 |L1NormFilterPruner |是 | 是 | 是 |是 |
|3 |L2NormFilterPruner |否 | 是 | 是 |是 |
|4 |SlimFilterPruner |是 | 否 | 是 |是 |
|5 |OptSlimFilterPruner |是 | 否 | 是 |是 |
注:
- 支持敏感度分支:意为是否支持通过各个层的敏感度分析来确定各个卷积层的剪裁率。
- 支持自定义各层剪裁率:意为是否支持手动指定各个卷积层的剪裁率。
除了以上内置策略,PaddleSlim还支持用户自定义卷积通道剪裁策略,请参考:[自定义卷积通道剪裁教程]()
## 各类方法效果对比
| 模型 | 压缩方法 | 精度(Top-1/Top-5) | 模型体积(MB) | GFLOPs |PaddleLite推理耗时|
|:--:|:---:|:--:|:--:|:--:|:--:|
| MobileNetV1 |Baseline |70.99%/89.68% |17|1.11 |66.052\35.8014\19.5762|
| MobileNetV1 |uniform + L1NormFilterPruner |69.40%/88.66% (-1.59%/-1.02%) |9 |0.56(-50%)|33.5636\18.6834\10.5076|
| MobileNetV1 |sensitive + L1NormFilterPruner|70.4%/89.3% (-0.59%/-0.38%) |12|0.74(-30%)| 46.5958\25.3098\13.6982|
| MobileNetV1 |sensitive + L1NormFilterPruner|69.8%/88.9% (-1.19%/-0.78%) |9 |0.56(50%) |37.9892\20.7882\11.3144|
| MobileNetV1 |uniform + FPGMFilterPruner |69.56%/89.14% (-1.43%/-0.53%) |9 |0.56(-50%)|33.5636\18.6834\10.5076|
注:
- uniform: 各层剪裁率保持一样。
- sensitive: 根据各层敏感度确定每层的剪裁率。
## 策略介绍
### L1NormFilterPruner
paper: https://arxiv.org/abs/1608.08710
该策略使用`l1-norm`统计量来表示一个卷积层内各个`Filters`的重要性,`l1-norm`越大的`Filter`越重要。
使用方法如下:
#### 动态图
```
pruner =paddleslim. L1NormFilterPruner(net, [1, 3, 128, 128])
pruner.prune_vars({"conv2d_0.w_0": 0.3})
```
[API文档](../../api_cn/dygraph/pruners/l1norm_filter_pruner.html) | [完整示例](../../quick_start/dygraph/dygraph_pruning_tutorial.html)
#### 静态图
```
pruner = paddleslim.prune.Pruner(criterion='l1_norm')
pruned_program, _, _ = pruner.prune(
train_program,
fluid.global_scope(),
params=["conv2d_0.w_0"],
ratios=[0.3],
place=fluid.CPUPlace())
```
[API文档](../../api_cn/static/prune/prune_api.html) | [完整示例](../../quick_start/static/pruning_tutorial.html)
### FPGMFilterPruner
论文: https://arxiv.org/abs/1811.00250
该策略通过统计`Filters`两两之间的几何距离来评估单个卷积内的`Filters`的重要性。直觉上理解,离其它`Filters`平均距离越远的`Filter`越重要。
使用方法如下:
#### 动态图
```
pruner =paddleslim.FPGMFilterPruner(net, [1, 3, 128, 128])
pruner.prune_vars({"conv2d_0.w_0": 0.3})
```
[API文档](../../api_cn/dygraph/pruners/fpgm_filter_pruner.html) | [完整示例](../../quick_start/dygraph/dygraph_pruning_tutorial.html)
#### 静态图
```
pruner = paddleslim.prune.Pruner(criterion='geometry_median')
pruned_program, _, _ = pruner.prune(
train_program,
fluid.global_scope(),
params=["conv2d_0.w_0"],
ratios=[0.3],
place=fluid.CPUPlace())
```
[API文档](../../api_cn/static/prune/prune_api.html) | [完整示例](../../quick_start/static/pruning_tutorial.html)
### SlimFilterPruner
论文: https://arxiv.org/pdf/1708.06519.pdf
该策略根据卷积之后的`batch_norm``scales`来评估当前卷积内各个`Filters`的重要性。`scale`越大,对应的`Filter`越重要。
使用方法如下:
#### 静态图
```
pruner = paddleslim.prune.Pruner(criterion='bn_scale')
pruned_program, _, _ = pruner.prune(
train_program,
fluid.global_scope(),
params=["conv2d_0.w_0"],
ratios=[0.3],
place=fluid.CPUPlace())
```
[API文档](../../api_cn/static/prune/prune_api.html) | [完整示例](../../quick_start/static/pruning_tutorial.html)
### OptSlimFilterPruner
论文: https://arxiv.org/pdf/1708.06519.pdf
使用方法如下:
#### 静态图
```
pruner = paddleslim.prune.Pruner(criterion='bn_scale', idx_selector="optimal_threshold")
pruned_program, _, _ = pruner.prune(
train_program,
fluid.global_scope(),
params=["conv2d_0.w_0"],
ratios=[0.3],
place=fluid.CPUPlace())
```
[API文档](../../api_cn/static/prune/prune_api.html) | [完整示例](../../quick_start/static/pruning_tutorial.html)
# 图像分类模型通道剪裁-敏感度分析
# 敏感度分析
该教程以图像分类模型MobileNetV1为例,说明如何快速使用[PaddleSlim的敏感度分析接口](https://paddlepaddle.github.io/PaddleSlim/api/prune_api/#sensitivity)
该示例包含以下步骤:
......
静态图
==============
.. toctree::
:maxdepth: 1
image_classification_sensitivity_analysis_tutorial.md
../../../quick_start/dygraph/dygraph_quant_post_tutorial.md
\ No newline at end of file
动态图
==============
.. toctree::
:maxdepth: 1
quant_aware_training_tutorial.md
# 量化训练
# 量化训练详细教程
在线量化是在模型训练的过程中建模定点量化对模型的影响,通过在模型计算图中插入量化节点,在训练建模量化对模型精度的影响降低量化损失。
......
量化
==============
.. toctree::
:maxdepth: 1
overview.md
dygraph/index
static/index
# PaddleSlim模型量化方法总览
# Overview
# 图像分类INT8量化模型在CPU上的部署和预测
## 图像分类INT8量化模型在CPU上的部署和预测
PaddleSlim主要包含三种量化方法:量化训练(Quant Aware Training, QAT)、动态离线量化(Post Training Quantization Dynamic, PTQ Dynamic)、静态离线量化(Post Training Quantization Static, PTQ Static)。
- [量化训练](quant_aware_training_tutorial.md) 量化训练让模型感知量化运算对模型精度带来的影响,通过finetune训练降低量化误差。
......
# 动态离线量化
动态离线量化,将模型中特定OP的权重从FP32类型量化成INT8/16类型。
量化前需要有训练好的预测模型,可以根据需要将模型转化为INT8或INT16类型,目前只支持反量化预测方式,主要可以减小模型大小,对特定加载权重费时的模型可以起到一定加速效果。
- 权重量化成INT16类型,模型精度不受影响,模型大小为原始的1/2。
- 权重量化成INT8类型,模型精度会受到影响,模型大小为原始的1/4。
## 使用方法
- 准备预测模型:先保存好FP32的预测模型,用于量化压缩。
- 产出量化模型:使用PaddlePaddle调用动态离线量化离线量化接口,产出量化模型。
主要代码实现如下:
```python
import paddleslim
model_dir = path/to/fp32_model_params
save_model_dir = path/to/save_model_path
paddleslim.quant.quant_post_dynamic(model_dir=model_dir,
save_model_dir=save_model_dir,
weight_bits=8,
quantizable_op_type=['conv2d', 'mul'],
weight_quantize_type="channel_wise_abs_max",
generate_test_model=False)
```
静态图
==============
.. toctree::
:maxdepth: 1
quant_aware_tutorial.md
quant_post_tutorial.md
mkldnn_quant_tutorial.md
embedding_quant_tutorial.md
# Intel CPU上部署量化模型教程
# Intel CPU量化训练
在Intel Casecade Lake机器上(如:Intel(R) Xeon(R) Gold 6271),经过量化和DNNL加速,INT8模型在单线程上性能为FP32模型的3~3.7倍;在Intel SkyLake机器上(如:Intel(R) Xeon(R) Gold 6148),单线程性能为FP32模型的1.5倍,而精度仅有极小下降。图像分类量化的样例教程请参考[图像分类INT8模型在CPU优化部署和预测](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/demo/mkldnn_quant/)。自然语言处理模型的量化请参考[ERNIE INT8 模型精度与性能复现](https://github.com/PaddlePaddle/benchmark/tree/master/Inference/c%2B%2B/ernie/mkldnn)
......
../../../quick_start/static/quant_aware_tutorial.md
\ No newline at end of file
# 静态离线量化
# 低比特离线量化
## 动态模式
动态离线量化,将模型中特定OP的权重从FP32类型量化成INT8/16类型。
量化前需要有训练好的预测模型,可以根据需要将模型转化为INT8或INT16类型,目前只支持反量化预测方式,主要可以减小模型大小,对特定加载权重费时的模型可以起到一定加速效果。
- 权重量化成INT16类型,模型精度不受影响,模型大小为原始的1/2。
- 权重量化成INT8类型,模型精度会受到影响,模型大小为原始的1/4。
### 使用方法
- 准备预测模型:先保存好FP32的预测模型,用于量化压缩。
- 产出量化模型:使用PaddlePaddle调用动态离线量化离线量化接口,产出量化模型。
主要代码实现如下:
```python
import paddleslim
model_dir = path/to/fp32_model_params
save_model_dir = path/to/save_model_path
paddleslim.quant.quant_post_dynamic(model_dir=model_dir,
save_model_dir=save_model_dir,
weight_bits=8,
quantizable_op_type=['conv2d', 'mul'],
weight_quantize_type="channel_wise_abs_max",
generate_test_model=False)
```
## 静态离线量化
静态离线量化是基于采样数据,采用KL散度等方法计算量化比例因子的方法。相比量化训练,静态离线量化不需要重新训练,可以快速得到量化模型。
静态离线量化的目标是求取量化比例因子,主要有两种方法:非饱和量化方法 ( No Saturation) 和饱和量化方法 (Saturation)。非饱和量化方法计算FP32类型Tensor中绝对值的最大值`abs_max`,将其映射为127,则量化比例因子等于`abs_max/127`。饱和量化方法使用KL散度计算一个合适的阈值`T` (`0<T<mab_max`),将其映射为127,则量化比例因子等于`T/127`。一般而言,对于待量化op的权重Tensor,采用非饱和量化方法,对于待量化op的激活Tensor(包括输入和输出),采用饱和量化方法 。
## 使用方法
### 使用方法
静态离线量化的实现步骤如下:
......@@ -31,7 +61,7 @@ paddleslim.quant.quant_post(
详细代码与例程请参考:[静态离线量化](https://github.com/PaddlePaddle/PaddleSlim/tree/develop/demo/quant/quant_post)
## 实验结果
### 实验结果
| 模型 | 压缩方法 | 原模型Top-1/Top-5 Acc | 量化模型Top-1/Top-5 Acc |
| :---------------: | :--------------: | :-------------------: | :---------------------: |
......
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 图像分类网络结构搜索-快速开始\n",
"\n",
"该教程以图像分类模型MobileNetV2为例,说明如何在cifar10数据集上快速使用[网络结构搜索接口](../api/nas_api.md)。\n",
"该示例包含以下步骤:\n",
"\n",
"1. 导入依赖\n",
"2. 初始化SANAS搜索实例\n",
"3. 构建网络\n",
"4. 定义输入数据函数\n",
"5. 定义训练函数\n",
"6. 定义评估函数\n",
"7. 启动搜索实验\n",
" 7.1 获取模型结构\n",
" 7.2 构造program\n",
" 7.3 定义输入数据\n",
" 7.4 训练模型\n",
" 7.5 评估模型\n",
" 7.6 回传当前模型的得分\n",
"8. 完整示例\n",
"\n",
"\n",
"以下章节依次介绍每个步骤的内容。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. 导入依赖\n",
"请确认已正确安装Paddle,导入需要的依赖包。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import paddle\n",
"import paddle.fluid as fluid\n",
"import paddleslim as slim\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. 初始化SANAS搜索实例"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-07 08:42:37,895-INFO: range table: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [7, 5, 8, 6, 2, 5, 8, 6, 2, 5, 8, 6, 2, 5, 10, 6, 2, 5, 10, 6, 2, 5, 12, 6, 2])\n",
"2020-02-07 08:42:37,897-INFO: ControllerServer - listen on: [10.255.125.38:8339]\n",
"2020-02-07 08:42:37,899-INFO: Controller Server run...\n"
]
}
],
"source": [
"sanas = slim.nas.SANAS(configs=[('MobileNetV2Space')], server_addr=(\"\", 8339), save_checkpoint=None)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. 构建网络\n",
"根据传入的网络结构构造训练program和测试program。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def build_program(archs):\n",
" train_program = fluid.Program()\n",
" startup_program = fluid.Program()\n",
" with fluid.program_guard(train_program, startup_program):\n",
" data = fluid.data(name='data', shape=[None, 3, 32, 32], dtype='float32')\n",
" label = fluid.data(name='label', shape=[None, 1], dtype='int64')\n",
" output = archs(data)\n",
" output = fluid.layers.fc(input=output, size=10)\n",
"\n",
" softmax_out = fluid.layers.softmax(input=output, use_cudnn=False)\n",
" cost = fluid.layers.cross_entropy(input=softmax_out, label=label)\n",
" avg_cost = fluid.layers.mean(cost)\n",
" acc_top1 = fluid.layers.accuracy(input=softmax_out, label=label, k=1)\n",
" acc_top5 = fluid.layers.accuracy(input=softmax_out, label=label, k=5)\n",
" test_program = fluid.default_main_program().clone(for_test=True)\n",
"\n",
" optimizer = fluid.optimizer.Adam(learning_rate=0.1)\n",
" optimizer.minimize(avg_cost)\n",
"\n",
" place = fluid.CPUPlace()\n",
" exe = fluid.Executor(place)\n",
" exe.run(startup_program)\n",
" return exe, train_program, test_program, (data, label), avg_cost, acc_top1, acc_top5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. 定义输入数据函数\n",
"使用的数据集为cifar10,paddle框架中`paddle.dataset.cifar`包括了cifar数据集的下载和读取,代码如下:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def input_data(inputs):\n",
" train_reader = paddle.fluid.io.batch(paddle.reader.shuffle(paddle.dataset.cifar.train10(cycle=False), buf_size=1024),batch_size=256)\n",
" train_feeder = fluid.DataFeeder(inputs, fluid.CPUPlace())\n",
" eval_reader = paddle.fluid.io.batch(paddle.dataset.cifar.test10(cycle=False), batch_size=256)\n",
" eval_feeder = fluid.DataFeeder(inputs, fluid.CPUPlace())\n",
" return train_reader, train_feeder, eval_reader, eval_feeder"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. 定义训练函数\n",
"根据训练program和训练数据进行训练。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def start_train(program, data_reader, data_feeder):\n",
" outputs = [avg_cost.name, acc_top1.name, acc_top5.name]\n",
" for data in data_reader():\n",
" batch_reward = exe.run(program, feed=data_feeder.feed(data), fetch_list = outputs)\n",
" print(\"TRAIN: loss: {}, acc1: {}, acc5:{}\".format(batch_reward[0], batch_reward[1], batch_reward[2]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. 定义评估函数\n",
"根据评估program和评估数据进行评估。"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def start_eval(program, data_reader, data_feeder):\n",
" reward = []\n",
" outputs = [avg_cost.name, acc_top1.name, acc_top5.name]\n",
" for data in data_reader():\n",
" batch_reward = exe.run(program, feed=data_feeder.feed(data), fetch_list = outputs)\n",
" reward_avg = np.mean(np.array(batch_reward), axis=1)\n",
" reward.append(reward_avg)\n",
" print(\"TEST: loss: {}, acc1: {}, acc5:{}\".format(batch_reward[0], batch_reward[1], batch_reward[2]))\n",
" finally_reward = np.mean(np.array(reward), axis=0)\n",
" print(\"FINAL TEST: avg_cost: {}, acc1: {}, acc5: {}\".format(finally_reward[0], finally_reward[1], finally_reward[2]))\n",
" return finally_reward"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. 启动搜索实验\n",
"以下步骤拆解说明了如何获得当前模型结构以及获得当前模型结构之后应该有的步骤,如果想要看如何启动搜索实验的完整示例可以看步骤9。\n",
"\n",
"### 7.1 获取模型结构\n",
"调用`next_archs()`函数获取到下一个模型结构。"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-07 08:42:45,035-INFO: current tokens: [4, 4, 5, 1, 0, 4, 4, 2, 0, 4, 4, 3, 0, 4, 5, 2, 0, 4, 7, 2, 0, 4, 9, 0, 0]\n"
]
}
],
"source": [
"archs = sanas.next_archs()[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.2 构造program"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"exe, train_program, eval_program, inputs, avg_cost, acc_top1, acc_top5 = build_program(archs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.3 定义输入数据"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"train_reader, train_feeder, eval_reader, eval_feeder = input_data(inputs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.4 训练模型\n",
"据上面得到的训练program和评估数据启动训练。"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TRAIN: loss: [2.7999306], acc1: [0.1015625], acc5:[0.44140625]\n"
]
}
],
"source": [
"start_train(train_program, train_reader, train_feeder)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.5 评估模型\n",
"根据上面得到的评估program和评估数据启动评估。"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"TEST: loss: [49.99942], acc1: [0.078125], acc5:[0.46484375]\n",
"FINAL TEST: avg_cost: 49.999420166, acc1: 0.078125, acc5: 0.46484375\n"
]
}
],
"source": [
"finally_reward = start_eval(eval_program, eval_reader, eval_feeder)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7.6 回传当前模型的得分"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-07 08:44:26,774-INFO: Controller - iter: 1; best_reward: 0.078125, best tokens: [4, 4, 5, 1, 0, 4, 4, 2, 0, 4, 4, 3, 0, 4, 5, 2, 0, 4, 7, 2, 0, 4, 9, 0, 0], current_reward: 0.078125; current tokens: [4, 4, 5, 1, 0, 4, 4, 2, 0, 4, 4, 3, 0, 4, 5, 2, 0, 4, 7, 2, 0, 4, 9, 0, 0]\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sanas.reward(float(finally_reward[1]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. 完整示例\n",
"以下是一个完整的搜索实验示例,示例中使用FLOPs作为约束条件,搜索实验一共搜索3个step,表示搜索到3个满足条件的模型结构进行训练,每搜>索到一个网络结构训练7个epoch。"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-07 08:45:06,927-INFO: current tokens: [4, 4, 5, 1, 0, 4, 4, 2, 0, 4, 4, 3, 1, 4, 5, 2, 0, 4, 7, 2, 0, 4, 9, 0, 0]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"TRAIN: loss: [2.6932292], acc1: [0.08203125], acc5:[0.51953125]\n",
"TRAIN: loss: [42.387478], acc1: [0.078125], acc5:[0.47265625]\n"
]
}
],
"source": [
"for step in range(3):\n",
" archs = sanas.next_archs()[0]\n",
" exe, train_program, eval_progarm, inputs, avg_cost, acc_top1, acc_top5 = build_program(archs)\n",
" train_reader, train_feeder, eval_reader, eval_feeder = input_data(inputs)\n",
"\n",
" current_flops = slim.analysis.flops(train_program)\n",
" if current_flops > 321208544:\n",
" continue\n",
"\n",
" for epoch in range(7):\n",
" start_train(train_program, train_reader, train_feeder)\n",
"\n",
" finally_reward = start_eval(eval_program, eval_reader, eval_feeder)\n",
"\n",
" sanas.reward(float(finally_reward[1]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
{
"cells": [
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"import paddle\n",
"import paddle.fluid as fluid\n",
"from paddleslim.nas import SANAS\n",
"import numpy as np\n",
"\n",
"BATCH_SIZE=96\n",
"SERVER_ADDRESS = \"\"\n",
"PORT = 8377\n",
"SEARCH_STEPS = 300\n",
"RETAIN_EPOCH=30\n",
"MAX_PARAMS=3.77\n",
"IMAGE_SHAPE=[3, 32, 32]\n",
"AUXILIARY = True\n",
"AUXILIARY_WEIGHT= 0.4\n",
"TRAINSET_NUM = 50000\n",
"LR = 0.025\n",
"MOMENTUM = 0.9\n",
"WEIGHT_DECAY = 0.0003\n",
"DROP_PATH_PROBILITY = 0.2"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-23 12:28:09,752-INFO: range table: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14])\n",
"2020-02-23 12:28:09,754-INFO: ControllerServer - listen on: [127.0.0.1:8377]\n",
"2020-02-23 12:28:09,756-INFO: Controller Server run...\n"
]
}
],
"source": [
"config = [('DartsSpace')]\n",
"sa_nas = SANAS(config, server_addr=(SERVER_ADDRESS, PORT), search_steps=SEARCH_STEPS, is_server=True)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def count_parameters_in_MB(all_params, prefix='model'):\n",
" parameters_number = 0\n",
" for param in all_params:\n",
" if param.name.startswith(\n",
" prefix) and param.trainable and 'aux' not in param.name:\n",
" parameters_number += np.prod(param.shape)\n",
" return parameters_number / 1e6"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def create_data_loader(IMAGE_SHAPE, is_train):\n",
" image = fluid.data(\n",
" name=\"image\", shape=[None] + IMAGE_SHAPE, dtype=\"float32\")\n",
" label = fluid.data(name=\"label\", shape=[None, 1], dtype=\"int64\")\n",
" data_loader = fluid.io.DataLoader.from_generator(\n",
" feed_list=[image, label],\n",
" capacity=64,\n",
" use_double_buffer=True,\n",
" iterable=True)\n",
" drop_path_prob = ''\n",
" drop_path_mask = ''\n",
" if is_train:\n",
" drop_path_prob = fluid.data(\n",
" name=\"drop_path_prob\", shape=[BATCH_SIZE, 1], dtype=\"float32\")\n",
" drop_path_mask = fluid.data(\n",
" name=\"drop_path_mask\",\n",
" shape=[BATCH_SIZE, 20, 4, 2],\n",
" dtype=\"float32\")\n",
"\n",
" return data_loader, image, label, drop_path_prob, drop_path_mask"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def build_program(main_program, startup_program, IMAGE_SHAPE, archs, is_train):\n",
" with fluid.program_guard(main_program, startup_program):\n",
" data_loader, data, label, drop_path_prob, drop_path_mask = create_data_loader(\n",
" IMAGE_SHAPE, is_train)\n",
" logits, logits_aux = archs(data, drop_path_prob, drop_path_mask,\n",
" is_train, 10)\n",
" top1 = fluid.layers.accuracy(input=logits, label=label, k=1)\n",
" top5 = fluid.layers.accuracy(input=logits, label=label, k=5)\n",
" loss = fluid.layers.reduce_mean(\n",
" fluid.layers.softmax_with_cross_entropy(logits, label))\n",
"\n",
" if is_train:\n",
" if AUXILIARY:\n",
" loss_aux = fluid.layers.reduce_mean(\n",
" fluid.layers.softmax_with_cross_entropy(logits_aux, label))\n",
" loss = loss + AUXILIARY_WEIGHT * loss_aux\n",
" step_per_epoch = int(TRAINSET_NUM / BATCH_SIZE)\n",
" learning_rate = fluid.layers.cosine_decay(LR, step_per_epoch, RETAIN_EPOCH)\n",
" fluid.clip.set_gradient_clip(\n",
" clip=fluid.clip.GradientClipByGlobalNorm(clip_norm=5.0))\n",
" optimizer = fluid.optimizer.MomentumOptimizer(\n",
" learning_rate,\n",
" MOMENTUM,\n",
" regularization=fluid.regularizer.L2DecayRegularizer(\n",
" WEIGHT_DECAY))\n",
" optimizer.minimize(loss)\n",
" outs = [loss, top1, top5, learning_rate]\n",
" else:\n",
" outs = [loss, top1, top5]\n",
" return outs, data_loader"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"def train(main_prog, exe, epoch_id, train_loader, fetch_list):\n",
" loss = []\n",
" top1 = []\n",
" top5 = []\n",
" for step_id, data in enumerate(train_loader()):\n",
" devices_num = len(data)\n",
" if DROP_PATH_PROBILITY > 0:\n",
" feed = []\n",
" for device_id in range(devices_num):\n",
" image = data[device_id]['image']\n",
" label = data[device_id]['label']\n",
" drop_path_prob = np.array(\n",
" [[DROP_PATH_PROBILITY * epoch_id / RETAIN_EPOCH]\n",
" for i in range(BATCH_SIZE)]).astype(np.float32)\n",
" drop_path_mask = 1 - np.random.binomial(\n",
" 1, drop_path_prob[0],\n",
" size=[BATCH_SIZE, 20, 4, 2]).astype(np.float32)\n",
" feed.append({\n",
" \"image\": image,\n",
" \"label\": label,\n",
" \"drop_path_prob\": drop_path_prob,\n",
" \"drop_path_mask\": drop_path_mask\n",
" })\n",
" else:\n",
" feed = data\n",
" loss_v, top1_v, top5_v, lr = exe.run(\n",
" main_prog, feed=feed, fetch_list=[v.name for v in fetch_list])\n",
" loss.append(loss_v)\n",
" top1.append(top1_v)\n",
" top5.append(top5_v)\n",
" if step_id % 10 == 0:\n",
" print(\n",
" \"Train Epoch {}, Step {}, Lr {:.8f}, loss {:.6f}, acc_1 {:.6f}, acc_5 {:.6f}\".\n",
" format(epoch_id, step_id, lr[0], np.mean(loss), np.mean(top1), np.mean(top5)))\n",
" return np.mean(top1)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"def valid(main_prog, exe, epoch_id, valid_loader, fetch_list):\n",
" loss = []\n",
" top1 = []\n",
" top5 = []\n",
" for step_id, data in enumerate(valid_loader()):\n",
" loss_v, top1_v, top5_v = exe.run(\n",
" main_prog, feed=data, fetch_list=[v.name for v in fetch_list])\n",
" loss.append(loss_v)\n",
" top1.append(top1_v)\n",
" top5.append(top5_v)\n",
" if step_id % 10 == 0:\n",
" print(\n",
" \"Valid Epoch {}, Step {}, loss {:.6f}, acc_1 {:.6f}, acc_5 {:.6f}\".\n",
" format(epoch_id, step_id, np.mean(loss), np.mean(top1), np.mean(top5)))\n",
" return np.mean(top1)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2020-02-23 12:28:57,462-INFO: current tokens: [5, 5, 5, 5, 5, 12, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10]\n"
]
}
],
"source": [
"archs = sa_nas.next_archs()[0]"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"train_program = fluid.Program()\n",
"test_program = fluid.Program()\n",
"startup_program = fluid.Program()\n",
"train_fetch_list, train_loader = build_program(train_program, startup_program, IMAGE_SHAPE, archs, is_train=True)\n",
"test_fetch_list, test_loader = build_program(test_program, startup_program, IMAGE_SHAPE, archs, is_train=False)\n",
"test_program = test_program.clone(for_test=True)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"place = fluid.CPUPlace()\n",
"exe = fluid.Executor(place)\n",
"exe.run(startup_program)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<paddle.fluid.reader.GeneratorLoader at 0x7fddc8fe7cd0>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_reader = paddle.fluid.io.batch(paddle.reader.shuffle(paddle.dataset.cifar.train10(cycle=False), buf_size=1024), batch_size=BATCH_SIZE, drop_last=True)\n",
"test_reader = paddle.fluid.io.batch(paddle.dataset.cifar.test10(cycle=False), batch_size=BATCH_SIZE, drop_last=False)\n",
"train_loader.set_sample_list_generator(train_reader, places=place)\n",
"test_loader.set_sample_list_generator(test_reader, places=place)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train Epoch 0, Step 0, Lr 0.02500000, loss 3.310467, acc_1 0.062500, acc_5 0.468750\n"
]
}
],
"source": [
"for epoch_id in range(RETAIN_EPOCH):\n",
" train_top1 = train(train_program, exe, epoch_id, train_loader, train_fetch_list)\n",
" print(\"TRAIN: Epoch {}, train_acc {:.6f}\".format(epoch_id, train_top1))\n",
" valid_top1 = valid(test_program, exe, epoch_id, test_loader, test_fetch_list)\n",
" print(\"TEST: Epoch {}, valid_acc {:.6f}\".format(epoch_id, valid_top1))\n",
" valid_top1_list.append(valid_top1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.12"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册