From 1d3b8e71c90eeeca300946679814aaac41abccf0 Mon Sep 17 00:00:00 2001 From: wangshipeng01 Date: Fri, 17 Apr 2020 10:34:58 +0000 Subject: [PATCH] add finetune doc --- .../{ => flower102}/generate_flower_list.py | 30 +++-- docs/zh_CN/tutorials/data.md | 40 ++++--- docs/zh_CN/tutorials/finetune.md | 110 ++++++++++++++++++ docs/zh_CN/tutorials/getting_started.md | 20 +--- 4 files changed, 156 insertions(+), 44 deletions(-) rename dataset/{ => flower102}/generate_flower_list.py (57%) create mode 100644 docs/zh_CN/tutorials/finetune.md diff --git a/dataset/generate_flower_list.py b/dataset/flower102/generate_flower_list.py similarity index 57% rename from dataset/generate_flower_list.py rename to dataset/flower102/generate_flower_list.py index 54965162..4d707fdc 100644 --- a/dataset/generate_flower_list.py +++ b/dataset/flower102/generate_flower_list.py @@ -3,21 +3,26 @@ import numpy as np import os import sys +""".mat files data format +imagelabel.mat +jpg_name 1 2 3 ... +label 32 12 66 ... + +setid.mat +jpg_name(10 records in a class) 24 6 100 65 32 ... +label 4 ... """ -Usage: python generate_flower_list.py ./jpg train > train_list.txt - python generate_flower_list.py ./jpg valid > val_list.txt """ +Usage: + python generate_flower_list.py prefix_folder mode - + python generate_flower_list.py jpg train > train_list.txt + python generate_flower_list.py jpg valid > val_list.txt +""" data_path = sys.argv[1] imagelabels_path='./imagelabels.mat' setid_path='./setid.mat' -""" -imagelabel.mat -jpg_name 1 2 3 ... -label 32 12 66 ... -""" labels = scipy.io.loadmat(imagelabels_path) labels = np.array(labels['labels'][0]) setid = scipy.io.loadmat(setid_path) @@ -27,13 +32,6 @@ d['train'] = np.array(setid['trnid'][0]) d['valid'] = np.array(setid['valid'][0]) d['test']=np.array(setid['tstid'][0]) -""" -setid.mat -jpg_name 24 6 100 65 32 ... -label 4 ... -""" - - for id in d[sys.argv[2]]: - message = str(data_path)+"/image_"+str(id).zfill(5)+" "+str(labels[id-1]) + message = str(data_path)+"/image_"+str(id).zfill(5)+".jpg "+str(labels[id-1]) print(message) diff --git a/docs/zh_CN/tutorials/data.md b/docs/zh_CN/tutorials/data.md index 2d16f591..1fee05a1 100644 --- a/docs/zh_CN/tutorials/data.md +++ b/docs/zh_CN/tutorials/data.md @@ -3,23 +3,25 @@ --- ## 1.简介 -PaddleClas支持ImageNet1000和Flower数据分类任务。 +本文档介绍ImageNet1k和Flower102数据准备过程。 PaddleClas提供了丰富的预训练模型,支持的模型列表请参考[模型库](../models/models_intro.md) ## 2.数据集准备 数据集 | 训练集大小 | 测试集大小 | 类别数 | 备注| :------:|:---------------:|:---------------------:|:-----------:|:-----------: -Flowers|1k | 6k | 102 | -[ImageNet](http://www.image-net.org/challenges/LSVRC/2012/)|1.2M| 50k | 1000 | +[Flower102](https://www.robots.ox.ac.uk/~vgg/data/flowers/102/)|1k | 6k | 102 | +[ImageNet1k](http://www.image-net.org/challenges/LSVRC/2012/)|1.2M| 50k | 1000 | 数据格式 +PaddleClas加载PaddleClas/dataset/中的数据,通过指定data_dir和file_list来进行加载 -PaddleClas加载PaddleClas/dataset/中的数据,请将下载后的数据按下面格式组织放置到PaddleClas/dataset/中。 +### ImageNet1k +从官方下载数据后,按如下组织数据 ```bash -PaddleClas/dataset/imagenet -|_ train +PaddleClas/dataset/imagenet/ +|_ train/ | |_ n01440764 | | |_ n01440764_10026.JPEG | | |_ ... @@ -28,29 +30,39 @@ PaddleClas/dataset/imagenet | |_ n15075141 | |_ ... | |_ n15075141_9993.JPEG -|_ val +|_ val/ | |_ ILSVRC2012_val_00000001.JPEG | |_ ... | |_ ILSVRC2012_val_00050000.JPEG |_ train_list.txt |_ val_list.txt ``` +### Flower +从VGG官方网站下载后的数据,解压后包括 +jpg/ +setid.mat +imagelabels.mat +将以上文件放置在PaddleClas/dataset/flower102/下 + +通过运行generate_flower_list.py生成train_list.txt和val_list.txt ```bash -PaddleClas/dataset/flower -|_ train +python generate_flower_list.py jpg train > train_list.txt +python generate_flower_list.py jpg valid > val_list.txt + +``` +按照如下结构组织数据: + +```bash +PaddleClas/dataset/flower102/ +|_ jpg/ | |_ image_03601.jpg | |_ ... -| |_ image_07073.jpg -|_ val -| |_ image_04121.jpg -| |_ ... | |_ image_02355.jpg |_ train_list.txt |_ val_list.txt ``` - 或是通过软链接将数据从实际地址链接到PaddleClas/dataset/下 ```bash diff --git a/docs/zh_CN/tutorials/finetune.md b/docs/zh_CN/tutorials/finetune.md new file mode 100644 index 00000000..fc64e04f --- /dev/null +++ b/docs/zh_CN/tutorials/finetune.md @@ -0,0 +1,110 @@ +# 模型微调 + +本文档将介绍如何使用PaddleClas进行模型微调(finetune) +模型微调使用PaddleClas提供的预训练模型,可以节省从头训练的计算资源和时间,并提高准确率。 + +> 在使用ResNet50_vd_ssld蒸馏模型对flower102数据进行模型微调,仅需要3分钟(V100 单卡)top1即可达到94.96% + + +模型微调大致包括如下四个步骤: +- 初始化预训练模型 +- 剔除FC层 +- 更新参数 +- 新的训练策略 + + +## 初始化预训练模型 + +这里我们以ResNet50_vd和ResNet50_vd_ssld预训练模型对flower102数据集进行微调 + +ResNet50_vd: 在ImageNet1k数据集上训练 top1 acc:79.1% 模型详细信息参考[模型库](https://paddleclas.readthedocs.io/zh_CN/latest/models/ResNet_and_vd.html) + +ResNet50_vd_ssld: 在ImageNet1k数据集训练的蒸馏模型 top1: 82.4% 模型详细信息参考[模型库](https://paddleclas.readthedocs.io/zh_CN/latest/models/ResNet_and_vd.html) + +flower数据集相关信息参考[数据文档](data.md) + +指定pretrained_model参数初始化预训练模型 +ResNet50_vd: + +```bash +python -m paddle.distributed.launch \ + --selected_gpus="0" \ + tools/train.py \ + -c ./configs/finetune/ResNet50_vd_finetune.yaml + -o pretrained_model= ResNet50_vd预训练模型 +``` + +ResNet50_vd_ssld: + +```bash +python -m paddle.distributed.launch \ + --selected_gpus="0" \ + tools/train.py \ + -c ./configs/finetune/ResNet50_vd_ssld_finetune.yaml + -o pretrained_model= ResNet50_vd_ssld预训练模型 +``` + + +##剔除FC层 + +由于新的数据集类别数(Flower102:102类)和ImgaeNet1k数据(1000类)不一致,一般需要对分类网络的最后FC层进行调整,PaddleClas默认剔除所有形状不一样的层 + +```python +#excerpt from PaddleClas/ppcls/utils/save_load.py + +def load_params(exe, prog, path): + # ... + + ignore_set = set() + state = _load_state(path) + + # 剔除预训练模型和模型间形状不一致的参数 + + all_var_shape = {} + for block in prog.blocks: + for param in block.all_parameters(): + all_var_shape[param.name] = param.shape + ignore_set.update([ + name for name, shape in all_var_shape.items() + if name in state and shape != state[name].shape + ]) + + # 用于迁移学习的代码段已被省略 ... + + if len(ignore_set) > 0: + for k in ignore_set: + if k in state: + # 剔除参数 + del state[k] + fluid.io.set_program_state(prog, state) +``` +在将shape不一致的层进行剔除正确加载预训练模型后,我们要选择需要更新的参数来让优化器进行参数更新。 + +## 更新参数 + +首先,分类网络中的卷积层大致可以分为 + +- ```浅层卷积层```:用于提取基础特征 +- ```深层卷积层```:用于提取抽象特征 +- ```FC层```:进行特征组合 + +其次,在衡量数据集大小差别和数据集的相似程度后,我们一般遵循如下的规则进行参数更新: + +- 1. 新的数据集很小,在类别,具体种类上和原数据很像。由于新数据集很小,这里可能出现过拟合的问题;由于数据很像,可以认为预训练模型的深层特征仍然会起作用,只需要训练一个最终的```FC层```即可。 +- 2. 新的数据集很大,在类别,具体种类上和原数据很像。推荐训练网络中全部层的参数。 +- 3. 新的数据集很小但是和原数据不相像,可以冻结网络中初始层的参数更新```stop_gradient=True```,对较高层进行重新训练。 +- 4. 新的数据集很大但是和原数据不相像,这时候预训练模型可能不会生效,需要从头训练。 + +PaddleClas模型微调默认更新所有层参数。 + +## 新的训练策略 + +1. 学习率 +由于已经加载了预训练模型,对于从头训练的随机初始化参数来讲,模型中的参数已经具备了一定的分类能力,所以建议使用与从头训练相比更小的学习率,例如减小10倍。 +2. 类别数和总图片数调整为新数据集数据 +3. 调整训练轮数,由于不需要从头开始训练,一般相对减少模型微调的训练轮数 + +## 模型微调结果 + +在使用ResNet50_vd预训练模型对flower102数据进行模型微调后,top1 acc 达到 92.71% +在使用ResNet50_vd_ssld预训练模型对flower102数据进行模型微调后,top1 acc 达到94.96% diff --git a/docs/zh_CN/tutorials/getting_started.md b/docs/zh_CN/tutorials/getting_started.md index 8546ae36..0945114b 100644 --- a/docs/zh_CN/tutorials/getting_started.md +++ b/docs/zh_CN/tutorials/getting_started.md @@ -16,15 +16,17 @@ export PYTHONPATH=path_to_PaddleClas:$PYTHONPATH PaddleClas 提供模型训练与评估脚本:tools/train.py和tools/eval.py ### 2.1 模型训练 +以flower102数据为例按如下方式启动模型训练,flower数据集准备请参考[数据集准备](./data.md) + ```bash # PaddleClas通过launch方式启动多卡多进程训练 # 通过设置FLAGS_selected_gpus 指定GPU运行卡号 python -m paddle.distributed.launch \ --selected_gpus="0,1,2,3" \ - --log_dir=log_ResNet50 \ + --log_dir=log_ResNet50_vd \ tools/train.py \ - -c ./configs/ResNet/ResNet50.yaml + -c ./configs/flower.yaml ``` - 输出日志示例如下: @@ -40,7 +42,7 @@ python -m paddle.distributed.launch \ --selected_gpus="0,1,2,3" \ --log_dir=log_ResNet50_vd \ tools/train.py \ - -c ./configs/ResNet/ResNet50_vd.yaml \ + -c ./configs/flower.yaml \ -o use_mix=1 ``` @@ -54,18 +56,8 @@ epoch:0 train step:522 loss:1.6330 lr:0.100000 elapse:0.210 或是直接修改模型对应的yaml配置文件,具体配置参数参考[配置文档](config.md)。 ### 2.3 模型微调 +模型微调请参照[模型微调文档](./finetune.md) -您可以通过如下命令进行模型微调,通过指定--pretrained_model参数加载预训练模型 - -```bash -python -m paddle.distributed.launch \ - --selected_gpus="0,1,2,3" \ - --log_dir=log_ResNet50_vd \ - train.py \ - -c ../configs/ResNet/ResNet50_vd.yaml \ - -o pretrained_model= 预训练模型路径\ - -``` ### 2.2 模型评估 -- GitLab