未验证 提交 0ae8aa6d 编写于 作者: W Wei Shengyu 提交者: GitHub

Merge pull request #863 from cuicheng01/develop

Update distillation and ImageAugment docs
......@@ -115,8 +115,6 @@ SSLD的流程图如下图所示。
### 3.4 实验过程中的一些问题
#### 3.4.1 bn的计算方法
* 在预测过程中,batch norm的平均值与方差是通过加载预训练模型得到(设其模式为test mode)。在训练过程中,batch norm是通过统计当前batch的信息(设其模式为train mode),与历史保存信息进行滑动平均计算得到,在蒸馏任务中,我们发现通过train mode,即教师模型的bn实时变化的模式,去指导学生模型,比通过test mode蒸馏,得到的学生模型性能更好一些,下面是一组实验结果。因此我们在该蒸馏方案中,均使用train mode去得到教师模型的soft label。
|Teacher Model | Teacher Top1 | Student Model | Student Top1|
......@@ -124,26 +122,6 @@ SSLD的流程图如下图所示。
| ResNet50_vd | 82.35% | MobileNetV3_large_x1_0 | 76.00% |
| ResNet50_vd | 82.35% | MobileNetV3_large_x1_0 | 75.84% |
#### 3.4.2 模型名字冲突问题的解决办法
* 在蒸馏过程中,如果遇到命名冲突的问题,如使用ResNet50_vd蒸馏ResNet34_vd,此时直接训练,会提示相同variable名称不匹配的问题,此时可以通过给学生模型或者教师模型中的变量名添加名称的方式解决该问题,如下所示。在训练之后也可以直接根据后缀区分学生模型和教师模型各自包含的参数。
```python
def net(self, input, class_dim=1000):
student = ResNet34_vd(postfix_name="_student")
out_student = student.net( input, class_dim=class_dim )
teacher = ResNet50_vd()
out_teacher = teacher.net( input, class_dim=class_dim )
out_teacher.stop_gradient = True
return out_teacher, out_student
```
* 训练完成后,可以通过批量重名的方式修改学生模型的参数,以上述代码为例,批量重命名的命令行如下。
```shell
cd model_final # enter model dir
for var in ./*_student; do cp "$var" "../student_model/${var%_student}"; done # batch copy and rename
```
## 四、蒸馏模型的应用
......@@ -151,7 +129,7 @@ for var in ./*_student; do cp "$var" "../student_model/${var%_student}"; done #
* 中间层学习率调整。蒸馏得到的模型的中间层特征图更加精细化,因此将蒸馏模型预训练应用到其他任务中时,如果采取和之前相同的学习率,容易破坏中间层特征。而如果降低整体模型训练的学习率,则会带来训练收敛速度慢的问题。因此我们使用了中间层学习率调整的策略。具体地:
* 针对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_1x0,由于其包含15个block,我们设置每3个block共享一个学习率倍数参数,因此需要共5个学习率倍数的参数,最终发现在分类和检测任务中,`[0.25,0.25,0.5,0.5,0.75]`的中间层学习率倍数能够带来更大的精度收益。
* 对于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%的提升)。
......@@ -201,64 +179,57 @@ for var in ./*_student; do cp "$var" "../student_model/${var%_student}"; done #
### 5.1 参数配置
实战部分提供了SSLD蒸馏的示例,在`ppcls/modeling/architectures/distillation_models.py`中提供了`ResNeXt101_32x16d_wsl`蒸馏`ResNet50_vd``ResNet50_vd_ssld`蒸馏`MobileNetV3_large_x1_0`的示例,`configs/Distillation`里分别提供了二者的配置文件,用户可以在`tools/run.sh`里直接替换配置文件的路径即可使用。
#### ResNeXt101_32x16d_wsl蒸馏ResNet50_vd
`ResNeXt101_32x16d_wsl`蒸馏`ResNet50_vd`的配置如下,其中`pretrained model`指定了`ResNeXt101_32x16d_wsl`(教师模型)的预训练模型的路径,该路径也可以同时指定教师模型与学生模型的预训练模型的路径,用户只需要同时传入二者预训练的路径即可(配置中的注释部分)。
实战部分提供了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
ARCHITECTURE:
name: 'ResNeXt101_32x16d_wsl_distill_ResNet50_vd'
pretrained_model: "./pretrained/ResNeXt101_32x16d_wsl_pretrained/"
# pretrained_model:
# - "./pretrained/ResNeXt101_32x16d_wsl_pretrained/"
# - "./pretrained/ResNet50_vd_pretrained/"
use_distillation: True
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"
```
#### ResNet50_vd_ssld蒸馏MobileNetV3_large_x1_0
类似于`ResNeXt101_32x16d_wsl`蒸馏`ResNet50_vd``ResNet50_vd_ssld`蒸馏`MobileNetV3_large_x1_0`的配置如下:
在参数配置中,`freeze_params_list`中需要指定模型是否需要冻结参数,`models`中需要指定Teacher模型和Student模型,其中Teacher模型需要加载预训练模型。用户可以直接在此处更改模型。
```yaml
ARCHITECTURE:
name: 'ResNet50_vd_distill_MobileNetV3_large_x1_0'
pretrained_model: "./pretrained/ResNet50_vd_ssld_pretrained/"
# pretrained_model:
# - "./pretrained/ResNet50_vd_ssld_pretrained/"
# - "./pretrained/ResNet50_vd_pretrained/"
use_distillation: True
```
### 5.2 启动命令
当用户配置完训练环境后,类似于训练其他分类任务,只需要将`tools/run.sh`中的配置文件替换成为相应的蒸馏配置文件即可。
当用户配置完训练环境后,类似于训练其他分类任务,只需要将`tools/train.sh`中的配置文件替换成为相应的蒸馏配置文件即可。
其中`run.sh`中的内容如下:
其中`train.sh`中的内容如下:
```bash
export PYTHONPATH=path_to_PaddleClas:$PYTHONPATH
python -m paddle.distributed.launch \
--selected_gpus="0,1,2,3" \
--log_dir=R50_vd_distill_MV3_large_x1_0 \
--log_dir=mv3_large_x1_0_distill_mv3_small_x1_0 \
tools/train.py \
-c ./configs/Distillation/R50_vd_distill_MV3_large_x1_0.yaml
-c ./ppcls/configs/ImageNet/Distillation/mv3_large_x1_0_distill_mv3_small_x1_0.yaml
```
运行`run.sh`
运行`train.sh`
```bash
sh tools/run.sh
sh tools/train.sh
```
### 5.3 注意事项
* 用户在使用SSLD蒸馏之前,首先需要在目标数据集上训练一个教师模型,该教师模型用于指导学生模型在该数据集上的训练。
* 在用户使用SSLD蒸馏的时候需要将配置文件中的`use_distillation`设置为`True`,另外由于学生模型学习带有知识信息的soft-label,所以需要关掉label_smoothing选项,即将`ls_epsilon`中的值设置在[0,1]之外。
* 如果学生模型没有加载预训练模型,训练的其他超参数可以参考该学生模型在ImageNet-1k上训练的超参数,如果学生模型加载了预训练模型,学习率可以调整到原来的1/10或者1/100。
* 在SSLD蒸馏的过程中,学生模型只学习soft-label导致训练目标变的更加复杂,建议可以适当的调小`l2_decay`的值来获得更高的验证集准确率。
......
......@@ -65,10 +65,6 @@ PaddleClas中集成了上述所有的数据增广策略,每种数据增广策
PaddleClas中`AutoAugment`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import ImageNetPolicy
from ppcls.data.imaug import transform
size = 224
......@@ -104,10 +100,6 @@ for f in fnames:
PaddleClas中`RandAugment`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import RandAugment
from ppcls.data.imaug import transform
size = 224
......@@ -154,10 +146,6 @@ Cutout 可以理解为 Dropout 的一种扩展操作,不同的是 Dropout 是
PaddleClas中`Cutout`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import Cutout
from ppcls.data.imaug import transform
size = 224
......@@ -190,11 +178,6 @@ for f in fnames:
PaddleClas中`RandomErasing`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import ToCHWImage
from ppcls.data.imaug import RandomErasing
from ppcls.data.imaug import transform
size = 224
......@@ -233,11 +216,6 @@ for f in fnames:
PaddleClas中`HideAndSeek`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import ToCHWImage
from ppcls.data.imaug import HideAndSeek
from ppcls.data.imaug import transform
size = 224
......@@ -290,11 +268,6 @@ PaddleClas中`GridMask`的使用方法如下所示。
```python
from data.imaug import DecodeImage
from data.imaug import ResizeImage
from data.imaug import ToCHWImage
from data.imaug import GridMask
from data.imaug import transform
size = 224
......@@ -341,11 +314,6 @@ Mixup 是最先提出的图像混叠增广方案,其原理简单、方便实
PaddleClas中`Mixup`的使用方法如下所示。
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import ToCHWImage
from ppcls.data.imaug import transform
from ppcls.data.imaug import MixupOperator
size = 224
......@@ -383,11 +351,6 @@ new_batch = mixup_op(batch)
`Mixup` 直接对两幅图进行相加不一样,`Cutmix` 是从一幅图中随机裁剪出一个 `ROI`,然后覆盖当前图像中对应的区域,代码实现如下所示:
```python
from ppcls.data.imaug import DecodeImage
from ppcls.data.imaug import ResizeImage
from ppcls.data.imaug import ToCHWImage
from ppcls.data.imaug import transform
from ppcls.data.imaug import CutmixOperator
size = 224
......@@ -450,11 +413,10 @@ new_batch = cutmix_op(batch)
`RandAugment`的图像增广方式的配置如下,其中用户需要指定其中的参数`num_layers``magnitude`,默认的数值分别是`2``5``RandAugment`是在uint8的数据格式上转换的,所以其处理过程应该放在归一化操作(`NormalizeImage`)之前。
```yaml
transforms:
```yaml
transform_ops:
- DecodeImage:
to_rgb: True
to_np: False
channel_first: False
- RandCropImage:
size: 224
......@@ -464,11 +426,10 @@ new_batch = cutmix_op(batch)
num_layers: 2
magnitude: 5
- NormalizeImage:
scale: 1./255.
scale: 1.0/255.0
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
order: ''
- ToCHWImage:
```
### Cutout
......@@ -476,24 +437,22 @@ new_batch = cutmix_op(batch)
`Cutout`的图像增广方式的配置如下,其中用户需要指定其中的参数`n_holes``length`,默认的数值分别是`1``112`。类似其他图像裁剪类的数据增广方式,`Cutout`既可以在uint8格式的数据上操作,也可以在归一化(`NormalizeImage`)后的数据上操作,此处给出的是在归一化后的操作。
```yaml
transforms:
transform_ops:
- DecodeImage:
to_rgb: True
to_np: False
channel_first: False
- RandCropImage:
size: 224
- RandFlipImage:
flip_code: 1
- NormalizeImage:
scale: 1./255.
scale: 1.0/255.0
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
order: ''
- Cutout:
n_holes: 1
length: 112
- ToCHWImage:
```
### Mixup
......@@ -501,51 +460,48 @@ new_batch = cutmix_op(batch)
`Mixup`的图像增广方式的配置如下,其中用户需要指定其中的参数`alpha`,默认的数值是`0.2`。类似其他图像混合类的数据增广方式,`Mixup`是在图像做完数据处理后将每个batch内的数据做图像混叠,将混叠后的图像和标签输入网络中训练,所以其是在图像数据处理(图像变换、图像裁剪)后操作。另外,在配置文件中,需要将`use_mix`参数设置为`True`
```yaml
transforms:
transform_ops:
- DecodeImage:
to_rgb: True
to_np: False
channel_first: False
- RandCropImage:
size: 224
- RandFlipImage:
flip_code: 1
- NormalizeImage:
scale: 1./255.
scale: 1.0/255.0
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
order: ''
- ToCHWImage:
mix:
batch_transform_ops:
- MixupOperator:
alpha: 0.2
```
## 7.2 启动命令
当用户配置完训练环境后,类似于训练其他分类任务,只需要将`tools/run.sh`中的配置文件替换成为相应的数据增广方式的配置文件即可。
当用户配置完训练环境后,类似于训练其他分类任务,只需要将`tools/train.sh`中的配置文件替换成为相应的数据增广方式的配置文件即可。
其中`run.sh`中的内容如下:
其中`train.sh`中的内容如下:
```bash
export PYTHONPATH=path_to_PaddleClas:$PYTHONPATH
python -m paddle.distributed.launch \
python3 -m paddle.distributed.launch \
--selected_gpus="0,1,2,3" \
--log_dir=ResNet50_Cutout \
tools/train.py \
-c ./configs/DataAugment/ResNet50_Cutout.yaml
-c ./ppcls/configs/ImageNet/DataAugment/ResNet50_Cutout.yaml
```
运行`run.sh`
运行`train.sh`
```bash
sh tools/run.sh
sh tools/train.sh
```
## 7.3 注意事项
* 在使用图像混叠类的数据处理时,需要将配置文件中的`use_mix`设置为`True`,另外由于图像混叠时需对label进行混叠,无法计算训练数据的准确率,所以在训练过程中没有打印训练准确率。
* 由于图像混叠时需对label进行混叠,无法计算训练数据的准确率,所以在训练过程中没有打印训练准确率。
* 在使用数据增广后,由于训练数据更难,所以训练损失函数可能较大,训练集的准确率相对较低,但其有拥更好的泛化能力,所以验证集的准确率相对较高。
......
......@@ -79,14 +79,12 @@ python3 tools/train.py \
python3 tools/train.py \
-c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml \
-o Global.checkpoints="./output/MobileNetV3_large_x1_0/epoch_5" \
-o Optimizer.lr.last_epoch=5 \
-o Global.device=gpu
```
其中配置文件不需要做任何修改,只需要在继续训练时设置`checkpoints`参数即可,表示加载的断点权重文件路径,使用该参数会同时加载保存的断点权重和学习率、优化器等信息。
**注意**
* 参数`-o Optimizer.lr.last_epoch=5`表示将上一次训练轮次数记为`5`,即本次训练轮次数从`6`开始计算,该值默认为-1,表示本次训练轮次数从`0`开始计算。
* `-o Global.checkpoints`参数无需包含断点权重文件的后缀名,上述训练命令会在训练过程中生成如下所示的断点权重文件,若想从断点`5`继续训练,则`Global.checkpoints`参数只需设置为`"../output/MobileNetV3_large_x1_0/epoch_5"`,PaddleClas会自动补充后缀名。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册