当前大模型官方评测结果大多基于N卡评测,昇腾卡适配和评测通常会滞后,同时昇腾卡上没有一个权威评测结果与N卡对标。以Qwen3-VL系列多模态大模型为例,其发布后,在学术界和工业界引起广泛关注,在视觉理解、图文交互任务中展现出较高性能,但是缺少在昇腾平台上的评测结果,因此在昇腾NPU+vllm Ascend推理平台上开展多模态系列模型的适配和评测工作,具有重要实用价值,在昇腾平台上复现与原框架一致的图文理解、逻辑推理等精度,能够确保下游业务的稳定可靠,也能为构建昇腾软硬件生态提供关键支撑。
为确保多模态模型在实际应用中的可靠性与性能最优,我们根据积累的一些经验整理了相关的评测与精度对齐的方法。主要分为如下三个部分

为基于昇腾NPU+vllm Ascend平台对齐Qwen3-VL多模态模型精度,我们需要先确定评测的范围及评测平台等。 下表是针对Qwen3-VL系列模型的范围和平台的选择结果,以及选择的背景说明。

为了能够做好评测和精度对齐任务,首先要提升推理服务稳定性,随着vllm 0.11.0rc0版本发布,我们在实际部署Qwen3-VL模型过程中,发现其对该模型的支持尚不稳定,易出现各类运行时问题。为确保服务平稳运行,我们探索一套稳定可靠的最小集推理服务启动配置,在避免引入不稳定新特性的同时,建立输入数据管控机制,以保障服务在评测时的稳定性。
我们遇到的主要问题参见下图,针对这些问题,我们同步在开源社区提交issue跟踪处理进展。同时,在分析掌握问题规律后,我们也制定了相应的规避策略与优化方案,以在短期内保障服务可用性。

结合一系列排查和实验后,我们摸索一套相对稳定的拉起容器+启动推理服务的流程和参数。
参考样例命令如下
export IMAGE=m.daocloud.io/quay.io/ascend/vllm-ascend:v0.11.0
export container_name="qwen3vl8b"
export model_dir="/opt/data/modelscope/models"
export container_model_dir="/root/models"
export home_dir="/home"
export container_home_dir="/home"
docker run -itd --privileged \
--name $container_name \
--net=host \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
--shm-size=512g \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-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 ${model_dir}:${container_model_dir} \
-v ${home_dir}:${container_home_dir} \
$IMAGE /bin/bash注:创建容器时,需显式指定shm-size大小,建议256GB以上,如不指定默认大小仅64MB,此时在推理服务负载变大时,易引发EngineCore异常终止。
# 单卡起推理服务样例命令如下
export VLLM_USE_V1=1; \
export VLLM_LOGGING_LEVEL=DEBUG; \
export ASCEND_RT_VISIBLE_DEVICES=3; \
export ts=`date "+%m%d%H%M%S"`; mkdir -p /home/outputs_vllm_qwen3vl_8b; \
nohup python -u /usr/local/python3.11.13/bin/vllm serve /root/models/Qwen/Qwen3-VL-8B-Instruct \
--served-model-name qwen3-vl-8b \
--host 0.0.0.0 \
--port 33008 \
--seed 1024 \
--dtype bfloat16 \
--max-num-seqs 8 \
--tensor-parallel-size 1 \
--max-model-len 36864 \
--max-num-batched-tokens 8192 \
--trust-remote-code \
--gpu-memory-utilization 0.8 \
--allowed-local-media-path /media_data \
--no-enable-prefix-caching > /home/outputs_vllm_qwen3vl_8b/136-qwen3vl-8b_${ts}.log 2>&1 &
tail -f /home/outputs_vllm_qwen3vl_8b/136-qwen3vl-8b_${ts}.log注:ASCEND_RT_VISIBLE_DEVICES需要设置为对应可用的NPU卡编号
# 4卡起推理服务样例命令如下
export VLLM_USE_V1=1;
export ts=`date "+%m%d%H%M%S"`; mkdir -p /root/outputs; \
nohup vllm serve /home/models/Qwen3-VL-8B-Instruct \
--served-model-name qwen3-vl-8b \
--host 0.0.0.0 \
--port 8000 \
--seed 1024 \
--dtype bfloat16 \
--max-num-seqs 8 \
--tensor-parallel-size 4 \
--max-model-len 34816 \
--max-num-batched-tokens 8192 \
--trust-remote-code \
--gpu-memory-utilization 0.8 \
--allowed-local-media-path / \
--no-enable-prefix-caching > /home/136-qwen3vl-8b_${ts}.log 2>&1 &
tail -f /home/136-qwen3vl-8b_${ts}.log注:如果容器内可见的NPU卡不止4张,则需要配置ASCEND_RT_VISIBLE_DEVICES为具体可用的4张NPU卡编号
在推理服务相对稳定后,排除基础问题后,即进行第一轮Qwen3-VL精度摸底工作,当时基于9个数据集,主要评测了Qwen3-VL-30B-Instruct模型,从得分看,只有OCRBench和DocVQA是可以对齐的结果。

在评测过程中,当出现评分未达预期的情况,我们需要一套快速的bad case分析、诊断与定界能力。特别在评测初期,推理服务不稳定,评测工具也不熟悉,可能有很多用例未得分,并不属于模型推理的问题,此时我们需要有方法能快速定界问题,明确优化改进方向,从而尽早稳定评测环境,为后续的精度对齐工作提供可靠基础。
在深入了解开源评测框架能力后,我们大致从如下维度进行入手构建此方面的能力。

如上图所示,为了能够快速识别问题,我们需要既可以查看评测集的打分结果,也可以查看每个具体用例的测试结果能力。 (注:开源评测框架的介绍和操作指导,可参考 附录2:评测框架使用指导 相关介绍 ) 开源评测框架在完成评测后,会按用例集维度自动汇总生成score或results文件。
评测集整体结果:可通过score文件(后缀通常为_acc)查看,该文件包含该数据集的用例划分(val/dev),整体打分(Overall),和细分领域打分等。如下为score文件样例:

用例级结果:可通过results文件查看,通常包含每个用例的详细执行情况,如问题(question),答案(answer/即ground truth),推理结果(prediction),每个用例是否得分(hit或score)。如下为results文件样例:

说明:不同数据集的score和results文件中的具体内容可能存在区别,属于正常现象。
仔细查看上述章节的用例级结果信息,可以发现每个用例其实缺少调用大模型推理时的完整输入参数,比如sampling param,也缺少完整的推理输出信息,比如推理结果的finish_reason等,当出现用例case丢分时,我们可能需要更进一步的信息进行问题定界定位。因此,我们需要对原有框架进行一些定制,增加定位信息的打印。
开源框架没有例行打印推理API调用相关的日志,需要修改源码增加打印相关信息。
修改样例:Lib\site-packages\vlmeval\api\gpt.py
def react_image_urls(obj): # 增加打印过滤函数,避免打印媒体文件转码信息,同时尽量保留文本,方便定位
"""
递归遍历 JSON 对象,将所有 image_url.url 的值替换为 "filtered"
"""
if isinstance(obj, dict):
# 如果当前字典包含 'image_url' 键,且其值是 dict 并含 'url'
if 'image_url' in obj and isinstance(obj['image_url'], dict) and 'url' in obj['image_url']:
obj['image_url']['url'] = 'filtered'
# 递归处理所有子项
for k, v in obj.items():
redact_image_urls(v)
elif isinstance(obj, list):
for item in obj:
redact_image_urls(item)
class OpenAIWrapper(BaseAPI):
……
def generate_inner(self, inputs, **kwargs) -> str: # 修改generate_inner函数增加api调用内容打印
……
response = requests.post(
self.api_base,
headers=headers, data=json.dumps(payload), timeout=self.timeout * 1.1)
ret_code = response.status_code
ret_code = 0 if (200 <= int(ret_code) < 300) else ret_code
answer = self.fail_msg
try:
resp_struct = json.loads(response.text)
payload_dump_content = copy.deepcopy(payload)
react_image_urls(payload_dump_content)
self.logger.warning(f'inputs={inputs}, self.api_base={self.api_base}, headers={headers}, payload={payload_dump_content}, '
f'timeout={self.timeout}, response.text={response.text}') # 将输入和输出信息打印到一行,方便关联定位
answer = resp_struct['choices'][0]['message']['content'].strip()
except Exception as err:
self.logger.error(f'{type(err)}: {err}')
self.logger.error(response.text if hasattr(response, 'text') else response)优化后的样例推理过程日志打印示例如下,如果需要更多其他信息,可进一步定制,也可以将推理过程整合输出到results表中。 从下方样例中,可以看出基本一次api调用的输入、输出、结果等均有完整记录。

基于阶段二的问题分析与诊断能力,接下我们逐个对待评测数据集进行跑批,分析用例失败原因,并且调整相关设置。 因为不同的数据集,对应题目和答案的数据差异较大,可能会常出现一些参数设置在某些数据集work,某些数据集不能work的情况,需要结合推理结果分析,设置合理的参数。 当前我们碰到的相关场景和优化措施整理如下

在清理了因配置不合理等导致的推理报错问题后,对应的精度可能仍存在一定差异,接下来就需要进行专项精度调优和对齐。 之前我们遇到两个类别的精度存在较大差异,一类是多模态推理&数学的MMMU类数据集,一类是通用问答的MMBench数据集。
MMMU数据集前期经过多轮优化,在Qwen3-VL-30B-A3B-Instruct模型上精度提升到68.3,但是距离公开评分74.2还有差距。 对于该数据集采用如下方式,可以复现对齐精度 注:如下此指导也可用于评测MathVision、ODinW-13、RealWorldQA
# 启动镜像
export model_dir="/opt/data/modelscope/models"
export container_model_dir="/root/models"
export container_name="Qwen3-VL"
export IMAGE=m.daocloud.io/quay.io/ascend/vllm-ascend:v0.11.0
docker run -itd --privileged \
--name $container_name \
--net=host \
--shm-size=1000g \
--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/davinci8 \
--device /dev/davinci_manager \
--device /dev/devmm_svm \
--device /dev/hisi_hdc \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-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 ${model_dir}:${container_model_dir} \
$IMAGE /bin/bash# 准备
cd /path/to/Qwen3-VL/evaluation/mmmu
pip install -r requirements.txt# 推理
python run_mmmu.py infer \
--model-path /root/models/Qwen/Qwen3-VL-30B-A3B-Instruct \
--data-dir /root/data/AI-ModelScope/MMMU/ \
--dataset MMMU_DEV_VAL \
--output-file results/mmmu_dev_val_predictions.jsonl \
--tensor-parallel-size 4 \
--max-new-tokens 32768 \
--temperature 0.7 \
--top-p 0.8 \
--top-k 20 \
--repetition-penalty 1.0 \
--presence-penalty 1.5# 评估
# max-model-len要设置大一些,有些推理结果超过了32768
nohup vllm serve /root/models/Qwen/Qwen3-30B-A3B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 4 \
--max-num-seqs 2 \
--max_num_batched_tokens 20000 \
--trust-remote-code \
--no-enable-prefix-caching \
--gpu-memory-utilization 0.9 \
--max-model-len 45000 \
--dtype=bfloat16 \
--served-model-name qwen3-30b > /root/qwen3-30b.log 2>&1 &export MIT_SPIDER_TOKEN="test"
export MIT_SPIDER_URL="http://127.0.0.1:8000/v1/chat/completions"
python run_mmmu.py eval \
--data-dir /root/data/AI-ModelScope/MMMU/ \
--input-file results/mmmu_dev_val_predictions.jsonl \
--output-file results/mmmu_dev_val_eval_results.csv \
--dataset MMMU_DEV_VAL \
--eval-model qwen3-30b \
--api-type mit \
--nproc 16
MMBench_V1.1_EN_Dev数据集经过多轮优化,在Qwen3-VL-8B-Instruct模型上与公开得分84.5分仍有差距,使用如下方式可以对齐
# 启动推理服务
export VLLM_USE_V1=1;
nohup vllm serve /home/models/Qwen3-VL-8B-Instruct \
--served-model-name qwen3-vl-8b \
--host 0.0.0.0 \
--port 8000 \
--seed 1024 \
--dtype bfloat16 \
--max-num-seqs 8 \
--tensor-parallel-size 4 \
--max-model-len 34816 \
--max-num-batched-tokens 8192 \
--trust-remote-code \
--gpu-memory-utilization 0.8 \
--allowed-local-media-path / \
--no-enable-prefix-caching > /root/qwen3vl-8b.log 2>&1 &# 评测 评测过程中使用的sampling parameters,需要参考如下设置

结果得分

在进行一些专项精度对齐后,接下来就可以对更多的数据集进行批量评测,这个阶段我们主要利用EvalScope框架 + VLMEvalKit + OpenCompass三者集成的能力,对评测任务做了封装,可以同时对多个多模态与纯语言测试任务进行一次性跑批,评测效率更高。 我们当前是按顺序每次评测1个数据集,这样在可以不区分多模态还是纯语言的。如果需要一次性评测多个数据集,评测框架也是支持的。
batch_run_benchmark.sh脚本样例代码
#!/bin/bash
datasets=( "MMBench_DEV_EN_V11" "DocVQA_VAL" "OCRBench" "BLINK" "mmlu" )
api_url="http://71.10.29.136:60007/v1/chat/completions"
model=qwen3-vl-8b
nproc=4
limit=0
judge=chatgpt-0125
use_cache=no_use_cache
mode=all
infer_framework=vllmascend
log_dir="./outputs_${infer_framework}_${model}"
declare -A dataset_type
dataset_type["MMBench_DEV_EN_V11"]="vllm"
dataset_type["MMBench_DEV_CN_V11"]="vllm"
dataset_type["DocVQA_VAL"]="vllm"
dataset_type["InfoVQA_VAL"]="vllm"
dataset_type["ChartQA_TEST"]="vllm"
dataset_type["MMMU_DEV_VAL"]="vllm"
dataset_type["MMMU_Pro_10c"]="vllm"
dataset_type["MMMU_Pro_V"]="vllm"
dataset_type["MMMU_Pro_10c_COT"]="vllm"
dataset_type["MMMU_Pro_V_COT"]="vllm"
dataset_type["MathVision"]="vllm"
dataset_type["OCRBench"]="vllm"
dataset_type["BLINK"]="vllm"
dataset_type["Video-MME"]="vllm"
dataset_type["RefCOCO"]="vllm"
dataset_type["ifeval"]="llm"
dataset_type["gpqa"]="llm"
dataset_type["mmlu"]="llm"
dataset_type["mmlu_pro"]="llm"
dataset_type["ceval"]="llm"
dataset_type["hellaswag"]="llm"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
log "Process datasets ${datasets[@]}"
mkdir -p ${log_dir}
for dataset in "${datasets[@]}"; do
type="${dataset_type[$dataset]}"
log "Running evaluation for dataset: ${dataset}, type: ${type}"
ts=$(date "+%m%d%H%M%S")
log_file="${log_dir}/batch_${infer_framework}_${model}_${dataset}_${nproc}_${limit}_${judge}_${ts}.log"
args=(
"./${infer_framework}_run_${type}_${model}.py"
"$api_url"
"$model"
"$dataset"
"$nproc"
"$limit"
"$judge"
"$use_cache"
"$mode"
)
printf -v full_command "python -u $(printf "%q " "${args[@]}")> %q 2>&1" "$log_file"
log "${full_command}"
python -u "${args[@]}" > "$log_file" 2>&1
exit_code=$?
if [ ${exit_code} -eq 0 ]; then
log "Success proc ${dataset}. Log saved to: ${log_file}"
else
log "Failed proc ${dataset} with err ${exit_code}. Log saved to: ${log_file}"
fi
echo "----------------------------------------"
done
log "All datasets processed."
echo ""
vllmascend_run_vllm_qwen3-vl-8b.py样例代码
from ruamel.yaml import YAML
from evalscope.run import run_task
from evalscope.summarizer import Summarizer
import time
import sys
import os
def replace_yml(config_path, url, model, dataset, nproc, limit, judge, use_cache, mode):
try:
yaml = YAML()
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=4, offset=2)
with open(config_path, 'r', encoding='utf-8') as file:
config_data = yaml.load(file)
if 'eval_config' in config_data and 'model' in config_data['eval_config']:
if len(config_data['eval_config']['model']) > 0:
config_data['eval_config']['model'][0]['type'] = model
config_data['eval_config']['model'][0]['api_base'] = url
if 'eval_config' in config_data and 'mode' in config_data['eval_config']:
config_data['eval_config']['mode'] = mode
if 'eval_config' in config_data and 'data' in config_data['eval_config']:
if len(config_data['eval_config']['data']) > 0:
config_data['eval_config']['data'][0] = dataset
if 'eval_config' in config_data:
config_data['eval_config']['nproc'] = nproc
config_data['eval_config']['limit'] = limit
config_data['eval_config']['judge'] = judge
if use_cache == "no_use_cache":
config_data['eval_config']['reuse'] = False
config_data['use_cache'] = ""
else:
config_data['eval_config']['reuse'] = True
config_data['use_cache'] = use_cache
with open(config_path, 'w', encoding='utf-8') as file:
yaml.dump(config_data, file)
print(f"{config_path} update successful!")
print(f"model={model}, url={url}, dataset={dataset}, nproc={nproc}, "
f"limit={limit}, judge={judge}, use_cache={use_cache}, mode={mode}")
return True
except Exception as e:
print(f"ERROR: {e}")
return False
def run_vllm_eval(url, model, dataset, nproc, limit, judge, use_cache, mode):
task_cfg = './vllmascend_run_vllm_qwen3-vl-8b.yaml'
replace_yml(task_cfg, url, model, dataset, nproc, limit, judge, use_cache, mode)
start_time = time.time()
print('>> Start time is', start_time)
run_task(task_cfg=task_cfg)
print('>> Start to get the report with summarizer...')
report_list = Summarizer.get_report_from_cfg(task_cfg)
end_time = time.time()
cost_time = end_time - start_time
print(f'\n>> The report list: {report_list}, and cost {cost_time:.6f}s.')
if __name__ == '__main__':
if len(sys.argv) <= 3:
print('error: try use run_**.py {url} {model} {dataset} ${option_nproc_default32} ${option_limit_default0}\n')
sys.exit(1)
nproc = 32
limit = 0
judge = "exact_matching"
use_cache = ""
mode = "all"
if len(sys.argv) > 3:
url = sys.argv[1]
model = sys.argv[2]
dataset = sys.argv[3]
if len(sys.argv) > 4:
nproc = int(sys.argv[4])
if len(sys.argv) > 5:
limit = int(sys.argv[5])
if len(sys.argv) > 6:
judge = sys.argv[6]
if len(sys.argv) > 7:
use_cache = sys.argv[7]
if len(sys.argv) > 8:
mode = sys.argv[8]
run_vllm_eval(url, model, dataset, nproc, limit, judge, use_cache, mode)
vllmascend_run_llm_qwen3-vl-8b.py样例代码
import sys
import time
from evalscope.run import run_task
from ruamel.yaml import YAML
from evalscope.summarizer import Summarizer
from evalscope.utils.logger import get_logger
logger = get_logger()
def replace_yml(config_path, url, model, dataset, nproc, limit, judge, use_cache, mode):
try:
yaml = YAML()
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=4, offset=2)
with open(config_path, 'r', encoding='utf-8') as file:
config_data = yaml.load(file)
if 'eval_config' in config_data and 'models' in config_data['eval_config']:
if len(config_data['eval_config']['models']) > 0:
config_data['eval_config']['models'][0]['path'] = model
config_data['eval_config']['models'][0]['openai_api_base'] = url
if 'eval_config' in config_data and 'mode' in config_data['eval_config']:
config_data['eval_config']['mode'] = mode
if 'eval_config' in config_data and 'datasets' in config_data['eval_config']:
if len(config_data['eval_config']['datasets']) > 0:
config_data['eval_config']['datasets'][0] = dataset
if 'eval_config' in config_data:
config_data['eval_config']['max_num_workers'] = nproc
config_data['eval_config']['limit'] = limit
with open(config_path, 'w', encoding='utf-8') as file:
yaml.dump(config_data, file)
logger.info(f"{config_path} update successful!")
logger.info(f"model={model}, url={url}, dataset={dataset}, nproc={nproc}, "
f"limit={limit}, judge={judge}, use_cache={use_cache}, mode={mode}")
return True
except Exception as e:
logger.info(f"ERROR: {e}")
return False
def run_llm_eval(url, model, dataset, nproc, limit, judge, use_cache, mode):
task_cfg = f'./vllmascend_run_llm_qwen3-vl-8b.yaml'
replace_yml(task_cfg, url, model, dataset, nproc, limit, judge, use_cache, mode)
run_task(task_cfg=task_cfg)
print('>> Start to get the report with summarizer ...')
report_list = Summarizer.get_report_from_cfg(task_cfg)
print(f'\n>> The report list: {report_list}')
if __name__ == '__main__':
if len(sys.argv) <= 3:
logger.info('error: try use run_**.py {url} {model} {dataset} {option_nproc_default_32} {option_limit_default_0} {option_judge_default_exact_matching} {option_use_cache_default_null}\n')
sys.exit(1)
nproc = 1
limit = 2
judge = "exact_matching"
use_cache = "no_use_cache"
mode = "all"
if len(sys.argv) > 3:
url = sys.argv[1]
model = sys.argv[2]
dataset = sys.argv[3]
if len(sys.argv) > 4:
nproc = int(sys.argv[4])
if len(sys.argv) > 5:
limit = int(sys.argv[5])
if len(sys.argv) > 6:
judge = sys.argv[6]
if len(sys.argv) > 7:
use_cache = sys.argv[7]
if len(sys.argv) > 8:
mode = sys.argv[8]
run_llm_eval(url, model, dataset, nproc, limit, judge, use_cache, mode)vllmascend_run_vllm_qwen3-vl-8b.yaml样例配置文件
work_dir: outputs_vllmascend_qwen3-vl-8b
eval_backend: VLMEvalKit
eval_config:
model:
- type: qwen3-vl-8b
name: CustomAPIModel
api_base: "http://71.10.29.136:33008/v1/chat/completions"
key: EMPTY
temperature: 0.7
top_p: 0.8
top_k: 20
repetition_penalty: 1.0
presence_penalty: 1.5
img_size: -1
img_detail: low
timeout: 600
retry: 1
max_tokens: 16384
data:
- MMMU_DEV_VAL
mode: all
fps: -1
nframe: 16
limit: 2
reuse: false
nproc: 1
verbose: true
judge: chatgpt-0125
OPENAI_API_KEY: EMPTY
OPENAI_API_BASE: http://71.10.29.136:33030/v1/chat/completions # judge model api
LOCAL_LLM: qwen3-vl-30b # judge model type
debug: true
use_cache: ''vllmascend_run_llm_qwen3-vl-8b.yaml样例配置文件
model: text_generation
eval_backend: OpenCompass
eval_type: openai_api
work_dir: outputs_vllmascend_qwen3-vl-8b
eval_config:
datasets:
- gpqa
models:
- openai_api_base: http://71.10.29.136:33008/v1/chat/completions
# type: OpenAI
path: qwen3-vl-8b
max_out_len: 16384
max_seq_len: 32768
temperature: 0.7
top_p: 0.8
top_k: 20
repetition_penalty: 1.0
presence_penalty: 1.5
query_per_second: 1
key: 'EMPTY'
is_chat: true
work_dir: outputs_vllmascend_pangu-vl-7b
mode: all # all, infer, eval, viz; must specify -r when running in eval or viz mode
debug: true
limit: 2
max_num_workers: 1
debug: true
eval_batch_size: 1
timeout: 600此时执行如下命令,可以一次性对MMBench_DEV_EN_V11,DocVQA_VAL,OCRBench,BLINK,mmlu这几个数据集进行评测
export ts=`date "+%m%d%H%M%S"`; export EVALSCOPE_LOG_LEVEL=DEBUG; \
nohup bash batch_run_benchmark.sh > /home/batch_run_benchmark_${ts}.log 2>&1 &
tail -f /home/batch_run_benchmark_${ts}.log具体的使用方式可参考 附录2:评测框架参考资料 相关介绍
在经过上述各个阶段的精度对齐工作后,截止目前Qwen3-VL精度在本地对齐的进展如下表:

经过一段时间持续投入,目前已达成每个评测维度至少1个数据集基本持平N卡公开评分的目标,从一开始只有2个数据集提升至12个数据集,有力证明了昇腾平台相对N卡的推理能力,有效提升客户对昇腾平台的信心。
在通用能力评测和精度对齐的过程中,我们积累了相关各类配置项的一些理解,并整理如下,供参考了解和指正。
| 维度 | 配置项 | 配置项说明 | 配置方式 | 推荐值 | 备注 |
|---|---|---|---|---|---|
| 推理服务参数 | max-model-len | 设置模型支持的推理最大上下文长度(以 token 为单位) 若用户输入的 prompt + 生成长度超过此值,请求将被拒绝。 可用于限制资源消耗或适配硬件限制。 | 推理服务启动参数 | 36864 | 针对不同的数据集的prompt和answer长度,可以观察实际长度,并适当调整;通常不需要一定这么大,比如32768也足够; 如果max_tokens设置为32768,那么该值一定要大于此值,否则推理判断会直接失败 |
| max-num-seqs | 设置单个 batch 中最多允许的序列数量(即并发请求数上限): 影响吞吐量和延迟:值越大,吞吐越高,但可能增加尾延迟。 | 推理服务启动参数 | 4 | 建议使用4,最高8,并发太高精度可能会下降,而且容易oom | |
| max-num-batched-tokens | 设置单个 batch 中所有序列的 总 token 数上限(包括 prompt 和生成部分),若 batch 中 token 总数超过此值,新请求需等待下一批次。 | 推理服务启动参数 | 8192 | 该值在vllm ascend v0.11.0上跑单卡4并发或8并发,基本都可以跑 | |
| gpu-memory-utilization | 设置 GPU 显存使用率上限: vLLM 会根据此比例预留显存用于模型权重、KVCache和临时缓冲区大小。 若设得太低,可能导致无法加载大模型或 batch size 过小; 若设得太高(如 >0.95),可能因系统/驱动开销导致 OOM | 推理服务启动参数 | 0.8~0.85 | 一般使用的0.8 | |
| tensor-parallel-size | 设置 张量并行(Tensor Parallelism) 的 NPU 数量: 将模型权重按层切分到多个 NPU 上,必须与实际可用 GPU 数匹配(如1、2、4、8)。 | 推理服务启动参数 | NPU卡数 | 该值与NPU卡匹配就行,8B模型推荐使用4卡 | |
| no-enable-prefix-caching | 禁用前缀缓存(Prefix Caching)功能: Prefix Caching:当多个请求共享相同的 prompt 前缀(如 system prompt 或 instruction template)时,vLLM 可复用已计算的 KV cache,节省计算和显存。 使用 --no-enable-prefix-caching 显式关闭该优化。 | 推理服务启动参数 | 开启 | 主要让显存使用更稳定些,提升推理过程的稳定性 | |
| allowed-local-media-path | 指定允许从本地文件系统读取媒体文件(如图像)的根路径: 用于 多模态模型推理时加载本地图片。 出于安全考虑,vLLM 默认禁止访问任意本地路径。 通过此参数白名单指定可信目录,防止路径遍历攻击。 可多次指定多个路径 | 推理服务启动参数 | 媒体文件的父路径 | 一般直接指定的/根目录,可以根据实际情况指定,有些视频/语音的评测集会需要从本地加载媒体文件 | |
| VLLM_LOGGING_LEVEL | 指定推理服务日志级别,提供更详细的推理服务日志打印,提升问题定位效率 | 推理服务环境变量 | DEBUG | 开启更详细的日志,方便定位,分析问题 | |
| 评测端框架参数 | retry | 设置单个样本(sample)在推理失败时的最大重试次数: 当模型调用因网络错误、超时、OOM 等原因失败时,自动重试以提高评测鲁棒性。适用于不稳定服务(如远程 API)或高负载本地推理。 | 评测框架的yaml文件 | 1 | 一般重试1次就可以,再失败多半还是会失败,也避免过多尝试影响评测效率 |
| timeout | 设置单次模型推理请求的超时时间(单位:秒): 防止因模型卡死或响应过慢导致评测长时间阻塞。 超时后视为失败,若启用了 retry 则会重试。 对于长文本生成或复杂多模态任务,应适当调高(如 120 秒) | 评测框架的yaml文件 | 600 | 根据不同数据集的推理时长可以灵活调整,有些图文推理时间很长,根据实际评测结果有没有失败来看,如果发现有timeout导致的,可能还要调大,一般设为600没有碰到超时的情况; 另外有些时间比较长的案例,也需要看下是否模型推理已经死循环,直到把所有token都用完,此时要进行其他修改 | |
| nproc | 设置并发 worker 数量,用于加速评端的推理性能,提升整体评测吞吐。 值过高可能导致评测端的内存压力或 I/O 瓶颈;建议设为 CPU 核心数的 50%~100%。 | 评测框架的yaml文件 | 4 | 该值通常设为与max-num-seqs匹配即可,不建议太高 | |
| limit | 限制参与评测的样本总数(用于调试或快速验证): 仅对评测数据集的前 limit 条样本进行评估。 常用于开发阶段快速测试 pipeline 是否正常。 若未设置或设为 null,则使用完整数据集; limit: 100 表示只评测前 100 个问题。 | 评测框架的yaml文件 | 0 | 该值为了方便调测使用,如果要先跑一下是否能通,可以设置为1或2,跑全量是设为0即可 | |
| img_size | 指定输入图像的缩放尺寸(通常为短边或长边长度,单位:像素): 用于多模态模型(如 Qwen-VL、LLaVA)的图像预处理。 控制显存占用和计算开销:图像越大,精度可能越高,但推理越慢、显存需求越高。 具体行为取决于模型实现(如“保持宽高比缩放至最大边 = img_size”)。 | 评测框架的yaml文件 | -1 | 设置为-1表示评测端不对图片做resize,如果设置为正值,则直接在评测端使用PIL库的Image.thumbnail对图片做等比缩放,img_size的值是限至长边不超过该值; 某些通用数据集的图片分辨率会很大,如果该图片不是图片内容识别的任务,比如是复杂推理的,适当缩小图片不影响推理精度,则可以缩小图片提升推理效率,减少显存压力; 其他VLMEVAL_MAX_IMAGE_SIZE,VLMEVAL_MIN_IMAGE_EDGE环境变量都不要设 | |
| img_detail | 控制图像内容的细节粒度,可选值通常包括: "low":低细节(快速、节省 token) "high":高细节(保留更多视觉信息,消耗更多上下文长度) 影响图像 tokenization 方式(如是否启用动态分辨率、区域描述等)。 在 EvalScope 中,该参数会透传给模型的视觉处理器。 | 评测框架的yaml文件 | low | ||
| verbose | 启用详细日志输出模式,便于调试 prompt 模板、解析逻辑或模型行为异常。 | 评测框架的yaml文件 | true | 开启更详细的日志,方便定位,分析问题 | |
| Sampling Params | temperature | 控制生成文本的随机性或“创造性”: 低值(如 0.1 高值(如 1.0 | 评测框架的yaml文件 | 0.7 | Qwen3-VL评测推荐组合,模型评测推理效果较好 |
| max_tokens | 控制生成文本的最大 token 数量: 限制响应长度,防止无限生成。 控制计算资源消耗和响应时间 | 评测框架的yaml文件 | 32768 | ||
| top_k | 从概率最高的前 K 个候选词中进行采样,限制采样空间,排除低概率词,提高生成质量: K 越小,输出越保守;K 越大(如 K=50),越接近全分布采样。 若 K=1,则退化为贪心搜索(greedy search)。 | 评测框架的yaml文件 | 20 | ||
| top_p | 从累积概率超过p的最小词集中采样,可以动态调整候选集大小:若前几个词概率很高,则只采样这几个;若分布平坦,则包含更多词,比 top_k 更灵活,能适应不同概率分布: p=1.0 表示使用全部词汇; p=0.9 平衡多样性和连贯性。 | 评测框架的yaml文件 | 0.8 | ||
| n | 生成多少个独立的回复(completions): 用于获取多个候选答案,便于后续选择或评估。 每个回复独立采样(即使 temperature=0,若支持并行解码也可能不同)。 | 评测框架的yaml文件 | 1 | ||
| repetition_penalty | 对已生成 token 的 logits 施加惩罚,降低其再次被选中的概率: 减少重复短语或词语。 值 >1.0 表示惩罚(如 1.2);<1.0 表示鼓励重复(罕见)。 | 评测框架的yaml文件 | 1.0 | ||
| presence_penalty | 只要某个 token 在历史文本中出现过至少一次,就对其施加惩罚: 鼓励话题多样性,避免反复提及相同概念。范围-2.0 到 2.0,默认值为 0无惩罚。负值会鼓励重复已出现过的内容,正值会抑制重复。 惩罚强度与出现次数无关,只看是否出现过(与frequency_penalty略有不同) | 评测框架的yaml文件 | 1.5 | ||
| frequency_penalty | 根据 token 在历史文本中的出现频率施加惩罚(出现越多,惩罚越重): 更精细地控制重复,尤其针对高频词。 比 presence_penalty 更敏感于重复程度 | yaml文件配置项 | 默认值 | ||
| Prompt | system prompt | 依赖评测框架是否支持,如不可配则需要修改代码。 针对不同模型,系统提示词会对评测结果有细微差别 | 评测框架代码 | 参考模型技术报告 | 根据模型技术报告中的填写,如果没有可不填 |
| user prompt | 依赖评测框架是否支持,如不可配则需要修改代码。 针对不同数据集,提示词可能需要微调,并且不同提示词对精度可能存在较显著影响 | 评测框架代码 | 参考模型技术报告 | 根据模型技术报告中的填写,不同数据集不同,为跑高分通常可能需要定制 | |
| 裁判模型 | judge | 多模态通用能力推理输出可能存在推理过程,建议增加裁判模型,提升对多模态推理结果的评分质量。 建议使用理解能力较强的大语言模型作为裁判模型,比如Qwen3-32B或DeepSeek等 | 评测框架的yaml文件 | chatgpt-0125 | 该值可能随不同的vlmevalkit版本会有变化,具体可以参考代码判断,比如对于mcq型评测任务,可以参考Lib\site-packages\vlmeval\dataset\image_mcq.py的evaluate_heuristic函数 |
| OPENAI_API_KEY | 提供访问推理服接口的身份认证密钥 | 评测框架的yaml文件 | 按需填写 | 如果无身份认证可以直接填EMPTY | |
| OPENAI_API_BASE | 兼容OpenAI API的服务端点(base URL) | 评测框架的yaml文件 | http://{ip}:{port_id}/v1/chat/completions | ||
| LOCAL_LLM | 裁判模型的推理服务名,对应vllm起服务参数served-model-name指定名称 | 评测框架的yaml文件 | 实际名称 |
1、EvalScope:模型评测与性能基准测试框架
2、VLMEvalKit:一种多模态大模型评测工具
3、OpenCompass:一种大语言模型评测框架
三者关系简要说明:
