1 前言
想要快速部署的就直接跳到部署示例,直接看一系列的配置肯定会云里雾里的!
2 关于PaddleServing
PaddleServing是飞桨官方推荐的服务化部署框架,围绕AI落地的最后一公里提供专业、可靠、易用的在线模型服务框架。训练完我们的模型之后,我们还想将模型做成服务化接口供外部调用,利用paddleserving即可部署PaddleOCR服务。
2.2流程
3 环境准备
本项目仅支持Python3.6/3.7/3.8/3.9,所有的与Python/Pip相关的操作都需要选择正确的Python版本。PaddleServing有三种安装方式,分别是docker镜像安装、原生系统环境安装和源码编译安装,三种方式选择其中一种即可。注意:请根据计算硬件、操作系统和软件驱动版本等环境差异,选择正确的安装程序。作为懒人租个服务器然后pip更快呢
3.1 使用docker部署Serving开发镜像
cpu版本和gpu版本二选一即可。使用docker部署的话,pip和python命令后要加上版本号,如pip3.6、python3.8。
- cpu版本
docker pull registry.baidubce.com/paddlepaddle/serving:0.9.0-devel
docker run -p 9292:9292--name test_cpu -dit registry.baidubce.com/paddlepaddle/serving:0.9.0-devel bash
docker exec -it test_cpu bash
- gpu版本
docker pull registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda11.2-cudnn8-devel
nvidia-docker run -p 9292:9292 --name test_gpu -dit registry.baidubce.com/paddlepaddle/serving:0.9.0-cuda11.2-cudnn8-devel bash
nvidia-docker exec -it test_gpu bash
paddle serving使用9292端口传输数据,按需修改端口。
3.2 依赖库安装
- cpu版本
git clone https://github.com/PaddlePaddle/Serving
cd Serving
pip3.8 install -r python/requirements.txt
pip3.8 install paddle-serving-client==0.9.0
pip3.8 install paddle-serving-app==0.9.0
pip3.8 install paddle-serving-server==0.9.0
pip3.8 install paddlepaddle==2.5.2
pip3.8 install paddlenlp==2.5.2
- gpu版本
gpu版本安装比cpu的复杂,首先要根据gpu环境选择合适的版本。
post112=CUDA11.2+cuDNN8+TensorRT8(推荐)
post101=CUDA10.1+cuDNN7+TensorRT6
post102=CUDA10.2+cuDNN7+TensorRT6(与Paddle镜像一致)
post1028=CUDA10.2+cuDNN8+TensorRT7
确认好版本之后,再安装依赖。
git clone https://github.com/PaddlePaddle/Serving
cd Serving
pip3.8 install -r python/requirements.txt
pip3.8 install paddle-serving-client==0.9.0
pip3.8 install paddle-serving-app==0.9.0 --no-deps
# GPUServer,需要确认环境再选择执行哪一条,推荐使用CUDA11.2的包
pip3.8 install paddle-serving-server-gpu==0.9.0.post112
pip3.8 install paddlepaddle-gpu==2.5.1 -f https://www.paddlepaddle.org.cn/whl/linux/mkl/avx/stable.html
pip3.8 install paddlenlp==2.5.2
如果使用Linux安装,不使用docker安装
git clone https://github.com/PaddlePaddle/Serving
pip install shapely==1.8.0
pip install wheel>=0.34.0, <0.35.0
pip install setuptools>=44.1.0
pip install google>=2.0.3
pip install protobuf>=3.12.2
pip install grpcio-tools==1.33.2
pip install grpcio>=1.28.1
pip install func-timeout>=4.3.5
pip install pyyaml>=5.1
pip install flask>=1.1.2
pip install click==7.1.2
pip install itsdangerous==1.1.0
pip install Jinja2==2.11.3
pip install pyclipper==1.2.1
pip install MarkupSafe==1.1.1
pip install Werkzeug==1.0.1
pip install ujson>=2.0.3
pip install sentencepiece==0.1.96
pip install opencv-python==3.4.17.63
pip install pytest==7.0.1
pip install prometheus-client==0.12.0
pip install pillow==9.0.0
pip install av==8.0.3
pip install decord==0.4.2
pip install SimpleITK==1.2.4 -i https://mirrors.ustc.edu.cn/pypi/simple
pip install paddle-serving-client==0.9.0
pip install paddle-serving-app==0.9.0 --no-deps
pip install paddle-serving-server-gpu==0.9.0.post112
pip install paddlepaddle-gpu==2.5.2
pip install paddlenlp==2.8.1
pip install aistudio-sdk==0.2.6
pip install numpy==1.24.4
3.3 环境检查
PaddleServing提供了一键运行示例,检查PaddleServing环境是否安装正确。
python -m paddle_serving_server.serve check
启动后输入对应的命令来检查各部分是否编译成功,命令如下表所示。
| 命令 | 描述 |
|---|---|
| check_all | 检查PaddleInference、PipelineServing、C++Serving。只打印检测结果,不记录日志 |
| check_pipeline | 检查PipelineServing,只打印检测结果,不记录日志 |
| check_cpp | 检查C++Serving,只打印检测结果,不记录日志 |
| check_inference | 检查PaddleInference是否安装正确,只打印检测结果,不记录日志 |
| debug | 发生报错后,该命令将打印提示日志到屏幕,并记录详细日志文件 |
| exit | 退出 |
4 模型开发
4.1 ocr模型训练
4.2 转换Serving
如果需要把训练好的模型部署至paddle自带的服务框架,需将inference模型转换为serving格式的文件。
python -m paddle_serving_client.convert \
--dirname '存放inference模型的文件夹名' \
--model_filename inference.pdmodel \ #需要转换的inference模型
--params_filename inference.pdiparams \ #需要转换的模型参数
--serving_server serving_server \ #转换后的模型文件和配置文件的存储路径
--serving_client serving_client #转换后的客户端配置文件存储路径
然后会生成serving_server、serving_client两个文件夹:
├──serving_client
│├──serving_client_conf.prototxt
│└──serving_client_conf.stream.prototxt
└──serving_server
├──inference.pdmodel
├──inference.pdiparams
├──serving_server_conf.prototxt
└──serving_server_conf.stream.prototxt
其中,serving_client_conf.prototxt和serving_server_conf.prototxt是PaddleServing的客户端和服务端需要加载的配置,serving_client_conf.stream.prototxt和serving_server_conf.stream.prototxt是配置文件的二进制形式。
打开文件【serving_server】-【serving_server_conf.prototxt】,找到fetch_var,记住fetch_var后面的name,这个在后面的配置文件需要用到。
- feed_var:模型输入
- fetch_var:模型输出
- name:名称
- alias_name:别名,与名称对应
- is_lod_tensor:是否为lod
- feed_type:数据类型
- shape:数据维度
5 服务启动
PaddleServing为用户提供了两种推理服务,RPC服务和Web服务。
- RPC服务是CS架构,用户使用客户端(Client)访问服务端(Server)获取推理结果,Client和Server之间的通信使用的是brpc框架来完成的。brpc是百度开源的、用C++编写的工业级RPC框架,具有高并发、低延时等特点,常用于搜索、存储、机器学习、广告、推荐等高性能系统,已经支持了包括百度在内上百万在线预估实例、上千个在线预估服务,稳定可靠。但是只适用于Linux操作系统,而且需要编写Client的代码。
- Web服务是BS架构,用户可以使用浏览器或其他web应用通过http协议访问服务端。与RPC服务相比,该服务仅需向指定的url请求即可获得推理结果,不受操作系统的限制也无需编写Client代码。
5.1 RPC服务
5.1.1开启服务端
首先启动服务:
python -m paddle_serving_server.serve\
--model model_filename \
--thread 10 \
--port 9292 \
--gpu_ids 0,1 \ #指定gpu
--op_num 4 #指定并发数
如果服务需要部署多个模型时,则在对应的参数后面添加,例如:
python -m paddle_serving_server.serve \
--model model1, model2 \
--thread 10 \
--port 9292 \
--gpu_ids 0,1 1,2 \ #指定gpu
--op_num 4,8 #指定并发数
服务端无需做任何改造,即可支持RPC方式。
5.1.2客户端请求
客户端请求只需要四步:
1、创建一个Client对象
2、加载Client端的prototxt文件
3、准备请求数据
4、调用predict函数,请求预测服务
from paddle_serving_client import Client
client = Client() #创建Client对象
client.load_client_config("serving_client/serving_client_conf.prototxt") #加载Client端的prototxt文件
client.connect(["127.0.0.1:9292"]) #连接server端,端口要与启动命令的一致
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]
result = client.predict(
feed = {"x":np.array(data).reshape(1,13,1)},
fetch = ["price"]
)
print(result)
其中feed是带有模型输入变量别名和值的dict,fetch从服务器返回的预测变量赋值。在训练过程中保存可服务模型时,被赋值的tensor名为"x"和"price"。
5.2 Web服务
5.2.1开启服务端
python -m paddle_serving_server.serve\
--model model_filename \
--thread 10 \
--port 9292 \
--gpu_ids 0,1 \ #指定gpu
--op_num 4 #指定并发数
5.2.2客户端请求
使用HTTPClient只需要四步:
1、创建一个HTTPClient对象
2、加载Client端的prototxt配置文件
3、调用connect函数
4、调用predict,通过http方式请求预测服务
curl -H "Content-Type:application/json" \
-XPOST http://127.0.0.1:9292/Service/prediction \
-d '{"feed":[{"x":[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]}],"fetch":["price"]}'
其中Service字段和inference字段分别是Service服务名和rpc方法名,-d后面的是请求的数据体。
6 Pipeline框架
在许多深度学习框架中,模型服务化部署通常用于单模型的一键部署。但在AI工业大生产的背景下,端到端的单一深度学习模型不能解决复杂问题,多个深度学习模型组合使用是解决现实复杂问题的常规手段,如文字识别OCR服务至少需要检测和识别2种模型;视频理解服务一般需要视频抽帧、切词、音频处理、分类等多种模型组合实现。当前,通用多模型组合服务的设计和实现是非常复杂的,既要能实现复杂的模型拓扑关系,又要保证服务的高并发、高可用和易于开发和维护等。
Paddle Serving实现了一套通用的多模型组合服务编程框架Python Pipeline,不仅解决上述痛点,同时还能大幅提高GPU利用率,并易于开发和维护。
6.1 框架设计
Pipeline框架分为网络服务层和图执行引擎两部分,网络服务层处理多种网络协议请求和通用输入参数问题,图执行引擎层解决复杂拓扑关系。
6.1.1 网络服务层
网络服务层包括了gRPC-gateway和gRPCServer。gPRCgateway接收HTTP请求,打包成proto格式后转发给gRPCServer,一套处理程序可同时处理HTTP、gRPC两种类型请求。在支持多种模型的输入输出数据类型上,该框架使用统一的service.proto结构,具有更好的通用性。
Pipeline服务包装了继承于WebService类,以OCR示例为例,派生出OcrService类,get_pipeline_response函数内实现DAG拓扑关系,默认服务入口为read_op,函数返回的Op为最后一个处理节点,此处要求最后返回的Op必须唯一。
所有服务和模型的所有配置信息在config.yml中记录,URL的name字段由OcrService初始化定义,run_service函数启动服务。
class OcrService(WebService):
def get_pipeline_response(self,read_op):
det_op = DetOp(name="det", input_ops=[read_op])
rec_op = RecOp(name="rec", input_ops=[det_op])
return ec_op
ocr_service = OcrService(name="ocr")
ocr_service.prepare_pipeline_config("config.yml")
ocr_service.run_service()
与网络框架相关的配置在config.yml中设置。其中worker_num表示框架主线程gRPC线程池工作线程数,可理解成网络同步线程并发数;rpc_port和http_port是服务端口,可同时开启,但不允许同时为空。
rpc_port: 9292
http_port: 18089
worker_num: 8
6.1.2 图执行引擎
图执行引擎(图论)的设计思路是基于有向无环图(DAG)实现多模型组合的复杂拓扑关系,有向无环图由单节点或多节点串联、并联结构构成。可以理解成工作流,每次执行时按照固定的顺序执行节点的内容。
图执行引擎抽象归纳出两种数据结构Op节点和Channel有向边,构建一条异步流水线工作流。核心概念和设计思路如下:
Op节点:可理解成一个处理方法,可独立运行,独立设置并发度。每个Op节点的计算结果放入其绑定的Channel中。Channel数据管道:可理解为一个单向缓冲队列。每个Channel只接收上游Op节点的计算输出,作为下游Op节点的输入。- 工作流:根据用户定义的节点依赖关系,图执行引擎自动生成有向无环图。每条用户请求到达图执行引擎时会生成一个唯一自增ID,通过这种唯一性绑定关系标记流水线中的不同请求。
Op的设计原则:
- 单个
Op默认的功能是根据输入的Channel数据,访问一个PaddleServing的单模型服务,并将结果存在输出的Channel - 单个
Op可以支持用户自定义,包括preprocess,process,postprocess三个函数都可以由用户继承和实现 - 单个
Op可以控制并发数,从而增加处理并发数 - 单个
Op可以获取多个不同RPC请求的数据,以实现Auto-Batching
其构造函数如下:
def __init__(
name=None, # 标识Op类型的字符串,必须全局唯一
input_ops=[], # 当前Op的所有前继Op的列表
server_endpoints=[],
fetch_list=[], # 远程Serving Service的fetch列表
client_config=None, # 对应的Client端配置
client_type=None, # 可选择brpc、grpc或local_predictor。local_predictor不启动Serving服务,进程内预测
concurrency=1, # Op的并发数
timeout=-1, # process操作的超时时间
retry=1, # 超时重试次数
batch_size=1, # 批量处理大小
)
Channel的设计原则:
- Channel是Op之间共享数据的数据结构,负责共享数据或者共享数据状态信息
- Channel可以支持多个OP的输出存储在同一个Channel,同一个Channel中的数据可以被多个Op使用
6.1.3 服务日志
Pipeline服务日志在当前目录的PipelineServingLogs目录下,目录下有pipeline.log、pipeline.log.wf、pipeline.tracer。
在服务发生异常时,错误信息会记录在pipeline.log.wf日志中。打印tracer日志要求在config.yml的DAG属性中添加tracer配置。通常,Pipeline框架打印的日志会同时带上data_id和log_id。
Pipeline的日志模块在logger.py中定义,使用了logging.handlers.RotatingFileHandler支持磁盘日志文件的轮换。根据不同文件级别和日质量分别设置了maxBytes和backupCount,当即将超出预定大小时,将关闭旧文件并打开一个新文件用于输出。
6.1.4 自定义信息
6.1.4.1 自定义Web服务URL
在Web服务中自定义服务名称是常见操作,尤其是将已有服务迁移到新框架。URL中核心字段包括ip、port、name和method,根据最新部署的环境信息设置前2个字段,重点介绍如何设置name和method,框架提供默认的method是prediciton,如http://127.0.0.1:9999/ocr/prediction。
框架有2处代码与此相关,分别是gRPCGateway的配置文件python/pipeline/gateway/proto/gateway.proto和服务启动文件web_server.py。
业务场景中通过设置name和验证method解决问题。以OCR示例为例,服务启动文件web_server.py通过类OcrService构造函数的name字段设置URL中name字段;
ocr_service=OcrService(name="ocr")
ocr_service.prepare_pipeline_config("config.yml")
ocr_service.run_service()
框架提供默认的method是prediciton,通过重载RequestOp::unpack_request_package来验证method。
def unpack_request_package(self, request):
dict_data = {}
log_id = None
if request is None:
_LOGGER.critical("request is None")
raiseValueError("request is None")
if request.method is not "prediction":
_LOGGER.critical("request method error")
raiseValueError("request method error")
在python/pipeline/gateway/proto/gateway.proto文件可以对name和method做严格限制,一般不需要修改,如需要特殊指定修改后,需要重新编译PaddleServing
6.1.4.2 自定义服务输入和输出结构
输入和输出结构包括proto中Request和Response结构,以及Op前后处理返回。
当默认proto结构不满足业务需求时,同时下面2个文件的proto的Request和Responsemessage结构,保持一致。
pipeline/gateway/proto/gateway.proto
pipeline/proto/pipeline_service.proto
修改后,需要重新编译。
6.1.4.3 自定义服务并发和模型配置
完整的配置信息可参考配置信息。
6.1.4.4 自定义推理过程
推理Op提供3个外部函数接口:
-
preprocess(self, input_dicts)
- 对从Channel中获取的数据进行处理,处理完的数据将作为process函数的输入。(该函数对一个sample进行处理)
-
process( self, feed_dict_list, typical_logid )
- 基于PaddleServingClient进行RPC预测,处理完的数据将作为postprocess函数的输入。(该函数对一个batch进行处理)
-
postprocess( self, input_dicts, fetch_dict )
- 处理预测结果,处理完的数据将被放入后继Channel中,以被后继Op获取。(该函数对一个sample进行处理)
-
init_op(self)
- 用于加载资源(如字典等)
-
self.concurrency_idx
- 当前进程(非线程)的并发数索引(不同种类的Op单独计算)
Op在一个运行周期中会依次执行preprocess,process,postprocess三个操作(当不设置server_endpoints参数时,不执行process操作),用户可以对这三个函数进行重写,默认实现如下:
def preprocess(self, input_dicts):
if len(input_dicts) != 1:
raise NotImplementedError('this Op has multiple previous inputs.Please override this func.')
(_, input_dict), = input_dicts.items()
return input_dict
def process(self, feed_dict_list, typical_logid):
err, err_info = ChannelData.check_batch_npdata(feed_dict_list)
if err != 0:
raise NotImplementedError("{}, Please over ride preprocess func.".format(err_info))
call_result = self.client.predict(
feed=feed_dict_list,
fetch=self._fetch_names,
log_id=typical_logid
)
if isinstance(self.client, MultiLangClient):
if call_result is None or call_result["serving_status_code"] != 0:
return None
call_result.pop("serving_status_code")
return call_result
def postprocess(self, input_dicts, fetch_dict):
return fetch_dict
-
preprocess的参数是前继Channel中的数据input_dicts,该变量(作为一个sample)是一个以前继Op的name为Key,对应Op的输出为Value的字典。 -
process的参数是PaddleServingClient预测接口的输入变量fetch_dict_list(preprocess函数的返回值的列表),该变量(作为一个batch)是一个列表,列表中的元素为以feed_name为Key,对应ndarray格式的数据为Value的字典。typical_logid作为向PaddleServingService穿透的logid。 -
postprocess的参数是input_dicts和fetch_dict,input_dicts与preprocess的参数一致,fetch_dict(作为一个sample)是process函数的返回batch中的一个sample(如果没有执行process,则该值为preprocess的返回值)。
用户还可以对init_op函数进行重写,已加载自定义的一些资源(比如字典等),默认实现如下:
def init_op(self):
pass
RequestOp和ResponseOp是Python Pipeline的中2个特殊Op,分别是用分解RPC数据加入到图执行引擎中,和拿到图执行引擎的预测结果并打包RPC数据到客户端。RequestOp类的设计如下所示,核心是在unpack_request_package函数中解析请求数据,因此,当修改Request结构后重写此函数实现全新的解包处理。
| 接口 | 说明 |
|---|---|
| init_op(self) | OP初始化,设置默认名称@DAGExecutor |
| unpack_request_package(self, request) | 解析请求数据 |
class RequestOp(Op):
def __init__(self):
# PipelineService.name="@DAGExecutor"
super(RequestOp, self).**init**(name="@DAGExecutor", input_ops=[])
# init op
try:
self.init_op()
except Exception as e:
_LOGGER.critical("Op(Request) Failed to init:{}".format(e))
os.\_exit(-1)
def unpack_request_package(self, request):
dict_data = {}
log_id = None
if request is None:
_LOGGER.critical("request is None")
raise ValueError("request is None")
for idx, key in enumerate(request.key):
dict_data[key] = request.value[idx]
log_id = request.logid
_LOGGER.info(
"RequestOp unpack one request.log_id:{},clientip:{}name:{},method:{}".format(log_id,request.clientip, request.name, request.method)
)
return dict_data, log_id, None, ""
ResponseOp类的设计如下所示,核心是在pack_response_package中打包返回结构,因此修改Response结构后重写此函数实现全新的打包格式。
| 接口 | 说明 |
|---|---|
| init_op(self) | OP初始化,设置默认名称@DAGExecutor |
| pack_response_package(self, channeldata) | 处理接收的RPC数据 |
class ResponseOp(Op):
def __init__(self, input_ops):
super(ResponseOp,self).__init__(
name="@DAGExecutor",
input_ops=input_ops
)
# init op
try:
self.init_op()
except Exception as e:
_LOGGER.critical("Op(ResponseOp) Failed to init:{}".format(e,exc_info=True))
os._exit(-1)
def pack_response_package(self, channeldata):
resp = pipeline_service_pb2.Response()
error_code = channeldata.error_code
error_info = ""
...
# pack results
if error_code is None:
error_code = 0
resp.err_no = error_code
resp.err_msg = error_info
return resp
5.1.4.5 自定义业务错误类型
用户可根据业务场景自定义错误码,继承ProductErrCode,在Op的preprocess或postprocess中返回列表中返回,下一阶段处理会根据自定义错误码跳过后置OP处理。详情请见官方文档,因为用的较少此处不在赘述。
6.2 服务启动与推理
从设计上,Python Pipeline 框架实现轻量级的服务化部署,提供了丰富的核心功能,既能满足服务基本使用,又能满足特性需求。
如果模型处理过程包含一个以上的模型推理环节(例如OCR一般需要det+rec两个环节),此时有两种做法可以满足您的需求:
- 启动两个Serving服务(例如Serving-det,Serving-rec)
在您的Client中,读入数据——>det前处理——>调用Serving-det预测——>det后处理——>rec前处理——>调用Serving-rec预测——>rec后处理——>输出结果。
- 通过修改代码,自定义模型预测行为(自定义OP),自定义服务处理的流程(自定义DAG),将多个模型的组合处理过程
(上述的det前处理——>调用Serving-det预测——>det后处理——>rec前处理——>调用Serving-rec预测——>rec后处理)集成在一个Serving服务中。此时,在您的Client中,读入数据——>调用集成后的Serving——>输出结果。
6.2.1 启动两个Serving服务
启动时要要同时启动serving服务与client端。
- Server端启动
要实现一个服务启动两个模型串联,只需要在--model后依次按顺序传入模型文件夹的相对路径,且需要在--op后依次传入自定义的Op类名称,其中--model后面的模型与--op后面的类名称的顺序需要对应。假设已经定义好了两个Op分别为GeneralDetOp和GeneralRecOp,则脚本代码如下:
#一个服务启动多模型串联
python3 -m paddle_serving_server.serve \
--model ocr_det_model ocr\_rec\_model \
--op GeneralDetOp GeneralRecOp \ #多模型串联ocr_det_model对应GeneralDetectionOpocr_rec_model对应GeneralRecOp
--port 9292
- Client端调用
Client端的调用也需要传入两个Client端的proto文件或文件夹的路径,假定文件ocr_client.py为调用脚本,此时Client调用如下:
#一个服务启动多模型串联
python3 ocr_client.py ocr_det_client ocr_rec_client
#ocr_det_client为第一个模型的Client端proto文件夹的相对路径
#ocr_rec_client为第二个模型的Client端proto文件夹的相对路径
此时,对于Server端而言,输入的数据的格式与第一个模型的Client端proto格式定义的一致,输出的数据格式与最后一个模型的Client端proto文件一致。
6.2.2 Pipeline自定义模型op实现本地与远程推理
本地推理是指在服务所在机器环境下开启多进程推理,而远程推理是指本地服务请求远程 C++ Serving 推理服务。
本地推理的优势是实现简单,一般本地处理相比于远程推理耗时更低。而远程推理的优势是可实现 Python Pipeline 较难实现的功能,如部署加密模型,大模型推理。
使用pipeline自定义op需要绑定配置,详细的配置项参考官方文档。
- 本地推理
op:
uci: # op名字自定义
concurrency: 10 # 并发数,is_thread_op=True时,为线程并发;否则为进程并发
local_service_conf:
model_config: ocr_det_model # 写到serving_server文件夹
device_type: 0
devices: ""
client_type: local_predictor
fetch_list: ["result"]
- 远程推理
由于配置项较多,仅重点介绍部分核心选项的使用。
op:
bow:
concurrency: 1 # cpu环境适当降低并发数
client_type: brpc
retry: 1 # Serving交互重试次数,默认不重试
timeout: 3000 # Serving交互超时时间, 单位ms
server_endpoints: ["127.0.0.1:9393"] # Serving IPs
# client端配置,写到serving_client_conf.prototxt
client_config: "imdb_bow_client_conf/serving_client_conf.prototxt"
fetch_list: ["prediction"] # fetch结果列表,以client_config中fetch_var的alias_name为准
#rpc端口,rpc_port和http_port不允许同时为空。当rpc_port为空且http_port不为空时,会自动将rpc_port设置为http_port+1
rpc_port: 18090
#http端口,rpc_port和http_port不允许同时为空。当rpc_port可用且http_port为空时,不自动生成http_port
http_port: 9999
worker_num: 20 # 最大并发数
build_dag_each_worker: False # False,框架在进程内创建一条DAG;True,框架会每个进程内创建多个独立的DAG
#有向无环图级别的选项
dag:
# op资源类型,True,为线程模型;False,为进程模型
is_thread_op: False
#重试次数
retry: 1
#使用性能分析,True,生成Timeline性能数据,对性能有一定影响;False为不使用
use_profile: False
#统计各个阶段耗时、Channel在PipelineServingLogs/pipeline.tracer
tracer:
#每次记录的间隔,单位:秒
interval_s: 10
op:
det:
concurrency: 6
# 当op配置没有server_endpoints时,从loca_service_conf读取本地服务配置
local_service_conf:
# client类型,包括brpc,grpc和local_predictor,local_predictor不启动Serving服务,进程内预测
client_type: local_predictor
model_config: ocr_det_model # 写到serving_server文件夹
# Fetch结果列表,以client_config中fetch_var的alias_name为准
fetch_list: ["save_infer_model/scale_0.tmp_1"]
device_type: 0 # 设备类型,0=cpu,1=gpu,2=tensorRT
devices: "" # 计算硬件ID,当devices为""或不写时为CPU预测
thread_num: 2 # 线程数
rec:
...(同上)
配置文件中要定义op的相关属性,每个op处理和图结构定义在web_service.py程序中,假设实现了DetOp和RecOp2个OP:
# DetOp对应配置文件Config.yml中detop
class DetOp(Op):
def init_op(self):
def preprocess(self, input_dicts, data_id, log_id):
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
# RecOp对应配置文件Config.yml中recop
class RecOp(Op):
def init_op(self):
def preprocess(self, input_dicts, data_id, log_id):
def postprocess(self, input_dicts, fetch_dict, data_id, log_id):
7 部署示例
通过6个步骤操作即可实现OCR实例部署:
- 获取模型
- 保存Serving部署的模型参数
- 下载测试数据集(可选)
- 修改config.yml配置(可选)
- 代码与配置信息绑定
- 启动服务与验证
本文仅展示使用pipeline部署的服务。
假定现在有一个身份证ocr识别项目,需要识别出姓名、性别、出生日期、住址和身份证件号。详细的流程如下图所示:
7.1 项目架构
OCRProject/
- model/ # 存放serving模型
- det_client
- serving_client_conf.prototxt
- serving_client_conf.stream.prototxt
- det_server
- inference.pdiparams
- inference.pdmodel
- serving_server_conf.prototxt
- serving_server_conf.stream.prototxt
- rec_client(如有)
- rec_server(如有)
- ser_client
- ser_server
- op/ # 自定义op
- remote_op/
- ocr_dict/ # ocr字典
- class_list.txt # 存放ser模型的字段
- process/ # 流程
- train_data/ # 训练数据
- config.yml # 配置文件
- web_service.py
- web_define.py
- pipeline_rpc_client.py
7.2 获取模型
按需训练det、rec、ser模型,并转换成serving格式的模型。训练方法见文章。
7.2 保存Serving部署的模型参数
将inference模型转换为serving格式的文件。
python -m paddle_serving_client.convert \
--dirname '存放inference模型的文件夹名' \
--model_filename inference.pdmodel \ #需要转换的inference模型
--params_filename inference.pdiparams \ #需要转换的模型参数
--serving_server serving_server \ #转换后的模型文件和配置文件的存储路径
--serving_client serving_client #转换后的客户端配置文件存储路径
然后会生成serving_server、serving_client两个文件夹:
├──serving_client
│├──serving_client_conf.prototxt
│└──serving_client_conf.stream.prototxt
└──serving_server
├──inference.pdmodel
├──inference.pdiparams
├──serving_server_conf.prototxt
└──serving_server_conf.stream.prototxt
打开文件【serving_server】-【serving_server_conf.prototxt】,找到fetch_var,记住fetch_var后面的name,这个在后面的配置文件需要用到。
7.3修改config配置
由于OP配置中存在大量重复配置,所以会使用yml的变量对配置进行简化处理。&为变量定义,*引用变量
7.4代码与配置信息绑定
7.5启动服务与验证
8 常见问题
- 依赖库av安装失败
原因:av包版本与FFmpeg不兼容,可以试试:重新编译FFmpeg
wget <https://ffmpeg.org/releases/ffmpeg-4.4.tar.gz>
tar -xzf ffmpeg-4.4.tar.gz
cd ffmpeg-4.4
apt-get install -y build-essential yasm pkg-config
./configure --enable-shared --prefix=/usr/local
make -j\$(nproc)
make install
ldconfig
/usr/local/bin/ffmpeg -version
#再重新安装
pip cache purge
pip install av==8.0.3 --no-cache-dir
出现安装依赖报错No module named '_distutils_hack',先安装setuptools库为57.5.0版本再安装av库。
- opencv-python==3.4.17.61安装失败
直接安装3.4.17.63版本,然后跳过61版本的安装
- 安装SimplrITK库卡住
pip install SimpleITK==1.2.4 -i <https://mirrors.ustc.edu.cn/pypi/simple>
- 安装过程问题
如果出现123的问题的话,后续可能会出现安装error,忽略第一个opencv的即可,其他几个库直接pip install 就行