|
| 1 | +# FastDeploy外部模型集成指引 |
| 2 | + |
| 3 | +在FastDeploy里面新增一个模型,包括增加C++/Python的部署支持。 本文以torchvision v0.12.0中的ResNet50模型为例,介绍使用FastDeploy做外部[模型集成](#modelsupport),具体包括如下3步。 |
| 4 | + |
| 5 | +| 步骤 | 说明 | 创建或修改的文件 | |
| 6 | +|:------:|:-------------------------------------:|:---------------------------------------------:| |
| 7 | +| [1](#step2) | 在fastdeploy/vision相应任务模块增加模型实现 | resnet.h、resnet.cc、vision.h | |
| 8 | +| [2](#step4) | 通过pybind完成Python接口绑定 | resnet_pybind.cc、classification_pybind.cc | |
| 9 | +| [3](#step5) | 实现Python相应调用接口 | resnet.py、\_\_init\_\_.py | |
| 10 | + |
| 11 | +在完成上述3步之后,一个外部模型就集成好了。 |
| 12 | +<br /> |
| 13 | +如果您想为FastDeploy贡献代码,还需要为新增模型添加测试代码、说明文档和代码注释,可在[测试](#test)中查看。 |
| 14 | +## 模型集成 <span id="modelsupport"></span> |
| 15 | + |
| 16 | +### 模型准备 <span id="step1"></span> |
| 17 | + |
| 18 | + |
| 19 | +在集成外部模型之前,先要将训练好的模型(.pt,.pdparams 等)转换成FastDeploy支持部署的模型格式(.onnx,.pdmodel)。多数开源仓库会提供模型转换脚本,可以直接利用脚本做模型的转换。由于torchvision没有提供转换脚本,因此手动编写转换脚本,本文中将 `torchvison.models.resnet50` 转换为 `resnet50.onnx`, 参考代码如下: |
| 20 | + |
| 21 | +```python |
| 22 | +import torch |
| 23 | +import torchvision.models as models |
| 24 | +model = models.resnet50(pretrained=True) |
| 25 | +batch_size = 1 #批处理大小 |
| 26 | +input_shape = (3, 224, 224) #输入数据,改成自己的输入shape |
| 27 | +model.eval() |
| 28 | +x = torch.randn(batch_size, *input_shape) # 生成张量 |
| 29 | +export_onnx_file = "resnet50.onnx" # 目的ONNX文件名 |
| 30 | +torch.onnx.export(model, |
| 31 | + x, |
| 32 | + export_onnx_file, |
| 33 | + opset_version=12, |
| 34 | + input_names=["input"], # 输入名 |
| 35 | + output_names=["output"], # 输出名 |
| 36 | + dynamic_axes={"input":{0:"batch_size"}, # 批处理变量 |
| 37 | + "output":{0:"batch_size"}}) |
| 38 | +``` |
| 39 | +执行上述脚本将会得到 `resnet50.onnx` 文件。 |
| 40 | + |
| 41 | +### C++部分 <span id="step2"></span> |
| 42 | +* 创建`resnet.h`文件 |
| 43 | + * 创建位置 |
| 44 | + * FastDeploy/fastdeploy/vision/classification/contrib/resnet.h (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名.h) |
| 45 | + * 创建内容 |
| 46 | + * 首先在resnet.h中创建 ResNet类并继承FastDeployModel父类,之后声明`Predict`、`Initialize`、`Preprocess`、`Postprocess`和`构造函数`,以及必要的变量,具体的代码细节请参考[resnet.h](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-69128489e918f305c208476ba793d8167e77de2aa7cadf5dcbac30da448bd28e)。 |
| 47 | + |
| 48 | +```C++ |
| 49 | +class FASTDEPLOY_DECL ResNet : public FastDeployModel { |
| 50 | + public: |
| 51 | + ResNet(...); |
| 52 | + virtual bool Predict(...); |
| 53 | + private: |
| 54 | + bool Initialize(); |
| 55 | + bool Preprocess(...); |
| 56 | + bool Postprocess(...); |
| 57 | +}; |
| 58 | +``` |
| 59 | +
|
| 60 | +* 创建`resnet.cc`文件 |
| 61 | + * 创建位置 |
| 62 | + * FastDeploy/fastdeploy/vision/classification/contrib/resnet.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名.cc) |
| 63 | + * 创建内容 |
| 64 | + * 在`resnet.cc`中实现`resnet.h`中声明函数的具体逻辑,其中`PreProcess` 和 `PostProcess`需要参考源官方库的前后处理逻辑复现,ResNet每个函数具体逻辑如下,具体的代码请参考[resnet.cc](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-d229d702de28345253a53f2a5839fd2c638f3d32fffa6a7d04d23db9da13a871)。 |
| 65 | +
|
| 66 | +```C++ |
| 67 | +ResNet::ResNet(...) { |
| 68 | + // 构造函数逻辑 |
| 69 | + // 1. 指定 Backend 2. 设置RuntimeOption 3. 调用Initialize()函数 |
| 70 | +} |
| 71 | +bool ResNet::Initialize() { |
| 72 | + // 初始化逻辑 |
| 73 | + // 1. 全局变量赋值 2. 调用InitRuntime()函数 |
| 74 | + return true; |
| 75 | +} |
| 76 | +bool ResNet::Preprocess(Mat* mat, FDTensor* output) { |
| 77 | +// 前处理逻辑 |
| 78 | +// 1. Resize 2. BGR2RGB 3. Normalize 4. HWC2CHW 5. 处理结果存入 FDTensor类中 |
| 79 | + return true; |
| 80 | +} |
| 81 | +bool ResNet::Postprocess(FDTensor& infer_result, ClassifyResult* result, int topk) { |
| 82 | + //后处理逻辑 |
| 83 | + // 1. Softmax 2. Choose topk labels 3. 结果存入 ClassifyResult类 |
| 84 | + return true; |
| 85 | +} |
| 86 | +bool ResNet::Predict(cv::Mat* im, ClassifyResult* result, int topk) { |
| 87 | + Preprocess(...) |
| 88 | + Infer(...) |
| 89 | + Postprocess(...) |
| 90 | + return true; |
| 91 | +} |
| 92 | +``` |
| 93 | +<span id="step3"></span> |
| 94 | +* 在`vision.h`文件中加入新增模型文件 |
| 95 | + * 修改位置 |
| 96 | + * FastDeploy/fastdeploy/vision.h |
| 97 | + * 修改内容 |
| 98 | + |
| 99 | +```C++ |
| 100 | +#ifdef ENABLE_VISION |
| 101 | +#include "fastdeploy/vision/classification/contrib/resnet.h" |
| 102 | +#endif |
| 103 | +``` |
| 104 | + |
| 105 | + |
| 106 | +### Pybind部分 <span id="step4"></span> |
| 107 | + |
| 108 | +* 创建Pybind文件 |
| 109 | + * 创建位置 |
| 110 | + * FastDeploy/fastdeploy/vision/classification/contrib/resnet_pybind.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/外部模型/模型名_pybind.cc) |
| 111 | + * 创建内容 |
| 112 | + * 利用Pybind将C++中的函数变量绑定到Python中,具体代码请参考[resnet_pybind.cc](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-270af0d65720310e2cfbd5373c391b2110d65c0f4efa547f7b7eeffcb958bdec)。 |
| 113 | +```C++ |
| 114 | +void BindResNet(pybind11::module& m) { |
| 115 | + pybind11::class_<vision::classification::ResNet, FastDeployModel>( |
| 116 | + m, "ResNet") |
| 117 | + .def(pybind11::init<std::string, std::string, RuntimeOption, ModelFormat>()) |
| 118 | + .def("predict", ...) |
| 119 | + .def_readwrite("size", &vision::classification::ResNet::size) |
| 120 | + .def_readwrite("mean_vals", &vision::classification::ResNet::mean_vals) |
| 121 | + .def_readwrite("std_vals", &vision::classification::ResNet::std_vals); |
| 122 | +} |
| 123 | +``` |
| 124 | +
|
| 125 | +* 调用Pybind函数 |
| 126 | + * 修改位置 |
| 127 | + * FastDeploy/fastdeploy/vision/classification/classification_pybind.cc (FastDeploy/C++代码存放位置/视觉模型/任务名称/任务名称}_pybind.cc) |
| 128 | + * 修改内容 |
| 129 | +```C++ |
| 130 | +void BindResNet(pybind11::module& m); |
| 131 | +void BindClassification(pybind11::module& m) { |
| 132 | + auto classification_module = |
| 133 | + m.def_submodule("classification", "Image classification models."); |
| 134 | + BindResNet(classification_module); |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | + |
| 139 | +### Python部分 <span id="step5"></span> |
| 140 | + |
| 141 | + |
| 142 | +* 创建`resnet.py`文件 |
| 143 | + * 创建位置 |
| 144 | + * FastDeploy/python/fastdeploy/vision/classification/contrib/resnet.py (FastDeploy/Python代码存放位置/fastdeploy/视觉模型/任务名称/外部模型/模型名.py) |
| 145 | + * 创建内容 |
| 146 | + * 创建ResNet类继承自FastDeployModel,实现 `\_\_init\_\_`、Pybind绑定的函数(如`predict()`)、以及`对Pybind绑定的全局变量进行赋值和获取的函数`,具体代码请参考[resnet.py](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-a4dc5ec2d450e91f1c03819bf314c238b37ac678df56d7dea3aab7feac10a157)。 |
| 147 | + |
| 148 | +```python |
| 149 | + |
| 150 | +class ResNet(FastDeployModel): |
| 151 | + def __init__(self, ...): |
| 152 | + self._model = C.vision.classification.ResNet(...) |
| 153 | + def predict(self, input_image, topk=1): |
| 154 | + return self._model.predict(input_image, topk) |
| 155 | + @property |
| 156 | + def size(self): |
| 157 | + return self._model.size |
| 158 | + @size.setter |
| 159 | + def size(self, wh): |
| 160 | + ... |
| 161 | +``` |
| 162 | +<span id="step6"></span> |
| 163 | +* 导入ResNet类 |
| 164 | + * 修改位置 |
| 165 | + * FastDeploy/python/fastdeploy/vision/classification/\_\_init\_\_.py (FastDeploy/Python代码存放位置/fastdeploy/视觉模型/任务名称/\_\_init\_\_.py) |
| 166 | + * 修改内容 |
| 167 | + |
| 168 | +```Python |
| 169 | +from .contrib.resnet import ResNet |
| 170 | +``` |
| 171 | + |
| 172 | +## 测试 <span id="test"></span> |
| 173 | +### 编译 |
| 174 | + * C++ |
| 175 | + * 位置:FastDeploy/ |
| 176 | + |
| 177 | +``` |
| 178 | +mkdir build & cd build |
| 179 | +cmake .. -DENABLE_ORT_BACKEND=ON -DENABLE_VISION=ON -DCMAKE_INSTALL_PREFIX=${PWD/fastdeploy-0.0.3 |
| 180 | +-DENABLE_PADDLE_BACKEND=ON -DENABLE_TRT_BACKEND=ON -DWITH_GPU=ON -DTRT_DIRECTORY=/PATH/TO/TensorRT/ |
| 181 | +make -j8 |
| 182 | +make install |
| 183 | +``` |
| 184 | + |
| 185 | + 编译会得到 build/fastdeploy-0.0.3/。 |
| 186 | + |
| 187 | + * Python |
| 188 | + * 位置:FastDeploy/python/ |
| 189 | + |
| 190 | +``` |
| 191 | +export TRT_DIRECTORY=/PATH/TO/TensorRT/ # 如果用TensorRT 需要填写TensorRT所在位置,并开启 ENABLE_TRT_BACKEND |
| 192 | +export ENABLE_TRT_BACKEND=ON |
| 193 | +export WITH_GPU=ON |
| 194 | +export ENABLE_PADDLE_BACKEND=ON |
| 195 | +export ENABLE_OPENVINO_BACKEND=ON |
| 196 | +export ENABLE_VISION=ON |
| 197 | +export ENABLE_ORT_BACKEND=ON |
| 198 | +python setup.py build |
| 199 | +python setup.py bdist_wheel |
| 200 | +cd dist |
| 201 | +pip install fastdeploy_gpu_python-版本号-cpxx-cpxxm-系统架构.whl |
| 202 | +``` |
| 203 | + |
| 204 | +### 编写测试代码 |
| 205 | + * 创建位置: FastDeploy/examples/vision/classification/resnet/ (FastDeploy/示例目录/视觉模型/任务名称/模型名/) |
| 206 | + * 创建目录结构 |
| 207 | + |
| 208 | +``` |
| 209 | +. |
| 210 | +├── cpp |
| 211 | +│ ├── CMakeLists.txt |
| 212 | +│ ├── infer.cc // C++ 版本测试代码 |
| 213 | +│ └── README.md // C++版本使用文档 |
| 214 | +├── python |
| 215 | +│ ├── infer.py // Python 版本测试代码 |
| 216 | +│ └── README.md // Python版本使用文档 |
| 217 | +└── README.md // ResNet 模型集成说明文档 |
| 218 | +``` |
| 219 | + |
| 220 | +* C++ |
| 221 | + * 编写CmakeLists文件、C++ 代码以及 README.md 内容请参考[cpp/](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-afcbe607b796509581f89e38b84190717f1eeda2df0419a2ac9034197ead5f96)。 |
| 222 | + * 编译 infer.cc |
| 223 | + * 位置:FastDeploy/examples/vision/classification/resnet/cpp/ |
| 224 | + |
| 225 | +``` |
| 226 | +mkdir build & cd build |
| 227 | +cmake .. -DFASTDEPLOY_INSTALL_DIR=/PATH/TO/FastDeploy/build/fastdeploy-0.0.3/ |
| 228 | +make |
| 229 | +``` |
| 230 | + |
| 231 | +* Python |
| 232 | + * Python 代码以及 README.md 内容请参考[python/](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-5a0d6be8c603a8b81454ac14c17fb93555288d9adf92bbe40454449309700135)。 |
| 233 | + |
| 234 | +### 为代码添加注释 |
| 235 | +为了方便用户理解代码,我们需要为新增代码添加注释,添加注释方法可参考如下示例。 |
| 236 | +- C++ 代码 |
| 237 | +您需要在resnet.h文件中为函数和变量增加注释,有如下三种注释方式,具体可参考[resnet.h](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-69128489e918f305c208476ba793d8167e77de2aa7cadf5dcbac30da448bd28e)。 |
| 238 | + |
| 239 | +```C++ |
| 240 | +/** \brief Predict for the input "im", the result will be saved in "result". |
| 241 | +* |
| 242 | +* \param[in] im Input image for inference. |
| 243 | +* \param[in] result Saving the inference result. |
| 244 | +* \param[in] topk The length of return values, e.g., if topk==2, the result will include the 2 most possible class label for input image. |
| 245 | +*/ |
| 246 | +virtual bool Predict(cv::Mat* im, ClassifyResult* result, int topk = 1); |
| 247 | + |
| 248 | +/// Tuple of (width, height) |
| 249 | +std::vector<int> size; |
| 250 | +/*! @brief Initialize for ResNet model, assign values to the global variables and call InitRuntime() |
| 251 | +*/ |
| 252 | +bool Initialize(); |
| 253 | +``` |
| 254 | +- Python 代码 |
| 255 | +你需要为resnet.py文件中的函数和变量增加适当的注释,示例如下,具体可参考[resnet.py](https://github.com/PaddlePaddle/FastDeploy/pull/347/files#diff-a4dc5ec2d450e91f1c03819bf314c238b37ac678df56d7dea3aab7feac10a157)。 |
| 256 | +
|
| 257 | +```python |
| 258 | + def predict(self, input_image, topk=1): |
| 259 | + """Classify an input image |
| 260 | +
|
| 261 | + :param input_image: (numpy.ndarray)The input image data, 3-D array with layout HWC, BGR format |
| 262 | + :param topk: (int)The topk result by the classify confidence score, default 1 |
| 263 | + :return: ClassifyResult |
| 264 | + """ |
| 265 | + return self._model.predict(input_image, topk) |
| 266 | +``` |
| 267 | + |
| 268 | +对于集成模型过程中的其他文件,您也可以对实现的细节添加适当的注释说明。 |
0 commit comments