- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
本文以 PaddleNLP 仓库中的 unimo-text 文本摘要应用 为对象,系统讲解如何借助 Paddle Serving 将其部署为工业级在线服务:包括环境准备、Serving 模型转换、Pipeline 服务端与客户端的配置与运行,并结合仓库内 pipeline_service.py、config.yml 等源码逐项拆解实现细节。读完本文,你将掌握从「训练好的动态图模型」到「可对外提供文本摘要能力的 gRPC/HTTP 在线服务」的完整落地路径。
背景介绍:Paddle Serving 能解决什么问题
Paddle Serving 是依托深度学习框架 PaddlePaddle 构建的工业级在线推理服务工具,目标是为深度学习开发者和企业提供高性能、灵活易用的推理部署方案。它支持 RESTful、gRPC、bRPC 等多种通信协议,覆盖多种异构硬件和多种操作系统环境,并内置多种经典预训练模型示例。
在推理引擎层面,Paddle Serving 集成了面向服务器端的高性能推理引擎Paddle Inference与端侧推理引擎Paddle Lite。在服务编排层面,它设计并实现了基于有向无环图(DAG)的异步流水线高性能推理框架,具备以下关键特性:
- 多模型组合:通过 DAG 将多个算子(Op)串联,实现复杂业务链路的编排;
- 异步调度:请求在算子间异步流转,避免同步阻塞;
- 并发推理:支持线程级/进程级并发,提升吞吐;
- 动态批量(Dynamic Batching):把离散到达的请求聚合成批,提高 GPU 利用率;
- 多卡多流推理:充分利用多 GPU 资源;
- 请求缓存:对重复请求做结果缓存。
在 PaddleNLP 仓库中,unimo-text 文本摘要应用 的部署目录同时提供了 Paddle Inference 本地高性能推理 与 Paddle Serving 服务化部署两套方案。前者面向单机自用、追求极致性能的场景;后者面向需要对外提供 HTTP/gRPC 接口、支持多路并发访问的线上服务场景。本文聚焦后者。
Paddle Serving Python 端预测部署主要包含三步:环境准备 → 模型转换 → 部署模型。
环境准备:安装 Serving 客户端与服务端
安装 client 和 serving app
用于向服务发送预测请求的机器(客户端)需要安装如下两个包:
pip install paddle_serving_app paddle_serving_clientpaddle_serving_client提供paddle_serving_client.convert模型转换工具与PipelineClient(在 pipeline_client.py 中被直接使用);paddle_serving_app提供配套的应用工具组件。
安装 GPU server
启动服务(服务端)的机器需要安装 Serving server,注意选择与本地环境一致的版本,以下是 Paddle Serving 0.8.3 系列与 CUDA/TensorRT 的对应关系:
# CUDA10.2 + Cudnn7 + TensorRT6 pip install paddle-serving-server-gpu==0.8.3.post102 # -i https://pypi.tuna.tsinghua.edu.cn/simple # CUDA10.1 + TensorRT6 pip install paddle-serving-server-gpu==0.8.3.post101 # -i https://pypi.tuna.tsinghua.edu.cn/simple # CUDA11.2 + TensorRT8 pip install paddle-serving-server-gpu==0.8.3.post112 # -i https://pypi.tuna.tsinghua.edu.cn/simpleNOTE:
- 可开启国内清华镜像源(命令末尾的
-i参数)来加速下载; - 如果要安装最新版本的 PaddleServing,请以官方最新包文档为准(仓库 README 中给出了对应链接说明,实际安装前建议核对与本地 CUDA 版本的匹配关系)。
模型转换:把静态图模型转成 Serving 可部署格式
使用 Paddle Serving 做服务化部署时,需要将保存的 inference 模型(静态图模型)转换为 Serving 易于部署的模型格式。
前置条件是:先用动态图训练好模型,并通过 FastGeneration 加速及模型静态图导出 得到静态图参数。具体地,在仓库的 unimo-text 目录下执行:
python export_model.py \ --model_name_or_path unimo-text-1.0-summary \ --decoding_strategy beam_search \ --inference_model_dir ./inference_model \ --max_out_len 30 \该命令通过 export_model.py 中paddle.jit.to_static与paddle.jit.save将动态图模型FasterUNIMOText导出为静态图,得到inference_model/unimo_text.pdmodel与inference_model/unimo_text.pdiparams两个文件——这就是 Serving 模型转换的输入。
使用 convert 命令完成转换
用已安装的paddle_serving_client将静态图参数模型转换成 serving 格式,命令如下:
python -m paddle_serving_client.convert --dirname ../../inference_model \ --model_filename unimo_text.pdmodel \ --params_filename unimo_text.pdiparams \ --serving_server inference_model_server \ --serving_client inference_model_client注意:以上命令需要在本部署目录slm/examples/text_summarization/unimo-text/deploy/paddle_serving下执行(../../inference_model指向 unimo-text 目录下导出的静态图模型)。
关键参数释义如下:
| 参数 | 含义 | 说明 |
|---|---|---|
dirname | 模型文件夹地址 | 指向包含.pdmodel与.pdiparams的静态图模型目录 |
model_filename | 模型文件名 | 此处为unimo_text.pdmodel |
params_filename | 模型参数名 | 此处为unimo_text.pdiparams |
serving_server | server 的模型文件和配置文件路径 | 默认"serving_server" |
serving_client | client 的配置文件路径 | 默认"serving_client" |
也可以直接使用仓库提供的 export_serving.sh 一键完成转换,其内容与上述命令完全一致。
更多参数可通过以下命令查询:
python -m paddle_serving_client.convert --help转换产物目录结构
模型转换完成后,会在当前目录下多出inference_model_server和inference_model_client两个文件夹:
inference_model_server/ ├── unimo_text.pdiparams ├── unimo_text.pdmodel ├── serving_server_conf.prototxt └── serving_server_conf.stream.prototxt inference_model_client/ ├── serving_client_conf.prototxt └── serving_client_conf.stream.prototxt其中服务端目录中的serving_server_conf.prototxt记录了模型的输入输出签名,供 Serving 框架在启动时加载;客户端目录中的serving_client_conf.prototxt则定义了 client 侧的输入输出变量别名(如fetch_list中使用的变量名就是从这里解析出来的)。该目录正是 config.yml 中model_config: ./inference_model_server指向的路径。
pipeline 部署:服务端与客户端的完整实现
paddle_serving目录包含启动 pipeline 服务和发送预测请求的全部代码:
paddle_serving/ ├── config.yml # 启动服务端的配置文件 ├── pipeline_client.py # 发送pipeline预测请求的脚本 └── pipeline_service.py # 启动pipeline服务端的脚本Paddle Serving 的Pipeline 模式是本例采用的核心架构:请求先进入一个read_op,再流转到业务算子UnimoTextOp(名字为text_summarization),算子内部完成「文本 → tokenizer → 模型输入 → 推理 → 后处理 → 摘要结果」的完整处理链。整个流程由服务端脚本 pipeline_service.py 定义,客户端则通过 pipeline_client.py 发起请求。
服务端实现拆解:pipeline_service.py
pipeline_service.py的骨架如下:
from paddle_serving_server.web_service import Op, WebService from paddlenlp.transformers import UNIMOTokenizer from paddlenlp.ops.ext_utils import load class UnimoTextOp(Op): """Op for unimo_text.""" def init_op(self): self.tokenizer = UNIMOTokenizer.from_pretrained("unimo-text-1.0-summary") def preprocess(self, input_dicts, data_id, log_id): # 将请求中的原始文本转为模型输入 # 返回 (input_dict, False, None, "") ... def postprocess(self, input_dicts, fetch_dict, data_id, log_id): # 将 fetch_dict["transpose_0.tmp_0"] 解码为摘要文本 ... class UnimoTextService(WebService): def get_pipeline_response(self, read_op): return UnimoTextOp(name="text_summarization", input_ops=[read_op]) if __name__ == "__main__": # Load FastGeneration lib. load("FastGeneration", verbose=True) service = UnimoTextService(name="text_summarization") service.prepare_pipeline_config("config.yml") service.run_service()关键点逐项说明:
load("FastGeneration", verbose=True):加载 PaddleNLP 的 FastGeneration 自定义算子库(来自paddlenlp.ops.ext_utils)。FastGeneration 是 PaddleNLP 提供的高性能解码加速方案,也是导出静态图模型(unimo_text.pdmodel)时使用的解码实现,因此 Serving 端必须加载对应库才能运行该模型。init_op:加载UNIMOTokenizer.from_pretrained("unimo-text-1.0-summary")。分词器权重会在首次启动时自动下载缓存到~/.paddlenlp/models/目录(启动日志中可以看到Already cached .../unimo-text-1.0-vocab.txt的提示)。preprocess:调用convert_example对每条文本执行tokenizer.gen_encode(...)(生成式任务的编码接口,带add_start_token_for_decoding=True),再通过batchify_fn完成 padding 与 mask 构建,最终产出四个模型输入:input_ids、token_type_ids、attention_mask(注意 shape 为[batch, 1, max_len, max_len],在第二维上扩出 head 维度以适配 Transformer 广播)、seq_len。postprocess:从fetch_dict["transpose_0.tmp_0"]中取回束搜索(beam search)解码结果,对每条样本只取前一半 beam(idx >= len(sample) // 2即跳出),再经postprocess_response从第一个<mask>(该模型的结束符即tokenizer.mask_token_id)处截断,merge_subword合并子词后输出最终摘要文本。transpose_0.tmp_0这个名字来自转换后的 serving 模型输出变量,正与config.yml中fetch_list的配置对应。
配置文件逐项解读:config.yml
config.yml 是服务端的核心配置文件,每个参数的作用如下:
#rpc端口, rpc_port和http_port不允许同时为空。当rpc_port为空且http_port不为空时,会自动将rpc_port设置为http_port+1 rpc_port: 18011 #http端口, rpc_port和http_port不允许同时为空。当rpc_port可用且http_port为空时,不自动生成http_port http_port: 9999 #worker_num, 最大并发数。 #当build_dag_each_worker=True时, 框架会创建worker_num个进程,每个进程内构建grpcSever和DAG #当build_dag_each_worker=False时,框架会设置主线程grpc线程池的max_workers=worker_num worker_num: 10 #build_dag_each_worker, False,框架在进程内创建一条DAG;True,框架会每个进程内创建多个独立的DAG build_dag_each_worker: false dag: #op资源类型, True, 为线程模型;False,为进程模型 is_thread_op: True #重试次数 retry: 1 #使用性能分析, True,生成Timeline性能数据,对性能有一定影响;False为不使用 use_profile: false tracer: interval_s: 10 op: text_summarization: #并发数,is_thread_op=True时,为线程并发;否则为进程并发 concurrency: 11 #当op配置没有server_endpoints时,从local_service_conf读取本地服务配置 local_service_conf: #client类型,包括brpc, grpc和local_predictor.local_predictor不启动Serving服务,进程内预测 client_type: local_predictor #模型路径 model_config: ./inference_model_server #Fetch结果列表,以client_config中fetch_var的alias_name为准,不设置默认取全部输出变量 fetch_list: ["_generated_var_3", "transpose_0.tmp_0"] # device_type, 0=cpu, 1=gpu, 2=tensorRT, 3=arm cpu, 4=kunlun xpu device_type: 1 #计算硬件ID,当devices为""或不写时为CPU预测;当devices为"0", "0,1,2"时为GPU预测,表示使用的GPU卡 devices: "0" #thread_num thread_num: 12 #ir_optim ir_optim: False参数含义与调优要点:
rpc_port/http_port:服务对外暴露的端口。二者不允许同时为空;当rpc_port为空而http_port非空时,框架会自动将rpc_port设为http_port + 1。本例中 gRPC 服务监听18011,HTTP 服务监听9999。客户端默认连接的127.0.0.1:18011即对应rpc_port。worker_num:最大并发数。当build_dag_each_worker=True时框架创建worker_num个进程,每个进程内构建 gRPC Server 与 DAG;为False时则设置主线程 gRPC 线程池的max_workers=worker_num。build_dag_each_worker:False表示框架在进程内创建一条 DAG;True则每个进程内创建多条独立 DAG。dag.is_thread_op:True为线程模型(本配置采用),False为进程模型;dag.retry为算子执行失败的重试次数;dag.use_profile开启后会生成 Timeline 性能数据但会带来一定性能开销。op.text_summarization.concurrency:该 Op 的并发数,线程模型下即线程并发数(示例值为 11)。local_service_conf.client_type: local_predictor:关键配置——local_predictor表示不启动独立 Serving 进程、在服务进程内直接调用本地 Paddle Inference 预测,这是单机部署最简方式。官方还支持brpc、grpc远程调用方式。model_config:指向模型转换阶段生成的./inference_model_server。fetch_list:以 client 配置(serving_client_conf.prototxt)中 fetch_var 的 alias_name 为准,不设置时默认取全部输出变量。本例显式声明了"_generated_var_3"与"transpose_0.tmp_0",后者在postprocess中用于取回解码结果。device_type:0=cpu, 1=gpu, 2=tensorRT, 3=arm cpu, 4=kunlun xpu,示例为 GPU(1)。devices:为""或不写时为 CPU 预测;为"0"、"0,1,2"时表示使用对应编号的 GPU 卡。thread_num:预测线程数(示例为 12)。ir_optim:是否开启 Paddle Inference 的 IR 图优化开关(示例为False)。
server 启动服务
修改好配置文件后,在paddle_serving目录下执行下面命令启动服务:
python pipeline_service.py成功启动服务后,log.txt中会打印类似如下日志:
--- Running analysis [ir_graph_to_program_pass] I0831 12:29:41.132828 28269 analysis_predictor.cc:1035] ======= optimize end ======= I0831 12:29:41.133375 28269 naive_executor.cc:102] --- skip [feed], feed -> seq_len I0831 12:29:41.133384 28269 naive_executor.cc:102] --- skip [feed], feed -> attention_mask I0831 12:29:41.133390 28269 naive_executor.cc:102] --- skip [feed], feed -> token_type_ids I0831 12:29:41.133401 28269 naive_executor.cc:102] --- skip [feed], feed -> input_ids I0831 12:29:41.134040 28269 naive_executor.cc:102] --- skip [_generated_var_3], fetch -> fetch I0831 12:29:41.134049 28269 naive_executor.cc:102] --- skip [gather_tree_0.tmp_0], fetch -> fetch [2022-08-31 12:29:41,138] [ INFO] - Already cached /root/.paddlenlp/models/unimo-text-1.0-summary/unimo-text-1.0-vocab.txt [2022-08-31 12:29:41,161] [ INFO] - tokenizer config file saved in /root/.paddlenlp/models/unimo-text-1.0-summary/tokenizer_config.json [2022-08-31 12:29:41,162] [ INFO] - Special tokens file saved in /root/.paddlenlp/models/unimo-text-1.0-summary/special_tokens_map.json [PipelineServicer] succ init [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success [OP Object] init success 2022/08/31 12:29:41 start proxy service这份日志可以从三个层面辅助排障:
- Paddle Inference 侧:
Running analysis [ir_graph_to_program_pass]与optimize end表示静态图模型加载并完成图优化;skip [feed], feed -> seq_len/attention_mask/token_type_ids/input_ids逐行列出了模型的四个输入变量,fetch -> fetch列出了输出变量,与preprocess构造的输入、fetch_list配置的输出一一对应; - 分词器侧:
Already cached .../unimo-text-1.0-vocab.txt表示UNIMOTokenizer首次运行时已把unimo-text-1.0-summary的词汇表与配置文件下载并缓存到本机; - Serving 框架侧:
[PipelineServicer] succ init、连续多次[OP Object] init success(对应 DAG 中创建的算子对象)以及最后的start proxy service,表示 pipeline 服务已就绪,可以接收请求。
client 发送服务请求
在另一终端(或客户端机器)执行以下命令发送文本摘要服务请求:
python pipeline_client.py注意:执行客户端请求时关闭代理(避免本地代理拦截 gRPC 流量),并根据实际情况修改server_url地址(指向启动服务所在的机器)。
PipelineClient 的核心逻辑如下:
from paddle_serving_server.pipeline import PipelineClient class Runner(object): def __init__(self, server_url: str): self.client = PipelineClient() self.client.connect([server_url]) def Run(self, data): inputs = np.array([i.encode("utf-8") for i in data], dtype=np.object_) ret = self.client.predict(feed_dict={"inputs": inputs}) ... for d, s in zip(data, eval(ret.value[0])): print("Text: ", d) print("Summary: ", s[0]) print("-" * 50) if __name__ == "__main__": server_url = "127.0.0.1:18011" runner = Runner(server_url) texts = [ "雪后的景色可真美丽呀!不管是大树上,屋顶上,还是菜地上,都穿上了一件精美的、洁白的羽绒服。放眼望去,整个世界变成了银装素裹似的,世界就像是粉妆玉砌的一样。", "根据“十个工作日”原则,下轮调价窗口为8月23日24时。……预计跌幅较为有限。", ] runner.Run(texts)这里有几个值得注意的实现细节:
feed_dict的 key 为"inputs",与 pipeline_service.py 中preprocess读取的input_dict["inputs"]严格对应;- 文本以
np.object_类型的 numpy 数组传输,服务端preprocess中[i.decode("utf-8") for i in data]负责还原为 Python 字符串; ret.value[0]是服务端postprocess返回的字符串化结果列表(out_dict["outputs"] = str(results)),因此客户端需要用eval还原为 Python 结构;- 脚本内置了两条中文示例文本作为冒烟测试数据,便于验证服务是否正常:运行后应能看到每条输入打印出对应的摘要文本。
如果希望在本地用几行代码先快速体验摘要效果(无需部署),可以直接使用 PaddleNLP 的 Taskflow 接口,详见 unimo-text 应用 README:
>>> from paddlenlp import Taskflow >>> summarizer = Taskflow("text_summarization") >>> summarizer("雪后的景色可真美丽呀!不管是大树上,屋顶上,还是菜地上,都穿上了一件精美的、洁白的羽绒服。放眼望去,整个世界变成了银装素裹似的,世界就像是粉妆玉砌的一样。") # 输出:'雪后的景色可真美丽呀!'部署链路小结
将整个服务化部署链路串起来,共四个阶段:
- 训练与导出:动态图微调
unimo-text-1.0-summary后,通过 export_model.py 结合 FastGeneration 导出静态图模型inference_model/; - Serving 模型转换:用
python -m paddle_serving_client.convert生成inference_model_server/与inference_model_client/; - 服务端启动:
python pipeline_service.py读取 config.yml,以local_predictor进程内预测方式加载模型并暴露rpc_port: 18011/http_port: 9999; - 客户端请求:
python pipeline_client.py通过PipelineClient连接服务,完成文本摘要请求的收发与结果打印。
作为对比,若不需要对外暴露接口,可以直接走 Paddle Inference 本地推理方案:执行python inference_unimo_text.py --inference_model_dir ../../inference_model即可在 GPU 上完成本地高性能预测,其 inference_unimo_text.py 中的convert_example、batchify_fn、postprocess_response与 Serving 端实现完全一致,可互为对照。
常见问题与调优建议
- 转换前必须先导出静态图模型:Serving 的输入是
unimo_text.pdmodel / unimo_text.pdiparams,它们由 export_model.py 的paddle.jit.to_static+paddle.jit.save产生,跳过这一步直接执行convert会因找不到模型文件而失败; - fetch 变量名必须与服务端配置一致:
postprocess中读取的transpose_0.tmp_0与config.yml的fetch_list需保持一致,若模型导出版本不同导致输出变量名变化,需要以转换产物serving_server_conf.prototxt/serving_client_conf.prototxt中的实际声明为准; - 并发与硬件调优:线上吞吐不足时,可优先调整
worker_num、op.text_summarization.concurrency、thread_num;开启dag.use_profile可生成 Timeline 性能数据辅助定位瓶颈(注意会带来性能损耗);device_type与devices决定使用 CPU 还是具体 GPU 卡; - 客户端网络环境:发送请求前务必关闭系统代理,
server_url需改成服务端所在机器的实际地址(跨机访问时不能使用127.0.0.1); - 多 GPU 场景:
devices: "0,1,2"即可让服务使用多卡推理,配合concurrency可进一步提升多路并发下的整体吞吐。
- 人工智能
- 大模型
- 预训练
- 微调
- LoRA
- RLHF
- 强化学习
- 分布式训练
【免费下载链接】PaddleNLP
Easy-to-use and powerful LLM and SLM library with awesome model zoo.
相关推荐
PaddleDetection 模型 Python Serving 服务化部署实战:基于 Paddle Serving Pipeline 框架的完整部署指南
PaddleDetection 模型 Python Serving 服务化部署实战:基于 Paddle Serving Pipeline 框架的完整部署指南 导
人工智能深度学习计算机视觉基于 Paddle Serving 的层次文本分类在线服务化部署实战指南
基于 Paddle Serving 的层次文本分类在线服务化部署实战指南 本文档介绍如何基于 PaddleNLP https://link.gitcode.co
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP基于 Paddle Serving 的多标签文本分类在线服务化部署(PaddleNLP 实战指南)
基于 Paddle Serving 的多标签文本分类在线服务化部署(PaddleNLP 实战指南) PaddleNLP 多标签分类应用( slm/applica
人工智能大模型预训练微调LoRARLHF强化学习分布式训练模型推理服务推理引擎模型量化模型压缩本地部署NLP
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考