第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)

嗯,就这么简单。你运行一下,如果一切正常,几秒钟后就能看到模型输出的中文回答。

小提示:第一次加载模型时,vLLM会下载权重文件。如果你网络不太好,建议先手动下载到本地,然后用 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 一张图看懂本章核心逻辑

说了这么多,我画个流程图帮你理一理。从加载模型到拿到结果,整个链路是这样的:

vLLM 推理流程 加载模型 设置采样参数 发送推理请求 流式? (stream?) 逐token返回 (增量输出) 一次性返回 (完整结果) 图:vLLM 推理流程——从加载模型到获取结果

这张图把整个流程串起来了。你从顶部开始,加载模型、设参数、发请求,然后根据是否流式走不同的分支。我个人建议,开发调试时用非流式,上线给用户用流式。

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服务。


专注资料整理