PaddlePaddle 框架 Serving 部署一则

2022-03-28

Paddle Paddle Serving

[[PaddlePaddle_Speech]]
[[PaddlePaddle_OCR]]
零基础实践深度学习

docker安装

官方文档 - Install_CN

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 配置文件

Serving_Configure

关于接口地址
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 )

Language Guide (proto3)

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类型,用来为定义增加键值对。

深入 Google ProtoBuf - 简介

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

github 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库

Ubuntu18.04 安装cuda-10.2 cudnn7.6.5.32 tensorrt7.0.0.11

posted @ 2025-11-30 22:17  daidaidaiyu  阅读(0)  评论(0)    收藏  举报