mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2025-12-24 13:28:13 +08:00
Sync v2.0 version of code to github repo
This commit is contained in:
25
docs/zh/features/chunked_prefill.md
Normal file
25
docs/zh/features/chunked_prefill.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Chunked Prefill 与 128K 长文推理部署
|
||||
|
||||
Chunked Prefill 采用分块策略,将预填充(Prefill)阶段请求拆解为小规模子任务,与解码(Decode)请求混合批处理执行。可以更好地平衡计算密集型(Prefill)和访存密集型(Decode)操作,优化GPU资源利用率,减少单次Prefill的计算量和显存占用,从而降低显存峰值,避免显存不足的问题。
|
||||
|
||||
在启用 Chunked Prefill 机制后,调度策略采用以下优先级规则:
|
||||
|
||||
- 解码请求优先处理:系统会优先将所有待处理的解码请求(Decode)进行批量整合,确保生成类任务获得即时响应能力。
|
||||
- 弹性分块机制:当存在等待处理的预填充任务时,系统会在 `max_num_batched_tokens` 预算范围内(即当前批次可容纳的最大 token 数量)调度预填充(Prefill)操作。若单个预填充请求的 token 量超过剩余预算容量,系统会自动将其拆分为多个符合预算限制的子块(Chunks)。
|
||||
|
||||
通过 Chunked Prefill 机制,可以有效降低 Token 间时延(Inter-Token Latency),同时优化显存占用,支持更大长度的输入。
|
||||
|
||||
更多信息请查阅相关论文 [https://arxiv.org/pdf/2308.16369](https://arxiv.org/pdf/2308.16369)。
|
||||
|
||||
## 使用与调优
|
||||
|
||||
在 FastDeploy 中开启 Chunked Prefill 时,可以通过 `max_num_batched_tokens` 参数调节服务性能:
|
||||
|
||||
- 较小的 `max_num_batched_tokens` 可以获得更好的 Token 间时延(Inter-Token Latency)。
|
||||
- 更大的 `max_num_batched_tokens` 可以获得更好的首 Token 时延(Time To First Token)。
|
||||
- 建议配置 `max_num_batched_tokens > 8096`,当在算力充足的硬件上部署小模型时,可以进一步增大 `max_num_batched_tokens` 来获得更高的吞吐量。
|
||||
|
||||
## 适用场景
|
||||
|
||||
1. 长文推理部署,如 128K 长文推理部署等场景。可以通过 Chunked prefill 技术降低显存峰值,避免显存不足的问题。
|
||||
2. 生成类任务,通过 Chunked prefill 技术可以降低 Token 间时延(Inter-Token Latency),提高生成类任务的响应速度。
|
||||
173
docs/zh/features/disaggregated.md
Normal file
173
docs/zh/features/disaggregated.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# 分离式部署
|
||||
|
||||
大模型推理分为两个部分Prefill和Decode阶段,分别为计算密集型(Prefill)和计算密集型(Decode)两部分。将Prefill 和 Decode 分开部署在一定场景下可以提高硬件利用率,有效提高吞吐,降低整句时延,
|
||||
|
||||
* Prefill阶段:处理输入的全部Token(如用户输入的Prompt),完成模型的前向传播(Forward),生成首token。
|
||||
* Decode阶段:从生成第首token后,采用自回归一次生成一个token,直到生成到stop token结束;设输出N✖️token,Decode阶段需要执行(N-1)次前向传播,只能串行执行,并且在生成过程中,需要关注的token数越来越多,计算量也逐渐增大。
|
||||
|
||||
分离式部署核心是将Prefill 和 Decode 部署在不同的计算资源上,提高各自的利用率。要想实现分离式部署,不可避免的需要考虑Prefill 和 Decode 之间的通信问题。
|
||||
在实际推理过程中Prefill 需要将其计算得到的KV Cache 传输至Decode 实例,Decode 读取KV Cache 进行续推。
|
||||
|
||||
## KV Cache 传输方式
|
||||
针对KV Cache 传输我们提供了2种传输方式,分别针对单机内与多机间的场景。
|
||||
|
||||
### 单机内传输
|
||||
通过cudaMemcpyPeer进行单机内两个GPU之间KV Cache传输,时延低且吞吐高
|
||||
|
||||
### 多机间传输
|
||||
针对多机之间的传输,通过高速网络RDMA传输KV Cache。 针对RDMA传输我们提供了高速传输的网络库`rdma_comm` 实现跨机的KV Cache传输。
|
||||
|
||||
## PD 分离调度
|
||||

|
||||
在全局调度器的基础上,FastDeploy 支持 PD 分离调度策略,专为大语言模型推理场景设计,将推理流程中的两个阶段解耦:
|
||||
* Prefill 阶段:构建 KV 缓存,计算密集,显存占用高但延迟低;
|
||||
* Decode 阶段:进行自回归解码,过程串行、耗时长但显存占用低。
|
||||
|
||||
多实例情况下,每收到一条请求需要根据不同的策略将请求分配到不同的Prefill实例和Decode实例。通过角色分离(prefill 节点负责接收并处理请求,decode节点完成后续生成),可以更细粒度地控制资源分配、提高吞吐量与 GPU 利用率。
|
||||
|
||||
|
||||
## 使用说明
|
||||
|
||||
|
||||
### 单机分离式部署
|
||||
|
||||
|
||||
#### 在线推理服务
|
||||
使用如下命令进行服务部署
|
||||
|
||||
**prefill 实例**
|
||||
|
||||
```bash
|
||||
export FD_LOG_DIR="log_prefill"
|
||||
export CUDA_VISIBLE_DEVICES=0,1,2,3
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ERNIE-4.5-300B-A47B-BF16 \
|
||||
--port 8180 --metrics-port 8181 \
|
||||
--engine-worker-queue-port 8182 \
|
||||
--cache-queue-port 8183 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--splitwise-role "prefill"
|
||||
```
|
||||
|
||||
**decode 实例**
|
||||
|
||||
```bash
|
||||
export FD_LOG_DIR="log_decode"
|
||||
export CUDA_VISIBLE_DEVICES=4,5,6,7
|
||||
# 注意innode-prefill-ports指定为Prefill服务的engine-worker-queue-port
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ERNIE-4.5-300B-A47B-BF16 \
|
||||
--port 8184 --metrics-port 8185 \
|
||||
--engine-worker-queue-port 8186 \
|
||||
--cache-queue-port 8187 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--innode-prefill-ports 8182 \
|
||||
--splitwise-role "decode"
|
||||
```
|
||||
|
||||
注意在请求单机PD分离服务时,**用户需请求Decode服务的端口**。
|
||||
|
||||
#### 离线推理服务
|
||||
|
||||
参考`fastdeploy/demo` 目录下 `offline_disaggregated_demo.py` 示例代码,进行离线推理服务部署
|
||||
|
||||
### 多机分离式部署
|
||||
|
||||
|
||||
#### 前置依赖 Redis
|
||||
- 使用`conda`安装
|
||||
```bash
|
||||
# 安装
|
||||
conda install redis
|
||||
# 启动
|
||||
nohup redis-server > redis.log 2>&1 &
|
||||
```
|
||||
|
||||
- 使用`apt`安装
|
||||
```bash
|
||||
# 安装
|
||||
sudo apt install redis-server -y
|
||||
# 启动
|
||||
sudo systemctl start redis-server
|
||||
```
|
||||
|
||||
- 使用`yum`安装
|
||||
```bash
|
||||
# 安装
|
||||
sudo yum install redis -y
|
||||
# 启动
|
||||
sudo systemctl start redis
|
||||
```
|
||||
|
||||
#### 在线推理服务
|
||||
|
||||
多机部署时需要确认当前网卡是否支持RDMA,并且需要集群中所有节点网络互通。
|
||||
|
||||
**注意**:
|
||||
* `KVCACHE_RDMA_NICS` 指定当前机器的RDMA网卡,多个网卡用逗号隔开。
|
||||
|
||||
**prefill 实例**
|
||||
|
||||
```bash
|
||||
export FD_LOG_DIR="log_prefill"
|
||||
export CUDA_VISIBLE_DEVICES=0,1,2,3
|
||||
export KVCACHE_RDMA_NICS="mlx5_2,mlx5_3,mlx5_4,mlx5_5"
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ERNIE-4.5-300B-A47B-BF16 \
|
||||
--port 8180 --metrics-port 8181 \
|
||||
--engine-worker-queue-port 8182 \
|
||||
--cache-queue-port 8183 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--cache-transfer-protocol "rdma,ipc" \
|
||||
--rdma-comm-ports "7671,7672,7673,7674" \
|
||||
--pd-comm-port "2334" \
|
||||
--splitwise-role "prefill" \
|
||||
--scheduler-name "splitwise" \
|
||||
--scheduler-host "127.0.0.1" \
|
||||
--scheduler-port 6379 \
|
||||
--scheduler-ttl 9000
|
||||
```
|
||||
|
||||
**decode 实例**
|
||||
|
||||
```bash
|
||||
export FD_LOG_DIR="log_decode"
|
||||
export CUDA_VISIBLE_DEVICES=4,5,6,7
|
||||
export KVCACHE_RDMA_NICS="mlx5_2,mlx5_3,mlx5_4,mlx5_5"
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ERNIE-4.5-300B-A47B-BF16 \
|
||||
--port 8184 --metrics-port 8185 \
|
||||
--engine-worker-queue-port 8186 \
|
||||
--cache-queue-port 8187 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--scheduler-name "splitwise" \
|
||||
--cache-transfer-protocol "rdma,ipc" \
|
||||
--rdma-comm-ports "7671,7672,7673,7674" \
|
||||
--pd-comm-port "2334" \
|
||||
--scheduler-host "127.0.0.1" \
|
||||
--scheduler-port 6379 \
|
||||
--scheduler-ttl 9000
|
||||
--splitwise-role "decode"
|
||||
```
|
||||
|
||||
### 参数说明
|
||||
|
||||
* --splitwise-role: 指定当前服务为prefill还是decode
|
||||
* --cache-queue-port: 指定cache服务的端口,用于prefill和decode服务通信
|
||||
|
||||
#### 单机参数说明
|
||||
|
||||
* --inner-prefill-ports: 仅需Decode实例填写,指定需要连接的prefill实例的端口列表
|
||||
|
||||
#### 多机参数说明
|
||||
* --cache-transfer-protocol: 指定KV Cache传输协议,支持ipc和rdma,默认ipc
|
||||
* --scheduler-name: PD分离情况下为splitwise
|
||||
* --scheduler-host: 连接的redis地址
|
||||
* --scheduler-port: 连接的redis端口
|
||||
* --scheduler-ttl: 指定redis的ttl时间,单位为秒
|
||||
* --pd-comm-port: 指定pd通信的端口
|
||||
* --rdma-comm-ports: 指定RDMA通信的端口,多个端口用逗号隔开,数量与卡数一致
|
||||
BIN
docs/zh/features/images/GlobalScheduler.png
Normal file
BIN
docs/zh/features/images/GlobalScheduler.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 444 KiB |
BIN
docs/zh/features/images/LocalScheduler.png
Normal file
BIN
docs/zh/features/images/LocalScheduler.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 329 KiB |
BIN
docs/zh/features/images/disaggregated.png
Normal file
BIN
docs/zh/features/images/disaggregated.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
69
docs/zh/features/load_balance.md
Normal file
69
docs/zh/features/load_balance.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 全局调度器: 多实例负载均衡
|
||||
|
||||
## 设计方案
|
||||
集群中的各个节点根据自身负载情况,空闲时主动从其他节点偷取任务,然后将任务的执行结果推送回原节点。
|
||||
|
||||
### 全局调度器解决了什么问题?
|
||||

|
||||
|
||||
在常规负载均衡策略中,集群按照轮询策略分发请求,可以确保每个推理实例接受到的请求个数是均匀的。
|
||||
|
||||
在LLM场景中,每个请求的处理时间和请求各自的输入输出token数有关。即使请求均匀的分发到每个推理实例上,每个推理实例完成推理的时间也会相差很大。
|
||||
|
||||
所以,我们希望通过全局调度器进一步优化集群负载。
|
||||
|
||||
### 全局调度器是如何工作的?
|
||||

|
||||
|
||||
如上图所示,节点1,2,n均收到了3个请求。在T时刻,节点1已经完成了对所有请求的处理,节点2和节点n正在处理第二个请求(各自的请求队列中还剩余1个请求)。此时,节点1窃取到了节点2的一个请求进行处理,并将结果推送回节点2的响应队列中。
|
||||
|
||||
全局调度器在集群内完成二次负载均衡,可以有效提高集群整体资源利用率,降低TTFT(Time To First Token)。
|
||||
|
||||
## 如何使用全局调度器
|
||||
|
||||
### 前置依赖 Redis
|
||||
- 使用`conda`安装
|
||||
```bash
|
||||
# 安装
|
||||
conda install redis
|
||||
# 启动
|
||||
nohup redis-server > redis.log 2>&1 &
|
||||
```
|
||||
|
||||
- 使用`apt`安装
|
||||
```bash
|
||||
# 安装
|
||||
sudo apt install redis-server -y
|
||||
# 启动
|
||||
sudo systemctl start redis-server
|
||||
```
|
||||
|
||||
- 使用`yum`安装
|
||||
```bash
|
||||
# 安装
|
||||
sudo yum install redis -y
|
||||
# 启动
|
||||
sudo systemctl start redis
|
||||
```
|
||||
|
||||
### 启动FastDeploy
|
||||
```bash
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--port 8801 \
|
||||
--metrics-port 8802 \
|
||||
--engine-worker-queue-port 8803 \
|
||||
--scheduler-name global \
|
||||
--scheduler-ttl 900 \
|
||||
--scheduler-host "127.0.0.1" \
|
||||
--scheduler-port 6379 \
|
||||
--scheduler-db 0 \
|
||||
--scheduler-password "" \
|
||||
--scheduler-topic "default" \
|
||||
--scheduler-min-load_score 3 \
|
||||
--scheduler-load-shards-num 1
|
||||
```
|
||||
[启动参数说明](../online_serving/scheduler.md)
|
||||
|
||||
可以将上述启动命令在多个机器执行,启动多个推理实例(如果是在一个机器中启动多个推理实例,注意端口不要冲突)。
|
||||
|
||||
集群外部的负载均衡可以使用`Nginx`进行搭建,集群内部的负载均衡由全局调度器负责。
|
||||
40
docs/zh/features/prefix_caching.md
Normal file
40
docs/zh/features/prefix_caching.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Prefix Caching
|
||||
|
||||
Prefix Caching(前缀缓存)是一种优化生成式模型推理效率的技术,核心思想是通过缓存输入序列的中间计算结果(KV Cache),避免重复计算,从而加速具有相同前缀的多个请求的响应速度。
|
||||
|
||||
**工作原理**
|
||||
|
||||
前缀识别:当多个请求共享相同的输入前缀(如提示词或上下文开头部分),系统会缓存该前缀对应的中间状态(KV Cache)。
|
||||
|
||||
增量计算:对于后续请求,只需计算新增部分(如用户追加的输入)并复用缓存的中间结果,显著减少计算量。
|
||||
|
||||
|
||||
## 服务化部署开启 Prefix Caching
|
||||
|
||||
启动服务增加以下参数 `enable-prefix-caching`,默认只开启一级缓存(GPU 缓存)。
|
||||
|
||||
若需要开启 CPU 缓存,需要指定参数 `swap-space`, 指定开启的 CPU Cache 的空间大小,单位为 GB。具体大小根据加载完模型机器可用内存大小设置。
|
||||
|
||||
> 注:ERNIE-4.5-VL 多模态模型暂不支持开启 prefix caching。
|
||||
|
||||
具体参数说明可参考文档[参数说明](../parameters.md)。
|
||||
|
||||
启动示例:
|
||||
|
||||
```shell
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model "baidu/ERNIE-4.5-21B-A3B-Paddle" \
|
||||
--port 8180 --engine-worker-queue-port 8181 \
|
||||
--metrics-port 8182 \
|
||||
--cache-queue-port 8183 \
|
||||
--enable-prefix-caching \
|
||||
--swap-space 50 \
|
||||
--max-model-len 8192 \
|
||||
--max-num-seqs 32
|
||||
```
|
||||
|
||||
## 离线推理开启Prefix Caching
|
||||
|
||||
FastDeploy 启动时设置 `enable_prefix_caching=True`,CPU Cache 根据机器内存选择开启 `swap_space`。
|
||||
|
||||
提供了测试示例 `demo/offline_prefix_caching_demo.py`。
|
||||
76
docs/zh/features/reasoning_output.md
Normal file
76
docs/zh/features/reasoning_output.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# 思考链内容
|
||||
|
||||
思考模型在输出中返回 `reasoning_content` 字段,表示思考链内容,即得出最终结论的思考步骤.
|
||||
|
||||
##目前支持思考链的模型
|
||||
| 模型名称 | 解析器名称 | 默认开启思考链 |
|
||||
|---------------|-------------|---------|
|
||||
| ernie-45-vl | ernie-45-vl | ✓ |
|
||||
| ernie-lite-vl | ernie-45-vl | ✓ |
|
||||
|
||||
思考模型需要指定解析器,以便于对思考内容进行解析. 通过`enable_thinking=False` 参数可以关闭模型思考模式.
|
||||
|
||||
可以支持思考模式开关的接口:
|
||||
1. OpenAI 服务中 `/v1/chat/completions` 请求.
|
||||
2. OpenAI Python客户端中 `/v1/chat/completions` 请求.
|
||||
3. Offline 接口中 `llm.chat`请求.
|
||||
|
||||
同时在思考模型中,支持通过```reasoning_max_tokens```控制思考内容的长度,在请求中添加```metadata={"reasoning_max_tokens": 1024}```即可。
|
||||
|
||||
|
||||
### 快速使用
|
||||
在启动模型服务时, 通过`--reasoning-parser`参数指定解析器名称.
|
||||
该解析器会解析思考模型的输出, 提取`reasoning_content`字段.
|
||||
```bash
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model /path/to/your/model \
|
||||
--enable-mm \
|
||||
--tensor-parallel-size 8 \
|
||||
--port 8192 \
|
||||
--quantization wint4 \
|
||||
--reasoning-parser ernie-45-vl
|
||||
```
|
||||
接下来, 向模型发送 `chat completion` 请求
|
||||
```bash
|
||||
curl -X POST "http://0.0.0.0:8192/v1/chat/completions" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": [
|
||||
{"type": "image_url", "image_url": {"url": "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_images/example2.jpg"}},
|
||||
{"type": "text", "text": "图中的文物属于哪个年代"}
|
||||
]}
|
||||
],
|
||||
"metadata": {"enable_thinking": true}
|
||||
}'
|
||||
|
||||
```
|
||||
字段`reasoning_content`包含得出最终结论的思考步骤,而`content`字段包含最终结论。
|
||||
|
||||
### 流式会话
|
||||
在流式会话中, `reasoning_content`字段会可以在`chat completion response chunks`中的 `delta` 中获取
|
||||
```python
|
||||
from openai import OpenAI
|
||||
# Set OpenAI's API key and API base to use vLLM's API server.
|
||||
openai_api_key = "EMPTY"
|
||||
openai_api_base = "http://localhost:8192/v1"
|
||||
client = OpenAI(
|
||||
api_key=openai_api_key,
|
||||
base_url=openai_api_base,
|
||||
)
|
||||
chat_response = client.chat.completions.create(
|
||||
messages=[
|
||||
{"role": "user", "content": [ {"type": "image_url", "image_url": {"url": "https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_images/example2.jpg"}},
|
||||
{"type": "text", "text": "图中的文物属于哪个年代"}]}
|
||||
],
|
||||
model="vl",
|
||||
stream=True,
|
||||
metadata={"enable_thinking": True}
|
||||
)
|
||||
for chunk in chat_response:
|
||||
if chunk.choices[0].delta is not None:
|
||||
print(chunk.choices[0].delta, end='')
|
||||
print("\n")
|
||||
|
||||
```
|
||||
|
||||
120
docs/zh/features/speculative_decoding.md
Normal file
120
docs/zh/features/speculative_decoding.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 🔮 投机解码
|
||||
本项目基于 PaddlePaddle 实现了高效的 **投机解码(Speculative Decoding)** 推理框架,支持多 Token 预测(Multi-token Proposing, MTP),用于加速大语言模型(LLM)的生成,显著降低时延并提升吞吐量。
|
||||
|
||||
## ✅ 投机解码方法支持
|
||||
### ✅ 支持列表
|
||||
|
||||
- **Ngram**
|
||||
|
||||
- **MTP (Multi-Token Prediction)**
|
||||
- ✅ 已支持:TP 切分
|
||||
- ✅ 已支持:共享前缀
|
||||
- ✅ 已支持:单机 TP 切分 + PD 分离
|
||||
- ⏳ 即将支持:EP + DP + PD 分离
|
||||
- ⏳ 即将支持:兼容 Chunk Prefill
|
||||
- ⏳ 即将支持:多层 MTP layer
|
||||
|
||||
---
|
||||
|
||||
### ⏳ 规划中
|
||||
|
||||
- Draft Model
|
||||
- Eagle
|
||||
- Hydra
|
||||
- Medusa
|
||||
- ...
|
||||
|
||||
## ⚙️ 高效投机解码框架设计
|
||||
- **Attention机制**:采用 [Cascade Append Attention](https://flashinfer.ai/2024/02/02/cascade-inference.html) 的 Attention 机制,支持变长查询统一处理,一次前向推理即可完成所有验证。此外,我们对 Kernel 实现进行了深度定制,以最大化 Tensor Core 的利用率,并在高并发场景下仍然保持高吞吐。
|
||||
- **虚拟填充机制**:采用虚拟填充快速定位输出 Token 的批次 ID,避免了高开销的数据拷贝与切片操作。
|
||||
- **并行采样与验证**:我们开发了多个融合 Cuda Kernel,用于同时执行采样与验证操作。该 Kernel 支持对每个 batch 样本进行并行处理,避免了显式循环的开销。
|
||||
- **高效 DraftModel/MTP 框架**:开发多个融合 Cuda Kernel,统一完成模型类方法的前后处理,相比传统的循环、切片方法,性能高效且易维护
|
||||
|
||||
## 🔧 参数说明
|
||||
- `method`: 解码策略,可选值为 `"mtp"` 或 `"ngram"`
|
||||
- `num_speculative_tokens`: 每轮预测的 Token 数,最大支持 5(当前 MTP 仅支持 1)
|
||||
- `model`: 若选择 MTP,则需指定 MTP 模型路径
|
||||
- `quantization`: 模型量化方式,推荐使用 `wint8`
|
||||
- `batch_size`: 当前支持最大值为 256
|
||||
|
||||
## 🚀 使用 Multi-Token-Prediction(MTP) 解码
|
||||
详见论文:[DeepSeek-V3](https://arxiv.org/pdf/2412.19437)
|
||||
### TP 并行部署
|
||||
> 使用 4×H100,量化方式选择 WINT4
|
||||
> 配置文件:`benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml`
|
||||
|
||||
```
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ${path_to_main_model} \
|
||||
--tensor-parallel-size 4 \
|
||||
--config ${path_to_FastDeploy}benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml \
|
||||
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": "${path_to_mtp_model}"}'
|
||||
```
|
||||
### PD 分离式部署(1P1D)
|
||||
> 在8×H100上部署1P1D,P、D节点 分别使用 4×H100;量化方式选择 WINT4
|
||||
> 与常规 PD 分离部署一致,仅需替换配置文件并新增 speculative_config
|
||||
详情请参考[PD分离式部署](./disaggregated.md)。
|
||||
- P 节点(Prefill)
|
||||
|
||||
> 配置文件: `benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml`
|
||||
```
|
||||
export FD_LOG_DIR="log_prefill"
|
||||
rm -rf ${FD_LOG_DIR}
|
||||
export CUDA_VISIBLE_DEVICES=0,1,2,3
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ${path_to_main_model} \
|
||||
--port 8180 \
|
||||
--metrics-port 8181 \
|
||||
--engine-worker-queue-port 8182 \
|
||||
--cache-queue-port 8183 \
|
||||
--workers 2 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--splitwise-role "prefill" \
|
||||
--scheduler-name "splitwise" \
|
||||
--scheduler-host "127.0.0.1" \
|
||||
--scheduler-port 6379 \
|
||||
--scheduler-ttl 9000 \
|
||||
--scheduler-topic mtp \
|
||||
--config ${path_to_FastDeploy}/benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml \
|
||||
--scheduler-password "scheduler_mtp" \
|
||||
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": ""${path_to_mtp_model}"}' &
|
||||
```
|
||||
- D 节点(Decode)
|
||||
|
||||
> 配置文件: `benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-decode.yaml`
|
||||
```
|
||||
export FD_LOG_DIR="log_prefill"
|
||||
rm -rf ${FD_LOG_DIR}
|
||||
export CUDA_VISIBLE_DEVICES=0,1,2,3
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ${path_to_main_model} \
|
||||
--port 8180 \
|
||||
--metrics-port 8181 \
|
||||
--engine-worker-queue-port 8182 \
|
||||
--cache-queue-port 8183 \
|
||||
--workers 2 \
|
||||
--tensor-parallel-size 4 \
|
||||
--quantization wint4 \
|
||||
--splitwise-role "prefill" \
|
||||
--scheduler-name "splitwise" \
|
||||
--scheduler-host "127.0.0.1" \
|
||||
--scheduler-port 6379 \
|
||||
--scheduler-ttl 9000 \
|
||||
--scheduler-topic mtp \
|
||||
--config ${path_to_FastDeploy}/benchmarks/yaml/eb45t-32k-wint4-mtp-tp4-prefill.yaml \
|
||||
--scheduler-password "scheduler_mtp" \
|
||||
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": ""${path_to_mtp_model}"}' &
|
||||
```
|
||||
|
||||
## 🧠 使用 Ngram 解码
|
||||
该算法通过 n-gram 窗口从 prompt 和已生成的 Token 中进行匹配生成草稿 Token,适合输入和输出有很大 overlap 的场景如代码编辑、文档查询等查看论文地址。
|
||||
> 使用 4×H100;量化方式选择 WINT4
|
||||
> 配置文件:benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml
|
||||
```
|
||||
python -m fastdeploy.entrypoints.openai.api_server \
|
||||
--model ${path_to_main_model} \
|
||||
--tensor-parallel-size 4 \
|
||||
--config ${path_to_FastDeploy}benchmarks/yaml/eb45t-32k-wint4-mtp-h100-tp4.yaml \
|
||||
--speculative-config '{"method": "mtp", "num_speculative_tokens": 1, "model": "${mtp_model_path}"}'
|
||||
```
|
||||
332
docs/zh/features/structured_outputs.md
Normal file
332
docs/zh/features/structured_outputs.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# Structured Outputs
|
||||
|
||||
## 概述
|
||||
|
||||
Structured Outputs 是指通过预定义格式约束,使大模型生成内容严格遵循指定结构。该功能可显著提升生成结果的可控性,适用于需要精确格式输出的场景(如API调用、数据解析、代码生成等),同时支持动态语法扩展,平衡灵活性与规范性。
|
||||
|
||||
FastDeploy 支持使用 [XGrammar](https://xgrammar.mlc.ai/docs/) 后端生成结构化输出。
|
||||
|
||||
支持输出格式
|
||||
|
||||
- `json格式`: 输出内容为标准的 JSON 格式
|
||||
- `正则表达式格式(regex)`: 精确控制文本模式(如日期、邮箱、身份证号等),支持复杂正则表达式语法
|
||||
- `选项格式(choice)`: 输出严格限定在预定义选项中
|
||||
- `语法格式(grammar)`: 使用扩展 BNF 语法定义复杂结构,支持字段名、类型及逻辑关系的联合约束
|
||||
- `结构标签格式(structural_tag)`: 通过标签树定义结构化输出,支持嵌套标签、属性校验及混合约束
|
||||
|
||||
## 使用方式
|
||||
|
||||
服务启动时,可以通过 `--guided-decoding-backend` 参数指定期望使用的后端,如果指定 `auto`, FastDeploy 会自动选择合适的后端。
|
||||
|
||||
### OpenAI 接口
|
||||
|
||||
FastDeploy 支持 OpenAI 的 [Completions](https://platform.openai.com/docs/api-reference/completions) 和 [Chat Completions](https://platform.openai.com/docs/api-reference/chat) API,OpenAI 通过 `response_format` 参数指定 Structured Outputs 的输出格式。
|
||||
|
||||
FastDeploy 支持通过指定 `json_object` 来保证输出为json格式,如果想指定 JSON 内部具体参数名、类型等信息,可以通过 `json_schema` 来指定详细信息,详细使用方法可以参考示例。
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "生成一个包含以下信息的JSON对象:中国四大发明名称、发明朝代及简要描述(每项不超过50字)",
|
||||
}
|
||||
],
|
||||
response_format={"type": "json_object"}
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
{"inventions": [{"name": "造纸术", "dynasty": "东汉", "description": "蔡伦改进造纸术,用树皮、麻头等为原料,成本低廉,推动文化传播。"}, {"name": "印刷术", "dynasty": "唐朝", "description": "雕版印刷术在唐朝出现,后毕昇发明活字印刷术,提高印刷效率。"}, {"name": "火药", "dynasty": "唐朝", "description": "古代炼丹家偶然发明,后用于军事,改变了战争方式和格局。"}, {"name": "指南针", "dynasty": "战国", "description": "最初称司南,后发展为指南针,为航海等提供方向指引。"}]}
|
||||
```
|
||||
|
||||
以下示例要求模型返回一个 book 信息的 json 数据,json 数据中必须包含`author、title、genre`三个字段,且`genre`必须在给定 `BookType` 中。
|
||||
|
||||
```python
|
||||
import openai
|
||||
from pydantic import BaseModel
|
||||
from enum import Enum
|
||||
|
||||
class BookType(str, Enum):
|
||||
romance = "Romance"
|
||||
historical = "Historical"
|
||||
adventure = "Adventure"
|
||||
mystery = "Mystery"
|
||||
dystopian = "Dystopian"
|
||||
|
||||
class BookDescription(BaseModel):
|
||||
author: str
|
||||
title: str
|
||||
genre: BookType
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "生成一个JSON,描述一本中国的著作,要包含作者、标题和书籍类型。",
|
||||
}
|
||||
],
|
||||
response_format={
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
"name": "book-description",
|
||||
"schema": BookDescription.model_json_schema()
|
||||
},
|
||||
},
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
{"author": "曹雪芹", "title": "红楼梦", "genre": "Historical"}
|
||||
```
|
||||
|
||||
`structural_tag` 可以提取输入内容中的重点信息,并调用指定方法,返回预定义的结构化输出。以下示例展示了通过 `structural_tag` 获取指定时区并输出格式化结果。
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
content_str = """
|
||||
你有以下函数可以调用:
|
||||
|
||||
{
|
||||
"name": "get_current_date",
|
||||
"description": "根据给定的时区获取当前日期和时间",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": "获取当前日期和时间的时区, 例如: Asia/Shanghai",
|
||||
}
|
||||
},
|
||||
"required": ["timezone"],
|
||||
}
|
||||
}
|
||||
|
||||
如果你选择只调用函数,请按照以下格式回复:
|
||||
<{start_tag}={function_name}>{parameters}{end_tag}
|
||||
其中
|
||||
|
||||
start_tag => `<function`
|
||||
parameters => 一个JSON字典,其中函数参数名为键,函数参数值为值。
|
||||
end_tag => `</function>`
|
||||
|
||||
这是一个示例,
|
||||
<function=example_function_name>{"example_name": "example_value"}</function>
|
||||
|
||||
注意:
|
||||
- 函数调用必须遵循指定的格式
|
||||
- 必须指定所需参数
|
||||
- 一次只能调用一个函数
|
||||
- 将整个函数调用回复放在一行上
|
||||
|
||||
你是一个人工智能助理,请回答下列问题。
|
||||
"""
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": content_str,
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你今天去上海出差",
|
||||
}
|
||||
],
|
||||
response_format={
|
||||
"type": "structural_tag",
|
||||
"structures": [
|
||||
{
|
||||
"begin": "<function=get_current_date>",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timezone": {
|
||||
"type": "string",
|
||||
"description": "获取当前日期和时间的时区, 例如: Asia/Shanghai",
|
||||
}
|
||||
},
|
||||
"required": ["timezone"],
|
||||
},
|
||||
"end": "</function>",
|
||||
}
|
||||
],
|
||||
"triggers": ["<function="],
|
||||
},
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
<function=get_current_date>{"timezone": "Asia/Shanghai"}</function>
|
||||
```
|
||||
|
||||
对于 `choice、grammar、regex`格式,FastDeploy 使用 `extra_body` 参数支持,`choice` 较为简单,指定后模型会在预定义选项中选择一个最优项。在 FastDeploy 中,可以通过 `guided_choice` 指定一组与定义选项,示例如下
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{"role": "user", "content": "深圳的地标建筑是什么?"}
|
||||
],
|
||||
extra_body={"guided_choice": ["深圳平安国际金融中心", "中国华润大厦(春笋大厦)", "深圳京基100", "深圳地王大厦"]},
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
深圳地王大厦
|
||||
```
|
||||
|
||||
`regex` 允许用户通过正则表达式限制输出格式,通常用于需要精确控制文本格式的场景。以下示例展示了通过模型生成一个标准格式的网络地址的过程。
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "生成一个标准格式的网络地址,包括协议、域名。\n",
|
||||
}
|
||||
],
|
||||
extra_body={"guided_regex": r"^https:\/\/www\.[a-zA-Z]+\.com\/?$\n"},
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
https://www.example.com/
|
||||
```
|
||||
|
||||
`grammar` 允许用户通过 EBNF(Extended Backus-Naur Form) 语法描述一个限制规则,以下示例展示了通过 `guided_grammar` 限制模型输出一段html代码。
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
html_h1_grammar = """
|
||||
root ::= html_statement
|
||||
|
||||
html_statement ::= "<h1" style_attribute? ">" text "</h1>"
|
||||
|
||||
style_attribute ::= " style=" dq style_value dq
|
||||
|
||||
style_value ::= (font_style ("; " font_weight)?) | (font_weight ("; " font_style)?)
|
||||
|
||||
font_style ::= "font-family: '" font_name "'"
|
||||
|
||||
font_weight ::= "font-weight: " weight_value
|
||||
|
||||
font_name ::= "Arial" | "Times New Roman" | "Courier New"
|
||||
|
||||
weight_value ::= "normal" | "bold"
|
||||
|
||||
text ::= [A-Za-z0-9 ]+
|
||||
|
||||
dq ::= ["]
|
||||
"""
|
||||
|
||||
completion = client.chat.completions.create(
|
||||
model="null",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "生成一段html的代码,对以下标题加粗、Times New Roman字体。标题:ERNIE Bot",
|
||||
}
|
||||
],
|
||||
extra_body={"guided_grammar": html_h1_grammar},
|
||||
)
|
||||
print(completion.choices[0].message.content)
|
||||
print("\n")
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
<h1 style="font-family: 'Times New Roman'; font-weight: bold">ERNIE Bot</h1>
|
||||
```
|
||||
|
||||
### OpenAI beta 接口
|
||||
|
||||
在 OpenAI Client 1.54.4 版本之后,提供了 `client.beta.chat.completions.parse` 接口,通过该接口,实现了与 Python 原生类型更好的支持。以下示例展示了使用该接口直接获取 `pydantic` 类型的结构化输出。
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
import openai
|
||||
|
||||
class Info(BaseModel):
|
||||
addr: str
|
||||
height: int
|
||||
|
||||
port = "8170"
|
||||
client = openai.Client(base_url=f"http://127.0.0.1:{port}/v1", api_key="null")
|
||||
|
||||
completion = client.beta.chat.completions.parse(
|
||||
model="null",
|
||||
messages=[
|
||||
{"role": "user", "content": "上海东方明珠在上海市浦东新区世纪大道1号,高度为468米,请问上海东方明珠的地址和高度是多少?"},
|
||||
],
|
||||
response_format=Info,
|
||||
)
|
||||
|
||||
message = completion.choices[0].message
|
||||
print(message)
|
||||
print("\n")
|
||||
assert message.parsed
|
||||
print("地址:", message.parsed.addr)
|
||||
print("高度:", message.parsed.height)
|
||||
```
|
||||
|
||||
输出
|
||||
|
||||
```
|
||||
ParsedChatCompletionMessage[Info](content='{"addr": "上海市浦东新区世纪大道1号", "height": 468}', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None, parsed=Info(addr='上海市浦东新区世纪大道1号', height=468), reasoning_content=None)
|
||||
|
||||
|
||||
地址: 上海市浦东新区世纪大道1号
|
||||
高度: 468
|
||||
```
|
||||
Reference in New Issue
Block a user