# Linux GPU/CPU 服务化部署功能开发文档
- [1 简介](#1---)
- [2 服务化部署开发](#2---)
* [2.1 准备测试数据和部署环境](#21---)
* [2.2 准备服务化部署模型](#22---)
* [2.3 复制部署样例程序](#23---)
* [2.4 初始化部署引擎](#24---)
* [2.5 开发数据预处理程序](#25---)
* [2.6 开发预测结果后处理程序](#26---)
* [2.7 启动模型预测服务](#27---)
* [2.8 开发客户端访问服务的程序](#28---)
* [2.9 验证服务化部署功能的正确性](#29---)
- [3 FAQ](#3---)
## 1 简介
Paddle Serving依托深度学习框架PaddlePaddle旨在帮助深度学习开发者和企业提供高性能、灵活易用的工业级在线推理服务。Paddle Serving支持RESTful、gRPC、bRPC等多种协议,提供多种异构硬件和多种操作系统环境下推理解决方案,和多种经典预训练模型示例。
本文档主要介绍飞桨模型在 Linux GPU/CPU 下服务化部署能力的开发。主要包含三个步骤:
(1)参考 [《Linux GPU/CPU 基础训练推理开发文档》](../train_infer_python/README.md) 完成模型的训练和基于Paddle Inference的模型推理开发。
(2)在Paddle Inference的模型推理基础上,完成服务化部署能力的开发(**本文档**)。
(3)参考[《Linux GPU/CPU 基础训练推理测试开发文档》](./test_serving.md),完成TIPC 服务化部署测试开发。
## 2 服务化部署能力开发
一个模型的服务化部署开发流程如下图所示,一般包含9个步骤。
![pipline](./images/serving_deploy_pipeline.jpg)
其中核验点如图黄色区域所示,具体如下:
- 第7步:启动模型预测服务
- 第9步:验证服务化部署能力的正确性
### 2.1 准备测试数据和部署环境
【基本流程】
**(1)准备测试数据:** 从验证集或者测试集中抽出至少一张图像,用于后续推理过程验证。
**(2)准备部署环境**
首先准备docker环境,AIStudio环境已经安装了合适的docker。如果是非AIStudio环境,请[参考文档](https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.3/doc/doc_ch/environment.md#2)中的 "1.3.2 Docker环境配置" 安装docker环境。
然后安装Paddle Serving三个安装包,paddle-serving-server,paddle-serving-client 和 paddle-serving-app。
```
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_server_gpu-0.7.0.post102-py3-none-any.whl
pip3 install paddle_serving_server_gpu-0.7.0.post102-py3-none-any.whl
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_client-0.7.0-cp37-none-any.whl
pip3 install paddle_serving_client-0.7.0-cp37-none-any.whl
wget https://paddle-serving.bj.bcebos.com/test-dev/whl/paddle_serving_app-0.7.0-py3-none-any.whl
pip3 install paddle_serving_app-0.7.0-py3-none-any.whl
```
Paddle Serving Server更多不同运行环境的whl包下载地址,请参考:[下载页面](https://github.com/PaddlePaddle/Serving/blob/v0.7.0/doc/Latest_Packages_CN.md)
### 2.2 准备服务化部署模型
【基本流程】
为了便于模型服务化部署,需要将jit.save保存的静态图模型(具体参考[《Linux GPU/CPU 基础训练推理开发文档》](../train_infer_python/README.md) 使用paddle_serving_client.convert按如下命令转换为服务化部署模型:
```
python3 -m paddle_serving_client.convert --dirname {静态图模型路径} --model_filename {模型结构文件} --params_filename {模型参数文件} --serving_server {转换后的服务器端模型和配置文件存储路径} --serving_client {转换后的客户端模型和配置文件存储路径}
```
上面命令中 "转换后的服务器端模型和配置文件" 将用于后续服务化部署。
【实战】
针对AlexNet网络,将静态图模型([下载地址](https://paddle-model-ecology.bj.bcebos.com/model/alexnet_reprod/alexnet_infer.tar) )转换为服务化部署模型的示例命令如下,转换完后在本地生成serving_server和serving_client两个文件夹。本教程后续主要使用serving_server文件夹中的模型。
```python
python3 -m paddle_serving_client.convert \
--dirname ./alexnet_infer/ \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--serving_server serving_server \
--serving_client serving_client
```
### 2.3 复制部署样例程序
**【基本流程】**
服务化部署的样例程序的目录地址为:"template/code/"
该目录下面包含3个文件,具体如下:
- web_service.py:用于开发服务端模型预测相关程序。由于使用多卡或者多机部署预测服务,设计高效的服务调度策略比较复杂,Paddle Serving将网络预测进行了封装,在这个程序里面开发者只需要关心部署服务引擎的初始化,模型预测的前处理和后处理开发,不用关心模型预测调度问题。
- config.yml:服务端模型预测相关配置文件,里面有各参数的详细说明。开发者只需要关注如下配置:http_port(服务的http端口),model_config(服务化部署模型的路径),device_type(计算硬件类型),devices(计算硬件ID)。
- pipeline_http_client.py:用于客户端访问服务的程序,开发者需要设置url(服务地址)、logid(日志ID)和测试图像。
**【实战】**
如果服务化部署AlexNet网络,需要拷贝上述三个文件到运行目录,建议在`$repo_name/deploy/serving`目录下。
### 2.4 初始化部署引擎
**【基本流程】**
针对模型名称,修改web_service.py中类TIPCExampleService、TIPCExampleOp的名称,以及这些类初始化中任务名称name,同时通过uci_service.prepare_pipeline_config设置配置参数。
同时修改服务配置文件中的配置:OP名称,http_port(服务的http端口),model_config(服务化部署模型的路径),device_type(计算硬件类型),devices(计算硬件ID)
**【实战】**
针对AlexNet网络,(1)修改web_service.py文件后的代码如下:
```
from paddle_serving_server.web_service import WebService, Op
class AlexNetOp(Op):
def init_op(self):
pass
def preprocess(self, input_dicts, data_id, log_id):
pass
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
pass
class AlexNetService(WebService):
def get_pipeline_response(self, read_op):
alexnet_op = AlexNetOp(name="alexnet", input_ops=[read_op])
return alexnet_op
uci_service = AlexNetService(name="alexnet")
uci_service.prepare_pipeline_config("config.yml")
uci_service.run_service()
```
(2)修改服务配置文件config.yml。
- http_port:使用默认的端口号18080
- OP名称:第14行更新为alexnet
- model_config:与2.2转换后服务化部署模型文件夹路径一致,这里使用默认配置 "./serving_server"
- device_type:使用默认配置1,基于GPU预测
- devices:使用默认配置"0",0号卡预测
### 2.5 开发数据预处理程序
**【基本流程】**
web_service.py文件中的TIPCExampleOp类的preprocess函数用于开发数据预处理程序,包含输入、处理流程和输出三部分。
**(1)输入:** 一般开发者使用时,只需要关心input_dicts和log_id两个输入参数。这两个参数与客户端访问服务程序tipc_pipeline_http_client.py中的请求参数相对应,即:
```
data = {"key": ["image"], "value": [image], "logid":logid}
```
其中key和value的数据类型都是列表,并且一一对应。input_dicts是一个字典,它的key和value和data中的key和value是一致的。log_id和data中的logid一致。
**(2)处理流程:** 数据预处理流程和基于Paddle Inference的模型预处理流程相同。
**(3)输出:** 需要返回四个参数,一般开发者使用时只关心第一个返回值,网络输入字典,其余返回值使用默认的即可。
```
{"input": input_imgs}, False, None, ""
```
上述网络输入字典的key可以通过服务化模型配置文件serving_server/serving_server_conf.prototxt中的feed_var字典的name字段获取。
**【实战】**
针对AlexNet网络的数据预处理开发,需要将[Paddle Inference中的preprocess_ops.py, 待补充文件路径] 复制到Paddle Serving的目录下,修改alex_web_service.py文件中代码如下:
添加头文件:
```py
import numpy as np
import sys
import base64
from PIL import Image
import io
from preprocess_ops import ResizeImage, CenterCropImage, NormalizeImage, ToCHW, Compose
```
修改AlexNetOp中的init_op和preprocess函数相关代码:
```py
class AlexNetOp(Op):
def init_op(self):
self.eval_transforms = Compose([
ResizeImage(256), CenterCropImage(224),
NormalizeImage(), ToCHW()])
def preprocess(self, input_dicts, data_id, log_id):
(_, input_dict), = input_dicts.items()
batch_size = len(input_dict.keys())
imgs = []
for key in input_dict.keys():
data = base64.b64decode(input_dict[key].encode('utf8'))
byte_stream = io.BytesIO(data)
img = Image.open(byte_stream)
img = img.convert("RGB")
img = self.eval_transforms(img)
imgs.append(img[np.newaxis, :].copy())
input_imgs = np.concatenate(imgs, axis=0)
return {"input": input_imgs}, False, None, ""
```
### 2.6 开发预测结果后处理程序
【基本流程】
web_service.py文件中的TIPCExampleOp类的 postprocess 函数用于开发预测结果后处理程序,包含输入、处理流程和输出三部分。
**(1)输入:** 包含四个参数,其中参数input_dicts、log_id和数据预处理函数preprocess中一样,data_id可忽略,fetch_dict 是网络预测输出字典,其中输出的key可以通过服务化模型配置文件serving_server/serving_server_conf.prototxt中的fetch_var字典的name字段获取。
**(2)处理流程:** 数据预处理流程和基于Paddle Inference的预测结果后处理一致。
**(3)输出:** 需要返回三个参数,一般开发者使用时只关心第一个返回值,预测结果字典,其余返回值使用默认的即可。
```
result, None, ""
```
【实战】
针对AlexNet网络的预测结果后处理开发,修改web_service.py文件中AlexNetOp中的postprocess函数相关代码如下:
```py
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
# 取输出结果
score_list = list(fetch_dict.values())[0]
result = {"class_id": [], "prob": []}
for score in score_list:
score = score.flatten()
class_id = score.argmax()
prob = score[class_id]
result["class_id"].append(class_id)
result["prob"].append(prob)
result["class_id"] = str(result["class_id"])
result["prob"] = str(result["prob"])
return result, None, ""
```
### 2.7 启动模型预测服务
**【基本流程】**
当完成服务化部署引擎初始化、数据预处理和预测结果后处理开发,则可以按如下命令启动模型预测服务:
```bash
python3 web_service.py &
```
**【实战】**
针对AlexNet网络, 启动成功的界面如下:
![](./images/serving_startup_visualization.jpg)
### 2.8 开发客户端访问服务的程序
**【基本流程】**
当成功启动了模型预测服务,可以修改pipeline_http_client.py程序,访问该服务。主要设置url(服务地址)、logid(日志ID)和测试图像。其中服务地址的url的样式为 "http://127.0.0.1:18080/tipc_example/prediction" ,url的设置需要将url中的tipc_example更新为TIPCExampleService类初始化的name。
**【实战】**
针对AlexNet网络, 修改pipeline_http_client.py程序中的url(服务地址)、logid(日志ID)和测试图像地址,其中url改为:
```
url = "http://127.0.0.1:18080/alexnet/prediction"
```
客户端访问服务的命令如下:
```
python3 pipeline_http_client.py
```
访问成功的界面如下:
![](./images/serving_client_results.jpg)
【注意事项】
如果访问不成功,可能设置了代理影响的,可以用下面命令取消代理设置。
```
unset http_proxy
unset https_proxy
```
### 2.9 验证服务化部署功能的正确性
**【基本流程】**
* 对于相同图像,分别使用Inference推理以及Serving部署,获取预测结果,比较二者结果。
**【实战】**
AlexNet中,模型推理的命令可以参考:[AlexNet 模型推理](https://github.com/littletomatodonkey/AlexNet-Prod/blob/tipc/pipeline/Step5/AlexNet_paddle/README.md#512-%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86)
模型部署的命令说明可以参考:[AlexNet模型部署](https://github.com/littletomatodonkey/AlexNet-Prod/blob/tipc/pipeline/Step5/AlexNet_paddle/deploy/serving/README.md#24-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AE%BF%E9%97%AE%E6%9C%8D%E5%8A%A1)
**【核验】**
* 二者预测结果完全相同
* 文档中给出基于相同图像的预测命令说明以及结果显示
## 3 FAQ