3、vLLM核心API详解:LLM类与AsyncLLMEngine、SamplingParams参数配置、流式输出与批处理
好,咱们直接进入正题。vLLM 这个框架,说白了就是帮你把大模型跑得更快、更省显存。但光知道它快没用,你得会用它的核心 API。今天我就带你把这几个关键组件掰开揉碎讲清楚。
3.1 LLM类:最常用的同步接口
我个人习惯,在写脚本或者做快速验证的时候,首选就是 LLM 类。它是个同步接口,用起来特别直接。
核心要点:LLM 类封装了模型加载、推理、显存管理全流程。你只需要传一个模型路径,它就能跑起来。
from vllm import LLM, SamplingParams
# 加载模型
llm = LLM(model="meta-llama/Llama-2-7b-chat-hf")
# 配置采样参数
params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=512)
# 单条推理
output = llm.generate("什么是注意力机制?", params)
print(output[0].outputs[0].text)
嗯,这里要注意。我第一次用的时候,以为 generate 返回的是字符串,结果发现是个对象列表。每个 output 里还包了一层 outputs。为什么这么设计?因为 vLLM 支持一次传多条 prompt,返回结构就得是嵌套的。
避坑指南:我曾经在批量生成时,忘了检查 outputs 列表的长度。如果某个 prompt 因为特殊 token 被截断了,它的 outputs 可能为空。记得加个 if output.outputs 判断。
3.2 AsyncLLMEngine:高性能异步引擎
如果你要搭一个在线服务,或者需要高并发处理请求,那 AsyncLLMEngine 才是你的菜。它底层用的是 asyncio 事件循环,能同时处理多个请求。
你想想看,同步接口一次只能处理一个请求,后面的都得排队。异步引擎就不一样了,它可以在等待 GPU 计算的时候,去处理其他请求的预处理工作。
from vllm import AsyncLLMEngine, SamplingParams
from vllm.engine.arg_utils import AsyncEngineArgs
# 配置引擎参数
engine_args = AsyncEngineArgs(
model="meta-llama/Llama-2-7b-chat-hf",
max_num_batched_tokens=4096,
max_num_seqs=256
)
# 创建异步引擎
engine = AsyncLLMEngine.from_engine_args(engine_args)
# 异步生成
async def generate_async(prompt):
params = SamplingParams(temperature=0.8, max_tokens=256)
results = []
async for output in engine.generate(prompt, params, request_id="req_001"):
results.append(output)
return results
重要提醒:AsyncLLMEngine 的 generate 方法返回的是一个异步生成器。你必须用 async for 来迭代,不能直接 await。我刚开始就犯过这个错,以为它返回的是完整结果。
3.3 SamplingParams:控制生成行为的核心参数
SamplingParams 是 vLLM 里最常用的配置类。它决定了模型怎么「选词」。我把它分成三组来讲:
| 参数类别 | 参数名 | 作用 | 我的建议值 |
|---|---|---|---|
| 多样性控制 | temperature | 控制随机性,值越高越随机 | 0.7(创意任务) / 0.1(精确任务) |
| 多样性控制 | top_p | 核采样,只从概率和超过 p 的 token 中选 | 0.9 |
| 多样性控制 | top_k | 只从概率最高的 k 个 token 中选 | 40 |
| 长度控制 | max_tokens | 最大生成 token 数 | 根据任务定,别设太大浪费显存 |
| 长度控制 | min_tokens | 最小生成 token 数 | 0(除非你要强制生成长文本) |
| 重复惩罚 | repetition_penalty | 惩罚重复出现的 token | 1.1(轻度惩罚) |
| 重复惩罚 | frequency_penalty | 根据 token 出现频率惩罚 | 0.0(一般不调) |
实战经验:我在做代码生成任务时,会把 temperature 设到 0.1 以下,甚至直接用 0。因为代码需要确定性。但写文案或者故事时,我会用 0.8 到 1.0,让输出更有创意。
3.4 流式输出:让用户不再干等
流式输出,说白了就是一边生成一边返回结果。用户体验会好很多——用户不用盯着空白页面等十几秒。
vLLM 的流式输出实现得很优雅。你只需要在 SamplingParams 里设置 stream=True,然后迭代输出即可。
from vllm import LLM, SamplingParams
llm = LLM(model="Qwen/Qwen2-7B-Instruct")
params = SamplingParams(
temperature=0.7,
max_tokens=1024,
stream=True # 开启流式输出
)
# 流式生成
for output in llm.generate("写一首关于春天的诗", params):
# output 是逐步返回的
print(output.outputs[0].text, end="", flush=True)
小技巧:流式输出时,每个 output 对象里只有新生成的 token 文本。如果你想拿到完整结果,需要自己拼接。我一般会在循环外维护一个 full_text = "" 变量,每次迭代时 full_text += token。
3.5 批处理:榨干 GPU 的每一分算力
批处理是 vLLM 的杀手锏。它利用 PagedAttention 技术,把多个请求打包在一起处理,显存利用率能提升好几倍。
你想想看,如果一次只处理一个请求,GPU 的算力大部分时间都在空转。批处理就能让 GPU 满负荷运转。
from vllm import LLM, SamplingParams
llm = LLM(model="mistralai/Mistral-7B-v0.1")
# 准备一批 prompt
prompts = [
"解释一下量子纠缠",
"写一段 Python 代码实现快速排序",
"翻译成英文:今天天气真好",
"总结一下深度学习的历史"
]
# 统一的采样参数
params = SamplingParams(temperature=0.7, max_tokens=512)
# 批量生成
outputs = llm.generate(prompts, params)
# 处理结果
for i, output in enumerate(outputs):
print(f"Prompt {i+1}: {output.outputs[0].text[:50]}...")
性能陷阱:批处理不是越大越好。我曾经把 batch size 设到 256,结果显存直接爆了。vLLM 虽然做了显存优化,但每个请求的 max_tokens 和模型大小都会影响显存占用。建议从 32 开始试,逐步往上加。
3.6 知识体系总览
下面这张图是我自己画的,把今天讲的几个核心概念串起来了。你看一眼就能明白它们之间的关系。
这张图里,LLM 类和 AsyncLLMEngine 是两种使用方式,SamplingParams 是它们的共同配置。批处理和流式输出是 vLLM 的两大特色功能,一个提升吞吐,一个改善体验。
总结一下:LLM 类适合脚本和快速验证,AsyncLLMEngine 适合生产环境。SamplingParams 是控制生成质量的关键,流式输出提升用户体验,批处理榨干 GPU 性能。把这几个点吃透了,vLLM 你就掌握了七八成。
最后说一句:API 文档写得再好,也不如自己跑一遍代码。我建议你打开 Jupyter Notebook,把上面的代码示例挨个跑一遍。遇到报错别慌,看看错误信息,八成是路径写错了或者显存不够。
公众号:蓝海资料掘金营,微信deep3321