diff --git a/docs/zh_CN/tutorials/getting_started_retrieval.md b/docs/zh_CN/tutorials/getting_started_retrieval.md index e78907d4c9851d75f3030a4761729e251c418dad..06dcc11c77af238aec346c4432cec570e2ac0c4f 100644 --- a/docs/zh_CN/tutorials/getting_started_retrieval.md +++ b/docs/zh_CN/tutorials/getting_started_retrieval.md @@ -1,195 +1,248 @@ # 开始使用 -## 注意: 本文主要介绍基于检索方式的识别 --- -请参考[安装指南](./install.md)配置运行环境,并根据[快速开始](./quick_start_new_user.md)文档准备flowers102数据集,本章节下面所有的实验均以flowers102数据集为例。 +首先,请参考[安装指南](./install.md)配置运行环境。 -PaddleClas目前支持的训练/评估环境如下: +PaddleClas图像检索部分目前支持的训练/评估环境如下: ```shell └── CPU/单卡GPU    ├── Linux    └── Windows +``` +## 目录 + +* [1. 数据准备与处理](#数据准备与处理) +* [2. 基于单卡GPU上的训练与评估](#基于单卡GPU上的训练与评估) + * [2.1 模型训练](#模型训练) + * [2.2 模型恢复训练](#模型恢复训练) + * [2.3 模型评估](#模型评估) +* [3. 导出inference模型](#导出inference模型) + + +## 1. 数据的准备与处理 -└── 多卡GPU - └── Linux +* 进入PaddleClas目录。 + +```bash +## linux or mac, $path_to_PaddleClas表示PaddleClas的根目录,用户需要根据自己的真实目录修改 +cd $path_to_PaddleClas ``` -## 1. 基于CPU/单卡GPU上的训练与评估 +* 进入`dataset`目录,为了快速体验PaddleClas图像检索模块,此处使用的数据集为[CUB_200_2011](http://vision.ucsd.edu/sites/default/files/WelinderEtal10_CUB-200.pdf),其是一个包含200类鸟的细粒度鸟类数据集。首先,下载CUB_200_2011数据集,下载方式请参考[官网](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html)。 -在基于CPU/单卡GPU上训练与评估,推荐使用`tools/train.py`与`tools/eval.py`脚本。关于Linux平台多卡GPU环境下的训练与评估,请参考[2. 基于Linux+GPU的模型训练与评估](#2)。 +```shell +# linux or mac +cd dataset - -### 1.1 模型训练 +# 将下载后的数据拷贝到此目录 +cp {数据存放的路径}/CUB_200_2011.tgz . -准备好配置文件之后,可以使用下面的方式启动训练。 +# 解压 +tar -xzvf CUB_200_2011.tgz -``` -python tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.use_gpu=True +#进入CUB_200_2011目录 +cd CUB_200_2011 ``` -其中,`-c`用于指定配置文件的路径,`-o`用于指定需要修改或者添加的参数,其中`-o use_gpu=True`表示使用GPU进行训练。如果希望使用CPU进行训练,则需要将`use_gpu`设置为`False`。 +该数据集在用作图像检索任务时,通常将前100类当做训练集,后100类当做测试集,所以此处需要将下载的数据集做一些后处理,来更好的适应PaddleClas的图像检索训练。 -更详细的训练配置,也可以直接修改模型对应的配置文件。具体配置参数参考[配置文档](config.md)。 +```shell +#新建train和test目录 +mkdir train && mkdir test + +#将数据分成训练集和测试集,前100类作为训练集,后100类作为测试集 +ls images | awk -F "." '{if(int($1)<101)print "mv images/"$0" train/"int($1)}' | sh +ls images | awk -F "." '{if(int($1)>100)print "mv images/"$0" test/"int($1)}' | sh -训练期间也可以通过VisualDL实时观察loss变化,详见[VisualDL](../extension/VisualDL.md)。 +#生成train_list和test_list +tree -r -i -f train | grep jpg | awk -F "/" '{print $0" "int($2) " "NR}' > train_list.txt +tree -r -i -f test | grep jpg | awk -F "/" '{print $0" "int($2) " "NR}' > test_list.txt +``` -### 1.2 模型微调 +至此,现在已经得到`CUB_200_2011`的训练集(`train`目录)、测试集(`test`目录)、`train_list.txt`、`test_list.txt`。 -根据自己的数据集路径设置好配置文件后,可以通过加载预训练模型的方式进行微调,如下所示。 +数据处理完毕后,`CUB_200_2011`中的`train`目录下应有如下结构: ``` -python tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Arch.Backbone.pretrained=True +├── 1 +│   ├── Black_Footed_Albatross_0001_796111.jpg +│   ├── Black_Footed_Albatross_0002_55.jpg + ... +├── 10 +│   ├── Red_Winged_Blackbird_0001_3695.jpg +│   ├── Red_Winged_Blackbird_0005_5636.jpg +... ``` -其中`-o Arch.Backbone.pretrained`用于设置是否加载预训练模型;为True时,会自动下载预训练模型,并加载。 - - -### 1.3 模型恢复训练 - -如果训练任务因为其他原因被终止,也可以加载断点权重文件,继续训练: +`train_list.txt`应为: ``` -python tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.checkpoints="./output/RecModel/epoch_5" \ +train/99/Ovenbird_0137_92639.jpg 99 1 +train/99/Ovenbird_0136_92859.jpg 99 2 +train/99/Ovenbird_0135_93168.jpg 99 3 +train/99/Ovenbird_0131_92559.jpg 99 4 +train/99/Ovenbird_0130_92452.jpg 99 5 +... ``` -只需要在继续训练时设置`Global.checkpoints`参数即可,表示加载的断点权重文件路径,使用该参数会同时加载保存的断点权重和学习率、优化器等信息。 +其中,分隔符为空格" ", 三列数据的含义分别是训练数据的路径、训练数据的label信息、训练数据的unique id。 - -### 1.4 模型评估 +测试集格式与训练集格式相同。 -可以通过以下命令进行模型评估。 +**注意**: -```bash -python tools/eval.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.pretrained_model="./output/RecModel/best_model"\ +* 当gallery dataset和query dataset相同时,为了去掉检索得到的第一个数据(检索图片本身无须评估),每个数据需要对应一个unique id,用于后续评测mAP、recall@1等指标。关于gallery dataset与query dataset的解析请参考[图像检索数据集介绍](#图像检索数据集介绍), 关于mAP、recall@1等评测指标请参考[图像检索评价指标](#图像检索评价指标)。 + +返回`PaddleClas`根目录 + +```shell +# linux or mac +cd ../../ ``` -其中`-o Global.pretrained_model`用于设置需要进行评估的模型的路径 - -## 2. 基于Linux+GPU的模型训练与评估 + +## 2. 基于单卡GPU上的训练与评估 -如果机器环境为Linux+GPU,那么推荐使用`paddle.distributed.launch`启动模型训练脚本(`tools/train.py`)、评估脚本(`tools/eval.py`),可以更方便地启动多卡训练与评估。 +在基于单卡GPU上训练与评估,推荐使用`tools/train.py`与`tools/eval.py`脚本。 + ### 2.1 模型训练 -参考如下方式启动模型训练,`paddle.distributed.launch`通过设置`gpus`指定GPU运行卡号: +准备好配置文件之后,可以使用下面的方式启动图像检索任务的训练。PaddleClas训练图像检索任务的方法是度量学习,关于度量学习的解析请参考[度量学习](#度量学习)。 -```bash -# PaddleClas通过launch方式启动多卡多进程训练 +``` +python3 tools/train.py \ + -c ./ppcls/configs/quick_start/MobileNetV1_retrieval.yaml \ + -o Arch.Backbone.pretrained=True \ + -o Global.device=gpu +``` -export CUDA_VISIBLE_DEVICES=0,1,2,3 +其中,`-c`用于指定配置文件的路径,`-o`用于指定需要修改或者添加的参数,其中`-o Arch.Backbone.pretrained=True`表示Backbone部分使用预训练模型,此外,`Arch.Backbone.pretrained`也可以指定具体的模型权重文件的地址,使用时需要换成自己的预训练模型权重文件的路径。`-o Global.device=gpu`表示使用GPU进行训练。如果希望使用CPU进行训练,则需要将`Global.device`设置为`cpu`。 -python -m paddle.distributed.launch \ - --gpus="0,1,2,3" \ - tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml -``` +更详细的训练配置,也可以直接修改模型对应的配置文件。具体配置参数参考[配置文档](config.md)。 -### 2.2 模型微调 +运行上述命令,可以看到输出日志,示例如下: -根据自己的数据集配置好配置文件之后,可以加载预训练模型进行微调,如下所示。 + ``` + ... + [Train][Epoch 1/50][Avg]CELoss: 6.59110, TripletLossV2: 0.54044, loss: 7.13154 + ... + [Eval][Epoch 1][Avg]recall1: 0.46962, recall5: 0.75608, mAP: 0.21238 + ... + ``` +此处配置文件的Backbone是MobileNetV1,如果想使用其他Backbone,可以重写参数`Arch.Backbone.name`,比如命令中增加`-o Arch.Backbone.name={其他Backbone}`。此外,由于不同模型`Neck`部分的输入维度不同,更换Backbone后可能需要改写此处的输入大小,改写方式类似替换Backbone的名字。 -``` -export CUDA_VISIBLE_DEVICES=0,1,2,3 +在训练Loss部分,此处使用了[CELoss](../../../ppcls/loss/celoss.py)和[TripletLossV2](../../../ppcls/loss/triplet.py),配置文件如下: -python -m paddle.distributed.launch \ - --gpus="0,1,2,3" \ - tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Arch.Backbone.pretrained=True ``` +Loss: + Train: + - CELoss: + weight: 1.0 + - TripletLossV2: + weight: 1.0 + margin: 0.5 +``` + +最终的总Loss是所有Loss的加权和,其中weight定义了特定Loss在最终总Loss的权重。如果想替换其他Loss,也可以在配置文件中更改Loss字段,目前支持的Loss请参考[Loss](../../../ppcls/loss)。 -### 2.3 模型恢复训练 + +### 2.2 模型恢复训练 -如果训练任务因为其他原因被终止,也可以加载断点权重文件继续训练。 +如果训练任务因为其他原因被终止,也可以加载断点权重文件,继续训练: ``` -export CUDA_VISIBLE_DEVICES=0,1,2,3 - -python -m paddle.distributed.launch \ - --gpus="0,1,2,3" \ - tools/train.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.checkpoints="./output/RecModel/epoch_5" \ +python3 tools/train.py \ + -c ./ppcls/configs/quick_start/MobileNetV1_retrieval.yaml \ + -o Global.checkpoints="./output/RecModel/epoch_5" \ + -o Global.device=gpu ``` -### 2.4 模型评估 +其中配置文件不需要做任何修改,只需要在继续训练时设置`Global.checkpoints`参数即可,表示加载的断点权重文件路径,使用该参数会同时加载保存的断点权重和学习率、优化器等信息。 + +**注意**: + +* `-o Global.checkpoints`参数无需包含断点权重文件的后缀名,上述训练命令会在训练过程中生成如下所示的断点权重文件,若想从断点`5`继续训练,则`Global.checkpoints`参数只需设置为`"./output/RecModel/epoch_5"`,PaddleClas会自动补充后缀名。 + + ```shell + output/ + └── RecModel + ├── best_model.pdopt + ├── best_model.pdparams + ├── best_model.pdstates + ├── epoch_1.pdopt + ├── epoch_1.pdparams + ├── epoch_1.pdstates + . + . + . + ``` + + +### 2.3 模型评估 可以通过以下命令进行模型评估。 ```bash -python. -m paddle.distributed.launch \ - --gpus="0,1,2,3" \ - tools/eval.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.pretrained_model="./output/RecModel/best_model"\ +python3 tools/eval.py \ + -c ./ppcls/configs/quick_start/MobileNetV1_retrieval.yaml \ + -o Global.pretrained_model=./output/RecModel/best_model ``` - -## 3. 使用inference模型进行模型推理 -### 3.1 导出推理模型 +上述命令将使用`./configs/quick_start/MobileNetV1_retrieval.yaml`作为配置文件,对上述训练得到的模型`./output/RecModel/best_model`进行评估。你也可以通过更改配置文件中的参数来设置评估,也可以通过`-o`参数更新配置,如上所示。 -通过导出inference模型,PaddlePaddle支持使用预测引擎进行预测推理。接下来介绍如何用预测引擎进行推理: -首先,对训练好的模型进行转换: +可配置的部分评估参数说明如下: +* `Arch.name`:模型名称 +* `Global.pretrained_model`:待评估的模型的预训练模型文件路径,不同于`Global.Backbone.pretrained`,此处的预训练模型是整个模型的权重,而`Global.Backbone.pretrained`只是Backbone部分的权重。当需要做模型评估时,需要加载整个模型的权重。 +* `Metric.Eval`:待评估的指标,默认评估recall@1、recall@5、mAP。当你不准备评测某一项指标时,可以将对应的试标从配置文件中删除;当你想增加某一项评测指标时,也可以参考[Metric](../../../ppcls/metric/metrics.py)部分在配置文件`Metric.Eval`中添加相关的指标。 -```bash -python tools/export_model.py \ - -c ppcls/configs/quick_start/ResNet50_vd_finetune_retrieval.yaml \ - -o Global.pretrained_model=./output/RecModel/best_model \ - -o Global.save_inference_dir=./inference \ -``` -其中,`--Global.pretrained_model`用于指定模型文件路径,该路径仍无需包含模型文件后缀名(如[1.3 模型恢复训练](#1.3)),`--Global.save_inference_dir`用于指定转换后模型的存储路径。 -若`--save_inference_dir=./inference`,则会在`inference`文件夹下生成`inference.pdiparams`、`inference.pdmodel`和`inference.pdiparams.info`文件。 +**注意:** -### 3.2 构建底库 -通过检索方式来进行图像识别,需要构建底库。 -首先, 将生成的模型拷贝到deploy目录下,并进入deploy目录: -```bash -mv ./inference ./deploy -cd deploy -``` +* 在加载待评估模型时,需要指定模型文件的路径,但无需包含文件后缀名,PaddleClas会自动补齐`.pdparams`的后缀,如[2.2 模型恢复训练](#模型恢复训练)。 -其次,构建底库,命令如下: -```bash -python python/build_gallery.py \ - -c configs/build_flowers.yaml \ - -o Global.rec_inference_model_dir="./inference" \ - -o IndexProcess.index_path="../dataset/flowers102/index" \ - -o IndexProcess.image_root="../dataset/flowers102/" \ - -o IndexProcess.data_file="../dataset/flowers102/train_list.txt" -``` -其中 -+ `Global.rec_inference_model_dir`:3.1生成的推理模型的路径 -+ `IndexProcess.index_path`:gallery库index的路径 -+ `IndexProcess.image_root`:gallery库图片的根目录 -+ `IndexProcess.data_file`:gallery库图片的文件列表 -执行完上述命令之后,会在`../dataset/flowers102`目录下面生成`index`目录,index目录下面包含3个文件`index.data`, `1index.graph`, `info.json` +* Metric learning任务一般不评测TopkAcc。 -### 3.3 推理预测 + +## 3. 导出inference模型 -通过3.1生成模型结构文件(`inference.pdmodel`)和模型权重文件(`inference.pdiparams`),通过3.2构建好底库, 然后可以使用预测引擎进行推理: +通过导出inference模型,PaddlePaddle支持使用预测引擎进行预测推理。对训练好的模型进行转换: ```bash -python python/predict_rec.py \ - -c configs/inference_flowers.yaml \ - -o Global.infer_imgs="./images/image_00002.jpg" \ - -o Global.rec_inference_model_dir="./inference" \ - -o Global.use_gpu=True \ - -o Global.use_tensorrt=False +python3 tools/export_model.py \ + -c ./ppcls/configs/quick_start/MobileNetV1_retrieval.yaml \ + -o Global.pretrained_model=output/RecModel/best_model \ + -o Global.save_inference_dir=./inference ``` -其中: -+ `Global.infer_imgs`:待预测的图片文件路径,如 `./images/image_00002.jpg` -+ `Global.rec_inference_model_dir`:预测模型文件路径,如 `./inference/` -+ `Global.use_tensorrt`:是否使用 TesorRT 预测引擎,默认值:`True` -+ `Global.use_gpu`:是否使用 GPU 预测,默认值:`True` -执行完上述命令之后,会得到输入图片对应的特征信息, 本例子中特征维度为2048, 日志显示如下: -``` -(1, 2048) -[[0.00033124 0.00056205 0.00032261 ... 0.00030939 0.00050748 0.00030271]] -``` +其中,`Global.pretrained_model`用于指定模型文件路径,该路径仍无需包含模型文件后缀名(如[2.2 模型恢复训练](#模型恢复训练))。当执行后,会在当前目录下生成`./inference`目录,目录下包含`inference.pdiparams`、`inference.pdiparams.info`、`inference.pdmodel`文件。`Global.save_inference_dir`可以指定导出inference模型的路径。此处保存的inference模型在embedding特征层做了截断,即模型最终的输出为n维embedding特征。 + +上述命令将生成模型结构文件(`inference.pdmodel`)和模型权重文件(`inference.pdiparams`),然后可以使用预测引擎进行推理。使用inference模型推理的流程可以参考[基于Python预测引擎预测推理](@shengyu)。 + +## 基础知识 + +图像检索指的是给定一个包含特定实例(例如特定目标、场景、物品等)的查询图像,图像检索旨在从数据库图像中找到包含相同实例的图像。不同于图像分类,图像检索解决的是一个开集问题,训练集中可能不包含被识别的图像的类别。图像检索的整体流程为:首先将图像中表示为一个合适的特征向量,其次,对这些图像的特征向量用欧式距离或余弦距离进行最近邻搜索以找到底库中相似的图像,最后,可以使用一些后处理技术对检索结果进行微调,确定被识别图像的类别等信息。所以,决定一个图像检索算法性能的关键在于图像对应的特征向量的好坏。 + + +- 度量学习(Metric Learning) + +度量学习研究如何在一个特定的任务上学习一个距离函数,使得该距离函数能够帮助基于近邻的算法 (kNN、k-means等) 取得较好的性能。深度度量学习 (Deep Metric Learning )是度量学习的一种方法,它的目标是学习一个从原始特征到低维稠密的向量空间 (嵌入空间,embedding space) 的映射,使得同类对象在嵌入空间上使用常用的距离函数 (欧氏距离、cosine距离等) 计算的距离比较近,而不同类的对象之间的距离则比较远。深度度量学习在计算机视觉领域取得了非常多的成功的应用,比如人脸识别、商品识别、图像检索、行人重识别等。 + + +- 图像检索数据集介绍 + + - 训练集合(train dataset):用来训练模型,使模型能够学习该集合的图像特征。 + - 底库数据集合(gallery dataset):用来提供图像检索任务中的底库数据,该集合可与训练集或测试集相同,也可以不同,当与训练集相同时,测试集的类别体系应与训练集的类别体系相同。 + - 测试集合(query dataset):用来测试模型的好坏,通常要对测试集的每一张测试图片进行特征提取,之后和底库数据的特征进行距离匹配,得到识别结果,后根据识别结果计算整个测试集的指标。 + + +- 图像检索评价指标 + + + - 召回率(recall):表示预测为正例且标签为正例的个数 / 标签为正例的个数 + + - recall@1:检索的top-1中预测正例且标签为正例的个数 / 标签为正例的个数 + - recall@5:检索的top-5中所有预测正例且标签为正例的个数 / 标签为正例的个数 + + + - 平均检索精度(mAP) + + - AP: AP指的是不同召回率上的正确率的平均值 + - mAP: 测试集中所有图片对应的AP的的平均值 \ No newline at end of file diff --git a/ppcls/configs/quick_start/MobileNetV1_retrieval.yaml b/ppcls/configs/quick_start/MobileNetV1_retrieval.yaml new file mode 100644 index 0000000000000000000000000000000000000000..17c8f1ba81b2ec134de2cf8cceb1f8237d1bd43a --- /dev/null +++ b/ppcls/configs/quick_start/MobileNetV1_retrieval.yaml @@ -0,0 +1,159 @@ +# global configs +Global: + checkpoints: null + pretrained_model: null + output_dir: ./output/ + device: gpu + class_num: 101 + save_interval: 5 + eval_during_train: True + eval_interval: 1 + epochs: 50 + print_batch_step: 10 + use_visualdl: False + # used for static mode and model export + image_shape: [3, 224, 224] + save_inference_dir: ./inference + eval_mode: retrieval + +# model architecture +Arch: + name: RecModel + infer_output_key: features + infer_add_softmax: False + + Backbone: + name: MobileNetV1 + pretrained: False + BackboneStopLayer: + name: flatten_0 + Neck: + name: FC + embedding_size: 1024 + class_num: 512 + Head: + name: ArcMargin + embedding_size: 512 + class_num: 101 + margin: 0.15 + scale: 30 + +# loss function config for traing/eval process +Loss: + Train: + - CELoss: + weight: 1.0 + - TripletLossV2: + weight: 1.0 + margin: 0.5 + Eval: + - CELoss: + weight: 1.0 + +Optimizer: + name: Momentum + momentum: 0.9 + lr: + name: MultiStepDecay + learning_rate: 0.01 + milestones: [20, 30, 40] + gamma: 0.5 + verbose: False + last_epoch: -1 + regularizer: + name: 'L2' + coeff: 0.0005 + +# data loader for train and eval +DataLoader: + Train: + dataset: + name: VeriWild + image_root: ./dataset/CUB_200_2011/ + cls_label_path: ./dataset/CUB_200_2011/train_list.txt + transform_ops: + - DecodeImage: + to_rgb: True + channel_first: False + - ResizeImage: + size: 224 + - RandFlipImage: + flip_code: 1 + - NormalizeImage: + scale: 0.00392157 + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + order: '' + - RandomErasing: + EPSILON: 0.5 + sl: 0.02 + sh: 0.4 + r1: 0.3 + mean: [0., 0., 0.] + sampler: + name: DistributedRandomIdentitySampler + batch_size: 64 + num_instances: 2 + drop_last: False + shuffle: True + loader: + num_workers: 4 + use_shared_memory: True + + Eval: + Query: + dataset: + name: VeriWild + image_root: ./dataset/CUB_200_2011/ + cls_label_path: ./dataset/CUB_200_2011/test_list.txt + transform_ops: + - DecodeImage: + to_rgb: True + channel_first: False + - ResizeImage: + size: 224 + - NormalizeImage: + scale: 0.00392157 + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + order: '' + sampler: + name: DistributedBatchSampler + batch_size: 64 + drop_last: False + shuffle: False + loader: + num_workers: 4 + use_shared_memory: True + + Gallery: + dataset: + name: VeriWild + image_root: ./dataset/CUB_200_2011/ + cls_label_path: ./dataset/CUB_200_2011/test_list.txt + transform_ops: + - DecodeImage: + to_rgb: True + channel_first: False + - ResizeImage: + size: 224 + - NormalizeImage: + scale: 1.0/255.0 + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + order: '' + sampler: + name: DistributedBatchSampler + batch_size: 64 + drop_last: False + shuffle: False + loader: + num_workers: 4 + use_shared_memory: True + +Metric: + Eval: + - Recallk: + topk: [1, 5] + - mAP: {} +