# 图像分类常见问题汇总 - 2020 第 1 季
## 目录
* [1. 第 1 期](#1)(2020.11.03)
* [2. 第 2 期](#2)(2020.11.11)
* [3. 第 3 期](#3)(2020.11.18)
* [4. 第 4 期](#4)(2020.12.07)
* [5. 第 5 期](#5)(2020.12.17)
* [6. 第 6 期](#6)(2020.12.30)
## 第 1 期
### Q1.1: PaddleClas 可以用来做什么?
**A**:PaddleClas 是飞桨为工业界和学术界所准备的一个图像分类任务的工具集,助力使用者训练出更好的视觉模型和应用落地。PaddleClas 提供了基于图像分类的模型训练、评估、预测、部署全流程的服务,方便大家更加高效地学习图像分类。具体地,PaddleClas 中包含如下一些特性。
* PaddleClas 提供了 36 个系列的分类网络结构(ResNet, ResNet_vd, MobileNetV3, Res2Net, HRNet 等)和训练配置,175 个预训练模型和性能评估与预测,供大家选择并使用。
* PaddleClas 提供了 TensorRT 预测、python inference、c++ inference、Paddle-Lite 预测部署、PaddleServing、PaddleHub 等多种预测部署推理方案,在方便在多种环境中进行部署推理。
* PaddleClas 提供了一种简单的 SSLD 知识蒸馏方案,基于该方案蒸馏模型的识别准确率普遍提升 3% 以上。
* PaddleClas 支持 AutoAugment、Cutout、Cutmix 等 8 种数据增广算法详细介绍、代码复现和在统一实验环境下的效果评估。
* PaddleClas 支持在 Windows/Linux/MacOS 环境中基于 CPU/GPU 进行使用。
### Q1.2: ResNet 系列模型是什么?有哪些模型?为什么在服务器端如此推荐 ResNet 系列模型?
**A**: ResNet 中创新性地引入了残差结构,通过堆叠多个残差结构从而构建了 ResNet 网络。实验表明使用残差块可以有效地提升收敛速度和精度,PaddleClas 中,ResNet 从小到达,依次有包含 18、34、50、101、152、200 层的 ResNet 结构,ResNet 系列模型于 2015 年被提出,在不同的应用场景中,如分类、检测、分割等,都已经验证过其有效性,业界也早已对其进行了大量优化,该系列模型在速度和精度方面都有着非常明显的优势,对基于 TensorRT 以及 FP16 的预测支持得也很好,因而推荐大家使用 ResNet 系列模型;由于其模型所占存储相对较大,因此常用于服务器端。更多关于 ResNet 模型的介绍可以参考论文 [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385)。
### Q1.3: ResNet_vd 和 ResNet、ResNet_vc 结构有什么区别呢?
**A**:
ResNet_va 至 vd 的结构如下图所示,ResNet 最早提出时为 va 结构,在降采样残差模块这个部分,在左边的特征变换通路中(Path A),第一个 1x1 卷积部分就行了降采样,从而导致信息丢失(卷积的 kernel size 为 1,stride 为 2,输入特征图中 有部分特征没有参与卷积的计算);在 vb 结构中,把降采样的步骤从最开始的第一个 1x1 卷积调整到中间的 3x3 卷积中,从而避免了信息丢失的问题,PaddleClas 中的 ResNet 模型默认就是 ResNet_vb;vc 结构则是将最开始这个 7x7 的卷积变成 3 个 3x3 的卷积,在感受野不变的情况下,计算量和存储大小几乎不变,而且实验证明精度相对于 vb 结构有所提升;vd 结构是修改了降采样残差模块右边的特征通路(Path B)。把降采样的过程由平均池化这个操作去替代了,这一系列的改进(va->vd),几乎没有带来新增的预测耗时,结合适当的训练策略,比如说标签平滑以及 mixup 数据增广,精度可以提升高达 2.7%。
![](../../images/faq/ResNet_vabcd_structure.png)
### Q1.4 如果确定使用 ResNet 系列模型,怎么根据实际的场景需求选用不同的模型呢?
**A**:
ResNet 系列模型中,相比于其他模型,ResNet_vd 模型在预测速度几乎不变的情况下,精度有非常明显的提升,因此推荐大家使用 ResNet_vd 系列模型。
[ResNet 及其 vd 系列模型文档](../models/ResNet_and_vd.md)中给出了 batch size=4 的情况下,在 T4 GPU 上,不同模型的的预测耗时、FLOPs、Params 与精度的变化曲线,可以根据自己自己的实际部署场景中的需求,去选择合适的模型,如果希望模型存储大小尽可能小或者预测速度尽可能快,则可以使用 ResNet18_vd 模型,如果希望获得尽可能高的精度,则建议使用 ResNet152_vd 或者 ResNet200_vd 模型。更多关于 ResNet 系列模型的介绍可以参考文档:[ResNet 及其 vd 系列模型文档](../models/ResNet_and_vd.md)。
* 精度-预测速度变化曲线
![](../../images/models/T4_benchmark/t4.fp32.bs4.ResNet.png)
* 精度-params 变化曲线
![](../../images/models/T4_benchmark/t4.fp32.bs4.ResNet.params.png)
* 精度-flops 变化曲线
![](../../images/models/T4_benchmark/t4.fp32.bs4.ResNet.flops.png)
### Q1.5 在网络中的 block 里 conv-bn-relu 是固定的形式吗?
**A**: 在 batch-norm 出现之前,主流的卷积神经网络的固定形式是 conv-relu。在现阶段的卷积神经网络中,conv-bn-relu 是大部分网络中 block 的固定形式,这样的设计是相对鲁棒的结构,此外,DenseNet 中的 block 选用的是 bn-relu-conv 的形式,ResNet-V2 中也使用的是这种组合方式。在 MobileNetV2 中,为了不丢失信息,部分 block 中间的层没有使用 relu 激活函数,选用的是 conv-bn 的形式。
### Q1.6 ResNet34 与 ResNet50 的区别?
**A**: ResNet 系列中有两种不同的 block,分别是 basic-block 和 bottleneck-block,堆叠较多这样的 block 组成了 ResNet 网络。basic-block 是带有 shortcut 的两个 3x3 的卷积核的堆叠,bottleneck-block 是带有 shortcut 的 1x1 卷积核、3x3 卷积核、1x1 卷积核的堆叠,所以 basic-block 中有两层,bottleneck-block 有三层。ResNet34 和 ResNet50 中堆叠的 block 数相同,但是堆叠的种类分别是 basic-block 和 bottleneck-block。
### Q1.7 大卷积核一定可以带来正向收益吗?
**A**: 不一定,将网络中的所有卷积核都增大未必会带来性能的提升,甚至会有有损性能,在论文 [MixConv: Mixed Depthwise Convolutional Kernels](https://arxiv.org/abs/1907.09595)
中指出,在一定范围内提升卷积核大小对精度的提升有正向作用,但是超出后会有损精度。所以考虑到模型的大小、计算量等问题,一般不选用大的卷积核去设计网络。同时,在 [PP-LCNet](../models/PP-LCNet.md) 文章中,也有关于大卷积核的实验。
## 第 2 期
### Q2.1: PaddleClas 如何训练自己的 backbone?
**A**:具体流程如下:
* 首先在 ppcls/arch/backbone/model_zoo/ 文件夹下新建一个自己的模型结构文件,即你自己的 backbone,模型搭建可以参考 resnet.py;
* 然后在 ppcls/arch/backbone/\_\_init\_\_.py 中添加自己设计的 backbone 的类;
* 其次配置训练的 yaml 文件,此处可以参考 ppcls/configs/ImageNet/ResNet/ResNet50.yaml;
* 最后启动训练即可。
### Q2.2: 如何利用已有的模型和权重对自己的分类任务进行迁移?
**A**: 具体流程如下:
* 首先,好的预训练模型往往会有更好的迁移效果,所以建议选用精度较高的预训练模型,PaddleClas 提供了一系列业界领先的预训练模型,建议使用;
* 其次,要根据迁移的数据集的规模来确定训练超参数,一般超参数需要调试才可以寻找到一个局部最优值,如果没有相关经验,建议先从 learning rate 开始调起,一般来说,规模较小的数据集使用较小的 learning rate,如 0.001,另外,建议学习率使用 warmup 策略,避免过大的学习率破坏预训练模型的权重。在迁移过程中,也可以设置 backbone 中不同层的学习率,往往从网络的头部到尾补学习率逐渐减小效果较好。在数据集规模较小的时候,也可以使用数据增强策略,PaddleClas 提供了 8 中强有力的数据增强策略,为更高的精度保驾护航。
* 训练结束后,可以反复迭代上述过程,直到寻找到局部最优值。
### Q2.3: PaddleClas 中 configs 下的默认参数适合任何一个数据集吗?
**A**: PaddleClas 中的 ppcls/configs/ImageNet/下的配置文件默认参数是 ImageNet-1k 的训练参数,这个参数并不适合所有的数据集,具体数据集需要在此基础上进一步调试。
### Q2.4 PaddleClas 中的不同的模型使用了不同的分辨率,标配的应该是多少呢?
**A**: PaddleClas 严格遵循了论文作者的使用的分辨率。自 2012 年 AlexNet 以来,大多数的卷积神经网络在 ImageNet 上训练的分辨率为 224x224,Google 在设计 InceptionV3 的时候为了适应网络结构将分辨率调至 299x299,之后其推出的 Xception、InceptionV4 也是使用的该分辨率。此外,在 EfficeintNet 中,作者分析了不同规模的网络应该使用不同的分辨率,所以该系列网络中每个不同大小的网络都使用了不同的分辨率。在实际使用场景中,推荐使用默认的分辨率,当然,层数较深或者宽度较大的网络也可以尝试使用更大的分辨率。
### Q2.5 PaddleClas 中提供了很多 ssld 模型,其应用的价值是?
**A**: PaddleClas 中提供了很多 ssld 预训练模型,其通过半监督知识蒸馏的方法获得了更好的预训练权重,在迁移任务或者下游视觉任务中,无须替换结构文件、只需要替换精度更高的 ssld 预训练模型即可提升精度,如在 PaddleSeg 中,[HRNet](https://github.com/PaddlePaddle/PaddleSeg/blob/release/v0.7.0/docs/model_zoo.md) 使用了 ssld 预训练模型的权重后,精度大幅度超越业界同样的模型的精度,在 PaddleDetection 中,[PP-YOLO](https://github.com/PaddlePaddle/PaddleDetection/blob/release/0.4/configs/ppyolo/README_cn.md) 使用了 ssld 预训练权重后,在较高的 baseline 上仍有进一步的提升。使用 ssld 预训练权重做分类的迁移表现也很抢眼,在 [SSLD 蒸馏策略](../advanced_tutorials/knowledge_distillation.md) 部分介绍了知识蒸馏对于分类任务迁移的收益。
## 第 3 期
### Q3.1: DenseNet 模型相比于 ResNet 有什么改进呢?有哪些特点或者应用场景呢?
**A**: DenseNet 相比于 ResNet,设计了一个更激进的密集连接机制,通过考虑特征重用和旁路的设置,进一步减少了参数量,而且从一定程度上缓解了梯度弥散的问题,因为引入了更加密集的连接,因此模型更容易训练,而且具有一定的正则化效果。在数据量不是很多的图像分类场景中,DenseNet 是一个不错的选择。更多关于 DenseNet 的介绍与系列模型可以参考 [DenseNet 模型文档](../models/DPN_DenseNet.md)。
### Q3.2: DPN 网络相比于 DenseNet 有哪些改进呢?
**A**:DPN 的全称是 Dual Path Networks,即双通道网络。该网络是由 DenseNet 和 ResNeXt 结合的一个网络,其证明了 DenseNet 能从靠前的层级中提取到新的特征,而 ResNeXt 本质上是对之前层级中已提取特征的复用。作者进一步分析发现,ResNeXt 对特征有高复用率,但冗余度低,DenseNet 能创造新特征,但冗余度高。结合二者结构的优势,作者设计了 DPN 网络。最终 DPN 网络在同样 FLOPS 和参数量下,取得了比 ResNeXt 与 DenseNet 更好的结果。更多关于 DPN 的介绍与系列模型可以参考 [DPN 模型文档](../models/DPN_DenseNet.md)。
### Q3.3: 怎么使用多个模型进行预测融合呢?
**A** 使用多个模型进行预测的时候,建议首先将预训练模型导出为 inference 模型,这样可以摆脱对网络结构定义的依赖,可以参考[模型导出脚本](../../../tools/export_model.py)进行模型导出,之后再参考 [inference 模型预测脚本](../../../deploy/python/predict_cls.py)进行预测即可,在这里需要根据自己使用模型的数量创建多个 predictor。
### Q3.4: PaddleClas 中怎么增加自己的数据增广方法呢?
**A**:
* 对于单张图像的增广,可以参考[基于单张图片的数据增广脚本](../../../ppcls/data/preprocess/ops),参考 `ResizeImage `或者 `CropImage` 等数据算子的写法,创建一个新的类,然后在 `__call__` 中,实现对应的增广方法即可。
* 对于一个 batch 图像的增广,可以参考[基于 batch 数据的数据增广脚本](../../../ppcls/data/preprocess/batch_ops),参考 `MixupOperator` 或者 `CutmixOperator` 等数据算子的写法,创建一个新的类,然后在 `__call__` 中,实现对应的增广方法即可。
### Q3.5: 怎么进一步加速模型训练过程呢?
**A**:
* 可以使用自动混合精度进行训练,这在精度几乎无损的情况下,可以有比较明显的速度收益,以 ResNet50 为例,PaddleClas 中使用自动混合精度训练的配置文件可以参考:[ResNet50_amp_O1.yml](../../../ppcls/configs/ImageNet/ResNet/ResNet50_amp_O1.yaml),主要就是需要在标准的配置文件中添加以下几行
```yaml
# mixed precision training
AMP:
scale_loss: 128.0
use_dynamic_loss_scaling: True
# O1: mixed fp16
level: O1
```
* 可以开启 dali,将数据预处理方法放在 GPU 上运行,在模型比较小时(reader 耗时占比更高一些),开启 dali 会带来比较明显的训练速度收益,在训练的时候,添加 `-o Global.use_dali=True` 即可使用 dali 进行训练,更多关于 dali 安装与介绍可以参考:[dali 安装教程](https://docs.nvidia.com/deeplearning/dali/user-guide/docs/installation.html#nightly-builds)。
## 第 4 期
### Q4.1: PaddlePaddle 的模型文件都有哪几种?
**A**:
* PaddlePaddle 保存的模型相关文件有两类:
* 一类是用于*推理部署*的文件,包括后缀名为 “`pdiparams`”、“`model`” 的文件,其中 “`pdiparams`” 文件存储了模型参数信息,“`model`” 文件存储了模型网络结构信息,对于推理部署文件,使用 `paddle.jit.save` 与 `paddle.jit.load` 接口进行保存、加载。
* 另一类模型相关文件则是用于*训练调优*过程中,包括后缀名为 “`pdparams`” 和 “`pdopt`” 的文件,其中 “`pdparams`” 文件存储了训练过程中的模型参数信息,“`pdopt`” 文件存储了模型训练过程中的优化器信息,对于训练调优文件,使用 `paddle.save` 与 `paddle.load` 接口进行保存、加载。
* 利用推理部署文件,即可构建模型网络结构并加载模型参数,用于预测,利用训练调优文件,即可加载模型参数、优化器信息,用于恢复训练过程。
### Q4.2: HRNet 的创新点体现在哪里?
**A**:
* 在图像分类领域,大部分神经网络的设计思想是提取图像的高维特征,具体来说,通常输入图像的空间分辨率较高,通过多层卷积、池化,可以逐步得到空间分辨率更低,但是维度更高的特征图,然后可用于分类等场景。
* 然而 *HRNet* 的作者认为这种逐步降低空间分辨率的设计思想并不适合目标检测(图像区域层次的分类任务)、语义分割(图像像素层次的分类任务)等场景,因为空间分辨率在逐步降低的过程中,会丢失很多信息,最终学习得到的特征难以表达原始图像在高空间分辨率的信息,而区域层次分类任务和像素层次分类任务都对空间精度十分敏感。
* 因此 *HRNet* 的作者提出了并联不同空间分辨率特征图的思想,与此相对,*VGG* 等神经网络则是通过不同的卷积池化层来串联不同空间分辨率的特征图。并且,*HRNet* 通过连接同等深度、不同空间分辨率的特征图,使得不同空间分辨率特征图的信息可以得到充分交换,具体的网络结构如下图所示。
![](../../images/faq/HRNet.png)
### Q4.3: 在 HRNet 中,对于不同空间分辨率的特征图之间,是如何建立连接的?
**A**:
* 首先,在 *HRNet* 中,对特征图使用 *stride* 为 *2* 的 *3 × 3* 卷积,可以得到低空间分辨率但是为度更高的特征图;而对低空间分辨率特征图先使用 *1 × 1* 卷积进行通道数匹配,再使用最近邻插值的方式进行上采样,即可得到与高空间分辨率特征图相同空间分辨率、通道数的特征图;而对于相同空间分辨率的特征图,直接进行恒等映射即可。具体如下图所示。
![](../../images/faq/HRNet_block.png)
### Q4.4: 模型中的“SE”表示什么意思?
**A**:
* SE 表示该模型使用了 SE 结构。SE 结构来自于 2017 年 ImageNet 分类比赛的冠军方案 *Squeeze-and-Excitation Networks(SENet)*,*SENet* 提出的 SE 结构可以迁移到任何其他网络中。其创新点是通过额外学习 *scale* 向量作为权重作用到特征图上, *scale* 向量维度与特征图通道数相同,学习到的 *scale* 向量中每个维度上的数值表示对该维度特征通道的增强或减弱的大小,以此达到对重要的特征通道进行增强,不重要特征通道减弱的效果,从而让提取的特征指向性更强。
### Q4.5: SE 结构具体如何实现的?
![](../../images/faq/SE_structure.png)
**A**:
* *SE*结构具体如上图所示,首先,*Ftr* 表示常规的卷积操作,*X* 和 *U* 则是 *Ftr* 的输入与输出的特征图,在得到特征图*U*后,使用 *Fsq* 和 *Fex* 操作求得 *scale* 向量,*scale* 向量维度为 *C*,与 *U* 通道数相同,因此可以通过乘积的方式作用到 *U* 上,进而得到 *X~*。
* 具体地,*Fsq* 为 *Global Average Pooling* 操作,*SENet* 作者将其称之为 *Squeeze*,因为该操作可以将 *U* 从 *C × H × W* 压缩到 *C × 1 × 1*,对 *Fsq* 的输出再做 *Fex* 操作。
* *Fex*操作表示两次全连接,作者将该操作称为 *Excitation*。其中第一次全连接将向量的维度从 *1 × 1 × C* 压缩到 *1 × 1 × C/r*,然后使用 *RELU*,再通过第二次全连接将向量的维度恢复到 *C*,这样操作的目的是为了减小计算量,*SENet* 作者通过实验得出结论:在 *r=16* 时可以获得增益与计算量之间的平衡。
* 对于*Fsq*部分,关键是求得 *C* 维的向量,因此不局限于使用 *Global Average Pooling* 操作,*SENet* 作者认为,最终求得的 *scale* 是按通道分别作用于 *U* 的,因此需要基于对应通道的信息计算对应的 *scale*,故使用了最简单的 *Global Average Pooling* 操作,最终求得的 *scale* 向量表示了不同通道之间的分布关系,而忽略了同一个通道中的分布关系。
* 对于 *Fex* 部分,其作用是为了在每一个 *mini batch* 上的训练来求得基于所有训练数据的分布。因为我们的训练是在*mini batch*上进行的,而基于全部训练数据求得的 *scale* 才是最佳的,使用 *Fex* 部分,可以通过在每个 *mini batch* 上的训练来求得更为逼近全部训练数据的 *scale*。
## 第 5 期
### Q5.1 如何选择优化器?
**A**:自深度学习发展以来,就有很多关于优化器的研究者工作,优化器的目的是为了让损失函数尽可能的小,从而找到合适的权重来完成某项任务。目前业界主要用到的优化器有 SGD、RMSProp、Adam、AdaDelt 等,其中由于带 momentum 的 SGD 优化器广泛应用于学术界和工业界(此处仅限于分类任务),所以我们发布的模型也大都使用该优化器来实现损失函数的梯度下降。带 momentum 的 SGD 优化器有两个劣势,其一是收敛速度慢,其二是初始学习率的设置需要依靠大量的经验,然而如果初始学习率设置得当并且迭代轮数充足,该优化器也会在众多的优化器中脱颖而出,使得其在验证集上获得更高的准确率。一些自适应学习率的优化器如 Adam、RMSProp 等,收敛速度往往比较快,但是最终的收敛精度会稍差一些。如果追求更快的收敛速度,我们推荐使用这些自适应学习率的优化器,如果追求更高的收敛精度,我们推荐使用带 momentum 的 SGD 优化器。具体到数据集来说:
- ImageNet-1k: 建议只使用带 momentum 的 SGD 优化器。
- 其他数据集(默认加载 ImageNet-1k 预训练): 加载预训练模型的时候可以考虑使用 Adam 等优化器(效果可能会更好),但使用带 momentum 的 SGD 优化器是绝对是比较不错的方案。
另外,为了进一步加速训练,Lookahead 优化器也是一个不错的选择,在 ImageNet-1k 上,其可以保证在更快的收敛速度下拥有相同的收敛精度,但在部分数据集上表现不太稳定,需要进一步调参。
### Q5.2 如何设置初始学习率以及学习率下降策略?
**A**:学习率的选择往往和优化器以及数据和任务有关系。学习率决定了网络种权重更新的速度。学习率越低,损失函数的变化速度就越慢。虽然使用低学习率可以确保不会错过任何局部极小值,但也意味着将花费更长的时间来进行收敛,特别是在被困在高原区域的情况下。
在整个训练过程中,我们不能使用同样的学习率来更新权重,否则无法到达最优点,所以需要在训练过程中调整学习率的大小。在训练初始阶段,由于权重处于随机初始化的状态,损失函数下降较快,所以可以设置一个较大的学习率。在训练后期,由于权重已经接近最优值,较大的学习率无法进一步寻找最优值,所以需要设置一个较小的学习率。至于学习率下降策略,很多研究者或者从业人员使用的学习率下降方式是 piecewise_decay(step_decay),即阶梯式下降学习率,此外,很多研究者也提出了学习率的其他下降方式,如 polynomial_decay(多项式下降)、exponential_decay(指数下降),cosine_decay(余弦下降)等,其中 cosine_decay 无需调整超参数,鲁棒性也比较高,所以成为现在提高模型精度首选的学习率下降方式。
Cosine_decay 和 piecewise_decay 的学习率变化曲线如下图所示,容易观察到,在整个训练过程中,cosine_decay 都保持着较大的学习率,所以其收敛较为缓慢,但是最终的收敛效果较 peicewise_decay 更好一些。
![](../../images/models/lr_decay.jpeg)
另外,从图中我们也可以看到,cosine_decay 中只有少数轮数使用了较小的学习率,这样会影响到最终的精度,所以为了使得 cosine_decay 发挥更好的效果,建议迭代更多的轮数。
最后,如果使用较大的 batch_size 训练神经网络时,建议您使用 warmup 策略。Warmup 策略顾名思义就是让学习率先预热一下,在训练初期不直接使用最大的学习率,而是用一个逐渐增大的学习率去训练网络,当学习率增大到最高点时,再去衰减学习率的值。实验表明,在 batch_size 较大时,warmup 可以稳定提升模型的精度。具体到数据集来说:
- ImageNet-1k:建议 batch-size 大小为 256、初始学习率为 0.1,cosine-decay 下降学习率。
- 其他数据集(默认加载 ImageNet-1k 预训练): 数据集规模越大,初始学习率也越大,但最好不要超过 0.1(batch-size 为 256 时候),数据集规模越小,初始学习率也越小,当数据集较小时,使用 warmup 也会带来一定的精度提升,学习率下降策略仍旧推荐 cosine-decay。
### Q5.3 如何设置 batch-size 的大小?
**A**:Batch_size 是训练神经网络中的一个重要的超参数,该值决定了一次将多少数据送入神经网络中训练。之前有研究者通过实验发现,当 batch_size 的值与学习率的值呈线性关系时,收敛精度几乎不受影响。在训练 ImageNet-1k 数据时,大部分的神经网络选择的初始学习率为 0.1,batch_size 是 256。具体到数据集来说:
- ImageNet-1k: 学习率设置为 0.1\*k,batch_size 设置为 256\*k。
- 其他数据集(默认加载 ImageNet-1k 预训练): 可以根据实际情况设置(如更小的学习率),但在调整学习率或者 batch-size 时,要同时调整另外一个值。
### Q5.4 weight_decay 是什么?怎么设置?
**A**:过拟合是机器学习中常见的一个名词,简单理解即为模型在训练数据上表现很好,但在测试数据上表现较差,在图像分类问题中,同样存在过拟合的问题,为了避免过拟合,很多正则方式被提出,其中,weight_decay 是其中一个广泛使用的避免过拟合的方式。当使用 SGD 优化器时,weight_decay 等价于在最终的损失函数后添加 L2 正则化,L2 正则化使得网络的权重倾向于选择更小的值,最终整个网络中的参数值更趋向于 0,模型的泛化性能相应提高。在各大深度学习框架的实现中,该值表达的含义是 L2 正则前的系数,在飞桨框架中,该值的名称是 L2Decay,所以以下都称其为 L2Decay。该系数越大,表示加入的正则越强,模型越趋于欠拟合状态。具体到数据集来说:
- ImageNet-1k:大多数的网络将该参数值设置为 1e-4,在一些小的网络如 MobileNet 系列网络中,为了避免网络欠拟合,该值设置为 1e-5~4e-5 之间。下表展示了 MobileNetV1_x0_25 在 ImageNet-1k 上使用不同 L2Decay 的精度情况。由于 MobileNetV1_x0_25 是一个比较小的网络,所以 L2Decay 过大会使网络趋向于欠拟合状态,所以在该网络中,相对 1e-4,3e-5 是更好的选择。
| 模型 | L2Decay | Train acc1/acc5 | Test acc1/acc5 |
|:--:|:--:|:--:|:--:|
| MobileNetV1_x0_25 | 1e-4 | 43.79%/67.61% | 50.41%/74.70% |
| MobileNetV1_x0_25 | 3e-5 | 47.38%/70.83% | 51.45%/75.45% |
另外,该值的设置也和训练过程中是否使用其他正则化有关系。如果训练过程中的数据预处理比较复杂,相当于训练任务变的更难,可以将该值适当减小,下表展示了在 ImageNet-1k 上,ResNet50 在使用 RandAugment 预处理方式后使用不同 L2Decay 的精度。容易观察到,在任务变难后,使用更小的 l2_decay 有助于模型精度的提升。
| 模型 | L2Decay | Train acc1/acc5 | Test acc1/acc5 |
|:--:|:--:|:--:|:--:|
| ResNet50 | 1e-4 | 75.13%/90.42% | 77.65%/93.79% |
| ResNet50 | 7e-5 | 75.56%/90.55% | 78.04%/93.74% |
- 其他数据集(默认加载 ImageNet-1k 预训练):在做迁移任务的时候,最好不要改变训练 ImageNet-1k 时的 L2Decay 的值(即训练得到预训练时的 L2Decay 值,每个 backbone 对应的 L2Decay 值都在相应的训练 yaml 配置文件中),一般的数据集只改变学习率足够。
### Q5.5 是否使用 label_smoothing,如何设置其中的参数值?
**A**:Label_smoothing 是深度学习中的一种正则化方法,其全称是 Label Smoothing Regularization(LSR),即标签平滑正则化。在传统的分类任务计算损失函数时,是将真实的 one hot 标签与神经网络的输出做相应的交叉熵计算,而 label_smoothing 是将真实的 one hot 标签做一个标签平滑的处理,使得网络学习的标签不再是一个 hard label,而是一个有概率值的 soft label,其中在类别对应的位置的概率最大,其他位置概率是一个非常小的数。在 label_smoothing 中,epsilon 参数描述了将标签软化的程度,该值越大,经过 label smoothing 后的标签向量的标签概率值越小,标签越平滑,反之,标签越趋向于 hard label。具体到数据集来说:
- ImageNet-1k:在训练 ImageNet-1k 的实验里通常将该值设置为 0.1,ResNet50 大小级别及其以上的模型在使用 label_smooting 后,精度有稳定的提升。下表展示了 ResNet50_vd 在使用 label_smoothing 前后的精度指标。
| 模型 | Use_label_smoothing(0.1) | Test acc1 |
|:--:|:--:|:--:|
| ResNet50_vd | 0 | 77.9% |
| ResNet50_vd | 1 | 78.4% |
同时,由于 label_smoohing 相当于一种正则方式,在相对较小的模型上,精度提升不明显甚至会有所下降,下表展示了 ResNet18 在 ImageNet-1k 上使用 label_smoothing 前后的精度指标。可以明显看到,在使用 label_smoothing 后,精度有所下降。
| 模型 | Use_label_smoohing(0.1) | Train acc1/acc5 | Test acc1/acc5 |
|:--:|:--:|:--:|:--:|
| ResNet18 | 0 | 69.81%/87.70% | 70.98%/89.92% |
| ResNet18 | 1 | 68.00%/86.56% | 70.81%/89.89% |
如何在较小的模型中也可以让 label-smoothing 有效,这里有一个技巧,即在 Global-Average-Pool 后接一个 1000-2000 大小的全连接层,该技巧可以与 label-smoothing 同时作用,发挥更好的效果。
- 其他数据集(默认加载 ImageNet-1k 预训练):使用 label-smooth 之后往往都会提升精度,规模越小的数据集 epsilon 值可以越大,在一些规模较小的细粒度图像中,最佳模型通常是在该值设置到 0.4-0.5 时获得的。
### Q5.6 默认的图像预处理中 random-crop 还可以调整吗?怎么调整?
**A**:在 ImageNet-1k 数据的标准预处理中,random_crop 函数中定义了 scale 和 ratio 两个值,两个值分别确定了图片 crop 的大小和图片的拉伸程度,其中 scale 的默认取值范围是 0.08-1(lower_scale-upper_scale),ratio 的默认取值范围是 3/4-4/3(lower_ratio-upper_ratio)。在非常小的网络训练中,此类数据增强会使得网络欠拟合,导致精度有所下降。为了提升网络的精度,可以使其数据增强变的更弱,即增大图片的 crop 区域或者减弱图片的拉伸变换程度。可以分别通过增大 lower_scale 的值或缩小 lower_ratio 与 upper_scale 的差距来实现更弱的图片变换。具体到数据集来说:
- ImageNet-1k:不是特别小的网络建议只用默认值,特别小的网络可以调大 lower_scale 的值(增大 crop 区域面积)或者缩小 ratio 值的范围(减弱图像伸缩变换),特别大的网络可以调小 lower_scale 的值(减小 crop 面积)或者增大 ratio 值的范围(增强图像伸缩变换)。下表列出了使用不同 lower_scale 训练 MobileNetV2_x0_25 的精度,可以看到,增大图片的 crop 区域面积后训练精度和验证精度均有提升。
| 模型 | Scale 取值范围 | Train_acc1/acc5 | Test_acc1/acc5 |
|:--:|:--:|:--:|:--:|
| MobileNetV2_x0_25 | [0.08,1] | 50.36%/72.98% | 52.35%/75.65% |
| MobileNetV2_x0_25 | [0.2,1] | 54.39%/77.08% | 53.18%/76.14% |
- 其他数据集(默认加载 ImageNet-1k 预训练):建议使用默认值,如果过拟合较严重,可以考虑调小 lower_scale 的值(减小 crop 面积)或者增大 ratio 值的范围(增强图像伸缩变换)。
### Q5.7 目前常用数据增广有哪些?如何选择?
**A**:一般来说,数据集的规模对性能影响至关重要,但是图片的标注往往比较昂贵,所以有标注的图片数量往往比较稀少,在这种情况下,数据的增广尤为重要。在训练 ImageNet-1k 的标准数据增广中,主要使用了 Random_Crop 与 Random_Flip 两种数据增广方式,然而,近些年,越来越多的数据增广方式被提出,如 cutout、mixup、cutmix、AutoAugment 等。实验表明,这些数据的增广方式可以有效提升模型的精度。具体到数据集来说:
- ImageNet-1k:下表列出了 ResNet50 在 8 种不同的数据增广方式的表现,可以看出,相比 baseline,所有的数据增广方式均有收益,其中 cutmix 是目前最有效的数据增广。更多数据增广的介绍请参考[**数据增广章节**](../advanced_tutorials/DataAugmentation.md)。
| 模型 | 数据增广方式 | Test top-1 |
|:--:|:--:|:--:|
| ResNet50 | 标准变换 | 77.31% |
| ResNet50 | Auto-Augment | 77.95% |
| ResNet50 | Mixup | 78.28% |
| ResNet50 | Cutmix | 78.39% |
| ResNet50 | Cutout | 78.01% |
| ResNet50 | Gridmask | 77.85% |
| ResNet50 | Random-Augment | 77.70% |
| ResNet50 | Random-Erasing | 77.91% |
| ResNet50 | Hide-and-Seek | 77.43% |
- 其他数据集(默认加载 ImageNet-1k 预训练):在其他数据集中除了使用 Auto-Augment,一般都会有精度的提升,Auto-Augment 会针对每一个数据集搜索的独立超参数,该超参数决定了数据如何处理,所以默认的 ImageNet-1k 的超参数并不适合所有的数据集,当然您可以使用 Random-Augment 来替代 Auto-Augment。其他策略可以正常使用,对于比较难的任务或者比较小的网络,建议不要使用较强的数据增广。
此外,多种数据增广也可以叠加使用,当数据集较为简单或数据规模较小时,叠加数据增广可以进一步提升精度。
### Q5.8 如何通过 train_acc 和 test_acc 确定调优策略?
**A**:在训练网络的过程中,通常会打印每一个 epoch 的训练集准确率和验证集准确率,二者刻画了该模型在两个数据集上的表现。通常来说,训练集的准确率反映了经过 Random-Crop 后的数据的精度,由于数据经过 Random-Crop 后,数据往往较难,所以训练集的准确率和验证集的准确率往往不是一个概念。
- ImageNet-1k:通常来说,训练集准确率比验证集准确率微高或者二者相当是比较不错的状态。如果发现训练集的准确率比验证集高很多,说明在这个任务上已经过拟合,需要在训练过程中加入更多的正则,如增大 L2Decay 的值,加入更多的数据增广策略,加入 label_smoothing 策略等;如果发现训练集的准确率比验证集低一些,说明在这个任务上可能欠拟合,需要在训练过程中减弱正则效果,如减小 L2Decay 的值,减少数据增广方式,增大图片 crop 区域面积,减弱图片拉伸变换,去除 label_smoothing 等。
- 其他数据集(默认加载 ImageNet-1k 预训练):基本和训练 ImageNet-1k 的调整策略相当,此外,在其他数据集上如果模型趋向于过拟合(train acc 远大于 test acc)状态,也可以使用更优的预训练权重,PaddleClas 为常用的网络提供了 SSLD 的蒸馏预训练权重,其比 ImageNet-1k 的权重更优,您可以优先选择。
- **【备注】** 不太建议根据 loss 来重新调整训练策略,在使用不同的数据增广后,train loss 的大小差异较大,如使用 Cutmix 或者 RandAugment 后,train loss 会大于 test loss,当数据增广策略减弱后,train loss 会小于 test loss,所以较难调整。
### Q5.9 如何通过预训练模型提升自己的数据集的精度?
**A**:在现阶段图像识别领域中,加载预训练模型来训练自己的任务已成为普遍的做法,相比从随机初始化开始训练,加载预训练模型往往可以提升特定任务的精度。一般来说,业界广泛使用的预训练模型是通过训练 128 万张图片 1000 类的 ImageNet-1k 数据集得到的,该预训练模型的 fc 层权重是是一个 k\*1000 的矩阵,其中 k 是 fc 层以前的神经元数,在加载预训练权重时,无需加载 fc 层的权重。在学习率方面,如果您的任务训练的数据集特别小(如小于 1 千张),我们建议你使用较小的初始学习率,如 0.001(batch_size:256,下同),以免较大的学习率破坏预训练权重。如果您的训练数据集规模相对较大(大于 10 万),我们建议你尝试更大的初始学习率,如 0.01 或者更大。如果目标数据集较小,也可以冻结一些浅层的权重。此外,如果训练一个特定垂类的小数据集,也可以先在相关的大的数据集上训练一个预训练权重,再在该权重上用较小的学习率微调模型。
### Q5.10 现有的策略已经让模型的精度趋于饱和,如何进一步提升特定模型的精度?
**A**:如果现有的策略不能进一步提升模型的精度,说明在现有数据集和现有的策略下,模型几乎到达饱和状态,这里提供两种进一步提升模型精度的方法。
- 挖掘相关数据:用在现有数据集上训练饱和的模型去对相关的数据做预测,将置信度较高的数据打 label 后加入训练集进一步训练,如此循环操作,可进一步提升模型的精度。
- 知识蒸馏:可以先使用一个较大的模型在该数据集上训练一个精度较高的 teacher model,然后使用该 teacher model 去教导一个 Student model,其中,Student model 即为目标模型。PaddleClas 提供了百度自研的 SSLD 知识蒸馏方案,即使在 ImageNet-1k 这么有挑战的分类任务上,其也能稳定提升 3% 以上。SSLD 知识蒸馏的的章节请参考 [**SSLD 知识蒸馏**](../advanced_tutorials/knowledge_distillation.md)。
## 第 6 期
### Q6.1: PaddleClas 的几个分支有什么区别?应该如何选择?
**A**: PaddleClas 目前共有 3 种分支:
* 开发分支:develop 分支是 PaddleClas 的开发分支,也是更新最快的分支。所有的新功能、新改动都会先在 develop 分支上进行。如果想追踪 PaddleClas 的最新进展,可以关注这个分支。这个分支主要支持动态图,会跟着 paddlepaddle 的版本一起更新。
* 稳定版本分支(如 release/2.1.3):快速更新能够让关注者了解最新进展,但也会带来不稳定性。因此在一些关键的时间点,我们会从 develop 分支中拉出分支,提供稳定的版本,最新的稳定版分支也是默认分支。需要注意,无特殊情况,我们只会维护最新的 release 稳定分支,并且一般只会修复 bug,而不更新新的特性和模型。
* 静态图分支(static):static 分支是使用静态图版本的分支,主要用来支持一些老用户的使用,也只进行一些简单维护,不会更新新的特性和模型。不建议新用户使用静态图分支。老用户如果有条件,也建议迁到动态图分支或稳定版本分支。
总的来说,如果想跟进 PaddleClas 的最新进展,建议选择 develop 分支,如果需要稳定版本,建议选择最新的稳定版本分支。
### Q6.2: 什么是静态图模式?
**A**: 静态图模式即为声明式编程模式。许多深度学习框架如 tensorflow,mxnet 等最初都使用这种模式。在静态图模式中,需要先定义好模型结构,之后框架会根据模型结构进行编译和优化,构建"计算图"。可以简单的理解为,静态图模式是"计算图"静态不变的模式。静态图的优势在于编译器一般只需要构建一次计算图,效率相对较高,缺点在于不够灵活,调试麻烦。例如在 paddle 中运行一次静态图模型,需要完整所有的运算,之后根据特定的 key 来提取输出,无法实时得到结果。
### Q6.3: 什么是动态图模式?
**A**: 动态图模式即为命令式编程模式,用户无需预先定义网络结构,每行代码都可以直接运行得到结果。相比静态图模式,动态图模式对用户更加友好,调试也更方便。此外,动态图模式的结构设计也更加灵活,可以在运行过程中随时调整结构。
PaddleClas 目前持续更新的 develop 分支和稳定版本的 release 分支,主要采用动态图模式。如果您是新用户,建议使用动态图模式来进行开发和训练。如果推理预测时有性能需求,可以在训练完成后,将动态图模型转为静态图模型提高效率。
### Q6.5: 构建分类数据集时,如何构建"背景"类别的数据?
**A**: 实际使用中,常常需要自己构建一个分类数据集来进行训练。除了所需要的类别数据之外,还需要一个额外的类别,即"背景"类别。例如做一个猫狗分类,猫为一类,狗为一类,如果我们的分类器只有两类,那么输入一张兔子的图片,也会被强制的分到这两个类别中的一个。因此在训练时,应添加一些非目标类别的数据,作为"背景"类别的数据。
构建"背景"类别的数据时,首先应从实际需求的角度出发。还是以猫狗分类器为例,如果实际测试的数据都是动物,那么"背景"类别的数据就应该包含一些除猫狗之外的动物。而如果测试的数据还包含更多类别,例如一棵树,那么"背景"类别的数据就要设置的更加丰富。
简单来说,"背景"类别的数据,要根据实际场景中可能出现的情况去收集。情况越多,需要涵盖的数据种类就越多,任务也会相应的越困难。因此实际处理中,最好能对问题进行限制,避免浪费资源和算力。