Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
Serving
提交
13bffa43
S
Serving
项目概览
PaddlePaddle
/
Serving
大约 1 年 前同步成功
通知
186
Star
833
Fork
253
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
105
列表
看板
标记
里程碑
合并请求
10
Wiki
2
Wiki
分析
仓库
DevOps
项目成员
Pages
S
Serving
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
105
Issue
105
列表
看板
标记
里程碑
合并请求
10
合并请求
10
Pages
分析
分析
仓库分析
DevOps
Wiki
2
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
13bffa43
编写于
3月 06, 2019
作者:
W
wangguibao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Documentation
Change-Id: Ibd7b47fddcb75236a5f1624c32c4a674e87ed633
上级
0a5dddd0
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
391 addition
and
24 deletion
+391
-24
README.md
README.md
+1
-7
doc/CLIENT_CONFIGURE.md
doc/CLIENT_CONFIGURE.md
+5
-5
doc/CREATING.md
doc/CREATING.md
+10
-10
doc/DESIGN.md
doc/DESIGN.md
+329
-2
doc/FAQ.md
doc/FAQ.md
+7
-0
doc/INSTALL.md
doc/INSTALL.md
+39
-0
未找到文件。
README.md
浏览文件 @
13bffa43
[TOC]
# 概述
PaddlePaddle是公司开源的机器学习框架,广泛支持各种深度学习模型的定制化开发; Paddle serving是Paddle的在线预测部分,与Paddle模型训练环节无缝衔接,提供机器学习预测云服务。
...
...
@@ -47,9 +46,4 @@ Paddle serving框架为策略工程师提供以下三层面的功能性扩展:
# 设计文档
# FAQ
1.
如何修改端口配置?
-
使用该框架搭建的服务需要申请一个端口,可以通过以下方式修改端口号:
-
如果在inferservice_file里指定了port:xxx,那么就去申请该端口号;
-
否则,如果在gflags.conf里指定了--port:xxx,那就去申请该端口号;
-
否则,使用程序里指定的默认端口号:8010。
[
设计文档
](
doc/DESIGN.md
)
doc/CLIENT_CONFIGURE.md
浏览文件 @
13bffa43
...
...
@@ -2,7 +2,7 @@
Paddle-serving C++ client SDK主配置文件为conf/predictors.prototxt。其中一个示例如下:
## Sample conf
##
1.
Sample conf
```
shell
default_variant_conf
{
...
...
@@ -65,14 +65,14 @@ predictors {
```
## 名词解释
##
2.
名词解释
-
预测服务 (Predictor):对一个Paddle预测服务的封装
-
端点(Endpoit):对一个预测需求的逻辑抽象,通常包含一到多个服务变体,以方便多版本模型管理;
-
变体(Variant):一套同质化的Paddle-serving集群服务,每个实例起一个Paddle-serving进程;
## 配置项解释
##
3.
配置项解释
### default_variant_conf
###
3.1
default_variant_conf
```
shell
default_variant_conf
{
...
...
@@ -110,7 +110,7 @@ compress_type: 0-None, 1-Snappy, 2-gzip, 3-zlib, 4-lz4, see [BRPC DOC: compress_
protocol: Maybe baidu_std/http/h2/h2:grpc/thrift/memcache/redis... see
[
BRPC DOC: protocol
](
https://github.com/apache/incubator-brpc/blob/master/docs/cn/client.md#%E5%8D%8F%E8%AE%AE
)
### Predictors
###
3.2
Predictors
可以为客户端配置多个predictor,每个predictor代表一个要访问的预测服务
...
...
doc/CREATING.md
浏览文件 @
13bffa43
...
...
@@ -13,7 +13,7 @@ paddle-serving已经提供了一个基于ResNet的模型预测服务,按照INS
### 2.1 定义预测接口
**
添加文件:serving/proto/image_class.proto
**
**
添加文件:serving/proto/image_class.proto
**
Paddle-serving服务端与客户端通过brpc进行通信,通信协议和格式可以自定,我们选择baidu_std协议。这是一种以protobuf为基本数据交换格式的协议,其说明可参考
[
BRPC文档: baidu_std
](
https://github.com/apache/incubator-brpc/blob/master/docs/cn/baidu_std.md
)
。
...
...
@@ -64,7 +64,7 @@ service ImageClassifyService {
#### 2.2.1 定制Op算子
**
在serving/op/目录下添加reader_op.cpp, classify_op.cpp, write_json_op.cpp
**
**
在serving/op/目录下添加reader_op.cpp, classify_op.cpp, write_json_op.cpp
**
-
预处理算子(ReaderOp, serving/op/reader_op.cpp):从Request中读取图像字节流,通过opencv解码,填充tensor对象并输出到channel;
-
预测调用算子(ClassifyOp, serving/op/classify_op.cpp):从ImageReaderOp的channel获得输入tensor,临时申请输出tensor,调用ModelToolkit进行预测,并将输出tensor写入channel
...
...
@@ -79,7 +79,7 @@ service ImageClassifyService {
-
配置文件示例:
**
添加文件 serving/conf/service.prototxt
**
**
添加文件 serving/conf/service.prototxt
**
```
shell
services
{
...
...
@@ -88,7 +88,7 @@ services {
}
```
**
添加文件 serving/conf/workflow.prototxt
**
**
添加文件 serving/conf/workflow.prototxt
**
```
shell
workflows
{
...
...
@@ -119,14 +119,14 @@ workflows {
以下配置文件为模型加载配置
**
添加文件 serving/conf/resource.prototxt
**
**
添加文件 serving/conf/resource.prototxt
**
```
shell
model_manager_path: ./conf
model_manager_file: model_toolkit.prototxt
```
**
添加文件 serving/conf/model_toolkit.prototxt
**
**
添加文件 serving/conf/model_toolkit.prototxt
**
```
shell
engines
{
...
...
@@ -221,7 +221,7 @@ target_link_libraries(serving opencv_imgcodecs
### 3.1 定义预测接口
**
在sdk-cpp/proto添加image_class.proto
**
**
在sdk-cpp/proto添加image_class.proto
**
与serving端预测接口protobuf文件基本一致,只要将
`generate_impl=true`
改为
`generate_stub=true`
...
...
@@ -266,7 +266,7 @@ class Predictor {
#### 3.2.1 请求逻辑
**
增加sdk-cpp/demo/ximage.cpp
**
**
增加sdk-cpp/demo/ximage.cpp
**
```
c++
// 进程级初始化
...
...
@@ -323,7 +323,7 @@ $ protoc --cpp_out=/path/to/paddle-serving/build/serving/ --pdcodegen_out=/path/
其中
`pdcodegen`
是由predictor/src/pdcodegen.cpp编译成的protobuf编译插件, --proto_path用来指定去哪里寻找
`import`
语句需要的protobuf文件
**
NOTE
**
**
NOTE
**
上述protoc命令在paddle-serving编译系统中被封装成一个CMake函数了,在cmake/generic.cmake::PROTOBUF_GENERATE_SERVING_CPP
CMakeLists.txt中调用函数的方法为:
```
shell
...
...
@@ -347,7 +347,7 @@ target_link_libraries(ximage -Wl,--whole-archive sdk-cpp
### 3.4 连接配置
**
增加配置文件sdk/conf/predictors.prototxt
**
**
增加配置文件sdk/conf/predictors.prototxt
**
```
shell
## 默认配置共享
...
...
doc/DESIGN.md
浏览文件 @
13bffa43
#
设计文档
#
Paddle-Serving设计方案
# 项目背景
## 1. 项目背景
PaddlePaddle是公司开源的机器学习框架,广泛支持各种深度学习模型的定制化开发; Paddle serving是Paddle的在线预测部分,与Paddle模型训练环节无缝衔接,提供机器学习预测云服务。本文将从模型、服务、接入等层面,自底向上描述Paddle-serving设计方案。
1.
模型是Paddle-Serving预测的核心,包括模型数据和推理计算的管理;
2.
预测框架封装模型推理计算,对外提供RPC接口,对接不同上游;
3.
预测服务SDK提供一套接入框架
最终形成一套完整的serving解决方案。
## 2. 名词解释
-
baidu-rpc 百度官方开源RPC框架,支持多种常见通信协议,提供基于protobuf的自定义接口体验
-
Variant Paddle-serving架构对一个最小预测集群的抽象,其特点是内部所有实例(副本)完全同质,逻辑上对应一个model的一个固定版本
-
Endpoint 多个Variant组成一个Endpoint,逻辑上看,Endpoint代表一个model,Endpoint内部的Variant代表不同的版本
-
OP PaddlePaddle用来封装一种数值计算的算子,Paddle-Serving用来表示一种基础的业务操作算子,核心接口是inference。OP通过配置其依赖的上游OP,将多个OP串联成一个workflow
-
Channel 一个OP所有请求级中间数据的抽象;OP之间通过Channel进行数据交互
-
Bus 对一个线程中所有channel的管理,以及根据DAG之间的DAG依赖图对OP和Channel两个集合间的访问关系进行调度
-
Stage Workflow按照DAG描述的拓扑图中,属于同一个环节且可并行执行的OP集合
-
Workflow 按照DAG描述的拓扑,有序执行每个OP的inference接口
-
App 对workflow的逻辑封装,框架加载每套workflow配置时,会生成一个App实例,可并行管理多个App
-
Service 对多个App组成的复杂调度拓扑的逻辑封装,同一个service内的App之间可能存在依赖关系;服务框架可加载多套service配置,并行管理多套service,一次请求只能访问一个service。
## 3. Paddle-Serving总体框架
![
Paddle-Serging总体框图
](
https://paddle-serving.bj.bcebos.com/doc/framework.png
)
**模型管理框架**
:对接多种机器学习平台的模型文件,向上提供统一的inference接口
**业务调度框架**
:对各种不同预测模型的计算逻辑进行抽象,提供通用的DAG调度框架,通过DAG图串联不同的算子,共同完成一次预测服务。该抽象模型使用户可以方便的实现自己的计算逻辑,同时便于算子共用。(用户搭建自己的预测服务,很大一部分工作是搭建DAG和提供算子的实现)
**PredictService**
:对外部提供的预测服务接口封装。通过protobuf定义与客户端的通信字段。
### 3.1 模型管理框架
模型管理框架负责管理机器学习框架训练出来的模型,总体可抽象成模型加载、模型数据和模型推理等3个层次。
#### 模型加载
将模型从磁盘加载到内存,支持多版本、热加载、增量更新等功能
#### 模型数据
模型在内存中的数据结构,集成fluid预测lib
#### inferencer
向上为预测服务提供统一的预测接口
```
C++
class FluidFamilyCore {
virtual bool Run(const void* in_data, void* out_data);
virtual int create(const std::string& data_path);
virtual int clone(void* origin_core);
};
```
### 3.2 业务调度框架
#### 3.2.1 预测服务Service
参考TF框架的模型计算的抽象思想,将业务逻辑抽象成DAG图,由配置驱动,生成workflow,跳过C++代码编译。业务的每个具体步骤,对应一个具体的OP,OP可配置自己依赖的上游OP。OP之间消息传递统一由线程级Bus和channel机制实现。例如,一个简单的预测服务的服务过程,可以抽象成读请求数据->调用预测接口->写回预测结果等3个步骤,相应的实现到3个OP: ReaderOp->ClassifyOp->WriteOp
![
预测服务Service
](
https://paddle-serving.bj.bcebos.com/doc/predict-service.png
)
关于OP之间的依赖关系,以及通过OP组建workflow,可以参考
[
从零开始写一个预测服务
](
CREATING.md
)
的相关章节
#### 3.2.2 Paddle-Serving的多服务机制
![
Paddle-Serving的多服务机制
](
https://paddle-serving.bj.bcebos.com/doc/multi-service.png
)
Paddle-Serving实例可以同时加载多个模型,每个模型用一个Service(以及其所配置的workflow)承接服务。可以参考
[
Demo例子中的service配置文件
](
../serving/conf/service.prototxt
)
了解如何为serving实例配置多个service
#### 3.2.3 业务调度层级关系
从客户端看,一个Paddle-Serving service从顶向下可分为Service, Endpoint, Variant等3个层级
![
调用层级关系
](
https://paddle-serving.bj.bcebos.com/doc/multi-variants.png
)
一个Service对应一个预测模型,模型下有1个endpoint。模型的不同版本,通过endpoint下多个variant概念实现:
同一个模型预测服务,可以配置多个variant,每个variant有自己的下游IP列表。客户端代码可以对各个variant配置相对权重,以达到调节流量比例的关系(参考
[
客户端配置
](
CLIENT_CONFIGURE.md
)
第3.2节中关于variant_weight_list的说明)。
## 4. 用户接口
在满足一定的接口规范前提下,服务框架不对用户数据字段做任何约束,以应对各种预测服务的不同业务接口。Baidu-rpc继承了Protobuf serice的接口,用户按照Protobuf语法规范描述Request和Response业务接口。Paddle-Serving基于Baidu-rpc框架搭建,默认支持该特性。
无论通信协议如何变化,框架只需确保Client和Server间通信协议和业务数据两种信息的格式同步,即可保证正常通信。这些信息又可细分如下:
-
协议:Server和Client之间事先约定的、确保相互识别数据格式的包头信息。Paddle-Serving用Protobuf作为基础通信格式
-
数据:用来描述Request和Response的接口,例如待预测样本数据,和预测返回的打分。包括:
-
数据字段:请求包Request和返回包Response两种数据结构包含的字段定义
-
描述接口:跟协议接口类似,默认支持Protobuf
### 4.1 数据压缩方法
Baidu-rpc内置了snappy, gzip, zlib等数据压缩方法,可在配置文件中配置(参考
[
客户端配置
](
CLIENT_CONFIGURE.md
)
第3.1节关于compress_type的介绍)
### 4.2 C++ SDK API接口
```
C++
class PredictorApi {
public:
int create(const char* path, const char* file);
int thrd_initialize();
int thrd_clear();
int thrd_finalize();
void destroy();
Predictor* fetch_predictor(std::string ep_name);
int free_predictor(Predictor* predictor);
};
class Predictor {
public:
// synchronize interface
virtual int inference(google::protobuf::Message* req,
google::protobuf::Message* res) = 0;
// asynchronize interface
virtual int inference(google::protobuf::Message* req,
google::protobuf::Message* res,
DoneType done,
brpc::CallId* cid = NULL) = 0;
// synchronize interface
virtual int debug(google::protobuf::Message* req,
google::protobuf::Message* res,
butil::IOBufBuilder* debug_os) = 0;
};
```
### 4.3 OP相关接口
```
C++
class Op {
// ------Getters for Channel/Data/Message of dependent OP-----
// Get the Channel object of dependent OP
Channel* mutable_depend_channel(const std::string& op);
// Get the Channel object of dependent OP
const Channel* get_depend_channel(const std::string& op) const;
template <typename T>
T* mutable_depend_argument(const std::string& op);
template <typename T>
const T* get_depend_argument(const std::string& op) const;
// -----Getters for Channel/Data/Message of current OP----
// Get pointer to the progobuf message of current OP
google::protobuf::Message* mutable_message();
// Get pointer to the protobuf message of current OP
const google::protobuf::Message* get_message() const;
// Get the template class data object of current OP
template <typename T>
T* mutable_data();
// Get the template class data object of current OP
template <typename T>
const T* get_data() const;
// ---------------- Other base class members ----------------
int init(Bus* bus,
Dag* dag,
uint32_t id,
const std::string& name,
const std::string& type,
void* conf);
int deinit();
int process(bool debug);
// Get the input object
const google::protobuf::Message* get_request_message();
const std::string& type() const;
uint32_t id() const;
// ------------------ OP Interface -------------------
// Get the derived Channel object of current OP
virtual Channel* mutable_channel() = 0;
// Get the derived Channel object of current OP
virtual const Channel* get_channel() const = 0;
// Release the derived Channel object of current OP
virtual int release_channel() = 0;
// Inference interface
virtual int inference() = 0;
// ------------------ Conf Interface -------------------
virtual void* create_config(const configure::DAGNode& conf) { return NULL; }
virtual void delete_config(void* conf) {}
virtual void set_config(void* conf) { return; }
// ------------------ Metric Interface -------------------
virtual void regist_metric() { return; }
};
```
### 4.4 框架相关接口
Service
```
C++
class InferService {
public:
static const char* tag() { return "service"; }
int init(const configure::InferService& conf);
int deinit() { return 0; }
int reload();
const std::string& name() const;
const std::string& full_name() const { return _infer_service_format; }
// Execute each workflow serially
virtual int inference(const google::protobuf::Message* request,
google::protobuf::Message* response,
butil::IOBufBuilder* debug_os = NULL);
int debug(const google::protobuf::Message* request,
google::protobuf::Message* response,
butil::IOBufBuilder* debug_os);
};
class ParallelInferService : public InferService {
public:
// Execute workflows in parallel
int inference(const google::protobuf::Message* request,
google::protobuf::Message* response,
butil::IOBufBuilder* debug_os) {
return 0;
}
};
```
ServerManager
```
C++
class ServerManager {
public:
typedef google::protobuf::Service Service;
ServerManager();
static ServerManager& instance() {
static ServerManager server;
return server;
}
static bool reload_starting() { return _s_reload_starting; }
static void stop_reloader() { _s_reload_starting = false; }
int add_service_by_format(const std::string& format);
int start_and_wait();
};
```
DAG
```
C++
class Dag {
public:
EdgeMode parse_mode(std::string& mode); // NOLINT
int init(const char* path, const char* file, const std::string& name);
int init(const configure::Workflow& conf, const std::string& name);
int deinit();
uint32_t nodes_size();
const DagNode* node_by_id(uint32_t id);
const DagNode* node_by_id(uint32_t id) const;
const DagNode* node_by_name(std::string& name); // NOLINT
const DagNode* node_by_name(const std::string& name) const;
uint32_t stage_size();
const DagStage* stage_by_index(uint32_t index);
const std::string& name() const { return _dag_name; }
const std::string& full_name() const { return _dag_name; }
void regist_metric(const std::string& service_name);
};
```
Workflow
```
C++
class Workflow {
public:
Workflow() {}
static const char* tag() { return "workflow"; }
// Each workflow object corresponds to an independent
// configure file, so you can share the object between
// different apps.
int init(const configure::Workflow& conf);
DagView* fetch_dag_view(const std::string& service_name);
int deinit() { return 0; }
void return_dag_view(DagView* view);
int reload();
const std::string& name() { return _name; }
const std::string& full_name() { return _name; }
};
```
doc/FAQ.md
0 → 100644
浏览文件 @
13bffa43
# FAQ
## 1. 如何修改端口配置?
使用该框架搭建的服务需要申请一个端口,可以通过以下方式修改端口号:
-
如果在inferservice_file里指定了port:xxx,那么就去申请该端口号;
-
否则,如果在gflags.conf里指定了--port:xxx,那就去申请该端口号;
-
否则,使用程序里指定的默认端口号:8010。
doc/INSTALL.md
浏览文件 @
13bffa43
...
...
@@ -3,7 +3,9 @@
## 系统需求
OS: Linux
CMake: 3.2
python
## 编译
...
...
@@ -16,3 +18,40 @@ $ cmake ..
$
make
-j4
$
make
install
```
`make install`
将把目标产出放在/path/to/paddle-serving/build/output/目录下,目录结构:
```
.
|-- bin # Paddle-serving protobuf编译插件pdcodegen所在目录
|-- demo # demo总目录
| |-- client
| | |-- dense_format # dense_format客户端
| | | |-- bin # bin/dense_format是dense_format客户端bin
| | | `-- conf
| | |-- echo # echo服务客户端
| | | |-- bin # bin/echo是echo客户端bin
| | | \-- conf
| | |-- image_classification # image_classification服务客户端
| | | |-- bin # bin/ximage是image_classification客户端bin
| | | |-- conf
| | | |-- data
| | | `-- images
| | |-- int64tensor_format # int64tensor_format服务客户端
| | | |-- bin # bin/int64tensor_format是客户端bin
| | | `-- conf
| | `-- sparse_format # sparse_format客户端
| | |-- bin # bin/sparse_format是客户端bin
| | `-- conf
| `-- serving # serving端,同时提供echo/dense_format/sparse_format/int64tensor_format/image_class等5种服务
| |-- bin # bin/serving是serving端可执行bin
| |-- conf # 配置文件目录
| |-- data
| | `-- model
| | `-- paddle
| | `-- fluid
| | `-- SE_ResNeXt50_32x4d # image_classification模型
`-- lib # Paddle-serving产出的静态库文件: libpdseving.a, libsdk-cpp.a, libconfigure.a, libfluid_cpu_engine.a
```
如要编写新的预测服务,请参考
[
从零开始写一个预测服务
](
CREATING.md
)
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录