7B 的推理模型在单卡上跑起来并不难,vLLM 是最省心的路径之一:一条命令就能把模型变成 OpenAI 兼容的 API 服务。这篇以 DeepSeek-R1-Distill-Qwen-7B 为例,从装环境、下模型到离线推理、起服务、测吞吐完整走一遍。它是 DeepSeek-R1 的蒸馏版本,把满血版的推理能力蒸馏进 Qwen 架构的小模型里,7B 的体积单卡就能跑,适合想在自己机器上体验推理模型的开发者和学生。
vLLM 是什么
vLLM 是一个高效的大语言模型推理和部署服务系统 [1]。它的几个特性正好对应部署痛点,部署推理模型时尤其明显:内存管理上,PagedAttention 算法高效管理 KV 缓存,减少内存浪费;吞吐上支持异步处理和连续批处理请求;易用性上与 HuggingFace 模型无缝集成,兼容 OpenAI 的 API 服务器;多卡环境支持分布式推理,靠模型并行策略处理大模型;再加上开源属性带来的活跃社区 [1]。对个人部署者来说,最直观的收益是显存和吞吐:KV 缓存管得好,同样的显存能塞下更长的上下文和更多并发请求,这也是它比直接用 transformers 起服务快得多的原因。
环境和依赖
参考环境:Ubuntu 22.04、Python 3.12、CUDA 12.1、PyTorch 2.3.0,PyTorch 的 CUDA 环境要提前配好 [1]。安装依赖先给 pip 换清华镜像源加速,五个包全部锁死版本 [1]:
python -m pip install --upgrade pip
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install modelscope==1.22.3
pip install openai==1.61.0
pip install tqdm==4.67.1
pip install transformers==4.48.2
pip install vllm==0.7.1锁版本不是习惯问题而是必要动作:推理框架对版本敏感,vllm 0.7.1 配 transformers 4.48.2,随手装最新版很容易报错。如果装依赖阶段就卡住,优先检查两件事:CUDA 版本和 PyTorch 是否匹配、Python 版本是否在 3.12 这一档。
下载模型
用 modelscope 的 snapshot_download 下载,第一个参数是模型名,cache_dir 是下载路径 [1]:
from modelscope import snapshot_download
model_dir = snapshot_download('deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', cache_dir='/root/autodl-tmp', revision='master')存成 model_download.py,执行 python model_download.py,7B 模型有十几个 GB,耐心等下载完成再往下走 [1]。下载完成后模型就固定在 cache_dir 里,后面脚本和服务都用这个路径,别在两个目录里各存一份。cache_dir 要改成自己的模型存放路径,国内网络环境用 modelscope 源比从 HuggingFace 拉模型稳定得多。
离线推理:第一个脚本
新建 vllm_model.py。脚本分三块:设置 VLLM_USE_MODELSCOPE 环境变量,让自动下载走 modelscope 而不是 HuggingFace;一个 get_completion 函数封装采样参数和推理引擎;主流程指定模型路径和输入 [1]:
# vllm_model.py
from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
import os
import json
# 自动下载模型时,指定使用modelscope; 否则,会从HuggingFace下载
os.environ['VLLM_USE_MODELSCOPE']='True'
def get_completion(prompts, model, tokenizer=None, max_tokens=8192, temperature=0.6, top_p=0.95, max_model_len=2048):
stop_token_ids = [151329, 151336, 151338]
# 创建采样参数。temperature 控制生成文本的多样性,top_p 控制核心采样的概率
sampling_params = SamplingParams(temperature=temperature, top_p=top_p, max_tokens=max_tokens, stop_token_ids=stop_token_ids)
# 初始化 vLLM 推理引擎
llm = LLM(model=model, tokenizer=tokenizer, max_model_len=max_model_len,trust_remote_code=True)
outputs = llm.generate(prompts, sampling_params)
return outputs采样参数不是随手设的:DeepSeek 官方建议思考类模型的 temperature 应在 0.5–0.7 之间、推荐 0.6;max_tokens 设 8192,因为思考过程本身就要消耗大量输出 token [1]。vLLM 的封装很省事——直接传模型名或路径即可,不必手动初始化模型和分词器 [1]。
if __name__ == "__main__":
# 初始化 vLLM 推理引擎
model='/root/autodl-temp/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B' # 指定模型路径
# model="deepseek-ai/DeepSeek-R1-Distill-Qwen-7B" # 指定模型名称,自动下载模型
tokenizer = None
# 加载分词器后传入vLLM 模型,但不是必要的。
# tokenizer = AutoTokenizer.from_pretrained(model, use_fast=False)
text = ["请帮我制定个简短的初学者Python学习计划<think>\n", ] # 可用 List 同时传入多个 prompt,根据 DeepSeek 官方的建议,每个 prompt 都需要以 <think>\n 结尾,如果是数学推理内容,建议包含(中英文皆可):Please reason step by step, and put your final answer within \boxed{}.
# messages = [
# {"role": "user", "content": prompt+"<think>\n"}
# ]
# 作为聊天模板的消息,不是必要的。
# text = tokenizer.apply_chat_template(
# messages,
# tokenize=False,
# add_generation_prompt=True
# )
outputs = get_completion(text, model, tokenizer=tokenizer, max_tokens=8192, temperature=0.6, top_p=0.95, max_model_len=2048) # 思考需要输出更多的 Token 数,max_tokens 设为 8K,根据 DeepSeek 官方的建议,temperature应在 0.5-0.7,推荐 0.6输入有两个约定要记住:每个 prompt 都要以 <think>\n 结尾(DeepSeek 官方建议);做数学推理时,提示里建议加上「Please reason step by step, and put your final answer within \boxed{}.」,中英文皆可 [1]。
# 输出是一个包含 prompt、生成文本和其他信息的 RequestOutput 对象列表。
# 打印输出。
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
if r"</think>" in generated_text:
think_content, answer_content = generated_text.split(r"</think>")
else:
think_content = ""
answer_content = generated_text
print(f"Prompt: {prompt!r}, Think: {think_content!r}, Answer: {answer_content!r}")输出是 RequestOutput 对象列表,代码按 </think> 把思考内容和最终答案切开分别打印 [1]。执行 python vllm_model.py,可以看到模型先输出一大段推理过程,再给出按阶段划分的学习计划 [1]。离线推理适合批量处理——一次传入一组 prompt 让引擎自己调度,做数据标注、评测这类批处理任务时比逐条请求服务端效率更高。这个「先想后答」的输出结构是推理模型的标志,也解释了为什么 max_tokens 要给到 8K:思考部分动辄几千 token,按普通对话的几百 token 设置会被截断在思考阶段,根本看不到答案。
起 OpenAI 兼容服务
比一次性脚本更常用的方式是把模型变成服务——好处是任何能说 OpenAI 协议的客户端(现成的 SDK、聊天前端、你自己的应用)都能直接接上,不用为每个调用方写一遍推理代码。DeepSeek-R1-Distill-Qwen 兼容 OpenAI API 协议,vLLM 一条命令就能起一个 OpenAI 兼容服务器:默认 http://localhost:8000,当前一次托管一个模型,实现模型列表、completions、chat completions 三类端点 [1]。completions 面向文本生成任务(文章、故事、邮件),chat completions 面向对话任务(聊天机器人、对话系统)[1]:
python -m vllm.entrypoints.openai.api_server \
--model /root/autodl-tmp/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B \
--served-model-name DeepSeek-R1-Distill-Qwen-7B \
--max-model-len=2048参数里 --model 指模型路径,--served-model-name 是对外暴露的模型名,--max-model-len 限最大长度,地址和端口用 --host/--port 调整,聊天模板用 --chat-template 指定 [1]。启动后先看模型列表 [1]:
curl http://localhost:8000/v1/models测 completions 接口,提示词同样以 <think>\n 结尾,temperature 设 0 [1]:
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "DeepSeek-R1-Distill-Qwen-7B",
"prompt": "我想问你,5的阶乘是多少?<think>\n",
"max_tokens": 1024,
"temperature": 0
}'返回文本先列推理步骤,最后以 \boxed{120} 给出答案,usage 显示 prompt 13 tokens、completion 164 tokens [1]。Python 客户端的接法如下,api_key 随便填,只是为了通过接口参数校验 [1]:
# vllm_openai_completions.py
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="sk-xxx", # 随便填写,只是为了通过接口参数校验
)
completion = client.chat.completions.create(
model="DeepSeek-R1-Distill-Qwen-7B",
messages=[
{"role": "user", "content": "我想问你,5的阶乘是多少?<think>\n"}
]
)
print(completion.choices[0].message)两类接口的分工可以记成一句话:要续写用 completions,要对话用 chat completions。chat 接口把多轮对话组织成 messages 数组,每条消息带角色标记,模型在对话历史的基础上接着说 [1]:
# vllm_openai_chat_completions.py
from openai import OpenAI
openai_api_key = "sk-xxx" # 随便填写,只是为了通过接口参数校验
openai_api_base = "http://localhost:8000/v1"
client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)
chat_outputs = client.chat.completions.create(
model="DeepSeek-R1-Distill-Qwen-7B",
messages=[
{"role": "user", "content": "什么是深度学习?"},
]
)
print(chat_outputs)测一测吞吐
vLLM 自带 benchmark_throughput.py 脚本。常用参数:--backend 可选 vllm、hf、mii 三种推理后端;--input-len/--output-len 设输入输出长度;--num-prompts 设 prompt 数量;--seed 固定随机种子;不设 --dataset 会自动生成数据 [1]:
python benchmark_throughput.py \
--model /root/autodl-tmp/qwen/DeepSeek-R1-Distill-Qwen-7B \
--backend vllm \
--input-len 64 \
--output-len 128 \
--num-prompts 25 \
--seed 2025 \
--dtype float16 \
--max-model-len 51225 个 prompt、输入 64、输出 128 的设置下,测得 Throughput: 29.34 requests/s、5632.68 total tokens/s、3755.12 output tokens/s [1]。换硬件、改参数之后重跑一次,就能量化改动带来的差别。
常见问题
部署推理模型最常见的三类问题,都可以提前预防:
- 显存不足。7B 模型本身不大,但 KV 缓存随并发和上下文长度增长,报显存相关错误时先把 --max-model-len 调小(示例里的 2048 就是保守值),再降低并发;
- 输出没有答案。几乎都是 max_tokens 太小或提示词忘了加
<think>\n结尾,模型还在思考阶段输出就被截断; - 接口报模型不存在。检查请求里的 model 字段和 --served-model-name 是否一致——两者对不上,服务找不到要调的模型。
本文改编自 Datawhale 开源教程《self-llm》的 DeepSeek-R1-Distill-Qwen-7B vLLM 部署一节[1],原文以 Apache-2.0 许可证发布。
来源
- 104-DeepSeek-R1-Distill-Qwen-7B vLLM 部署调用 | self-llmDatawhale self-llm (Apache-2.0)
引用本页: DeepSeek-R1 蒸馏版用 vLLM 部署. TellWorld, https://www.tellworld.net/zh/practice/practice-deepseek-r1-distill-vllm. 更新于 2026-11-14