可去vllm官网提供的镜像地址拉取
地址:Using Docker — vLLM
Vllm镜像运行需要不同的cuda版本依赖,如上vllm/vllm-openai:v0.7.2需要cuda12.1方可运行。
DeepSeek-R1-Distill-Qwen-32B可去modelscope下载:整体大小约为60GB
部署DeepSeek-R1-Distill-Qwen-32B模型,A40显卡,每张显存为48GB,四卡部署时每张卡显存占用情况统计:
model weights take 15.41GiB;
non_torch_memory takes 0.25GiB;
PyTorch activation peak memory takes 2.60GiB;
the rest of the memory reserved for KV Cache is 17.22GiB.
Graph capturing finished in 24 secs, took 2.29 GiB
仅仅模型权重加载,总计消耗约60G显存
docker run -it -d --restart always --name vllm_ds32 -v /data/chuangchuang/LLM_stores:/data -p 18005:8000 --ipc=host --gpus '"device=1,4,5,7"' vllm/vllm-openai:v0.7.2 --dtype bfloat16 --served-model-name deepseek-qwen32B --gpu-memory-utilization 0.8 --model "/data/DeepSeek-R1-Distill-Qwen-32B" --tensor-parallel-size 4 --max-model-len 30000
参数说明:
-it(等价于 -i -t)
-i(--interactive)
保持容器的标准输入(STDIN)开放,允许用户与容器交互(如输入命令)。
-t(--tty)
为容器分配一个伪终端(pseudo-TTY),使容器的输出格式化为终端友好的形式(如支持颜色、光标移动)。
-d(--detach)
让容器在后台运行(守护进程模式),不占用当前终端
--restart always
让容器自启动,因意外中断可自行重启
--name
指定容器名为vllm_ds32
-v
本地目录与容器内目录进行映射,如上命令将本地/data/chuangchuang/LLM_stores路径与容器内/data路径映射
-p
端口映射,18005:8000 ,本地18005端口和容器内8000端口映射
--gpus '"device=1,4"'
指定使用本地服务器某几张显卡
--dtype
指定模型参数类型,可选项为 auto (自行根据情况加载) 、float32、bfloat16、float16等,可自行查阅部署模型可选参数类型有哪些,不知道的情况可选 auto 。若模型支持bfloat16,建议选择此项,同时兼具保证模型性能良好和显存消耗降低。
--served-model-name
为部署模型自定义名称
--gpu-memory-utilization
控制 GPU显存的利用率,范围在 0~1 之间。默认值:0.9(即占用 90% 的可用显存)。
当多卡部署时,默认每张卡都会占用相同比例显存,若某张卡显存不足,可能导致失败
--model
容器内模型所在路径地址
--tensor-parallel-size
多卡部署,指定所需卡数,其需要和--gpus参数所设置显卡数量保持一致
可选参数:
--max-model-len 16384
设置模型处理的最大上下文长度(token数)。默认行为从模型配置(如 config.json 的 max_position_embeddings)自动推断。Deepseek-32B模型默认为131072,7B类型的模型一般为3万多,具体视情况而定
--enforce-eager
强制使用 PyTorch Eager 模式,禁用 CUDA 图优化。默认行为:混合使用 CUDA 图和 Eager 模式以优化性能。启用场景:调试兼容性问题(如内核不兼容报错);避免 CUDA 图内存泄漏(长期运行服务);性能影响:可能降低推理速度 10%~30%。
--privileged(Docker 参数)
作用:赋予容器 特权模式,允许访问宿主机设备(如 GPU、NVIDIA 驱动)。
必要性:在容器内使用 GPU 时通常需启用,否则无法调用 CUDA 接口
--enable-auto-tool-choice
启用模型的自动工具选择能力,允许模型根据用户输入和预定义的工具列表,自动决定是否需要调用工具以及选择具体的工具。默认行为:若未启用该参数,模型可能仅生成文本回复,而不会触发工具调用逻辑。
--tool-call-parser hermes
指定模型工具调用响应的解析器风格,hermes 表示使用与 Hermes 模型兼容的解析格式。
Hermes 格式特点:工具调用结果以 JSON 形式嵌套在 tool_calls 字段中。
每个工具调用包含 id、function.name(工具名称)和 function.arguments(参数) 。
其他可选解析器:default:vLLM 原生格式(可能兼容性较差)。自定义解析器(需扩展 vLLM 代码)。
更多可选参数可查阅:
https://vllm.hyper.ai/docs/models/engine-arguments/
情况1 :ValueError: Total number of attention heads (40) must be divisible by tensor parallel size (3).
错误原因
模型注意力头数(40)无法被当前设置的 tensor_parallel_size(3) 整除。
在分布式训练/推理中,注意力头需要均匀分配到每个张量并行组(Tensor Parallel Group)中,因此必须满足整除关系。
解决方法
调整 tensor_parallel_size
将 tensor_parallel_size 参数设置为 40 的因数(1, 2, 4, 5, 8, 10, 20, 40)
在部署deepseek-32B模型时遇见此问题,故其多卡部署要求卡数可选为1, 2, 4, 5, 8, 10, 20, 40
情况2:torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 20.00 MiB. GPU 1 has a total capacity of 44.35 GiB of which 14.12 MiB is free. Process 38541 has 6.31 GiB memory in use. Process 22815 has 38.00 GiB memory in use. Of the allocated memory 37.10 GiB is allocated by PyTorch, with 42.00 MiB allocated in private pools (e.g., CUDA Graphs), and 346.64 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.
加载CUDA优化显存报错,解决办法降低--gpu-memory-utilization 参数数值,降低显卡显存占用比例,给与一定空余。
或设置 --enforce-eager禁用CUDA优化,不建议禁用,禁用后会导致推理速度降低
情况3:The model's max seq len (131072) is larger than the maximum number of tokens that can be stored in KV cache (61936). Try increasing `gpu_memory_utilization` or decreasing `max_model_len` when initializing the engine.
模型处理的最大上下文长度(token数)太长导致显存不足,可设置--max-model-len
降低显存占用。
基于url直接调用
注意url路径和模型名称要正确,模型名称是启动容器时自己设定的
模型名称获取也可通过调用/v1/models获取
url_model = 'http://192.168.124.xxx:18005/v1/models'
respond_model = requests.get(url_model)
model = respond_model.content.decode('utf-8')
model_json = json.loads(model)
model_name = model_json['data'][0]['id']
print(model_name)
import requests
url = 'http://192.168.124.xxx:18005/v1/chat/completions'
MESSAGES = [
{"role": "system",
"content": "你是一名诗歌达人,请帮助用户回答相关问题"},
{"role": "user", "content": "请给我写一首简短欢快的诗,要求诗的意境要乐观,要求诗的意境要乐观."}]
input_model = {
"model": "deepseek-qwen32B",
"messages": MESSAGES,
"temperature": 0.7,
"top_p": 0.8,
"repetition_penalty": 1.05,
"max_tokens": 512,
"stream": False,
}
import json
response = requests.post(url, json=input_model)
if response.status_code == 200:
completition = response.content.decode('utf-8')
completition = json.loads(completition)
res = completition["choices"][0]["message"]["content"]
print(res)
print(f"{response.status_code}")
else:
print(f"Error: {response.status_code}")
基于url直接调用+function_call功能实现
注意使用函数调用功能需要模型本身支持此功能,ds系列蒸馏模型不支持,qwen2.5-7b模型支持;此外还需要在使用vllm部署模型时设置--enable-auto-tool-choice --tool-call-parser hermes参数方可使用
import requests
url = 'http://192.168.124.xxx:18000/v1/chat/completions'
TOOLS = [
{
"type": "function",
"function": {
"name": "Classify",
"description": "对用户输入的问题进行分类,分类结果有三种,分别为‘公司招股说明书咨询问题’,‘股票基金数据查询问题’,‘其他问题’",
"parameters": {
"type": "object",
"properties": {
"招股说明书咨询": {
"type": "string",
"description": "如果对用户输入问题分类后,认为是招股说明书咨询问题,则返回内容为'1',否则返回'0'",
},
"数据查询": {"type": "string",
"description": "如果对用户输入问题分类后,认为是数据查询问题,则返回内容为'1',否则返回'0'"},
"其他问题": {"type": "string",
"description": "如果对用户输入问题分类后,认为是其他问题,则返回内容为'1',否则返回'0'"}},
},
"required": ["招股说明书咨询", "数据查询", "其他问题"],
},
},
]
MESSAGES = [
{"role": "system",
"content": "你是一名高级智能任务分类助手,请对用户输入问题进行理解,在此基础上对问题进行分类,分类结果为“公司照顾说明书咨询问题”,“股票基金数据查询问题”,“其他问题”三种之一."},
{"role": "user", "content": "请对用户输入问题进行分类,以下是用户提出的我问题:请帮我查询出20210415日,建筑材料一级行业涨幅超过5%(不包含)的股票数量。"}]
input_model = {
"model": "qwen2.5-7b-instruction",
"messages": MESSAGES,
"tools": TOOLS,
"temperature": 0.7,
"top_p": 0.8,
"repetition_penalty": 1.05,
"max_tokens": 512,
"stream": False,
}
response = requests.post(url, json=input_model)
if response.status_code == 200:
completition = response.content.decode('utf-8')
completition = json.loads(completition)
res = completition["choices"][0]["message"]["content"]
print(res)
print(f"{response.status_code}")
else:
print(f"Error: {response.status_code}")
基于兼容openai接口+python工具包调用
from openai import OpenAI
openai_api_key = "EMPTY"
openai_api_base = "http://192.168.124.xxx:18005/v1"
client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)
models = client.models.list()
model_name = models.data[0].id #获取模型名称
MESSAGES = [
{"role": "system",
"content": "你是一名诗歌达人,请帮助用户回答相关问题"},
{"role": "user", "content": "请给我写一首简短欢快的诗,要求诗的意境要乐观"}]
response = client.chat.completions.create(
model=model_name,
messages=MESSAGES,
#tools=tools,
temperature=0.7,
top_p=0.8,
max_tokens=512,
stream=False,
extra_body={
"repetition_penalty": 1.05,
}
)
final_res = response.choices[0].message.content
print(final_res)
基于兼容openai接口+python结构化输出
以下是用于信息抽取简单示范:
from pydantic import BaseModel
from enum import Enum
from openai import OpenAI
import json
openai_api_key = "EMPTY"
openai_api_base = "http://192.168.124.xxx:18005/v1"
client = OpenAI(
api_key=openai_api_key,
base_url=openai_api_base,
)
classEentityDescription(BaseModel):
人名: str
地名: str
机构名: str
json_schema = classEentityDescription.model_json_schema()
completion = client.chat.completions.create(
model="deepseek-qwen32B",
messages=[
{
"role": "user",
"content": "请从以下文本中提前实体信息,实体信息包括 人名 地名 机构名,文本内容为:中国位于地区上,中国有个机构名,一个机构名是:中国Bank,一个地名是:北京,一个人名是:张三。",
}
],
extra_body={"guided_json": json_schema},
)
res = completion.choices[0].message.content
print(completion.choices[0].message.content)