week9

目标:

部署一个VLLM大模型服务,验证速度提升。

内容:

使用同一个开源大模型,分别采用 Hugging Face Transformers 原生推理和 vLLM 服务化推理,在相同硬件、相同输入长度、相同输出长度下,对比单请求延迟、并发吞吐、TTFT、TPOT 和 Tokens/s,验证 vLLM 在在线推理场景中的性能提升。


一、第九周作业目标

本周重点掌握 5 个知识点:

  1. 学会下载并运行一个开源大语言模型。
  2. 使用 Hugging Face Transformers 实现基线推理。
  3. 使用 vLLM 部署 OpenAI Compatible API 服务。
  4. 学会进行并发压测。
  5. 对比不同推理框架下的延迟和吞吐量。

最终实验结构:

text 复制代码
同一个模型
   │
   ├── Hugging Face Transformers
   │        ↓
   │    普通推理基线
   │
   └── vLLM
            ↓
       OpenAI API Server
            ↓
        Benchmark
            ↓
延迟 / Tokens/s / 吞吐量

二、为什么要学习 vLLM?

前面的课程重点基本都是模型训练,例如:

text 复制代码
RNN
LSTM
Transformer
文本分类
序列标注
文本匹配

但真正把大模型应用到线上,还会遇到:

text 复制代码
模型推理太慢
GPU 利用率不高
并发能力差
显存容易浪费

比如直接使用 Transformers:

python 复制代码
model.generate(...)

一两个请求问题不大,但如果线上同时来了 10、50、100 个请求,如何高效利用 GPU 就变得非常重要。

vLLM 的定位就是:

面向大语言模型的高吞吐推理和服务框架。


三、实验整体设计

建议选择一个能在本机 GPU 上稳定运行的模型。

如果使用 RTX 4060 Laptop GPU 这类显卡,显存通常会成为主要约束,因此建议优先使用:

text 复制代码
Qwen/Qwen2.5-1.5B-Instruct

如果显存更充足,可以考虑:

text 复制代码
Qwen 3B / 4B
7B 量化模型

为了保证实验公平,最重要的是:

text 复制代码
基线和 vLLM 必须使用同一个模型。

四、实验环境建议

项目 配置
OS Ubuntu 22.04 / Linux
GPU RTX 4060 Laptop GPU
CUDA 按实际环境填写
Python 3.10 / 3.11
PyTorch 按实际版本填写
vLLM 按实际版本填写
Transformers 按实际版本填写
Model Qwen2.5-1.5B-Instruct
dtype float16 / auto
输入长度 约 128 tokens
最大输出 128 tokens
Benchmark Requests 100 / 500
并发 1 / 4 / 8 / 16

如果实验机是 Windows,建议使用:

text 复制代码
WSL2 + Ubuntu

或者直接 Linux。


五、项目结构

text 复制代码
week9/
├── baseline_transformers.py
├── test_vllm.py
├── benchmark_client.py
├── requirements.txt
├── results/
│   ├── baseline.json
│   ├── vllm_concurrency_1.json
│   ├── vllm_concurrency_4.json
│   ├── vllm_concurrency_8.json
│   └── vllm_concurrency_16.json
└── SUMMARY.md

六、第一步:检查 GPU 环境

先执行:

bash 复制代码
nvidia-smi

重点查看:

text 复制代码
GPU 型号
CUDA Version
显存大小
显存占用

然后 Python 中检查:

python 复制代码
import torch

print("CUDA available:", torch.cuda.is_available())
print("CUDA version:", torch.version.cuda)

if torch.cuda.is_available():
    print("GPU:", torch.cuda.get_device_name(0))

七、安装环境

建议创建独立虚拟环境:

bash 复制代码
conda create -n week9-vllm python=3.11 -y
conda activate week9-vllm

安装:

bash 复制代码
pip install vllm
pip install transformers accelerate openai

查看版本:

bash 复制代码
python -c "import torch; print(torch.__version__)"
python -c "import vllm; print(vllm.__version__)"
python -c "import transformers; print(transformers.__version__)"

八、实验一:Transformers 原生推理基线

创建:

text 复制代码
baseline_transformers.py

代码:

python 复制代码
import time
import torch

from transformers import AutoTokenizer, AutoModelForCausalLM

MODEL_NAME = "Qwen/Qwen2.5-1.5B-Instruct"
device = "cuda"

tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)

model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    torch_dtype=torch.float16,
    device_map=device
)

model.eval()

prompt = "请简单介绍一下 Transformer 模型的工作原理。"

messages = [
    {
        "role": "user",
        "content": prompt
    }
]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)

inputs = tokenizer(
    text,
    return_tensors="pt"
).to(device)

# Warmup
with torch.no_grad():
    _ = model.generate(
        **inputs,
        max_new_tokens=32
    )

torch.cuda.synchronize()

start = time.perf_counter()

with torch.no_grad():
    outputs = model.generate(
        **inputs,
        max_new_tokens=128,
        do_sample=False
    )

torch.cuda.synchronize()

elapsed = time.perf_counter() - start

input_tokens = inputs.input_ids.shape[1]
output_tokens = outputs.shape[1] - input_tokens
tokens_per_second = output_tokens / elapsed

print("=" * 50)
print("Input tokens:", input_tokens)
print("Output tokens:", output_tokens)
print(f"Latency: {elapsed:.3f}s")
print(f"Generation speed: {tokens_per_second:.2f} token/s")

result = tokenizer.decode(
    outputs[0][input_tokens:],
    skip_special_tokens=True
)

print("\nResult:")
print(result)

运行:

bash 复制代码
python baseline_transformers.py

注意:作业报告必须填写自己实际测试的数据。


九、为什么一定要 Warmup?

第一次推理通常包含:

text 复制代码
CUDA 初始化
Kernel 初始化
内存分配
模型缓存

因此第一次调用:

python 复制代码
model.generate()

通常不能直接作为最终性能数据。

正确流程:

text 复制代码
Warmup
↓
正式 Benchmark

十、实验二:启动 vLLM 服务

启动:

bash 复制代码
vllm serve Qwen/Qwen2.5-1.5B-Instruct

或者:

bash 复制代码
vllm serve \
    Qwen/Qwen2.5-1.5B-Instruct \
    --host 0.0.0.0 \
    --port 8000

默认服务地址:

text 复制代码
http://localhost:8000

十一、验证服务是否启动成功

首先:

bash 复制代码
curl http://localhost:8000/v1/models

然后测试 Chat Completions:

bash 复制代码
curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen2.5-1.5B-Instruct",
        "messages": [
            {
                "role": "user",
                "content": "请简单介绍Transformer。"
            }
        ],
        "temperature": 0,
        "max_tokens": 128
    }'

十二、使用 Python 调 vLLM 服务

创建:

text 复制代码
test_vllm.py
python 复制代码
import time
from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="EMPTY"
)

start = time.perf_counter()

response = client.chat.completions.create(
    model="Qwen/Qwen2.5-1.5B-Instruct",
    messages=[
        {
            "role": "user",
            "content": "请简单介绍一下 Transformer 模型。"
        }
    ],
    temperature=0,
    max_tokens=128
)

elapsed = time.perf_counter() - start

usage = response.usage

print("Prompt tokens:", usage.prompt_tokens)
print("Completion tokens:", usage.completion_tokens)
print(f"Latency: {elapsed:.3f}s")

speed = usage.completion_tokens / elapsed
print(f"Output speed: {speed:.2f} token/s")

print("\nResult:")
print(response.choices[0].message.content)

运行:

bash 复制代码
python test_vllm.py

十三、只测一个请求,可能看不出 vLLM 的优势

如果 Transformers 和 vLLM 都只跑 1 个请求,你可能发现差异并不明显。

vLLM 真正需要重点验证的是:

并发请求下的整体吞吐量。

因此建议测试:

text 复制代码
1 并发
4 并发
8 并发
16 并发

十四、核心实验:并发性能测试

实验 并发
Test 1 1
Test 2 4
Test 3 8
Test 4 16
Test 5 32(显存允许时)

每次建议:

text 复制代码
100 个 Prompt
input ≈ 128 tokens
output = 128 tokens

十五、自己写并发 Benchmark

创建:

text 复制代码
benchmark_client.py
python 复制代码
import asyncio
import time
import statistics

from openai import AsyncOpenAI

MODEL = "Qwen/Qwen2.5-1.5B-Instruct"

client = AsyncOpenAI(
    base_url="http://localhost:8000/v1",
    api_key="EMPTY"
)

PROMPT = "请解释 Transformer 中 Self-Attention 的基本工作原理。"

async def request_once():
    start = time.perf_counter()

    response = await client.chat.completions.create(
        model=MODEL,
        messages=[
            {
                "role": "user",
                "content": PROMPT
            }
        ],
        max_tokens=128,
        temperature=0
    )

    elapsed = time.perf_counter() - start
    output_tokens = response.usage.completion_tokens

    return elapsed, output_tokens


async def worker(semaphore):
    async with semaphore:
        return await request_once()


async def benchmark(total_requests, concurrency):
    semaphore = asyncio.Semaphore(concurrency)
    start = time.perf_counter()

    tasks = [
        worker(semaphore)
        for _ in range(total_requests)
    ]

    results = await asyncio.gather(*tasks)
    total_time = time.perf_counter() - start

    latencies = [x[0] for x in results]
    total_tokens = sum(x[1] for x in results)

    throughput = total_tokens / total_time
    request_per_second = total_requests / total_time

    print("\n=============================")
    print("Concurrency:", concurrency)
    print("Requests:", total_requests)
    print(f"Total time: {total_time:.2f}s")
    print(f"RPS: {request_per_second:.2f}")
    print(f"Output throughput: {throughput:.2f} token/s")
    print(f"Average latency: {statistics.mean(latencies):.2f}s")
    print(f"P50 latency: {statistics.median(latencies):.2f}s")
    print(f"Max latency: {max(latencies):.2f}s")


async def main():
    for concurrency in [1, 4, 8, 16]:
        await benchmark(
            total_requests=100,
            concurrency=concurrency
        )

asyncio.run(main())

运行:

bash 复制代码
python benchmark_client.py

十六、推荐:直接使用 vLLM 官方 Benchmark

可以查看:

bash 复制代码
vllm bench serve --help

通过官方 benchmark 工具,可以更标准地测试:

text 复制代码
模型
数据集
输入长度
输出长度
请求数量
请求速率
并发吞吐

十七、真正应该关注哪些指标?

1. TTFT

text 复制代码
Time To First Token

即:

用户发送请求到看到第一个 Token 的时间。

这个指标直接影响用户对"响应快不快"的感知。

2. TPOT

text 复制代码
Time Per Output Token

表示第一个 Token 出来以后,平均生成一个 Token 需要多少时间。

例如:

text 复制代码
TPOT = 30 ms

大致相当于:

text 复制代码
≈ 33 token/s

3. End-to-End Latency

完整请求耗时:

text 复制代码
请求开始
↓
Prompt Prefill
↓
Decode
↓
生成结束

4. Throughput

这是验证 vLLM 优势的核心指标。

计算:

text 复制代码
Speedup = vLLM Throughput / Baseline Throughput

最终应该报告:

text 复制代码
Speedup = X.XX ×

必须来自实际测试结果。


十八、为什么 vLLM 吞吐通常会提升?

1. KV Cache

Transformer 在生成过程中,过去 Token 的 Key / Value 可以缓存下来,避免重复计算。

随着:

text 复制代码
上下文长度 ↑
并发用户 ↑

KV Cache 的显存需求也会快速增大。

2. PagedAttention

可以将 KV Cache 的管理方式类比操作系统分页:

text 复制代码
虚拟内存
↓
Page
↓
按页管理

相比为每个请求预留连续空间,分页方式可以更加灵活地利用 GPU 显存。

3. Continuous Batching

假设三个请求:

text 复制代码
A:生成 20 token
B:生成 200 token
C:生成 50 token

当 A 完成后,可以及时将新请求加入,而不是等待整个固定 Batch 都结束。

因此在并发场景中:

text 复制代码
GPU 利用率 ↑
吞吐量 ↑

十九、不要把"速度提升"简单理解为单请求更快

更准确的结论是:

vLLM 的主要优势通常体现在大模型在线 Serving 的 GPU 利用率、并发调度和总体吞吐能力上;具体单请求延迟是否改善,以及吞吐可以提高多少,需要结合模型、GPU、输入输出长度、并发量和配置通过实测确定。


二十、实验时一定要控制变量

必须保持:

text 复制代码
同一个模型
同一个 GPU
同一个 dtype
相同 Prompt
相同输入长度
相同输出长度
相同 temperature

例如:

text 复制代码
Model: Qwen2.5-1.5B-Instruct
temperature: 0
max_tokens: 128

二十一、推荐测试矩阵

建议至少完成:

text 复制代码
Transformers
├── concurrency 1
├── concurrency 4
├── concurrency 8
└── concurrency 16

vLLM
├── concurrency 1
├── concurrency 4
├── concurrency 8
└── concurrency 16

形成:

text 复制代码
2 个推理框架 × 4 个并发级别 = 8 组实验

每组建议:

text 复制代码
Warmup 10
正式测试 100

二十二、建议最终实验结果表

单请求测试

Framework Latency Output Tokens Tokens/s
Transformers 实测 128 实测
vLLM 实测 128 实测

并发吞吐测试

并发 Transformers vLLM Speedup
1 实测 实测 x
4 实测 实测 x
8 实测 实测 x
16 实测 实测 x

单位统一:

text 复制代码
output tokens / second

二十三、建议额外记录 GPU 数据

压测时开另一个终端:

bash 复制代码
watch -n 1 nvidia-smi

记录:

Framework GPU Util GPU Memory
Transformers 实测 实测
vLLM 实测 实测

这样可以进一步验证:

text 复制代码
性能提升是否来自更高的 GPU 利用率

二十四、第九周最终实验结论模板

markdown 复制代码
## 实验结论

本实验尝试将开源大语言模型部署为在线推理服务,
并比较 Hugging Face Transformers 与 vLLM
在相同模型和硬件条件下的推理性能。

实验重点考察单请求延迟、TTFT、TPOT、
Output Tokens/s、Requests/s 以及不同并发条件下的吞吐量。

单请求场景下,两种方案的性能差异需要结合实际测试结果分析。

随着并发请求数量增加,vLLM 可以通过更高效的
KV Cache 管理、PagedAttention 与 Continuous Batching
提升 GPU 利用率和整体吞吐能力。

本实验说明,大模型推理系统不能只关注模型本身,
还需要关注推理框架、并发调度、显存管理以及服务吞吐量。

二十五、本周进阶实验

实验一:不同并发数

text 复制代码
1
2
4
8
16
32

绘制:

text 复制代码
Concurrency → Tokens/s

曲线,观察 GPU 最大吞吐点。

实验二:不同输入长度

text 复制代码
128
512
1024
2048

观察:

text 复制代码
TTFT
显存
Throughput

变化。

实验三:不同输出长度

text 复制代码
32
128
512

观察 Decode 阶段对性能的影响。


二十六、第九周作业验收标准

建议至少完成:

  1. 成功启动一个 vLLM 大模型服务。
  2. /v1/chat/completions 能正常返回结果。
  3. 完成 Transformers 原生推理基线。
  4. 测试并发 1 / 4 / 8 / 16。
  5. 记录单请求延迟。
  6. 记录 Requests/s。
  7. 记录 Output Tokens/s。
  8. 最好记录 TTFT / TPOT。
  9. 对比 GPU 显存和利用率。
  10. 计算实际 Speedup:
text 复制代码
vLLM Throughput
----------------
Baseline Throughput

最终得到:

text 复制代码
Speedup = X.XX ×

总结

第九周最核心的知识点不是"把模型跑起来",而是理解为什么同一个大模型,在不同推理框架和不同并发条件下,会表现出完全不同的服务能力。

通过本次实验,可以进一步理解:

text 复制代码
大模型部署
↓
推理服务
↓
KV Cache
↓
PagedAttention
↓
Continuous Batching
↓
并发压测
↓
吞吐优化

这也为后续学习大模型分布式推理、Tensor Parallel、多 GPU 部署和生产级 LLM Serving 打下基础。

相关推荐
数商思语行2 小时前
Palantir Study 27|Global Branching:安全修改生产 Ontology
人工智能·ai·供应链·商业分析·ontology·本体·fde
七夜zippoe2 小时前
Agent 自动化测试:怎么给“会自己决策“的程序写测试
ai·自动化·agent·测试·openclaw
kali-Myon2 小时前
ARTEX:AI 驱动的自动化漏洞挖掘平台
人工智能·安全·ai·自动化·web
bigdata-余建新2 小时前
week8
ai
深度智能Ai2 小时前
IndexTTS 2.5 语音合成 API 接口使用文档
ai·语音合成·tts·在线语音合成
liulilittle3 小时前
多智能体编排的三个点
ai·llm·agent·tools·opencode
七夜zippoe4 小时前
第一季·阶段总结:Agent 核心技能栈检查清单与实战自测
网络·ai·agent·核心技能·实战自测
一 铭12 小时前
Pi实战 05:本地模型 · MCP · 安全沙箱篇
人工智能·ai·agent·harness
落魄实习生16 小时前
Agent Scope Java 2.x 系列【10】Middleware
java·开发语言·ai