从原理到实践:PagedAttention 与推理服务部署
前言
去年我写过一篇纯 CPU 部署知识库的文章,收到不少反馈说”终于有人关注没有 GPU 的场景了”。但说实话,CPU 推理的体验确实差强人意——2-3 tokens/s 的速度,用户问一个问题要等半分钟,这在生产环境里几乎不可接受。
后来公司采购了一批 RTX 4090,我终于有机会认真研究 GPU 推理了。折腾了两周,把市面上主流的推理框架都试了一遍,最终锁定了 vLLM。这篇文章,我想把这两周踩过的坑、学到的东西,整理分享出来,希望能帮大家少走弯路。
什么是 vLLM?
vLLM 是由加州大学伯克利分校的研究团队开发的高性能大模型推理框架。其核心创新是 PagedAttention 技术,通过借鉴操作系统的虚拟内存分页机制来管理 KV Cache,大幅提升了显存利用率和推理吞吐量。目前 vLLM 已成为业界最流行的开源推理框架之一。
为什么选择 vLLM
先说结论:vLLM 在显存管理上的创新,确实带来了显著的性能提升。
我最初用的是 Hugging Face Transformers 原生推理。代码简单,model.generate() 一行搞定。但当我把服务上线后,问题来了——10 个用户同时访问,服务响应变慢。查了半天发现,原生推理是串行的,一个请求没处理完,后面的全在排队。
然后我试了 Text Generation Inference (TGI),Hugging Face 官方出品。确实比原生快很多,但配置相对复杂。更关键的是,我发现显存利用率还有提升空间——24GB 的 4090,跑个 7B 模型占了 18GB。
直到我遇到 vLLM,才明白其中的优化空间。
KV Cache 与显存占用
要理解 vLLM 为什么快,得先理解一个概念:KV Cache。
KV Cache 是什么?
Transformer 模型在生成每个 token 时,需要计算自注意力(Self-Attention)。为了避免对历史 token 重复计算 Key 和 Value 向量,模型会把它们缓存起来,这就是 KV Cache。KV Cache 是一种典型的”空间换时间”策略。
问题是,KV Cache 的显存占用是动态的——输出越长,占用越大。
传统框架怎么处理的?预分配。假设你设置 max_length=2048,框架会一次性为每个请求分配 2048 个 token 的 KV Cache 空间,不管实际输出是 100 个还是 2000 个。
这导致了两个问题:
- 显存浪费:大部分请求输出都很短,预分配的空间大量闲置
- 并发受限:显存被少数长请求占满,新请求进不来
PagedAttention 的核心思想
vLLM 的创始团队来自伯克利,他们提出了一个想法:把操作系统的虚拟内存分页机制用到 KV Cache 管理上。
传统方式像是给每个人分配一间固定大小的房间,不管你住不住得满。PagedAttention 则是把显存切成固定大小的”页”(比如 16 个 token 一页),按需分配。用完一页再分配下一页,释放时也是按页回收。
这带来了三个改进:
-
显存利用率提升:不再有预分配浪费,每个 token 都物尽其用
-
并发能力提升:同样的显存可以服务更多请求,因为短请求释放的页可以立即给新请求用
-
支持复杂的采样策略:Beam search、parallel sampling 这些需要同时维护多个序列的场景,共享相同前缀的 KV Cache,显存占用大幅降低
简单来说:vLLM 让显存分配更加灵活高效。
环境准备与注意事项
硬件选择建议
我的测试环境:
- GPU:NVIDIA RTX 4090 24GB
- CPU:Intel i9-13900K(24核32线程)
- 内存:64GB DDR5-5600
- 存储:2TB PCIe 4.0 NVMe SSD
但这不是最优解。如果是企业用户,A10 (24GB) 或 L4 (24GB) 也是不错的选择——价格适中,稳定性更好,而且支持 ECC 内存。
显存容量估算公式
所需显存 (GB) ≈ 模型参数量 (B) × 2 × (1 + 0.1 × 最大并发数)
以 7B 模型为例:
- 单请求:7 × 2 × 1.1 ≈ 15.4GB
- 10 并发:7 × 2 × 2 = 28GB(需要更大显存或量化)
软件环境与 CUDA 版本
# 第一步:确认 CUDA 版本
nvidia-smi
# 输出的 CUDA Version 是驱动支持的最高版本,不是实际安装版本
nvcc --version
# 这才是实际安装的 CUDA 版本
nvidia-smi 与 nvcc 的区别
nvidia-smi显示的 CUDA Version 是 驱动支持的最高版本,不代表你已安装该版本。nvcc --version才是实际安装的 CUDA Toolkit 版本。很多人被这个坑过。
vLLM 对 CUDA 版本要求严格:
- vLLM 0.4.x:需要 CUDA 12.1+
- vLLM 0.5.x:需要 CUDA 12.4+(推荐)
我第一次安装时遇到过问题:服务器上有 CUDA 11.8,nvidia-smi 显示”CUDA Version: 12.2”,我以为没问题,结果 vLLM 报错。后来才知道那个 12.2 只是驱动支持的最高版本,实际用的还是 11.8。
正确的安装流程:
# 1. 卸载旧版 CUDA(如果有)
sudo apt remove --purge cuda*
sudo apt autoremove
# 2. 安装 CUDA 12.4(以 Ubuntu 22.04 为例)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt install cuda-toolkit-12-4
# 3. 配置环境变量
echo 'export PATH=/usr/local/cuda-12.4/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
# 4. 验证
nvcc --version # 应该显示 12.4
安装 vLLM
# 创建干净的虚拟环境
conda create -n vllm python=3.10 -y
conda activate vllm
# 安装 vLLM(会自动安装匹配的 PyTorch)
pip install vllm
# 验证安装
python -c "import vllm; print(f'vLLM version: {vllm.__version__}')"
python -c "import torch; print(f'PyTorch CUDA: {torch.version.cuda}')"
如果安装失败,大部分情况是 CUDA 版本问题。
快速上手
离线批量推理示例
先从最简单的场景开始——批量处理一组 prompt:
from vllm import LLM, SamplingParams
import time
# 初始化模型(第一次会从 HuggingFace 下载)
print("Loading model...")
start = time.time()
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
trust_remote_code=True, # Qwen 模型需要这个
dtype="float16", # 半精度,平衡速度和精度
)
print(f"Model loaded in {time.time() - start:.1f}s")
# 采样参数
sampling_params = SamplingParams(
temperature=0.7, # 控制随机性,0=确定性,1=高随机
top_p=0.9, # 核采样,只从概率累积达到 90% 的 token 中采样
max_tokens=512, # 最大生成长度
stop=["<|im_end|>"] # 停止符
)
# 准备一批 prompt
prompts = [
"用简单的语言解释量子计算",
"写一首关于编程的俳句",
"Python 的 GIL 是什么?为什么它是个问题?",
"解释为什么天空是蓝色的",
"如何向一个 5 岁小孩解释人工智能",
]
# 批量推理
print(f"\nProcessing {len(prompts)} prompts...")
start = time.time()
outputs = llm.generate(prompts, sampling_params)
elapsed = time.time() - start
# 统计
total_tokens = sum(len(o.outputs[0].token_ids) for o in outputs)
print(f"\nGenerated {total_tokens} tokens in {elapsed:.1f}s")
print(f"Throughput: {total_tokens / elapsed:.1f} tokens/s")
# 打印结果
for output in outputs:
print(f"\n{'='*60}")
print(f"Prompt: {output.prompt[:50]}...")
print(f"Response: {output.outputs[0].text[:200]}...")
运行这段代码,你会看到类似这样的输出:
Loading model...
Model loaded in 12.3s
Processing 5 prompts...
Generated 1847 tokens in 4.8s
Throughput: 384.8 tokens/s
384 tokens/s。相比 CPU 推理的 2-3 tokens/s,提升非常明显。
除了批量推理,vLLM 也支持在线服务。
OpenAI 兼容 API
vLLM 内置了一个 OpenAI API 兼容服务器,这意味着你现有的代码几乎不用改就能迁移过来:
# 启动服务
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--trust-remote-code
服务启动后,用 OpenAI SDK 调用:
from openai import OpenAI
# 只需要改 base_url,其他代码完全不变!
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed" # vLLM 默认不验证 key
)
# 和调用 GPT-4 一模一样的代码
response = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[
{"role": "system", "content": "你是一个资深的 Python 工程师"},
{"role": "user", "content": "如何用 Python 实现一个高性能的 Web 爬虫?"}
],
temperature=0.7,
max_tokens=1024,
stream=True # 支持流式输出!
)
# 流式打印
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
OpenAI 兼容的好处
你的应用代码不需要知道后端是 GPT-4 还是本地模型,只需要改一个 URL。灰度切换、A/B 测试、成本优化——所有这些都变得轻而易举。这也是 vLLM 被广泛采用的重要原因之一。
性能调优经验
把 vLLM 跑起来很容易,但要在生产环境稳定运行,还需要精细调优。这是我花了最多时间的部分。
关键参数说明
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--dtype float16 \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--max-num-batched-tokens 32768 \
--max-num-seqs 256 \
--enable-chunked-prefill
让我逐个解释这些参数:
| 参数 | 说明 | 建议值 |
|---|---|---|
--dtype |
数据类型,float16/bfloat16/auto | 消费卡用 float16,A100/H100 用 bfloat16 |
--max-model-len |
最大上下文长度 | 按需设置,太大会 OOM |
--gpu-memory-utilization |
显存利用率上限 | 测试 0.95,生产 0.88 |
--max-num-seqs |
最大并发数 | 根据显存计算 |
--enable-chunked-prefill |
分块预填充 | 推荐开启 |
什么是 Chunked Prefill?
当一个新请求进来时,vLLM 需要先处理整个 prompt(prefill 阶段),然后才能开始生成。如果 prompt 很长(比如 4000 tokens),prefill 会阻塞其他请求。
启用 chunked-prefill 后,长 prompt 会被切成小块,穿插在其他请求之间处理。首 token 延迟可能略增,但整体吞吐量和公平性大幅提升。
调优流程参考
以下是我总结的一套调优流程:
Step 1:建立基准
# 先用默认参数跑,记录性能指标
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct
Step 2:压力测试
# benchmark.py
import asyncio
import aiohttp
import time
async def send_request(session, prompt):
start = time.time()
async with session.post(
"http://localhost:8000/v1/chat/completions",
json={
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 256
}
) as response:
result = await response.json()
return time.time() - start
async def benchmark(concurrency=10, total_requests=100):
prompts = ["用100字介绍Python语言"] * total_requests
async with aiohttp.ClientSession() as session:
start = time.time()
tasks = [send_request(session, p) for p in prompts[:concurrency]]
completed = 0
latencies = []
while tasks or completed < total_requests:
done, tasks = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
for task in done:
latencies.append(task.result())
completed += 1
if completed < total_requests:
tasks.add(asyncio.create_task(
send_request(session, prompts[completed])
))
elapsed = time.time() - start
print(f"Concurrency: {concurrency}")
print(f"Total requests: {total_requests}")
print(f"Total time: {elapsed:.1f}s")
print(f"Throughput: {total_requests / elapsed:.1f} req/s")
print(f"Avg latency: {sum(latencies) / len(latencies) * 1000:.0f}ms")
print(f"P99 latency: {sorted(latencies)[int(len(latencies) * 0.99)] * 1000:.0f}ms")
asyncio.run(benchmark(concurrency=20, total_requests=200))
Step 3:观察瓶颈
运行压测时,开另一个终端监控:
# 监控 GPU
watch -n 1 nvidia-smi
# 重点关注:
# - GPU 利用率:应该接近 100%
# - 显存占用:应该接近 gpu-memory-utilization 设定值
# - 功耗:满载时接近 TDP
Step 4:针对性调优
| 现象 | 瓶颈 | 调优方向 |
|---|---|---|
| GPU 利用率低 | 批处理不够 | 增大 max-num-seqs |
| 显存占用低 | 预留太多 | 增大 gpu-memory-utilization |
| P99 延迟高 | 长请求阻塞 | 启用 chunked-prefill |
| OOM | 显存不够 | 减小 max-model-len 或量化 |
多卡部署配置
如果单卡性能不够,vLLM 支持多卡并行:
# 2 卡张量并行(模型切分到 2 张卡)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-72B-Instruct \
--tensor-parallel-size 2
# 4 卡流水线并行(每张卡跑一部分层)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-72B-Instruct \
--pipeline-parallel-size 4
张量并行 vs 流水线并行
- 张量并行 (TP):把每一层的计算切分到多张卡,单个请求延迟敏感,适合交互场景
- 流水线并行 (PP):把不同的层分到不同的卡,吞吐量优先,适合批处理场景
- 混合并行:大模型 + 多卡,比如 72B 模型用 TP=2 × PP=2
我的 8×4090 集群配置是 72B 模型用 TP=4,剩下 4 张卡跑 4 个独立的 7B 实例做负载均衡。
RAG 集成示例
单独的 LLM 就像一个聪明但健忘的人——它知道很多通用知识,但对你公司的内部文档一无所知。RAG(检索增强生成)就是给它装上”外部记忆”。
什么是 RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索和生成的技术。先从知识库中检索相关文档,再把检索结果作为上下文输入给 LLM,让模型基于这些”外部知识”生成回答。这样可以大幅减少幻觉,并让模型回答特定领域的问题。
结合我之前的知识库方案,把推理后端换成 vLLM:
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
# 加载向量数据库(假设已经构建好)
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-small-zh-v1.5",
model_kwargs={'device': 'cuda'} # embedding 也用 GPU 加速
)
vectorstore = FAISS.load_local(
"faiss_index",
embeddings,
allow_dangerous_deserialization=True
)
# 连接 vLLM 后端
llm = ChatOpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed",
model="Qwen/Qwen2.5-7B-Instruct",
temperature=0.7,
max_tokens=1024
)
# 自定义 prompt 模板
prompt_template = """你是一个专业的技术顾问。请根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请诚实地说"我不知道",不要编造答案。
参考资料:
{context}
用户问题:{question}
请用清晰、专业的语言回答:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# 构建 RAG Chain
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(
search_type="mmr", # 最大边际相关性,避免重复
search_kwargs={"k": 5} # 检索 5 个相关片段
),
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True
)
# 查询
result = qa_chain.invoke({"query": "vLLM 的 PagedAttention 是如何工作的?"})
print("Answer:", result["result"])
print("\nSources:")
for doc in result["source_documents"]:
print(f" - {doc.metadata.get('source', 'unknown')}: {doc.page_content[:100]}...")
RAG 调优技巧
- chunk_size 很重要:太大会稀释相关性,太小会丢失上下文。我一般用 500-800。
- 检索数量 k 的选择:k 太小可能漏掉关键信息,k 太大会引入噪声。先从 k=5 开始。
- MMR vs 相似度:如果检索结果重复度高,用 MMR(最大边际相关性)会更好。
- rerank 可以锦上添花:检索后用一个小模型对结果重排序,能显著提升准确率。
生产环境部署建议
从实验环境到生产环境,还需要考虑稳定性等问题。
Docker 部署
# Dockerfile
FROM nvidia/cuda:12.4.0-devel-ubuntu22.04
# 安装 Python 和依赖
RUN apt-get update && apt-get install -y \
python3.10 python3-pip git \
&& rm -rf /var/lib/apt/lists/*
# 安装 vLLM
RUN pip install vllm
# 预下载模型(可选,加速启动)
# RUN python -c "from huggingface_hub import snapshot_download; snapshot_download('Qwen/Qwen2.5-7B-Instruct')"
EXPOSE 8000
ENTRYPOINT ["python", "-m", "vllm.entrypoints.openai.api_server"]
CMD ["--model", "Qwen/Qwen2.5-7B-Instruct", "--host", "0.0.0.0", "--port", "8000"]
# 构建镜像
docker build -t vllm-server:latest .
# 启动容器
docker run -d \
--gpus '"device=0"' \
--name vllm-server \
--restart unless-stopped \
-p 8000:8000 \
-v /data/models:/root/.cache/huggingface \
-e CUDA_VISIBLE_DEVICES=0 \
vllm-server:latest \
--model Qwen/Qwen2.5-7B-Instruct \
--gpu-memory-utilization 0.88 \
--max-num-seqs 128
几个细节:
-
--restart unless-stopped:容器挂了自动重启 -
-v /data/models:/root/.cache/huggingface:模型持久化,避免每次重启都下载 -
--gpu-memory-utilization 0.88:留点余量,生产环境稳定优先
多实例 + Nginx 负载均衡
单个 vLLM 实例的并发能力有限。要支撑高流量,需要多实例 + 负载均衡:
# 启动多个实例(不同端口)
for port in 8000 8001 8002 8003; do
docker run -d \
--gpus '"device='$((port-8000))'"' \
--name vllm-$port \
-p $port:8000 \
vllm-server:latest
done
# /etc/nginx/conf.d/vllm.conf
upstream vllm_cluster {
least_conn; # 最少连接数负载均衡
server 127.0.0.1:8000 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8001 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8002 weight=1 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8003 weight=1 max_fails=3 fail_timeout=30s;
keepalive 32; # 保持长连接
}
server {
listen 443 ssl http2;
server_name llm-api.example.com;
ssl_certificate /etc/ssl/certs/llm.pem;
ssl_certificate_key /etc/ssl/private/llm.key;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location /v1 {
proxy_pass http://vllm_cluster;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Connection ""; # 启用 keepalive
# 超时设置(LLM 推理可能很慢)
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 缓冲设置
proxy_buffering off; # 流式响应需要关闭缓冲
}
# 健康检查端点
location /health {
proxy_pass http://vllm_cluster/health;
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
}
}
监控告警
# monitor.py
import requests
import time
import smtplib
from email.mime.text import MIMEText
class VLLMMonitor:
def __init__(self, endpoints, alert_email):
self.endpoints = endpoints
self.alert_email = alert_email
self.healthy = {ep: True for ep in endpoints}
def check_health(self, endpoint):
try:
resp = requests.get(f"{endpoint}/health", timeout=10)
return resp.status_code == 200
except:
return False
def check_metrics(self, endpoint):
"""检查关键指标"""
try:
resp = requests.get(f"{endpoint}/metrics", timeout=10)
# 解析 Prometheus 格式的指标
metrics = {}
for line in resp.text.split('\n'):
if line and not line.startswith('#'):
parts = line.split(' ')
if len(parts) == 2:
metrics[parts[0]] = float(parts[1])
return metrics
except:
return None
def send_alert(self, message):
print(f"ALERT: {message}")
# 这里可以接入钉钉、飞书、PagerDuty 等
def run(self, interval=30):
print(f"Monitoring {len(self.endpoints)} endpoints...")
while True:
for endpoint in self.endpoints:
is_healthy = self.check_health(endpoint)
# 状态变化时告警
if is_healthy != self.healthy[endpoint]:
if is_healthy:
self.send_alert(f"✅ {endpoint} recovered")
else:
self.send_alert(f"🚨 {endpoint} is DOWN!")
self.healthy[endpoint] = is_healthy
# 检查指标
if is_healthy:
metrics = self.check_metrics(endpoint)
if metrics:
# GPU 显存使用率过高
gpu_util = metrics.get('vllm:gpu_cache_usage_perc', 0)
if gpu_util > 95:
self.send_alert(f"⚠️ {endpoint} GPU memory usage: {gpu_util:.1f}%")
# 队列积压
pending = metrics.get('vllm:num_requests_waiting', 0)
if pending > 100:
self.send_alert(f"⚠️ {endpoint} request queue: {pending}")
time.sleep(interval)
if __name__ == "__main__":
monitor = VLLMMonitor(
endpoints=[
"http://localhost:8000",
"http://localhost:8001",
],
alert_email="[email protected]"
)
monitor.run()
性能测试数据
以下是在 RTX 4090 上的实测数据:
测试环境
- GPU:NVIDIA RTX 4090 24GB
- 模型:Qwen2.5-7B-Instruct
- 输入:平均 200 tokens
- 输出:最大 512 tokens
测试结果
| 指标 | Transformers | vLLM | 提升 |
|---|---|---|---|
| 首 token 延迟 | 850ms | 85ms | 10x |
| 单请求吞吐量 | 35 tok/s | 95 tok/s | 2.7x |
| 10 并发吞吐量 | 42 tok/s | 380 tok/s | 9x |
| 50 并发吞吐量 | OOM | 420 tok/s | N/A |
| 显存占用 | 18GB | 14GB | -22% |
几个关键发现:
-
首 token 延迟是体验的关键:用户不在乎总耗时,在乎的是”有没有反应”。85ms 的首 token 延迟,用户几乎感觉不到等待。
-
vLLM 在高并发下优势更明显:单请求差距不大,50 并发时 Transformers 直接 OOM,vLLM 还游刃有余。
-
显存效率是并发能力的基础:省下的 4GB 显存可以多服务 30% 的并发请求。
常见问题排查
Q1: CUDA out of memory
这是最常见的问题。解决方案按优先级:
# 1. 降低显存利用率
--gpu-memory-utilization 0.8
# 2. 减少最大序列数
--max-num-seqs 64
# 3. 缩短上下文长度
--max-model-len 4096
# 4. 使用量化模型
--model TheBloke/Qwen-7B-Chat-AWQ --quantization awq
Q2: 模型加载巨慢
首次加载需要从 HuggingFace 下载,国内网络你懂的。解决方案:
# 方案 1:设置镜像
export HF_ENDPOINT=https://hf-mirror.com
# 方案 2:提前下载到本地
huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir ./models/qwen
# 然后用本地路径启动
--model ./models/qwen
Q3: 输出质量不如预期
可能是采样参数没调好:
SamplingParams(
temperature=0.7, # 太高会胡说八道,太低会重复
top_p=0.9, # 和 temperature 配合使用
repetition_penalty=1.1, # 避免重复
presence_penalty=0.1, # 鼓励新话题
)
Q4: 流式输出有问题
确保 Nginx 配置正确:
proxy_buffering off; # 关闭缓冲
proxy_cache off; # 关闭缓存
chunked_transfer_encoding on; # 启用分块传输
结语
写到这里,我想说的不只是 vLLM 的技术细节。
过去一年,大模型从实验室走向生产环境。我见过太多团队卡在”最后一公里”——模型效果不错,但推理成本太高、延迟太大、并发上不去。vLLM 的出现,把这道门槛大大降低了。
vLLM 降低了高性能推理的门槛,使得单卡部署也能支撑不错的并发量。
对于大模型落地,vLLM 是一个非常值得尝试的方案。它不是万能的——超长上下文、超低延迟、超大模型,都有更专业的方案。但对于 90% 的场景,它是最佳选择。
参考资料
- vLLM 官方文档
- PagedAttention 论文:Efficient Memory Management for Large Language Model Serving with PagedAttention
- vLLM GitHub 仓库
- Hugging Face Text Generation Inference
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 vLLM 高性能推理系列——入门篇 》
本文链接:http://localhost:3015/ai/vLLM%E9%AB%98%E6%80%A7%E8%83%BD%E6%8E%A8%E7%90%86%E9%83%A8%E7%BD%B2.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!