diff --git a/docs/advanced_tutorials/inference/EXPORT_MODEL.md b/docs/advanced_tutorials/inference/EXPORT_MODEL.md index 68f8b9edc1c9e9fdaca8da15ecc12d95c8da51b5..70a5b874323f8ca20d2f4e776be200b468b374bf 100644 --- a/docs/advanced_tutorials/inference/EXPORT_MODEL.md +++ b/docs/advanced_tutorials/inference/EXPORT_MODEL.md @@ -41,6 +41,7 @@ python tools/export_model.py -c configs/faster_rcnn_r50_1x.yml \ 预测模型会导出到`inference_model/faster_rcnn_r50_1x`目录下,模型名和参数名分别为`__model__`和`__params__`。 + ## 设置导出模型的输入大小 使用Fluid-TensorRT进行预测时,由于<=TensorRT 5.1的版本仅支持定长输入,保存模型的`data`层的图片大小需要和实际输入图片大小一致。而Fluid C++预测引擎没有此限制。可通过设置TestReader中`image_shape`可以修改保存模型中的输入图片大小。示例如下: @@ -64,3 +65,20 @@ python tools/export_model.py -c configs/ssd/ssd_mobilenet_v1_voc.yml \ -o weights=https://paddlemodels.bj.bcebos.com/object_detection/ssd_mobilenet_v1_voc.tar \ TestReader.inputs_def.image_shape=[3,300,300] ``` + +## Paddle Serving部署模型导出 + +如果您要将上述模型用于[Paddle Serving](https://github.com/PaddlePaddle/Serving)在线预估服务,操作如下 + +```bash +# 导出Serving模型需要安装paddle-serving-client +pip install paddle-serving-client +# 导出FasterRCNN模型, 模型中data层默认的shape为3x800x1333 +python tools/export_serving_model.py -c configs/faster_rcnn_r50_1x.yml \ + --output_dir=./inference_model \ + -o weights=output/faster_rcnn_r50_1x/model_final \ +``` + +用于Serving的预测模型会导出到`inference_model/faster_rcnn_r50_1x`目录下,其中`serving_client`为客户端配置文件夹,`serving_server`为服务端配置文件夹,模型参数也在服务端配置文件夹中。 + +更多的信息详情参见 [使用Paddle Serving部署Faster RCNN模型](https://github.com/PaddlePaddle/Serving/tree/develop/python/examples/faster_rcnn_model) diff --git a/tools/export_model.py b/tools/export_model.py index 7045d122149acfe243607b3fad0b8eb7a27fb032..554840a0b9e217da9d2043acea9ff020d0fc24f6 100644 --- a/tools/export_model.py +++ b/tools/export_model.py @@ -86,7 +86,7 @@ def parse_reader(reader_cfg, metric, arch): return with_background, preprocess_list, label_list -def dump_infer_config(config): +def dump_infer_config(FLAGS, config): cfg_name = os.path.basename(FLAGS.config).split('.')[0] save_dir = os.path.join(FLAGS.output_dir, cfg_name) from ppdet.core.config.yaml_helpers import setup_orderdict @@ -192,7 +192,7 @@ def main(): checkpoint.load_params(exe, infer_prog, cfg.weights) save_infer_model(FLAGS, exe, feed_vars, test_fetches, infer_prog) - dump_infer_config(cfg) + dump_infer_config(FLAGS, cfg) if __name__ == '__main__': diff --git a/tools/export_serving_model.py b/tools/export_serving_model.py new file mode 100644 index 0000000000000000000000000000000000000000..177399e9503303eb8220173234b2e2e5d4e62887 --- /dev/null +++ b/tools/export_serving_model.py @@ -0,0 +1,98 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +from paddle import fluid + +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.utils.cli import ArgsParser +import ppdet.utils.checkpoint as checkpoint +import yaml +import logging +from export_model import parse_reader, dump_infer_config, prune_feed_vars +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def save_serving_model(FLAGS, exe, feed_vars, test_fetches, infer_prog): + cfg_name = os.path.basename(FLAGS.config).split('.')[0] + save_dir = os.path.join(FLAGS.output_dir, cfg_name) + feed_var_names = [var.name for var in feed_vars.values()] + fetch_list = sorted(test_fetches.items(), key=lambda i: i[0]) + target_vars = [var[1] for var in fetch_list] + feed_var_names = prune_feed_vars(feed_var_names, target_vars, infer_prog) + serving_client = os.path.join(FLAGS.output_dir, 'serving_client') + serving_server = os.path.join(FLAGS.output_dir, 'serving_server') + logger.info( + "Export serving model to {}, client side: {}, server side: {}. input: {}, output: " + "{}...".format(FLAGS.output_dir, serving_client, serving_server, + feed_var_names, [str(var.name) for var in target_vars])) + feed_dict = {x: infer_prog.global_block().var(x) for x in feed_var_names} + fetch_dict = {x.name: x for x in target_vars} + import paddle_serving_client.io as serving_io + serving_client = os.path.join(save_dir, 'serving_client') + serving_server = os.path.join(save_dir, 'serving_server') + serving_io.save_model(serving_client, serving_server, feed_dict, fetch_dict, + infer_prog) + + +def main(): + cfg = load_config(FLAGS.config) + + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + + # Use CPU for exporting inference model instead of GPU + place = fluid.CPUPlace() + exe = fluid.Executor(place) + + model = create(main_arch) + + startup_prog = fluid.Program() + infer_prog = fluid.Program() + with fluid.program_guard(infer_prog, startup_prog): + with fluid.unique_name.guard(): + inputs_def = cfg['TestReader']['inputs_def'] + inputs_def['use_dataloader'] = False + feed_vars, _ = model.build_inputs(**inputs_def) + test_fetches = model.test(feed_vars) + infer_prog = infer_prog.clone(True) + + exe.run(startup_prog) + checkpoint.load_params(exe, infer_prog, cfg.weights) + + save_serving_model(FLAGS, exe, feed_vars, test_fetches, infer_prog) + dump_infer_config(FLAGS, cfg) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "--output_dir", + type=str, + default="output", + help="Directory for storing the output model files.") + + FLAGS = parser.parse_args() + main()