提交 b50d2560 编写于 作者: T TeslaZhao

Update doc

上级 2e98f359
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
The goal of Paddle Serving is to provide high-performance, flexible and easy-to-use industrial-grade online inference services for machine learning developers and enterprises.Paddle Serving supports multiple protocols such as RESTful, gRPC, bRPC, and provides inference solutions under a variety of hardware and multiple operating system environments, and many famous pre-trained model examples. The core features are as follows: The goal of Paddle Serving is to provide high-performance, flexible and easy-to-use industrial-grade online inference services for machine learning developers and enterprises.Paddle Serving supports multiple protocols such as RESTful, gRPC, bRPC, and provides inference solutions under a variety of hardware and multiple operating system environments, and many famous pre-trained model examples. The core features are as follows:
- Integrate high-performance server-side inference engine paddle Inference and mobile-side engine paddle Lite. Models of other machine learning platforms (Caffe/TensorFlow/ONNX/PyTorch) can be migrated to paddle through [x2paddle](https://github.com/PaddlePaddle/X2Paddle). - Integrate high-performance server-side inference engine [Paddle Inference](https://paddleinference.paddlepaddle.org.cn/product_introduction/inference_intro.html) and mobile-side engine [Paddle Lite](https://paddlelite.paddlepaddle.org.cn/introduction/tech_highlights.html). Models of other machine learning platforms (Caffe/TensorFlow/ONNX/PyTorch) can be migrated to paddle through [x2paddle](https://github.com/PaddlePaddle/X2Paddle).
- There are two frameworks, namely high-performance C++ Serving and high-easy-to-use Python pipeline. The C++ Serving is based on the bRPC network framework to create a high-throughput, low-latency inference service, and its performance indicators are ahead of competing products. The Python pipeline is based on the gRPC/gRPC-Gateway network framework and the Python language to build a highly easy-to-use and high-throughput inference service. How to choose which one please see [Techinical Selection](doc/Serving_Design_EN.md#21-design-selection). - There are two frameworks, namely high-performance C++ Serving and high-easy-to-use Python pipeline. The C++ Serving is based on the bRPC network framework to create a high-throughput, low-latency inference service, and its performance indicators are ahead of competing products. The Python pipeline is based on the gRPC/gRPC-Gateway network framework and the Python language to build a highly easy-to-use and high-throughput inference service. How to choose which one please see [Techinical Selection](doc/Serving_Design_EN.md#21-design-selection).
- Support multiple [protocols](doc/C++_Serving/Inference_Protocols_CN.md) such as HTTP, gRPC, bRPC, and provide C++, Python, Java language SDK. - Support multiple [protocols](doc/C++_Serving/Inference_Protocols_CN.md) such as HTTP, gRPC, bRPC, and provide C++, Python, Java language SDK.
- Design and implement a high-performance inference service framework for asynchronous pipelines based on directed acyclic graph (DAG), with features such as multi-model combination, asynchronous scheduling, concurrent inference, dynamic batch, multi-card multi-stream inference, request cache, etc. - Design and implement a high-performance inference service framework for asynchronous pipelines based on directed acyclic graph (DAG), with features such as multi-model combination, asynchronous scheduling, concurrent inference, dynamic batch, multi-card multi-stream inference, request cache, etc.
...@@ -40,13 +40,17 @@ The goal of Paddle Serving is to provide high-performance, flexible and easy-to- ...@@ -40,13 +40,17 @@ The goal of Paddle Serving is to provide high-performance, flexible and easy-to-
- Support service monitoring, provide prometheus-based performance statistics and port access - Support service monitoring, provide prometheus-based performance statistics and port access
<h2 align="center">Tutorial and Papers</h2> <h2 align="center">Tutorial and Solutions</h2>
- AIStudio tutorial(Chinese) : [Paddle Serving服务化部署框架](https://www.paddlepaddle.org.cn/tutorials/projectdetail/3946013) - AIStudio tutorial(Chinese) : [Paddle Serving服务化部署框架](https://www.paddlepaddle.org.cn/tutorials/projectdetail/3946013)
- AIStudio OCR practice(Chinese) : [基于PaddleServing的OCR服务化部署实战](https://aistudio.baidu.com/aistudio/projectdetail/3630726) - AIStudio OCR practice(Chinese) : [基于PaddleServing的OCR服务化部署实战](https://aistudio.baidu.com/aistudio/projectdetail/3630726)
- Video tutorial(Chinese) : [深度学习服务化部署-以互联网应用为例](https://aistudio.baidu.com/aistudio/course/introduce/19084) - Video tutorial(Chinese) : [深度学习服务化部署-以互联网应用为例](https://aistudio.baidu.com/aistudio/course/introduce/19084)
- Edge AI solution(Chinese) : [基于Paddle Serving&百度智能边缘BIE的边缘AI解决方案](https://mp.weixin.qq.com/s/j0EVlQXaZ7qmoz9Fv96Yrw) - Edge AI solution(Chinese) : [基于Paddle Serving&百度智能边缘BIE的边缘AI解决方案](https://mp.weixin.qq.com/s/j0EVlQXaZ7qmoz9Fv96Yrw)
- GOVT Q&A Solution(Chinese) : [政务问答检索式 FAQ System](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/question_answering/faq_system)
- Smart Q&A Solution(Chinese) : [保险智能问答](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/question_answering/faq_finance)
- Semantic Indexing Solution(Chinese) : [In-batch Negatives](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/neural_search/recall/in_batch_negative)
<h2 align="center">Papers</h2>
- Paper : [JiZhi: A Fast and Cost-Effective Model-As-A-Service System for - Paper : [JiZhi: A Fast and Cost-Effective Model-As-A-Service System for
Web-Scale Online Inference at Baidu](https://arxiv.org/pdf/2106.01674.pdf) Web-Scale Online Inference at Baidu](https://arxiv.org/pdf/2106.01674.pdf)
...@@ -67,6 +71,7 @@ This chapter guides you through the installation and deployment steps. It is str ...@@ -67,6 +71,7 @@ This chapter guides you through the installation and deployment steps. It is str
- [Install Paddle Serving using docker](doc/Install_EN.md) - [Install Paddle Serving using docker](doc/Install_EN.md)
- [Build Paddle Serving from Source with Docker](doc/Compile_EN.md) - [Build Paddle Serving from Source with Docker](doc/Compile_EN.md)
- [Install Paddle Serving on linux system](doc/Install_Linux_Env_CN.md)
- [Deploy Paddle Serving on Kubernetes(Chinese)](doc/Run_On_Kubernetes_CN.md) - [Deploy Paddle Serving on Kubernetes(Chinese)](doc/Run_On_Kubernetes_CN.md)
- [Deploy Paddle Serving with Security gateway(Chinese)](doc/Serving_Auth_Docker_CN.md) - [Deploy Paddle Serving with Security gateway(Chinese)](doc/Serving_Auth_Docker_CN.md)
- Deploy on more hardwares[[ARM CPU、百度昆仑](doc/Run_On_XPU_EN.md)[华为昇腾](doc/Run_On_NPU_CN.md)[海光DCU](doc/Run_On_DCU_CN.md)[Jetson](doc/Run_On_JETSON_CN.md)] - Deploy on more hardwares[[ARM CPU、百度昆仑](doc/Run_On_XPU_EN.md)[华为昇腾](doc/Run_On_NPU_CN.md)[海光DCU](doc/Run_On_DCU_CN.md)[Jetson](doc/Run_On_JETSON_CN.md)]
...@@ -93,10 +98,11 @@ The first step is to call the model save interface to generate a model parameter ...@@ -93,10 +98,11 @@ The first step is to call the model save interface to generate a model parameter
- [Benchmark(Chinese)](doc/C++_Serving/Benchmark_CN.md) - [Benchmark(Chinese)](doc/C++_Serving/Benchmark_CN.md)
- [Multiple models in series(Chinese)](doc/C++_Serving/2+_model.md) - [Multiple models in series(Chinese)](doc/C++_Serving/2+_model.md)
- [Request Cache(Chinese)](doc/C++_Serving/Request_Cache_CN.md) - [Request Cache(Chinese)](doc/C++_Serving/Request_Cache_CN.md)
- [Python Pipeline](doc/Python_Pipeline/Pipeline_Design_EN.md) - [Python Pipeline Overview(Chinese)](doc/Python_Pipeline/Pipeline_Int_CN.md)
- [Analyze and optimize performance](doc/Python_Pipeline/Performance_Tuning_EN.md) - [Architecture Design(Chinese)](doc/Python_Pipeline/Pipeline_Design_CN.md)
- [TensorRT dynamic Shape](doc/TensorRT_Dynamic_Shape_EN.md) - [Core Features(Chinese)](doc/Python_Pipeline/Pipeline_Features_CN.md)
- [Benchmark(Chinese)](doc/Python_Pipeline/Benchmark_CN.md) - [Performance Optimization(Chinese)](doc/Python_Pipeline/Pipeline_Optimize_CN.md)
- [Benchmark(Chinese)](doc/Python_Pipeline/Pipeline_Benchmark_CN.md)
- Client SDK - Client SDK
- [Python SDK(Chinese)](doc/C++_Serving/Introduction_CN.md#42-多语言多协议Client) - [Python SDK(Chinese)](doc/C++_Serving/Introduction_CN.md#42-多语言多协议Client)
- [JAVA SDK](doc/Java_SDK_EN.md) - [JAVA SDK](doc/Java_SDK_EN.md)
......
...@@ -24,27 +24,32 @@ ...@@ -24,27 +24,32 @@
*** ***
Paddle Serving依托深度学习框架PaddlePaddle旨在帮助深度学习开发者和企业提供高性能、灵活易用的工业级在线推理服务。Paddle Serving支持RESTful、gRPC、bRPC等多种协议,提供多种异构硬件和多种操作系统环境下推理解决方案,和多种经典预训练模型示例。核心特性如下: Paddle Serving 依托深度学习框架 PaddlePaddle 旨在帮助深度学习开发者和企业提供高性能、灵活易用的工业级在线推理服务。Paddle Serving 支持 RESTful、gRPC、bRPC 等多种协议,提供多种异构硬件和多种操作系统环境下推理解决方案,和多种经典预训练模型示例。核心特性如下:
- 集成高性能服务端推理引擎paddle Inference和移动端引擎paddle Lite,其他机器学习平台(Caffe/TensorFlow/ONNX/PyTorch)可通过[x2paddle](https://github.com/PaddlePaddle/X2Paddle)工具迁移模型 - 集成高性能服务端推理引擎 [Paddle Inference](https://paddleinference.paddlepaddle.org.cn/product_introduction/inference_intro.html) 和端侧引擎 [Paddle Lite](https://paddlelite.paddlepaddle.org.cn/introduction/tech_highlights.html),其他机器学习平台(Caffe/TensorFlow/ONNX/PyTorch)可通过 [x2paddle](https://github.com/PaddlePaddle/X2Paddle) 工具迁移模型
- 具有高性能C++和高易用Python 2套框架。C++框架基于高性能bRPC网络框架打造高吞吐、低延迟的推理服务,性能领先竞品。Python框架基于gRPC/gRPC-Gateway网络框架和Python语言构建高易用、高吞吐推理服务框架。技术选型参考[技术选型](doc/Serving_Design_CN.md#21-设计选型) - 具有高性能 C++ Serving 和高易用 Python Pipeline 2套框架。C++ Serving 基于高性能 bRPC 网络框架打造高吞吐、低延迟的推理服务,性能领先竞品。Python Pipeline 基于 gRPC/gRPC-Gateway 网络框架和 Python 语言构建高易用、高吞吐推理服务框架。技术选型参考[技术选型](doc/Serving_Design_CN.md#21-设计选型)
- 支持HTTP、gRPC、bRPC等多种[协议](doc/C++_Serving/Inference_Protocols_CN.md);提供C++、Python、Java语言SDK - 支持 HTTP、gRPC、bRPC 等多种[协议](doc/C++_Serving/Inference_Protocols_CN.md);提供 C++、Python、Java 语言 SDK
- 设计并实现基于有向无环图(DAG)的异步流水线高性能推理框架,具有多模型组合、异步调度、并发推理、动态批量、多卡多流推理、请求缓存等特性 - 设计并实现基于有向无环图(DAG) 的异步流水线高性能推理框架,具有多模型组合、异步调度、并发推理、动态批量、多卡多流推理、请求缓存等特性
- 适配x86(Intel) CPU、ARM CPU、Nvidia GPU、昆仑XPU、华为昇腾310/910、海光DCU、Nvidia Jetson等多种硬件 - 适配 x86(Intel) CPU、ARM CPU、Nvidia GPU、昆仑 XPU、华为昇腾310/910、海光 DCU、Nvidia Jetson 等多种硬件
- 集成Intel MKLDNN、Nvidia TensorRT加速库,以及低精度和量化推理 - 集成 Intel MKLDNN、Nvidia TensorRT 加速库,以及低精度量化推理
- 提供一套模型安全部署解决方案,包括加密模型部署、鉴权校验、HTTPs安全网关,并在实际项目中应用 - 提供一套模型安全部署解决方案,包括加密模型部署、鉴权校验、HTTPs 安全网关,并在实际项目中应用
- 支持云端部署,提供百度云智能云kubernetes集群部署Paddle Serving案例 - 支持云端部署,提供百度云智能云 kubernetes 集群部署 Paddle Serving 案例
- 提供丰富的经典模型部署示例,如PaddleOCR、PaddleClas、PaddleDetection、PaddleSeg、PaddleNLP、PaddleRec等套件,共计40+个预训练精品模型 - 提供丰富的经典模型部署示例,如 PaddleOCR、PaddleClas、PaddleDetection、PaddleSeg、PaddleNLP、PaddleRec 等套件,共计40+个预训练精品模型
- 支持大规模稀疏参数索引模型分布式部署,具有多表、多分片、多副本、本地高频cache等特性、可单机或云端部署 - 支持大规模稀疏参数索引模型分布式部署,具有多表、多分片、多副本、本地高频 cache 等特性、可单机或云端部署
- 支持服务监控,提供基于普罗米修斯的性能数据统计及端口访问 - 支持服务监控,提供基于普罗米修斯的性能数据统计及端口访问
<h2 align="center">教程与论文</h2> <h2 align="center">教程与案例</h2>
- AIStudio 使用教程 : [Paddle Serving服务化部署框架](https://www.paddlepaddle.org.cn/tutorials/projectdetail/3946013) - AIStudio 使用教程 : [Paddle Serving服务化部署框架](https://www.paddlepaddle.org.cn/tutorials/projectdetail/3946013)
- AIStudio OCR实战 : [基于PaddleServing的OCR服务化部署实战](https://aistudio.baidu.com/aistudio/projectdetail/3630726) - AIStudio OCR 实战 : [基于Paddle Serving的OCR服务化部署实战](https://aistudio.baidu.com/aistudio/projectdetail/3630726)
- 视频教程 : [深度学习服务化部署-以互联网应用为例](https://aistudio.baidu.com/aistudio/course/introduce/19084) - 视频教程 : [深度学习服务化部署-以互联网应用为例](https://aistudio.baidu.com/aistudio/course/introduce/19084)
- 边缘AI 解决方案 : [基于Paddle Serving&百度智能边缘BIE的边缘AI解决方案](https://mp.weixin.qq.com/s/j0EVlQXaZ7qmoz9Fv96Yrw) - 边缘 AI 解决方案 : [基于Paddle Serving&百度智能边缘BIE的边缘AI解决方案](https://mp.weixin.qq.com/s/j0EVlQXaZ7qmoz9Fv96Yrw)
- 政务问答解决方案 : [政务问答检索式 FAQ System](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/question_answering/faq_system)
- 智能问答解决方案 : [保险智能问答](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/question_answering/faq_finance)
- 语义索引解决方案 : [In-batch Negatives](https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/neural_search/recall/in_batch_negative)
<h2 align="center">论文</h2>
- 论文 : [JiZhi: A Fast and Cost-Effective Model-As-A-Service System for - 论文 : [JiZhi: A Fast and Cost-Effective Model-As-A-Service System for
Web-Scale Online Inference at Baidu](https://arxiv.org/pdf/2106.01674.pdf) Web-Scale Online Inference at Baidu](https://arxiv.org/pdf/2106.01674.pdf)
...@@ -61,13 +66,14 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf) ...@@ -61,13 +66,14 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf)
> 部署 > 部署
此章节引导您完成安装和部署步骤,强烈推荐使用Docker部署Paddle Serving,如您不使用docker,省略docker相关步骤。在云服务器上可以使用Kubernetes部署Paddle Serving。在异构硬件如ARM CPU、昆仑XPU上编译或使用Paddle Serving可阅读以下文档。每天编译生成develop分支的最新开发包供开发者使用。 此章节引导您完成安装和部署步骤,强烈推荐使用Docker部署Paddle Serving,如您不使用docker,省略docker相关步骤。在云服务器上可以使用Kubernetes部署Paddle Serving。在异构硬件如ARM CPU、昆仑XPU上编译或使用Paddle Serving可阅读以下文档。每天编译生成develop分支的最新开发包供开发者使用。
- [使用docker安装Paddle Serving](doc/Install_CN.md) - [使用 Docker 安装 Paddle Serving](doc/Install_CN.md)
- [源码编译安装Paddle Serving](doc/Compile_CN.md) - [Linux 原生系统安装 Paddle Serving](doc/Install_Linux_Env_CN.md)
- [在Kuberntes集群上部署Paddle Serving](doc/Run_On_Kubernetes_CN.md) - [源码编译安装 Paddle Serving](doc/Compile_CN.md)
- [部署Paddle Serving安全网关](doc/Serving_Auth_Docker_CN.md) - [Kuberntes集群部署 Paddle Serving](doc/Run_On_Kubernetes_CN.md)
- [部署 Paddle Serving 安全网关](doc/Serving_Auth_Docker_CN.md)
- 异构硬件部署[[ARM CPU、百度昆仑](doc/Run_On_XPU_CN.md)[华为昇腾](doc/Run_On_NPU_CN.md)[海光DCU](doc/Run_On_DCU_CN.md)[Jetson](doc/Run_On_JETSON_CN.md)] - 异构硬件部署[[ARM CPU、百度昆仑](doc/Run_On_XPU_CN.md)[华为昇腾](doc/Run_On_NPU_CN.md)[海光DCU](doc/Run_On_DCU_CN.md)[Jetson](doc/Run_On_JETSON_CN.md)]
- [Docker镜像](doc/Docker_Images_CN.md) - [Docker 镜像列表](doc/Docker_Images_CN.md)
- [下载Wheel包](doc/Latest_Packages_CN.md) - [下载 Python Wheels](doc/Latest_Packages_CN.md)
> 使用 > 使用
...@@ -79,7 +85,9 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf) ...@@ -79,7 +85,9 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf)
- [低精度推理](doc/Low_Precision_CN.md) - [低精度推理](doc/Low_Precision_CN.md)
- [常见模型数据处理](doc/Process_data_CN.md) - [常见模型数据处理](doc/Process_data_CN.md)
- [普罗米修斯](doc/Prometheus_CN.md) - [普罗米修斯](doc/Prometheus_CN.md)
- [C++ Serving简介](doc/C++_Serving/Introduction_CN.md) - [设置 TensorRT 动态shape](doc/TensorRT_Dynamic_Shape_CN.md)
- [C++ Serving 概述](doc/C++_Serving/Introduction_CN.md)
- [异步框架](doc/C++_Serving/Asynchronous_Framwork_CN.md)
- [协议](doc/C++_Serving/Inference_Protocols_CN.md) - [协议](doc/C++_Serving/Inference_Protocols_CN.md)
- [模型热加载](doc/C++_Serving/Hot_Loading_CN.md) - [模型热加载](doc/C++_Serving/Hot_Loading_CN.md)
- [A/B Test](doc/C++_Serving/ABTest_CN.md) - [A/B Test](doc/C++_Serving/ABTest_CN.md)
...@@ -88,10 +96,11 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf) ...@@ -88,10 +96,11 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf)
- [性能指标](doc/C++_Serving/Benchmark_CN.md) - [性能指标](doc/C++_Serving/Benchmark_CN.md)
- [多模型串联](doc/C++_Serving/2+_model.md) - [多模型串联](doc/C++_Serving/2+_model.md)
- [请求缓存](doc/C++_Serving/Request_Cache_CN.md) - [请求缓存](doc/C++_Serving/Request_Cache_CN.md)
- [Python Pipeline设计](doc/Python_Pipeline/Pipeline_Design_CN.md) - [Python Pipeline 概述](doc/Python_Pipeline/Pipeline_Int_CN.md)
- [性能优化指南](doc/Python_Pipeline/Performance_Tuning_CN.md) - [框架设计](doc/Python_Pipeline/Pipeline_Design_CN.md)
- [TensorRT动态shape](doc/TensorRT_Dynamic_Shape_CN.md) - [核心功能](doc/Python_Pipeline/Pipeline_Features_CN.md)
- [性能指标](doc/Python_Pipeline/Benchmark_CN.md) - [性能优化](doc/Python_Pipeline/Pipeline_Optimize_CN.md)
- [性能指标](doc/Python_Pipeline/Pipeline_Benchmark_CN.md)
- 客户端SDK - 客户端SDK
- [Python SDK](doc/C++_Serving/Introduction_CN.md#42-多语言多协议Client) - [Python SDK](doc/C++_Serving/Introduction_CN.md#42-多语言多协议Client)
- [JAVA SDK](doc/Java_SDK_CN.md) - [JAVA SDK](doc/Java_SDK_CN.md)
...@@ -107,13 +116,13 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf) ...@@ -107,13 +116,13 @@ AND GENERATION](https://arxiv.org/pdf/2112.12731.pdf)
<h2 align="center">模型库</h2> <h2 align="center">模型库</h2>
Paddle Serving与Paddle模型套件紧密配合,实现大量服务化部署,包括图像分类、物体检测、语言文本识别、中文词性、情感分析、内容推荐等多种类型示例,以及Paddle全链条项目,共计45个模型。 Paddle Serving与Paddle模型套件紧密配合,实现大量服务化部署,包括图像分类、物体检测、语言文本识别、中文词性、情感分析、内容推荐等多种类型示例,以及Paddle全链条项目,共计47个模型。
<p align="center"> <p align="center">
| PaddleOCR | PaddleDetection | PaddleClas | PaddleSeg | PaddleRec | Paddle NLP | Paddle Video | | PaddleOCR | PaddleDetection | PaddleClas | PaddleSeg | PaddleRec | Paddle NLP | Paddle Video |
| :----: | :----: | :----: | :----: | :----: | :----: | :----: | | :----: | :----: | :----: | :----: | :----: | :----: | :----: |
| 8 | 12 | 14 | 2 | 3 | 6 | 1 | | 8 | 12 | 14 | 2 | 3 | 7 | 1 |
</p> </p>
......
# 如何使用Paddle Serving做ABTEST # C++ Serving ABTest
(简体中文|[English](./ABTest_EN.md)) - [功能设计](#1)
- [使用案例](#2)
- [1.1 安装 Paddle Serving Wheels](#2.1)
- [1.2 下载多个模型并保存模型参数](#2.2)
- [1.3 启动 A,B,C 3个服务](#2.3)
- [1.4 客户端注册 A,B,C 服务端地址](#2.4)
- [1.5 启动客户端并验证结果](#2.5)
该文档将会用一个基于IMDB数据集的文本分类任务的例子,介绍如何使用Paddle Serving搭建A/B Test框架,例中的Client端、Server端结构如下图所示 ABTest 是一种功能测试方案,一般是为同一个产品目标制定多种方案,让一部分用户使用 A 方案,另一部分用户使用 B 或 C 方案,根据测试效果,如点击率、转化率等来评价方案的优劣
<img src="../images/abtest.png" style="zoom:33%;" /> 模型服务化部署框架中,ABTest 属于一个重要的基础功能,为模型迭代升级提供实验环境。Paddle Serving 的 PYTHON SDK 中实现 ABTest 功能,为用户提供简单易用功能测试环境。
需要注意的是:A/B Test只适用于RPC模式,不适用于WEB模式。 <a name="1"></a>
### 下载数据以及模型 ## 功能设计
``` shell Paddle Serving 的 ABTest 功能是基于 PYTHON SDK 和 多个服务端构成。每个服务端加载不同模型,在客户端上注册多个服务端地址和访问比例,最终确定访问。
cd Serving/examples/C++/imdb
sh get_data.sh <div align=center>
``` <img src='images/6-5_Cpp_ABTest_CN_1.png' height = "400" align="middle"/>
</div
<a name="2"></a>
### 处理数据 ## 使用案例
由于处理数据需要用到相关库,请使用pip进行安装
``` shell
pip install paddlepaddle
pip install paddle-serving-app
pip install Shapely
````
您可以直接运行下面的命令来处理数据。
[python abtest_get_data.py](../../examples/C++/imdb/abtest_get_data.py) [imdb](https://github.com/PaddlePaddle/Serving/tree/develop/examples/C%2B%2B/imdb) 示例为例,介绍 ABTest 的使用,部署有5个步骤:
文件中的Python代码将处理`test_data/part-0`的数据,并将处理后的数据生成并写入`processed.data`文件中。 1. 安装 Paddle Serving Wheels
2. 下载多个模型并保存模型参数
3. 启动 A,B,C 3个服务
4. 客户端注册 A,B,C 服务端地址
5. 启动客户端并验证结果
### 启动Server端 <a name="2.1"></a>
这里采用[Docker方式](../Install_CN.md)启动Server端服务。 **一.安装 Paddle Serving Wheels**
首先启动BOW Server,该服务启用`8000`端口 使用 ABTest 功能的前提是使用 PYTHON SDK,因此需要安装 `paddle_serving_client` 的 wheel 包。[安装方法](./2-1_Docker_Images_CN.md) 如下
```bash
docker run -dit -v $PWD/imdb_bow_model:/model -p 8000:8000 --name bow-server registry.baidubce.com/paddlepaddle/serving:latest /bin/bash
docker exec -it bow-server /bin/bash
pip install paddle-serving-server -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install paddle-serving-client -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m paddle_serving_server.serve --model model --port 8000 >std.log 2>err.log &
exit
``` ```
pip3 install paddle-serving-client==0.8.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
<a name="2.2"></a>
**二.下载多个模型并保存模型参数**
同理启动LSTM Server,该服务启用`9000`端口: 本示例已提供了一键下载脚本 `sh get_data.sh`,下载自训练的模型 `bow``cnn``lstm` 3种不同方式训练的模型。
```bash ```
docker run -dit -v $PWD/imdb_lstm_model:/model -p 9000:9000 --name lstm-server registry.baidubce.com/paddlepaddle/serving:latest /bin/bash sh get_data.sh
docker exec -it lstm-server /bin/bash
pip install paddle-serving-server -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install paddle-serving-client -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m paddle_serving_server.serve --model model --port 9000 >std.log 2>err.log &
exit
``` ```
### 启动Client端 3种模型的所有文件如下所示,已为用户提前保存模型参数,无需执行保存操作。
为了模拟ABTEST工况,您可以在宿主机运行下面Python代码启动Client端,但需确保宿主机具备相关环境,您也可以在docker环境下运行. ```
├── imdb_bow_client_conf
│   ├── serving_client_conf.prototxt
│   └── serving_client_conf.stream.prototxt
├── imdb_bow_model
│   ├── embedding_0.w_0
│   ├── fc_0.b_0
│   ├── fc_0.w_0
│   ├── fc_1.b_0
│   ├── fc_1.w_0
│   ├── fc_2.b_0
│   ├── fc_2.w_0
│   ├── fluid_time_file
│   ├── __model__
│   ├── serving_server_conf.prototxt
│   └── serving_server_conf.stream.prototxt
├── imdb_cnn_client_conf
│   ├── serving_client_conf.prototxt
│   └── serving_client_conf.stream.prototxt
├── imdb_cnn_model
│   ├── embedding_0.w_0
│   ├── fc_0.b_0
│   ├── fc_0.w_0
│   ├── fc_1.b_0
│   ├── fc_1.w_0
│   ├── fluid_time_file
│   ├── __model__
│   ├── sequence_conv_0.b_0
│   ├── sequence_conv_0.w_0
│   ├── serving_server_conf.prototxt
│   └── serving_server_conf.stream.prototxt
├── imdb_lstm_client_conf
│   ├── serving_client_conf.prototxt
│   └── serving_client_conf.stream.prototxt
├── imdb_lstm_model
│   ├── embedding_0.w_0
│   ├── fc_0.b_0
│   ├── fc_0.w_0
│   ├── fc_1.b_0
│   ├── fc_1.w_0
│   ├── fc_2.b_0
│   ├── fc_2.w_0
│   ├── lstm_0.b_0
│   ├── lstm_0.w_0
│   ├── __model__
│   ├── serving_server_conf.prototxt
│   └── serving_server_conf.stream.prototxt
```
运行前使用`pip install paddle-serving-client`安装paddle-serving-client包。 虽然3个模型的网络结构不同,但是 `feed var``fetch_var` 都是相同的便于做 ABTest。
```
feed_var {
name: "words"
alias_name: "words"
is_lod_tensor: true
feed_type: 0
shape: -1
}
fetch_var {
name: "fc_2.tmp_2"
alias_name: "prediction"
is_lod_tensor: false
fetch_type: 1
shape: 2
}
```
<a name="2.3"></a>
您可以直接使用下面的命令,进行ABTEST预测。 **三.启动 A,B,C 3个服务**
[python abtest_client.py](../../examples/C++/imdb/abtest_client.py) 后台启动 `bow``cnn``lstm` 模型服务:
```python ```python
## 启动 bow 模型服务
python3 -m paddle_serving_server.serve --model imdb_bow_model/ --port 9297 >/dev/null 2>&1 &
## 启动 cnn 模型服务
python3 -m paddle_serving_server.serve --model imdb_cnn_model/ --port 9298 >/dev/null 2>&1 &
## 启动 lstm 模型服务
python3 -m paddle_serving_server.serve --model imdb_lstm_model/ --port 9299 >/dev/null 2>&1 &
```
<a name="2.4"></a>
**四.客户端注册 A,B,C 服务端地址**
使用 `paddle_serving_client``Client::add_variant(self, tag, cluster, variant_weight)` 接口注册服务标签、服务地址和权重。框架会将所有权重求和后计算每个服务的比例。本示例中,bow 服务的权重是10,cnn 服务的权重是30, lstm的权重是60,每次请求分别请求到3个服务的比例是10%、30%和60%。
```
from paddle_serving_client import Client from paddle_serving_client import Client
from paddle_serving_app.reader.imdb_reader import IMDBDataset
import sys
import numpy as np import numpy as np
client = Client() client = Client()
client.load_client_config('imdb_bow_client_conf/serving_client_conf.prototxt') client.load_client_config(sys.argv[1])
client.add_variant("bow", ["127.0.0.1:8000"], 10) client.add_variant("bow", ["127.0.0.1:9297"], 10)
client.add_variant("lstm", ["127.0.0.1:9000"], 90) client.add_variant("cnn", ["127.0.0.1:9298"], 30)
client.add_variant("lstm", ["127.0.0.1:9299"], 60)
client.connect() client.connect()
```
如要在结果中打印请求到了哪个服务,在 `client.predict(feed, fetch, batch, need_variant_tag, logid)` 中设置 `need_variant_tag=True`
<a name="2.5"></a>
print('please wait for about 10s') **五.启动客户端并验证结果**
with open('processed.data') as f:
cnt = {"bow": {'acc': 0, 'total': 0}, "lstm": {'acc': 0, 'total': 0}} 运行命令:
for line in f: ```
word_ids, label = line.split(';') head test_data/part-0 | python3.7 abtest_client.py imdb_cnn_client_conf/serving_client_conf.prototxt imdb.vocab
word_ids = [int(x) for x in word_ids.split(',')]
word_len = len(word_ids)
feed = {
"words": np.array(word_ids).reshape(word_len, 1),
"words.lod": [0, word_len]
}
fetch = ["acc", "cost", "prediction"]
[fetch_map, tag] = client.predict(feed=feed, fetch=fetch, need_variant_tag=True,batch=True)
if (float(fetch_map["prediction"][0][1]) - 0.5) * (float(label[0]) - 0.5) > 0:
cnt[tag]['acc'] += 1
cnt[tag]['total'] += 1
for tag, data in cnt.items():
print('[{}]<total: {}> acc: {}'.format(tag, data['total'], float(data['acc'])/float(data['total']) ))
``` ```
代码中,`client.add_variant(tag, clusters, variant_weight)`是为了添加一个标签为`tag`、流量权重为`variant_weight`的variant。在这个样例中,添加了一个标签为`bow`、流量权重为`10`的BOW variant,以及一个标签为`lstm`、流量权重为`90`的LSTM variant。Client端的流量会根据`10:90`的比例分发到两个variant。
Client端做预测时,若指定参数`need_variant_tag=True`,返回值则包含分发流量对应的variant标签。 运行结果如下,10次请求中,bow 服务2次,cnn 服务3次,lstm 服务5次,与设置的比例基本相近。
```
I0506 04:02:46.720135 44567 naming_service_thread.cpp:202] brpc::policy::ListNamingService("127.0.0.1:9297"): added 1
I0506 04:02:46.722630 44567 naming_service_thread.cpp:202] brpc::policy::ListNamingService("127.0.0.1:9298"): added 1
I0506 04:02:46.723577 44567 naming_service_thread.cpp:202] brpc::policy::ListNamingService("127.0.0.1:9299"): added 1
I0506 04:02:46.814075 44567 general_model.cpp:490] [client]logid=0,client_cost=9.889ms,server_cost=6.283ms.
server_tag=lstm prediction=[0.500398 0.49960205]
I0506 04:02:46.826339 44567 general_model.cpp:490] [client]logid=0,client_cost=10.261ms,server_cost=9.503ms.
server_tag=lstm prediction=[0.5007235 0.49927652]
I0506 04:02:46.828992 44567 general_model.cpp:490] [client]logid=0,client_cost=1.667ms,server_cost=0.741ms.
server_tag=bow prediction=[0.25859657 0.74140346]
I0506 04:02:46.843299 44567 general_model.cpp:490] [client]logid=0,client_cost=13.402ms,server_cost=12.827ms.
server_tag=lstm prediction=[0.50039905 0.4996009 ]
I0506 04:02:46.850219 44567 general_model.cpp:490] [client]logid=0,client_cost=5.129ms,server_cost=4.332ms.
server_tag=cnn prediction=[0.6369219 0.36307803]
I0506 04:02:46.854203 44567 general_model.cpp:490] [client]logid=0,client_cost=2.804ms,server_cost=0.782ms.
server_tag=bow prediction=[0.15088597 0.849114 ]
I0506 04:02:46.858268 44567 general_model.cpp:490] [client]logid=0,client_cost=3.292ms,server_cost=2.677ms.
server_tag=cnn prediction=[0.4608788 0.5391212]
I0506 04:02:46.869217 44567 general_model.cpp:490] [client]logid=0,client_cost=10.13ms,server_cost=9.556ms.
server_tag=lstm prediction=[0.5000269 0.49997318]
I0506 04:02:46.883790 44567 general_model.cpp:490] [client]logid=0,client_cost=13.312ms,server_cost=12.822ms.
server_tag=lstm prediction=[0.50083774 0.49916226]
I0506 04:02:46.887256 44567 general_model.cpp:490] [client]logid=0,client_cost=2.432ms,server_cost=1.812ms.
server_tag=cnn prediction=[0.47895813 0.52104187]
### 预期结果
由于网络情况的不同,可能每次预测的结果略有差异。
``` bash
[lstm]<total: 1867> acc: 0.490091055169
[bow]<total: 217> acc: 0.73732718894
``` ```
# C++ Serving 异步模式
- [设计方案](#1)
- [网络同步线程](#1.1)
- [异步调度线程](#1.2)
- [动态批量](#1.3)
- [使用案例](#2)
- [开启同步模式](#2.1)
- [开启异步模式](#2.2)
- [性能测试](#3)
- [测试结果](#3.1)
- [测试数据](#3.2)
<a name="1"></a>
## 设计方案
<a name="1.1"></a>
**一.同步网络线程**
Paddle Serving 的网络框架层面是同步处理模式,即 bRPC 网络处理线程从系统内核拿到完整请求数据后( epoll 模式),在同一线程内完成业务处理,C++ Serving 默认使用同步模式。同步模式比较简单直接,适用于模型预测时间短,或单个 Request 请求批量较大的情况。
<p align="center">
<img src='../images/syn_mode.png' width = "350" height = "300">
<p>
Server 端线程数 N = 模型预测引擎数 N = 同时处理 Request 请求数 N,超发的 Request 请求需要等待当前线程处理结束后才能得到响应和处理。
<a name="1.2"></a>
**二.异步调度线程**
为了提高计算芯片吞吐和计算资源利用率,C++ Serving 在调度层实现异步多线程并发合并请求,实现动态批量推理。异步模型主要适用于模型支持批量,单个 Request 请求的无批量或较小,单次预测时间较长的情况。
<p align="center">
<img src='../images/asyn_mode.png'>
<p>
异步模式下,Server 端 N 个线程只负责接收 Request 请求,实际调用预测引擎是在异步框架的线程池中,异步框架的线程数可以由配置选项来指定。为了方便理解,我们假设每个 Request 请求批量均为1,此时异步框架会尽可能多得从请求池中取 n(n≤M)个 Request 并将其拼装为1个 Request(batch=n),调用1次预测引擎,得到1个 Response(batch = n),再将其对应拆分为 n 个 Response 作为返回结果。
<a name="1.3"></a>
**三.动态批量**
通常,异步框架合并多个请求的前提是所有请求的 `feed var` 的维度除 batch 维度外必须是相同的。例如,以 OCR 文字识别案例中检测模型为例,A 请求的 `x` 变量的 shape 是 [1, 3, 960, 960],B 请求的 `x` 变量的 shape 是 [2, 3, 960, 960],虽然第一个维度值不相同,但第一个维度属于 `batch` 维度,因此,请求 A 和 请求 B 可以合并。C 请求的 `x` 变量的 shape 是 [1, 3, 640, 480],由于除了 `batch` 维度外还有2个维度值不同,A 和 C 不能直接合并。
从经验来看,当2个请求的同一个变量 shape 维度的数量相等时,通过 `padding` 补0的方式按最大 shape 值对齐即可。即 C 请求的 shape 补齐到 [1, 3, 960, 960],那么就可以与 A 和 B 请求合并了。Paddle Serving 框架实现了动态 Padding 功能补齐 shape。
当多个将要合并的请求中有一个 shape 值很大时,所有请求的 shape 都要按最大补齐,导致计算量成倍增长。Paddle Serving 设计了一套合并策略,满足任何一个条件均可合并:
- 条件 1:绝对值差的字节数小于 **1024** 字节,评估补齐绝对长度
- 条件 2:相似度的乘积大于 **50%**,评估相似度,评估补齐绝对值整体数据量比例
场景1:`Shape-1 = [batch, 500, 500], Shape-2 = [batch, 400, 400]`。此时,`绝对值差 = 500*500 - 400*400 = 90000` 字节,`相对误差= (400/500) * (400/500) = 0.8*0.8 = 0.64`,满足条件1,不满足条件2,触发动态 Padding。
场景2:`Shape-1 = [batch, 1, 1], Shape-2 = [batch, 2, 2]`。此时,`绝对值差 = 2*2 - 1*1 = 3`字节,`相对误差 = (1/2) * (1/2) = 0.5*0.5 = 0.25`,满足条件2,不满足条件1,触发动态 Padding。
场景3:`Shape-1 = [batch, 3, 320, 320], Shape-2 = [batch, 3, 960, 960]`。此时,`绝对值差 = 3*960*960 - 3*320*320 = 2457600`字节,`相对误差 = (3/3) * (320/960) * (320/960) = 0.3*0.3 = 0.09`,条件1和条件2均不满足,未触发动态 Padding。
<a name="2"></a>
## 使用案例
<a name="2.1"></a>
**一.开启同步模式**
启动命令不使用 `--runtime_thread_num``--batch_infer_size` 时,属于同步处理模式,未开启异步模式。`--thread 16` 表示启动16个同步网络处理线程。
```
python3 -m paddle_serving_server.serve --model uci_housing_model --thread 16 --port 9292
```
<a name="2.2"></a>
**二.开启异步模式**
启动命令使用 `--runtime_thread_num 2``--batch_infer_size 32` 开启异步模式,Serving 框架会启动2个异步线程,单次合并最大批量为32,自动开启动态 Padding。
```
python3 -m paddle_serving_server.serve --model uci_housing_model --thread 16 --port 9292 --runtime_thread_num 4 --batch_infer_size 32 --ir_optim --gpu_multi_stream --gpu_ids 0
```
<a name="3"></a>
## 性能测试
- GPU:Tesla P4 7611 MiB
- CUDA:cuda11.2-cudnn8-trt8
- Python 版本:python3.7
- 模型:ResNet_v2_50
- 测试数据:构造全1输入,单client请求100次,shape 范围(1, 224 ± 50, 224 ± 50)
同步模式启动命令:
```
python3 -m paddle_serving_server.serve --model resnet_v2_50_imagenet_model --port 9393 --thread 8 --ir_optim --gpu_multi_stream --gpu_ids 1 --enable_prometheus --prometheus_port 1939
```
异步模式启动命令:
```
python3 -m paddle_serving_server.serve --model resnet_v2_50_imagenet_model --port 9393 --thread 64 --runtime_thread_num 8 --ir_optim --gpu_multi_stream --gpu_ids 1 --enable_prometheus --prometheus_port 19393
```
<a name="3.1"></a>
**一.测试结果**
使用异步模式,并开启动态批量后,并发测试不同 shape 数据时,吞吐性能大幅提升。
<div align=center>
<img src='images/6-1_Cpp_Asynchronous_Framwork_CN_1.png' height = "600" align="middle"/>
</div
由于动态批量导致响应时长增长,经过测试,大多数场景下吞吐增量高于响应时长增长,尤其在高并发场景(client=70时),在响应时长增长 33% 情况下,吞吐增加 105%。
|Client |1 |5 |10 | 20 |30 |40 |50 |70 |
|---|---|---|---|---|---|---|---|---|
|QPS |-2.08% |-7.23% |-1.89% |20.55% |23.02% |23.34% |46.41% |105.27% |
|响应时长 | 2.70% |7.09% |5.24% |13.34% |10.80% |43.60% |8.72% |33.89% |
异步模式可有效提升服务吞吐性能。
<a name="3.2"></a>
**二.测试数据**
1. 同步模式
| client_num | batch_size |CPU_util_pre(%) |CPU_util(%) |GPU_memory(mb) |GPU_util(%) |qps(samples/s) |total count |mean(ms) |median(ms) |80 percent(ms) |90 percent(ms) |99 percent(ms) |total cost(s) |each cost(s)|infer_count_total|infer_cost_total(ms)|infer_cost_avg(ms)|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 |1 |1.30 |18.90 |2066 |71.56 |22.938 |100 |43.594 |23.516 |78.118 |78.323 |133.544 |4.4262 |4.3596 |7100.0000 |1666392.70 | 41.1081 |
| 5 |1 |2.00 |28.20 |3668 |92.57 |33.630 |500 |148.673 |39.531 |373.231 |396.306 |419.088 |15.0606 |14.8676 |7600.0000 |1739372.7480| 145.9601 |
|10 |1 |1.90 |29.80 |4202 |91.98 |34.303 |1000 |291.512 |76.728 |613.963 |632.736 |1217.863 |29.8004 |29.1516 |8600.0000 |1974147.7420| 234.7750 |
|20 |1 |4.70 |49.60 |4736 |92.63 |34.359 |2000 |582.089 |154.952 |1239.115 |1813.371 |1858.128 |59.7303 |58.2093 |12100.0000 |2798459.6330 |235.6248 |
|30 |1 |5.70 |65.70 |4736 |92.60 |34.162 |3000 |878.164 |231.121 |2391.687 |2442.744 |2499.963 |89.6546 |87.8168 |17600.0000 |4100408.9560 |236.6877 |
|40 |1 |5.40 |74.40 |5270 |92.44 |34.090 |4000 |1173.373 |306.244 |3037.038 |3070.198 |3134.894 |119.4162 |117.3377 |21600.0000 |5048139.2170 |236.9326|
|50 |1 |1.40 |64.70 |5270 |92.37 |34.031 |5000 |1469.250 |384.327 |3676.812 |3784.330 |4366.862 |149.7041 |146.9254 |26600.0000 |6236269.4230 |237.6260|
|70 |1 |3.70 |79.70 |5270 |91.89 |33.976 |7000 |2060.246 |533.439 |5429.255 |5552.704 |5661.492 |210.1008 |206.0250 |33600.0000 |7905005.9940 |238.3909|
2. 异步模式 - 未开启动态批量
| client_num | batch_size |CPU_util_pre(%) |CPU_util(%) |GPU_memory(mb) |GPU_util(%) |qps(samples/s) |total count |mean(ms) |median(ms) |80 percent(ms) |90 percent(ms) |99 percent(ms) |total cost(s) |each cost(s)|infer_count_total|infer_cost_total(ms)|infer_cost_avg(ms)|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 |1 |6.20 |13.60 |5170 |71.11 |22.894 |100 |43.677 |23.992 |78.285 |78.788 |123.542 |4.4253 |4.3679 |3695.0000 |745061.9120 |40.6655 |
| 5 |1 |6.10 |32.20 |7306 |89.54 |33.532 |500 |149.109 |43.906 |376.889 |401.999 |422.753 |15.1623 |14.9113 |4184.0000 |816834.2250 |146.7736|
|10 |1 |4.90 |43.60 |7306 |91.55 |38.136 |1000 |262.216 |75.393 |575.788 |632.016 |1247.775 |27.1019 |26.2220 |5107.0000 |1026490.3950 |227.1464|
|20 |1 |5.70 |39.60 |7306 |91.36 |58.601 |2000 |341.287 |145.774 |646.824 |994.748 |1132.979 |38.3915 |34.1291 |7461.0000 |1555234.6260 |229.9113|
|30 |1 |1.30 |45.40 |7484 |91.10 |69.008 |3000 |434.728 |204.347 |959.184 |1092.181 |1661.289 |46.3822 |43.4732 |10289.0000 |2269499.9730 |249.4257|
|40 |1 |3.10 |73.00 |7562 |91.83 |80.956 |4000 |494.091 |272.889 |966.072 |1310.011 |1851.887 |52.0609 |49.4095 |12102.0000 |2678878.2010 |225.8016|
|50 |1 |0.80 |68.00 |7522 |91.10 |83.018 |5000 |602.276 |364.064 |1058.261 |1473.051 |1671.025 |72.9869 |60.2280 |14225.0000 |3256628.2820 |272.1385|
|70 |1 |6.10 |78.40 |7584 |92.02 |65.069 |7000 |1075.777 |474.014 |2411.296 |2705.863 |3409.085 |111.6653 |107.5781 |17974.0000 |4139377.4050 |235.4626
3. 异步模式 - 开启动态批量
| client_num | batch_size |CPU_util_pre(%) |CPU_util(%) |GPU_memory(mb) |GPU_util(%) |qps(samples/s) |total count |mean(ms) |median(ms) |80 percent(ms) |90 percent(ms) |99 percent(ms) |total cost(s) |each cost(s)|infer_count_total|infer_cost_total(ms)|infer_cost_avg(ms)|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 |1 |1.20 |13.30 |6048 |70.07 |22.417 |100 |44.606 |24.486 |78.365 |78.707 |139.349 |4.5201 |4.4608 |1569.0000 |462418.6390 |41.7646 |
| 5 |1 |1.20 |50.80 |7116 |87.37 |31.106 |500 |160.740 |42.506 |414.903 |458.841 |481.112 |16.3525 |16.0743 |2059.0000 |539439.3300 |157.1851
|10 |1 |0.80 |26.20 |7264 |88.74 |37.417 |1000 |267.254 |79.452 |604.451 |686.477 |1345.528 |27.9848 |26.7258 |2950.0000 |752428.0570 |239.0446|
|20 |1 |1.50 |32.80 |7264 |89.52 |70.641 |2000 |283.117 |133.441 |516.066 |652.089 |1274.957 |33.0280 |28.3121 |4805.0000 |1210814.5610 |260.5873|
|30 |1 |0.90 |59.10 |7348 |89.57 |84.894 |3000 |353.380 |217.385 |613.587 |757.829 |1277.283 |40.7093 |35.3384 |6924.0000 |1817515.1710 |276.3695|
|40 |1 |1.30 |57.30 |7356 |89.30 |99.853 |4000 |400.584 |204.425 |666.015 |1031.186 |1380.650 |49.4807 |40.0588 |8104.0000 |2200137.0060 |324.2558|
|50 |1 |1.50 |50.60 |7578 |89.04 |121.545 |5000 |411.364 |331.118 |605.809 |874.543 |1285.650 |48.2343 |41.1369 |9350.0000 |2568777.6400 |295.8593|
|70 |1 |3.80 |83.20 |7602 |89.59 |133.568 |7000 |524.073 |382.653 |799.463 |1202.179 |1576.809 |57.2885 |52.4077 |10761.0000 |3013600.9670 |315.2540|
# 加密模型预测 # 加密模型预测
(简体中文|[English](./Encryption_EN.md)) Padle Serving 提供了模型加密预测功能,本文档显示了详细信息。
Padle Serving提供了模型加密预测功能,本文档显示了详细信息。
## 原理 ## 原理
采用对称加密算法对模型进行加密。对称加密算法采用同一密钥进行加解密,它计算量小,速度快,是最常用的加密方法。 采用对称加密算法对模型进行加密。对称加密算法采用同一密钥进行加解密,它计算量小,速度快,是最常用的加密方法。
### 获得加密模型 **一. 获得加密模型:**
普通的模型和参数可以理解为一个字符串,通过对其使用加密算法(参数是您的密钥),普通模型和参数就变成了一个加密的模型和参数。 普通的模型和参数可以理解为一个字符串,通过对其使用加密算法(参数是您的密钥),普通模型和参数就变成了一个加密的模型和参数。
我们提供了一个简单的演示来加密模型。请参阅[examples/C++/encryption/encrypt.py](../../examples/C++/encryption/encrypt.py) 我们提供了一个简单的演示来加密模型。请参阅[examples/C++/encryption/encrypt.py](../../examples/C++/encryption/encrypt.py)
### 启动加密服务 **二. 启动加密服务:**
假设您已经有一个已经加密的模型(在`encrypt_server/`路径下),您可以通过添加一个额外的命令行参数 `--use_encryption_model`来启动加密模型服务。 假设您已经有一个已经加密的模型(在`encrypt_server/`路径下),您可以通过添加一个额外的命令行参数 `--use_encryption_model`来启动加密模型服务。
...@@ -30,7 +28,7 @@ python -m paddle_serving_server.serve --model encrypt_server/ --port 9300 --use_ ...@@ -30,7 +28,7 @@ python -m paddle_serving_server.serve --model encrypt_server/ --port 9300 --use_
此时,服务器不会真正启动,而是等待密钥。 此时,服务器不会真正启动,而是等待密钥。
### Client Encryption Inference **三. Client Encryption Inference:**
首先,您必须拥有模型加密过程中使用的密钥。 首先,您必须拥有模型加密过程中使用的密钥。
...@@ -39,5 +37,6 @@ python -m paddle_serving_server.serve --model encrypt_server/ --port 9300 --use_ ...@@ -39,5 +37,6 @@ python -m paddle_serving_server.serve --model encrypt_server/ --port 9300 --use_
一旦服务器获得密钥,它就使用该密钥解析模型并启动模型预测服务。 一旦服务器获得密钥,它就使用该密钥解析模型并启动模型预测服务。
### 模型加密推理示例 **四. 模型加密推理示例:**
模型加密推理示例, 请参见[examples/C++/encryption/](../../examples/C++/encryption/) 模型加密推理示例, 请参见[examples/C++/encryption/](../../examples/C++/encryption/)
# Paddle Serving中的模型热加载 # Paddle Serving 中的模型热加载
(简体中文|[English](./Hot_Loading_EN.md))
## 背景 ## 背景
...@@ -8,35 +6,35 @@ ...@@ -8,35 +6,35 @@
## Server Monitor ## Server Monitor
Paddle Serving提供了一个自动监控脚本,远端地址更新模型后会拉取新模型更新本地模型,同时更新本地模型文件夹中的时间戳文件`fluid_time_stamp`实现热加载。 Paddle Serving 提供了一个自动监控脚本,远端地址更新模型后会拉取新模型更新本地模型,同时更新本地模型文件夹中的时间戳文件 `fluid_time_stamp` 实现热加载。
目前支持下面几种类型的远端监控Monitor: 目前支持下面几种类型的远端监控 Monitor:
| Monitor类型 | 描述 | 特殊选项 | | Monitor类型 | 描述 | 特殊选项 |
| :---------: | :----------------------------------------------------------: | :----------------------------------------------------------: | | :---------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| general | 远端无认证,可以通过`wget`直接访问下载文件(如无需认证的FTP,BOS等) | `general_host` 通用远端host | | general | 远端无认证,可以通过 `wget` 直接访问下载文件(如无需认证的FTP,BOS等) | `general_host` 通用远端host |
| hdfs/afs(HadoopMonitor) | 远端为HDFS或AFS,通过Hadoop-Client执行相关命令 | `hadoop_bin` Hadoop二进制的路径<br/>`fs_name` Hadoop fs_name,默认为空<br/>`fs_ugi` Hadoop fs_ugi,默认为空 | | hdfs/afs(HadoopMonitor) | 远端为 HDFS 或 AFS,通过 Hadoop-Client 执行相关命令 | `hadoop_bin` Hadoop 二进制的路径 <br/>`fs_name` Hadoop fs_name,默认为空<br/>`fs_ugi` Hadoop fs_ugi,默认为空 |
| ftp | 远端为FTP,通过`ftplib`进行相关访问(使用该Monitor,您可能需要执行`pip install ftplib`下载`ftplib`) | `ftp_host` FTP host<br>`ftp_port` FTP port<br>`ftp_username` FTP username,默认为空<br>`ftp_password` FTP password,默认为空 | | ftp | 远端为 FTP,通过 `ftplib` 进行相关访问(使用该 Monitor,您可能需要执行 `pip install ftplib` 下载 `ftplib`) | `ftp_host` FTP host<br>`ftp_port` FTP port<br>`ftp_username` FTP username,默认为空<br>`ftp_password` FTP password,默认为空 |
| Monitor通用选项 | 描述 | 默认值 | | Monitor通用选项 | 描述 | 默认值 |
| :--------------------: | :----------------------------------------------------------: | :--------------------: | | :--------------------: | :----------------------------------------------------------: | :--------------------: |
| `type` | 指定Monitor类型 | 无 | | `type` | 指定 Monitor 类型 | 无 |
| `remote_path` | 指定远端的基础路径 | 无 | | `remote_path` | 指定远端的基础路径 | 无 |
| `remote_model_name` | 指定远端需要拉取的模型名 | 无 | | `remote_model_name` | 指定远端需要拉取的模型名 | 无 |
| `remote_donefile_name` | 指定远端标志模型更新完毕的donefile文件名 | 无 | | `remote_donefile_name` | 指定远端标志模型更新完毕的 donefile 文件名 | 无 |
| `local_path` | 指定本地工作路径 | 无 | | `local_path` | 指定本地工作路径 | 无 |
| `local_model_name` | 指定本地模型名 | 无 | | `local_model_name` | 指定本地模型名 | 无 |
| `local_timestamp_file` | 指定本地用于热加载的时间戳文件,该文件被认为在`local_path/local_model_name`下。 | `fluid_time_file` | | `local_timestamp_file` | 指定本地用于热加载的时间戳文件,该文件被认为在 `local_path/local_model_name` 下。 | `fluid_time_file` |
| `local_tmp_path` | 指定本地存放临时文件的文件夹路径,若不存在则自动创建。 | `_serving_monitor_tmp` | | `local_tmp_path` | 指定本地存放临时文件的文件夹路径,若不存在则自动创建。 | `_serving_monitor_tmp` |
| `interval` | 指定轮询间隔时间,单位为秒。 | `10` | | `interval` | 指定轮询间隔时间,单位为秒。 | `10` |
| `unpacked_filename` | Monitor支持tarfile打包的远程模型。如果远程模型是打包格式,则需要设置该选项来告知Monitor解压后的文件名。 | `None` | | `unpacked_filename` | Monitor 支持 tarfile 打包的远程模型。如果远程模型是打包格式,则需要设置该选项来告知 Monitor 解压后的文件名。 | `None` |
| `debug` | 如果添加`--debug`选项,则输出更详细的中间信息。 | 默认不添加该选项 | | `debug` | 如果添加 `--debug` 选项,则输出更详细的中间信息。 | 默认不添加该选项 |
下面通过HadoopMonitor示例来展示Paddle Serving的模型热加载功能。 下面通过 HadoopMonitor 示例来展示 Paddle Serving 的模型热加载功能。
## HadoopMonitor示例 ## HadoopMonitor 示例
示例中在`product_path`中生产模型上传至hdfs,在`server_path`中模拟服务端模型热加载: 示例中在 `product_path` 中生产模型上传至 hdfs,在 `server_path` 中模拟服务端模型热加载:
```shell ```shell
. .
...@@ -44,9 +42,9 @@ Paddle Serving提供了一个自动监控脚本,远端地址更新模型后会 ...@@ -44,9 +42,9 @@ Paddle Serving提供了一个自动监控脚本,远端地址更新模型后会
└── server_path └── server_path
``` ```
### 生产模型 **一.生产模型**
`product_path`下运行下面的Python代码生产模型(运行前需要修改hadoop相关的参数),每隔 60 秒会产出 Boston 房价预测模型的打包文件`uci_housing.tar.gz`并上传至hdfs的`/`路径下,上传完毕后更新时间戳文件`donefile`并上传至hdfs`/`路径下。 `product_path` 下运行下面的 Python 代码生产模型(运行前需要修改 hadoop 相关的参数),每隔 60 秒会产出 Boston 房价预测模型的打包文件 `uci_housing.tar.gz` 并上传至 hdfs 的`/`路径下,上传完毕后更新时间戳文件 `donefile` 并上传至 hdfs `/`路径下。
```python ```python
import os import os
...@@ -121,7 +119,7 @@ for pass_id in range(30): ...@@ -121,7 +119,7 @@ for pass_id in range(30):
push_to_hdfs(donefile_name, '/') push_to_hdfs(donefile_name, '/')
``` ```
hdfs上的文件如下列所示: hdfs 上的文件如下列所示:
```bash ```bash
# hadoop fs -ls / # hadoop fs -ls /
...@@ -130,11 +128,11 @@ Found 2 items ...@@ -130,11 +128,11 @@ Found 2 items
-rw-r--r-- 1 root supergroup 2101 2020-04-02 02:54 /uci_housing.tar.gz -rw-r--r-- 1 root supergroup 2101 2020-04-02 02:54 /uci_housing.tar.gz
``` ```
### 服务端加载模型 **二.服务端加载模型**
进入`server_path`文件夹。 进入 `server_path` 文件夹。
#### 用初始模型启动Server 1. 用初始模型启动 Server
这里使用预训练的 Boston 房价预测模型作为初始模型: 这里使用预训练的 Boston 房价预测模型作为初始模型:
...@@ -143,15 +141,15 @@ wget --no-check-certificate https://paddle-serving.bj.bcebos.com/uci_housing.tar ...@@ -143,15 +141,15 @@ wget --no-check-certificate https://paddle-serving.bj.bcebos.com/uci_housing.tar
tar -xzf uci_housing.tar.gz tar -xzf uci_housing.tar.gz
``` ```
启动Server端: 启动 Server 端:
```shell ```shell
python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292 python -m paddle_serving_server.serve --model uci_housing_model --thread 10 --port 9292
``` ```
#### 执行监控程序 2. 执行监控程序
用下面的命令来执行HDFS监控程序: 用下面的命令来执行 HDFS 监控程序:
```shell ```shell
python -m paddle_serving_server.monitor \ python -m paddle_serving_server.monitor \
...@@ -162,7 +160,7 @@ python -m paddle_serving_server.monitor \ ...@@ -162,7 +160,7 @@ python -m paddle_serving_server.monitor \
--local_tmp_path='_tmp' --unpacked_filename='uci_housing_model' --debug --local_tmp_path='_tmp' --unpacked_filename='uci_housing_model' --debug
``` ```
上面代码通过轮询方式监控远程HDFS地址`/`的时间戳文件`/donefile`,当时间戳变更则认为远程模型已经更新,将远程打包模型`/uci_housing.tar.gz`拉取到本地临时路径`./_tmp/uci_housing.tar.gz`下,解包出模型文件`./_tmp/uci_housing_model`后,更新本地模型`./uci_housing_model`以及Paddle Serving的时间戳文件`./uci_housing_model/fluid_time_file` 上面代码通过轮询方式监控远程 HDFS 地址`/`的时间戳文件`/donefile`,当时间戳变更则认为远程模型已经更新,将远程打包模型`/uci_housing.tar.gz`拉取到本地临时路径`./_tmp/uci_housing.tar.gz`下,解包出模型文件`./_tmp/uci_housing_model`后,更新本地模型`./uci_housing_model`以及Paddle Serving的时间戳文件`./uci_housing_model/fluid_time_file`
预计输出如下: 预计输出如下:
...@@ -197,9 +195,9 @@ python -m paddle_serving_server.monitor \ ...@@ -197,9 +195,9 @@ python -m paddle_serving_server.monitor \
2020-04-02 10:12 INFO [monitor.py:161] sleep 10s. 2020-04-02 10:12 INFO [monitor.py:161] sleep 10s.
``` ```
#### 查看Server日志 3. 查看 Server 日志
通过下面命令查看Server的运行日志: 通过下面命令查看 Server 的运行日志:
```shell ```shell
tail -f log/serving.INFO tail -f log/serving.INFO
......
# Inference Protocols # Inference Protocols
C++ Serving基于BRPC进行服务构建,支持BRPC、GRPC、RESTful请求。请求数据为protobuf格式,详见`core/general-server/proto/general_model_service.proto`。本文介绍构建请求以及解析结果的方法。 C++ Serving 基于 BRPC 进行服务构建,支持 BRPC、GRPC、RESTful 请求。请求数据为 protobuf 格式,详见 `core/general-server/proto/general_model_service.proto`。本文介绍构建请求以及解析结果的方法。
## Tensor ## Tensor
Tensor可以装载多种类型的数据,是Request和Response的基础单元。Tensor的具体定义如下: **一.Tensor 定义**
Tensor 可以装载多种类型的数据,是 Request 和 Response 的基础单元。Tensor 的具体定义如下:
```protobuf ```protobuf
message Tensor { message Tensor {
...@@ -71,7 +73,7 @@ message Tensor { ...@@ -71,7 +73,7 @@ message Tensor {
}; };
``` ```
- elem_type:数据类型,当前支持FLOAT32, INT64, INT32, UINT8, INT8, FLOAT16 - elem_type:数据类型,当前支持 FLOAT32, INT64, INT32, UINT8, INT8, FLOAT16
|elem_type|类型| |elem_type|类型|
|---------|----| |---------|----|
...@@ -86,10 +88,12 @@ message Tensor { ...@@ -86,10 +88,12 @@ message Tensor {
|8|INT8| |8|INT8|
- shape:数据维度 - shape:数据维度
- lod:lod信息,LoD(Level-of-Detail) Tensor是Paddle的高级特性,是对Tensor的一种扩充,用于支持更自由的数据输入。详见[LOD](../LOD_CN.md) - lod:lod 信息,LoD(Level-of-Detail) Tensor 是 Paddle 的高级特性,是对 Tensor 的一种扩充,用于支持更自由的数据输入。Lod 相关原理介绍,请参考[相关文档](../LOD_CN.md)
- name/alias_name: 名称及别名,与模型配置对应 - name/alias_name: 名称及别名,与模型配置对应
### 构建FLOAT32数据Tensor **二.构建 Tensor 数据**
1. FLOAT32 类型 Tensor
```C ```C
// 原始数据 // 原始数据
...@@ -99,7 +103,7 @@ Tensor *tensor = new Tensor; ...@@ -99,7 +103,7 @@ Tensor *tensor = new Tensor;
for (uint32_t j = 0; j < float_shape.size(); ++j) { for (uint32_t j = 0; j < float_shape.size(); ++j) {
tensor->add_shape(float_shape[j]); tensor->add_shape(float_shape[j]);
} }
// 设置LOD信息 // 设置 LOD 信息
for (uint32_t j = 0; j < float_lod.size(); ++j) { for (uint32_t j = 0; j < float_lod.size(); ++j) {
tensor->add_lod(float_lod[j]); tensor->add_lod(float_lod[j]);
} }
...@@ -113,7 +117,7 @@ tensor->mutable_float_data()->Resize(total_number, 0); ...@@ -113,7 +117,7 @@ tensor->mutable_float_data()->Resize(total_number, 0);
memcpy(tensor->mutable_float_data()->mutable_data(), float_datadata(), total_number * sizeof(float)); memcpy(tensor->mutable_float_data()->mutable_data(), float_datadata(), total_number * sizeof(float));
``` ```
### 构建INT8数据Tensor 2. INT8 类型 Tensor
```C ```C
// 原始数据 // 原始数据
...@@ -133,7 +137,9 @@ tensor->set_tensor_content(string_data); ...@@ -133,7 +137,9 @@ tensor->set_tensor_content(string_data);
## Request ## Request
Request为客户端需要发送的请求数据,其以Tensor为基础数据单元,并包含了额外的请求信息。定义如下: **一.Request 定义**
Request 为客户端需要发送的请求数据,其以 Tensor 为基础数据单元,并包含了额外的请求信息。定义如下:
```protobuf ```protobuf
message Request { message Request {
...@@ -148,9 +154,11 @@ message Request { ...@@ -148,9 +154,11 @@ message Request {
- profile_server: 调试参数,打开时会输出性能信息 - profile_server: 调试参数,打开时会输出性能信息
- log_id: 请求ID - log_id: 请求ID
### 构建Request **二.构建 Request**
当使用BRPC或GRPC进行请求时,使用protobuf形式数据,构建方式如下: 1. Protobuf 形式
当使用 BRPC 或 GRPC 进行请求时,使用 protobuf 形式数据,构建方式如下:
```C ```C
Request req; Request req;
...@@ -162,16 +170,19 @@ for (auto &name : fetch_name) { ...@@ -162,16 +170,19 @@ for (auto &name : fetch_name) {
Tensor *tensor = req.add_tensor(); Tensor *tensor = req.add_tensor();
... ...
``` ```
2. Json 形式
当使用RESTful请求时,可以使用JSON形式数据,具体格式如下: 当使用 RESTful 请求时,可以使用 Json 形式数据,具体格式如下:
```JSON ```Json
{"tensor":[{"float_data":[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],"elem_type":1,"name":"x","alias_name":"x","shape":[1,13]}],"fetch_var_names":["price"],"log_id":0} {"tensor":[{"float_data":[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],"elem_type":1,"name":"x","alias_name":"x","shape":[1,13]}],"fetch_var_names":["price"],"log_id":0}
``` ```
## Response ## Response
Response为服务端返回给客户端的结果,包含了Tensor数据、错误码、错误信息等。定义如下: **一.Response 定义**
Response 为服务端返回给客户端的结果,包含了 Tensor 数据、错误码、错误信息等。定义如下:
```protobuf ```protobuf
message Response { message Response {
...@@ -190,7 +201,7 @@ message ModelOutput { ...@@ -190,7 +201,7 @@ message ModelOutput {
} }
``` ```
- profile_time:当设置request->set_profile_server(true)时,会返回性能信息 - profile_time:当设置 request->set_profile_server(true) 时,会返回性能信息
- err_no:错误码,详见`core/predictor/common/constant.h` - err_no:错误码,详见`core/predictor/common/constant.h`
- err_msg:错误信息,详见`core/predictor/common/constant.h` - err_msg:错误信息,详见`core/predictor/common/constant.h`
- engine_name:输出节点名称 - engine_name:输出节点名称
...@@ -203,19 +214,19 @@ message ModelOutput { ...@@ -203,19 +214,19 @@ message ModelOutput {
|-5002|"Paddle Serving Array Overflow Error."| |-5002|"Paddle Serving Array Overflow Error."|
|-5100|"Paddle Serving Op Inference Error."| |-5100|"Paddle Serving Op Inference Error."|
### 读取Response数据 **二.读取 Response 数据**
```C ```C
uint32_t model_num = res.outputs_size(); uint32_t model_num = res.outputs_size();
for (uint32_t m_idx = 0; m_idx < model_num; ++m_idx) { for (uint32_t m_idx = 0; m_idx < model_num; ++m_idx) {
std::string engine_name = output.engine_name(); std::string engine_name = output.engine_name();
int idx = 0; int idx = 0;
// 读取tensor维度 // 读取 tensor 维度
int shape_size = output.tensor(idx).shape_size(); int shape_size = output.tensor(idx).shape_size();
for (int i = 0; i < shape_size; ++i) { for (int i = 0; i < shape_size; ++i) {
shape[i] = output.tensor(idx).shape(i); shape[i] = output.tensor(idx).shape(i);
} }
// 读取LOD信息 // 读取 LOD 信息
int lod_size = output.tensor(idx).lod_size(); int lod_size = output.tensor(idx).lod_size();
if (lod_size > 0) { if (lod_size > 0) {
lod.resize(lod_size); lod.resize(lod_size);
...@@ -223,12 +234,12 @@ for (uint32_t m_idx = 0; m_idx < model_num; ++m_idx) { ...@@ -223,12 +234,12 @@ for (uint32_t m_idx = 0; m_idx < model_num; ++m_idx) {
lod[i] = output.tensor(idx).lod(i); lod[i] = output.tensor(idx).lod(i);
} }
} }
// 读取float数据 // 读取 float 数据
int size = output.tensor(idx).float_data_size(); int size = output.tensor(idx).float_data_size();
float_data = std::vector<float>( float_data = std::vector<float>(
output.tensor(idx).float_data().begin(), output.tensor(idx).float_data().begin(),
output.tensor(idx).float_data().begin() + size); output.tensor(idx).float_data().begin() + size);
// 读取int8数据 // 读取 int8 数据
string_data = output.tensor(idx).tensor_content(); string_data = output.tensor(idx).tensor_content();
} }
``` ```
\ No newline at end of file
# Paddle Serving中的集成预测 # 如何使用 C++ 定义模型组合
(简体中文|[English](./Model_Ensemble_EN.md)) 如果您的模型处理过程包含一个以上的模型推理环节(例如 OCR 一般需要 det+rec 两个环节),此时有两种做法可以满足您的需求。
在一些场景中,可能使用多个相同输入的模型并行集成预测以获得更好的预测效果,Paddle Serving提供了这项功能。 1. 启动两个 Serving 服务(例如 Serving-det, Serving-rec),在您的 Client 中,读入数据——>det 前处理——>调用 Serving-det 预测——>det 后处理——>rec 前处理——>调用 Serving-rec 预测——>rec 后处理——>输出结果。
- 优点:无须改动 Paddle Serving 代码
下面将以文本分类任务为例,来展示Paddle Serving的集成预测功能(暂时还是串行预测,我们会尽快支持并行化)。 - 缺点:需要两次请求服务,请求数据量越大,效率稍差。
2. 通过修改代码,自定义模型预测行为(自定义 OP),自定义服务处理的流程(自定义 DAG),将多个模型的组合处理过程(上述的 det 前处理——>调用 Serving-det 预测——>det 后处理——>rec 前处理——>调用 Serving-rec 预测——>rec 后处理)集成在一个 Serving 服务中。此时,在您的 Client 中,读入数据——>调用集成后的 Serving——>输出结果。
## 集成预测样例 - 优点:只需要一次请求服务,效率高。
- 缺点:需要改动代码,且需要重新编译。
该样例中(见下图),Server端在一项服务中并行预测相同输入的BOW和CNN模型,Client端获取两个模型的预测结果并进行后处理,得到最终的预测结果。
本文主要介绍自定义服务处理流程的方法,该方法的基本步骤如下:
![simple example](../images/model_ensemble_example.png) 1. 自定义 OP(即定义单个模型的前处理-模型预测-模型后处理)
2. 编译
需要注意的是,目前只支持在同一个服务中使用多个相同格式输入输出的模型。在该例子中,CNN模型和BOW模型的输入输出格式是相同的。 3. 服务启动与调用
样例中用到的代码保存在`examples/C++/imdb`路径下: ## 自定义 OP
一个 OP 定义了单个模型的前处理-模型预测-模型后处理,定义 OP 需要以下 2 步:
```shell 1. 定义 C++.h 头文件
. 2. 定义 C++.cpp 源文件
├── get_data.sh
├── imdb_reader.py **一. 定义 C++.h 头文件**
├── test_ensemble_client.py 复制下方的代码,将其中`/*自定义 Class 名称*/`更换为自定义的类名即可,如 `GeneralDetectionOp`
└── test_ensemble_server.py
放置于 `core/general-server/op/` 路径下,文件名自定义即可,如 `general_detection_op.h`
``` C++
#pragma once
#include <string>
#include <vector>
#include "core/general-server/general_model_service.pb.h"
#include "core/general-server/op/general_infer_helper.h"
#include "paddle_inference_api.h" // NOLINT
namespace baidu {
namespace paddle_serving {
namespace serving {
class /*自定义Class名称*/
: public baidu::paddle_serving::predictor::OpWithChannel<GeneralBlob> {
public:
typedef std::vector<paddle::PaddleTensor> TensorVector;
DECLARE_OP(/*自定义Class名称*/);
int inference();
};
} // namespace serving
} // namespace paddle_serving
} // namespace baidu
``` ```
**二. 定义 C++.cpp 源文件**
### 数据准备 复制下方的代码,将其中`/*自定义 Class 名称*/`更换为自定义的类名,如 `GeneralDetectionOp`
通过下面命令获取预训练的CNN和BOW模型(您也可以直接运行`get_data.sh`脚本): 将前处理和后处理的代码添加在下方的代码中注释的前处理和后处理的位置。
```shell 放置于 `core/general-server/op/` 路径下,文件名自定义即可,如 `general_detection_op.cpp`
wget --no-check-certificate https://fleet.bj.bcebos.com/text_classification_data.tar.gz
wget --no-check-certificate https://paddle-serving.bj.bcebos.com/imdb-demo/imdb_model.tar.gz ``` C++
tar -zxvf text_classification_data.tar.gz #include "core/general-server/op/自定义的头文件名"
tar -zxvf imdb_model.tar.gz #include <algorithm>
#include <iostream>
#include <memory>
#include <sstream>
#include "core/predictor/framework/infer.h"
#include "core/predictor/framework/memory.h"
#include "core/predictor/framework/resource.h"
#include "core/util/include/timer.h"
namespace baidu {
namespace paddle_serving {
namespace serving {
using baidu::paddle_serving::Timer;
using baidu::paddle_serving::predictor::MempoolWrapper;
using baidu::paddle_serving::predictor::general_model::Tensor;
using baidu::paddle_serving::predictor::general_model::Response;
using baidu::paddle_serving::predictor::general_model::Request;
using baidu::paddle_serving::predictor::InferManager;
using baidu::paddle_serving::predictor::PaddleGeneralModelConfig;
int /*自定义Class名称*/::inference() {
//获取前置OP节点
const std::vector<std::string> pre_node_names = pre_names();
if (pre_node_names.size() != 1) {
LOG(ERROR) << "This op(" << op_name()
<< ") can only have one predecessor op, but received "
<< pre_node_names.size();
return -1;
}
const std::string pre_name = pre_node_names[0];
//将前置OP的输出,作为本OP的输入。
GeneralBlob *input_blob = mutable_depend_argument<GeneralBlob>(pre_name);
if (!input_blob) {
LOG(ERROR) << "input_blob is nullptr,error";
return -1;
}
TensorVector *in = &input_blob->tensor_vector;
uint64_t log_id = input_blob->GetLogId();
int batch_size = input_blob->_batch_size;
//初始化本OP的输出。
GeneralBlob *output_blob = mutable_data<GeneralBlob>();
output_blob->SetLogId(log_id);
output_blob->_batch_size = batch_size;
VLOG(2) << "(logid=" << log_id << ") infer batch size: " << batch_size;
TensorVector *out = &output_blob->tensor_vector;
//前处理的代码添加在此处,前处理直接修改上文的TensorVector* in
//注意in里面的数据是前置节点的输出经过后处理后的out中的数据
Timer timeline;
int64_t start = timeline.TimeStampUS();
timeline.Start();
// 将前处理后的in,初始化的out传入,进行模型预测,模型预测的输出会直接修改out指向的内存中的数据
// 如果您想定义一个不需要模型调用,只进行数据处理的OP,删除下面这一部分的代码即可。
if (InferManager::instance().infer(
engine_name().c_str(), in, out, batch_size)) {
LOG(ERROR) << "(logid=" << log_id
<< ") Failed do infer in fluid model: " << engine_name().c_str();
return -1;
}
//后处理的代码添加在此处,后处理直接修改上文的TensorVector* out
//后处理后的out会被传递给后续的节点
int64_t end = timeline.TimeStampUS();
CopyBlobInfo(input_blob, output_blob);
AddBlobInfo(output_blob, start);
AddBlobInfo(output_blob, end);
return 0;
}
DEFINE_OP(/*自定义Class名称*/);
} // namespace serving
} // namespace paddle_serving
} // namespace baidu
``` ```
### 启动Server 1. TensorVector数据结构
通过下面的Python代码启动Server端(您也可以直接运行`test_ensemble_server.py`脚本): TensorVector* in 和 out 都是一个 TensorVector 类型的指针,其使用方法跟 Paddle C++ API 中的 Tensor 几乎一样,相关的数据结构如下所示
```python ``` C++
from paddle_serving_server import OpMaker //TensorVector
from paddle_serving_server import OpGraphMaker typedef std::vector<paddle::PaddleTensor> TensorVector;
from paddle_serving_server import Server
//paddle::PaddleTensor
op_maker = OpMaker() struct PD_INFER_DECL PaddleTensor {
read_op = op_maker.create('GeneralReaderOp') PaddleTensor() = default;
cnn_infer_op = op_maker.create( std::string name; ///< variable name.
'GeneralInferOp', engine_name='cnn', inputs=[read_op]) std::vector<int> shape;
bow_infer_op = op_maker.create( PaddleBuf data; ///< blob of data.
'GeneralInferOp', engine_name='bow', inputs=[read_op]) PaddleDType dtype;
response_op = op_maker.create( std::vector<std::vector<size_t>> lod; ///< Tensor+LoD equals LoDTensor
'GeneralResponseOp', inputs=[cnn_infer_op, bow_infer_op]) };
op_graph_maker = OpGraphMaker() //PaddleBuf
op_graph_maker.add_op(read_op) class PD_INFER_DECL PaddleBuf {
op_graph_maker.add_op(cnn_infer_op) public:
op_graph_maker.add_op(bow_infer_op)
op_graph_maker.add_op(response_op) explicit PaddleBuf(size_t length)
: data_(new char[length]), length_(length), memory_owned_(true) {}
server = Server()
server.set_op_graph(op_graph_maker.get_op_graph()) PaddleBuf(void* data, size_t length)
model_config = {cnn_infer_op: 'imdb_cnn_model', bow_infer_op: 'imdb_bow_model'} : data_(data), length_(length), memory_owned_{false} {}
server.load_model_config(model_config)
server.prepare_server(workdir="work_dir1", port=9393, device="cpu") explicit PaddleBuf(const PaddleBuf& other);
server.run_server()
void Resize(size_t length);
void Reset(void* data, size_t length);
bool empty() const { return length_ == 0; }
void* data() const { return data_; }
size_t length() const { return length_; }
~PaddleBuf() { Free(); }
PaddleBuf& operator=(const PaddleBuf&);
PaddleBuf& operator=(PaddleBuf&&);
PaddleBuf() = default;
PaddleBuf(PaddleBuf&& other);
private:
void Free();
void* data_{nullptr}; ///< pointer to the data memory.
size_t length_{0}; ///< number of memory bytes.
bool memory_owned_{true};
};
``` ```
与普通预测服务不同的是,这里我们需要用DAG来描述Server端的运行逻辑。 2. TensorVector 代码示例
```C++
/*例如,你想访问输入数据中的第1个Tensor*/
paddle::PaddleTensor& tensor_1 = in->at(0);
/*例如,你想修改输入数据中的第1个Tensor的名称*/
tensor_1.name = "new name";
/*例如,你想获取输入数据中的第1个Tensor的shape信息*/
std::vector<int> tensor_1_shape = tensor_1.shape;
/*例如,你想修改输入数据中的第1个Tensor中的数据*/
void* data_1 = tensor_1.data.data();
//后续直接修改data_1指向的内存即可
//比如,当您的数据是int类型,将void*转换为int*进行处理即可
```
在创建Op的时候需要指定当前Op的前继(在该例子中,`cnn_infer_op``bow_infer_op`的前继均是`read_op``response_op`的前继是`cnn_infer_op``bow_infer_op`),对于预测Op`infer_op`还需要定义预测引擎名称`engine_name`(也可以使用默认值,建议设置该值方便Client端获取预测结果)。
同时在配置模型路径时,需要以预测Op为key,对应的模型路径为value,创建模型配置字典,来告知Serving每个预测Op使用哪个模型。 ## 修改后编译
此时,需要您重新编译生成 serving,并通过 `export SERVING_BIN` 设置环境变量来指定使用您编译生成的 serving 二进制文件,并通过 `pip3 install` 的方式安装相关 python 包,细节请参考[如何编译Serving](2-3_Compile_CN.md)
### 启动Client ## 服务启动与调用
通过下面的Python代码运行Client端(您也可以直接运行`test_ensemble_client.py`脚本): **一. Server 端启动**
在前面两个小节工作做好的基础上,一个服务启动两个模型串联,只需要在`--model 后依次按顺序传入模型文件夹的相对路径`,且需要在`--op 后依次传入自定义 C++OP 类名称`,其中--model 后面的模型与--op 后面的类名称的顺序需要对应,`这里假设我们已经定义好了两个 OP 分别为 GeneralDetectionOp 和 GeneralRecOp`,则脚本代码如下:
```python ```python
from paddle_serving_client import Client #一个服务启动多模型串联
from imdb_reader import IMDBDataset python3 -m paddle_serving_server.serve --model ocr_det_model ocr_rec_model --op GeneralDetectionOp GeneralRecOp --port 9292
#多模型串联 ocr_det_model 对应 GeneralDetectionOp ocr_rec_model 对应 GeneralRecOp
client = Client()
# If you have more than one model, make sure that the input
# and output of more than one model are the same.
client.load_client_config('imdb_bow_client_conf/serving_client_conf.prototxt')
client.connect(["127.0.0.1:9393"])
# you can define any english sentence or dataset here
# This example reuses imdb reader in training, you
# can define your own data preprocessing easily.
imdb_dataset = IMDBDataset()
imdb_dataset.load_resource('imdb.vocab')
for i in range(3):
line = 'i am very sad | 0'
word_ids, label = imdb_dataset.get_words_and_label(line)
feed = {"words": word_ids}
fetch = ["acc", "cost", "prediction"]
fetch_maps = client.predict(feed=feed, fetch=fetch)
if len(fetch_maps) == 1:
print("step: {}, res: {}".format(i, fetch_maps['prediction'][0][1]))
else:
for model, fetch_map in fetch_maps.items():
print("step: {}, model: {}, res: {}".format(i, model, fetch_map[
'prediction'][0][1]))
``` ```
Client端与普通预测服务没有发生太大的变化。当使用多个模型预测时,预测服务将返回一个key为Server端定义的引擎名称`engine_name`,value为对应的模型预测结果的字典。 **二. Client 端调用**
### 预期结果 此时,Client 端的调用,也需要传入两个 Client 端的 proto 文件或文件夹的路径,以 OCR 为例,可以参考[ocr_cpp_client.py](../../examples/C++/PaddleOCR/ocr/ocr_cpp_client.py)来自行编写您的脚本,此时 Client 调用如下:
```python
```txt #一个服务启动多模型串联
step: 0, model: cnn, res: 0.560272455215 python3 自定义.py ocr_det_client ocr_rec_client
step: 0, model: bow, res: 0.633530199528 #ocr_det_client为第一个模型的Client端proto文件夹的相对路径
step: 1, model: cnn, res: 0.560272455215 #ocr_rec_client为第二个模型的Client端proto文件夹的相对路径
step: 1, model: bow, res: 0.633530199528
step: 2, model: cnn, res: 0.560272455215
step: 2, model: bow, res: 0.633530199528
``` ```
此时,对于 Server 端而言,输入的数据的格式与`第一个模型的 Client 端 proto 格式`定义的一致,输出的数据格式与`最后一个模型的 Client 端 proto`文件一致。一般情况下您无须关注此事,当您需要了解详细的proto的定义,请参考[Serving 配置](5-3_Serving_Configure_CN.md)
# 如何开发一个新的General Op? # 如何开发一个新的General Op?
(简体中文|[English](./OP_EN.md)) - [定义一个Op](#1)
- [在Op之间使用 `GeneralBlob`](#2)
- [2.1 实现 `int Inference()`](#2.1)
- [定义 Python API](#3)
在本文档中,我们主要集中于如何为Paddle Serving开发新的服务器端运算符。 在开始编写新运算符之前,让我们看一些示例代码以获得为服务器编写新运算符的基本思想。 我们假设您已经知道Paddle Serving服务器端的基本计算逻辑。 下面的代码您可以在 Serving代码库下的 `core/general-server/op` 目录查阅。 在本文档中,我们主要集中于如何为 Paddle Serving 开发新的服务器端运算符。在开始编写新运算符之前,让我们看一些示例代码以获得为服务器编写新运算符的基本思想。我们假设您已经知道 Paddle Serving 服务器端的基本计算逻辑。 下面的代码您可以在 Serving代码库下的 `core/general-server/op` 目录查阅。
``` c++ ``` c++
// Copyright (c) 2019 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.
#pragma once #pragma once
#include <string> #include <string>
#include <vector> #include <vector>
#ifdef BCLOUD
#ifdef WITH_GPU
#include "paddle/paddle_inference_api.h"
#else
#include "paddle/fluid/inference/api/paddle_inference_api.h"
#endif
#else
#include "paddle_inference_api.h" // NOLINT #include "paddle_inference_api.h" // NOLINT
#endif
#include "core/general-server/general_model_service.pb.h" #include "core/general-server/general_model_service.pb.h"
#include "core/general-server/op/general_infer_helper.h" #include "core/general-server/op/general_infer_helper.h"
...@@ -54,14 +36,17 @@ class GeneralInferOp ...@@ -54,14 +36,17 @@ class GeneralInferOp
} // namespace paddle_serving } // namespace paddle_serving
} // namespace baidu } // namespace baidu
``` ```
<a name="1"></a>
## 定义一个Op ## 定义一个Op
上面的头文件声明了一个名为`GeneralInferOp`的PaddleServing运算符。 在运行时,将调用函数 `int inference()`。 通常,我们将服务器端运算符定义为baidu::paddle_serving::predictor::OpWithChannel的子类,并使用 `GeneralBlob` 数据结构。 上面的头文件声明了一个名为 `GeneralInferOp` 的 Paddle Serving 运算符。 在运行时,将调用函数 `int inference()`。 通常,我们将服务器端运算符定义为baidu::paddle_serving::predictor::OpWithChannel 的子类,并使用 `GeneralBlob` 数据结构。
<a name="2"></a>
## 在Op之间使用 `GeneralBlob` ## 在Op之间使用 `GeneralBlob`
`GeneralBlob` 是一种可以在服务器端运算符之间使用的数据结构。 `tensor_vector``GeneralBlob`中最重要的数据结构。 服务器端的操作员可以将多个`paddle::PaddleTensor`作为输入,并可以将多个`paddle::PaddleTensor`作为输出。 特别是,`tensor_vector`可以在没有内存拷贝的操作下输入到Paddle推理引擎中。 `GeneralBlob` 是一种可以在服务器端运算符之间使用的数据结构。 `tensor_vector``GeneralBlob` 中最重要的数据结构。 服务器端的操作员可以将多个 `paddle::PaddleTensor` 作为输入,并可以将多个 `paddle::PaddleTensor `作为输出。 特别是,`tensor_vector` 可以在没有内存拷贝的操作下输入到 Paddle 推理引擎中。
``` c++ ``` c++
struct GeneralBlob { struct GeneralBlob {
...@@ -86,7 +71,9 @@ struct GeneralBlob { ...@@ -86,7 +71,9 @@ struct GeneralBlob {
}; };
``` ```
### 实现 `int Inference()` <a name="2.1"></a>
**一. 实现 `int Inference()`**
``` c++ ``` c++
int GeneralInferOp::inference() { int GeneralInferOp::inference() {
...@@ -127,14 +114,13 @@ int GeneralInferOp::inference() { ...@@ -127,14 +114,13 @@ int GeneralInferOp::inference() {
DEFINE_OP(GeneralInferOp); DEFINE_OP(GeneralInferOp);
``` ```
`input_blob``output_blob` 都有很多的 `paddle::PaddleTensor`, 且Paddle预测库会被 `InferManager::instance().infer(engine_name().c_str(), in, out, batch_size)`调用。此函数中的其他大多数代码都与性能分析有关,将来我们也可能会删除多余的代码。 `input_blob``output_blob` 都有很多的 `paddle::PaddleTensor`, 且 Paddle 预测库会被 `InferManager::instance().infer(engine_name().c_str(), in, out, batch_size)` 调用。此函数中的其他大多数代码都与性能分析有关,将来我们也可能会删除多余的代码。
基本上,以上代码可以实现一个新的运算符。如果您想访问字典资源,可以参考`core/predictor/framework/resource.cpp`来添加全局可见资源。资源的初始化在启动服务器的运行时执行。 <a name="3"></a>
## 定义 Python API ## 定义 Python API
在服务器端为Paddle Serving定义C++运算符后,最后一步是在Python API中为Paddle Serving服务器API添加注册, `python/paddle_serving_server/dag.py`文件里有关于API注册的代码如下 在服务器端为 Paddle Serving 定义 C++ 运算符后,最后一步是在 Python API 中为 Paddle Serving 服务器 API 添加注册, `python/paddle_serving_server/dag.py` 文件里有关于 API 注册的代码如下
``` python ``` python
...@@ -152,7 +138,7 @@ self.op_list = [ ...@@ -152,7 +138,7 @@ self.op_list = [
] ]
``` ```
`python/paddle_serving_server/server.py`文件中仅添加`需要加载模型,执行推理预测的自定义的C++OP类的类名`。例如`GeneralReaderOp`由于只是做一些简单的数据处理而不加载模型调用预测,故在👆的代码中需要添加,而不添加在👇的代码中。 `python/paddle_serving_server/server.py` 文件中仅添加`需要加载模型,执行推理预测的自定义的 C++ OP 类的类名`。例如 `GeneralReaderOp` 由于只是做一些简单的数据处理而不加载模型调用预测,故在上述的代码中需要添加,而不添加在下方的代码中。
``` python ``` python
default_engine_types = [ default_engine_types = [
'GeneralInferOp', 'GeneralInferOp',
......
# C++ Serving性能分析与优化 # C++ Serving性能分析与优化
# 1.背景知识介绍
## 背景知识介绍
1) 首先,应确保您知道C++ Serving常用的一些[功能特点](./Introduction_CN.md)[C++ Serving 参数配置和启动的详细说明](../Serving_Configure_CN.md) 1) 首先,应确保您知道C++ Serving常用的一些[功能特点](./Introduction_CN.md)[C++ Serving 参数配置和启动的详细说明](../Serving_Configure_CN.md)
2) 关于C++ Serving框架本身的性能分析和介绍,请参考[C++ Serving框架性能测试](./Frame_Performance_CN.md) 2) 关于C++ Serving框架本身的性能分析和介绍,请参考[C++ Serving框架性能测试](./Frame_Performance_CN.md)
3) 您需要对您使用的模型、机器环境、需要部署上线的业务有一些了解,例如,您使用CPU还是GPU进行预测;是否可以开启TRT进行加速;你的机器CPU是多少core的;您的业务包含几个模型;每个模型的输入和输出需要做些什么处理;您业务的最大线上流量是多少;您的模型支持的最大输入batch是多少等等. 3) 您需要对您使用的模型、机器环境、需要部署上线的业务有一些了解,例如,您使用CPU还是GPU进行预测;是否可以开启TRT进行加速;你的机器CPU是多少core的;您的业务包含几个模型;每个模型的输入和输出需要做些什么处理;您业务的最大线上流量是多少;您的模型支持的最大输入batch是多少等等.
......
# Request Cache # 请求缓存
本文主要介绍请求缓存功能及实现原理。 本文主要介绍请求缓存功能及实现原理。
服务中请求由张量tensor、结果名称fetch_var_names、调试开关profile_server、标识码log_id组成,预测结果包含输出张量等。这里缓存会保存请求与结果的键值对。当请求命中缓存时,服务不会执行模型预测,而是会直接从缓存中提取结果。对于某些特定场景而言,这能显著降低请求耗时。 ## 基本原理
缓存可以通过设置`--request_cache_size`来开启。该标志默认为0,即不开启缓存。当设置非零值时,服务会以设置大小为存储上限开启缓存。这里设置的内存单位为字节。注意,如果设置`--request_cache_size`为0是不能开启缓存的 服务中请求由张量 tensor、结果名称 fetch_var_names、调试开关 profile_server、标识码 log_id 组成,预测结果包含输出张量 tensor 等。这里缓存会保存请求与结果的键值对。当请求命中缓存时,服务不会执行模型预测,而是会直接从缓存中提取结果。对于某些特定场景而言,这能显著降低请求耗时
缓存中的键为64位整形数,是由请求中的tensor和fetch_var_names数据生成的128位哈希值。如果请求命中,那么对应的处理结果会提取出来用于构建响应数据。如果请求没有命中,服务则会执行模型预测,在返回结果的同时将处理结果放入缓存中。由于缓存设置了存储上限,因此需要淘汰机制来限制缓存容量。当前,服务采用了最近最少使用(LRU)机制用于淘汰缓存数据。 缓存可以通过设置`--request_cache_size`来开启。该标志默认为 0,即不开启缓存。当设置非零值时,服务会以设置大小为存储上限开启缓存。这里设置的内存单位为字节。注意,如果设置`--request_cache_size`为 0 是不能开启缓存的。
缓存中的键为 64 位整形数,是由请求中的 tensor 和 fetch_var_names 数据生成的 64 位哈希值。如果请求命中,那么对应的处理结果会提取出来用于构建响应数据。如果请求没有命中,服务则会执行模型预测,在返回结果的同时将处理结果放入缓存中。由于缓存设置了存储上限,因此需要淘汰机制来限制缓存容量。当前,服务采用了最近最少使用(LRU)机制用于淘汰缓存数据。
## 注意事项 ## 注意事项
- 只有预测成功的请求会进行缓存。如果请求失败或者在预测过程中返回错误,则处理结果不会缓存。 - 只有预测成功的请求会进行缓存。如果请求失败或者在预测过程中返回错误,则处理结果不会缓存。
- 缓存是基于请求数据的哈希值实现。因此,可能会出现两个不同的请求生成了相同的哈希值即哈希碰撞,这时服务可能会返回错误的响应数据。哈希值为64位数据,发生哈希碰撞的可能性较小。 - 缓存是基于请求数据的哈希值实现。因此,可能会出现两个不同的请求生成了相同的哈希值即哈希碰撞,这时服务可能会返回错误的响应数据。哈希值为 64 位数据,发生哈希碰撞的可能性较小。
- 不论使用同步模式还是异步模式,均可以正常使用缓存功能。 - 不论使用同步模式还是异步模式,均可以正常使用缓存功能。
...@@ -35,9 +35,10 @@ ...@@ -35,9 +35,10 @@
| 镜像选择 | 操作系统 | TAG | Dockerfile | | 镜像选择 | 操作系统 | TAG | Dockerfile |
| :----------------------------------------------------------: | :-----: | :--------------------------: | :----------------------------------------------------------: | | :----------------------------------------------------------: | :-----: | :--------------------------: | :----------------------------------------------------------: |
| CPU development | Ubuntu16 | 0.9.0-devel | [Dockerfile.devel](../tools/Dockerfile.devel) | | CPU development | Ubuntu16 | 0.9.0-devel | [Dockerfile.devel](../tools/Dockerfile.devel) |
| GPU (cuda10.1-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.1-cudnn7-devel | [Dockerfile.cuda10.1-cudnn7.devel](../tools/Dockerfile.cuda10.1-cudnn7.devel) | | GPU (cuda10.1-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.1-cudnn7-devel | [Dockerfile.cuda10.1-cudnn7.devel](../tools/Dockerfile.cuda10.1-cudnn7.devel) |
| GPU (cuda10.2-cudnn8-tensorRT7) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn8-devel | [Dockerfile.cuda10.2-cudnn8.devel](../tools/Dockerfile.cuda10.2-cudnn8.devel) | | GPU (cuda10.2-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn7-devel | [Dockerfile.cuda10.2-cudnn7.devel](../tools/Dockerfile.cuda10.2-cudnn7.devel)
| GPU (cuda11.2-cudnn8-tensorRT8) development | Ubuntu16 | 0.9.0-cuda11.2-cudnn8-devel | [Dockerfile.cuda11.2-cudnn8.devel](../tools/Dockerfile.cuda11.2-cudnn8.devel) | | GPU (cuda10.2-cudnn8-tensorRT7) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn8-devel | [Dockerfile.cuda10.2-cudnn8.devel](../tools/Dockerfile.cuda10.2-cudnn8.devel) |
| GPU (cuda11.2-cudnn8-tensorRT8) development | Ubuntu16 | 0.9.0-cuda11.2-cudnn8-devel | [Dockerfile.cuda11.2-cudnn8.devel](../tools/Dockerfile.cuda11.2-cudnn8.devel) |
**运行镜像:** **运行镜像:**
...@@ -48,13 +49,14 @@ ...@@ -48,13 +49,14 @@
|----------|---------|------------------------------|-----------|-------------|------| |----------|---------|------------------------------|-----------|-------------|------|
| CPU | 0.9.0 | 0.9.0-runtime | Ubuntu 16 | 8.2.0 | 3.9 GB | | CPU | 0.9.0 | 0.9.0-runtime | Ubuntu 16 | 8.2.0 | 3.9 GB |
| CUDA 10.1 + cuDNN 7 | 0.9.0 | 0.9.0-cuda10.1-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10 GB | | CUDA 10.1 + cuDNN 7 | 0.9.0 | 0.9.0-cuda10.1-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10 GB |
| CUDA 10.2 + cuDNN 7 | 0.9.0 | 0.9.0-cuda10.2-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10.1 GB |
| CUDA 10.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda10.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 10.1 GB | | CUDA 10.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda10.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 10.1 GB |
| CUDA 11.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda11.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 14.2 GB | | CUDA 11.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda11.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 14.2 GB |
**Java镜像:** **Java镜像:**
``` ```
registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda11.2-java registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda10.2-cudnn8-java
``` ```
**XPU镜像:** **XPU镜像:**
......
...@@ -39,6 +39,7 @@ A variety of development tools are installed in the development image, which can ...@@ -39,6 +39,7 @@ A variety of development tools are installed in the development image, which can
| :----------------------------------------------------------: | :-----: | :--------------------------: | :----------------------------------------------------------: | | :----------------------------------------------------------: | :-----: | :--------------------------: | :----------------------------------------------------------: |
| CPU development | Ubuntu16 | 0.9.0-devel | [Dockerfile.devel](../tools/Dockerfile.devel) | | CPU development | Ubuntu16 | 0.9.0-devel | [Dockerfile.devel](../tools/Dockerfile.devel) |
| GPU (cuda10.1-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.1-cudnn7-devel | [Dockerfile.cuda10.1-cudnn7.devel](../tools/Dockerfile.cuda10.1-cudnn7.devel) | | GPU (cuda10.1-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.1-cudnn7-devel | [Dockerfile.cuda10.1-cudnn7.devel](../tools/Dockerfile.cuda10.1-cudnn7.devel) |
| GPU (cuda10.2-cudnn7-tensorRT6) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn7-devel | [Dockerfile.cuda10.2-cudnn7.devel](../tools/Dockerfile.cuda10.2-cudnn7.devel)
| GPU (cuda10.2-cudnn8-tensorRT7) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn8-devel | [Dockerfile.cuda10.2-cudnn8.devel](../tools/Dockerfile.cuda10.2-cudnn8.devel) | | GPU (cuda10.2-cudnn8-tensorRT7) development | Ubuntu16 | 0.9.0-cuda10.2-cudnn8-devel | [Dockerfile.cuda10.2-cudnn8.devel](../tools/Dockerfile.cuda10.2-cudnn8.devel) |
| GPU (cuda11.2-cudnn8-tensorRT8) development | Ubuntu16 | 0.9.0-cuda11.2-cudnn8-devel | [Dockerfile.cuda11.2-cudnn8.devel](../tools/Dockerfile.cuda11.2-cudnn8.devel) | | GPU (cuda11.2-cudnn8-tensorRT8) development | Ubuntu16 | 0.9.0-cuda11.2-cudnn8-devel | [Dockerfile.cuda11.2-cudnn8.devel](../tools/Dockerfile.cuda11.2-cudnn8.devel) |
...@@ -50,13 +51,14 @@ Runtime Docker Images is lighter than Develop Images, and Running Images are mad ...@@ -50,13 +51,14 @@ Runtime Docker Images is lighter than Develop Images, and Running Images are mad
| Env | Version | Docker images tag | OS | Gcc Version | Size | | Env | Version | Docker images tag | OS | Gcc Version | Size |
|----------|---------|------------------------------|-----------|-------------|------| |----------|---------|------------------------------|-----------|-------------|------|
| CPU | 0.9.0 | 0.9.0-runtime | Ubuntu 16 | 8.2.0 | 3.9 GB | | CPU | 0.9.0 | 0.9.0-runtime | Ubuntu 16 | 8.2.0 | 3.9 GB |
| Cuda10.1 | 0.9.0 | 0.9.0-cuda10.1-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10 GB | | CUDA 10.1 + cuDNN 7 | 0.9.0 | 0.9.0-cuda10.1-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10 GB |
| Cuda10.2 | 0.9.0 | 0.9.0-cuda10.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 10.1 GB | | CUDA 10.2 + cuDNN 7 | 0.9.0 | 0.9.0-cuda10.2-cudnn7-runtime | Ubuntu 16 | 8.2.0 | 10.1 GB |
| Cuda11.2 | 0.9.0 | 0.9.0-cuda11.2-cudnn8-runtime| Ubuntu 16 | 8.2.0 | 14.2 GB | | CUDA 10.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda10.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 10.1
| CUDA 11.2 + cuDNN 8 | 0.9.0 | 0.9.0-cuda11.2-cudnn8-runtime | Ubuntu 16 | 8.2.0 | 14.2 GB |
**Java SDK Docker Image:** **Java SDK Docker Image:**
``` ```
registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda11.2-java registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda10.2-cudnn8-java
``` ```
**XPU Docker Images:** **XPU Docker Images:**
......
此差异已折叠。
...@@ -62,8 +62,9 @@ pip3 install -r python/requirements.txt ...@@ -62,8 +62,9 @@ pip3 install -r python/requirements.txt
安装服务whl包,共有3种client、app、server,Server分为CPU和GPU,GPU包根据您的环境选择一种安装 安装服务whl包,共有3种client、app、server,Server分为CPU和GPU,GPU包根据您的环境选择一种安装
- post112 = CUDA11.2 + cuDNN8 + TensorRT8(推荐) - post112 = CUDA11.2 + cuDNN8 + TensorRT8(推荐)
- post102 = CUDA10.2 + cuDNN8 + TensorRT7
- post101 = CUDA10.1 + cuDNN7 + TensorRT6 - post101 = CUDA10.1 + cuDNN7 + TensorRT6
- post102 = CUDA10.2 + cuDNN7 + TensorRT6 (与Paddle 镜像一致)
- post1028 = CUDA10.2 + cuDNN8 + TensorRT7
```shell ```shell
...@@ -75,8 +76,6 @@ pip3 install paddle-serving-server==0.9.0 -i https://pypi.tuna.tsinghua.edu.cn/s ...@@ -75,8 +76,6 @@ pip3 install paddle-serving-server==0.9.0 -i https://pypi.tuna.tsinghua.edu.cn/s
# GPU Server,需要确认环境再选择执行哪一条,推荐使用CUDA 11.2的包 # GPU Server,需要确认环境再选择执行哪一条,推荐使用CUDA 11.2的包
pip3 install paddle-serving-server-gpu==0.9.0.post112 -i https://pypi.tuna.tsinghua.edu.cn/simple pip3 install paddle-serving-server-gpu==0.9.0.post112 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install paddle-serving-server-gpu==0.9.0.post101 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install paddle-serving-server-gpu==0.9.0.post102 -i https://pypi.tuna.tsinghua.edu.cn/simple
``` ```
默认开启国内清华镜像源来加速下载,如果您使用HTTP代理可以关闭(`-i https://pypi.tuna.tsinghua.edu.cn/simple`) 默认开启国内清华镜像源来加速下载,如果您使用HTTP代理可以关闭(`-i https://pypi.tuna.tsinghua.edu.cn/simple`)
...@@ -135,4 +134,4 @@ pip3 install https://paddle-inference-lib.bj.bcebos.com/2.3.0/python/Linux/GPU/x ...@@ -135,4 +134,4 @@ pip3 install https://paddle-inference-lib.bj.bcebos.com/2.3.0/python/Linux/GPU/x
``` ```
python3 -m paddle_serving_server.serve check python3 -m paddle_serving_server.serve check
``` ```
详情请参考[环境检查文档](./Check_Env_CN.md) 详情请参考[环境检查文档](./Check_Env_CN.md)
\ No newline at end of file
# 原生系统标准环境安装
本文介绍基于原生系统标准环境进行配置安装。
<img src="images/2-2_Environment_CN_1.png">
## CentOS 7 环境配置(第一步)
**一.环境准备**
* **Python 版本 3.6/3.7/3.8/3.9 (64 bit)**
**二.选择 CPU/GPU**
* 如果您的计算机有 NVIDIA® GPU,请确保满足以下条件
* **CUDA 工具包:10.1/10.2 配合 cuDNN 7 (cuDNN 版本>=7.6.5) 或者 11.2 配合 cuDNN v8.1.1**
* **兼容版本的 TensorRT**
* **GPU运算能力超过3.5的硬件设备**
您可参考NVIDIA官方文档了解CUDA和CUDNN的安装流程和配置方法,请见[CUDA](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/),[cuDNN](https://docs.nvidia.com/deeplearning/sdk/cudnn-install/),[TensorRT](https://docs.nvidia.com/deeplearning/tensorrt/index.html), [GPU算力](https://developer.nvidia.com/cuda-gpus)
**三.安装必要工具**
需要安装的依赖库及工具详见下表:
| 组件 | 版本要求 |
| :--------------------------: | :-------------------------------: |
| bzip2-devel | 1.0.6 and later |
| make | later |
| gcc | 8.2.0 |
| gcc-c++ | 8.2.0 |
| cmake | 3.15.0 and later |
| Go | 1.17.2 and later |
| openssl-devel | 1.0.2k |
| patchelf | 0.9 |
1. 更新系统源
更新`yum`的源:
```
yum update
```
并添加必要的yum源:
```
yum install -y epel-release
```
2. 安装工具
`bzip2`以及`make`:
```
yum install -y bzip2
```
```
yum install -y make
```
cmake 需要3.15以上,建议使用3.16.0:
```
wget -q https://cmake.org/files/v3.16/cmake-3.16.0-Linux-x86_64.tar.gz
```
```
tar -zxvf cmake-3.16.0-Linux-x86_64.tar.gz
```
```
rm cmake-3.16.0-Linux-x86_64.tar.gz
```
```
PATH=/home/cmake-3.16.0-Linux-x86_64/bin:$PATH
```
gcc 需要5.4以上,建议使用8.2.0:
```
wget -q https://paddle-docker-tar.bj.bcebos.com/home/users/tianshuo/bce-python-sdk-0.8.27/gcc-8.2.0.tar.xz && \
tar -xvf gcc-8.2.0.tar.xz && \
cd gcc-8.2.0 && \
sed -i 's#ftp://gcc.gnu.org/pub/gcc/infrastructure/#https://paddle-ci.gz.bcebos.com/#g' ./contrib/download_prerequisites && \
unset LIBRARY_PATH CPATH C_INCLUDE_PATH PKG_CONFIG_PATH CPLUS_INCLUDE_PATH INCLUDE && \
./contrib/download_prerequisites && \
cd .. && mkdir temp_gcc82 && cd temp_gcc82 && \
../gcc-8.2.0/configure --prefix=/usr/local/gcc-8.2 --enable-threads=posix --disable-checking --disable-multilib && \
make -j8 && make install
```
3. 安装GOLANG
建议使用 go1.17.2:
```
wget -qO- https://go.dev/dl/go1.17.2.linux-amd64.tar.gz | \
tar -xz -C /usr/local && \
mkdir /root/go && \
mkdir /root/go/bin && \
mkdir /root/go/src && \
echo "GOROOT=/usr/local/go" >> /root/.bashrc && \
echo "GOPATH=/root/go" >> /root/.bashrc && \
echo "PATH=/usr/local/go/bin:/root/go/bin:$PATH" >> /root/.bashrc
source /root/.bashrc
```
4. 安装依赖库
安装相关依赖库 patchelf:
```
yum install patchelf
```
配置 ssl 依赖库
```
wget https://paddle-serving.bj.bcebos.com/others/centos_ssl.tar && \
tar xf centos_ssl.tar && rm -rf centos_ssl.tar && \
mv libcrypto.so.1.0.2k /usr/lib/libcrypto.so.1.0.2k && mv libssl.so.1.0.2k /usr/lib/libssl.so.1.0.2k && \
ln -sf /usr/lib/libcrypto.so.1.0.2k /usr/lib/libcrypto.so.10 && \
ln -sf /usr/lib/libssl.so.1.0.2k /usr/lib/libssl.so.10 && \
ln -sf /usr/lib/libcrypto.so.10 /usr/lib/libcrypto.so && \
ln -sf /usr/lib/libssl.so.10 /usr/lib/libssl.so
```
## Ubuntu 16.04/18.04 环境配置(第一步)
**一.环境准备**
* **Python 版本 3.6/3.7/3.8/3.9 (64 bit)**
**二.选择 CPU/GPU**
* 如果您的计算机有 NVIDIA® GPU,请确保满足以下条件
* **CUDA 工具包 10.1/10.2 配合 cuDNN 7 (cuDNN 版本>=7.6.5)**
* **CUDA 工具包 11.2 配合 cuDNN v8.1.1**
* **配套版本的 TensorRT**
* **GPU运算能力超过3.5的硬件设备**
您可参考NVIDIA官方文档了解CUDA和CUDNN的安装流程和配置方法,请见[CUDA](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/),[cuDNN](https://docs.nvidia.com/deeplearning/sdk/cudnn-install/),[TensorRT](https://docs.nvidia.com/deeplearning/tensorrt/index.html)
**三.安装必要工具**
1. 更新系统源
更新`apt`的源:
```
apt update
```
2. 安装工具
`bzip2`以及`make`:
```
apt install -y bzip2
```
```
apt install -y make
```
cmake 需要3.15以上,建议使用3.16.0:
```
wget -q https://cmake.org/files/v3.16/cmake-3.16.0-Linux-x86_64.tar.gz
```
```
tar -zxvf cmake-3.16.0-Linux-x86_64.tar.gz
```
```
rm cmake-3.16.0-Linux-x86_64.tar.gz
```
```
PATH=/home/cmake-3.16.0-Linux-x86_64/bin:$PATH
```
gcc 需要5.4以上,建议使用8.2.0:
```
wget -q https://paddle-docker-tar.bj.bcebos.com/home/users/tianshuo/bce-python-sdk-0.8.27/gcc-8.2.0.tar.xz && \
tar -xvf gcc-8.2.0.tar.xz && \
cd gcc-8.2.0 && \
sed -i 's#ftp://gcc.gnu.org/pub/gcc/infrastructure/#https://paddle-ci.gz.bcebos.com/#g' ./contrib/download_prerequisites && \
unset LIBRARY_PATH CPATH C_INCLUDE_PATH PKG_CONFIG_PATH CPLUS_INCLUDE_PATH INCLUDE && \
./contrib/download_prerequisites && \
cd .. && mkdir temp_gcc82 && cd temp_gcc82 && \
../gcc-8.2.0/configure --prefix=/usr/local/gcc-8.2 --enable-threads=posix --disable-checking --disable-multilib && \
make -j8 && make install
```
3. 安装GOLANG
建议使用 go1.17.2:
```
wget -qO- https://go.dev/dl/go1.17.2.linux-amd64.tar.gz | \
tar -xz -C /usr/local && \
mkdir /root/go && \
mkdir /root/go/bin && \
mkdir /root/go/src && \
echo "GOROOT=/usr/local/go" >> /root/.bashrc && \
echo "GOPATH=/root/go" >> /root/.bashrc && \
echo "PATH=/usr/local/go/bin:/root/go/bin:$PATH" >> /root/.bashrc
source /root/.bashrc
```
4. 安装依赖库
安装相关依赖库 patchelf:
```
apt-get install patchelf
```
配置 ssl 依赖库
```
wget https://paddle-serving.bj.bcebos.com/others/centos_ssl.tar && \
tar xf centos_ssl.tar && rm -rf centos_ssl.tar && \
mv libcrypto.so.1.0.2k /usr/lib/libcrypto.so.1.0.2k && mv libssl.so.1.0.2k /usr/lib/libssl.so.1.0.2k && \
ln -sf /usr/lib/libcrypto.so.1.0.2k /usr/lib/libcrypto.so.10 && \
ln -sf /usr/lib/libssl.so.1.0.2k /usr/lib/libssl.so.10 && \
ln -sf /usr/lib/libcrypto.so.10 /usr/lib/libcrypto.so && \
ln -sf /usr/lib/libssl.so.10 /usr/lib/libssl.so
```
## Windows 环境配置(第一步)
由于受限第三方库的支持,Windows平台目前只支持用web service的方式搭建local predictor预测服务。
**一.环境准备**
* **Python 版本 3.6/3.7/3.8/3.9 (64 bit)**
**二.选择 CPU/GPU**
* 如果您的计算机有 NVIDIA® GPU,请确保满足以下条件
* **CUDA 工具包 10.1/10.2 配合 cuDNN 7 (cuDNN 版本>=7.6.5)**
* **CUDA 工具包 11.2 配合 cuDNN v8.1.1**
* **配套版本的 TensorRT**
* **GPU运算能力超过3.5的硬件设备**
您可参考NVIDIA官方文档了解CUDA和CUDNN的安装流程和配置方法,请见[CUDA](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/),[cuDNN](https://docs.nvidia.com/deeplearning/sdk/cudnn-install/),[TensorRT](https://docs.nvidia.com/deeplearning/tensorrt/index.html)
**三.安装必要工具**
1. 更新 wget 工具
在链接[下载wget](http://gnuwin32.sourceforge.net/packages/wget.htm),解压后复制到`C:\Windows\System32`下,如有安全提示需要通过。
2. 安装git工具
详情参见[Git官网](https://git-scm.com/downloads)
3. 安装必要的C++库(可选)
部分用户可能会在`import paddle`阶段遇见dll无法链接的问题,建议[安装Visual Studio社区版本](https://visualstudio.microsoft.com/) ,并且安装C++的相关组件。
## 使用 pip 安装(第二步)
**一. 安装服务 whl 包**
服务 whl 包包括: client、app、server,其中 Server 分为 CPU 和 GPU,GPU 包根据您的环境选择一种安装
```
pip3 install paddle-serving-client==0.8.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install paddle-serving-app==0.8.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
# CPU Server
pip3 install paddle-serving-server==0.8.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
# GPU Server,需要确认环境再选择执行哪一条,推荐使用CUDA 10.2的包
# CUDA10.2 + Cudnn7 + TensorRT6(推荐)
pip3 install paddle-serving-server-gpu==0.8.3.post102 -i https://pypi.tuna.tsinghua.edu.cn/simple
# CUDA10.1 + TensorRT6
pip3 install paddle-serving-server-gpu==0.8.3.post101 -i https://pypi.tuna.tsinghua.edu.cn/simple
# CUDA11.2 + TensorRT8
pip3 install paddle-serving-server-gpu==0.8.3.post112 -i https://pypi.tuna.tsinghua.edu.cn/simple
```
默认开启国内清华镜像源来加速下载,如果您使用 HTTP 代理可以关闭(`-i https://pypi.tuna.tsinghua.edu.cn/simple`)
**二. 安装 Paddle 相关 Python 库**
**当您使用`paddle_serving_client.convert`命令或者`Python Pipeline 框架`时才需要安装。**
```
# CPU 环境请执行
pip3 install paddlepaddle==2.2.2
# GPU CUDA 10.2环境请执行
pip3 install paddlepaddle-gpu==2.2.2
```
**注意**: 如果您的 Cuda 版本不是10.2,或者您需要在 GPU 环境上使用 TensorRT,请勿直接执行上述命令,需要参考[Paddle-Inference官方文档-下载安装Linux预测库](https:/paddleinference.paddlepaddle.org.cn/master/user_guides/download_lib.html#python)选择相应的 GPU 环境的 url 链接并进行安装。
**三. 安装完成后的环境检查**
当以上步骤均完成后可使用命令行运行环境检查功能,自动运行 Paddle Serving 相关示例,进行环境相关配置校验。
```
python3 -m paddle_serving_server.serve check
# 以下输出表明环境检查正常
(Cmd) check_all
PaddlePaddle inference environment running success
C++ cpu environment running success
C++ gpu environment running success
Pipeline cpu environment running success
Pipeline gpu environment running success
```
详情请参考[环境检查文档](./Check_Env_CN.md)
\ No newline at end of file
...@@ -10,11 +10,11 @@ Check the following table, and copy the address of hyperlink then run `pip3 inst ...@@ -10,11 +10,11 @@ Check the following table, and copy the address of hyperlink then run `pip3 inst
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| |---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| cpu-avx-mkl | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [serving-cpu-avx-mkl-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-mkl-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-avx-mkl-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-mkl-0.9.0.tar.gz) | | cpu-avx-mkl | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [serving-cpu-avx-mkl-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-mkl-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-avx-mkl-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-mkl-0.9.0.tar.gz) |
| cpu-avx-openblas | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [serving-cpu-avx-openblas-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-openblas-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-avx-openblas-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-openblas-0.9.0.tar.gz) | | cpu-avx-openblas | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [serving-cpu-avx-openblas-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-openblas-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-avx-openblas-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-avx-openblas-0.9.0.tar.gz) |
| cpu-noavx-openblas | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [ serving-cpu-noavx-openblas-0.0.0.tar.gz ]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-noavx-openblas-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-noavx-openblas-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-noavx-openblas-0.9.0.tar.gz) | | cpu-noavx-openblas | [paddle_serving_server-0.0.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.0.0-py3-none-any.whl) | [serving-cpu-noavx-openblas-0.0.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-noavx-openblas-0.0.0.tar.gz) | [paddle_serving_server-0.9.0-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server-0.9.0-py3-none-any.whl) | [serving-cpu-noavx-openblas-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-cpu-noavx-openblas-0.9.0.tar.gz) |
| cuda10.1-cudnn7-TensorRT6 | [paddle_serving_server_gpu-0.0.0.post101-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post101-py3-none-any.whl) | [serving-gpu-101-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-101-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post101-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post101-py3-none-any.whl) | [serving-gpu-101-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-101-0.9.0.tar.gz) | | cuda10.1-cudnn7-TensorRT6 | [paddle_serving_server_gpu-0.0.0.post101-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post101-py3-none-any.whl) | [serving-gpu-101-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-101-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post101-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post101-py3-none-any.whl) | [serving-gpu-101-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-101-0.9.0.tar.gz) |
| cuda10.2-cudnn7-TensorRT6 | [paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl) | [serving-gpu-102-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-102-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl) | [serving-gpu-102-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-102-0.9.0.tar.gz) | | cuda10.2-cudnn7-TensorRT6 | [paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl) | [serving-gpu-102-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-102-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl) | [serving-gpu-102-0.9.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-102-0.9.0.tar.gz) |
| cuda10.2-cudnn8-TensorRT7 | [paddle_serving_server_gpu-0.0.0.post1028-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl) | [ serving-gpu-1028-0.0.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-1028-0.0.0.tar.gz ) | [paddle_serving_server_gpu-0.9.0.post1028-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl) | [serving-gpu-1028-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-1028-0.9.0.tar.gz ) | | cuda10.2-cudnn8-TensorRT7 | [paddle_serving_server_gpu-0.0.0.post1028-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post102-py3-none-any.whl) | [serving-gpu-1028-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-1028-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post1028-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post102-py3-none-any.whl) | [serving-gpu-1028-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-1028-0.9.0.tar.gz ) |
| cuda11.2-cudnn8-TensorRT8 | [paddle_serving_server_gpu-0.0.0.post112-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post112-py3-none-any.whl) | [ serving-gpu-112-0.0.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-112-0.0.0.tar.gz ) | [paddle_serving_server_gpu-0.9.0.post112-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post112-py3-none-any.whl) | [serving-gpu-112-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-112-0.9.0.tar.gz ) | | cuda11.2-cudnn8-TensorRT8 | [paddle_serving_server_gpu-0.0.0.post112-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.0.0.post112-py3-none-any.whl) | [serving-gpu-112-0.0.0.tar.gz](https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-112-0.0.0.tar.gz) | [paddle_serving_server_gpu-0.9.0.post112-py3-none-any.whl ](https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.9.0.post112-py3-none-any.whl) | [serving-gpu-112-0.9.0.tar.gz]( https://paddle-serving.bj.bcebos.com/test-dev/bin/serving-gpu-112-0.9.0.tar.gz ) |
### Binary Package ### Binary Package
for most users, we do not need to read this section. But if you deploy your Paddle Serving on a machine without network, you will encounter a problem that the binary executable tar file cannot be downloaded. Therefore, here we give you all the download links for various environment. for most users, we do not need to read this section. But if you deploy your Paddle Serving on a machine without network, you will encounter a problem that the binary executable tar file cannot be downloaded. Therefore, here we give you all the download links for various environment.
......
此差异已折叠。
# Pipeline Serving 性能优化
([English](./Performance_Tuning_EN.md)|简体中文)
## 1. 性能分析与优化
### 1.1 如何通过 Timeline 工具进行优化
为了更好地对性能进行优化,PipelineServing 提供了 Timeline 工具,对整个服务的各个阶段时间进行打点。
### 1.2 在 Server 端输出 Profile 信息
Server 端用 yaml 中的 `use_profile` 字段进行控制:
```yaml
dag:
use_profile: true
```
开启该功能后,Server 端在预测的过程中会将对应的日志信息打印到标准输出,为了更直观地展现各阶段的耗时,提供 Analyst 模块对日志文件做进一步的分析处理。
使用时先将 Server 的输出保存到文件,以 `profile.txt` 为例,脚本将日志中的时间打点信息转换成 json 格式保存到 `trace` 文件,`trace` 文件可以通过 chrome 浏览器的 tracing 功能进行可视化。
```python
from paddle_serving_server.pipeline import Analyst
import json
import sys
if __name__ == "__main__":
log_filename = "profile.txt"
trace_filename = "trace"
analyst = Analyst(log_filename)
analyst.save_trace(trace_filename)
```
具体操作:打开 chrome 浏览器,在地址栏输入 `chrome://tracing/` ,跳转至 tracing 页面,点击 load 按钮,打开保存的 `trace` 文件,即可将预测服务的各阶段时间信息可视化。
### 1.3 在 Client 端输出 Profile 信息
Client 端在 `predict` 接口设置 `profile=True`,即可开启 Profile 功能。
开启该功能后,Client 端在预测的过程中会将该次预测对应的日志信息打印到标准输出,后续分析处理同 Server。
### 1.4 分析方法
根据pipeline.tracer日志中的各个阶段耗时,按以下公式逐步分析出主要耗时在哪个阶段。
```
单OP耗时:
op_cost = process(pre + mid + post)
OP期望并发数:
op_concurrency = 单OP耗时(s) * 期望QPS
服务吞吐量:
service_throughput = 1 / 最慢OP的耗时 * 并发数
服务平响:
service_avg_cost = ∑op_concurrency 【关键路径】
Channel堆积:
channel_acc_size = QPS(down - up) * time
批量预测平均耗时:
avg_batch_cost = (N * pre + mid + post) / N
```
### 1.5 优化思路
根据长耗时在不同阶段,采用不同的优化方法.
- OP推理阶段(mid-process):
- 增加OP并发度
- 开启auto-batching(前提是多个请求的shape一致)
- 若批量数据中某条数据的shape很大,padding很大导致推理很慢,可使用mini-batch
- 开启TensorRT/MKL-DNN优化
- 开启低精度推理
- OP前处理阶段(pre-process):
- 增加OP并发度
- 优化前处理逻辑
- in/out耗时长(channel堆积>5)
- 检查channel传递的数据大小和延迟
- 优化传入数据,不传递数据或压缩后再传入
- 增加OP并发度
- 减少上游OP并发度
# Pipeline Serving Performance Optimization
(English|[简体中文](./Performance_Tuning_CN.md))
## 1. Performance analysis and optimization
### 1.1 How to optimize with the timeline tool
In order to better optimize the performance, PipelineServing provides a timeline tool to monitor the time of each stage of the whole service.
### 1.2 Output profile information on server side
The server is controlled by the `use_profile` field in yaml:
```yaml
dag:
use_profile: true
```
After the function is enabled, the server will print the corresponding log information to the standard output in the process of prediction. In order to show the time consumption of each stage more intuitively, Analyst module is provided for further analysis and processing of log files.
The output of the server is first saved to a file. Taking `profile.txt` as an example, the script converts the time monitoring information in the log into JSON format and saves it to the `trace` file. The `trace` file can be visualized through the tracing function of Chrome browser.
```shell
from paddle_serving_server.pipeline import Analyst
import json
import sys
if __name__ == "__main__":
log_filename = "profile.txt"
trace_filename = "trace"
analyst = Analyst(log_filename)
analyst.save_trace(trace_filename)
```
Specific operation: open Chrome browser, input in the address bar `chrome://tracing/` , jump to the tracing page, click the load button, open the saved `trace` file, and then visualize the time information of each stage of the prediction service.
### 1.3 Output profile information on client side
The profile function can be enabled by setting `profile=True` in the `predict` interface on the client side.
After the function is enabled, the client will print the log information corresponding to the prediction to the standard output during the prediction process, and the subsequent analysis and processing are the same as that of the server.
### 1.4 Analytical methods
According to the time consumption of each stage in the pipeline.tracer log, the following formula is used to gradually analyze which stage is the main time consumption.
```
cost of one single OP:
op_cost = process(pre + mid + post)
OP Concurrency:
op_concurrency = op_cost(s) * qps_expected
Service throughput:
service_throughput = 1 / slowest_op_cost * op_concurrency
Service average cost:
service_avg_cost = ∑op_concurrency in critical Path
Channel accumulations:
channel_acc_size = QPS(down - up) * time
Average cost of batch predictor:
avg_batch_cost = (N * pre + mid + post) / N
```
### 1.5 Optimization ideas
According to the long time consuming in stages below, different optimization methods are adopted.
- OP Inference stage(mid-process):
- Increase `concurrency`
- Turn on `auto-batching`(Ensure that the shapes of multiple requests are consistent)
- Use `mini-batch`, If the shape of data is very large.
- Turn on TensorRT for GPU
- Turn on MKLDNN for CPU
- Turn on low precison inference
- OP preprocess or postprocess stage:
- Increase `concurrency`
- Optimize processing logic
- In/Out stage(channel accumulation > 5):
- Check the size and delay of the data passed by the channel
- Optimize the channel to transmit data, do not transmit data or compress it before passing it in
- Increase `concurrency`
- Decrease `concurrency` upstreams.
本次提测的Serving版本,支持GPU预测,希望以此任务为例,对Paddle Serving支持GPU预测的性能给出测试数据。 # Python Pipeline 性能测试
## 1. 测试环境说明 - [测试环境](#1)
- [性能指标与结论](#2)
<a name="1"></a>
## 测试环境
测试环境如下表所示:
| | GPU | 显存 | CPU | 内存 | | | GPU | 显存 | CPU | 内存 |
|----------|---------|----------|----------------------------------------------|------| |----------|---------|----------|----------------------------------------------|------|
| Serving端 | 4x Tesla P4-8GB | 7611MiB | Intel(R) Xeon(R) Gold 5117 CPU @ 2.00GHz 48核 | 216G | | Serving端 | 4x Tesla P4-8GB | 7611MiB | Intel(R) Xeon(R) Gold 5117 CPU @ 2.00GHz 48核 | 216G |
...@@ -10,7 +16,15 @@ ...@@ -10,7 +16,15 @@
使用单卡GPU,未开启TensorRT。 使用单卡GPU,未开启TensorRT。
模型:ResNet_v2_50 模型:ResNet_v2_50
## 2. PaddleServing-PipeLine(python) <a name="2"></a>
## 性能指标与结论
通过测试,使用 Python Pipeline 模式通过多进程并发,充分利用 GPU 显卡,具有较好的吞吐性能。
测试数据如下:
|model_name |thread_num |batch_size |CPU_util(%) |GPU_memory(mb) |GPU_util(%) |qps(samples/s) |total count |mean(ms) |median(ms) |80 percent(ms) |90 percent(ms) |99 percent(ms) |total cost(s) |each cost(s)| |model_name |thread_num |batch_size |CPU_util(%) |GPU_memory(mb) |GPU_util(%) |qps(samples/s) |total count |mean(ms) |median(ms) |80 percent(ms) |90 percent(ms) |99 percent(ms) |total cost(s) |each cost(s)|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:-- |:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--
|ResNet_v2_50 |1 |1 |2.2 |3327 |17.25 |17.633658869240787 |355 |56.428481238996476 |38.646728515625 |39.496826171875 |39.98369140625 |1273.1911083984373 |20.131953477859497 |20.033540725708008| |ResNet_v2_50 |1 |1 |2.2 |3327 |17.25 |17.633658869240787 |355 |56.428481238996476 |38.646728515625 |39.496826171875 |39.98369140625 |1273.1911083984373 |20.131953477859497 |20.033540725708008|
......
此差异已折叠。
此差异已折叠。
# Python Pipeline 框架
在许多深度学习框架中,模型服务化部署通常用于单模型的一键部署。但在 AI 工业大生产的背景下,端到端的单一深度学习模型不能解决复杂问题,多个深度学习模型组合使用是解决现实复杂问题的常规手段,如文字识别 OCR 服务至少需要检测和识别2种模型;视频理解服务一般需要视频抽帧、切词、音频处理、分类等多种模型组合实现。当前,通用多模型组合服务的设计和实现是非常复杂的,既要能实现复杂的模型拓扑关系,又要保证服务的高并发、高可用和易于开发和维护等。
Paddle Serving 实现了一套通用的多模型组合服务编程框架 Python Pipeline,不仅解决上述痛点,同时还能大幅提高 GPU 利用率,并易于开发和维护。
Python Pipeline 使用案例请阅读[Python Pipeline 快速部署案例](../Quick_Start_CN.md)
通过阅读以下内容掌握 Python Pipeline 核心功能和使用方法、高阶功能用法和性能优化指南等。
- [Python Pipeline 框架设计](Pipeline_Design_CN.md)
- [Python Pipeline 核心功能](Pipeline_Senior_CN.md)
- [Python Pipeline 优化指南](Pipeline_Optimize_CN.md)
- [Python Pipeline 性能指标](Pipeline_Benchmark_CN.md)
# Python Pipeline 优化指南
- [优化响应时长](#1)
- [1.1 分析响应时长](#1.1)
- [Pipeline Trace Tool](#1.1.1)
- [Pipeline Profile Tool](#1.1.2)
- [1.2 优化思路](#1.2)
- [优化服务吞吐](#2)
- [2.1 分析吞吐瓶颈](#2.1)
- [2.2 优化思路](#2.2)
- [增加 Op 并发](#2.2.1)
- [动态批量](#2.2.2)
- [CPU 与 GPU 处理分离](#2.2.3)
通常,服务的性能优化是基于耗时分析,首先要掌握服务运行的各阶段耗时信息,从中找到耗时最长的性能瓶颈再做针对性优化。对于模型推理服务化不仅要关注耗时,由于 GPU 芯片昂贵,更要关注服务吞吐,从而提升 GPU 利用率实现降本增效。因此,模型推理服务化可总结为:
- 优化响应时长
- 优化服务吞吐
经过分析和调优后,各个阶段实现整体服务的性能最优。
<a name="1"></a>
## 优化响应时长
首先,优化响应时长的主要思路首先要掌握各阶段耗时,并分析出性能瓶颈或者耗时占比较高的阶段,再针对性能瓶颈做专项优化。
Paddle Serving 提供2种耗时分析工具,`Pipeline Trace Tool``Pipeline Profile Tool`。2个工具的特点如下:
- Pipeline Trace Tool : 统计服务端所有进程各个阶段的平均耗时,包括每个 `Op``Channel`,用于定量分析。
- Pipeline Profile Tool : 是可视化 Trace View 工具,生成多进程并发效果图,用定性和定量分析执行和并发效果。
<a name="1.1"></a>
**一.耗时分析**
<a name="1.1.1"></a>
1.Pipeline Trace Tool
`Pipeline Trace Tool` 统计每个 `Op``Channel` 中各阶段的处理耗时,
开启方法在配置文件 `config.yml``dag` 区段内添加 `tracer` 字段,框架会每隔 `interval_s` 时间生成 Trace 信息。
```
dag:
#op资源类型, True, 为线程模型;False,为进程模型
is_thread_op: True
#tracer, 跟踪框架吞吐,每个OP和channel的工作情况。无tracer时不生成数据
tracer:
#每次trace的时间间隔,单位秒/s
interval_s: 10
```
生成的 Trace 信息保存在 `./PipelineServingLogs/pipeline.tracer` 日志中。如下图所示
```
==================== TRACER ======================
Op(uci):
in[8473.507333333333 ms]: # 等待前置 Channel 中数据放入 Op 的耗时,如长时间无请求,此值会变大
prep[0.6753333333333333 ms] # 推理前处理 preprocess 阶段耗时
midp[26.476333333333333 ms] # 推理 process 阶段耗时
postp[1.8616666666666666 ms] # 推理后处理 postprocess 阶段耗时
out[1.3236666666666668 ms] # 后处理结果放入后置 channel 耗时
idle[0.9965882097324374] # 框架自循环耗时,间隔 1 ms,如此值很大说明系统负载高,调度变慢
DAGExecutor:
Query count[30] # interval_s 间隔时间内请求数量
QPS[27.35 q/s] # interval_s 间隔时间内服务 QPS
Succ[1.0] # interval_s 间隔时间内请求成功率
Error req[] # 异常请求信息
Latency:
ave[36.55233333333334 ms] # 平均延时
.50[8.702 ms] # 50分位延时
.60[8.702 ms] # 60分位延时
.70[92.346 ms] # 70分位延时
.80[92.346 ms] # 70分位延时
.90[92.346 ms] # 90分位延时
.95[92.346 ms] # 95分位延时
.99[92.346 ms] # 99分位延时
Channel (server worker num[1]):
chl0(In: ['@DAGExecutor'], Out: ['uci']) size[0/0] # 框架 RequestOp 与 uci Op 之间 Channel 中堆积请求数。此值较大,说明下游 uci Op 消费能力不足。
chl1(In: ['uci'], Out: ['@DAGExecutor']) size[0/0] # uci Op 与 框架 ResponseOp 之间 Channel 中堆积的请求数。此值较大,说明下游 ReponseOp 消费能力不足。
==================== TRACER ======================
```
<a name="1.1.2"></a>
2.Pipeline Profile Tool
```
dag:
#op资源类型, True, 为线程模型;False,为进程模型
is_thread_op: True
#使用性能分析, 默认为 False,imeline性能数据,对性能有一定影响
use_profile: True,
```
开启后,Server 端在预测的过程中会将对应的日志信息打印到`标准输出`,为了更直观地展现各阶段的耗时,因此服务启动要使用如下命令:
```
python3.7 web_service.py > profile.txt 2>&1
```
服务接收请求后,输出 Profile 信息到 `profile.txt` 文件中。再粘贴如下代码到 `trace.py`, 使用框架提供 Analyst 模块对日志文件做进一步的分析处理。
```
from paddle_serving_server.pipeline import Analyst
import json
import sys
if __name__ == "__main__":
log_filename = "profile.txt"
trace_filename = "trace"
analyst = Analyst(log_filename)
analyst.save_trace(trace_filename)
```
运行命令,脚本将日志中的时间打点信息转换成 json 格式保存到 `trace` 文件。
```
python3.7 trace.py
```
`trace` 文件可以通过 `chrome` 浏览器的 `tracing` 功能进行可视化。
```
打开 chrome 浏览器,在地址栏输入 chrome://tracing/ ,跳转至 tracing 页面,点击 load 按钮,打开保存的 trace 文件,即可将预测服务的各阶段时间信息可视化。
```
通过图示中并发请求的处理流程可观测到推理阶段的流水线状态,以及多个请求在推理阶段的`间隔`信息,进行优化。
<a name="1.2"></a>
**二.降低响应时长优化思路**
根据 `Pipeline Trace Tool` 输出结果在不同阶段耗时长的问题,常见场景的优化方法如下:
- Op 推理阶段(midp) 耗时长:
- 增加 Op 并发度
- 开启 auto-batching (前提是多个请求的 shape 一致)
- 若批量数据中某条数据的 shape 很大,padding 很大导致推理很慢,可参考 OCR 示例中 mini-batch 方法。
- 开启 TensorRT/MKL-DNN 优化
- 开启低精度推理
- Op 前处理阶段(prep) 或 后处理阶段耗时长:
- 增加 OP 并发度
- 优化前后处理逻辑
- in/out 耗时长(channel 堆积>5)
- 检查 channel 传递的数据大小,可能为传输的数据大导致延迟大。
- 优化传入数据,不传递数据或压缩后再传入
- 增加 Op 并发度
- 减少上游 Op 并发度
根据 `Pipeline Profile Tool` 输出结果优化流水行并发的效果
- 增加 Op 并发度,或调整不同 Op 的并发度
- 开启 auto-batching
此外,还有一些优化思路,如将 CPU 处理较慢的过程转换到 GPU 上处理等,客户端与服务端传输较大数据时,可使用共享内存方式传递内存或显存地址等。
<a name="2"></a>
## 优化服务吞吐
<a name="2.1"></a>
**一.分析吞吐瓶颈**
服务的吞吐量受到多种多因素条件制约,如 Op 处理时长、传输数据耗时、并发数和 DAG 图结构等,可以将这些因素进一步拆解,当传输数据不是极端庞大的时候,最重要因素是流水线中`最慢 Op 的处理时长和并发数`
```
Op 处理时长:
op_cost = process(pre + mid + post)
服务吞吐量:
service_throughput = 1 / 最慢 op_cost * 并发数
服务平响:
service_avg_cost = ∑op_concurrency 【关键路径】
批量预测平均耗时:
avg_batch_cost = (N * pre + mid + post) / N
```
<a name="2.2"></a>
**二.优化思路**
优化吞吐的主要方法是 `增大 Op 并发数``自动批量``CPU 与 GPU 处理分离`
<a name="2.2.1"></a>
1.增加 Op 并发**
调整 Op 的并发数量通过设置 `is_thread_op: False` 进程类型 Op 和 `uci` Op 的 `concurrency` 字段
```
dag:
#op资源类型, True, 为线程模型;False,为进程模型
is_thread_op: False
op:
uci:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 10
```
Op 的进程数量不是越大越好,受到机器 CPU 核数、内存和显存大小的限制,推荐设置 Op 的并发数不超过系统 CPU 核数。
<a name="2.2.2"></a>
2.动态批量
动态批量是增加吞吐的有一种方法,开启方式可参考[Python Pipeline 核心功能](./7-2_Python_Pipeline_Senior_CN.md#批量推理)
<a name="2.2.3"></a>
3.CPU 与 GPU 处理分离
`CV` 模型中,对图片或视频的前后处理成为主要瓶颈时,可考虑此方案,即将前后处理过程独立成一个 Op 并独立设置并发度。
将 CPU 前后处理和 GPU 推理过程比例调整到服务最佳配比。以 OCR 为例,原有流水线设计为 `RequestOp -> DetOp -> RecOp -> ResponseOp`
根据耗时分析,`DetOp``RecOp` 的前处理耗时很长,因此,将2个模型前处理分离成独立 Op,最新的流水线设计为:
`RequestOp -> PreDetOp -> DetOp -> PreRecOp -> RecOp -> ResponseOp`,并调大 `PreDetOp``PreRecOp`的并发度,从而获得 20% 的性能提升。
由于增加了2次数据传递,单条请求的处理延时会增加。
此差异已折叠。
# 怎样保存用于Paddle Serving的模型? # 保存用于 Serving 部署的模型参数
(简体中文|[English](./Save_EN.md)) - [背景介绍](#1)
- [功能设计](#2)
- [功能使用](#3)
- [PYTHON 命令执行](#3.1)
- [代码引入执行](#3.2)
- [Serving 部署](#4)
- [服务端部署示例](#4.1)
- [客户端部署示例](#4.2)
## 保存用于 Serving 部署模型的意义 <a name="1"></a>
## 背景介绍
模型参数信息保存在模型文件中,为什么还要保存用于 Paddle Serving 部署的模型参数呢,原因有3个:
1. 服务化场景分为客户端和服务端,服务端加载模型,而在客户端没有模型信息,但需要在客户端需实现数据拼装和类型转换。
2. 模型升级过程中 `feed vars``fetch vars` 的名称变化会导致代码升级,通过增加一个 `alias_name` 字段映射名称,代码无需升级。
3. 部署 `Web` 服务,并使用 `URL` 方式访问时,请求信息中缺少类型和维度信息,在服务端推理前需要进行转换。
<a name="2"></a>
## 从已保存的模型文件中导出 ## 功能设计
如果已使用Paddle 的`save_inference_model`接口保存出预测要使用的模型,你可以使用Paddle Serving提供的名为`paddle_serving_client.convert`的内置模块进行转换。
```python 飞桨训推一体框架中,从动态图模型训练到静态图推理部署,一体化流程如下所示
python -m paddle_serving_client.convert --dirname ./your_inference_model_dir ```
①动态图训练 → ②模型动转静 → ③静态模型 → ④模型保存 → ⑤Serving 部署
``` ```
在飞桨框架2.1对模型与参数的保存与载入相关接口进行了梳理,完整文档参考[模型保存与载入](https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/02_paddle2.0_develop/08_model_save_load_cn.html)
- 对于训练调优场景,我们推荐使用 `paddle.save/load` 保存和载入模型;
- 对于推理部署场景,我们推荐使用 `paddle.jit.save/load`(动态图)和 `paddle.static.save/load_inference_model` (静态图)保存载入模型;
也可以通过Paddle Serving的`inference_model_to_serving`接口转换成可用于Paddle Serving的模型文件。 Paddle Serving 模型参数保存接口定位是在 `②模型动转静` 导出 `③静态模型`后,使用 `paddle.static.load_inference_model` 接口加载模型,和 `paddle.static.save_vars` 接口保存模型参数。
```python
import paddle_serving_client.io as serving_io 生成的模型参数信息保存在 `paddle_serving_server/client.prototxt` 文件中,其格式如下
serving_io.inference_model_to_serving(dirname, serving_server="serving_server", serving_client="serving_client", model_filename=None, params_filename=None) ```
feed_var {
name: "x"
alias_name: "image"
is_lod_tensor: false
feed_type: 1
shape: 3
shape: 960
shape: 960
}
fetch_var {
name: "save_infer_model/scale_0.tmp_1"
alias_name: "save_infer_model/scale_0.tmp_1"
is_lod_tensor: false
fetch_type: 1
shape: 1
shape: 960
shape: 960
}
``` ```
模块参数与`inference_model_to_serving`接口参数相同。 | 参数 | 描述 |
|------|---------|
| name | 实际变量名 |
| alias_name | 变量别名,与 name 的关联业务场景中变量名 |
| is_lod_tensor | 是否为 LOD Tensor |
| feed_type | feed 变量类型|
| fetch_type | fetch 变量类型|
| shape 数组 | 变量的 Shape 信息 |
feed 与 fetch 变量的类型列表如下:
| 类型 | 类型值 |
|------|------|
| int64 | 0 |
| float32 |1 |
| int32 | 2 |
| float64 | 3 |
| int16 | 4 |
| float16 | 5 |
| bfloat16 | 6 |
| uint8 | 7 |
| int8 | 8 |
| bool | 9 |
| complex64 | 10
| complex128 | 11 |
<a name="3"></a>
## 功能使用
Paddle 推理模型有3种形式,每种形式的读模型的方式都不同,散列方式必须以路径方式加载,其余2种采用目录或文件方式均可。
1) Paddle 2.0前版本:`__model__`, `__params__`
2) Paddle 2.0后版本:`*.pdmodel`, `*.pdiparams`
3) 散列:`__model__`, `conv2d_1.w_0`, `conv2d_2.w_0`, `fc_1.w_0`, `conv2d_1.b_0`, ...
`paddle_serving_client.convert` 接口既支持 PYTHON 命令方式执行,又支持 代码中引入运行。
| 参数 | 类型 | 默认值 | 描述 | | 参数 | 类型 | 默认值 | 描述 |
|--------------|------|-----------|--------------------------------| |--------------|------|-----------|--------------------------------|
| `dirname` | str | - | 需要转换的模型文件存储路径,Program结构文件和参数文件均保存在此目录。| | `dirname` | str | - | 需要转换的模型文件存储路径,Program结构文件和参数文件均保存在此目录。|
...@@ -29,24 +100,73 @@ serving_io.inference_model_to_serving(dirname, serving_server="serving_server", ...@@ -29,24 +100,73 @@ serving_io.inference_model_to_serving(dirname, serving_server="serving_server",
| `model_filename` | str | None | 存储需要转换的模型Inference Program结构的文件名称。如果设置为None,则使用 `__model__` 作为默认的文件名 | | `model_filename` | str | None | 存储需要转换的模型Inference Program结构的文件名称。如果设置为None,则使用 `__model__` 作为默认的文件名 |
| `params_filename` | str | None | 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保>存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为None | | `params_filename` | str | None | 存储需要转换的模型所有参数的文件名称。当且仅当所有模型参数被保>存在一个单独的二进制文件中,它才需要被指定。如果模型参数是存储在各自分离的文件中,设置它的值为None |
### 从动态图模型中导出 <a name="3.1"></a>
**一.PYTHON 命令执行**
首先需要安装 `paddle_serivng_client` 包,以目录方式加载模型。
示例一,是以模型路径方式加载模型,适用于全部3种类型。
```python
python3 -m paddle_serving_client.convert --dirname ./your_inference_model_dir
```
示例二,以指定加载 `当前路径` 下模型 `dygraph_model.pdmodel``dygraph_model.pdiparams`,保存结果在 `serving_server``serving_client` 目录。
```python
python3 -m paddle_serving_client.convert --dirname . --model_filename dygraph_model.pdmodel --params_filename dygraph_model.pdiparams --serving_server serving_server --serving_client serving_client
```
<a name="3.2"></a>
**二.代码引入执行**
代码引入执行方式,通过 `import io` 包并调用 `inference_model_to_serving` 实现模型参数保存。
```python
import paddle_serving_client.io as serving_io
serving_io.inference_model_to_serving(dirname, serving_server="serving_server", serving_client="serving_client", model_filename=None, params_filename=None)
```
<a name="4"></a>
PaddlePaddle 2.0提供了全新的动态图模式,因此我们这里以imagenet ResNet50动态图为示例教学如何从已保存模型导出,并用于真实的在线预测场景。 ## Serving 部署
生成完的模型可直接用于服务化推理,服务端使用和客户端使用。
<a name="4.1"></a>
**一.服务端部署示例**
示例一:C++ Serving 启动服务
``` ```
wget https://paddle-serving.bj.bcebos.com/others/dygraph_res50.tar #模型 python3 -m paddle_serving_server.serve --model serving_server --port 9393 --gpu_id 0
wget https://paddle-serving.bj.bcebos.com/imagenet-example/daisy.jpg #示例输入(向日葵)
tar xf dygraph_res50.tar
python -m paddle_serving_client.convert --dirname . --model_filename dygraph_model.pdmodel --params_filename dygraph_model.pdiparams --serving_server serving_server --serving_client serving_client
``` ```
我们可以看到`serving_server``serving_client`文件夹分别保存着模型的服务端和客户端配置
启动服务端(GPU) 示例二:Python Pipeline 启动服务,在 `config.yml` 中指定模型路径
``` ```
python -m paddle_serving_server.serve --model serving_server --port 9393 --gpu_id 0 op:
det:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 6
#当op配置没有server_endpoints时,从local_service_conf读取本地服务配置
local_service_conf:
#client类型,包括brpc, grpc和local_predictor.local_predictor不启动Serving服务,进程内预测
client_type: local_predictor
#det模型路径
model_config: ocr_det_model
#Fetch结果列表,以client_config中fetch_var的alias_name为准
fetch_list: ["save_infer_model/scale_0.tmp_1"]
# device_type, 0=cpu, 1=gpu, 2=tensorRT, 3=arm cpu, 4=kunlun xpu
device_type: 0
``` ```
客户端写法,保存为`test_client.py` <a name="4.2"></a>
**二.客户端部署示例**
通过 `client` 对象的 `load_client_config` 接口加载模型配置信息
``` ```
from paddle_serving_client import Client from paddle_serving_client import Client
from paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop from paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop
...@@ -65,41 +185,4 @@ seq = Sequential([ ...@@ -65,41 +185,4 @@ seq = Sequential([
image_file = "daisy.jpg" image_file = "daisy.jpg"
img = seq(image_file) img = seq(image_file)
fetch_map = client.predict(feed={"inputs": img}, fetch=["save_infer_model/scale_0.tmp_0"]) fetch_map = client.predict(feed={"inputs": img}, fetch=["save_infer_model/scale_0.tmp_0"])
print(fetch_map["save_infer_model/scale_0.tmp_0"].reshape(-1))
```
执行
``` ```
python test_client.py
```
即可看到成功的执行了预测,以上就是动态图ResNet50模型在Serving上预测的内容,其他动态图模型使用方式与之类似。
## 从训练或预测脚本中保存(静态图)
目前,Paddle Serving提供了一个save_model接口供用户访问,该接口与Paddle的`save_inference_model`类似。
``` python
import paddle_serving_client.io as serving_io
serving_io.save_model("imdb_model", "imdb_client_conf",
{"words": data}, {"prediction": prediction},
paddle.static.default_main_program())
```
imdb_model是具有服务配置的服务器端模型。 imdb_client_conf是客户端rpc配置。
Serving有一个提供给用户存放Feed和Fetch变量信息的字典。 在示例中,`{"words":data}` 是用于指定已保存推理模型输入的提要字典。`{"prediction":projection}`是指定保存的推理模型输出的字典。可以为feed和fetch变量定义一个别名。 如何使用别名的例子 示例如下:
``` python
from paddle_serving_client import Client
import sys
client = Client()
client.load_client_config(sys.argv[1])
client.connect(["127.0.0.1:9393"])
for line in sys.stdin:
group = line.strip().split()
words = [int(x) for x in group[1:int(group[0]) + 1]]
label = [int(group[-1])]
feed = {"words": words, "label": label}
fetch = ["acc", "cost", "prediction"]
fetch_map = client.predict(feed=feed, fetch=fetch)
print("{} {}".format(fetch_map["prediction"][1], label[0]))
```
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册