快速上手:加载第一个模型
说实话,第一次用 vLLM 加载模型的时候,我心里还挺忐忑的。毕竟之前用其他推理框架,光是装依赖就能折腾半天。但 vLLM 给我的第一印象就是——干净利落。
今天咱们就拿 Qwen2.5-7B 来开刀。这个模型不大不小,刚好适合练手。你想想看,7B 参数在消费级显卡上也能跑,我自己的 4090 就跑得挺欢。
环境准备
先确认你装了 vLLM。我个人习惯用 pip 安装:
pip install vllm
嗯,这里要注意。如果你用的是 CUDA 11.8 或 12.1,vLLM 会自动匹配对应的 wheel 包。我遇到过有人用 CUDA 11.7 死活装不上,后来才发现是版本不匹配。
加载模型
加载模型就一行代码。但这一行背后,vLLM 做了很多事:
from vllm import LLM
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")
运行这段代码,你会看到控制台刷刷刷地输出日志。它先下载模型(如果本地没有),然后分配显存,最后初始化推理引擎。整个过程大概 30 秒到 1 分钟,取决于你的网络和硬盘速度。
dtype="float16" 参数。这样显存占用直接减半,我经常在调试时这么干。
单次推理(离线推理)
模型加载好了,咱们来问它一个问题。vLLM 的离线推理接口非常直观:
prompt = "请用一句话解释什么是大语言模型"
output = llm.generate(prompt)
print(output[0].outputs[0].text)
输出结果:
大语言模型是一种基于深度学习的人工智能模型,能够理解和生成人类语言。
你看,就这么简单。但这里有个坑——llm.generate() 返回的是一个列表,哪怕你只传了一个 prompt。我第一次用的时候就忘了取索引,直接打印整个对象,结果看到一堆乱码。
llm.generate() 返回的是 RequestOutput 对象列表。每个 prompt 对应一个 RequestOutput,里面又包含多个 CompletionOutput(如果你设置了 n>1)。
理解输出结构
咱们来拆解一下这个输出对象。说白了,vLLM 的输出是一个三层嵌套结构:
output = llm.generate(prompt)
# 第一层:RequestOutput
request = output[0]
# 第二层:CompletionOutput(可能有多个)
completion = request.outputs[0]
# 第三层:具体内容
print(completion.text) # 生成的文本
print(completion.token_ids) # token ID 列表
print(completion.logprobs) # 每个 token 的对数概率
为什么会这样设计?因为 vLLM 支持批量推理和多次采样。你想想看,如果你一次传了 10 个 prompt,每个 prompt 生成 3 个候选结果,那输出就是 10×3 的矩阵。这种结构刚好能容纳所有组合。
token 和 logprobs
token 是模型理解的最小单位。一个中文词可能被拆成 1 到 3 个 token。比如「大语言模型」这四个字,在 Qwen2.5 的 tokenizer 里会被拆成:
['大', '语言', '模型']
嗯,这里要注意。中文的 token 化跟英文不一样。英文一个单词通常是一个 token,但中文一个词可能被拆得更细。我在项目中遇到过,同样的 prompt,中文比英文多占 30% 的 token 长度。
logprobs 是每个 token 的对数概率。数值越大,说明模型对这个 token 越有把握。比如:
token: '大' logprob: -0.12
token: '语言' logprob: -0.08
token: '模型' logprob: -0.15
你看,「语言」的 logprob 最高(-0.08),说明模型最确定这个词。而「模型」的 logprob 稍低,可能模型在「模型」和「系统」之间犹豫了一下。
完整示例代码
最后,给你一个完整的离线推理脚本。我习惯把参数都显式写出来,这样调试时一目了然:
from vllm import LLM, SamplingParams
# 初始化模型
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
dtype="float16",
max_model_len=4096
)
# 设置采样参数
sampling_params = SamplingParams(
temperature=0.7,
top_p=0.9,
max_tokens=512,
logprobs=5 # 返回 top-5 的 logprobs
)
# 准备 prompt
prompts = [
"什么是机器学习?",
"解释一下反向传播算法"
]
# 批量推理
outputs = llm.generate(prompts, sampling_params)
# 遍历结果
for i, output in enumerate(outputs):
print(f"Prompt {i+1}: {output.prompt}")
for completion in output.outputs:
print(f"生成: {completion.text}")
print(f"Token IDs: {completion.token_ids[:10]}...")
print(f"Top logprobs: {completion.logprobs[0]}")
print("-" * 50)
运行这段代码,你会看到每个 prompt 的生成结果,以及前几个 token 的 logprobs。我个人习惯把 logprobs 打印出来,看看模型在每个 token 上的置信度分布。
max_tokens 设得太大(比如 4096),结果显存直接爆了。建议根据你的显存大小来设置,一般 7B 模型 512 到 1024 个 token 就够用了。
知识体系图
下面这张图总结了本章的核心逻辑。从加载模型到理解输出,每一步都有对应的数据结构和参数:
这张图把整个流程串起来了。从左到右,先加载模型,再设置参数,然后执行推理,最后解析输出。输出结构是三层嵌套,每一层都有明确的用途。
好了,现在你已经掌握了 vLLM 离线推理的基本操作。加载模型、单次推理、理解输出结构——这三步走完,你就正式入门了。剩下的就是多跑几个例子,熟悉各种参数的效果。