Skip to content

Latest commit

 

History

History
386 lines (342 loc) · 16.2 KB

develop_a_new_model.md

File metadata and controls

386 lines (342 loc) · 16.2 KB

English | 中文

FastDeploy集成新模型流程

在FastDeploy里面新增一个模型,包括增加C++/Python的部署支持。 本文以YOLOv7Face模型为例,介绍使用FastDeploy做外部模型集成,具体包括如下3步。

步骤 说明 创建或修改的文件
1 在fastdeploy/vision相应任务模块增加模型实现 yolov7face.h、yolov7face.cc、preprocessor.h、preprocess.cc、postprocessor.h、postprocessor.cc、vision.h
2 通过pybind完成Python接口绑定 yolov7face_pybind.cc
3 实现Python相应调用接口 yolov7face.py、__init__.py

在完成上述3步之后,一个外部模型就集成好了。
如果您想为FastDeploy贡献代码,还需要为新增模型添加测试代码、说明文档和代码注释,可在测试中查看。

模型集成

1、模型准备

在集成外部模型之前,先要将训练好的模型(.pt,.pdparams 等)转换成FastDeploy支持部署的模型格式(.onnx,.pdmodel)。多数开源仓库会提供模型转换脚本,可以直接利用脚本做模型的转换。例如yolov7face官方库提供的export.py文件, 若官方库未提供转换导出文件,则需要手动编写转换脚本,如torchvision没有提供转换脚本,因此手动编写转换脚本,下文中将 torchvison.models.resnet50 转换为 resnet50.onnx,参考代码如下:

import torch
import torchvision.models as models
model = models.resnet50(pretrained=True)
batch_size = 1  #批处理大小
input_shape = (3, 224, 224)   #输入数据,改成自己的输入shape
model.eval()
x = torch.randn(batch_size, *input_shape)	# 生成张量
export_onnx_file = "resnet50.onnx"			# 目的ONNX文件名
torch.onnx.export(model,
                    x,
                    export_onnx_file,
                    opset_version=12,
                    input_names=["input"],	# 输入名
                    output_names=["output"],	# 输出名
                    dynamic_axes={"input":{0:"batch_size"},  # 批处理变量
                                    "output":{0:"batch_size"}})

执行上述脚本将会得到 resnet50.onnx 文件。

2、CPP代码实现

2.1、前处理类实现

  • 创建preprocessor.h文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/preprocess.h (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/precessor.h)
    • 创建内容
      • 首先在preprocess.h中创建 Yolov7FacePreprocess 类,之后声明RunpreprocessLetterBox构造函数,以及必要的变量及其setget方法,具体的代码细节请参考preprocess.h
class FASTDEPLOY_DECL Yolov7FacePreprocessor {
 public:
  Yolov7FacePreprocessor(...);
  bool Run(...);
 protected:
  bool Preprocess(...);
  void LetterBox(...);
};
  • 创建preprocessor.cc文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/preprocessor.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/preprocessor.cc)
    • 创建内容
      • preprocessor.cc中实现preprocessor.h中声明函数的具体逻辑,其中Preprocess需要参考源官方库的前后处理逻辑复现,preprocessor每个函数具体逻辑如下,具体的代码请参考preprocessor.cc
Yolov7FacePreprocessor::Yolov7FacePreprocessor(...) {
  // 构造函数逻辑
  // 全局变量赋值
}
bool Yolov7FacePreprocessor::Run() {
  // 执行前处理
  // 根据传入图片数量对每张图片进行处理,通过循环的方式将每张图片传入Preprocess函数进行预处理,
  // 即Preprocess为处理单元,Run方法为每张图片调用处理单元处理
  return true;
}
bool Yolov7FacePreprocessor::Preprocess(FDMat* mat, FDTensor* output,
                                        std::map<std::string, std::array<float, 2>>* im_info) {
// 前处理逻辑
// 1. LetterBox 2. convert and permute 3. 处理结果存入 FDTensor类中  
  return true;
}
void Yolov7FacePreprocessor::LetterBox(FDMat* mat) {
  //LetterBox
  return true;
}

2.2、后处理类实现

  • 创建postprocessor.h文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/postprocessor.h (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/postprocessor.h)
    • 创建内容
      • 首先在postprocess.h中创建 Yolov7FacePostprocess 类,之后声明Run构造函数,以及必要的变量及其setget方法,具体的代码细节请参考postprocessor.h
class FASTDEPLOY_DECL Yolov7FacePostprocessor {
 public:
  Yolov7FacePostprocessor(...);
  bool Run(...);
};
  • 创建postprocessor.cc文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/postprocessor.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/postprocessor.cc)
    • 创建内容
      • postprocessor.cc中实现postprocessor.h中声明函数的具体逻辑,其中Postprocess需要参考源官方库的前后处理逻辑复现,postprocessor每个函数具体逻辑如下,具体的代码请参考postprocessor.cc
Yolov7FacePostprocessor::Yolov7FacePostprocessor(...) {
  // 构造函数逻辑
  // 全局变量赋值
}
bool Yolov7FacePostprocessor::Run() {
  // 后处理逻辑
  // 1. Padding 2. Choose box by conf_threshold 3. NMS 4. 结果存入 FaceDetectionResult类
  return true;
}

2.3、YOLOv7Face实现

  • 创建yolov7face.h文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/yolov7face.h (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/模型名.h)
    • 创建内容
      • 首先在yolov7face.h中创建 YOLOv7Face 类并继承FastDeployModel父类,之后声明PredictBatchPredictInitialize构造函数,以及必要的变量及其get方法,具体的代码细节请参考yolov7face.h
class FASTDEPLOY_DECL YOLOv7Face : public FastDeployModel {
 public:
  YOLOv7Face(...);
  virtual bool Predict(...);
  virtual bool BatchPredict(...);
 protected:
  bool Initialize();
  Yolov7FacePreprocessor preprocessor_;
  Yolov7FacePostprocessor postprocessor_;
};
  • 创建yolov7face.cc文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/yolov7face.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/模型名.cc)
    • 创建内容
      • yolov7face.cc中实现yolov7face.h中声明函数的具体逻辑,YOLOv7Face每个函数具体逻辑如下,具体的代码请参考yolov7face.cc
YOLOv7Face::YOLOv7Face(...) {
  // 构造函数逻辑
  // 1. 指定 Backend 2. 设置RuntimeOption 3. 调用Initialize()函数
}
bool YOLOv7Face::Initialize() {
  // 初始化逻辑
  // 1. 全局变量赋值 2. 调用InitRuntime()函数
  return true;
}
bool YOLOv7Face::Predict(const cv::Mat& im, FaceDetectionResult* result) {
  std::vector<FaceDetectionResult> results;
  if (!BatchPredict({im}, &results)) {
    return false;
  }
  *result = std::move(results[0]);
  return true;
}
// Predict是对单张图片进行预测,通过将含有一张图片的数组送入BatchPredict实现
bool YOLOv7Face::BatchPredict(const std::vector<cv::Mat>& images, std::vector<FaceDetectionResult>* result) {
  Preprocess(...)
  Infer(...)
  Postprocess(...)
  return true;
}
// BatchPredict为对批量图片进行预测,接收一个含有若干张图片的动态数组vector

  • vision.h文件中加入新增模型文件
    • 修改位置
      • FastDeploy/fastdeploy/vision.h
    • 修改内容
#ifdef ENABLE_VISION
#include "fastdeploy/vision/facedet/contrib/yolov7face.h"
#endif

3、Python接口封装

3.1、Pybind部分

  • 创建Pybind文件
    • 创建位置
      • FastDeploy/fastdeploy/vision/facedet/contrib/yolov7face/yolov7face_pybind.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名/模型名_pybind.cc)
    • 创建内容
      • 利用Pybind将C++中的函数变量绑定到Python中,具体代码请参考yolov7face_pybind.cc
void BindYOLOv7Face(pybind11::module& m) {
  pybind11::class_<vision::facedet::YOLOv7Face, FastDeployModel>(
      m, "YOLOv7Face")
      .def(pybind11::init<std::string, std::string, RuntimeOption, ModelFormat>())
      .def("predict", ...)
      .def("batch_predict", ...)
      .def_property_readonly("preprocessor", ...)
      .def_property_readonly("postprocessor", ...);
  pybind11::class_<vision::facedet::Yolov7FacePreprocessor>(
    m, "Yolov7FacePreprocessor")
    .def(pybind11::init<>())
    .def("run", ...)
    .def_property("size", ...)
    .def_property("padding_color_value", ...)
    .def_property("is_scale_up", ...);
  pybind11::class_<vision::facedet::Yolov7FacePostprocessor>(
    m, "Yolov7FacePostprocessor")
    .def(pybind11::init<>())
    .def("run", ...)
    .def_property("conf_threshold", ...)
    .def_property("nms_threshold", ...);
}
  • 调用Pybind函数
    • 修改位置
      • FastDeploy/fastdeploy/vision/facedet/facedet_pybind.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/任务名称}_pybind.cc)
    • 修改内容
void BindYOLOv7Face(pybind11::module& m);
void BindFaceDet(pybind11::module& m) {
  auto facedet_module =
      m.def_submodule("facedet", "Face detection models.");
  BindYOLOv7Face(facedet_module);
}

3.2、python部分

  • 创建yolov7face.py文件
    • 创建位置
      • FastDeploy/python/fastdeploy/vision/facedet/contrib/yolov7face.py (FastDeploy/Python代码存放位置/fastdeploy/视觉模型/任务名称/外部模型/模型名.py)
    • 创建内容
      • 创建YOLOv7Face类继承自FastDeployModel、preprocess以及postprocess类,实现 \_\_init\_\_、Pybind绑定的函数(如predict())、以及对Pybind绑定的全局变量进行赋值和获取的函数,具体代码请参考yolov7face.py
class YOLOv7Face(FastDeployModel):
    def __init__(self, ...):
        self._model = C.vision.facedet.YOLOv7Face(...)
    def predict(self, input_image):
        return self._model.predict(input_image)
    def batch_predict(self, images):
        return self._model.batch_predict(images)
    @property
    def preprocessor(self):
        return self._model.preprocessor
    @property
    def postprocessor(self):
        return self._model.postprocessor

class Yolov7FacePreprocessor():
    def __init__(self, ...):
        self._model = C.vision.facedet.Yolov7FacePreprocessor(...)
    def run(self, input_ims):
        return self._preprocessor.run(input_ims)
    @property
    def size(self):
        return self._preprocessor.size
    @property
    def padding_color_value(self):
        return self._preprocessor.padding_color_value
    ...

class Yolov7FacePreprocessor():
    def __init__(self, ...):
        self._model = C.vision.facedet.Yolov7FacePostprocessor(...)
    def run(self, ...):
        return self._postprocessor.run(...)
    @property
    def conf_threshold(self):
        return self._postprocessor.conf_threshold
    @property
    def nms_threshold(self):
        return self._postprocessor.nms_threshold
    ...

  • 导入YOLOv7Face、Yolov7FacePreprocessor、Yolov7facePostprocessor类
    • 修改位置
      • FastDeploy/python/fastdeploy/vision/facedet/__init__.py (FastDeploy/Python代码存放位置/fastdeploy/视觉模型/任务名称/__init__.py)
    • 修改内容
from .contrib.yolov7face import *

4、测试

编译

  • C++
    • 位置:FastDeploy/
mkdir build & cd build
cmake .. -DENABLE_ORT_BACKEND=ON -DENABLE_VISION=ON -DCMAKE_INSTALL_PREFIX=${PWD/fastdeploy-0.0.3
-DENABLE_PADDLE_BACKEND=ON -DENABLE_TRT_BACKEND=ON -DWITH_GPU=ON -DTRT_DIRECTORY=/PATH/TO/TensorRT/
make -j8
make install

编译会得到 build/fastdeploy-0.0.3/。

  • Python
    • 位置:FastDeploy/python/
export TRT_DIRECTORY=/PATH/TO/TensorRT/    # 如果用TensorRT 需要填写TensorRT所在位置,并开启 ENABLE_TRT_BACKEND
export ENABLE_TRT_BACKEND=ON
export WITH_GPU=ON
export ENABLE_PADDLE_BACKEND=ON
export ENABLE_OPENVINO_BACKEND=ON
export ENABLE_VISION=ON
export ENABLE_ORT_BACKEND=ON
python setup.py build
python setup.py bdist_wheel
cd dist
pip install fastdeploy_gpu_python-版本号-cpxx-cpxxm-系统架构.whl

5、示例代码开发

  • 创建位置: FastDeploy/examples/vision/facedet/yolov7face/ (FastDeploy/示例目录/视觉模型/任务名称/模型名/)
  • 创建目录结构
.
├── cpp
│   ├── CMakeLists.txt
│   ├── infer.cc    // C++ 版本测试代码
│   └── README.md   // C++版本使用文档
├── python
│   ├── infer.py    // Python 版本测试代码
│   └── README.md   // Python版本使用文档
└── README.md   // ResNet 模型集成说明文档
  • C++
    • 编写CmakeLists文件、C++ 代码以及 README.md 内容请参考cpp/
    • 编译 infer.cc
      • 位置:FastDeploy/examples/vision/facedet/yolov7face/cpp/
mkdir build & cd build
cmake .. -DFASTDEPLOY_INSTALL_DIR=/PATH/TO/FastDeploy/build/fastdeploy-0.0.3/
make
  • Python
    • Python 代码以及 README.md 内容请参考python/

为代码添加注释

为了方便用户理解代码,我们需要为新增代码添加注释,添加注释方法可参考如下示例。

  • C++ 代码 您需要在resnet.h文件中为函数和变量增加注释,有如下三种注释方式,具体可参考yolov7face.h
/** \brief Predict for the input "im", the result will be saved in "result".
*
* \param[in] im Input image for inference.
* \param[in] result Saving the inference result.
*/
virtual bool Predict(const cv::Mat& im, FaceDetectionResult* result);
/// Tuple of (width, height)
std::vector<int> size;
/*! @brief Initialize for YOLOv7Face model, assign values to the global variables and call InitRuntime()
*/
bool Initialize();
  • Python 代码 你需要为yolov7face.py文件中的函数和变量增加适当的注释,示例如下,具体可参考yolov7face.py
    def predict(self, input_image):
         """Detect the location and key points of human faces from an input image
         :param input_image: (numpy.ndarray)The input image data, 3-D array with layout HWC, BGR format
         :return: FaceDetectionResult
         """
         return self._model.predict(input_image)

对于集成模型过程中的其他文件,您也可以对实现的细节添加适当的注释说明。