安装

下面的内容由 vLLM-Ascend 的文档整理而来。

物理机安装

物理机安装需要先安装驱动和 CANN,详细安装教程请见

因为 torch-NPU 托管在华为云,所以我们需要增加额外的 pip 源:

pip config set global.extra-index-url "https://download.pytorch.org/whl/cpu/ https://mirrors.huaweicloud.com/ascend/repos/pypi"

我们需要依次安装 vLLM 和 vLLM-Ascend,建议从源码安装:

# 安装 vLLM
git clone --depth 1 --branch v0.11.0rc3 https://github.com/vllm-project/vllm
VLLM_TARGET_DEVICE=empty pip install -v -e vllm
 
# 安装 vLLM-Ascend
pip install vllm-ascend==0.11.0rc0

容器安装

如果觉得物理机安装太麻烦,可以使用构建好的容器。

虽然已有打包好的镜像可供使用,但如果想知道容器内包含什么内容,详细容器构建命令可见:

git clone https://github.com/vllm-project/vllm-ascend.git
cd vllm-ascend
docker build -t vllm-ascend-dev-image:latest -f ./Dockerfile .

可以使用下面的命令拉起容器:

export IMAGE=quay.io/ascend/vLLM-Ascend:v0.11.0rc0
export CONTAINER_NAME=vllm-test
 
docker run \
    --name $CONTAINER_NAME \
    --shm-size=128g \
    --device /dev/davinci0 \
    --device /dev/davinci1 \
    --device /dev/davinci2 \
    --device /dev/davinci3 \
    --device /dev/davinci4 \
    --device /dev/davinci5 \
    --device /dev/davinci6 \
    --device /dev/davinci7 \
    --device /dev/davinci_manager \
    --device /dev/devmm_svm \
    --device /dev/hisi_hdc \
    -v /usr/local/dcmi:/usr/local/dcmi \
    -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
    -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \
    -v /usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info \
    -v /etc/ascend_install.info:/etc/ascend_install.info \
    -v /root/.cache:/root/.cache \
    -it $IMAGE bash

进入容器后,基本上不需要再做操作,如果运行的模型较新容器内 vLLM 版本不支持,可以先卸载 vLLM 和 vLLM-Ascend,然后再安装最新版本。

模型下载

可以从 Hugging Face、ModelScope 和 Modelers 获取模型。

在容器化环境中,系统默认将模型文件下载至 $HOME/.cache 目录。若 Docker 根目录分区存储容量不足,可能导致模型下载失败或容器运行异常。因此,强烈建议在下载前预先规划并指定专用的模型存储路径,确保系统稳定性和可维护性。

如使用 ModelScope 下载 Qwen/Qwen3-0.6B 模型到 /home/model_weights/Qwen3-0.6B

modelscope download --model Qwen/Qwen3-0.6B --local_dir /home/model_weights/Qwen3-0.6B

如使用 Hugging Face 下载 Qwen/Qwen3-0.6B 模型到 /home/model_weights/Qwen3-0.6B

huggingface-cli download Qwen/Qwen3-0.6B --local-dir /home/model_weights/Qwen3-0.6B

开始推理

vLLM 推理分离线推理在线推理

离线推理是指一次性处理输入数据,不需要保持与服务器的持久连接。这种方式通常用于批处理任务。

在线推理需要启动一个持续运行的服务端。服务端启动后,客户端可以通过 HTTP 请求(如 curl 或其他网络工具)发送推理请求并获取结果。

我们这里使用在线推理做演示。

API 格式支持

vLLM 支持多种 API 服务格式,其中最常用的是 OpenAI 兼容的 API 格式,便于与现有应用生态集成。

下面我们以最广泛使用的 OpenAI API 格式为例进行说明。

启动 API 服务器

使用以下命令启动最简的 API 服务:

vllm serve /home/model_weights/Qwen3-0.6B \
    --served-model-name qwen \
    --port 8002

参数说明:

  • --served-model-name qwen: 设置服务暴露的模型名称
  • --port 8002: 指定服务监听端口(默认 8000)

客户端调用示例

服务启动后,可以通过以下方式发送请求:

curl "http://localhost:8002/v1/chat/completions" \
    -H "Content-Type: application/json" \
    -d '{
        "model": "qwen",
        "messages": [{"role": "user", "content": "Hello, how are you?"}],
        "temperature": 0.7,
        "top_p": 0.9
    }'

下面对请求做一些说明:

API 端点类型

vLLM 提供了多种 OpenAI 兼容的 API 端点来满足不同的使用场景。我们这里主要使用聊天对话端点 /v1/chat/completions,这是最推荐的交互方式,因为它专门为对话场景设计,能够很好地处理上下文和角色扮演。

除此之外,vLLM 还提供了其他几个常用的端点:

  • 补全端点 (/v1/completions): 这是传统的文本生成方式,适合简单的文本续写任务;
  • 嵌入端点 (/v1/embeddings): 拉起 embedding 模型后,用于将文本转换为向量表示,常用于语义搜索和相似度计算;
  • 模型端点 (/v1/models): 可以查询当前服务加载了哪些模型。

Messages 格式

消息数组的设计使得我们可以轻松实现多轮对话。每个消息都包含一个角色(role)和内容(content),通过不同角色的组合,模型能够理解对话的上下文。

系统首先会看到 system 角色的消息,这相当于给模型设定一个角色或行为准则。然后模型会按照时间顺序阅读用户和助手的历史对话,最后根据最新的用户消息生成回复。这种设计让对话更加自然和连贯。

角色类型说明:

  • user 代表用户的输入,这是模型需要回应的内容
  • assistant 记录模型之前的回复,帮助模型保持对话的一致性
  • system 用于设置系统级的指令,比如要求模型扮演特定角色或遵循某种回答风格

多轮对话示例:

"messages": [
  {"role": "system", "content": "你是一个有帮助的助手"},
  {"role": "user", "content": "请介绍一下人工智能"},
  {"role": "assistant", "content": "人工智能是..."},
  {"role": "user", "content": "能详细说说机器学习吗?"}
]

常用参数

通过调整各种参数,我们可以精确控制模型生成文本的行为和风格。这些参数主要分为几类:

生成控制参数影响文本的生成过程和输出方式:

  • max_tokens 限制生成文本的长度,防止输出过长
  • temperature 控制文本的创造性,温度越高输出越随机多变
  • top_p 采用核采样策略,只在概率最高的词中选择,平衡创造性和连贯性
  • stream 决定是否实时流式返回结果,提升用户体验

内容控制参数用于调整生成内容的特点:

  • frequency_penalty 减少重复词语的出现,让文本更加丰富多样
  • presence_penalty 鼓励模型谈论新的话题,增加内容的广度
  • stop 设置停止词,让模型在特定位置停止生成,便于控制输出格式

性能和精度测试

现在市面上有多种多样的测试工具,vLLM 也有自己的 benchmark,我们这里使用 aisbench 来作性能和精度测试。

测试前准备

安装 aisbench

aisbench 使用源码安装的方式进行安装:

git clone https://gitee.com/aisbench/benchmark.git
cd benchmark/
pip3 install -e ./ --use-pep517

下载 GSM8K 数据集

GSM8K 数据集可以使用 opencompass 提供的版本

cd ais_bench/datasets
wget https://opencompass.oss-cn-shanghai.aliyuncs.com/datasets/data/gsm8k.zip
unzip gsm8k.zip

性能测试

性能测试的关键在于对数据集长度的精确控制。

根据不同的测试需求,有时需要模拟固定长度的输入输出场景来测试模型的稳定性,有时则需要模拟可变长度的场景来验证模型的适应性。通过结合不同的并发请求数,我们可以全面评估模型在各种负载条件下的性能表现。

下面我们以固定 3500 输入,1500 输出,并发 32 作样例拉起性能测试。

制作定长数据集

由于现有的主流数据集输入长度不一,为了进行精确的性能测试,我们需要对这些原始数据进行预处理,通过截断的方式构建固定长度的测试数据集。

下面提供了一个实用的脚本来帮助您制作定长数据集。在使用时,请根据实际情况配置模型路径(脚本需要借助模型的分词器来精确计算 token 数量)以及所需的输入长度参数 input_len 还有数据集路径 dataset_path。注意,运行请备份原始 jsonl

import json
from transformers import AutoTokenizer
 
# 使用实际的模型分词器
tokenizer = AutoTokenizer.from_pretrained(
    "/home/model_weights/Qwen3-0.6B/"
)
 
batch_size = 2000 # 测试数据条数
input_len = 3500
dataset_path = "./ais_bench/datasets/gsm8k/test.jsonl"  # 使用GSM8K测试集
 
dataset = []
 
with open(dataset_path, "r", encoding="utf-8") as f:
    for line in f:
        data = json.loads(line)
        dataset.append(data["question"])
 
 
# repeat input_len
dataset_2k = []
for sentence in dataset:
    words = tokenizer.tokenize(sentence)
    print(len(words))
    len_num = len(words) // input_len
    if len_num == 0:
        multiplier = (input_len // len(words)) + 1
        repeated_len = words * multiplier
        words = repeated_len[:input_len]
        decoded_text = tokenizer.convert_tokens_to_string(words)
        print(len(words))
        dataset_2k.append(decoded_text)
    # else:
    #    words = words[:input_len]
    #    merged_sentence = " ".join(words)  # 合并
    #    print(len(words))
    #    dataset_2k.append(merged_sentence)
 
 
# repeat to batch_size
batch_num = len(dataset_2k) // batch_size
if batch_num == 0:
    multiplier = (batch_size // len(dataset_2k)) + 1
    repeated_batch = dataset_2k * multiplier
    dataset_2k = repeated_batch[:batch_size]
else:
    dataset_2k = dataset_2k[:batch_size]
 
print(len(dataset_2k))
 
json_str = json.dumps(dataset_2k, ensure_ascii=False, indent=4)
with open(f"GSM8K-in{input_len}-bs{batch_size}.jsonl", "w", encoding="utf-8") as f:
    for i in range(len(dataset_2k)):
        f.write(
            json.dumps(
                {"question": dataset_2k[i], "answer": "none"}, ensure_ascii=False
            )
        )
        f.write("\n")

同时需要确保模型配置文件正确(在 benchmark/ais_bench/benchmark/configs/models/vllm_api/vllm_api_stream_chat.py):

from ais_bench.benchmark.models import VLLMCustomAPIChatStream
from ais_bench.benchmark.utils.model_postprocessors import extract_non_reasoning_content
 
models = [
    dict(
        attr="service",
        type=VLLMCustomAPIChatStream,
        abbr='vllm-api-stream-chat',
        path="/home/model_weights/Qwen3-0.6B",  # 模型位置
        model="qwen", # 拉起时的模型别名
        request_rate = 0,
        retry = 2,
        host_ip = "localhost",
        host_port = 8002,  # 拉起时的端口
        max_out_len = 1500,  # 输出长度
        batch_size=32, # 并发数
        trust_remote_code=False,
        generation_kwargs = dict(
            temperature = 0.5,
            top_k = 10,
            top_p = 0.95,
            seed = None,
            repetition_penalty = 1.03,
        ),
        pred_postprocessor=dict(type=extract_non_reasoning_content)
    )
]

vLLM 服务拉起后,可以用下面的命令发起性能测试:

ais_bench --models vllm_api_stream_chat --datasets gsm8k_gen_0_shot_cot_str_perf --debug -m perf

精度测试

理论上,配置文件修改过后,可以直接用下面的命令拉起精度测试,但注意,数据集要用原始的 GSM8K 数据集,不要用我们前面测试性能的截断的数据集:

ais_bench --models vllm_api_stream_chat --datasets gsm8k_gen_0_shot_cot_str --debug