第4章:快速上手——加载第一个模型
好,咱们现在开始动真格的了。
前面几章我们把环境搭好了,概念也理清了。现在,我带你亲手加载一个模型,发一个推理请求出去。说实话,我第一次用vLLM跑LLaMA的时候,心里还挺忐忑的——怕显存爆了,怕速度慢,怕各种报错。但跑通之后,那种感觉确实很爽。
4.1 加载模型:三行代码搞定
先别管什么分布式、量化、调度策略。咱们先跑起来再说。
我个人习惯用Python脚本直接调vLLM的API,而不是先启动服务。这样调试起来更直接。你打开终端,进到你的虚拟环境里,然后敲下面这段代码:
from vllm import LLM, SamplingParams
# 加载模型
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")
# 设置采样参数
sampling_params = SamplingParams(temperature=0.7, top_p=0.9, max_tokens=512)
# 发送推理请求
outputs = llm.generate("请用一句话解释什么是大语言模型", sampling_params)
# 打印结果
print(outputs[0].outputs[0].text)
嗯,就这么简单。你运行一下,如果一切正常,几秒钟后就能看到模型输出的中文回答。
model="本地路径" 来加载。我在公司内网环境就吃过这个亏,等了好久才发现是网络问题。
4.2 支持哪些模型?
vLLM目前对主流开源模型的支持已经很成熟了。我列个表,你一看就明白:
| 模型系列 | 加载方式 | 我踩过的坑 |
|---|---|---|
| LLaMA / LLaMA 2 / LLaMA 3 | model="meta-llama/Llama-2-7b-chat-hf" |
记得用chat版本,base版对话效果差 |
| Qwen 2.5 | model="Qwen/Qwen2.5-7B-Instruct" |
这个我推荐新手用,中文支持最好 |
| ChatGLM3 / GLM-4 | model="THUDM/glm-4-9b-chat" |
注意要用chat后缀,否则格式不对 |
| Mistral / Mixtral | model="mistralai/Mistral-7B-Instruct-v0.3" |
Mixtral 8x7B需要至少48G显存 |
你想想看,这些模型在HuggingFace上都是公开的。vLLM直接兼容它们的权重格式,不需要你做任何转换。这一点确实省心。
4.3 理解vLLM的输出格式
第一次看到输出结果时,你可能会觉得有点奇怪。为什么是个嵌套结构?
我来拆解一下。vLLM的返回对象是 RequestOutput,它长这样:
outputs = llm.generate(prompts, sampling_params)
# outputs 是一个列表,每个元素对应一个prompt
for output in outputs:
# output.request_id # 请求的唯一ID
# output.prompt # 你输入的原始文本
# output.outputs # 模型生成的结果列表(支持多个候选)
for seq in output.outputs:
# seq.text # 生成的文本
# seq.token_ids # 对应的token ID列表
# seq.cumulative_logprob # 累计对数概率
# seq.finish_reason # 结束原因:stop / length
为什么会设计成 outputs 是一个列表?因为你可以设置 n=3,让模型一次返回3个不同的候选结果。这在做对比实验时特别有用。
outputs[0].outputs[0].text 就行。但如果你要做后处理、概率分析、或者多候选排序,那就要用到上面这些字段了。
4.4 单次推理 vs 流式推理
好,现在咱们聊聊两种推理模式。说白了,就是一个等全部结果出来再给你,另一个边生成边给你。
4.4.1 单次推理(非流式)
刚才我们用的就是单次推理。你发一个请求,vLLM把整个回答生成完,然后一次性返回。优点是简单,缺点是如果回答很长,你要等很久才能看到第一个字。
# 单次推理
outputs = llm.generate("写一首关于春天的诗", sampling_params)
print(outputs[0].outputs[0].text)
4.4.2 流式推理(Streaming)
流式推理就不一样了。它像打字一样,一个字一个字地往外吐。用户体验更好,用户不用干等。
在vLLM里,流式推理需要设置 stream=True:
from vllm import LLM, SamplingParams
llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")
sampling_params = SamplingParams(temperature=0.7, max_tokens=512)
# 流式生成
outputs = llm.generate("写一首关于春天的诗", sampling_params, stream=True)
for output in outputs:
# 每次迭代只返回新生成的token
for seq in output.outputs:
if seq.text:
print(seq.text, end="", flush=True)
注意看,这里 outputs 变成了一个生成器。每次循环,你拿到的是最新生成的那一小段文本。我用这个模式做过一个聊天机器人,用户反馈说「感觉模型在实时思考」,体验确实好很多。
outputs 的每个元素仍然是 RequestOutput 对象。但它的 outputs[0].text 只包含增量文本,不是完整结果。如果你需要完整结果,可以自己拼接,或者等最后一个 finish_reason 不为 None 时再取。
4.5 一张图看懂本章核心逻辑
说了这么多,我画个流程图帮你理一理。从加载模型到拿到结果,整个链路是这样的:
这张图把整个流程串起来了。你从顶部开始,加载模型、设参数、发请求,然后根据是否流式走不同的分支。我个人建议,开发调试时用非流式,上线给用户用流式。
4.6 避坑指南
最后,分享几个我实际踩过的坑:
- 显存不够怎么办? 我曾经用一张RTX 3090(24G)跑Qwen2.5-7B,直接OOM了。后来加了
--max-model-len 2048参数,限制最大序列长度,才跑起来。你也可以试试量化版本。 - 模型加载慢? 第一次加载要下载权重,确实慢。我建议你提前用
huggingface-cli download下载到本地,然后指定本地路径。 - 输出乱码? 检查一下模型名称。如果是Chat模型,一定要用带
-Chat或-Instruct后缀的版本。Base模型不会做对话格式处理。 - 流式输出卡顿? 把
max_tokens设小一点,比如128。如果设成2048,流式输出会持续很久,前端体验不好。
好了,这一章的内容就到这里。你现在应该能亲手加载一个模型,发一个推理请求,并且理解单次和流式的区别了。下一章我们聊聊更高级的话题——如何用vLLM搭建一个真正的API服务。