vLLM
===
###### tags: `LLM / inference`
###### tags: `LLM`, `inference`, `推論`, `vLLM`, `--enforce-eager`, `eager=true`
<br>
[TOC]
<br>
## Intro
- [[github] vllm](https://github.com/vllm-project/vllm/)
<br>
## 啟動方式
```
python3 -m vllm.entrypoints.openai.api_server --port 5000 --model /model/llama3
```
### 完整啟動方式
> Model: vllm-llama32-11b-vision
```bash
#!/bin/bash
MAX_CONTEXT_LEN=131072 #32768 #8192 #32768
MAX_NUM_SEQ=64
GPU_UTILIZATION=0.8
TP=4
python3 -m vllm_ocisext.entrypoints.openai.api_server \
--served-model-name vllm-llama32-11b-vision \
--port 5000 \
--model /models/Llama-3.2-11B-Vision-Instruct \
--dtype bfloat16 \
--tensor-parallel-size $TP \
--pipeline_parallel_size 1 \
--max-num-batched-tokens $MAX_CONTEXT_LEN \
--max-model-len $MAX_CONTEXT_LEN \
--max-num-seqs $MAX_NUM_SEQ \
--gpu-memory-utilization $GPU_UTILIZATION \
--distributed-executor-backend mp \
--enable-chunked-prefill false \
--enforce-eager \
--guided-decoding-backend lm-format-enforcer \
--tool-call-parser llama31
```
- 用 fp8 跑?
```
python3 -m vllm.entrypoints.openai.api_server \
--served-model-name meta-llama33-70b-inst \
--port 5000 \
--model /models/Llama-3.3-70B-Instruct/ \
--dtype bfloat16 \
--tensor-parallel-size 4 \
--pipeline_parallel_size 1 \
--max-num-batched-tokens 8192 \
--max-model-len 32768 \
--max-num-seqs 128 \
--gpu-memory-utilization 0.90 \
--quantization="fp8"
```
## 參考資料
- [VLLM推理流程梳理](https://blog.csdn.net/just_sort/article/details/132115735)
- [Ray: 大模型时代的AI计算基础设施](https://www.jiqizhixin.com/articles/2023-08-17-6)
- [vllm/vllm
/sampling_params.py#skip_special_tokens](https://github.com/vllm-project/vllm/blob/v0.4.0.post1/vllm/sampling_params.py#L91-L93)
<br>
<hr>
<hr>
<br>
# 參數說明
## `--enforce-eager`
- [doc](https://docs.vllm.ai/en/latest/models/engine_args.html)
Always use eager-mode PyTorch. If False, will use eager mode and CUDA graph in hybrid for maximal performance and flexibility.
- [[Usage]: what is enforce_eager #4449](https://github.com/vllm-project/vllm/issues/4449)
- it disables the construction of CUDA graph in Pytorch. This may harm performance but reduces the memory requirement (of maintaining the CUDA graph)
- CUDAGraphs take a bit of memory and time to compile, but can be helpful for performance if the model is small
- 結論:
- true -> memory 下降 -> performance 下降
- false -> default
### Q&A
- enforce_eager=True or False 都可以情況,優先選 False ? (enable CUDA graph)
[time=2024 11 20 07:51] Frank: 我是會優先false,輸出在8k內的seq有加速作用
### Blog
- [Vllm Enforce Eager=True Explained](https://www.restack.io/p/vllm-knowledge-eager-true-cat-ai)
<br>
### [討論] vLLM 中啟用 `eager=true` 的重點總結
- ### gpt-4o-2024-11-20
### 1. **Eager Execution 的定義**
啟用 `eager=true` 會強制模型以即時執行模式運作,操作會立即執行,而不是延遲到整個計算圖完成。
### 2. **啟用 Eager Execution 的好處**
- **即時回饋**:能立即獲得操作結果,方便快速發現問題。
- **簡化除錯**:可以檢查中間結果與狀態,讓除錯過程更加直觀。
- **支援動態計算圖**:允許計算圖隨需求動態調整,適合靈活的模型架構。
### 3. **實作步驟**
1. **設定參數**:在模型的初始化設定中添加 `eager=true`。
2. **測試**:啟用後運行測試,檢查是否有執行行為的差異。
3. **性能監控**:觀察性能指標,避免對生產環境的效率產生不良影響。
### 4. **範例程式碼**
```python=
from vllm import ModelExecutor
# 啟用即時執行模式
model_executor = ModelExecutor(model_name='your_model_name', eager=True)
# 運行推論
output = model_executor.run(input_data)
print(output)
```
### 5. **考量因素**
- **性能取捨**:即時執行雖然有助於開發,但可能增加開銷,影響生產環境的效能。
- **社群貢獻**:參與 vLLM 社群,分享使用經驗與建議,幫助改善模型的穩定性與效能。
### 6. **應用場合**
- 適合開發與除錯階段,因為能提供更多互動性與靈活性。
- 生產環境需權衡性能需求,可能更適合選擇非即時執行模式。
<br>
### [討論] eager=true 中文是什麼意思?
- ### gpt-4o-2024-11-20
`eager=true` 在中文中的意思是「啟用即時執行模式」,也可翻譯為「強制即時執行」。當此設定啟用時,系統會立即執行每一個操作,而不是等待整個計算圖(computation graph)構建完成後再批量執行。
### 關鍵概念
- **Eager(即時)**:指的是操作在呼叫時馬上執行,而不是採用延遲執行(lazy execution)。
- **執行模式**:即時執行模式適合開發、測試或除錯(debugging)過程,因為可以在每一步操作中即時檢查結果與狀態。
### 執行模式比較
1. **lazy(延遲執行)**:構建好所有操作後才一次性執行。
2. **eager(即時執行)**:每個操作都在呼叫的當下執行。
### 示例程式碼
```python=
from vllm import ModelExecutor
# 啟用即時執行模式
model_executor = ModelExecutor(model_name='your_model_name', eager=True)
# 運行推論
output = model_executor.run(input_data)
print(output)
```
---
## `--distributed-executor-backend=mp`
> `pipeline_parallel_size` x `tensor_parallel_size` <= 單機GPU數量: -> mp
> - **換句話說**:
> - 在單一節點(單台機器)部署時,`--distributed-executor-backend` 預設就是 `mp`(多處理程序 backend)。
> - 這是因為 vLLM 在偵測到所有 GPU 都在同一台機器上,且 `pipeline_parallel_size × tensor_parallel_size ≤ GPU 數量` 的情況下,會自動選擇 `mp` 作為執行後端,而不需要使用 `ray`。
> - 如果是用單機跑 vLLM,而且沒特別指定 `--distributed-executor-backend`,它預設就會用 `mp` 模式,也就是用多個處理程序分配到每張 GPU 上。
> - 如需在多節點部署時才會考慮使用 `ray`。
- ### gpt-4o + Internet (2025/06/04)
在 vLLM(大語言模型推論引擎)中,`--distributed-executor-backend=mp` 參數的作用是指定使用「多處理程序(multiprocessing)」作為分散式執行後端。
這種模式特別適用於單一機器上擁有多張 GPU 的部署情境,當模型太大無法在單一 GPU 上運行,但可以分散到同一台機器的多個 GPU 上時,使用此模式可以有效地進行推論。
### `mp` 模式的特點與適用情境
- **單機多 GPU 支援**:當模型需要多個 GPU 但所有 GPU 都在同一台機器上時,`mp` 模式允許每個 GPU 對應一個 Python 程序,進行並行處理。
- **無需安裝 Ray**:與需要跨多節點協調的 Ray 模式不同,`mp` 模式不依賴於 Ray 框架,減少了額外的安裝和配置需求。
- **較低的系統開銷**:由於不涉及跨節點通信,`mp` 模式在單機環境下具有更低的系統開銷和更簡單的部署流程。
### 自動選擇與手動指定
vLLM 具有自動選擇執行後端的能力。
當 `pipeline_parallel_size` 和 `tensor_parallel_size` 的乘積小於或等於可用 GPU 的數量時,系統預設使用 `mp` 模式。
否則,如果已安裝 Ray,則會選擇 `ray` 模式。
不過,使用者可以透過 `--distributed-executor-backend` 參數手動指定所需的後端。
### 使用範例
假設您有一台配備 4 張 GPU 的機器,並希望在這些 GPU 上運行推理服務,可以使用以下命令:
```
vllm serve facebook/opt-13b \
--tensor-parallel-size 4 \
--distributed-executor-backend mp
```
這將啟動一個使用 4 個程序(每個 GPU 一個程序)的服務,每個程序負責模型的一部分,實現張量並行處理。
### 總結
`--distributed-executor-backend=mp` 參數使 vLLM 能夠在單機多 GPU 環境下高效運行,無需依賴外部分散式框架,適合需要在單台機器上部署大型語言模型的場景。
---
<br>
## `--guided-decoding-backend lm-format-enforcer`
### docs
- [DecodingConfig](https://docs.vllm.ai/en/latest/configuration/engine_args.html#decodingconfig)
- [[github] noamgat/lm-format-enforcer](https://github.com/noamgat/lm-format-enforcer?tab=readme-ov-file#comparison-to-other-libraries)

<br>
### 範例講解
- ### JSON 內容:
```json
{ "key": "value"
```
預期下一個 token 只能是:
- " " (可以允許空白)
- "," (接續 key, i.e. 還有其他 key)
- "}" (結束 dict)
其他 token 機率設為 0
<br>
---
### [討論] `--guided-decoding-backend lm-format-enforcer` 用途為何?
- ### gpt-4.1-mini (2025/07/14)
`--guided-decoding-backend lm-format-enforcer` 是 vLLM 這個推理框架中一個用於 **guided decoding(引導式解碼)** 的參數選項。
---
### 用途說明
* **guided decoding(引導式解碼)**:
是一種在生成過程中「引導」模型朝特定方向生成文本的技術,通常用來保證生成結果符合某種格式或規則。
* **lm-format-enforcer(語言模型格式強制器)**:
這個後端的作用是對模型生成的輸出進行格式上的約束和校驗,確保生成的文字符合預定的語言格式或特定規則。
例如:
* 保證生成文本符合 JSON、XML、正規表達式等特定格式。
* 避免語法錯誤或結構錯誤的生成結果。
* 幫助模型輸出更嚴謹、結構化的文本。
---
### 總結
使用 `--guided-decoding-backend lm-format-enforcer`,vLLM 會在生成過程中啟用一個格式強制器,確保生成內容嚴格遵守指定格式,減少格式錯誤,提高輸出品質及一致性。
---
如果你想,我也可以幫你找更簡單的使用範例或示意,讓你更直觀了解它的運作!
<br>
---
### [討論] token 輸出不是模型決定的嗎?為何 LM Format Enforcer 可以決定輸出 token ?
- ### gpt-4.1-mini (2025/07/14)
好問題!你說得沒錯:**token 輸出是模型根據機率分布決定的**,模型會對每個可能的下一個 token 給一個概率,然後選出一個(透過 sampling、beam search 等方式)。
那為什麼 LM Format Enforcer 可以「決定」或「限制」模型輸出 token 呢?以下是原理與做法說明:
---
### 為什麼 LM Format Enforcer 可以限制 token 輸出?
#### 1. **模型會給每個可能 token 一個機率分數 (logits)**
模型在每一步生成時,會針對整個詞彙表(所有 token)算出「每個 token 出現的可能性」。這就是「logits」或「機率分布」。
---
#### 2. **Guided decoding 是在模型輸出機率分布後,做「後處理過濾」**
LM Format Enforcer 不是直接改變模型內部運算,而是**接收模型算出的機率分布,然後在選 token 前,根據格式規則「篩選」允許出現的 token,剔除不合法的 token。**
* 具體來說:
* 它會先根據格式規則計算出「下一步允許的 token 集合」(allowed tokens)。
* 然後,把不在允許集合裡的 token 機率直接砍掉(設定成非常低或負無限大),讓模型**不可能選擇那些非法 token**。
---
#### 3. **實際解碼流程**
1. **模型算出所有 token 的機率分布**。
2. **LM Format Enforcer 根據目前生成的內容與格式規則,計算出下一步哪些 token 合法**。
3. **把不合法 token 的機率設為0(或負無限)以剔除**。
4. **從剩下的合法 token 中根據機率採樣或選擇下一個 token**。
---
#### 4. **類比**
就像:
* 你讓模型先列出所有可能答案(token)。
* 你幫模型過濾一遍,說「只能從這些答案裡選」。
* 模型只能從「被允許的答案」中挑,不能挑其他的。
---
#### 5. **技術意義**
* 這樣就實現了「在解碼時對模型的生成過程施加約束」(constrained decoding)。
* 並且不需改模型本身,只是改解碼時的行為。
* 讓生成內容嚴格符合格式規則。
---
### 簡單結論
* **模型負責算機率分布。**
* **LM Format Enforcer 負責在選 token 前過濾機率分布,禁止不符合格式的 token 被選出。**
這樣整個生成過程才能同時有模型的語言能力,又能確保格式正確。
---
如果你想要,我可以幫你用程式碼示意來說明這個「過濾機率」的流程,讓理解更直觀!
<br>
<br>
# Misc
## deepseek_r1_reasoning_parser.py vs qwen3_reasoning_parser.py
- ### Sources
- [deepseek_r1_reasoning_parser.py](https://github.com/vllm-project/vllm/blob/main/vllm/reasoning/deepseek_r1_reasoning_parser.py)
- [qwen3_reasoning_parser.py](https://github.com/vllm-project/vllm/blob/main/vllm/reasoning/qwen3_reasoning_parser.py)
- ### gpt-o4-mini-high (2025/05/20)
### 1. 標籤存在與否時的預設行為
- **DeepSeekR1**
- **不一定產生 `<think>` 標籤**,因此它假定:「整段輸出一開始」都是 reasoning,直到看到 `</think>` 為止。
- `extract_reasoning_content` 如果找不到 `</think>`,就回傳 `(model_output, None)` —— 把全部當 reasoning、沒有 content。
- **Qwen3**
- **嚴格依賴**同時出現 `<think>` 和 `</think>` 來切分 reasoning。
- 若任一標籤短缺,就回傳 `(None, model_output)` —— 全部當 content、沒有 reasoning。
### 2. `extract_reasoning_content` 的實作差異
| | DeepSeekR1 | Qwen3 |
|---------------|----------------------------------------------------------|-----------------------------------------------------------|
| 有 `<think>` | 先丟掉 `<think>` 之前的部分,再遇 `</think>` 分割 | 用 `partition("<think>")` 拆分,然後再用 `partition("</think>")` 分割 |
| 無 `</think>` | 回 `(全部字串, None)` | 回 `(None, 全部字串)` |
### 3. 串流(`extract_reasoning_content_streaming`)邏輯
- **DeepSeekR1**
- 支援多種情境:
1. **`<think>` 已在先前出現**,後續 delta 裡碰到 `</think>` 抽 reasoning,再把後面剩餘當 content
2. **只有 `</think>` 出現在 delta**(或先前),也能處理、抽 reasoning 再分出 content
3. **連 `<think>` 都沒出現**,就把 delta 全當 reasoning(因為預設都是 reasoning)
- **Qwen3**
- 流程簡化:
1. **只有在 delta 或先前「出現 `<think>`」**,才進一步檢查 `</think>`;否則一律當 content 回傳
2. 不處理「單獨出現 `</think>`」的特殊情境──如果 `<think>` 根本沒來,就不會把任何東西當 reasoning
### 4. 適用模型的差異
- **DeepSeek R1**
- 模型本身**不再**輸出 `<think>` 起始標籤(但保留 `</think>` 作為分界),故 parser 必須「寬鬆地」把一開始都當 reasoning,然後透過 `</think>` 來切分。
- **Qwen3**
- 模型**會**完整輸出 `<think>…</think>`(並且可關閉 thinking 功能),所以 parser 才能「嚴格地」只有在真的看到這對標籤時,才認定中間是 reasoning,其餘文字全當最終回答。
---
簡言之,**DeepSeekR1** 的 parser 是為了配合「幾乎不出起始標籤」的模型行為,預設把輸出都當 reasoning;而 **Qwen3** 的 parser 則是為了那個「必須完整出現 `<think>…</think>`」的模型設計,預設把文字當 content,只有在看到完整標籤時才抽 reasoning。
<br>
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}