From 4fc82eef5a8de6547afb7ad66070bc1dc7895e36 Mon Sep 17 00:00:00 2001 From: gaotingquan Date: Fri, 25 Feb 2022 08:40:07 +0000 Subject: [PATCH] feat: add the doc and demo about gallery2fc add description doc and demo config --- deploy/lite_shitu/README.md | 5 +- docs/zh_CN/advanced_tutorials/gallery2fc.md | 51 +++++++++++++++++++ .../Gallery2FC_PPLCNet_x2_5.yaml | 51 +++++++++++++++++++ ppcls/utils/gallery2fc.py | 23 +++++++-- 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 docs/zh_CN/advanced_tutorials/gallery2fc.md create mode 100644 ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml diff --git a/deploy/lite_shitu/README.md b/deploy/lite_shitu/README.md index 8f5462f69..3f1cff5ea 100644 --- a/deploy/lite_shitu/README.md +++ b/deploy/lite_shitu/README.md @@ -2,7 +2,7 @@ 本教程将介绍基于[Paddle Lite](https://github.com/PaddlePaddle/Paddle-Lite) 在移动端部署PaddleClas PP-ShiTu模型的详细步骤。 -Paddle Lite是飞桨轻量化推理引擎,为手机、IOT端提供高效推理能力,并广泛整合跨平台硬件,为端侧部署及应用落地问题提供轻量化的部署方案。 +Paddle Lite是飞桨轻量化推理引擎,为手机、IoT端提供高效推理能力,并广泛整合跨平台硬件,为端侧部署及应用落地问题提供轻量化的部署方案。 ## 1. 准备环境 @@ -216,3 +216,6 @@ A1:如果已经走通了上述步骤,更换模型只需要替换 `.nb` 模 Q2:换一个图测试怎么做? A2:替换 deploy 下的测试图像为你想要测试的图像,并重新生成json配置文件(或者直接修改图像路径),使用 ADB 再次 push 到手机上即可。 + +Q3:如果需要更换模型/预训练模型/底库数据集,需要怎么做: +A:请参考文档 [基于分类方法的 PP-Shitu 移动端部署方案说明](./gallery2fc.md)。 diff --git a/docs/zh_CN/advanced_tutorials/gallery2fc.md b/docs/zh_CN/advanced_tutorials/gallery2fc.md new file mode 100644 index 000000000..466e35875 --- /dev/null +++ b/docs/zh_CN/advanced_tutorials/gallery2fc.md @@ -0,0 +1,51 @@ +# 识别模型转分类模型 + +PaddleClas 提供了 `gallery2fc.py` 工具,帮助大家将识别模型转为分类模型。 + +## 一、模型转换说明 + +### 1.1 准备底库数据、预训练模型 + +#### 1. 底库数据集 + +首先需要准备好底库数据,下面以 PaddleClas 提供饮料数据集(drink_dataset_v1.0)为例进行说明,饮料数据集获取方法: + +```shell +cd PaddleClas/ +wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/data/drink_dataset_v1.0.tar +tar -xf drink_dataset_v1.0.tar +``` + +饮料数据集的底库图片路径为 `drink_dataset_v1.0/gallery/`,底库图片列表可在 `drink_dataset_v1.0/gallery/drink_label.txt` 中查看,关于底库数据格式说明,请参考文档[数据集格式说明](../data_preparation/recognition_dataset.md#1-数据集格式说明)。 + +#### 2. 预训练模型 + +在开始转换模型前,需要准备好预训练模型,下面以量化后的 `general_PPLCNet_x2_5` 模型为例,下载预训练模型: + +```shell +cd PaddleClas/pretrained/ +wget https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/rec/models/pretrain/general_PPLCNet_x2_5_pretrained_v1.0_quant.pdparams +``` + +### 1.2 准备配置文件 + +在进行模型转换时,需要通过配置文件定义所需参数,本例中所用配置文件为 `ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml`,对于配置文件字段的说明,如下所示: + +* Global: + * pretrained_model: 预训练模型路径,无需包含 `.pdparams` 后缀名; + * image_shape: 模型输入数据尺寸,无需包含 batch size 维度; + * save_inference_dir: 转换后模型的保存路径; +* Arch: 模型结构相关定义,可参考 [配置说明](../models_training/config_description.md#3-%E8%AF%86%E5%88%AB%E6%A8%A1%E5%9E%8B); +* IndexProcess: 底库数据集相关定义 + * image_root: 底库数据集路径; + * data_file: 底库数据集列表文件路径; + +### 1.3 转换特征提取模型 + +在完成上述准备工作后,即可进行模型转换,命令如下所示: + +```python +python ppcls/utils/gallery2fc.py -c ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml +``` + +在上述命令执行完成后,转换并导出的模型保存在目录 `./inference/general_PPLCNet_x2_5_quant/` 下。在推理部署时,需要注意的是,模型的输出结果通常有多个,应选取分类结果作为模型输出,需要注意区分。 diff --git a/ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml b/ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml new file mode 100644 index 000000000..fbaefdcf5 --- /dev/null +++ b/ppcls/configs/GeneralRecognition/Gallery2FC_PPLCNet_x2_5.yaml @@ -0,0 +1,51 @@ +# global configs +Global: + pretrained_model: ./pretrained/general_PPLCNet_x2_5_pretrained_v1.0_quant + # used for static mode and model export + image_shape: [3, 224, 224] + save_inference_dir: ./inference/general_PPLCNet_x2_5_quant/inference + +# for quantizaiton or prune model +Slim: + ## for prune + quant: + name: pact + +# model architecture +Arch: + name: RecModel + + Backbone: + name: PPLCNet_x2_5 + pretrained: False + use_ssld: True + BackboneStopLayer: + name: "flatten" + Neck: + name: FC + embedding_size: 1280 + class_num: 512 + Head: + name: ArcMargin + embedding_size: 512 + class_num: 185341 + margin: 0.2 + scale: 30 + +# indexing engine config +IndexProcess: + image_root: "./drink_dataset_v1.0/gallery/" + data_file: "./drink_dataset_v1.0/gallery/drink_label.txt" + delimiter: "\t" + batch_size: 2 + transform_ops: + - ResizeImage: + resize_short: 256 + - CropImage: + size: 224 + - NormalizeImage: + scale: 1.0/255.0 + mean: [ 0.485, 0.456, 0.406 ] + std: [ 0.229, 0.224, 0.225 ] + order: '' + - ToCHWImage: diff --git a/ppcls/utils/gallery2fc.py b/ppcls/utils/gallery2fc.py index 67b08529e..e654522cf 100644 --- a/ppcls/utils/gallery2fc.py +++ b/ppcls/utils/gallery2fc.py @@ -12,10 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import paddle import cv2 +import os +import sys +__dir__ = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.abspath(os.path.join(__dir__, '../../'))) + from ppcls.arch import build_model from ppcls.utils.config import parse_config, parse_args from ppcls.utils.save_load import load_dygraph_pretrain @@ -51,12 +55,17 @@ class GalleryLayer(paddle.nn.Layer): self.gallery_images.append(image_file) gallery_docs.append(ori_line.strip()) gallery_labels.append(line[1].strip()) - self.gallery_layer = paddle.nn.Linear(embedding_size, len(self.gallery_images), bias_attr=False) + self.gallery_layer = paddle.nn.Linear( + embedding_size, len(self.gallery_images), bias_attr=False) self.gallery_layer.skip_quant = True output_label_str = "" for i, label_i in enumerate(gallery_labels): output_label_str += "{} {}\n".format(i, label_i) output_path = configs["Global"]["save_inference_dir"] + "_label.txt" + + save_dir = os.path.dirname(configs["Global"]["save_inference_dir"]) + if not os.path.exists(save_dir): + os.makedirs(save_dir) with open(output_path, "w") as f: f.write(output_label_str) @@ -71,19 +80,23 @@ class GalleryLayer(paddle.nn.Layer): embedding_size = self.configs["Arch"]["Head"]["embedding_size"] batch_index = 0 input_tensor = paddle.zeros(self.image_shape) - gallery_feature = paddle.zeros((len(self.gallery_images), embedding_size)) + gallery_feature = paddle.zeros( + (len(self.gallery_images), embedding_size)) for i, image_path in enumerate(self.gallery_images): image = cv2.imread(image_path)[:, :, ::-1] for op in preprocess_ops: image = op(image) input_tensor[batch_index] = image batch_index += 1 - if batch_index == self.batch_size or i == len(self.gallery_images) - 1: + if batch_index == self.batch_size or i == len( + self.gallery_images) - 1: batch_feature = feature_extractor(input_tensor)["features"] for j in range(batch_index): feature = batch_feature[j] - norm_feature = paddle.nn.functional.normalize(feature, axis=0) + norm_feature = paddle.nn.functional.normalize( + feature, axis=0) gallery_feature[i - batch_index + j + 1] = norm_feature + batch_index = 0 self.gallery_layer.set_state_dict({"_layer.weight": gallery_feature.T})