WAVE SUMMIT 2019 深度学习开发者峰会上,PaddleSlim 全新发布,对于在内存紧张、功耗限制、存储有限的设备上进行深度学习应用的开发者是一份重磅惊喜。
PaddleSlim 是一个无论是新手还是经验者都可以很方便用来优化模型以进行部署的模型压缩库:在普通的模型训练上,只需要两行 python 代码,构造一个 Compressor 对象,即可调用。PaddleSlim 实现了目前主流的网络量化、剪枝、蒸馏三种压缩策略,并可快速配置多种压缩策略组合使用。针对体积已经很小的 MobileNet 模型,在模型效果不损失的前提下实现 70% 以上的体积压缩。
项目地址:https://github.com/PaddlePaddle/models/tree/v1.4/PaddleSlim
深度学习技术已经在互联网的诸多方向产生影响,关于深度学习和神经网络的讨论越来越多。深度学习技术近几年在计算机视觉、语音识别、自然语言处理等领域大放异彩,各种互联网产品都争相应用深度学习技术,我们的生活中也越来越多的 AI 时代新能力,例如人脸识别、智能翻译、语音助手等。
根据《2019 年移动市场报告》,2018 年,用户支出已经突破了 1010 亿美元。用户越来越习惯于在手机上完成各种事项,平均每天在移动设备上花费的时间已经达到 3 小时。随着移动设备被广泛使用,在移动互联网产品应用深度学习和神经网络技术已经成为必然趋势。而移动端设备通常内存少、运算能力也比较弱小,并且移动端的 CPU 需要将功耗指标维持在很低的水平,当前主流的模型很难直接部署到移动设备中。在这种情况下,PaddleSlim 应运而生,实现了目前主流的网络量化、剪枝、蒸馏三种压缩策略,并可快速配置多种压缩策略组合使用,在多种压缩策略上达到了业绩领先的效果。
PaddleSlim架构图
如图所示,PaddleSlim 从上到下为 API 依赖关系。蒸馏、量化和剪切模块都依赖底层的基础框架。最上层为用户接口,在 Python 脚本中调用模型压缩功能时,只需要构造一个 Compressor 对象即可。我们将每个压缩算法称为压缩策略,在迭代训练模型的过程中调用用户注册的压缩策略完成模型压缩。模型压缩工具封装好了模型训练逻辑,用户只需要提供训练模型需要的网络结构、数据、优化策略(optimizer)等。
PaddleSlim 特点
接口简单
- 以配置文件方式集中管理可配参数,方便实验管理
- 在普通模型训练脚本上,添加极少代码即可完成模型压缩
效果好
- 对于冗余信息较少的 MobileNetv1 模型,卷积核剪切策略依然可缩减模型大小,并保持尽量少的精度损失。
- 蒸馏压缩策略可明显提升原始模型的精度。
- 量化训练与蒸馏的组合使用,可同时做到缩减模型大小和提升模型精度。
功能更强更灵活
- 剪切压缩过程自动化
- 剪切压缩策略支持更多网络结构
- 蒸馏支持多种方式,用户可自定义组合 loss
- 支持快速配置多种压缩策略组合使用
PaddleSlim 蒸馏、剪切、量化简要介绍
蒸馏
模型蒸馏是将复杂网络中的有用信息提取出来,迁移到一个更小的网络中去,PaddleSlim 提供传统的蒸馏方法和基于 FSP 的蒸馏方法。用户可以在配置文件中,用 FSP_loss, L2_loss 和 softmax_with_cross_entropy_loss
这三种 loss 组合 teacher net 和 student net 的任意一层。
卷积核剪切
该策略通过减少指定卷积层中卷积核的数量,达到缩减模型大小和计算复杂度的目的。根据选取剪切比例的策略的不同,PaddleSlim 提供以下两个方式:
- uniform pruning: 每层剪切一样比例的卷积核。在剪切一个卷积核之前,按 l1_norm 对 filter 从高到低排序,越靠后的 filter 越不重要,优先剪掉靠后的 filter.
- sensitive pruning: 根据每层敏感度,剪切掉不同比例的卷积核数量。
两种剪切方式都需要加载预训练模型。
量化
PaddleSlim 为开发者提供在训练过程中对量化进行建模以确定量化参数的 Quantization Aware Training 量化模式,提供更高的预测精度。现阶段的量化训练主要针对卷积层(包括二维卷积和 Depthwise 卷积)以及全连接层进行量化。卷积层和全连接层在 PaddlePaddle 框架中对应算子包括 conv2d、depthwise_conv2d 和 mul 等。量化训练会对所有的 conv2d、depthwise_conv2d 和 mul 进行量化操作,且要求它们的输入中必须包括激活和参数两部分。
PaddleSlim 使用示例
该示例参考 PaddlePaddle/models/fluid/PaddleCV/image_classification 下代码,分别实现了以下策略:
- 蒸馏:用 ResNet50 对 MobileNetv1 的在 ImageNet 1000 数据上的蒸馏训练。
- 剪切:对预训练好的 MobileNetv1 进行剪切
- 量化:对预训练好的 MobileNetv1 进行 int8 量化训练
- 蒸馏量化组合:先用 ResNet50 对 MobileNetv1 进行蒸馏,再对蒸馏后得到的模型进行 int8 量化训练。
- 剪切量化组合:先用 Uniform 剪切策略对 MobileNetv1 进行剪切,再对剪切后的模型进行 int8 量化训练
本示例完整代码链接:https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleSlim
使用方式:克隆 PaddlePaddle/models 到本地,并进入 models/fluid/PaddleSlim 路径。
文件结构
本示例中的五个压缩策略使用相同的训练数据和压缩 Python 脚本 compress.py,每种策略对应独立的配置文件。
1. 数据准备
1.1 训练数据准备
参考 models/fluid/PaddleCV/image_classification 下的数据准备教程准备训练数据,并放入 PaddleSlim/data 路径下。
1.2 预训练模型准备
脚本 run.sh 会自动从 models/fluid/PaddleCV/image_classification 下载 ResNet50 和 MobileNetv1 的预训练模型,并放入 PaddleSlim/pretrain 路径下。
2. 压缩脚本介绍
在 compress.py 中定义了执行压缩任务需要的所有模型相关的信息,这里对几个关键的步骤进行简要介绍:
2.1 目标网络的定义
compress.py 的以下代码片段定义了 train program, 这里 train program 只有前向计算操作。
然后,通过 clone 方法得到 eval_program, 用来在压缩过程中评估模型精度,如下:
定义完目标网络结构,需要对其初始化,并根据需要加载预训练模型。
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_fetch_list 里必须有 loss 这一项。
对于 eval program. 同上定义 eval_feed_list 和 train_fetch_list:
2.3 定义 teacher 网络
以下代码片段定义了 teacher 网络,并对其进行了初始化操作。
需要注意的是:
- 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, 执行以下命令,执行蒸馏压缩示例:
该示例在评估数据集上的准确率结果如下:
3.2 Uniform 剪切
在该示例中,将 MobileNetv1 模型剪掉 50% 的 FLOPS. 修改 run.sh, 执行以下命令,执行 Uniform 卷积核剪切模型压缩示例:
该示例在评估数据集上的准确率结果如下:
3.3 敏感度剪切
在该示例中,将 MobileNetv1 模型剪掉 50% 的 FLOPS. 修改 run.sh, 执行以下命令,执行敏感度卷积核剪切压缩示例:
该示例在评估数据集上的准确率结果如下:
3.4 int8 量化训练
修改 run.sh, 执行以下命令,执行 int8 量化训练示例:
该示例结果如下:
3.5 蒸馏后 int8 量化
本示例先用 ResNet50 模型对 MobileNetv1 蒸馏训练 120 个 epochs,然后再对 MobileNetv1 模型进行动态 int8 量化训练。修改 run.sh, 执行以下命令,执行蒸馏与 int8 量化训练结合的模型压缩示例:
该示例结果如下:
3.6 剪切后 int8 量化
本示例先将预训练好的 MobileNetv1 模型剪掉 50% FLOPS, 让后再对其进行动态 int8 量化训练。修改 run.sh, 执行以下命令,执行剪切与 int8 量化训练结合的模型压缩示例:
该示例结果如下: