未验证 提交 e15857e4 编写于 作者: littletomatodonkey's avatar littletomatodonkey 提交者: GitHub

add distillation (#1332)

* add distillation

* add advanced tut of distillation

* add contribute

* add code overview

* add quick_start_recognition

* fix eng

* fix some typo

* add slim
上级 63357d4e
# PaddleClas代码解析
## 1. 整体代码结构解析
### 1.1 整体代码和目录概览
PaddleClas主要代码和目录结构如下
* benchmark: 文件夹下存放了一些shell脚本,主要是为了测试PaddleClas中不同模型的速度指标,如单卡训练速度指标、多卡训练速度指标等。
* dataset:文件夹下存放数据集和用于处理数据集的脚本。脚本负责将数据集处理为适合Dataloader处理的格式。
* deploy:部署核心代码,文件夹存放的是部署工具,支持 python/cpp inference、Hub Serveing、Paddle Lite、Slim离线量化等多种部署方式。
* ppcls:训练核心代码,文件夹下存放PaddleClas框架主体。配置文件、模型训练、评估、预测、动转静导出等具体代码实现均在这里。
* tools:训练、评估、预测、模型动转静导出的入口函数和脚本均在该文件下。
* requirements.txt 文件用于安装 PaddleClas 的依赖项。使用pip进行升级安装使用。
* tests:PaddleClas模型从训练到预测的全链路测试,验证各功能是否能够正常使用。
### 1.2 训练模块定义
深度学习模型训练过程中,主要包含以下几个核心模块。
* 数据:对于有监督任务来说,训练数据一般包含原始数据及其标注。在基于单标签的图像分类任务中,原始数据指的是图像数据,而标注则是该图像数据所属的类比。PaddleClas中,训练时需要提供标签文件,形式如下,每一行包含一条训练样本,分别表示图片路径和类别标签,用分隔符隔开(默认为空格)。
```
train/n01440764/n01440764_10026.JPEG 0
train/n01440764/n01440764_10027.JPEG 0
```
在代码`ppcls/data/dataloader/common_dataset.py`中,包含`CommonDataset`类,继承自`paddle.io.Dataset`,该数据集类可以通过一个键值进行索引并获取指定样本。`ImageNetDataset`, `LogoDataset`, `CommonDataset` 等数据集类都对这个类别
对于读入的数据,需要通过数据转换,将原始的图像数据进行转换。训练时,标准的数据预处理包含:`DecodeImage`, `RandCropImage`, `RandFlipImage`, `NormalizeImage`, `ToCHWImage`。在配置文件中体现如下,数据预处理主要包含在`transforms`字段中,以列表形式呈现,会按照顺序对数据依次做这些转换。
```yaml
DataLoader:
Train:
dataset:
name: ImageNetDataset
image_root: ./dataset/ILSVRC2012/
cls_label_path: ./dataset/ILSVRC2012/train_list.txt
transform_ops:
- DecodeImage:
to_rgb: True
channel_first: False
- RandCropImage:
size: 224
- RandFlipImage:
flip_code: 1
- NormalizeImage:
scale: 1.0/255.0
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
order: ''
```
PaddleClas 中也包含了 `AutoAugment`, `RandAugment` 等数据增广方法,也可以通过在配置文件中配置,从而添加到训练过程的数据预处理中。每个数据转换的方法均以类实现,方便迁移和复用,更多的数据处理具体实现过程可以参考 `ppcls/data/preprocess/ops/` 下的代码。
对于组成一个 batch 的数据,也可以使用 mixup 或者 cutmix 等方法进行数据增广。 PaddleClas 中集成了 `MixupOperator`, `CutmixOperator`, `FmixOperator` 等基于 batch 的数据增广方法,可以在配置文件中配置 mix 参数进行配置,更加具体的实现可以参考 `ppcls/data/preprocess/batch_ops/batch_operators.py`
图像分类中,数据后处理主要为 `argmax` 操作,在此不再赘述。
* 模型结构
在配置文件中,模型结构定义如下
```yaml
Arch:
name: ResNet50
class_num: 1000
pretrained: False
use_ssld: False
```
`Arch.name` 表示模型名称, `Arch.pretrained` 表示是否添加预训练模型,`use_ssld` 表示是否使用基于 `SSLD` 知识蒸馏得到的预训练模型。所有的模型名称均在 `ppcls/arch/backbone/__init__.py` 中定义。
对应的,在 `ppcls/arch/__init__.py` 中,通过 `build_model` 方法创建模型对象。
```python
def build_model(config):
config = copy.deepcopy(config)
model_type = config.pop("name")
mod = importlib.import_module(__name__)
arch = getattr(mod, model_type)(**config)
return arch
```
* 损失函数
PaddleClas中,包含了 `CELoss` , `JSDivLoss`, `TripletLoss`, `CenterLoss` 等损失函数,均定义在 `ppcls/loss` 中。
`ppcls/loss/__init__.py` 文件中,使用 `CombinedLoss` 来构建及合并损失函数,不同训练策略中所需要的损失函数与计算方法不同,PaddleClas 在构建损失函数过程中,主要考虑了以下几个因素。
1. 是否使用 label smooth
2. 是否使用 mixup 或者 cutmix
3. 是否使用蒸馏方法进行训练
4. 是否是训练 metric learning
用户可以在配置文件中指定损失函数的类型及权重,如在训练中添加 TripletLossV2 ,配置文件如下:
```yaml
Loss:
Train:
- CELoss:
weight: 1.0
- TripletLossV2:
weight: 1.0
margin: 0.5
```
* 优化器和学习率衰减、权重衰减策略
图像分类任务中,`Momentum` 是一种比较常用的优化器, PaddleClas 中提供了 `Momentum``RMSProp``Adam``AdamW`等几种优化器策略。
权重衰减策略是一种比较常用的正则化方法,主要用于防止模型过拟合。 PaddleClas 中提供了 `L1Decay``L2Decay` 两种权重衰减策略。
学习率衰减是图像分类任务中必不可少的精度提升训练方法, PaddleClas 目前支持 `Cosine` , `Piecewise`, `Linear` 等学习率衰减策略。
在配置文件中,优化器、权重衰减策略、学习率衰减策略可以通过以下的字段进行配置。
```yaml
Optimizer:
name: Momentum
momentum: 0.9
lr:
name: Piecewise
learning_rate: 0.1
decay_epochs: [30, 60, 90]
values: [0.1, 0.01, 0.001, 0.0001]
regularizer:
name: 'L2'
coeff: 0.0001
```
`ppcls/optimizer/__init__.py` 中使用 `build_optimizer` 创建优化器和学习率对象。
```python
def build_optimizer(config, epochs, step_each_epoch, parameters):
config = copy.deepcopy(config)
# step1 build lr
lr = build_lr_scheduler(config.pop('lr'), epochs, step_each_epoch)
logger.debug("build lr ({}) success..".format(lr))
# step2 build regularization
if 'regularizer' in config and config['regularizer'] is not None:
reg_config = config.pop('regularizer')
reg_name = reg_config.pop('name') + 'Decay'
reg = getattr(paddle.regularizer, reg_name)(**reg_config)
else:
reg = None
logger.debug("build regularizer ({}) success..".format(reg))
# step3 build optimizer
optim_name = config.pop('name')
if 'clip_norm' in config:
clip_norm = config.pop('clip_norm')
grad_clip = paddle.nn.ClipGradByNorm(clip_norm=clip_norm)
else:
grad_clip = None
optim = getattr(optimizer, optim_name)(learning_rate=lr,
weight_decay=reg,
grad_clip=grad_clip,
**config)(parameters=parameters)
logger.debug("build optimizer ({}) success..".format(optim))
return optim, lr
```
不同优化器和权重衰减策略均以类的形式实现,具体实现可以参考文件 `ppcls/optimizer/optimizer.py` ;不同的学习率衰减策略可以参考文件 `ppcls/optimizer/learning_rate.py`
* 训练时评估与模型存储
模型在训练的时候,可以设置模型保存的间隔,也可以选择每隔若干个epoch对验证集进行评估,从而可以保存在验证集上精度最佳的模型。配置文件中,可以通过下面的字段进行配置。
```yaml
Global:
save_interval: 1 # 模型保存的epoch间隔
eval_during_train: True # 是否进行训练时评估
eval_interval: 1 # 评估的epoch间隔
```
模型存储是通过 Paddle 框架的 `paddle.save()` 函数实现的,存储的是模型的动态图版本,以字典的形式存储,便于继续训练。具体实现如下
```python
def save_model(program, model_path, epoch_id, prefix='ppcls'):
model_path = os.path.join(model_path, str(epoch_id))
_mkdir_if_not_exist(model_path)
model_prefix = os.path.join(model_path, prefix)
paddle.static.save(program, model_prefix)
logger.info(
logger.coloring("Already save model in {}".format(model_path), "HEADER"))
```
在保存的时候有两点需要注意:
1. 只在 0 号节点上保存模型。否则多卡训练的时候,如果所有节点都保存模型到相同的路径,则多个节点写文件时可能会发生写文件冲突,导致最终保存的模型无法被正确加载。
2. 优化器参数也需要存储,方便后续的加载断点进行训练。
* 模型裁剪、量化训练
如果想对模型进行压缩训练,则通过下面字段进行配置
模型裁剪:
```yaml
Slim:
prune:
name: fpgm
pruned_ratio: 0.3
```
模型量化:
```yaml
Slim:
quant:
name: pact
```
训练方法详见模型[裁剪量化使用介绍](../advanced_tutorials/model_prune_quantization.md), 算法介绍详见[裁剪量化算法介绍](../algorithm_introduction/model_prune_quantization.md)
### 1.3 预测部署代码和方式
* 如果希望在服务端使用 cpp 进行部署,可以参考 [cpp inference 预测教程](../../../deploy/cpp_infer/readme.md)
* 如果希望将分类模型部署为服务,可以参考 [hub serving 预测部署教程](../../../deploy/hubserving/readme.md)
* 如果希望将对分类模型进行离线量化,可以参考 [模型量化裁剪教程](../advanced_tutorials/model_prune_quantization.md) 中离线量化部分。
* 如果希望在移动端使用分类模型进行预测,可以参考 [PaddleLite 预测部署教程](../../../deploy/lite/readme.md)
# PaddleClas 社区贡献指南
### 1.1 PaddleClas 分支说明
PaddleClas 未来将维护 2 种分支,分别为:
* release/x.x 系列分支:为稳定的发行版本分支,会适时打 tag 发布版本,适配 Paddle 的 release 版本。当前最新的分支为 release/2.3 分支,是当前默认分支,适配 Paddle v2.1.0 。随着版本迭代, release/x.x 系列分支会越来越多,默认维护最新版本的 release 分支,前 1 个版本分支会修复 bug ,其他的分支不再维护。
* develop 分支:为开发分支,适配 Paddle 的 develop 版本,主要用于开发新功能。如果有同学需要进行二次开发,请选择 develop 分支。为了保证 develop 分支能在需要的时候拉出 release/x.x 分支, develop 分支的代码只能使用 Paddle 最新 release 分支中有效的 api 。也就是说,如果 Paddle develop 分支中开发了新的 api,但尚未出现在 release 分支代码中,那么请不要在 PaddleClas 中使用。除此之外,对于不涉及 api 的性能优化、参数调整、策略更新等,都可以正常进行开发。
PaddleClas 的历史分支,未来将不再维护。考虑到一些同学可能仍在使用,这些分支还会继续保留:
* release/static 分支:这个分支曾用于静态图的开发与测试,目前兼容 >=1.7 版本的 Paddle 。如果有特殊需求,要适配旧版本的 Paddle ,那还可以使用这个分支,但除了修复 bug 外不再更新代码。
* dygraph-dev 分支:这个分支将不再维护,也不再接受新的代码,请使用的同学尽快迁移到 develop 分支。
PaddleClas 欢迎大家向 repo 中积极贡献代码,下面给出一些贡献代码的基本流程。
### 1.2 PaddleClas 代码提交流程与规范
#### 1.2.1 fork 和 clone 代码
* 跳转到 [PaddleClas GitHub首页](https://github.com/PaddlePaddle/PaddleClas) ,然后单击 Fork 按钮,生成自己目录下的仓库,比如 `https://github.com/USERNAME/PaddleClas`
<div align="center">
<img src="../../images/quick_start/community/001_fork.png" width = "600" />
</div>
* 将远程仓库 clone 到本地
```shell
# 拉取develop分支的代码
git clone https://github.com/USERNAME/PaddleClas.git -b develop
cd PaddleClas
```
clone 的地址可以从下面获取
<div align="center">
<img src="../../images/quick_start/community/002_clone.png" width = "600" />
</div>
#### 1.2.2 和远程仓库建立连接
首先通过 `git remote -v` 查看当前远程仓库的信息。
```
origin https://github.com/USERNAME/PaddleClas.git (fetch)
origin https://github.com/USERNAME/PaddleClas.git (push)
```
上面的信息只包含了 clone 的远程仓库的信息,也就是自己用户名下的 PaddleClas ,接下来我们创建一个原始 PaddleClas 仓库的远程主机,命名为 upstream 。
```shell
git remote add upstream https://github.com/PaddlePaddle/PaddleClas.git
```
使用 `git remote -v` 查看当前远程仓库的信息,输出如下,发现包括了 origin 和 upstream 2 个远程仓库。
```
origin https://github.com/USERNAME/PaddleClas.git (fetch)
origin https://github.com/USERNAME/PaddleClas.git (push)
upstream https://github.com/PaddlePaddle/PaddleClas.git (fetch)
upstream https://github.com/PaddlePaddle/PaddleClas.git (push)
```
这主要是为了后续在提交 pull request (PR) 时,始终保持本地仓库最新。
#### 1.2.3 创建本地分支
可以基于当前分支创建新的本地分支,命令如下。
```shell
git checkout -b new_branch
```
也可以基于远程或者上游的分支创建新的分支,命令如下。
```shell
# 基于用户远程仓库(origin)的develop创建new_branch分支
git checkout -b new_branch origin/develop
# 基于上游远程仓库(upstream)的develop创建new_branch分支
# 如果需要从upstream创建新的分支,需要首先使用git fetch upstream获取上游代码
git checkout -b new_branch upstream/develop
```
最终会显示切换到新的分支,输出信息如下
```
Branch new_branch set up to track remote branch develop from upstream.
Switched to a new branch 'new_branch'
```
#### 1.2.4 使用 pre-commit 勾子
Paddle 开发人员使用 pre-commit 工具来管理 Git 预提交钩子。 它可以帮助我们格式化源代码(C++,Python),在提交(commit)前自动检查一些基本事宜(如每个文件只有一个 EOL,Git 中不要添加大文件等)。
pre-commit 测试是 Travis-CI 中单元测试的一部分,不满足钩子的 PR 不能被提交到 PaddleClas ,首先安装并在当前目录运行它:
```shell
pip install pre-commit
pre-commit install
```
* **注意**
1. Paddle 使用 clang-format 来调整 C/C++ 源代码格式,请确保 `clang-format` 版本在 3.8 以上。
2. 通过 `pip install pre-commit``conda install -c conda-forge pre-commit` 安装的 `yapf` 稍有不同的,PaddleClas 开发人员使用的是 `pip install pre-commit`
#### 1.2.5 修改与提交代码
可以通过 `git status` 查看改动的文件。
对 PaddleClas 的 `README.md` 做了一些修改,希望提交上去。则可以通过以下步骤
```shell
git add README.md
pre-commit
```
重复上述步骤,直到 pre-comit 格式检查不报错。如下所示。
<div align="center">
<img src="../../images/quick_start/community/003_precommit_pass.png" width = "600" />
</div>
使用下面的命令完成提交。
```shell
git commit -m "your commit info"
```
#### 1.2.6 保持本地仓库最新
获取 upstream 的最新代码并更新当前分支。这里的 upstream 来自于 1.2 节的`和远程仓库建立连接`部分。
```shell
git fetch upstream
# 如果是希望提交到其他分支,则需要从upstream的其他分支pull代码,这里是develop
git pull upstream develop
```
#### 1.2.7 push到远程仓库
```shell
git push origin new_branch
```
#### 1.2.8 提交Pull Request
点击 new pull request,选择本地分支和目标分支,如下图所示。在 PR 的描述说明中,填写该 PR 所完成的功能。接下来等待 review ,如果有需要修改的地方,参照上述步骤更新 origin 中的对应分支即可。
<div align="center">
<img src="../../images/quick_start/community/004_create_pr.png" width = "600" />
</div>
#### 1.2.9 签署 CLA 协议和通过单元测试
* 签署 CLA
在首次向 PaddlePaddle 提交 Pull Request 时,您需要您签署一次 CLA (Contributor License Agreement) 协议,以保证您的代码可以被合入,具体签署方式如下:
1. 请您查看 PR 中的 Check 部分,找到 license/cla ,并点击右侧 detail ,进入 CLA 网站
2. 点击 CLA 网站中的 `Sign in with GitHub to agree` , 点击完成后将会跳转回您的 Pull Request 页面
#### 1.2.10 删除分支
* 删除远程分支
在 PR 被 merge 进主仓库后,我们可以在 PR 的页面删除远程仓库的分支。
也可以使用 `git push origin :分支名` 删除远程分支,如:
```shell
git push origin :new_branch
```
* 删除本地分支
```shell
# 切换到develop分支,否则无法删除当前分支
git checkout develop
# 删除new_branch分支
git branch -D new_branch
```
#### 1.2.11 提交代码的一些约定
为了使官方维护人员在评审代码时更好地专注于代码本身,请您每次提交代码时,遵守以下约定:
1)请保证 Travis-CI 中单元测试能顺利通过。如果没过,说明提交的代码存在问题,官方维护人员一般不做评审。
2)提交 Pull Request前:
请注意 commit 的数量。
原因:如果仅仅修改一个文件但提交了十几个 commit ,每个 commit 只做了少量的修改,这会给评审人带来很大困扰。评审人需要逐一查看每个 commit 才能知道做了哪些修改,且不排除 commit 之间的修改存在相互覆盖的情况。
建议:每次提交时,保持尽量少的 commit ,可以通过 `git commit --amend` 补充上次的 commit 。对已经 Push 到远程仓库的多个 commit ,可以参考 [squash commits after push](https://stackoverflow.com/questions/5667884/how-to-squash-commits-in-git-after-they-have-been-pushed)
请注意每个 commit 的名称:应能反映当前 commit 的内容,不能太随意。
3)如果解决了某个 Issue 的问题,请在该 Pull Request 的第一个评论框中加上: `fix #issue_number` ,这样当该 Pull Request 被合并后,会自动关闭对应的 Issue 。关键词包括: close, closes, closed, fix, fixes, fixed, resolve, resolves, resolved ,请选择合适的词汇。详细可参考 [Closing issues via commit messages](https://help.github.com/articles/closing-issues-via-commit-messages)
此外,在回复评审人意见时,请您遵守以下约定:
1)官方维护人员的每一个 review 意见都希望得到回复,这样会更好地提升开源社区的贡献。
- 对评审意见同意且按其修改完的,给个简单的 Done 即可;
- 对评审意见不同意的,请给出您自己的反驳理由。
2)如果评审意见比较多,
- 请给出总体的修改情况。
- 请采用 `start a review` 进行回复,而非直接回复的方式。原因是每个回复都会发送一封邮件,会造成邮件灾难。
## 2. 总结
* 开源社区依赖于众多开发者与用户的贡献和反馈,在这里感谢与期待大家向 PaddleClas 提出宝贵的意见与 Pull Request ,希望我们可以一起打造一个领先实用全面的图像识别代码仓库!
## 3. 参考文献
1. [PaddlePaddle本地开发指南](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/08_contribution/index_cn.html)
2. [向开源框架提交pr的过程](https://blog.csdn.net/vim_wj/article/details/78300239)
# 知识蒸馏
## 一、模型压缩与知识蒸馏方法简介
近年来,深度神经网络在计算机视觉、自然语言处理等领域被验证是一种极其有效的解决问题的方法。通过构建合适的神经网络,加以训练,最终网络模型的性能指标基本上都会超过传统算法。
在数据量足够大的情况下,通过合理构建网络模型的方式增加其参数量,可以显著改善模型性能,但是这又带来了模型复杂度急剧提升的问题。大模型在实际场景中使用的成本较高。
深度神经网络一般有较多的参数冗余,目前有几种主要的方法对模型进行压缩,减小其参数量。如裁剪、量化、知识蒸馏等,其中知识蒸馏是指使用教师模型 (teacher model) 去指导学生模型 (student model) 学习特定任务,保证小模型在参数量不变的情况下,得到比较大的性能提升,甚至获得与大模型相似的精度指标 [1]。 PaddleClas 融合已有的蒸馏方法 [2,3] ,提供了一种简单的半监督标签知识蒸馏方案 (SSLD,Simple Semi-supervised Label Distillation) ,基于 ImageNet1k 分类数据集,在 ResNet_vd 以及 MobileNet 系列上的精度均有超过 3% 的绝对精度提升,具体指标如下图所示。
<div align="center">
<img src="../../images/distillation/distillation_perform_s.jpg" width = "600" />
</div>
## 二、SSLD 蒸馏策略
### 2.1 简介
SSLD 的流程图如下图所示。
<div align="center">
<img src="../../images/distillation/ppcls_distillation.png" width = "600" />
</div>
首先,我们从 ImageNet22k 中挖掘出了近400万张图片,同时与 ImageNet-1k 训练集整合在一起,得到了一个新的包含 500 万张图片的数据集。然后,我们将学生模型与教师模型组合成一个新的网络,该网络分别输出学生模型和教师模型的预测分布,与此同时,固定教师模型整个网络的梯度,而学生模型可以做正常的反向传播。最后,我们将两个模型的 logits 经过 softmax 激活函数转换为 soft label ,并将二者的 soft label 做 JS 散度作为损失函数,用于蒸馏模型训练。下面以 MobileNetV3 (该模型直接训练,精度为 75.3%) 的知识蒸馏为例,介绍该方案的核心关键点( baseline 为 79.12% 的 ResNet50_vd 模型蒸馏 MobileNetV3 ,训练集为 ImageNet1k 训练集, loss 为 cross entropy loss ,迭代轮数为 120epoch ,精度指标为 75.6% )。
* 教师模型的选择。在进行知识蒸馏时,如果教师模型与学生模型的结构差异太大,蒸馏得到的结果反而不会有太大收益。相同结构下,精度更高的教师模型对结果也有很大影响。相比于 79.12% 的 ResNet50_vd 教师模型,使用 82.4% 的 ResNet50_vd 教师模型可以带来 0.4% 的绝对精度收益( `75.6%->76.0%` )。
* 改进 loss 计算方法。分类 loss 计算最常用的方法就是 cross entropy loss ,我们经过实验发现,在使用 soft label 进行训练时,相对于 cross entropy loss , KL div loss 对模型性能提升几乎无帮助,但是使用具有对称特性的 JS div loss 时,在多个蒸馏任务上相比 cross entropy loss 均有 0.2% 左右的收益( `76.0%->76.2%` ), SSLD 中也基于 JS div loss 展开实验。
* 更多的迭代轮数。蒸馏的 baseline 实验只迭代了 120 个 epoch 。实验发现,迭代轮数越多,蒸馏效果越好,最终我们迭代了 360epoch ,精度指标可以达到 77.1%(`76.2%->77.1%`) 。
* 无需数据集的真值标签,很容易扩展训练集。 SSLD 的 loss 在计算过程中,仅涉及到教师和学生模型对于相同图片的处理结果(经过 softmax 激活函数处理之后的 soft label ),因此即使图片数据不包含真值标签,也可以用来进行训练并提升模型性能。该蒸馏方案的无标签蒸馏策略也大大提升了学生模型的性能上限( `77.1%->78.5%` )。
* ImageNet1k 蒸馏 finetune 。 我们仅使用 ImageNet1k 数据,使用蒸馏方法对上述模型进行 finetune ,最终仍然可以获得 0.4% 的性能提升( `78.5%->78.9%` )。
### 2.2 数据选择
* SSLD 蒸馏方案的一大特色就是无需使用图像的真值标签,因此可以任意扩展数据集的大小,考虑到计算资源的限制,我们在这里仅基于 ImageNet22k 数据集对蒸馏任务的训练集进行扩充。在 SSLD 蒸馏任务中,我们使用了 `Top-k per class` 的数据采样方案 [3] 。具体步骤如下。
* 训练集去重。我们首先基于 SIFT 特征相似度匹配的方式对 ImageNet22k 数据集与 ImageNet1k 验证集进行去重,防止添加的 ImageNet22k 训练集中包含 ImageNet1k 验证集图像,最终去除了 4511 张相似图片。部分过滤的相似图片如下所示。
<div align="center">
<img src="../../images/distillation/22k_1k_val_compare_w_sift.png" width = "600" />
</div>
* 大数据集 soft label 获取,对于去重后的 ImageNet22k 数据集,我们使用 `ResNeXt101_32x16d_wsl` 模型进行预测,得到每张图片的 soft label 。
* Top-k 数据选择, ImageNet1k 数据共有 1000 类,对于每一类,找出属于该类并且得分最高的 `k` 张图片,最终得到一个数据量不超过`1000*k`的数据集(某些类上得到的图片数量可能少于 `k` 张)。
* 将该数据集与 ImageNet1k 的训练集融合组成最终蒸馏模型所使用的数据集,数据量为 500 万。
## 三、实验
* PaddleClas 的蒸馏策略为`大数据集训练 + ImageNet1k 蒸馏 finetune`的策略。选择合适的教师模型,首先在挑选得到的 500 万数据集上进行训练,然后在 ImageNet1k 训练集上进行 finetune,最终得到蒸馏后的学生模型。
### 3.1 教师模型的选择
为了验证教师模型和学生模型的模型大小差异和教师模型的模型精度对蒸馏结果的影响,我们做了几组实验验证。训练策略统一为:`cosine_decay_warmup,lr=1.3, epoch=120, bs=2048`,学生模型均为从头训练。
|Teacher Model | Teacher Top1 | Student Model | Student Top1|
|- |:-: |:-: | :-: |
| ResNeXt101_32x16d_wsl | 84.2% | MobileNetV3_large_x1_0 | 75.78% |
| ResNet50_vd | 79.12% | MobileNetV3_large_x1_0 | 75.60% |
| ResNet50_vd | 82.35% | MobileNetV3_large_x1_0 | 76.00% |
从表中可以看出
> 教师模型结构相同时,其精度越高,最终的蒸馏效果也会更好一些。
>
> 教师模型与学生模型的模型大小差异不宜过大,否则反而会影响蒸馏结果的精度。
因此最终在蒸馏实验中,对于ResNet系列学生模型,我们使用 `ResNeXt101_32x16d_wsl` 作为教师模型;对于 MobileNet 系列学生模型,我们使用蒸馏得到的 `ResNet50_vd` 作为教师模型。
### 3.2 大数据蒸馏
基于 PaddleClas 的蒸馏策略为`大数据集训练 + imagenet1k finetune` 的策略。
针对从 ImageNet22k 挑选出的 400 万数据,融合 imagenet1k 训练集,组成共 500 万的训练集进行训练,具体地,在不同模型上的训练超参及效果如下。
|Student Model | num_epoch | l2_ecay | batch size/gpu cards | base lr | learning rate decay | top1 acc |
| - |:-: |:-: | :-: |:-: |:-: |:-: |
| MobileNetV1 | 360 | 3e-5 | 4096/8 | 1.6 | cosine_decay_warmup | 77.65% |
| MobileNetV2 | 360 | 1e-5 | 3072/8 | 0.54 | cosine_decay_warmup | 76.34% |
| MobileNetV3_large_x1_0 | 360 | 1e-5 | 5760/24 | 3.65625 | cosine_decay_warmup | 78.54% |
| MobileNetV3_small_x1_0 | 360 | 1e-5 | 5760/24 | 3.65625 | cosine_decay_warmup | 70.11% |
| ResNet50_vd | 360 | 7e-5 | 1024/32 | 0.4 | cosine_decay_warmup | 82.07% |
| ResNet101_vd | 360 | 7e-5 | 1024/32 | 0.4 | cosine_decay_warmup | 83.41% |
| Res2Net200_vd_26w_4s | 360 | 4e-5 | 1024/32 | 0.4 | cosine_decay_warmup | 84.82% |
### 3.3 ImageNet1k 训练集 finetune
对于在大数据集上训练的模型,其学习到的特征可能与 ImageNet1k 数据特征有偏,因此在这里使用 ImageNet1k 数据集对模型进行 finetune。 finetune 的超参和 finetune 的精度收益如下。
|Student Model | num_epoch | l2_ecay | batch size/gpu cards | base lr | learning rate decay | top1 acc |
| - |:-: |:-: | :-: |:-: |:-: |:-: |
| MobileNetV1 | 30 | 3e-5 | 4096/8 | 0.016 | cosine_decay_warmup | 77.89% |
| MobileNetV2 | 30 | 1e-5 | 3072/8 | 0.0054 | cosine_decay_warmup | 76.73% |
| MobileNetV3_large_x1_0 | 30 | 1e-5 | 2048/8 | 0.008 | cosine_decay_warmup | 78.96% |
| MobileNetV3_small_x1_0 | 30 | 1e-5 | 6400/32 | 0.025 | cosine_decay_warmup | 71.28% |
| ResNet50_vd | 60 | 7e-5 | 1024/32 | 0.004 | cosine_decay_warmup | 82.39% |
| ResNet101_vd | 30 | 7e-5 | 1024/32 | 0.004 | cosine_decay_warmup | 83.73% |
| Res2Net200_vd_26w_4s | 360 | 4e-5 | 1024/32 | 0.004 | cosine_decay_warmup | 85.13% |
### 3.4 数据增广以及基于 Fix 策略的微调
* 基于前文所述的实验结论,我们在训练的过程中加入自动增广 (AutoAugment)[4] ,同时进一步减小了 l2_decay (4e-5->2e-5) ,最终 ResNet50_vd 经过 SSLD 蒸馏策略,在 ImageNet1k 上的精度可以达到 82.99% ,相比之前不加数据增广的蒸馏策略再次增加了 0.6% 。
* 对于图像分类任务,在测试的时候,测试尺度为训练尺度的 1.15 倍左右时,往往在不需要重新训练模型的情况下,模型的精度指标就可以进一步提升 [5] ,对于 82.99% 的 ResNet50_vd 在 320x320 的尺度下测试,精度可达 83.7% ,我们进一步使用 Fix 策略,即在 320x320 的尺度下进行训练,使用与预测时相同的数据预处理方法,同时固定除 FC 层以外的所有参数,最终在 320x320 的预测尺度下,精度可以达到 **84.0%**
### 3.5 实验过程中的一些问题
* 在预测过程中, batch norm 的平均值与方差是通过加载预训练模型得到(设其模式为 test mode )。在训练过程中, batch norm 是通过统计当前 batch 的信息(设其模式为 train mode ),与历史保存信息进行滑动平均计算得到,在蒸馏任务中,我们发现通过 train mode ,即教师模型的均值与方差实时变化的模式,去指导学生模型,比通过 test mode 蒸馏,得到的学生模型性能更好一些,下面是一组实验结果。因此我们在该蒸馏方案中,均使用 train mode 去得到教师模型的 soft label 。
|Teacher Model | Teacher Top1 | Student Model | Student Top1|
|- |:-: |:-: | :-: |
| ResNet50_vd | 82.35% | MobileNetV3_large_x1_0 | 76.00% |
| ResNet50_vd | 82.35% | MobileNetV3_large_x1_0 | 75.84% |
## 四、蒸馏模型的应用
### 4.1 使用方法
* 中间层学习率调整。蒸馏得到的模型的中间层特征图更加精细化,因此将蒸馏模型预训练应用到其他任务中时,如果采取和之前相同的学习率,容易破坏中间层特征。而如果降低整体模型训练的学习率,则会带来训练收敛速度慢的问题。因此我们使用了中间层学习率调整的策略。具体地:
* 针对 ResNet50_vd ,我们设置一个学习率倍数列表, res block 之前的 3 个 conv2d 卷积参数具有统一的学习率倍数, 4 个 res block 的 conv2d 分别有一个学习率参数,共需设置 5 个学习率倍数的超参。在实验中发现。用于迁移学习finetune分类模型时, `[0.1,0.1,0.2,0.2,0.3]` 的中间层学习率倍数设置在绝大多数的任务中都性能更好;而在目标检测任务中, `[0.05,0.05,0.05,0.1,0.15]` 的中间层学习率倍数设置能够带来更大的精度收益。
* 对于 MoblileNetV3_large_x1_0 ,由于其包含 15 个 block ,我们设置每 3 个 block 共享一个学习率倍数参数,因此需要共 5 个学习率倍数的参数,最终发现在分类和检测任务中, `[0.25,0.25,0.5,0.5,0.75]` 的中间层学习率倍数能够带来更大的精度收益。
* 适当的 l2 decay 。不同分类模型在训练的时候一般都会根据模型设置不同的 l2 decay ,大模型为了防止过拟合,往往会设置更大的 l2 decay ,如 ResNet50 等模型,一般设置为 `1e-4` ;而如 MobileNet 系列模型,在训练时往往都会设置为 `1e-5~4e-5` ,防止模型过度欠拟合,在蒸馏时亦是如此。在将蒸馏模型应用到目标检测任务中时,我们发现也需要调节 backbone 甚至特定任务模型模型的 l2 decay ,和预训练蒸馏时的 l2 decay 尽可能保持一致。以 Faster RCNN MobiletNetV3 FPN 为例,我们发现仅修改该参数,在 COCO2017 数据集上就可以带来最多 0.5% 左右的精度 (mAP) 提升(默认 Faster RCNN l2 decay 为 1e-4 ,我们修改为 1e-5~4e-5 均有 0.3%~0.5% 的提升)。
### 4.2 迁移学习 finetune
* 为验证迁移学习的效果,我们在 10 个小的数据集上验证其效果。在这里为了保证实验的可对比性,我们均使用 ImageNet1k 数据集训练的标准预处理过程,对于蒸馏模型我们也添加了蒸馏模型中间层学习率的搜索。
* 对于 ResNet50_vd , baseline 为 Top1 Acc 79.12% 的预训练模型基于 grid search 搜索得到的最佳精度,对比实验则为基于该精度对预训练和中间层学习率进一步搜索得到的最佳精度。下面给出 10 个数据集上所有 baseline 和蒸馏模型的精度对比。
| Dataset | Model | Baseline Top1 Acc | Distillation Model Finetune |
|- |:-: |:-: | :-: |
| Oxford102 flowers | ResNete50_vd | 97.18% | 97.41% |
| caltech-101 | ResNete50_vd | 92.57% | 93.21% |
| Oxford-IIIT-Pets | ResNete50_vd | 94.30% | 94.76% |
| DTD | ResNete50_vd | 76.48% | 77.71% |
| fgvc-aircraft-2013b | ResNete50_vd | 88.98% | 90.00% |
| Stanford-Cars | ResNete50_vd | 92.65% | 92.76% |
| SUN397 | ResNete50_vd | 64.02% | 68.36% |
| cifar100 | ResNete50_vd | 86.50% | 87.58% |
| cifar10 | ResNete50_vd | 97.72% | 97.94% |
| Food-101 | ResNete50_vd | 89.58% | 89.99% |
* 可以看出在上面 10 个数据集上,结合适当的中间层学习率倍数设置,蒸馏模型平均能够带来 1% 以上的精度提升。
### 4.3 目标检测
我们基于两阶段目标检测 Faster/Cascade RCNN 模型验证蒸馏得到的预训练模型的效果。
* ResNet50_vd
设置训练与评测的尺度均为 640x640 ,最终 COCO 上检测指标如下。
| Model | train/test scale | pretrain top1 acc | feature map lr | coco mAP |
|- |:-: |:-: | :-: | :-: |
| Faster RCNN R50_vd FPN | 640/640 | 79.12% | [1.0,1.0,1.0,1.0,1.0] | 34.8% |
| Faster RCNN R50_vd FPN | 640/640 | 79.12% | [0.05,0.05,0.1,0.1,0.15] | 34.3% |
| Faster RCNN R50_vd FPN | 640/640 | 82.18% | [0.05,0.05,0.1,0.1,0.15] | 36.3% |
在这里可以看出,对于未蒸馏模型,过度调整中间层学习率反而降低最终检测模型的性能指标。基于该蒸馏模型,我们也提供了领先的服务端实用目标检测方案,详细的配置与训练代码均已开源,可以参考 [PaddleDetection](https://github.com/PaddlePaddle/PaddleDetection/tree/master/configs/rcnn_enhance)
## 五、SSLD实战
本节将基于 ImageNet-1K 的数据集详细介绍 SSLD 蒸馏实验,如果想快速体验此方法,可以参考 [**30分钟玩转PaddleClas(进阶版)**](../../tutorials/quick_start_professional.md) 中基于 CIFAR100 的 SSLD 蒸馏实验。
### 5.1 参数配置
实战部分提供了 SSLD 蒸馏的示例,在 `ppcls/configs/ImageNet/Distillation/mv3_large_x1_0_distill_mv3_small_x1_0.yaml` 中提供了 `MobileNetV3_large_x1_0` 蒸馏`MobileNetV3_small_x1_0` 的配置文件,用户可以在 `tools/train.sh` 里直接替换配置文件的路径即可使用。
```yaml
Arch:
name: "DistillationModel"
# if not null, its lengths should be same as models
pretrained_list:
# if not null, its lengths should be same as models
freeze_params_list:
- True
- False
models:
- Teacher:
name: MobileNetV3_large_x1_0
pretrained: True
use_ssld: True
- Student:
name: MobileNetV3_small_x1_0
pretrained: False
infer_model_name: "Student"
```
在参数配置中,`freeze_params_list` 中需要指定模型是否需要冻结参数, `models` 中需要指定 Teacher 模型和 Student 模型,其中 Teacher 模型需要加载预训练模型。用户可以直接在此处更改模型。
### 5.2 启动命令
当用户配置完训练环境后,类似于训练其他分类任务,只需要将 `tools/train.sh` 中的配置文件替换成为相应的蒸馏配置文件即可。
其中 `train.sh` 中的内容如下:
```bash
python -m paddle.distributed.launch \
--selected_gpus="0,1,2,3" \
--log_dir=mv3_large_x1_0_distill_mv3_small_x1_0 \
tools/train.py \
-c ./ppcls/configs/ImageNet/Distillation/mv3_large_x1_0_distill_mv3_small_x1_0.yaml
```
运行 `train.sh`
```bash
sh tools/train.sh
```
### 5.3 注意事项
* 用户在使用 SSLD 蒸馏之前,首先需要在目标数据集上训练一个教师模型,该教师模型用于指导学生模型在该数据集上的训练。
* 如果学生模型没有加载预训练模型,训练的其他超参数可以参考该学生模型在 ImageNet-1k 上训练的超参数,如果学生模型加载了预训练模型,学习率可以调整到原来的 1/10 或者 1/100 。
* 在 SSLD 蒸馏的过程中,学生模型只学习 soft-label 导致训练目标变的更加复杂,建议可以适当的调小 `l2_decay` 的值来获得更高的验证集准确率。
* 若用户准备添加无标签的训练数据,只需要将新的训练数据放置在原本训练数据的路径下,生成新的数据 list 即可,另外,新生成的数据 list 需要将无标签的数据添加伪标签(只是为了统一读数据)。
## 参考文献
[1] Hinton G, Vinyals O, Dean J. Distilling the knowledge in a neural network[J]. arXiv preprint arXiv:1503.02531, 2015.
[2] Bagherinezhad H, Horton M, Rastegari M, et al. Label refinery: Improving imagenet classification through label progression[J]. arXiv preprint arXiv:1805.02641, 2018.
[3] Yalniz I Z, Jégou H, Chen K, et al. Billion-scale semi-supervised learning for image classification[J]. arXiv preprint arXiv:1905.00546, 2019.
[4] Cubuk E D, Zoph B, Mane D, et al. Autoaugment: Learning augmentation strategies from data[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2019: 113-123.
[5] Touvron H, Vedaldi A, Douze M, et al. Fixing the train-test resolution discrepancy[C]//Advances in Neural Information Processing Systems. 2019: 8250-8260.
# 知识蒸馏
## 1. 模型压缩和知识蒸馏方法简介
近年来,深度神经网络在计算机视觉、自然语言处理等领域被验证是一种极其有效的解决问题的方法。通过构建合适的神经网络,加以训练,最终网络模型的性能指标基本上都会超过传统算法。
在数据量足够大的情况下,通过合理构建网络模型的方式增加其参数量,可以显著改善模型性能,但是这又带来了模型复杂度急剧提升的问题。大模型在实际场景中使用的成本较高。
深度神经网络一般有较多的参数冗余,目前有几种主要的方法对模型进行压缩,减小其参数量。如裁剪、量化、知识蒸馏等,其中知识蒸馏是指使用教师模型(teacher model)去指导学生模型(student model)学习特定任务,保证小模型在参数量不变的情况下,得到比较大的性能提升,甚至获得与大模型相似的精度指标 [1]。
目前知识蒸馏的方法大致可以分为以下三种。
* Response based distillation:教师模型对学生模型的输出进行监督。
* Feature based distillation:教师模型对学生模型的中间层 feature map 进行监督。
* Relation based distillation:对于不同的样本,使用教师模型和学生模型同时计算样本之间 feature map 的相关性,使得学生模型和教师模型得到的相关性矩阵尽可能一致。
## 2. 知识蒸馏应用
知识蒸馏算法在模型轻量化过程任务中应用广泛,对于需要满足特定的精度的任务,通过使用知识蒸馏的方法,我们可以使用更小的模型便能达到要求的精度,从而减小了模型部署的成本。
此外,对于相同的模型结构,使用知识蒸馏训练得到的预训练模型精度往往更高,这些预训练模型往往也可以提升下游任务的模型精度。比如在图像分类任务中,基于知识蒸馏算法得到的精度更高的预训练模型,也能够在目标检测、图像分割、OCR、视频分类等任务中获得明显的精度收益。
## 3. 知识蒸馏算法介绍
### 3.1 Response based distillation
最早的知识蒸馏算法KD,由Hinton提出,训练的损失函数中除了 gt loss 之外,还引入了学生模型与教师模型输出的 KL 散度,最终精度超过单纯使用 gt loss 训练的精度。这里需要注意的是,在训练的时候,需要首先训练得到一个更大的教师模型,来指导学生模型的训练过程。
PaddleClas 中提出了一种简单使用的 SSLD 知识蒸馏算法 [6],在训练的时候去除了对 gt label 的依赖,结合大量无标注数据,最终蒸馏训练得到的预训练模型在 15 个模型上的精度提升平均高达 3%。
上述标准的蒸馏方法是通过一个大模型作为教师模型来指导学生模型提升效果,而后来又发展出 DML (Deep Mutual Learning) 互学习蒸馏方法 [7],即通过两个结构相同的模型互相学习。具体的。相比于 KD 等依赖于大的教师模型的知识蒸馏算法,DML 脱离了对大的教师模型的依赖,蒸馏训练的流程更加简单,模型产出效率也要更高一些。
### 3.2 Feature based distillation
Heo 等人提出了 OverHaul [8], 计算学生模型与教师模型的 feature map distance,作为蒸馏的 loss,在这里使用了学生模型、教师模型的转移,来保证二者的 feature map 可以正常地进行 distance 的计算。
基于 feature map distance 的知识蒸馏方法也能够和 `3.1章节` 中的基于 response 的知识蒸馏算法融合在一起,同时对学生模型的输出结果和中间层 feature map 进行监督。而对于 DML 方法来说,这种融合过程更为简单,因为不需要对学生和教师模型的 feature map 进行转换,便可以完成对齐 (alignment) 过程。PP-OCRv2 系统中便使用了这种方法,最终大幅提升了 OCR 文字识别模型的精度。
### 3.3 Relation based distillation

`3.1``3.2` 章节中的论文中主要是考虑到学生模型与教师模型的输出或者中间层 feature map,这些知识蒸馏算法只关注个体的输出结果,没有考虑到个体之间的输出关系。
Park 等人提出了 RKD [10],基于关系的知识蒸馏算法,RKD 中进一步考虑个体输出之间的关系,使用 2 种损失函数,二阶的距离损失(distance-wise)和三阶的角度损失(angle-wise)
本论文提出的算法关系知识蒸馏(RKD)迁移教师模型得到的输出结果间的结构化关系给学生模型,不同于之前的只关注个体输出结果,RKD 算法使用两种损失函数:二阶的距离损失 (distance-wise) 和三阶的角度损失 (angle-wise)。在最终计算蒸馏损失函数的时候,同时考虑KD loss 和 RKD loss。最终精度优于单独使用 KD loss 蒸馏得到的模型精度。
## 4. 参考文献
[1] Hinton G, Vinyals O, Dean J. Distilling the knowledge in a neural network[J]. arXiv preprint arXiv:1503.02531, 2015.
[2] Bagherinezhad H, Horton M, Rastegari M, et al. Label refinery: Improving imagenet classification through label progression[J]. arXiv preprint arXiv:1805.02641, 2018.
[3] Yalniz I Z, Jégou H, Chen K, et al. Billion-scale semi-supervised learning for image classification[J]. arXiv preprint arXiv:1905.00546, 2019.
[4] Cubuk E D, Zoph B, Mane D, et al. Autoaugment: Learning augmentation strategies from data[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2019: 113-123.
[5] Touvron H, Vedaldi A, Douze M, et al. Fixing the train-test resolution discrepancy[C]//Advances in Neural Information Processing Systems. 2019: 8250-8260.
[6] Cui C, Guo R, Du Y, et al. Beyond Self-Supervision: A Simple Yet Effective Network Distillation Alternative to Improve Backbones[J]. arXiv preprint arXiv:2103.05959, 2021.
[7] Zhang Y, Xiang T, Hospedales T M, et al. Deep mutual learning[C]//Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2018: 4320-4328.
[8] Heo B, Kim J, Yun S, et al. A comprehensive overhaul of feature distillation[C]//Proceedings of the IEEE/CVF International Conference on Computer Vision. 2019: 1921-1930.
[9] Du Y, Li C, Guo R, et al. PP-OCRv2: Bag of Tricks for Ultra Lightweight OCR System[J]. arXiv preprint arXiv:2109.03144, 2021.
[10] Park W, Kim D, Lu Y, et al. Relational knowledge distillation[C]//Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2019: 3967-3976.
# 图像识别快速开始
本文档包含 3 个部分:环境配置、图像识别体验、未知类别的图像识别体验。
如果图像类别已经存在于图像索引库中,那么可以直接参考[图像识别体验](#图像识别体验)章节,完成图像识别过程;如果希望识别未知类别的图像,即图像类别之前不存在于索引库中,那么可以参考[未知类别的图像识别体验](#未知类别的图像识别体验)章节,完成建立索引并识别的过程。
## 目录
* [1. 环境配置](#环境配置)
* [2. 图像识别体验](#图像识别体验)
* [2.1 下载、解压inference 模型与demo数据](#下载、解压inference_模型与demo数据)
* [2.2 商品别与检索](#商品识别与检索)
* [2.2.1 识别单张图像](#识别单张图像)
* [2.2.2 基于文件夹的批量识别](#基于文件夹的批量识别)
* [3. 未知类别的图像识别体验](#未知类别的图像识别体验)
* [3.1 准备新的数据与标签](#准备新的数据与标签)
* [3.2 建立新的索引库](#建立新的索引库)
* [3.3 基于新的索引库的图像识别](#基于新的索引库的图像识别)
<a name="环境配置"></a>
## 1. 环境配置
* 安装:请先参考[快速安装](./install.md)配置 PaddleClas 运行环境。
* 进入 `deploy` 运行目录。本部分所有内容与命令均需要在 `deploy` 目录下运行,可以通过下面的命令进入 `deploy` 目录。
```
cd deploy
```
<a name="图像识别体验"></a>
## 2. 图像识别体验
检测模型与 4 个方向( Logo、动漫人物、车辆、商品 )的识别 inference 模型、测试数据下载地址以及对应的配置文件地址如下。
服务器端通用主体检测模型与各方向识别模型:
| 模型简介 | 推荐场景 | inference模型 | 预测配置文件 | 构建索引库的配置文件 |
| ------------ | ------------- | -------- | ------- | -------- |
| 通用主体检测模型 | 通用场景 |[模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/ppyolov2_r50vd_dcn_mainbody_v1.0_infer.tar) | - | - |
| Logo 识别模型 | Logo场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/logo_rec_ResNet50_Logo3K_v1.0_infer.tar) | [inference_logo.yaml](../../../deploy/configs/inference_logo.yaml) | [build_logo.yaml](../../../deploy/configs/build_logo.yaml) |
| 动漫人物识别模型 | 动漫人物场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/cartoon_rec_ResNet50_iCartoon_v1.0_infer.tar) | [inference_cartoon.yaml](../../../deploy/configs/inference_cartoon.yaml) | [build_cartoon.yaml](../../../deploy/configs/build_cartoon.yaml) |
| 车辆细分类模型 | 车辆场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/vehicle_cls_ResNet50_CompCars_v1.0_infer.tar) | [inference_vehicle.yaml](../../../deploy/configs/inference_vehicle.yaml) | [build_vehicle.yaml](../../../deploy/configs/build_vehicle.yaml) |
| 商品识别模型 | 商品场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/product_ResNet50_vd_aliproduct_v1.0_infer.tar) | [inference_product.yaml](../../../deploy/configs/inference_product.yaml) | [build_product.yaml](../../../deploy/configs/build_product.yaml) |
| 车辆ReID模型 | 车辆ReID场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/vehicle_reid_ResNet50_VERIWild_v1.0_infer.tar) | - | - |
轻量级通用主体检测模型与轻量级通用识别模型:
| 模型简介 | 推荐场景 | inference模型 | 预测配置文件 | 构建索引库的配置文件 |
| ------------ | ------------- | -------- | ------- | -------- |
| 轻量级通用主体检测模型 | 通用场景 |[模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/picodet_PPLCNet_x2_5_mainbody_lite_v1.0_infer.tar) | - | - |
| 轻量级通用识别模型 | 通用场景 | [模型下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/general_PPLCNet_x2_5_lite_v1.0_infer.tar) | [inference_product.yaml](../../../deploy/configs/inference_product.yaml) | [build_product.yaml](../../../deploy/configs/build_product.yaml) |
本章节 demo 数据下载地址如下: [数据下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/recognition_demo_data_v1.1.tar)
**注意**
1. windows 环境下如果没有安装 wget , 可以按照下面的步骤安装 wget 与 tar 命令,也可以在下载模型时将链接复制到浏览器中下载,并解压放置在相应目录下; linux 或者 macOS 用户可以右键点击,然后复制下载链接,即可通过 `wget` 命令下载。
2. 如果 macOS 环境下没有安装 `wget` 命令,可以运行下面的命令进行安装。
3. 轻量级通用识别模型的预测配置文件和构建索引的配置文件目前使用的是服务器端商品识别模型的配置,您可以自行修改模型的路径完成相应的索引构建和识别预测。
```shell
# 安装 homebrew
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)";
# 安装wget
brew install wget
```
4. 如果希望在 windows 环境下安装 wget ,可以参考:[链接](https://www.cnblogs.com/jeshy/p/10518062.html);如果希望在 windows 环境中安装 tar 命令,可以参考:[链接](https://www.cnblogs.com/chooperman/p/14190107.html)
* 可以按照下面的命令下载并解压数据与模型
```shell
mkdir models
cd models
# 下载识别inference模型并解压
wget {模型下载链接地址} && tar -xf {压缩包的名称}
cd ..
# 下载demo数据并解压
wget {数据下载链接地址} && tar -xf {压缩包的名称}
```
<a name="下载、解压inference_模型与demo数据"></a>
### 2.1 下载、解压 inference 模型与 demo 数据
以商品识别为例,下载 demo 数据集以及通用检测、识别模型,命令如下。
```shell
mkdir models
cd models
# 下载通用检测 inference 模型并解压
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/ppyolov2_r50vd_dcn_mainbody_v1.0_infer.tar && tar -xf ppyolov2_r50vd_dcn_mainbody_v1.0_infer.tar
# 下载识别 inference 模型并解压
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/inference/product_ResNet50_vd_aliproduct_v1.0_infer.tar && tar -xf product_ResNet50_vd_aliproduct_v1.0_infer.tar
cd ../
# 下载 demo 数据并解压
wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/recognition_demo_data_v1.1.tar && tar -xf recognition_demo_data_v1.1.tar
```
解压完毕后,`recognition_demo_data_v1.1` 文件夹下应有如下文件结构:
```
├── recognition_demo_data_v1.1
│ ├── gallery_cartoon
│ ├── gallery_logo
│ ├── gallery_product
│ ├── gallery_vehicle
│ ├── test_cartoon
│ ├── test_logo
│ ├── test_product
│ └── test_vehicle
├── ...
```
其中 `gallery_xxx` 文件夹中存放的是用于构建索引库的原始图像, `test_xxx` 文件夹中存放的是用于测试识别效果的图像列表。
`models` 文件夹下应有如下文件结构:
```
├── product_ResNet50_vd_aliproduct_v1.0_infer
│ ├── inference.pdiparams
│ ├── inference.pdiparams.info
│ └── inference.pdmodel
├── ppyolov2_r50vd_dcn_mainbody_v1.0_infer
│ ├── inference.pdiparams
│ ├── inference.pdiparams.info
│ └── inference.pdmodel
```
**注意**
如果使用轻量级通用识别模型, Demo 数据需要重新提取特征、够建索引,方式如下:
```shell
python3.7 python/build_gallery.py -c configs/build_product.yaml -o Global.rec_inference_model_dir=./models/general_PPLCNet_x2_5_lite_v1.0_infer
```
<a name="商品识别与检索"></a>
### 2.2 商品识别与检索
以商品识别 demo 为例,展示识别与检索过程(如果希望尝试其他方向的识别与检索效果,在下载解压好对应的 demo 数据与模型之后,替换对应的配置文件即可完成预测)。
注意,此部分使用了 `faiss` 作为检索库,安装方法如下:
```python
pip install faiss-cpu==1.7.1post2
```
若使用时,不能正常引用,则 `uninstall` 之后,重新 `install` ,尤其是 windows 下。
<a name="识别单张图像"></a>
#### 2.2.1 识别单张图像
运行下面的命令,对图像 `./recognition_demo_data_v1.1/test_product/daoxiangcunjinzhubing_6.jpg` 进行识别与检索
```shell
# 使用下面的命令使用GPU进行预测
python3.7 python/predict_system.py -c configs/inference_product.yaml
# 使用下面的命令使用CPU进行预测
python3.7 python/predict_system.py -c configs/inference_product.yaml -o Global.use_gpu=False
```
待检索图像如下所示。
<div align="center">
<img src="../../images/recognition/product_demo/query/daoxiangcunjinzhubing_6.jpg" width = "400" />
</div>
最终输出结果如下。
```json
[{'bbox': [287, 129, 497, 326], 'rec_docs': '稻香村金猪饼', 'rec_scores': 0.8309420943260193}, {'bbox': [99, 242, 313, 426], 'rec_docs': '稻香村金猪饼', 'rec_scores': 0.7245652079582214}]
```
其中bbox表示检测出的主体所在位置,rec_docs表示索引库中与检测框最为相似的类别,rec_scores表示对应的置信度。
检测的可视化结果也保存在`output`文件夹下,对于本张图像,识别结果可视化如下所示。
<div align="center">
<img src="../../images/recognition/product_demo/result/daoxiangcunjinzhubing_6.jpg" width = "400" />
</div>
<a name="基于文件夹的批量识别"></a>
#### 2.2.2 基于文件夹的批量识别
如果希望预测文件夹内的图像,可以直接修改配置文件中的 `Global.infer_imgs` 字段,也可以通过下面的 `-o` 参数修改对应的配置。
```shell
# 使用下面的命令使用GPU进行预测,如果希望使用CPU预测,可以在命令后面添加 -o Global.use_gpu=False
python3.7 python/predict_system.py -c configs/inference_product.yaml -o Global.infer_imgs="./recognition_demo_data_v1.1/test_product/"
```
终端中会输出该文件夹内所有图像的识别结果,如下所示。
```json
...
[{'bbox': [37, 29, 123, 89], 'rec_docs': '香奈儿包', 'rec_scores': 0.6163763999938965}, {'bbox': [153, 96, 235, 175], 'rec_docs': '香奈儿包', 'rec_scores': 0.5279821157455444}]
[{'bbox': [735, 562, 1133, 851], 'rec_docs': '香奈儿包', 'rec_scores': 0.5588355660438538}]
[{'bbox': [124, 50, 230, 129], 'rec_docs': '香奈儿包', 'rec_scores': 0.6980369687080383}]
[{'bbox': [0, 0, 275, 183], 'rec_docs': '香奈儿包', 'rec_scores': 0.5818190574645996}]
[{'bbox': [400, 1179, 905, 1537], 'rec_docs': '香奈儿包', 'rec_scores': 0.9814301133155823}]
[{'bbox': [544, 4, 1482, 932], 'rec_docs': '香奈儿包', 'rec_scores': 0.5143815279006958}]
[{'bbox': [29, 42, 194, 183], 'rec_docs': '香奈儿包', 'rec_scores': 0.9543638229370117}]
...
```
所有图像的识别结果可视化图像也保存在 `output`文件夹内。
更多地,可以通过修改 `Global.rec_inference_model_dir` 字段来更改识别 inference 模型的路径,通过修改 `IndexProcess.index_dir` 字段来更改索引库索引的路径。
<a name="未知类别的图像识别体验"></a>
## 3. 未知类别的图像识别体验
对图像 `./recognition_demo_data_v1.1/test_product/anmuxi.jpg` 进行识别,命令如下
```shell
# 使用下面的命令使用 GPU 进行预测,如果希望使用 CPU 预测,可以在命令后面添加 -o Global.use_gpu=False
python3.7 python/predict_system.py -c configs/inference_product.yaml -o Global.infer_imgs="./recognition_demo_data_v1.1/test_product/anmuxi.jpg"
```
待检索图像如下所示。
<div align="center">
<img src="../../images/recognition/product_demo/query/anmuxi.jpg" width = "400" />
</div>
输出结果为空。
由于默认的索引库中不包含对应的索引信息,所以这里的识别结果有误,此时我们可以通过构建新的索引库的方式,完成未知类别的图像识别。
当索引库中的图像无法覆盖我们实际识别的场景时,即在预测未知类别的图像时,我们需要将对应类别的相似图像添加到索引库中,从而完成对未知类别的图像识别,这一过程是不需要重新训练的。
<a name="准备新的数据与标签"></a>
### 3.1 准备新的数据与标签
首先需要将与待检索图像相似的图像列表拷贝到索引库原始图像的文件夹( `./recognition_demo_data_v1.1/gallery_product/gallery` )中,运行下面的命令拷贝相似图像。
```shell
cp -r ../docs/images/recognition/product_demo/gallery/anmuxi ./recognition_demo_data_v1.1/gallery_product/gallery/
```
然后需要编辑记录了图像路径和标签信息的文本文件( `./recognition_demo_data_v1.1/gallery_product/data_file_update.txt` ),这里基于原始标签文件,新建一个文件。命令如下。
```shell
# 复制文件
cp recognition_demo_data_v1.1/gallery_product/data_file.txt recognition_demo_data_v1.1/gallery_product/data_file_update.txt
```
然后在文件 `recognition_demo_data_v1.1/gallery_product/data_file_update.txt` 中添加以下的信息,
```
gallery/anmuxi/001.jpg 安慕希酸奶
gallery/anmuxi/002.jpg 安慕希酸奶
gallery/anmuxi/003.jpg 安慕希酸奶
gallery/anmuxi/004.jpg 安慕希酸奶
gallery/anmuxi/005.jpg 安慕希酸奶
gallery/anmuxi/006.jpg 安慕希酸奶
```
每一行的文本中,第一个字段表示图像的相对路径,第二个字段表示图像对应的标签信息,中间用 `tab` 键分隔开(注意:有些编辑器会将 `tab` 自动转换为 `空格` ,这种情况下会导致文件解析报错)。
<a name="建立新的索引库"></a>
### 3.2 建立新的索引库
使用下面的命令构建 `index` 索引,加速识别后的检索过程。
```shell
python3.7 python/build_gallery.py -c configs/build_product.yaml -o IndexProcess.data_file="./recognition_demo_data_v1.1/gallery_product/data_file_update.txt" -o IndexProcess.index_dir="./recognition_demo_data_v1.1/gallery_product/index_update"
```
最终新的索引信息保存在文件夹 `./recognition_demo_data_v1.1/gallery_product/index_update` 中。
<a name="基于新的索引库的图像识别"></a>
### 3.3 基于新的索引库的图像识别
使用新的索引库,对上述图像进行识别,运行命令如下。
```shell
# 使用下面的命令使用 GPU 进行预测,如果希望使用 CPU 预测,可以在命令后面添加 -o Global.use_gpu=False
python3.7 python/predict_system.py -c configs/inference_product.yaml -o Global.infer_imgs="./recognition_demo_data_v1.1/test_product/anmuxi.jpg" -o IndexProcess.index_dir="./recognition_demo_data_v1.1/gallery_product/index_update"
```
输出结果如下。
```json
[{'bbox': [243, 80, 523, 522], 'rec_docs': '安慕希酸奶', 'rec_scores': 0.5570770502090454}]
```
最终识别结果为`安慕希酸奶`,识别正确,识别结果可视化如下所示。
<div align="center">
<img src="../../images/recognition/product_demo/result/anmuxi.jpg" width = "400" />
</div>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册