PaddlePaddle 框架 Serving 部署一则
2022-03-28
Paddle Paddle Serving
[[PaddlePaddle_Speech]]
[[PaddlePaddle_OCR]]
零基础实践深度学习
docker安装
pull & 进入容器
CPU
docker pull registry.baidubce.com/paddlepaddle/serving:0.8.0-devel
docker run -p 9292:9292 --name PaddleServing_0 -dit registry.baidubce.com/paddlepaddle/serving:0.8.0-devel bash
docker exec -it PaddleServing_0 bash
// 克隆项目
clone project
git clone https://github.com/PaddlePaddle/Serving
GPU
nvidia-docker pull registry.baidubce.com/paddlepaddle/serving:0.8.0-cuda10.2-cudnn7-devel
nvidia-docker run -p 9292:9292 --name test -dit registry.baidubce.com/paddlepaddle/serving:0.8.0-cuda10.2-cudnn7-devel bash
nvidia-docker exec -it test bash
// 克隆项目
clone project
git clone https://github.com/PaddlePaddle/Serving
通用依赖
Paddle开发镜像需要执行以下脚本增加 Serving所需依赖项
bash Serving/tools/paddle_env_install.sh
cd Serving
pip3 install -r python/requirements.txt -i https://pypi.douban.com/simple
pip install grpcio -i https://pypi.douban.com/simple --trusted-host pypi.douban.com
可选wheel包
安装服务whl包, 共有3种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的包
# post102 = CUDA10.2 + cuDNN7 + TensorRT6(推荐)
# post101 = CUDA10.1 + cuDNN7 + TensorRT6
# post112 = CUDA11.2 + cuDNN8 + TensorRT8
pip3 install paddle-serving-server-gpu==0.8.3.post102 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install paddle-serving-server-gpu==0.8.3.post101 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip3 install paddle-serving-server-gpu==0.8.3.post112 -i https://pypi.tuna.tsinghua.edu.cn/simple
nvidia-smi -l //可以查看 CUDA 版本
cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 //可以查看CUDNN 版本,
Paddle相关Python库
当您使用 paddle_serving_client.convert 命令或者Python Pipeline框架时才需要安装;
# CPU环境请执行
pip3 install paddlepaddle==2.2.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
# GPU CUDA 10.2环境请执行
pip3 install paddlepaddle-gpu==2.2.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
用到这-m用到的脚本, 可以通过sys.path 找到:
python3
>>> import sys
>>> sys.path //python 查找的脚本的目录变量
C++ Serving
启动服务
Server端的核心是一个由项目代码编译产生的名称为serving的二进制可执行文件, 启动serving时需要用户指定一些参数(例如, 网络IP和Port端口, brpc线程数, 使用哪个显卡, 模型文件路径, 模型是否开启trt, XPU推理, 模型精度设置等等), 有些参数是通过命令行直接传入的, 还有一些是写在指定的配置文件中配置文件中;
为了方便用户快速的启动C++ Serving的Server端, 除了用户自行修改配置文件并通过命令行传参运行serving二进制可执行文件以外, 我们也提供了另外一种通过python脚本启动的方式;
python脚本启动本质上仍是运行serving二进制可执行文件, 但python脚本中会自动完成两件事: 1, 配置文件的生成; 2, 根据需要配置的参数, 生成命令行, 通过命令行的方式, 传入参数信息并运行serving二进制可执行文件;
python3 -m paddle_serving_server.serve --model serving_model --port 9393
该命令会自动生成workdir_9393工作文件夹, 和配置文件, 并使用生成的配置文件启动C++ Serving;
当您的某个模型想使用多张GPU卡部署时.
python3 -m paddle_serving_server.serve --model serving_model --thread 10 --port 9292 --gpu_ids 0,1,2
当您的一个服务包含两个模型部署时.
python3 -m paddle_serving_server.serve --model serving_model_1 serving_model_2 --thread 10 --port 9292
错误 for GPU
error while loading shared libraries: libnvinfer.so.6
sudo find / -name libnvinfer.so.6//找不到
原因: 未安装 tensorRT6; Docker镜像不带cuda, cuDNN7,tensorRT
C++ Serving 配置文件
关于接口地址
infer_service.prototxt
停止服务
在启动Serving/Pipeline服务路径或者环境变量SERVING_HOME路径下(该路径下存在文件ProcessInfo.json)
python3 -m paddle_serving_server.serve stop
Python Pipeline Serving
TODO
官方文档- Python Pipeline Serving
SDK 开发
如需 Java SDK PRC开发, Git Serving项目里面缺失一个jar包
在java SDK 中, 图片读写用到了 Deeplearning4j (深度学习开源库)
wget https://paddle-serving.bj.bcebos.com/jar/paddle-serving-sdk-java-0.0.1.jar
<dependency>
<groupId>io.paddle.serving.client</groupId>
<artifactId>paddle-serving-sdk-java</artifactId>
<version>0.0.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/paddle-serving-sdk-java-0.0.1.jar</systemPath>
</dependency>
for java
客户端初始化
final String model_config_path = "E:\\content-for-work\\2022-03PaddlePaddle\\deploy_model" +
//"\\ppyolov2_r50vd_dcn_ph_serving_client\\serving_client_conf.prototxt"; //目标识别
"\\inference_PPLCNet_hand\\PPLCNet_hand_client\\serving_client_conf.prototxt"; //分类模型 手势识别
io.paddle.serving.client.Client client = new Client();
client.setIP("192.168.30.136");
// client.setPort("9292");// CPU
client.setPort("9393");// GPU
client.loadClientConfig(model_config_path);
调用
参考 PaddleServingClientExample.java
int height = 224;
int width = 224;
int channels = 3;
NativeImageLoader loader = new NativeImageLoader(height, width, channels);
INDArray BGRimage = null;
try {
BGRimage = loader.asMatrix(new File(filename));
} catch (java.io.IOException e) {
System.out.println("load image fail.");
return null;
}
// shape: (channels, height, width)
BGRimage = BGRimage.reshape(channels, height, width);
INDArray RGBimage = Nd4j.create(BGRimage.shape());
// BGR2RGB
CustomOp op = DynamicCustomOp.builder("reverse")
.addInputs(BGRimage)
.addOutputs(RGBimage)
.addIntegerArguments(0)
.build();
Nd4j.getExecutioner().exec(op);
//可以归一化处理; INDArray image = RGBimage.divi(255);
INDArray image = RGBimage;
long[] batch_shape = {1,image.shape()[0],image.shape()[1],image.shape()[2]};
INDArray batch_image = image.reshape(batch_shape);
// feed_var 入参
HashMap<String, Object> feed_data
= new HashMap<String, Object>() {{
put("x", batch_image);
}};
// fetch_var 出参
List<String> fetch = Arrays.asList("softmax_1.tmp_0");
// 请求预测
String result = client.predict(feed_data, fetch, true, 0);
序列消息结果(Protocol Buffers )
Protocol buffers提供一个语言中立,平台中立,可扩展的机制,用于向前和向后兼容来序列化结构化数据。它类似于JSON,但是它更小更快,同时它生成原生语言绑定。
返回的是 google 的 Protocol Buffers 格式数据, 格式的定义可参考: proto 文件夹的general_model_service.proto 文件
注意其 HTTP接口返回的 是Pretty String , 不是原生二进制格式, 是用TextFormat反序列化的!! 二进制使用: toByteArray, parseFrom 不适用
//Pretty String
/**
outputs {
tensor {
name: "Nam"
alias_name: "AliasN"
}
}
*/
response.toString()
Response response = (Response) TextFormat.parse(new String(contentBts),Response.class);
// byte[]
Response.parseFrom()
response.toByteArray();
proto文件 规则定义
message xxx {
字段规则 类型 名称 = 字段编号;
// 字段规则: required -> 字段只能也必须出现 1 次
// 字段规则: optional -> 字段可出现 0 次或1次
// 字段规则: repeated -> 字段可出现任意多次(包括 0)
// 类型: int32, int64, sint32, sint64, string, 32-bit ....
// 字段编号: 0 ~ 536870911(除去 19000 到 19999 之间的数字)
}
\baidu\paddle_serving\predictor\general_model\Response.java 注意路径啊, 有另外一个应该是 pipeline 的结构
你可以指定一个字段是 optional 还是 repeated (proto2和proto3) 或 singular (proto3)。(设置字段为required的选项在proto3废弃了,并且在proto2中强烈不建议。
当指定了一个字段是可选的或可重复的后,你可以指定数据类型。Protocol buffers支持常用的基本数据类型,例如整型、布尔值、浮点型。全部类型参见标量类型。
一个字段也可以是:
一个message类型,因此你可以嵌套部分定义,例如用于重复数据集。
一个enum类型,因此你可以指定一个可选数据集。
一个oneof类型,可用于消息中含有多个可选字段,并且同时只会有一个字段被设置的情形。
一个map类型,用来为定义增加键值对。
proto 数据类型
| .proto类型 | Java 类型 | C++类型 | 备注 |
|---|---|---|---|
| double | double | double | |
| float | float | float | |
| int32 | int | int32 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
| int64 | long | int64 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。 |
| uint32 | int[1] | uint32 | 无符号整数使用可变长编码方式 |
| uint64 | long[1] | uint64 | 无符号整数使用可变长编码方式 |
| sint32 | int | int32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
| sint64 | long | int64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
| fixed32 | int[1] | uint32 | 总是4个字节。如果数值总是比总是比2^28大的话,这个类型会比uint32高效。 |
| fixed64 | long[1] | uint64 | 总是8个字节。如果数值总是比总是比2^56大的话,这个类型会比uint64高效。 |
| sfixed32 | int | int32 | 总是4个字节。 |
| sfixed64 | long | int64 | 总是8个字节。 |
| bool | boolean | bool | |
| string | String | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
| bytes | ByteString | string | 可能包含任意顺序的字节数据。 |
// 0 => INT64
// 1 => FP32
// 2 => INT32
// 3 => FP64
// 4 => INT16
// 5 => FP16
// 6 => BF16
// 7 => UINT8
// 8 => INT8
// 9 => BOOL
// 10 => COMPLEX64
// 11 => COMPLEX128
// 20 => STRING
Paddle Serving 中的模型热重载
remote 端
准备一个监测文件
fluid_time_file
准备一个模型 tar压缩文件 inference_PPLCNet_hand_new.tar 目录结构
─inference_PPLCNet_hand_new
├─PPLCNet_hand_client
└─PPLCNet_hand_serving
PaddleServing端
/var/PaddleServing/deploy_model/ 目录结构
├── _tmp
├── inference_PPLCNet_hand_htest
│ ├── PPLCNet_hand_client
│ │ ├── serving_client_conf.prototxt
│ │ └── serving_client_conf.stream.prototxt
│ ├── PPLCNet_hand_serving
├── fluid_time_file
启动模型
cd /var/PaddleServing/deploy_model/inference_PPLCNet_hand
python3 -m paddle_serving_server.serve --model PPLCNet_hand_serving --thread 4 --port 9393 --gpu_ids 0
准备一个监测文件(同上!)
fluid_time_file 指定本地用于热加载的时间戳文件, 该文件被认为在local_path/local_model_name下;
monitor 脚本参数
python -m paddle_serving_server.monitor \
--type='general' \
--remote_path='paddle_model/' \
--general_host='http://172.16.2.68' \ # 远程主机
--remote_model_name='inference_PPLCNet_hand_new.tar' \ #压缩包
--unpacked_filename='inference_PPLCNet_hand_new' \ #压缩包内的模型文件夹名
--remote_donefile_name='fluid_time_file' \ #远程监测时间戳
--local_path='/var/PaddleServing/deploy_model/' \ #本地工作目录, 它下面有模型文件夹
--local_model_name='inference_PPLCNet_hand_htest' \ #模型文件夹
--local_timestamp_file='fluid_time_file' \ #本地监测时间戳
--local_tmp_path='_tmp' # 临时工作目录
--interval=5 --debug # 监测间隔, 调试信息
其他依赖组件
nvidia-docker
[参考官方文档]https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker
添加repository 和key
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add - \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
更新
sudo apt-get update
安装套件
//sudo apt-get install -y nvidia-container-toolkit ?
sudo apt-get install -y nvidia-docker2
重启docket
sudo systemctl restart docker
测试
sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi
tensorRT 安装
添加 lib 环境变量
vi /etc/profile
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/var/PaddleServing/TensorRT-6.0.1.8/lib"
安装py库

浙公网安备 33010602011771号