Skip to content

Commit 3c73020

Browse files
authored
Merge pull request #22 from little1d/ch_07-llm-deploy-framework
Ch_07 llm deploy framework
2 parents 959d0c4 + 10d5653 commit 3c73020

File tree

5 files changed

+167
-5
lines changed

5 files changed

+167
-5
lines changed

docs/chapter7/chapter7_1.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,42 @@
1-
# 7.1 框架
1+
# 7.1 推理框架概述
22

3-
  我是正文。
3+
## 7-1-1 大模型部署框架是什么?
44

5-
  markdown语法代码如下:
6-
```markdown
7-
![图1.1 我是图片名称](./images/1_1.jpeg)
5+
在前面的章节中,我们介绍了量化、蒸馏、剪枝等技术,他们从模型本身入手,通过改变模型的结构和参数,使其变得更小、更快、更高效。这个章节我们会了解推理部署框架(如 `Triton Inference Server, TensorFlow Serving`),这些框架的核心目标是优化模型的推理过程,让模型在实际应用中,面对高流量,高并发的复杂情况,也能够快速、稳定地为用户提供服务,毫不夸张的说,大模型推理框架是模型部署的最后一公里。
86

7+
试想你是一位大模型工程师,你和你的团队完成了模型的训练以及各种复杂的优化任务(如量化等),但是此时老板想体验一下你们的成果,让你在他的电脑上演示一下,于是你开始下载 `python`、下载 `vscode`、顺便帮老板充值个魔法…… 一天忙活下来撂下一句“用我电脑吧”。这显然不太优雅。
8+
9+
大模型训练完成后,如何将其封装成一个可调用的服务,是模型部署的最后一公里,也是用户使用的第一公里。推理部署框架提供标准化的 API 接口(如 `HTTP`/`gRPC`),将模型包装成服务,使得开发者可以像调用普通函数一样调用你的模型。
10+
11+
上文只是抛砖引玉,其实大模型推理部署框架对与开发者至关重要,是大模型落地过程中的“全能助手”。他们通过提供模型服务化、高性能推理、多框架支持、资源管理、版本管理、监控日志和安全性保障等功能,帮助开发者轻松应对部署中的各种挑战。选择一款合适的推理部署框架,可以让开发者更专注于模型本身的优化和创新,无需被部署的复杂性所困扰。
12+
13+
## 7-1-2 框架宏观上应该包含哪些组分?
14+
` Tirton-Inference-Server` 是目前较为优秀的部署框架,这里用 Triton 的宏观设计图为大家介绍。
15+
16+
17+
![](./images/figure-1.png)
18+
19+
推理服务框架从宏观上来看一定会分为两个部分,一部分是 `client` 端,另一部分是服务器的架构(`server`)。也就是说,我们说的Triton 其实不等于推理服务,它只是负责在单节点上进行推理服务。
20+
21+
下面我们来仔细看看这张图,整体来说 `server` 端是一个 k8s集群,也就是图片绿色的部分,为什么一定要用集群呢?因为每个 GPU 上可能挂载的服务不同,每一个小的服务,其启动,生命周期的维护,以及弹性扩容等等操作,都需要一个管理系统做到自动化运维,开发者不可能来手动维护每一个小的容器。下面来具体看看 `server` 端 k8s 的主要组成部分:
22+
23+
- 前端负载均衡--`Load banlancer`:这一模块负责将从客户端(`client`)的推理请求,合理有效的分配给后端推理容器。
24+
25+
- 模型仓库持久化容器--`model repo persistent volume`:真正生产环境推理时,可能涉及到很多模型组合完成任务。例如在现代智能客服系统中,它首先要先识别你的意图,判断你是想咨询、投诉还是下单,这里会有一个意图识别模型,接着,它会根据用户输入和上下文生成文本回答,这里会有一个文本生成模型,可能最后还会跟据聊天内容进行检索,给你推荐你可能需要的商品,这里又会有一个推荐模型。这些模型需首先被单独的加入模型仓库(`Model repository`)中,随后经持久化容器,进入容器化的推理服务设备上。如图 `ONNX model、Tensorflow model`以及 `TensorRT engine`就指代不同的模型实体
26+
27+
- `metrics servic`e:监控整个推理服务是否正常,同时也可以对推理时的一些数据进行观测
28+
29+
## 7-1-3 框架设计的核心理念
30+
31+
在简单了解了推理部署模型架构之后,在我们大脑还没有进入太多芝士之前,不妨把自己当成一位合格的大模型工程师,如果要你设计一个框架,你应该考虑哪些问题,来保证框架高效、灵活和可拓展。
32+
33+
### 深度学习框架与 LLM-Deploy 框架解耦合
34+
模型部署前是模型训练,在之前我们肯定听说过许多的深度学习框架(如 `Pytorch``TensorFlow`),这些深度框架底层大有不同,如果要考量训练框架的细节来设计你的框架,是非常困难的,有句古话说得好,自古 `pytorch``tensorflow` 难两全。同时我们也要保证模型的灵活性,避免与训练框架的绑定。
35+
一般的部署框架提供统一的抽象 API接口,不去考虑训练框架的实现,让训练框架开发者自己写代码来适配我们,这样哪怕训练框架后来迭代到什么程度,你都不需要再烦恼。
36+
### Backend 客制化
37+
什么是 Backend,在这里指的是推理后端,通俗点也就是开发者自己开发的模型。从模型的角度出发,模型的应用场景千变万化,客制化也是保证部署框架可拓展性的重要之处。关于详细怎么来客制化会在后面介绍。
38+
### 模型部署全流程覆盖
39+
大模型的部署不仅仅是推理服务的启动,还涉及模型托管、性能监控、生命周期管理等多个环节。
40+
模型托管:集中化管理模型版本,对训练数据、超参数等元信息进行存储,支持模型的热更新和多版本共存
41+
模型性能监控:观测推理系统的推理性能,如吞吐量、错误率等,在模型服务异常时可以返回日志,并展示模型运行时的性能数据,方便开发者进行问题排查。
42+
模型服务生命周期管理:如推理请求的整合、分发、派送,以及最后推理结果的整合

docs/chapter7/chapter7_2.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 推理框架基础功能
2+
现如今,大模型的应用场景越来越多样化,包括 NLP、CV、Audio 等类型的大模型层出不穷,如何让一款框架能够适应这样的场景,并满足开发者与学习者的长期需求呢?在介绍完 7 -1 的内容之后,想必大家对推理框架已经有个大致的了解了。我们可以进一步走进“框架”,站在框架设计者的角度,认识一个完备的框架应该包含哪些功能,并且如何依赖于之前介绍对宏观设计理念和架构完成这些具体功能。
3+
4+
## 7-2-1 支持多模型推理后端
5+
之前也提到过要完成深度学习框架与部署框架解耦和,具体来讲,就是无论深度学习框架如何变更,部署框架自巍然不动。框架需支持多种深度学习框架(如 TensorFlow、PyTorch)和模型格式(如 ONNX、TorchScript、TensorRT),同时允许用户自定义后端(Custom Backend)
6+
框架开发者无需因训练框架不同重建部署环境,而是海纳百川,“插件化”开发模型持久化容器,这样也是出于对未来对考虑,这种“插件”机制确保新框架或硬件出现时,框架可通过扩展完成快速适配。
7+
8+
## 7-2-2 硬件资源支持
9+
首先为基础比较差的小伙伴介绍几个概念
10+
1. 吞吐量:系统在单位时间内处理请求的数量
11+
2. 响应时间:系统对请求做出响应的时间,等于响应时间-请求时间
12+
3. 并行:并行是指系统同时执行多个进程
13+
4. 并发:两个或多个事件在同一时刻发生
14+
对于单用户的系统而言,响应时间可以很好度量系统的性能,这个指标与人对软件性能的主观感受是非常一致的。
15+
> 0.1 秒:让用户感觉到系统在即时响应的界限,这意味着除了显示结果外,不需要专门的反馈。
16+
>
17+
> 1.0秒是让用户思绪保持连续的界限,虽然用户能够感觉到延迟。正常情况下,不需要对0.1秒~1.0秒的延迟做特别的反馈,但用户会失去直接操作数据的流畅感。
18+
>
19+
> 10秒:用户就想要在等待期间执行其它任务了,所以必须在任务完成时给出反馈提示。
20+
21+
但是对于多用户系统,如果只有一个用户使用时,平均响应时间是 t,当你有 n 个用户时,由于系统存在并行能力,用户看到的响应时间通常并不是 n*t,往往比这个值小很多,当然,某些特殊情况下也可能比这个值大很多甚至使系统“故障“(死锁)。在资源配置合理的前提下,总的响应时间并不随用户数的增加而线性增加,所以应该用吞吐量来衡量并发系统的性能。
22+
23+
当模型部署到容器中,系统要对请求完成分布式调度,协调请求在硬件资源上的分配,榨干每一块 GPU 或者是 CPU 的性能,以此来降低推理延迟,提升吞吐量。关于并行的部分,我们都知道,Pytorch 是一个拥有很好并行处理能力的深度学习框架,实际上这是由于 Transformer 的能够很好的并行完成任务,其多头注意力机制,保证了每个注意力头都能独立的处理输入的一部分,实现了将数据并行,模型并行;类似的,对于一个多阶段任务,在推理过程中也可以将每一步放到不同硬件上,实现并行。但如果对于任务/模型过度切片,我们还需要考虑通信开销,本质上是一种 trade-off。
24+
25+
## 7-2-3 推理服务请求队列分发和调度
26+
当一个推理请求从 client 端输入进来,部署框架应该有哪些动作,例如对请求的整合?整合完进行分发?
27+
实际上应该想到,由于模型的不同,任务的不同,对于推理请求的处理方式也应该是不同的,其分发调度策略也有所变化。参考 Triton 框架的设计,它把模型分为以下三种类型
28+
29+
1. 无状态模型(Stateless models):简单来说,对于不同推理请求,它们相互之间没有相互依赖的情况,平时遇到的大部分模型都属于这一类,比如:文本分类、实体抽取、目标检测等
30+
2. 有状态模型(Stateful models):当前模型输出依赖于上一刻模型的状态
31+
3. 集成模型(Ensemble models):由多个模型组成工作流,例如 MinerU,通过将多个模型组合,实现对于论文内容的高度提取
32+
33+
Triton 还提供了一个调度器(Scheduler),可以根据不同的调度策略将推理请求分配到模型实例上执行推理,常见的调度策略有下面几种
34+
35+
1. 默认调度:简单的将推理请求按比例分配
36+
2. 动态调度:将一定时间内的请求聚合成一个批次(batch),但是这种方式只能用于调度无状态的模型。
37+
3. 顺序调度:类似于动态调度,顺序调度也是将推理请求动态聚合,但是顺序调度应用于有状态模型
38+
4. 集成调度:字面意思,只能调度集成模型的请求
39+
40+
## 启发式思考指南
41+
刚介绍了几种推理框架的应该包含的基础功能,但是到网上乍一看,好像大家都有这些功能,那用户应该来如何选择呢,如何判断框架是否完善呢。这一点向来是仁者见仁,因此这里只做一些启发性的引导
42+
1. 计算效率和并行化能力
43+
2. 模块化,插件化和可拓展能力
44+
3. 训练效率和资源利用率:如果使用了分布式训练,是否对通信开销进行分析和处理
45+
4. 最最重要的,用户友好性与详尽的文档:是否提供了一个友好的 api 接口,降低用户对学习曲线,并且一个活跃的社区也有助于提高开发效率

docs/chapter7/chapter7_3.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# 7-3 推理框架的辅助增值功能
2+
> 不属于框架的核心功能,但让用户用起来比较舒服的一些东西
3+
>
4+
> 这里还是用 Triton 举例,妥妥的业界标杆
5+
6+
## 7-3-1 Model Analyzer
7+
[Model Analyzer](https://github.com/triton-inference-server/model_analyzer)是一个 CLI 工具,可以帮助模型训练者在给定的硬件上找到最优的模型配置,并且可以生成报告,帮助你了解不同参数设置,或者其他的 trade-off 操作下,模型的性能变化。
8+
9+
首先,对于 Triton-inference-server 支持的模型类型,model_analyzer 也都是支持的,如下所示:
10+
1. Single/Multi-Model
11+
2. Ensemble
12+
3. BLS
13+
4. LLM(大语言模型)
14+
15+
此外,model_analyzer 通过参数搜索完成最优模型配置的寻找,对于参数的搜索,model analyzer 集成了几种 Search Modes,帮助我们简化调参过程
16+
> 参考链接:https://github.com/triton-inference-server/model_analyzer/blob/main/docs/config_search.md
17+
18+
1. Default Search Mode:对于不同模型类型,其 Default Search Mode 是不同的,例如 single 模型而言,其 default search mode 是 Brute Force Search,但是对于 Multi-model 而言,其 Default search mode是 Quick Search
19+
2. Optuna Seach:使用一些超参数优化框架进行启发式扫描,来查找最佳配置
20+
3. Quick Search:快速搜索,运用一些启发式的算法,稀疏的对参数进行搜索
21+
4. Automatic Brute Search:自动暴力搜索
22+
5. Manual Brute Search:手动暴力搜索,手动扫描模型配置中指定的参数
23+
24+
## 7-3-2 Model Navigator
25+
[Model Navigator](https://github.com/triton-inference-server/model_navigator)是一个推理工具包,简化了模型的移动工作,并且提供了很多 [pipeline示例](https://github.com/triton-inference-server/model_navigator/tree/main/examples)
26+
27+
它可以自动执行几个关键步骤,包含模型导出、转换,性能测试和分析,并且可以将生成的优化模型轻松的部署到 Triton Inference Server上,下面简单介绍一下它的 Features
28+
29+
- Model Export and Conversion:自动执行各种格式之间的模型导出和转换过程
30+
- Correctness Testing:正确性测试,确保转换后的模型笨狗产生正确的输出,并进行验证
31+
- Models Depolyment:通过专用的 API 在 PyTrition 和 Triton Inference Server 上自动部署模型或 Pipelines
32+
- Pipelines Optimazation:管道功能,优化了像 Stable Diffusion 和 Whisper 这样的 Pytorch 模型的代码流程
33+
```python
34+
import model_navigator as nav
35+
from transformers.modeling_outputs import BaseModelOutputWithPooling
36+
from diffusers import DPMSolverMultistepScheduler, StableDiffusionPipeline
37+
38+
39+
def get_pipeline():
40+
# Initialize Stable Diffusion pipeline and wrap modules for optimization
41+
pipe = StableDiffusionPipeline.from_pretrained("stabilityai/stable-diffusion-2-1")
42+
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
43+
pipe = pipe.to("cuda")
44+
pipe.text_encoder = nav.Module(
45+
pipe.text_encoder,
46+
name="clip",
47+
output_mapping=lambda output: BaseModelOutputWithPooling(**output),
48+
)
49+
pipe.unet = nav.Module(
50+
pipe.unet,
51+
name="unet",
52+
)
53+
pipe.vae.decoder = nav.Module(
54+
pipe.vae.decoder,
55+
name="vae",
56+
)
57+
return pipe
58+
```
59+
使用 nav.Module包裹模型组件,就可以在数据上完成端到端的优化。例如下面我们准备一个简单的数据加载器:
60+
```python
61+
def get_dataloader():
62+
# 第一个元素是 batch size
63+
return [(1, "a photo of an astronaut riding a horse on mars")]
64+
```
65+
接着,我们执行模型优化并显式的加载最高性能版本
66+
```python
67+
pipe = get_pipeline()
68+
dataloader = get_dataloader()
69+
nav.optimize(pipe, dataloader)
70+
nav.load
71+
```
72+
同样的,也可以使用 pipeline 直接进行优化模型的推理
73+
```python
74+
pipe.to("cuda")
75+
images = pipe(["a photo of an astronaut riding a horse on mars"])
76+
image = images[0][0]
77+
image.save("an_astronaut_riding_a_horse.png")
78+
```
79+
80+
81+
82+
83+

docs/chapter7/images/figure-1.png

366 KB
Loading

docs/chapter7/images/figure-2.png

136 KB
Loading

0 commit comments

Comments
 (0)