diff --git a/CMakeLists.txt b/CMakeLists.txt index af065158699199af61aca02f563dda1b1cddf2b1..7c497e3e048c4dd8d5c1291286de2ab9d218b914 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,17 @@ include(generic) include(flags) endif() +if (APP) +include(external/zlib) +include(external/boost) +include(external/protobuf) +include(external/gflags) +include(external/glog) +include(external/pybind11) +include(external/python) +include(generic) +endif() + if (SERVER) include(external/cudnn) include(paddlepaddle) diff --git a/README.md b/README.md index 747c140ded49f279c289b0bc8a3b4b1963243040..9d1ec854ba67d220a481816cda5eeebf2bc89739 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,9 @@ python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --po | `port` | int | `9292` | Exposed port of current service to users| | `name` | str | `""` | Service name, can be used to generate HTTP request url | | `model` | str | `""` | Path of paddle model directory to be served | -| `mem_optim` | bool | `False` | Enable memory optimization | +| `mem_optim` | bool | `False` | Enable memory / graphic memory optimization | +| `ir_optim` | bool | `False` | Enable analysis and optimization of calculation graph | +| `use_mkl` (Only for cpu version) | bool | `False` | Run inference with MKL | Here, we use `curl` to send a HTTP POST request to the service we just started. Users can use any python library to send HTTP POST as well, e.g, [requests](https://requests.readthedocs.io/en/master/). diff --git a/README_CN.md b/README_CN.md index 266fca330d7597d6188fa0022e6376bc23149c74..0c30ef0cffea7d2940c544c55b641255108908fd 100644 --- a/README_CN.md +++ b/README_CN.md @@ -87,6 +87,8 @@ python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --po | `name` | str | `""` | Service name, can be used to generate HTTP request url | | `model` | str | `""` | Path of paddle model directory to be served | | `mem_optim` | bool | `False` | Enable memory optimization | +| `ir_optim` | bool | `False` | Enable analysis and optimization of calculation graph | +| `use_mkl` (Only for cpu version) | bool | `False` | Run inference with MKL | 我们使用 `curl` 命令来发送HTTP POST请求给刚刚启动的服务。用户也可以调用python库来发送HTTP POST请求,请参考英文文档 [requests](https://requests.readthedocs.io/en/master/)。 diff --git a/cmake/paddlepaddle.cmake b/cmake/paddlepaddle.cmake index c9ac3d2f04db833f34211af3cc7aaac2d5184bf9..7670444ed1e021376fa44491973bb748cf611ecf 100644 --- a/cmake/paddlepaddle.cmake +++ b/cmake/paddlepaddle.cmake @@ -31,7 +31,7 @@ message( "WITH_GPU = ${WITH_GPU}") # Paddle Version should be one of: # latest: latest develop build # version number like 1.5.2 -SET(PADDLE_VERSION "1.7.1") +SET(PADDLE_VERSION "1.7.2") if (WITH_GPU) SET(PADDLE_LIB_VERSION "${PADDLE_VERSION}-gpu-cuda${CUDA_VERSION_MAJOR}-cudnn7-avx-mkl") diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index ce2e5e3814ae1e585976c5d9c8848b506293ee67..56296b53319fb185c772ffa10e8b31c8203862fb 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -23,6 +23,11 @@ add_subdirectory(pdcodegen) add_subdirectory(sdk-cpp) endif() +if (APP) +add_subdirectory(configure) +endif() + + if(CLIENT) add_subdirectory(general-client) endif() diff --git a/core/configure/CMakeLists.txt b/core/configure/CMakeLists.txt index b6384fc99ea3df6d71a61865e3aabf5b39b510dd..d3e5b75da96ad7a0789866a4a2c474fad988c21b 100644 --- a/core/configure/CMakeLists.txt +++ b/core/configure/CMakeLists.txt @@ -1,3 +1,4 @@ +if (SERVER OR CLIENT) LIST(APPEND protofiles ${CMAKE_CURRENT_LIST_DIR}/proto/server_configure.proto ${CMAKE_CURRENT_LIST_DIR}/proto/sdk_configure.proto @@ -28,6 +29,7 @@ FILE(GLOB inc ${CMAKE_CURRENT_BINARY_DIR}/*.pb.h) install(FILES ${inc} DESTINATION ${PADDLE_SERVING_INSTALL_DIR}/include/configure) +endif() py_proto_compile(general_model_config_py_proto SRCS proto/general_model_config.proto) add_custom_target(general_model_config_py_proto_init ALL COMMAND ${CMAKE_COMMAND} -E touch __init__.py) @@ -51,6 +53,14 @@ add_custom_command(TARGET general_model_config_py_proto POST_BUILD endif() +if (APP) +add_custom_command(TARGET general_model_config_py_proto POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_app/proto + COMMAND cp *.py ${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_app/proto + COMMENT "Copy generated general_model_config proto file into directory paddle_serving_app/proto." + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +endif() + if (SERVER) py_proto_compile(server_config_py_proto SRCS proto/server_configure.proto) add_custom_target(server_config_py_proto_init ALL COMMAND ${CMAKE_COMMAND} -E touch __init__.py) diff --git a/core/configure/proto/server_configure.proto b/core/configure/proto/server_configure.proto index 4bdc233099cffbc7949a6b5cf8627fe6461f565c..8956022685090c94be2037445c646e9fbffd1a5c 100644 --- a/core/configure/proto/server_configure.proto +++ b/core/configure/proto/server_configure.proto @@ -43,6 +43,7 @@ message EngineDesc { optional bool enable_memory_optimization = 13; optional bool static_optimization = 14; optional bool force_update_static_cache = 15; + optional bool enable_ir_optimization = 16; }; // model_toolkit conf diff --git a/core/general-client/CMakeLists.txt b/core/general-client/CMakeLists.txt index 88abcbcb776ae999cbf9123d1dad0864a987ecf4..d6079317a75d3f45b61920836e6695bd6b31d951 100644 --- a/core/general-client/CMakeLists.txt +++ b/core/general-client/CMakeLists.txt @@ -1,5 +1,5 @@ if(CLIENT) add_subdirectory(pybind11) pybind11_add_module(serving_client src/general_model.cpp src/pybind_general_model.cpp) -target_link_libraries(serving_client PRIVATE -Wl,--whole-archive utils sdk-cpp pybind python -Wl,--no-whole-archive -lpthread -lcrypto -lm -lrt -lssl -ldl -lz) +target_link_libraries(serving_client PRIVATE -Wl,--whole-archive utils sdk-cpp pybind python -Wl,--no-whole-archive -lpthread -lcrypto -lm -lrt -lssl -ldl -lz -Wl,-rpath,'$ORIGIN'/lib) endif() diff --git a/core/general-client/src/general_model.cpp b/core/general-client/src/general_model.cpp index 86f75bc1c1b401cd14f2c6651ea52ef08fdb8c40..cab050e732fb701120c7f1a5c80737fc75282324 100644 --- a/core/general-client/src/general_model.cpp +++ b/core/general-client/src/general_model.cpp @@ -345,7 +345,7 @@ int PredictorClient::numpy_predict( PredictorRes &predict_res_batch, const int &pid) { int batch_size = std::max(float_feed_batch.size(), int_feed_batch.size()); - + VLOG(2) << "batch size: " << batch_size; predict_res_batch.clear(); Timer timeline; int64_t preprocess_start = timeline.TimeStampUS(); @@ -462,7 +462,7 @@ int PredictorClient::numpy_predict( for (ssize_t j = 0; j < int_array.shape(1); j++) { for (ssize_t k = 0; k < int_array.shape(2); k++) { for (ssize_t l = 0; k < int_array.shape(3); l++) { - tensor->add_float_data(int_array(i, j, k, l)); + tensor->add_int64_data(int_array(i, j, k, l)); } } } @@ -474,7 +474,7 @@ int PredictorClient::numpy_predict( for (ssize_t i = 0; i < int_array.shape(0); i++) { for (ssize_t j = 0; j < int_array.shape(1); j++) { for (ssize_t k = 0; k < int_array.shape(2); k++) { - tensor->add_float_data(int_array(i, j, k)); + tensor->add_int64_data(int_array(i, j, k)); } } } @@ -484,7 +484,7 @@ int PredictorClient::numpy_predict( auto int_array = int_feed[vec_idx].unchecked<2>(); for (ssize_t i = 0; i < int_array.shape(0); i++) { for (ssize_t j = 0; j < int_array.shape(1); j++) { - tensor->add_float_data(int_array(i, j)); + tensor->add_int64_data(int_array(i, j)); } } break; @@ -492,7 +492,7 @@ int PredictorClient::numpy_predict( case 1: { auto int_array = int_feed[vec_idx].unchecked<1>(); for (ssize_t i = 0; i < int_array.shape(0); i++) { - tensor->add_float_data(int_array(i)); + tensor->add_int64_data(int_array(i)); } break; } diff --git a/core/predictor/framework/infer.h b/core/predictor/framework/infer.h index 4bb3be9ad2c3dc7ef94a32200b014325aceedf45..e8c0ff47d86f081516a35576655f843a28b0591b 100644 --- a/core/predictor/framework/infer.h +++ b/core/predictor/framework/infer.h @@ -35,6 +35,7 @@ class InferEngineCreationParams { InferEngineCreationParams() { _path = ""; _enable_memory_optimization = false; + _enable_ir_optimization = false; _static_optimization = false; _force_update_static_cache = false; } @@ -45,10 +46,16 @@ class InferEngineCreationParams { _enable_memory_optimization = enable_memory_optimization; } + void set_enable_ir_optimization(bool enable_ir_optimization) { + _enable_ir_optimization = enable_ir_optimization; + } + bool enable_memory_optimization() const { return _enable_memory_optimization; } + bool enable_ir_optimization() const { return _enable_ir_optimization; } + void set_static_optimization(bool static_optimization = false) { _static_optimization = static_optimization; } @@ -68,6 +75,7 @@ class InferEngineCreationParams { << "model_path = " << _path << ", " << "enable_memory_optimization = " << _enable_memory_optimization << ", " + << "enable_ir_optimization = " << _enable_ir_optimization << ", " << "static_optimization = " << _static_optimization << ", " << "force_update_static_cache = " << _force_update_static_cache; } @@ -75,6 +83,7 @@ class InferEngineCreationParams { private: std::string _path; bool _enable_memory_optimization; + bool _enable_ir_optimization; bool _static_optimization; bool _force_update_static_cache; }; @@ -150,6 +159,11 @@ class ReloadableInferEngine : public InferEngine { force_update_static_cache = conf.force_update_static_cache(); } + if (conf.has_enable_ir_optimization()) { + _infer_engine_params.set_enable_ir_optimization( + conf.enable_ir_optimization()); + } + _infer_engine_params.set_path(_model_data_path); if (enable_memory_optimization) { _infer_engine_params.set_enable_memory_optimization(true); diff --git a/core/sdk-cpp/include/endpoint_config.h b/core/sdk-cpp/include/endpoint_config.h index 6edb6ed8ab3b7c62be12c35c1658a2adf3140341..f814b659a24c4e5cb6e352c9a39bcfef1df147c1 100644 --- a/core/sdk-cpp/include/endpoint_config.h +++ b/core/sdk-cpp/include/endpoint_config.h @@ -22,23 +22,23 @@ namespace baidu { namespace paddle_serving { namespace sdk_cpp { -#define PARSE_CONF_ITEM(conf, item, name, fail) \ - do { \ - if (conf.has_##name()) { \ - item.set(conf.name()); \ - } else { \ - LOG(ERROR) << "Not found key in configue: " << #name; \ - } \ +#define PARSE_CONF_ITEM(conf, item, name, fail) \ + do { \ + if (conf.has_##name()) { \ + item.set(conf.name()); \ + } else { \ + VLOG(2) << "Not found key in configue: " << #name; \ + } \ } while (0) -#define ASSIGN_CONF_ITEM(dest, src, fail) \ - do { \ - if (!src.init) { \ - LOG(ERROR) << "Cannot assign an unintialized item: " << #src \ - << " to dest: " << #dest; \ - return fail; \ - } \ - dest = src.value; \ +#define ASSIGN_CONF_ITEM(dest, src, fail) \ + do { \ + if (!src.init) { \ + VLOG(2) << "Cannot assign an unintialized item: " << #src \ + << " to dest: " << #dest; \ + return fail; \ + } \ + dest = src.value; \ } while (0) template diff --git a/doc/COMPILE.md b/doc/COMPILE.md index 41a79f082494b0ac22bb4479a5d246cdb6882a3d..f61ac061883581090087a2202e694c9a07468c5f 100644 --- a/doc/COMPILE.md +++ b/doc/COMPILE.md @@ -9,14 +9,18 @@ - Golang: 1.9.2 and later - Git:2.17.1 and later - CMake:3.2.2 and later -- Python:2.7.2 and later +- Python:2.7.2 and later / 3.6 and later It is recommended to use Docker for compilation. We have prepared the Paddle Serving compilation environment for you: - CPU: `hub.baidubce.com/paddlepaddle/serving:0.2.0-devel`,dockerfile: [Dockerfile.devel](../tools/Dockerfile.devel) - GPU: `hub.baidubce.com/paddlepaddle/serving:0.2.0-gpu-devel`,dockerfile: [Dockerfile.gpu.devel](../tools/Dockerfile.gpu.devel) -This document will take Python2 as an example to show how to compile Paddle Serving. If you want to compile with Python 3, just adjust the Python options of cmake. +This document will take Python2 as an example to show how to compile Paddle Serving. If you want to compile with Python3, just adjust the Python options of cmake: + +- Set `DPYTHON_INCLUDE_DIR` to `$PYTHONROOT/include/python3.6m/` +- Set `DPYTHON_LIBRARIES` to `$PYTHONROOT/lib64/libpython3.6.so` +- Set `DPYTHON_EXECUTABLE` to `$PYTHONROOT/bin/python3` ## Get Code @@ -54,6 +58,8 @@ make -j10 execute `make install` to put targets under directory `./output` +**Attention:** After the compilation is successful, you need to set the path of `SERVING_BIN`. See [Note](https://github.com/PaddlePaddle/Serving/blob/develop/doc/COMPILE.md#Note) for details. + ## Compile Client ``` shell diff --git a/doc/COMPILE_CN.md b/doc/COMPILE_CN.md index eb334232d98f26e68d719d10cbe458a356738d2f..c6e5426f02335598277ceb40fafc5215c7f03b2b 100644 --- a/doc/COMPILE_CN.md +++ b/doc/COMPILE_CN.md @@ -9,14 +9,18 @@ - Golang: 1.9.2及以上 - Git:2.17.1及以上 - CMake:3.2.2及以上 -- Python:2.7.2及以上 +- Python:2.7.2及以上 / 3.6及以上 推荐使用Docker编译,我们已经为您准备好了Paddle Serving编译环境: - CPU: `hub.baidubce.com/paddlepaddle/serving:0.2.0-devel`,dockerfile: [Dockerfile.devel](../tools/Dockerfile.devel) - GPU: `hub.baidubce.com/paddlepaddle/serving:0.2.0-gpu-devel`,dockerfile: [Dockerfile.gpu.devel](../tools/Dockerfile.gpu.devel) -本文档将以Python2为例介绍如何编译Paddle Serving。如果您想用Python3进行编译,只需要调整cmake的Python相关选项即可。 +本文档将以Python2为例介绍如何编译Paddle Serving。如果您想用Python3进行编译,只需要调整cmake的Python相关选项即可: + +- 将`DPYTHON_INCLUDE_DIR`设置为`$PYTHONROOT/include/python3.6m/` +- 将`DPYTHON_LIBRARIES`设置为`$PYTHONROOT/lib64/libpython3.6.so` +- 将`DPYTHON_EXECUTABLE`设置为`$PYTHONROOT/bin/python3` ## 获取代码 @@ -54,6 +58,8 @@ make -j10 执行`make install`可以把目标产出放在`./output`目录下。 +**注意:** 编译成功后,需要设置`SERVING_BIN`路径,详见后面的[注意事项](https://github.com/PaddlePaddle/Serving/blob/develop/doc/COMPILE_CN.md#注意事项)。 + ## 编译Client部分 ``` shell diff --git a/doc/PERFORMANCE_OPTIM.md b/doc/PERFORMANCE_OPTIM.md new file mode 100644 index 0000000000000000000000000000000000000000..4b025e94d6f8d3ed69fb76898eb6afada9ca6613 --- /dev/null +++ b/doc/PERFORMANCE_OPTIM.md @@ -0,0 +1,18 @@ +# Performance optimization + +Due to different model structures, different prediction services consume different computing resources when performing predictions. For online prediction services, models that require less computing resources will have a higher proportion of communication time cost, which is called communication-intensive service. Models that require more computing resources have a higher time cost for inference calculations, which is called computationa-intensive services. + +For a prediction service, the easiest way to determine what type it is is to look at the time ratio. Paddle Serving provides [Timeline tool] (../python/examples/util/README_CN.md), which can intuitively display the time spent in each stage of the prediction service. + +For communication-intensive prediction services, requests can be aggregated, and within a limit that can tolerate delay, multiple prediction requests can be combined into a batch for prediction. + +For computation-intensive prediction services, you can use GPU prediction services instead of CPU prediction services, or increase the number of graphics cards for GPU prediction services. + +Under the same conditions, the communication time of the HTTP prediction service provided by Paddle Serving is longer than that of the RPC prediction service, so for communication-intensive services, please give priority to using RPC communication. + +Parameters for performance optimization: + +| Parameters | Type | Default | Description | +| ---------- | ---- | ------- | ------------------------------------------------------------ | +| mem_optim | bool | False | Enable memory / graphic memory optimization | +| ir_optim | bool | Fasle | Enable analysis and optimization of calculation graph,including OP fusion, etc | diff --git a/doc/PERFORMANCE_OPTIM_CN.md b/doc/PERFORMANCE_OPTIM_CN.md index dd17bc8afab8472f8f55b4870f73e4c481e97cd3..7bd64d3e2d645c9328ead55e867d0b97946840ad 100644 --- a/doc/PERFORMANCE_OPTIM_CN.md +++ b/doc/PERFORMANCE_OPTIM_CN.md @@ -1,6 +1,6 @@ # 性能优化 -由于模型结构的不同,在执行预测时不同的预测对计算资源的消耗也不相同,对于在线的预测服务来说,对计算资源要求较少的模型,通信的时间成本占比就会较高,称为通信密集型服务,对计算资源要求较多的模型,推理计算的时间成本较高,称为计算密集型服务。对于这两种服务类型,可以根据实际需求采取不同的方式进行优化 +由于模型结构的不同,在执行预测时不同的预测服务对计算资源的消耗也不相同。对于在线的预测服务来说,对计算资源要求较少的模型,通信的时间成本占比就会较高,称为通信密集型服务,对计算资源要求较多的模型,推理计算的时间成本较高,称为计算密集型服务。对于这两种服务类型,可以根据实际需求采取不同的方式进行优化 对于一个预测服务来说,想要判断属于哪种类型,最简单的方法就是看时间占比,Paddle Serving提供了[Timeline工具](../python/examples/util/README_CN.md),可以直观的展现预测服务中各阶段的耗时。 @@ -10,4 +10,9 @@ 在相同条件下,Paddle Serving提供的HTTP预测服务的通信时间是大于RPC预测服务的,因此对于通信密集型的服务请优先考虑使用RPC的通信方式。 -对于模型较大,预测服务内存或显存占用较多的情况,可以通过将--mem_optim选项设置为True来开启内存/显存优化。 +性能优化相关参数: + +| 参数 | 类型 | 默认值 | 含义 | +| --------- | ---- | ------ | -------------------------------- | +| mem_optim | bool | False | 开启内存/显存优化 | +| ir_optim | bool | Fasle | 开启计算图分析优化,包括OP融合等 | diff --git a/doc/RUN_IN_DOCKER.md b/doc/RUN_IN_DOCKER.md index e7b25362d113b18f6e779ccb9b92a3e3c8d13343..327176297518ff65d788e3e59b23db27f1e7178c 100644 --- a/doc/RUN_IN_DOCKER.md +++ b/doc/RUN_IN_DOCKER.md @@ -53,12 +53,6 @@ pip install paddle-serving-server -i https://pypi.tuna.tsinghua.edu.cn/simple ### Test example -Before running the GPU version of the Server side code, you need to set the `CUDA_VISIBLE_DEVICES` environment variable to specify which GPUs the prediction service uses. The following example specifies two GPUs with indexes 0 and 1: - -```bash -export CUDA_VISIBLE_DEVICES=0,1 -``` - Get the trained Boston house price prediction model by the following command: ```bash @@ -71,13 +65,13 @@ tar -xzf uci_housing.tar.gz Running on the Server side (inside the container): ```bash - python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 --name uci &>std.log 2>err.log & + python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 --name uci >std.log 2>err.log & ``` Running on the Client side (inside or outside the container): ```bash - curl -H "Content-Type:application/json" -X POST -d '{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332], "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction + curl -H "Content-Type:application/json" -X POST -d '{"feed":{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332]}, "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction ``` - Test RPC service @@ -85,7 +79,7 @@ tar -xzf uci_housing.tar.gz Running on the Server side (inside the container): ```bash - python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 &>std.log 2>err.log & + python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 >std.log 2>err.log & ``` Running following Python code on the Client side (inside or outside the container, The `paddle-serving-client` package needs to be installed): @@ -176,7 +170,7 @@ tar -xzf uci_housing.tar.gz Running on the Client side (inside or outside the container): ```bash - curl -H "Content-Type:application/json" -X POST -d '{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332], "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction + curl -H "Content-Type:application/json" -X POST -d '{"feed":{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332]}, "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction ``` - Test RPC service diff --git a/doc/RUN_IN_DOCKER_CN.md b/doc/RUN_IN_DOCKER_CN.md index 3e84cf08c015b7fda0d957bf621173ec18c19498..4a995f9acf611c550e866ed12502734220a2e71c 100644 --- a/doc/RUN_IN_DOCKER_CN.md +++ b/doc/RUN_IN_DOCKER_CN.md @@ -65,13 +65,13 @@ tar -xzf uci_housing.tar.gz 在Server端(容器内)运行: ```bash - python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 --name uci &>std.log 2>err.log & + python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 --name uci >std.log 2>err.log & ``` 在Client端(容器内或容器外)运行: ```bash - curl -H "Content-Type:application/json" -X POST -d '{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332], "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction + curl -H "Content-Type:application/json" -X POST -d '{"feed":{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332]}, "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction ``` - 测试RPC服务 @@ -79,7 +79,7 @@ tar -xzf uci_housing.tar.gz 在Server端(容器内)运行: ```bash - python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 &>std.log 2>err.log & + python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 >std.log 2>err.log & ``` 在Client端(容器内或容器外,需要安装`paddle-serving-client`包)运行下面Python代码: @@ -168,7 +168,7 @@ tar -xzf uci_housing.tar.gz 在Client端(容器内或容器外)运行: ```bash - curl -H "Content-Type:application/json" -X POST -d '{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332], "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction + curl -H "Content-Type:application/json" -X POST -d '{"feed":{"x": [0.0137, -0.1136, 0.2553, -0.0692, 0.0582, -0.0727, -0.1583, -0.0584, 0.6283, 0.4919, 0.1856, 0.0795, -0.0332]}, "fetch":["price"]}' http://127.0.0.1:9292/uci/prediction ``` - 测试RPC服务 diff --git a/doc/SAVE.md b/doc/SAVE.md index c1e6b19a45c75a64207802984f52c734d44f8fc8..3f7f97e12e1e309ff0933e150ea7bcd23298b60e 100644 --- a/doc/SAVE.md +++ b/doc/SAVE.md @@ -1,8 +1,9 @@ -## How to save a servable model of Paddle Serving? +# How to save a servable model of Paddle Serving? ([简体中文](./SAVE_CN.md)|English) -- Currently, paddle serving provides a save_model interface for users to access, the interface is similar with `save_inference_model` of Paddle. +## Save from training or prediction script +Currently, paddle serving provides a save_model interface for users to access, the interface is similar with `save_inference_model` of Paddle. ``` python import paddle_serving_client.io as serving_io serving_io.save_model("imdb_model", "imdb_client_conf", @@ -29,3 +30,15 @@ for line in sys.stdin: fetch_map = client.predict(feed=feed, fetch=fetch) print("{} {}".format(fetch_map["prediction"][1], label[0])) ``` + +## Export from saved model files +If you have saved model files using Paddle's `save_inference_model` API, you can use Paddle Serving's` inference_model_to_serving` API to convert it into a model file that can be used for Paddle Serving. +``` +import paddle_serving_client.io as serving_io +serving_io.inference_model_to_serving(dirname, model_filename=None, params_filename=None, serving_server="serving_server", serving_client="serving_client") +``` +dirname (str) - Path of saved model files. Program file and parameter files are saved in this directory. +model_filename (str, optional) - The name of file to load the inference program. If it is None, the default filename __model__ will be used. Default: None. +paras_filename (str, optional) - The name of file to load all parameters. It is only used for the case that all parameters were saved in a single binary file. If parameters were saved in separate files, set it as None. Default: None. +serving_server (str, optional) - The path of model files and configuration files for server. Default: "serving_server". +serving_client (str, optional) - The path of configuration files for client. Default: "serving_client". diff --git a/doc/SAVE_CN.md b/doc/SAVE_CN.md index 43b62c2ac623b386505356194ac136ea305fe683..fc75cd8d015a6d6f42a08f29e4035db20f450d91 100644 --- a/doc/SAVE_CN.md +++ b/doc/SAVE_CN.md @@ -1,8 +1,9 @@ -## 怎样保存用于Paddle Serving的模型? +# 怎样保存用于Paddle Serving的模型? (简体中文|[English](./SAVE.md)) -- 目前,Paddle Serving提供了一个save_model接口供用户访问,该接口与Paddle的`save_inference_model`类似。 +## 从训练或预测脚本中保存 +目前,Paddle Serving提供了一个save_model接口供用户访问,该接口与Paddle的`save_inference_model`类似。 ``` python import paddle_serving_client.io as serving_io @@ -29,3 +30,15 @@ for line in sys.stdin: fetch_map = client.predict(feed=feed, fetch=fetch) print("{} {}".format(fetch_map["prediction"][1], label[0])) ``` + +## 从已保存的模型文件中导出 +如果已使用Paddle 的`save_inference_model`接口保存出预测要使用的模型,则可以通过Paddle Serving的`inference_model_to_serving`接口转换成可用于Paddle Serving的模型文件。 +``` +import paddle_serving_client.io as serving_io +serving_io.inference_model_to_serving(dirname, model_filename=None, params_filename=None, serving_server="serving_server", serving_client="serving_client") +``` +dirname (str) – 需要转换的模型文件存储路径,Program结构文件和参数文件均保存在此目录。 +model_filename (str,可选) – 存储需要转换的模型Inference Program结构的文件名称。如果设置为None,则使用 __model__ 作为默认的文件名。默认值为None。 +params_filename (str,可选) – 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为None。默认值为None。 +serving_server (str, 可选) - 转换后的模型文件和配置文件的存储路径。默认值为"serving_server"。 +serving_client (str, 可选) - 转换后的客户端配置文件存储路径。默认值为"serving_client"。 diff --git a/doc/TRAIN_TO_SERVICE.md b/doc/TRAIN_TO_SERVICE.md index 40d5dd95e4d7aad3b198898559321419b4b17833..90046b03ebc4af1394fb85fb41fccf1d844f6917 100644 --- a/doc/TRAIN_TO_SERVICE.md +++ b/doc/TRAIN_TO_SERVICE.md @@ -350,12 +350,12 @@ In the above command, the first parameter is the saved server-side model and con After starting the HTTP prediction service, you can make prediction with a single command: ``` -curl -H "Content-Type: application/json" -X POST -d '{"words": "i am very sad | 0", "fetch": ["prediction"]}' http://127.0.0.1:9292/imdb/prediction +curl -H "Content-Type:application/json" -X POST -d '{"feed":[{"words": "i am very sad | 0"}], "fetch":["prediction"]}' http://127.0.0.1:9292/imdb/prediction ``` When the inference process is normal, the prediction probability is returned, as shown below. ``` -{"prediction": [0.5592559576034546,0.44074398279190063]} +{"result":{"prediction":[[0.4389057457447052,0.561094343662262]]}} ``` **Note**: The effect of each model training may be slightly different, and the inferred probability value using the trained model may not be consistent with the example. diff --git a/doc/TRAIN_TO_SERVICE_CN.md b/doc/TRAIN_TO_SERVICE_CN.md index ad2a43c30b1cd0d4701ebb3c8b3a46a4b07c1bda..1c8a2848bcc198c66e617be145c43d2651b7f885 100644 --- a/doc/TRAIN_TO_SERVICE_CN.md +++ b/doc/TRAIN_TO_SERVICE_CN.md @@ -353,12 +353,12 @@ python text_classify_service.py imdb_cnn_model/ workdir/ 9292 imdb.vocab 启动完HTTP预测服务,即可通过一行命令进行预测: ``` -curl -H "Content-Type:application/json" -X POST -d '{"words": "i am very sad | 0", "fetch":["prediction"]}' http://127.0.0.1:9292/imdb/prediction +curl -H "Content-Type:application/json" -X POST -d '{"feed":[{"words": "i am very sad | 0"}], "fetch":["prediction"]}' http://127.0.0.1:9292/imdb/prediction ``` 预测流程正常时,会返回预测概率,示例如下。 ``` -{"prediction":[0.5592559576034546,0.44074398279190063]} +{"result":{"prediction":[[0.4389057457447052,0.561094343662262]]}} ``` **注意**:每次模型训练的效果可能略有不同,使用训练出的模型预测概率数值可能与示例不一致。 diff --git a/paddle_inference/inferencer-fluid-cpu/include/fluid_cpu_engine.h b/paddle_inference/inferencer-fluid-cpu/include/fluid_cpu_engine.h index 24148e374e51cb42cb0d8d1423e0ca009e9e8294..a4d8dda71a7977185106bb1552cb8f39ef6bc50e 100644 --- a/paddle_inference/inferencer-fluid-cpu/include/fluid_cpu_engine.h +++ b/paddle_inference/inferencer-fluid-cpu/include/fluid_cpu_engine.h @@ -194,6 +194,12 @@ class FluidCpuAnalysisDirCore : public FluidFamilyCore { analysis_config.EnableMemoryOptim(); } + if (params.enable_ir_optimization()) { + analysis_config.SwitchIrOptim(true); + } else { + analysis_config.SwitchIrOptim(false); + } + AutoLock lock(GlobalPaddleCreateMutex::instance()); _core = paddle::CreatePaddlePredictor(analysis_config); diff --git a/paddle_inference/inferencer-fluid-gpu/include/fluid_gpu_engine.h b/paddle_inference/inferencer-fluid-gpu/include/fluid_gpu_engine.h index a3fa365444a40d505b16b22e702d4a8b69699073..2fc6ae587ff26f5f05ff9332f08067ab49d06254 100644 --- a/paddle_inference/inferencer-fluid-gpu/include/fluid_gpu_engine.h +++ b/paddle_inference/inferencer-fluid-gpu/include/fluid_gpu_engine.h @@ -198,6 +198,12 @@ class FluidGpuAnalysisDirCore : public FluidFamilyCore { analysis_config.EnableMemoryOptim(); } + if (params.enable_ir_optimization()) { + analysis_config.SwitchIrOptim(true); + } else { + analysis_config.SwitchIrOptim(false); + } + AutoLock lock(GlobalPaddleCreateMutex::instance()); _core = paddle::CreatePaddlePredictor(analysis_config); diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index c1590fb1b36de669f89711f95c4d49aedadb0c91..07699da458ab62ad1a5b9ece83547799d08f8cf7 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -19,6 +19,8 @@ endif() if (CLIENT) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.client.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../tools/python_tag.py + ${CMAKE_CURRENT_BINARY_DIR}/python_tag.py) endif() if (APP) @@ -43,7 +45,8 @@ if (APP) add_custom_command( OUTPUT ${PADDLE_SERVING_BINARY_DIR}/.timestamp COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/paddle_serving_app/ ${PADDLE_SERVING_BINARY_DIR}/python/ - COMMAND env ${py_env} ${PYTHON_EXECUTABLE} setup.py bdist_wheel) + COMMAND env ${py_env} ${PYTHON_EXECUTABLE} setup.py bdist_wheel + DEPENDS ${SERVING_APP_CORE} general_model_config_py_proto ${PY_FILES}) add_custom_target(paddle_python ALL DEPENDS ${PADDLE_SERVING_BINARY_DIR}/.timestamp) endif() @@ -52,6 +55,7 @@ add_custom_command( OUTPUT ${PADDLE_SERVING_BINARY_DIR}/.timestamp COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/paddle_serving_client/ ${PADDLE_SERVING_BINARY_DIR}/python/ COMMAND ${CMAKE_COMMAND} -E copy ${SERVING_CLIENT_CORE} ${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_client/serving_client.so + COMMAND env ${py_env} ${PYTHON_EXECUTABLE} python_tag.py COMMAND env ${py_env} ${PYTHON_EXECUTABLE} setup.py bdist_wheel DEPENDS ${SERVING_CLIENT_CORE} sdk_configure_py_proto ${PY_FILES}) add_custom_target(paddle_python ALL DEPENDS serving_client ${PADDLE_SERVING_BINARY_DIR}/.timestamp) diff --git a/python/examples/criteo_ctr_with_cube/README.md b/python/examples/criteo_ctr_with_cube/README.md index 25f171f5b07a6c58de68809ae6092c85f92b8116..02125422af7e7ce53a05a1eff9a43159034a79dc 100755 --- a/python/examples/criteo_ctr_with_cube/README.md +++ b/python/examples/criteo_ctr_with_cube/README.md @@ -2,16 +2,6 @@ ([简体中文](./README_CN.md)|English) -### Compile Source Code -in the root directory of this git project -``` -mkdir build_server -cd build_server -cmake -DPYTHON_INCLUDE_DIR=$PYTHONROOT/include/python2.7/ -DPYTHON_LIBRARIES=$PYTHONROOT/lib64/libpython2.7.so -DPYTHON_EXECUTABLE=$PYTHONROOT/bin/python -DSERVER=ON .. -make -j10 -make install -j10 -``` - ### Get Sample Dataset go to directory `python/examples/criteo_ctr_with_cube` @@ -31,7 +21,9 @@ the model will be in ./ctr_server_model_kv and ./ctr_client_config. ### Start Sparse Parameter Indexing Service ``` -cp ../../../build_server/output/bin/cube* ./cube/ +wget https://paddle-serving.bj.bcebos.com/others/cube_app.tar.gz +tar xf cube_app.tar.gz +mv cube_app/cube* ./cube/ sh cube_prepare.sh & ``` diff --git a/python/examples/criteo_ctr_with_cube/README_CN.md b/python/examples/criteo_ctr_with_cube/README_CN.md index 47279cc8c2dd781324fe4c73d98f58cbd69319c9..3b6f812ca53bd435e9b11b59e2a459c46ee3f864 100644 --- a/python/examples/criteo_ctr_with_cube/README_CN.md +++ b/python/examples/criteo_ctr_with_cube/README_CN.md @@ -1,16 +1,6 @@ ## 带稀疏参数索引服务的CTR预测服务 (简体中文|[English](./README.md)) -### 编译源代码 -在本项目的根目录下,执行 -``` -mkdir build_server -cd build_server -cmake -DPYTHON_INCLUDE_DIR=$PYTHONROOT/include/python2.7/ -DPYTHON_LIBRARIES=$PYTHONROOT/lib64/libpython2.7.so -DPYTHON_EXECUTABLE=$PYTHONROOT/bin/python -DSERVER=ON .. -make -j10 -make install -j10 -``` - ### 获取样例数据 进入目录 `python/examples/criteo_ctr_with_cube` ``` @@ -29,7 +19,9 @@ mv models/data ./cube/ ### 启动稀疏参数索引服务 ``` -cp ../../../build_server/output/bin/cube* ./cube/ +wget https://paddle-serving.bj.bcebos.com/others/cube_app.tar.gz +tar xf cube_app.tar.gz +mv cube_app/cube* ./cube/ sh cube_prepare.sh & ``` diff --git a/python/examples/deeplabv3/N0060.jpg b/python/examples/deeplabv3/N0060.jpg new file mode 100644 index 0000000000000000000000000000000000000000..feac2837eaa5ae5db414d9769a0c5a830dde268d Binary files /dev/null and b/python/examples/deeplabv3/N0060.jpg differ diff --git a/python/examples/faster_rcnn_model/test_client.py b/python/examples/deeplabv3/deeplabv3_client.py old mode 100755 new mode 100644 similarity index 55% rename from python/examples/faster_rcnn_model/test_client.py rename to python/examples/deeplabv3/deeplabv3_client.py index ae2e5b8f6e961d965555d8f268f38be14c0263d0..75ea6b0a01868af30c94fb0686159571c2c1c966 --- a/python/examples/faster_rcnn_model/test_client.py +++ b/python/examples/deeplabv3/deeplabv3_client.py @@ -13,21 +13,22 @@ # limitations under the License. from paddle_serving_client import Client +from paddle_serving_app.reader import Sequential, File2Image, Resize, Transpose, BGR2RGB, SegPostprocess import sys -import os -import time -from paddle_serving_app.reader.pddet import Detection -import numpy as np +import cv2 -py_version = sys.version_info[0] - -feed_var_names = ['image', 'im_shape', 'im_info'] -fetch_var_names = ['multiclass_nms'] -pddet = Detection(config_path=sys.argv[2], output_dir="./output") -feed_dict = pddet.preprocess(feed_var_names, sys.argv[3]) client = Client() -client.load_client_config(sys.argv[1]) -client.connect(['127.0.0.1:9494']) -fetch_map = client.predict(feed=feed_dict, fetch=fetch_var_names) -outs = fetch_map.values() -pddet.postprocess(fetch_map, fetch_var_names) +client.load_client_config("seg_client/serving_client_conf.prototxt") +client.connect(["127.0.0.1:9494"]) + +preprocess = Sequential( + [File2Image(), Resize( + (512, 512), interpolation=cv2.INTER_LINEAR)]) + +postprocess = SegPostprocess(2) + +filename = "N0060.jpg" +im = preprocess(filename) +fetch_map = client.predict(feed={"image": im}, fetch=["output"]) +fetch_map["filename"] = filename +postprocess(fetch_map) diff --git a/python/examples/faster_rcnn_model/README_CN.md b/python/examples/faster_rcnn_model/README_CN.md index 7aa4d343f05df92068d36499b48d9aa5ad7b2a2e..a2c3618f071a3650d50c791595bc04ba0c1d378a 100644 --- a/python/examples/faster_rcnn_model/README_CN.md +++ b/python/examples/faster_rcnn_model/README_CN.md @@ -12,7 +12,7 @@ wget https://paddle-serving.bj.bcebos.com/pddet_demo/infer_cfg.yml ### 启动服务 ``` tar xf faster_rcnn_model.tar.gz -mv faster_rcnn_model/pddet* . +mv faster_rcnn_model/pddet* ./ GLOG_v=2 python -m paddle_serving_server_gpu.serve --model pddet_serving_model --port 9494 --gpu_id 0 ``` diff --git a/python/examples/faster_rcnn_model/label_list.txt b/python/examples/faster_rcnn_model/label_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..d7d43a94adf73208f997f0efd6581bef11ca734e --- /dev/null +++ b/python/examples/faster_rcnn_model/label_list.txt @@ -0,0 +1,81 @@ +background +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +dining table +toilet +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/python/examples/faster_rcnn_model/new_test_client.py b/python/examples/faster_rcnn_model/new_test_client.py new file mode 100755 index 0000000000000000000000000000000000000000..0c6c615f8f3dff10626256de59101c401457509f --- /dev/null +++ b/python/examples/faster_rcnn_model/new_test_client.py @@ -0,0 +1,40 @@ +# 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 paddle_serving_client import Client +from paddle_serving_app.reader import * + +preprocess = Sequential([ + File2Image(), BGR2RGB(), Div(255.0), + Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], False), + Resize(640, 640), Transpose((2, 0, 1)) +]) + +postprocess = RCNNPostprocess("label_list.txt", "output") +client = Client() + +client.load_client_config( + "faster_rcnn_client_conf/serving_client_conf.prototxt") +client.connect(['127.0.0.1:9393']) + +im = preprocess(sys.argv[2]) +fetch_map = client.predict( + feed={ + "image": im, + "im_info": np.array(list(im.shape[1:]) + [1.0]), + "im_shape": np.array(list(im.shape[1:]) + [1.0]) + }, + fetch=["multiclass_nms"]) +fetch_map["image"] = sys.argv[1] +postprocess(fetch_map) diff --git a/python/examples/imagenet/README.md b/python/examples/imagenet/README.md index 5eba4892f5c394eaff999c1b36c457fc9c80b2d6..52518d211a4350284cea19546fb3e55d49fc265f 100644 --- a/python/examples/imagenet/README.md +++ b/python/examples/imagenet/README.md @@ -8,6 +8,13 @@ The example uses the ResNet50_vd model to perform the imagenet 1000 classificati ``` sh get_model.sh ``` + +### Install preprocess module + +``` +pip install paddle_serving_app +``` + ### HTTP Infer launch server side diff --git a/python/examples/imagenet/README_CN.md b/python/examples/imagenet/README_CN.md index 074709a3705d83367f9cdce7cd6ba426167ccd32..3b865cf91ecb62dacd0be5d35fa97bc2e0d50ce3 100644 --- a/python/examples/imagenet/README_CN.md +++ b/python/examples/imagenet/README_CN.md @@ -8,6 +8,13 @@ ``` sh get_model.sh ``` + +### 安装数据预处理模块 + +``` +pip install paddle_serving_app +``` + ### 执行HTTP预测服务 启动server端 diff --git a/python/examples/imagenet/image_rpc_client.py b/python/examples/imagenet/image_rpc_client.py index f905179629f0dfc8c9da09b0cae90bae7be3687e..4d74d2ed26a757a6f7978d8071286d3d4bcd5dfb 100644 --- a/python/examples/imagenet/image_rpc_client.py +++ b/python/examples/imagenet/image_rpc_client.py @@ -13,22 +13,24 @@ # limitations under the License. import sys -from image_reader import ImageReader from paddle_serving_client import Client +from paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop, RGB2BGR, Transpose, Div, Normalize import time client = Client() client.load_client_config(sys.argv[1]) client.connect(["127.0.0.1:9393"]) -reader = ImageReader() + +seq = Sequential([ + File2Image(), Resize(256), CenterCrop(224), RGB2BGR(), Transpose((2, 0, 1)), + Div(255), Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) +]) +print(seq) start = time.time() +image_file = "daisy.jpg" for i in range(1000): - with open("./data/n01440764_10026.JPEG", "rb") as f: - img = f.read() - img = reader.process_image(img) + img = seq(image_file) fetch_map = client.predict(feed={"image": img}, fetch=["score"]) end = time.time() print(end - start) - -#print(fetch_map["score"]) diff --git a/python/examples/imdb/test_client.py b/python/examples/imdb/test_client.py index 9de7a45b0646167c43ea7d3b98b0f3782112f6f0..fdc3ced25377487a2844d57c4e6121801e9fa7fa 100644 --- a/python/examples/imdb/test_client.py +++ b/python/examples/imdb/test_client.py @@ -31,4 +31,4 @@ for line in sys.stdin: feed = {"words": word_ids} fetch = ["acc", "cost", "prediction"] fetch_map = client.predict(feed=feed, fetch=fetch) - print("{} {}".format(fetch_map["prediction"][0][1], label[0])) + print("{} {}".format(fetch_map["prediction"][0], label[0])) diff --git a/python/examples/lac/lac_reader.py b/python/examples/lac/lac_reader.py index c9f31c148123e1975c0102903abf2f2b3b15d3f6..488e7ced1ce27f914f299c45295e82f33c68d6d0 100644 --- a/python/examples/lac/lac_reader.py +++ b/python/examples/lac/lac_reader.py @@ -14,8 +14,10 @@ from paddle_serving_client import Client import sys -reload(sys) -sys.setdefaultencoding('utf-8') +py_version = sys.version_info[0] +if py_version == 2: + reload(sys) + sys.setdefaultencoding('utf-8') import os import io diff --git a/python/examples/mobilenet/daisy.jpg b/python/examples/mobilenet/daisy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7edeca63e5f32e68550ef720d81f59df58a8eabc Binary files /dev/null and b/python/examples/mobilenet/daisy.jpg differ diff --git a/python/examples/mobilenet/mobilenet_tutorial.py b/python/examples/mobilenet/mobilenet_tutorial.py new file mode 100644 index 0000000000000000000000000000000000000000..9550a5ff705d23d3f6a97d8498d5a8b1e4f152b7 --- /dev/null +++ b/python/examples/mobilenet/mobilenet_tutorial.py @@ -0,0 +1,32 @@ +# 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 paddle_serving_client import Client +from paddle_serving_app.reader import Sequential, File2Image, Resize +from paddle_serving_app.reader import CenterCrop, RGB2BGR, Transpose, Div, Normalize + +client = Client() +client.load_client_config( + "mobilenet_v2_imagenet_client/serving_client_conf.prototxt") +client.connect(["127.0.0.1:9393"]) + +seq = Sequential([ + File2Image(), Resize(256), CenterCrop(224), RGB2BGR(), Transpose((2, 0, 1)), + Div(255), Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], True) +]) + +image_file = "daisy.jpg" +img = seq(image_file) +fetch_map = client.predict(feed={"image": img}, fetch=["feature_map"]) +print(fetch_map["feature_map"].reshape(-1)) diff --git a/python/examples/resnet_v2_50/daisy.jpg b/python/examples/resnet_v2_50/daisy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7edeca63e5f32e68550ef720d81f59df58a8eabc Binary files /dev/null and b/python/examples/resnet_v2_50/daisy.jpg differ diff --git a/python/examples/resnet_v2_50/resnet50_debug.py b/python/examples/resnet_v2_50/resnet50_debug.py new file mode 100644 index 0000000000000000000000000000000000000000..62cb1812c5718ae1f9e10e9e9a57d7c1ae6736b7 --- /dev/null +++ b/python/examples/resnet_v2_50/resnet50_debug.py @@ -0,0 +1,31 @@ +# 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 paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop +from paddle_serving_app.reader import RGB2BGR, Transpose, Div, Normalize +from paddle_serving_app import Debugger +import sys + +debugger = Debugger() +debugger.load_model_config(sys.argv[1], gpu=True) + +seq = Sequential([ + File2Image(), Resize(256), CenterCrop(224), RGB2BGR(), Transpose((2, 0, 1)), + Div(255), Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], True) +]) + +image_file = "daisy.jpg" +img = seq(image_file) +fetch_map = debugger.predict(feed={"image": img}, fetch=["feature_map"]) +print(fetch_map["feature_map"].reshape(-1)) diff --git a/python/examples/resnet_v2_50/resnet50_v2_tutorial.py b/python/examples/resnet_v2_50/resnet50_v2_tutorial.py new file mode 100644 index 0000000000000000000000000000000000000000..8d916cbd8145cdc73424a05fdb2855412f4d4fe2 --- /dev/null +++ b/python/examples/resnet_v2_50/resnet50_v2_tutorial.py @@ -0,0 +1,32 @@ +# 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 paddle_serving_client import Client +from paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop +from apddle_serving_app.reader import RGB2BGR, Transpose, Div, Normalize + +client = Client() +client.load_client_config( + "resnet_v2_50_imagenet_client/serving_client_conf.prototxt") +client.connect(["127.0.0.1:9393"]) + +seq = Sequential([ + File2Image(), Resize(256), CenterCrop(224), RGB2BGR(), Transpose((2, 0, 1)), + Div(255), Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], True) +]) + +image_file = "daisy.jpg" +img = seq(image_file) +fetch_map = client.predict(feed={"image": img}, fetch=["feature_map"]) +print(fetch_map["feature_map"].reshape(-1)) diff --git a/python/examples/senta/README.md b/python/examples/senta/README.md index 9d6c3a0221f924e5d8f1893e6c618e3b2f88a3e1..88aac352110850a71ae0f9a28c1a98293f8e0ab9 100644 --- a/python/examples/senta/README.md +++ b/python/examples/senta/README.md @@ -4,6 +4,12 @@ ``` sh get_data.sh ``` +## Install preprocess module + +``` +pip install paddle_serving_app +``` + ## Start http service ``` python senta_web_service.py senta_bilstm_model/ workdir 9292 diff --git a/python/examples/senta/README_CN.md b/python/examples/senta/README_CN.md index bb1e706554a57b29fc784d064dd4b550846f6e76..f5011334db768c5f0869c296769ead7cb38613d8 100644 --- a/python/examples/senta/README_CN.md +++ b/python/examples/senta/README_CN.md @@ -4,6 +4,11 @@ ``` sh get_data.sh ``` +## 安装数据预处理模块 +``` +pip install paddle_serving_app +``` + ## 启动HTTP服务 ``` python senta_web_service.py senta_bilstm_model/ workdir 9292 diff --git a/python/examples/senta/senta_web_service.py b/python/examples/senta/senta_web_service.py index 9a59d9d030ae55584f1b9939a02c782bae3c5011..0c0205e73cdd26231a94b2f0c9c41da84aaca961 100644 --- a/python/examples/senta/senta_web_service.py +++ b/python/examples/senta/senta_web_service.py @@ -39,6 +39,8 @@ class SentaService(WebService): self.show = show def start_lac_service(self): + if not os.path.exists('./lac_serving'): + os.mkdir("./lac_serving") os.chdir('./lac_serving') self.lac_port = self.port + 100 r = os.popen( diff --git a/python/examples/unet_for_image_seg/N0060.jpg b/python/examples/unet_for_image_seg/N0060.jpg new file mode 100644 index 0000000000000000000000000000000000000000..feac2837eaa5ae5db414d9769a0c5a830dde268d Binary files /dev/null and b/python/examples/unet_for_image_seg/N0060.jpg differ diff --git a/python/examples/unet_for_image_seg/seg_client.py b/python/examples/unet_for_image_seg/seg_client.py new file mode 100644 index 0000000000000000000000000000000000000000..9e76b060955ec74492312c8896efaf3946a3f7ab --- /dev/null +++ b/python/examples/unet_for_image_seg/seg_client.py @@ -0,0 +1,33 @@ +# 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 paddle_serving_client import Client +from paddle_serving_app.reader import Sequential, File2Image, Resize, Transpose, BGR2RGB, SegPostprocess +import sys +import cv2 + +client = Client() +client.load_client_config("unet_client/serving_client_conf.prototxt") +client.connect(["127.0.0.1:9494"]) + +preprocess = Sequential( + [File2Image(), Resize( + (512, 512), interpolation=cv2.INTER_LINEAR)]) + +postprocess = SegPostprocess(2) + +im = preprocess("N0060.jpg") +fetch_map = client.predict(feed={"image": im}, fetch=["output"]) +fetch_map["filename"] = filename +postprocess(fetch_map) diff --git a/python/paddle_serving_app/__init__.py b/python/paddle_serving_app/__init__.py index 3db901249df41f5d9cd5846d131ec6cfed376a18..fd9260284b4103f00ca8b9cda8b99173591d23eb 100644 --- a/python/paddle_serving_app/__init__.py +++ b/python/paddle_serving_app/__init__.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from .reader.chinese_bert_reader import ChineseBertReader -from .reader.image_reader import ImageReader +from .reader.image_reader import ImageReader, File2Image, URL2Image, Sequential, Normalize, CenterCrop, Resize from .reader.lac_reader import LACReader from .reader.senta_reader import SentaReader from .models import ServingModels +from .local_predict import Debugger diff --git a/python/paddle_serving_app/local_predict.py b/python/paddle_serving_app/local_predict.py new file mode 100644 index 0000000000000000000000000000000000000000..6620994165306a550204498e5185bb3aacca8ffd --- /dev/null +++ b/python/paddle_serving_app/local_predict.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +""" +# 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. +""" + +import os +import google.protobuf.text_format +import numpy as np +import argparse +import paddle.fluid as fluid +from .proto import general_model_config_pb2 as m_config +from paddle.fluid.core import PaddleTensor +from paddle.fluid.core import AnalysisConfig +from paddle.fluid.core import create_paddle_predictor +import logging + +logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s") +logger = logging.getLogger("fluid") +logger.setLevel(logging.INFO) + + +class Debugger(object): + def __init__(self): + self.feed_names_ = [] + self.fetch_names_ = [] + self.feed_types_ = {} + self.fetch_types_ = {} + self.feed_shapes_ = {} + self.feed_names_to_idx_ = {} + self.fetch_names_to_idx_ = {} + self.fetch_names_to_type_ = {} + + def load_model_config(self, model_path, gpu=False, profile=True, cpu_num=1): + client_config = "{}/serving_server_conf.prototxt".format(model_path) + model_conf = m_config.GeneralModelConfig() + f = open(client_config, 'r') + model_conf = google.protobuf.text_format.Merge( + str(f.read()), model_conf) + config = AnalysisConfig(model_path) + + self.feed_names_ = [var.alias_name for var in model_conf.feed_var] + self.fetch_names_ = [var.alias_name for var in model_conf.fetch_var] + self.feed_names_to_idx_ = {} + self.fetch_names_to_idx_ = {} + + for i, var in enumerate(model_conf.feed_var): + self.feed_names_to_idx_[var.alias_name] = i + self.feed_types_[var.alias_name] = var.feed_type + self.feed_shapes_[var.alias_name] = var.shape + + for i, var in enumerate(model_conf.fetch_var): + self.fetch_names_to_idx_[var.alias_name] = i + self.fetch_names_to_type_[var.alias_name] = var.fetch_type + + if not gpu: + config.disable_gpu() + else: + config.enable_use_gpu(100, 0) + if profile: + config.enable_profile() + config.set_cpu_math_library_num_threads(cpu_num) + config.switch_ir_optim(False) + + self.predictor = create_paddle_predictor(config) + + def predict(self, feed=None, fetch=None): + if feed is None or fetch is None: + raise ValueError("You should specify feed and fetch for prediction") + fetch_list = [] + if isinstance(fetch, str): + fetch_list = [fetch] + elif isinstance(fetch, list): + fetch_list = fetch + else: + raise ValueError("Fetch only accepts string and list of string") + + feed_batch = [] + if isinstance(feed, dict): + feed_batch.append(feed) + elif isinstance(feed, list): + feed_batch = feed + else: + raise ValueError("Feed only accepts dict and list of dict") + + int_slot_batch = [] + float_slot_batch = [] + int_feed_names = [] + float_feed_names = [] + int_shape = [] + float_shape = [] + fetch_names = [] + counter = 0 + batch_size = len(feed_batch) + + for key in fetch_list: + if key in self.fetch_names_: + fetch_names.append(key) + + if len(fetch_names) == 0: + raise ValueError( + "Fetch names should not be empty or out of saved fetch list.") + return {} + + inputs = [] + for name in self.feed_names_: + inputs.append(PaddleTensor(feed[name][np.newaxis, :])) + + outputs = self.predictor.run(inputs) + fetch_map = {} + for name in fetch: + fetch_map[name] = outputs[self.fetch_names_to_idx_[ + name]].as_ndarray() + return fetch_map diff --git a/python/paddle_serving_app/models/model_list.py b/python/paddle_serving_app/models/model_list.py index 6709c8aea06c3fa0cce2acdc0cbaf7d4a9c9c64e..a2019997968ce21a30669b2acd1421355b1e0fdd 100644 --- a/python/paddle_serving_app/models/model_list.py +++ b/python/paddle_serving_app/models/model_list.py @@ -20,78 +20,49 @@ from collections import OrderedDict class ServingModels(object): def __init__(self): self.model_dict = OrderedDict() - #senta - for key in [ - "senta_bilstm", "senta_bow", "senta_cnn", "senta_gru", - "senta_lstm" - ]: - self.model_dict[ - key] = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/text/SentimentAnalysis/" + key + ".tar.gz" - #image classification - for key in [ - "alexnet_imagenet", - "darknet53-imagenet", - "densenet121_imagenet", - "densenet161_imagenet", - "densenet169_imagenet", - "densenet201_imagenet", - "densenet264_imagenet" - "dpn107_imagenet", - "dpn131_imagenet", - "dpn68_imagenet", - "dpn92_imagenet", - "dpn98_imagenet", - "efficientnetb0_imagenet", - "efficientnetb1_imagenet", - "efficientnetb2_imagenet", - "efficientnetb3_imagenet", - "efficientnetb4_imagenet", - "efficientnetb5_imagenet", - "efficientnetb6_imagenet", - "googlenet_imagenet", - "inception_v4_imagenet", - "inception_v2_imagenet", - "nasnet_imagenet", - "pnasnet_imagenet", - "resnet_v2_101_imagenet", - "resnet_v2_151_imagenet", - "resnet_v2_18_imagenet", - "resnet_v2_34_imagenet", - "resnet_v2_50_imagenet", - "resnext101_32x16d_wsl", - "resnext101_32x32d_wsl", - "resnext101_32x48d_wsl", - "resnext101_32x8d_wsl", - "resnext101_32x4d_imagenet", - "resnext101_64x4d_imagenet", - "resnext101_vd_32x4d_imagenet", - "resnext101_vd_64x4d_imagenet", - "resnext152_64x4d_imagenet", - "resnext152_vd_64x4d_imagenet", - "resnext50_64x4d_imagenet", - "resnext50_vd_32x4d_imagenet", - "resnext50_vd_64x4d_imagenet", - "se_resnext101_32x4d_imagenet", - "se_resnext50_32x4d_imagenet", - "shufflenet_v2_imagenet", - "vgg11_imagenet", - "vgg13_imagenet", - "vgg16_imagenet", - "vgg19_imagenet", - "xception65_imagenet", - "xception71_imagenet", - ]: - self.model_dict[ - key] = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/image/ImageClassification/" + key + ".tar.gz" + self.model_dict[ + "SentimentAnalysis"] = ["senta_bilstm", "senta_bow", "senta_cnn"] + self.model_dict["SemanticRepresentation"] = ["ernie_base"] + self.model_dict["ChineseWordSegmentation"] = ["lac"] + self.model_dict["ObjectDetection"] = ["faster_rcnn", "yolov3"] + self.model_dict["ImageSegmentation"] = ["unet", "deeplabv3"] + self.model_dict["ImageClassification"] = [ + "resnet_v2_50_imagenet", "mobilenet_v2_imagenet" + ] + + image_class_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/image/ImageClassification/" + image_seg_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/image/ImageSegmentation/" + object_detection_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/image/ObjectDetection/" + senta_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/text/SentimentAnalysis/" + semantic_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/text/SemanticRepresentation/" + wordseg_url = "https://paddle-serving.bj.bcebos.com/paddle_hub_models/text/ChineseWordSegmentation/" + + self.url_dict = {} + + def pack_url(model_dict, key, url): + for i, value in enumerate(model_dict[key]): + self.url_dict[model_dict[key][i]] = url + model_dict[key][ + i] + ".tar.gz" + + pack_url(self.model_dict, "SentimentAnalysis", senta_url) + pack_url(self.model_dict, "SemanticRepresentation", semantic_url) + pack_url(self.model_dict, "ChineseWordSegmentation", wordseg_url) + pack_url(self.model_dict, "ObjectDetection", object_detection_url) + pack_url(self.model_dict, "ImageSegmentation", image_seg_url) + pack_url(self.model_dict, "ImageClassification", image_class_url) def get_model_list(self): - return (self.model_dict.keys()) + return self.model_dict def download(self, model_name): - if model_name in self.model_dict: - url = self.model_dict[model_name] + if model_name in self.url_dict: + url = self.url_dict[model_name] r = os.system('wget ' + url + ' --no-check-certificate') + def get_tutorial(self, model_name): + if model_name in self.tutorial_url: + return "Tutorial of {} to be added".format(model_name) + if __name__ == "__main__": models = ServingModels() diff --git a/python/paddle_serving_app/package.py b/python/paddle_serving_app/package.py index 98e42f365397e6ecae5171c47eb1cfabee182a7d..e27914931d4f64c98627cd54025fcf87ac0f241d 100644 --- a/python/paddle_serving_app/package.py +++ b/python/paddle_serving_app/package.py @@ -20,6 +20,7 @@ Usage: """ import argparse +import sys from .models import ServingModels @@ -29,6 +30,8 @@ def parse_args(): # pylint: disable=doc-string-missing "--get_model", type=str, default="", help="Download a specific model") parser.add_argument( '--list_model', nargs='*', default=None, help="List Models") + parser.add_argument( + '--tutorial', type=str, default="", help="Get running command") return parser.parse_args() @@ -36,18 +39,33 @@ if __name__ == "__main__": args = parse_args() if args.list_model != None: model_handle = ServingModels() - model_names = model_handle.get_model_list() - for key in model_names: - print(key) + model_dict = model_handle.get_model_list() + # Task level model list + # Text Classification, Semantic Representation + # Image Classification, Object Detection, Image Segmentation + for key in model_dict: + print("-----------------------------------------------") + print("{}: {}".format(key, " | ".join(model_dict[key]))) + elif args.get_model != "": model_handle = ServingModels() - model_names = model_handle.get_model_list() - if args.get_model not in model_names: + model_dict = model_handle.url_dict + if args.get_model not in model_dict: print( "Your model name does not exist in current model list, stay tuned" ) sys.exit(0) model_handle.download(args.get_model) + elif args.tutorial != "": + model_handle = ServingModels() + model_dict = model_handle.url_dict + if args.get_model not in model_dict: + print( + "Your model name does not exist in current model list, stay tuned" + ) + sys.exit(0) + tutorial_str = model_handle.get_tutorial() + print(tutorial_str) else: print("Wrong argument") print(""" diff --git a/python/paddle_serving_app/reader/__init__.py b/python/paddle_serving_app/reader/__init__.py index 847ddc47ac89114f2012bc6b9990a69abfe39fb3..01cad9e6bbdbe11191e3bc44ec2c63f2db3939bc 100644 --- a/python/paddle_serving_app/reader/__init__.py +++ b/python/paddle_serving_app/reader/__init__.py @@ -11,3 +11,4 @@ # 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 .image_reader import ImageReader, File2Image, URL2Image, Sequential, Normalize, CenterCrop, Resize, Transpose, Div, RGB2BGR, BGR2RGB, RCNNPostprocess, SegPostprocess diff --git a/python/paddle_serving_app/reader/daisy.jpg b/python/paddle_serving_app/reader/daisy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7edeca63e5f32e68550ef720d81f59df58a8eabc Binary files /dev/null and b/python/paddle_serving_app/reader/daisy.jpg differ diff --git a/python/paddle_serving_app/reader/functional.py b/python/paddle_serving_app/reader/functional.py new file mode 100644 index 0000000000000000000000000000000000000000..4240641dd99fceb278ff60a5ba1dbb5275e534aa --- /dev/null +++ b/python/paddle_serving_app/reader/functional.py @@ -0,0 +1,68 @@ +# 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. + +import cv2 +import numpy as np + + +def transpose(img, transpose_target): + img = img.transpose(transpose_target) + return img + + +def normalize(img, mean, std, channel_first): + # need to optimize here + if channel_first: + img_mean = np.array(mean).reshape((3, 1, 1)) + img_std = np.array(std).reshape((3, 1, 1)) + else: + img_mean = np.array(mean).reshape((1, 1, 3)) + img_std = np.array(std).reshape((1, 1, 3)) + img -= img_mean + img /= img_std + return img + + +def crop(img, target_size, center): + height, width = img.shape[:2] + size = target_size + if center == True: + w_start = (width - size) // 2 + h_start = (height - size) // 2 + else: + w_start = np.random.randint(0, width - size + 1) + h_start = np.random.randint(0, height - size + 1) + w_end = w_start + size + h_end = h_start + size + img = img[h_start:h_end, w_start:w_end, :] + return img + + +def resize(img, target_size, max_size=2147483647, interpolation=None): + if isinstance(target_size, tuple): + resized_width = min(target_size[0], max_size) + resized_height = min(target_size[1], max_size) + else: + im_max_size = max(img.shape[0], img.shape[1]) + percent = float(target_size) / min(img.shape[0], img.shape[1]) + if np.round(percent * im_max_size) > max_size: + percent = float(max_size) / float(im_max_size) + resized_width = int(round(img.shape[1] * percent)) + resized_height = int(round(img.shape[0] * percent)) + if interpolation: + resized = cv2.resize( + img, (resized_width, resized_height), interpolation=interpolation) + else: + resized = cv2.resize(img, (resized_width, resized_height)) + return resized diff --git a/python/paddle_serving_app/reader/image_reader.py b/python/paddle_serving_app/reader/image_reader.py index 2647eb6fdf3ca0f1682ca794051b9d0dd95a9a07..8791e94ba8456f25deed1cbd5a2262218327c44e 100644 --- a/python/paddle_serving_app/reader/image_reader.py +++ b/python/paddle_serving_app/reader/image_reader.py @@ -11,9 +11,472 @@ # 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. - import cv2 +import os +import urllib import numpy as np +import base64 +from . import functional as F +from PIL import Image, ImageDraw +import json + +_cv2_interpolation_to_str = {cv2.INTER_LINEAR: "cv2.INTER_LINEAR", None: "None"} + + +def generate_colormap(num_classes): + color_map = num_classes * [0, 0, 0] + for i in range(0, num_classes): + j = 0 + lab = i + while lab: + color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) + color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) + color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) + j += 1 + lab >>= 3 + color_map = [color_map[i:i + 3] for i in range(0, len(color_map), 3)] + return color_map + + +class SegPostprocess(object): + def __init__(self, class_num): + self.class_num = class_num + + def __call__(self, image_with_result): + if "filename" not in image_with_result: + raise ("filename should be specified in postprocess") + img_name = image_with_result["filename"] + ori_img = cv2.imread(img_name, -1) + ori_shape = ori_img.shape + mask = None + for key in image_with_result: + if ".lod" in key or "filename" in key: + continue + mask = image_with_result[key] + if mask is None: + raise ("segment mask should be specified in postprocess") + mask = mask.astype("uint8") + mask_png = mask.reshape((512, 512, 1)) + #score_png = mask_png[:, :, np.newaxis] + score_png = mask_png + score_png = np.concatenate([score_png] * 3, axis=2) + color_map = generate_colormap(self.class_num) + for i in range(score_png.shape[0]): + for j in range(score_png.shape[1]): + score_png[i, j] = color_map[score_png[i, j, 0]] + ext_pos = img_name.rfind(".") + img_name_fix = img_name[:ext_pos] + "_" + img_name[ext_pos + 1:] + mask_save_name = img_name_fix + "_mask.png" + cv2.imwrite(mask_save_name, mask_png, [cv2.CV_8UC1]) + vis_result_name = img_name_fix + "_result.png" + result_png = score_png + + result_png = cv2.resize( + result_png, + ori_shape[:2], + fx=0, + fy=0, + interpolation=cv2.INTER_CUBIC) + cv2.imwrite(vis_result_name, result_png, [cv2.CV_8UC1]) + + +class RCNNPostprocess(object): + def __init__(self, label_file, output_dir): + self.output_dir = output_dir + self.label_file = label_file + self.label_list = [] + with open(label_file) as fin: + for line in fin: + self.label_list.append(line.strip()) + self.clsid2catid = {i: i for i in range(len(self.label_list))} + self.catid2name = {i: name for i, name in enumerate(self.label_list)} + + def _offset_to_lengths(self, lod): + offset = lod[0] + lengths = [offset[i + 1] - offset[i] for i in range(len(offset) - 1)] + return [lengths] + + def _bbox2out(self, results, clsid2catid, is_bbox_normalized=False): + xywh_res = [] + for t in results: + bboxes = t['bbox'][0] + lengths = t['bbox'][1][0] + if bboxes.shape == (1, 1) or bboxes is None: + continue + + k = 0 + for i in range(len(lengths)): + num = lengths[i] + for j in range(num): + dt = bboxes[k] + clsid, score, xmin, ymin, xmax, ymax = dt.tolist() + catid = (clsid2catid[int(clsid)]) + + if is_bbox_normalized: + xmin, ymin, xmax, ymax = \ + self.clip_bbox([xmin, ymin, xmax, ymax]) + w = xmax - xmin + h = ymax - ymin + im_shape = t['im_shape'][0][i].tolist() + im_height, im_width = int(im_shape[0]), int(im_shape[1]) + xmin *= im_width + ymin *= im_height + w *= im_width + h *= im_height + else: + w = xmax - xmin + 1 + h = ymax - ymin + 1 + + bbox = [xmin, ymin, w, h] + coco_res = { + 'category_id': catid, + 'bbox': bbox, + 'score': score + } + xywh_res.append(coco_res) + k += 1 + return xywh_res + + def _get_bbox_result(self, fetch_map, fetch_name, clsid2catid): + result = {} + is_bbox_normalized = False + output = fetch_map[fetch_name] + lod = [fetch_map[fetch_name + '.lod']] + lengths = self._offset_to_lengths(lod) + np_data = np.array(output) + result['bbox'] = (np_data, lengths) + result['im_id'] = np.array([[0]]) + + bbox_results = self._bbox2out([result], clsid2catid, is_bbox_normalized) + return bbox_results + + def color_map(self, num_classes): + color_map = num_classes * [0, 0, 0] + for i in range(0, num_classes): + j = 0 + lab = i + while lab: + color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) + color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) + color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) + j += 1 + lab >>= 3 + color_map = np.array(color_map).reshape(-1, 3) + return color_map + + def draw_bbox(self, image, catid2name, bboxes, threshold, color_list): + """ + draw bbox on image + """ + draw = ImageDraw.Draw(image) + + for dt in np.array(bboxes): + catid, bbox, score = dt['category_id'], dt['bbox'], dt['score'] + if score < threshold: + continue + + xmin, ymin, w, h = bbox + xmax = xmin + w + ymax = ymin + h + + color = tuple(color_list[catid]) + + # draw bbox + draw.line( + [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), + (xmin, ymin)], + width=2, + fill=color) + + # draw label + text = "{} {:.2f}".format(catid2name[catid], score) + tw, th = draw.textsize(text) + draw.rectangle( + [(xmin + 1, ymin - th), (xmin + tw + 1, ymin)], fill=color) + draw.text((xmin + 1, ymin - th), text, fill=(255, 255, 255)) + + return image + + def visualize(self, infer_img, bbox_results, catid2name, num_classes): + image = Image.open(infer_img).convert('RGB') + color_list = self.color_map(num_classes) + image = self.draw_bbox(image, self.catid2name, bbox_results, 0.5, + color_list) + image_path = os.path.split(infer_img)[-1] + if not os.path.exists(self.output_dir): + os.makedirs(self.output_dir) + out_path = os.path.join(self.output_dir, image_path) + image.save(out_path, quality=95) + + def __call__(self, image_with_bbox): + fetch_name = "" + for key in image_with_bbox: + if key == "image": + continue + if ".lod" in key: + continue + fetch_name = key + bbox_result = self._get_bbox_result(image_with_bbox, fetch_name, + self.clsid2catid) + if os.path.isdir(self.output_dir) is False: + os.mkdir(self.output_dir) + self.visualize(image_with_bbox["image"], bbox_result, self.catid2name, + len(self.label_list)) + if os.path.isdir(self.output_dir) is False: + os.mkdir(self.output_dir) + bbox_file = os.path.join(self.output_dir, 'bbox.json') + with open(bbox_file, 'w') as f: + json.dump(bbox_result, f, indent=4) + + def __repr__(self): + return self.__class__.__name__ + "label_file: {1}, output_dir: {2}".format( + self.label_file, self.output_dir) + + +class Sequential(object): + """ + Args: + sequence (sequence of ``Transform`` objects): list of transforms to chain. + + This API references some of the design pattern of torchvision + Users can simply use this API in training as well + + Example: + >>> image_reader.Sequnece([ + >>> transforms.CenterCrop(10), + >>> ]) + """ + + def __init__(self, transforms): + self.transforms = transforms + + def __call__(self, img): + for t in self.transforms: + img = t(img) + return img + + def __repr__(self): + format_string_ = self.__class__.__name__ + '(' + for t in self.transforms: + format_string_ += '\n' + format_string_ += ' {0}'.format(t) + format_string_ += '\n)' + return format_string_ + + +class RGB2BGR(object): + def __init__(self): + pass + + def __call__(self, img): + return img[:, :, ::-1] + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class BGR2RGB(object): + def __init__(self): + pass + + def __call__(self, img): + return img[:, :, ::-1] + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class File2Image(object): + def __init__(self): + pass + + def __call__(self, img_path): + fin = open(img_path) + sample = fin.read() + data = np.fromstring(sample, np.uint8) + img = cv2.imdecode(data, cv2.IMREAD_COLOR) + ''' + img = cv2.imread(img_path, -1) + channels = img.shape[2] + ori_h = img.shape[0] + ori_w = img.shape[1] + ''' + return img + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class URL2Image(object): + def __init__(self): + pass + + def __call__(self, img_url): + resp = urllib.urlopen(img_url) + sample = resp.read() + data = np.fromstring(sample, np.uint8) + img = cv2.imdecode(data, cv2.IMREAD_COLOR) + return img + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class Base64ToImage(object): + def __init__(self): + pass + + def __call__(self, img_base64): + img = base64.b64decode(img_base64) + return img + + def __repr__(self): + return self.__class__.__name__ + "()" + + +class Div(object): + """ divide by some float number """ + + def __init__(self, value): + self.value = value + + def __call__(self, img): + """ + Args: + img (numpy array): (int8 numpy array) + + Returns: + img (numpy array): (float32 numpy array) + """ + img = img.astype('float32') / self.value + + return img + + def __repr__(self): + return self.__class__.__name__ + "({})".format(self.value) + + +class Normalize(object): + """Normalize a tensor image with mean and standard deviation. + Given mean: ``(M1,...,Mn)`` and std: ``(S1,..,Sn)`` for ``n`` channels, this transform + will normalize each channel of the input ``torch.*Tensor`` i.e. + ``output[channel] = (input[channel] - mean[channel]) / std[channel]`` + + .. note:: + This transform acts out of place, i.e., it does not mutate the input tensor. + + Args: + mean (sequence): Sequence of means for each channel. + std (sequence): Sequence of standard deviations for each channel. + + """ + + def __init__(self, mean, std, channel_first=False): + self.mean = mean + self.std = std + self.channel_first = channel_first + + def __call__(self, img): + """ + Args: + img (numpy array): (C, H, W) to be normalized. + + Returns: + Tensor: Normalized Tensor image. + """ + return F.normalize(img, self.mean, self.std, self.channel_first) + + def __repr__(self): + return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, + self.std) + + +class Lambda(object): + """Apply a user-defined lambda as a transform. + Very shame to just copy from + https://github.com/pytorch/vision/blob/master/torchvision/transforms/transforms.py#L301 + + Args: + lambd (function): Lambda/function to be used for transform. + """ + + def __init__(self, lambd): + assert callable(lambd), repr(type(lambd) + .__name__) + " object is not callable" + self.lambd = lambd + + def __call__(self, img): + return self.lambd(img) + + def __repr__(self): + return self.__class__.__name__ + '()' + + +class CenterCrop(object): + """Crops the given Image at the center. + + Args: + size (sequence or int): Desired output size of the crop. If size is an + int instead of sequence like (h, w), a square crop (size, size) is + made. + """ + + def __init__(self, size): + self.size = size + + def __call__(self, img): + """ + Args: + img (numpy array): Image to be cropped. + + Returns: + numpy array Image: Cropped image. + """ + return F.crop(img, self.size, True) + + def __repr__(self): + return self.__class__.__name__ + '(size={0})'.format(self.size) + + +class Resize(object): + """Resize the input numpy array Image to the given size. + + Args: + size (sequence or int): Desired output size. If size is a sequence like + (h, w), output size will be matched to this. If size is an int, + smaller edge of the image will be matched to this number. + i.e, if height > width, then image will be rescaled to + (size * height / width, size) + interpolation (int, optional): Desired interpolation. Default is + ``None`` + """ + + def __init__(self, size, max_size=2147483647, interpolation=None): + self.size = size + self.max_size = max_size + self.interpolation = interpolation + + def __call__(self, img): + return F.resize(img, self.size, self.max_size, self.interpolation) + + def __repr__(self): + return self.__class__.__name__ + '(size={0}, max_size={1}, interpolation={2})'.format( + self.size, self.max_size, + _cv2_interpolation_to_str[self.interpolation]) + + +class Transpose(object): + def __init__(self, transpose_target): + self.transpose_target = transpose_target + + def __call__(self, img): + return F.transpose(img, self.transpose_target) + return img + + def __repr__(self): + format_string = self.__class__.__name__ + \ + "({})".format(self.transpose_target) + return format_string class ImageReader(): diff --git a/python/paddle_serving_app/reader/lac_reader.py b/python/paddle_serving_app/reader/lac_reader.py index a0ed0bbe44460993649675f627310e1a7b53c344..720bbf9c61051dcdc877f0a1f4933718be32263d 100644 --- a/python/paddle_serving_app/reader/lac_reader.py +++ b/python/paddle_serving_app/reader/lac_reader.py @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from paddle_serving_client import Client import sys -reload(sys) -sys.setdefaultencoding('utf-8') +py_version = sys.version_info[0] +if py_version == 2: + reload(sys) + sys.setdefaultencoding('utf-8') import os import io diff --git a/python/paddle_serving_app/reader/test_image_reader.py b/python/paddle_serving_app/reader/test_image_reader.py new file mode 100644 index 0000000000000000000000000000000000000000..f2dc52771919651f586e1e9720fe0ae8f82e8c12 --- /dev/null +++ b/python/paddle_serving_app/reader/test_image_reader.py @@ -0,0 +1,30 @@ +# 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 image_reader import File2Image +from image_reader import URL2Image +from image_reader import Sequential +from image_reader import Normalize +from image_reader import CenterCrop +from image_reader import Resize + +seq = Sequential([ + File2Image(), CenterCrop(30), + Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), Resize((5, 5)) +]) + +url = "daisy.jpg" +for x in range(100): + img = seq(url) + print(img.shape) diff --git a/python/paddle_serving_app/version.py b/python/paddle_serving_app/version.py index 80f647be56d09740adfb9d68dd47bb0b1fa2c985..766bf4e397e46153193b1e3cac6fed5323241c45 100644 --- a/python/paddle_serving_app/version.py +++ b/python/paddle_serving_app/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. """ Paddle Serving App version string """ -serving_app_version = "0.0.1" +serving_app_version = "0.0.3" diff --git a/python/paddle_serving_client/__init__.py b/python/paddle_serving_client/__init__.py index 3380934931d5872afca81934724f72614bb64a13..d97a88f51cc20db1a5c3a6acafd949cbdd153112 100644 --- a/python/paddle_serving_client/__init__.py +++ b/python/paddle_serving_client/__init__.py @@ -112,7 +112,6 @@ class Client(object): self.feed_shapes_ = {} self.feed_types_ = {} self.feed_names_to_idx_ = {} - self.rpath() self.pid = os.getpid() self.predictor_sdk_ = None self.producers = [] @@ -121,12 +120,6 @@ class Client(object): self.all_numpy_input = True self.has_numpy_input = False - def rpath(self): - lib_path = os.path.dirname(paddle_serving_client.__file__) - client_path = os.path.join(lib_path, 'serving_client.so') - lib_path = os.path.join(lib_path, 'lib') - os.system('patchelf --set-rpath {} {}'.format(lib_path, client_path)) - def load_client_config(self, path): from .serving_client import PredictorClient from .serving_client import PredictorRes @@ -267,10 +260,16 @@ class Client(object): if i == 0: int_feed_names.append(key) if isinstance(feed_i[key], np.ndarray): + if key in self.lod_tensor_set: + raise ValueError( + "LodTensor var can not be ndarray type.") int_shape.append(list(feed_i[key].shape)) else: int_shape.append(self.feed_shapes_[key]) if isinstance(feed_i[key], np.ndarray): + if key in self.lod_tensor_set: + raise ValueError( + "LodTensor var can not be ndarray type.") #int_slot.append(np.reshape(feed_i[key], (-1)).tolist()) int_slot.append(feed_i[key]) self.has_numpy_input = True @@ -281,10 +280,16 @@ class Client(object): if i == 0: float_feed_names.append(key) if isinstance(feed_i[key], np.ndarray): + if key in self.lod_tensor_set: + raise ValueError( + "LodTensor var can not be ndarray type.") float_shape.append(list(feed_i[key].shape)) else: float_shape.append(self.feed_shapes_[key]) if isinstance(feed_i[key], np.ndarray): + if key in self.lod_tensor_set: + raise ValueError( + "LodTensor var can not be ndarray type.") #float_slot.append(np.reshape(feed_i[key], (-1)).tolist()) float_slot.append(feed_i[key]) self.has_numpy_input = True diff --git a/python/paddle_serving_client/io/__init__.py b/python/paddle_serving_client/io/__init__.py index 74a6ca871b5c1e32b3c1ecbc6656c95d7c78a399..4f174866e5521577ba35f39216f7dd0793879a6c 100644 --- a/python/paddle_serving_client/io/__init__.py +++ b/python/paddle_serving_client/io/__init__.py @@ -103,17 +103,21 @@ def save_model(server_model_folder, fout.write(config.SerializeToString()) -def inference_model_to_serving(infer_model, serving_client, serving_server): +def inference_model_to_serving(dirname, + model_filename=None, + params_filename=None, + serving_server="serving_server", + serving_client="serving_client"): place = fluid.CPUPlace() exe = fluid.Executor(place) inference_program, feed_target_names, fetch_targets = \ - fluid.io.load_inference_model(dirname=infer_model, executor=exe) + fluid.io.load_inference_model(dirname=dirname, executor=exe, model_filename=model_filename, params_filename=params_filename) feed_dict = { x: inference_program.global_block().var(x) for x in feed_target_names } fetch_dict = {x.name: x for x in fetch_targets} - save_model(serving_client, serving_server, feed_dict, fetch_dict, + save_model(serving_server, serving_client, feed_dict, fetch_dict, inference_program) feed_names = feed_dict.keys() fetch_names = fetch_dict.keys() diff --git a/python/paddle_serving_client/version.py b/python/paddle_serving_client/version.py index 99322ee8280a66a54371b296905d54f0766b016d..4870767dfcb95f9502dfa5880a85b1c11c62964f 100644 --- a/python/paddle_serving_client/version.py +++ b/python/paddle_serving_client/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """ Paddle Serving Client version string """ -serving_client_version = "0.2.0" -serving_server_version = "0.2.0" -module_proto_version = "0.2.0" +serving_client_version = "0.2.2" +serving_server_version = "0.2.2" +module_proto_version = "0.2.2" diff --git a/python/paddle_serving_server/__init__.py b/python/paddle_serving_server/__init__.py index a58fb11ac3ee1fbe5086ae4381f6d6208c0c73ec..971359fca0df3a122b28889e0711c86364a1c45d 100644 --- a/python/paddle_serving_server/__init__.py +++ b/python/paddle_serving_server/__init__.py @@ -127,6 +127,7 @@ class Server(object): self.model_toolkit_conf = None self.resource_conf = None self.memory_optimization = False + self.ir_optimization = False self.model_conf = None self.workflow_fn = "workflow.prototxt" self.resource_fn = "resource.prototxt" @@ -175,6 +176,9 @@ class Server(object): def set_memory_optimize(self, flag=False): self.memory_optimization = flag + def set_ir_optimize(self, flag=False): + self.ir_optimization = flag + def check_local_bin(self): if "SERVING_BIN" in os.environ: self.use_local_bin = True @@ -195,6 +199,7 @@ class Server(object): engine.enable_batch_align = 0 engine.model_data_path = model_config_path engine.enable_memory_optimization = self.memory_optimization + engine.enable_ir_optimization = self.ir_optimization engine.static_optimization = False engine.force_update_static_cache = False @@ -244,7 +249,7 @@ class Server(object): workflow_oi_config_path = None if isinstance(model_config_paths, str): # If there is only one model path, use the default infer_op. - # Because there are several infer_op type, we need to find + # Because there are several infer_op type, we need to find # it from workflow_conf. default_engine_names = [ 'general_infer_0', 'general_dist_kv_infer_0', @@ -284,8 +289,8 @@ class Server(object): # check config here # print config here - def use_mkl(self): - self.mkl_flag = True + def use_mkl(self, flag): + self.mkl_flag = flag def get_device_version(self): avx_flag = False @@ -300,6 +305,10 @@ class Server(object): else: device_version = "serving-cpu-avx-openblas-" else: + if mkl_flag: + print( + "Your CPU does not support AVX, server will running with noavx-openblas mode." + ) device_version = "serving-cpu-noavx-openblas-" return device_version diff --git a/python/paddle_serving_server/serve.py b/python/paddle_serving_server/serve.py index 395177a8c77e5c608c2e0364b1d43ac534172d66..70aafbf5c3da4d1a2a8ec50ce5a2258383863057 100644 --- a/python/paddle_serving_server/serve.py +++ b/python/paddle_serving_server/serve.py @@ -41,6 +41,9 @@ def parse_args(): # pylint: disable=doc-string-missing "--device", type=str, default="cpu", help="Type of device") parser.add_argument( "--mem_optim", type=bool, default=False, help="Memory optimize") + parser.add_argument( + "--ir_optim", type=bool, default=False, help="Graph optimize") + parser.add_argument("--use_mkl", type=bool, default=False, help="Use MKL") parser.add_argument( "--max_body_size", type=int, @@ -57,7 +60,9 @@ def start_standard_model(): # pylint: disable=doc-string-missing workdir = args.workdir device = args.device mem_optim = args.mem_optim + ir_optim = args.ir_optim max_body_size = args.max_body_size + use_mkl = args.use_mkl if model == "": print("You must specify your serving model") @@ -78,6 +83,8 @@ def start_standard_model(): # pylint: disable=doc-string-missing server.set_op_sequence(op_seq_maker.get_op_sequence()) server.set_num_threads(thread_num) server.set_memory_optimize(mem_optim) + server.set_ir_optimize(ir_optim) + server.use_mkl(use_mkl) server.set_max_body_size(max_body_size) server.set_port(port) diff --git a/python/paddle_serving_server/version.py b/python/paddle_serving_server/version.py index 99322ee8280a66a54371b296905d54f0766b016d..4870767dfcb95f9502dfa5880a85b1c11c62964f 100644 --- a/python/paddle_serving_server/version.py +++ b/python/paddle_serving_server/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """ Paddle Serving Client version string """ -serving_client_version = "0.2.0" -serving_server_version = "0.2.0" -module_proto_version = "0.2.0" +serving_client_version = "0.2.2" +serving_server_version = "0.2.2" +module_proto_version = "0.2.2" diff --git a/python/paddle_serving_server_gpu/__init__.py b/python/paddle_serving_server_gpu/__init__.py index 5fa4f010f2112bd400b81ba2f616e4ebe963a810..5a06bd712a836617047b0cc947956fc5d2213daa 100644 --- a/python/paddle_serving_server_gpu/__init__.py +++ b/python/paddle_serving_server_gpu/__init__.py @@ -47,6 +47,8 @@ def serve_args(): "--name", type=str, default="None", help="Default service name") parser.add_argument( "--mem_optim", type=bool, default=False, help="Memory optimize") + parser.add_argument( + "--ir_optim", type=bool, default=False, help="Graph optimize") parser.add_argument( "--max_body_size", type=int, @@ -156,6 +158,7 @@ class Server(object): self.model_toolkit_conf = None self.resource_conf = None self.memory_optimization = False + self.ir_optimization = False self.model_conf = None self.workflow_fn = "workflow.prototxt" self.resource_fn = "resource.prototxt" @@ -204,6 +207,9 @@ class Server(object): def set_memory_optimize(self, flag=False): self.memory_optimization = flag + def set_ir_optimize(self, flag=False): + self.ir_optimization = flag + def check_local_bin(self): if "SERVING_BIN" in os.environ: self.use_local_bin = True @@ -240,6 +246,7 @@ class Server(object): engine.enable_batch_align = 0 engine.model_data_path = model_config_path engine.enable_memory_optimization = self.memory_optimization + engine.enable_ir_optimization = self.ir_optimization engine.static_optimization = False engine.force_update_static_cache = False diff --git a/python/paddle_serving_server_gpu/serve.py b/python/paddle_serving_server_gpu/serve.py index 512b5ec0a7d15a030afdcaa5e8daa344b29fb96e..297ff25d2084bead186fa4b9037e5de8282df0fe 100644 --- a/python/paddle_serving_server_gpu/serve.py +++ b/python/paddle_serving_server_gpu/serve.py @@ -35,6 +35,7 @@ def start_gpu_card_model(index, gpuid, args): # pylint: disable=doc-string-miss thread_num = args.thread model = args.model mem_optim = args.mem_optim + ir_optim = args.ir_optim max_body_size = args.max_body_size workdir = "{}_{}".format(args.workdir, gpuid) @@ -57,6 +58,7 @@ def start_gpu_card_model(index, gpuid, args): # pylint: disable=doc-string-miss server.set_op_sequence(op_seq_maker.get_op_sequence()) server.set_num_threads(thread_num) server.set_memory_optimize(mem_optim) + server.set_ir_optimize(ir_optim) server.set_max_body_size(max_body_size) server.load_model_config(model) diff --git a/python/paddle_serving_server_gpu/version.py b/python/paddle_serving_server_gpu/version.py index 99322ee8280a66a54371b296905d54f0766b016d..4870767dfcb95f9502dfa5880a85b1c11c62964f 100644 --- a/python/paddle_serving_server_gpu/version.py +++ b/python/paddle_serving_server_gpu/version.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. """ Paddle Serving Client version string """ -serving_client_version = "0.2.0" -serving_server_version = "0.2.0" -module_proto_version = "0.2.0" +serving_client_version = "0.2.2" +serving_server_version = "0.2.2" +module_proto_version = "0.2.2" diff --git a/python/setup.py.app.in b/python/setup.py.app.in index d981caa7c34ff1f84d4cdee0e64a2b03e47b7b66..77099e667e880f3f62ab4cde9d5ae3b6295d1b90 100644 --- a/python/setup.py.app.in +++ b/python/setup.py.app.in @@ -42,10 +42,11 @@ if '${PACK}' == 'ON': REQUIRED_PACKAGES = [ - 'six >= 1.10.0', 'sentencepiece' + 'six >= 1.10.0', 'sentencepiece', 'opencv-python', 'pillow' ] packages=['paddle_serving_app', + 'paddle_serving_app.proto', 'paddle_serving_app.reader', 'paddle_serving_app.utils', 'paddle_serving_app.models', @@ -54,6 +55,8 @@ packages=['paddle_serving_app', package_data={} package_dir={'paddle_serving_app': '${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_app', + 'paddle_serving_app.proto': + '${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_app/proto', 'paddle_serving_app.reader': '${PADDLE_SERVING_BINARY_DIR}/python/paddle_serving_app/reader', 'paddle_serving_app.utils': diff --git a/python/setup.py.client.in b/python/setup.py.client.in index 58061f7c887be23223f554d383c98bd75fb4828b..c46a58733a2c6ac6785e0047ab19080e92dd5695 100644 --- a/python/setup.py.client.in +++ b/python/setup.py.client.in @@ -26,7 +26,7 @@ from setuptools import setup from paddle_serving_client.version import serving_client_version from pkg_resources import DistributionNotFound, get_distribution -py_version = sys.version_info[0] +py_version = sys.version_info def python_version(): return [int(v) for v in platform.python_version().split(".")] @@ -39,7 +39,12 @@ def find_package(pkgname): return False def copy_lib(): - lib_list = ['libpython2.7.so.1.0', 'libssl.so.10', 'libcrypto.so.10'] if py_version == 2 else ['libpython3.6m.so.1.0', 'libssl.so.10', 'libcrypto.so.10'] + if py_version[0] == 2: + lib_list = ['libpython2.7.so.1.0', 'libssl.so.10', 'libcrypto.so.10'] + elif py_version[1] == 6: + lib_list = ['libpython3.6m.so.1.0', 'libssl.so.10', 'libcrypto.so.10'] + elif py_version[1] == 7: + lib_list = ['libpython3.7m.so.1.0', 'libssl.so.10', 'libcrypto.so.10'] os.popen('mkdir -p paddle_serving_client/lib') for lib in lib_list: r = os.popen('whereis {}'.format(lib)) diff --git a/tools/Dockerfile.centos6.devel b/tools/Dockerfile.centos6.devel index dd519a0a08bd9fc02b7ad51d248912c2a22a811d..dd5a2ef786ed8a9c239a99cabbcfe2d482e6341c 100644 --- a/tools/Dockerfile.centos6.devel +++ b/tools/Dockerfile.centos6.devel @@ -21,7 +21,7 @@ RUN yum -y install wget && \ wget https://www.python.org/ftp/python/2.7.5/Python-2.7.5.tgz && \ tar -zxf Python-2.7.5.tgz && \ cd Python-2.7.5 && \ - ./configure --prefix=/usr/local/python2.7 --enable-shared && \ + ./configure --prefix=/usr/local/python2.7 --enable-shared --enable-unicode=ucs4 && \ make all && make install && \ make clean && \ echo 'export PATH=/usr/local/python2.7/bin:$PATH' >> /root/.bashrc && \ diff --git a/tools/Dockerfile.centos6.gpu.devel b/tools/Dockerfile.centos6.gpu.devel index 3288f09d4cacc8aa7fa0bd112dc6bf97939ecde5..c34780c151e960134af5f8b448e0465b8285e8b2 100644 --- a/tools/Dockerfile.centos6.gpu.devel +++ b/tools/Dockerfile.centos6.gpu.devel @@ -21,7 +21,7 @@ RUN yum -y install wget && \ wget https://www.python.org/ftp/python/2.7.5/Python-2.7.5.tgz && \ tar -zxf Python-2.7.5.tgz && \ cd Python-2.7.5 && \ - ./configure --prefix=/usr/local/python2.7 --enable-shared && \ + ./configure --prefix=/usr/local/python2.7 --enable-shared --enable-unicode=ucs4 && \ make all && make install && \ make clean && \ echo 'export PATH=/usr/local/python2.7/bin:$PATH' >> /root/.bashrc && \ diff --git a/tools/python_tag.py b/tools/python_tag.py new file mode 100644 index 0000000000000000000000000000000000000000..75947cff0b1b39d4c262a306bbe2bc878ae7d3ba --- /dev/null +++ b/tools/python_tag.py @@ -0,0 +1,20 @@ +# 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 wheel.pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag +import re +with open("setup.cfg", "w") as f: + line = "[bdist_wheel]\npython-tag={0}{1}\nplat-name=linux_x86_64".format( + get_abbr_impl(), get_impl_ver()) + f.write(line)