diff --git a/docs/images/feature_extraction_output.png b/docs/images/feature_extraction_output.png new file mode 100644 index 0000000000000000000000000000000000000000..8e8d0ddf55358455f236e3bf2c02d64fb04a44ef Binary files /dev/null and b/docs/images/feature_extraction_output.png differ diff --git a/docs/zh_CN/image_recognition_pipeline/feature_extraction.md b/docs/zh_CN/image_recognition_pipeline/feature_extraction.md index 03f0c9fafde750e19fd19825281c2a9ece9ca519..620f25865b4473af3c8c35663496f683808574a0 100644 --- a/docs/zh_CN/image_recognition_pipeline/feature_extraction.md +++ b/docs/zh_CN/image_recognition_pipeline/feature_extraction.md @@ -1,134 +1,132 @@ # 特征提取 -## 1. 特征提取简介 -特征提取是图像识别中的关键一环,它的作用是将输入的图片转化为固定维度的特征向量,用于后续的[向量检索](./vector_search.md)。好的特征需要具备相似度保持性,即在特征空间中,相似度高的图片对其特征相似度要比较高(距离比较近);相似度低的图片,其特征相似度要比较小(距离比较远)。依据应用场景的不同, 我们可以选用不同长度的实值特征(real-valued feature)或者是二值特征(binary feature)。顾名思义,实值特征的每个元素都是一个实数,而二值特征每个元素为非0即1(或者表示为-1和1),二者的差异如下图所示。 -![image](../../images/feature_compare.png) -[Deep Metric Learning](../algorithm_introduction/metric_learning.md)和 DeepHash分别用以研究如何通过深度学习的方法获得好的实值和二值特征表示。本文主要介绍如何使用PaddleClas构建一个特征学习网络, 如何进行训练、评估和推理。 +## 1. 简介 +特征提取是图像识别中的关键一环,它的作用是将输入的图片转化为固定维度的特征向量,用于后续的[向量检索](./vector_search.md)。好的特征需要具备相似度保持性,即在特征空间中,相似度高的图片对其特征相似度要比较高(距离比较近),相似度低的图片对,其特征相似度要比较小(距离比较远)。[Deep Metric Learning](../algorithm_introduction/metric_learning.md)用以研究如何通过深度学习的方法获得具有强表征能力的特征。 + ## 2. 网络结构 +为了图像识别任务的灵活定制,我们将整个网络分为Backbone、 Neck、 Head以及Loss部分,整体结构如下图所示: ![](../../images/feature_extraction_framework.png) -为了图像识别任务的灵活定制,我们将整个网络分为Backbone, Neck, Head以及Loss部分,整体结构如上图所示,下面分别介绍各自的功能: -- Backbone: 指定所使用的骨干网络。 值得注意的是,PaddleClas提供的基于ImageNet的预训练模型,最后一层的输出为1000, 我们需要依据所需的特征维度定制最后一层的输出。 -- Neck: 用以特征增强及特征维度变换。 这儿的Neck,可以是一个简单的Linear Layer,用来做特征维度变换;也可以是较复杂的FPN结构,用以做特征增强。 -- Head: 用来将feature转化为logits。 除了常用的Fc Layer外,还可以替换为cosmargin, arcmargin, circlemargin等模块 -- Loss: 指定所使用的Loss函数。 我们将Loss设计为组合loss的形式, 可以方便得将Classification Loss和Similarity Preserving Loss组合在一起 +图中各个模块的功能为: +- **Backbone**: 指定所使用的骨干网络。 值得注意的是,PaddleClas提供的基于ImageNet的预训练模型,最后一层的输出为1000, 我们需要依据所需的特征维度定制最后一层的输出。 +- **Neck**: 用以特征增强及特征维度变换。 这儿的Neck,可以是一个简单的Linear Layer,用来做特征维度变换;也可以是较复杂的FPN结构,用以做特征增强。 +- **Head**: 用来将feature转化为logits。 除了常用的Fc Layer外,还可以替换为cosmargin, arcmargin, circlemargin等模块。 +- **Loss**: 指定所使用的Loss函数。 我们将Loss设计为组合loss的形式, 可以方便得将Classification Loss和Pair_wise Loss组合在一起。 + +## 3. 通用识别模型 +在PP-Shitu中, 我们采用[PP_LCNet_x2_5](../models/PP-LCNet.md)作为骨干网络, Neck部分选用Linear Layer, Head部分选用[ArcMargin](../../../ppcls/arch/gears/arcmargin.py), Loss部分选用CELoss,详细的配置文件见[通用识别配置文件](../../../ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml)。其中,训练数据为如下7个公开数据集的汇总: +| 数据集 | 数据量 | 类别数 | 场景 | 数据集地址 | +| :------------: | :-------------: | :-------: | :-------: | :--------: | +| Aliproduct | 2498771 | 50030 | 商品 | [地址](https://retailvisionworkshop.github.io/recognition_challenge_2020/) | +| GLDv2 | 1580470 | 81313 | 地标 | [地址](https://github.com/cvdfoundation/google-landmark) | +| VeRI-Wild | 277797 | 30671 | 车辆 | [地址](https://github.com/PKU-IMRE/VERI-Wild)| +| LogoDet-3K | 155427 | 3000 | Logo | [地址](https://github.com/Wangjing1551/LogoDet-3K-Dataset) | +| iCartoonFace | 389678 | 5013 | 动漫人物 | [地址](http://challenge.ai.iqiyi.com/detail?raceId=5def69ace9fcf68aef76a75d) | +| SOP | 59551 | 11318 | 商品 | [地址](https://cvgl.stanford.edu/projects/lifted_struct/) | +| Inshop | 25882 | 3997 | 商品 | [地址](http://mmlab.ie.cuhk.edu.hk/projects/DeepFashion.html) | +| **Total** | **5M** | **185K** | ---- | ---- | -## 3. 配置文件介绍 -下面以通用图像识别为例,介绍配置文件的含义: -## 网络结构 -``` -Arch: - name: RecModel - infer_output_key: features - infer_add_softmax: False +最终的模型效果如下表所示: +| 模型 | Aliproduct | VeRI-Wild | LogoDet-3K | iCartoonFace | SOP | Inshop | Latency(ms) | +| :----------: | :---------: | :-------: | :-------: | :--------: | :--------: | :--------: | :--------: | +PP-LCNet-2.5x | 0.839 | 0.888 | 0.861 | 0.841 | 0.793 | 0.892 | 5.0 +* 采用的评测指标为:`Recall@1` +* 速度评测机器的CPU具体信息为:`Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz` +* 速度指标的评测条件为: 开启MKLDNN, 线程数设置为10 +* 预训练模型地址:[通用识别预训练模型](https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/pretrain/general_PPLCNet_x2_5_pretrained_v1.0.pdparams) - Backbone: - name: PPLCNet_x2_5 - pretrained: True - use_ssld: True - BackboneStopLayer: - name: flatten_0 - Neck: - name: FC - embedding_size: 1280 - class_num: 512 +# 4. 自定义特征提取 +自定义特征提取,是指依据自己的任务,重新训练特征提取模型。主要包含四个步骤:1)数据准备;2)模型训练;3)模型评估;4)模型推理。 +## 4.1 数据准备 +首先,需要基于任务定制自己的数据集。数据集格式参见[格式说明](https://github.com/PaddlePaddle/PaddleClas/blob/develop/docs/zh_CN/data_preparation/recognition_dataset.md#%E6%95%B0%E6%8D%AE%E9%9B%86%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E)。在启动模型训练之前,需要在配置文件中修改数据配置相关的内容, 主要包括数据集的地址以及类别数量。对应到配置文件中的位置如下所示: +``` Head: name: ArcMargin embedding_size: 512 - class_num: 185341 - margin: 0.2 - scale: 30 -``` -- **name**: 模型的名字。有别于PaddleClas提供的标准分类模型,定制化的识别模型,统一命名为RecModel -- **infer_output_key**: 推理时需要用到的Tensor的key名。 训练模型时,网络会以字典的形式输出features和logits。识别任务中,推理时只需要用到features -- **infer_output_key**: 推理时是否需要加softmax。为了和分类任务的统一后处理;分类任务推理时需要加softmax操作,识别任务不需要 -- **Backbone**: 骨干网络。 此处选用的是经过SSLD蒸馏之后的PPLCNet_x2_5预训练模型 -- **BackboneStopLayer**: 网络截断指示。用以去除预训练模型的最后一层Linear, 方便后续定制特征增强功能和特征维度变化 -- **Neck**: 用以进行特征维度转换。 此处输出为512维的特征向量 -- **Head**: 用来将feature转化为logits。 此处采用ArcMargin, 可以依据训练数据修改类别数class_num, 以及超参数margin和scale - -## Loss构成 -### 单Loss示例 + class_num: 185341 #此处表示类别数 +``` ``` -Loss: Train: - - CELoss: - weight: 1.0 - Eval: - - CELoss: - weight: 1.0 + dataset: + name: ImageNetDataset + image_root: ./dataset/ #此处表示train数据所在的目录 + cls_label_path: ./dataset/train_reg_all_data.txt #此处表示train数据集label文件的地址 ``` -此处选用的是CELoss, 结合Head部分的ArcMargin, 因此使用的是[ArcFace](https://arxiv.org/abs/1801.07698)中的算法 - -### 组合Loss示例 ``` -Loss: - Train: - - CELoss: - weight: 1.0 - - TripletLossV2: - weight: 1.0 - margin: 0.5 - Eval: - - CELoss: - weight: 1.0 -``` -此处选用的是CELoss和TripletLoss的一个组合,两者的比例为1:1. - -# 4.训练、评估、推理 -下面以`ppcls/configs/Products/ResNet50_vd_SOP.yaml`为例,介绍模型的训练、评估、推理过程 -## 4.1 数据准备 -首先,下载[SOP](https://cvgl.stanford.edu/projects/lifted_struct/)数据集, 数据文件结构如下所示: -``` -. -├── bicycle_final -├── bicycle_final.txt -├── cabinet_final -├── cabinet_final.txt -├── chair_final -...... -├── Ebay_final.txt -├── Ebay_info.txt -├── Ebay_test.txt -├── Ebay_train.txt -├── fan_final -...... -├── LICENSE + Query: + dataset: + name: VeriWild + image_root: ./dataset/Aliproduct/. #此处表示query数据集所在的目录 + cls_label_path: ./dataset/Aliproduct/val_list.txt. #此处表示query数据集label文件的地址 +``` ``` + Gallery: + dataset: + name: VeriWild + image_root: ./dataset/Aliproduct/ #此处表示gallery数据集所在的目录 + cls_label_path: ./dataset/Aliproduct/val_list.txt. #此处表示gallery数据集label文件的地址 +``` + -## 4.2 训练 +## 4.2 模型训练 - 单机单卡训练 -``` -python tools/train.py -c ppcls/configs/ResNet50_vd_SOP.yaml +```shell +export CUDA_VISIBLE_DEVICES=0 +python tools/train.py -c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml ``` - 单机多卡训练 +```shell +export CUDA_VISIBLE_DEVICES=0,1,2,3 +python -m paddle.distributed.launch \ + --gpus="0,1,2,3" tools/train.py \ + -c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml +``` +**注意:** +配置文件中默认采用`在线评估`的方式,如果你想加快训练速度,去除`在线评估`,只需要在上述命令后面,增加`-o eval_during_train=False`。训练完毕后,在output目录下会生成最终模型文件`latest`,`best_model`和训练日志文件`train.log`。其中,`best_model`用来存储当前评测指标下的最佳模型;`latest`用来存储最新生成的模型, 方便在任务中断的情况下从断点位置启动训练。 +- 断点续训: +```shell +export CUDA_VISIBLE_DEVICES=0,1,2,3 +python -m paddle.distributed.launch \ + --gpus="0,1,2,3" tools/train.py \ + -c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml \ + -o Global.checkpoint="output/RecModel/latest" ``` -python -m paddle.distributed.launch - --gpus="0,1,2,3" tools/train.py - -c ppcls/configs/ResNet50_vd_SOP.yaml -``` -训练完成之后,会在`output`目录下生成`best_model`模型文件。 -## 4.3 评估 +## 4.3 模型评估 - 单卡评估 +```shell +export CUDA_VISIBLE_DEVICES=0 +python tools/eval.py \ +-c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml \ +-o Global.pretrained_model="output/RecModel/best_model" ``` -python tools/eval.py -c ppcls/configs/ResNet50_vd_SOP.yaml -o Global.pretrained_model="output/RecModel/best_model" -``` + - 多卡评估 -``` -python -m paddle.distributed.launch - --gpus="0,1,2,3" tools/eval.py - -c ppcls/configs/ResNet50_vd_SOP.yaml +```shell +export CUDA_VISIBLE_DEVICES=0,1,2,3 +python -m paddle.distributed.launch \ + --gpus="0,1,2,3" tools/eval.py \ + -c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml \ -o Global.pretrained_model="output/RecModel/best_model" ``` -## 4.4 推理 -推理过程包括两个步骤: 1)导出推理模型, 2)获取特征向量 +**推荐:** 建议使用多卡评估。多卡评估方式可以利用多卡并行计算快速得到整体数据集的特征集合,能够加速评估的过程。 + +## 4.4 模型推理 +推理过程包括两个步骤: 1)导出推理模型; 2)获取特征向量 ### 4.4.1 导出推理模型 ``` -python tools/export_model -c ppcls/configs/ResNet50_vd_SOP.yaml -o Global.pretrained_model="output/RecModel/best_model" +python tools/export_model \ +-c ppcls/configs/GeneralRecognition/GeneralRecognition_PPLCNet_x2_5.yaml \ +-o Global.pretrained_model="output/RecModel/best_model" ``` -生成的推理模型位于`inference`目录,名字为`inference.pd*` - +生成的推理模型位于`inference`目录,里面包含三个文件,分别为`inference.pdmodel`、`inference.pdiparams`、`inference.pdiparams.info`。 +其中: `inference.pdmodel`用来存储推理模型的结构, `inference.pdiparams`和 `inference.pdiparams.info`用来存储推理模型相关的参数信息。 ### 4.4.2 获取特征向量 ``` cd deploy -python python/predict_rec.py -c configs/inference_rec.yaml -o Global.rec_inference_model_dir="../inference" +python python/predict_rec.py \ +-c configs/inference_rec.yaml \ +-o Global.rec_inference_model_dir="../inference" ``` +得到的特征输出格式如下图所示: +![](../../images/feature_extraction_output.png) + +在实际使用过程中,单纯得到特征往往并不能够满足业务的需求。如果想进一步通过特征检索来进行图像识别,可以参照文档[向量检索](./vector_search.md)。