评测与部署:用 lmms-eval 量化抗幻觉,用 SGLang 把 VLM 推上线|多模态大模型专栏⑧
一句话讲透本篇:同一个模型、同一张图、两次 greedy 推理可能给出不同分数——这往往不是模型变差了,而是评分规则太脆弱。用 lmms-eval 跑标准基准、用 SGLang 部署服务,本篇打通从训练到上线的最后一公里。
写在前面
经过前七篇,文档 VLM 已经具备"看图答问、SFT 对齐、DPO 抗幻觉"的能力。但"训出来"不等于"能用"——还需回答两个问题:模型究竟有多好(评测),以及如何让它服务真实流量(部署)。
本篇覆盖评测与部署的完整链条。核心认知:VLM 评测远不如 NLP 稳定,部署也比纯 LLM 多几个坑(动态分辨率、视觉 token 显存)。一份零依赖 demo(评测稳定性)附带真实运行结果。
阅读本篇后可获得:
- ✅ 识别 VLM 评测不稳定的三个根因,掌握"评分规则脆弱性"这一最可控因素
- ✅ 选择文档 VLM 的最小验证集与对应评分规则(ANLS / relaxed acc / POPE F1)
- ✅ 用 lmms-eval 一条命令跑通多个基准并正确解读报告
- ✅ 用 SGLang 完成部署的五个生命周期步骤,掌握压测与量化决策
一、VLM 评测为何不稳定
VLM 评测有一个反直觉的特性:同样提示、两次 greedy 推理可能给出不同答案。这源于三个原因:
| 原因 | 机理 | 缓解 |
|---|---|---|
| CUDA 非确定性 | 即使do_sample=False+ 固定 seed,attention kernel 在不同 batch 大小下浮点累加顺序不同,1e-4 级差异经 softmax 放大后可能改变 argmax | 固定batch_size=1,或多次跑取众数 |
| Batch 组成漂移 | padding 长度受同 batch 其他样本影响,改变某些算子的数值路径 | 评测时batch_size=1(牺牲速度换确定性) |
| 评分规则脆弱 | "1,234 wan yuan"vs"1,234 万元"vs"1234 万元"语义都对,exact match 只认第一个 | 用 benchmark 官方评分规则 |
第三个原因贡献最大且最易控制。code/05_eval_deploy/eval_stability_demo.py(纯标准库可跑)用 ground truth"1,234 wan yuan"与 10 个候选输出(前 8 个语义正确)演示:
评分规则对比(demo 实测)
| 候选输出 | exact | normalized | edit-sim | numeric |
|---|---|---|---|---|
1,234 wan yuan(精确) | 1.0 | 1.0 | 1.0 | 1.0 |
1,234 wan yuan(多空格) | 0.0 | 1.0 | 0.93 | 1.0 |
1,234 wan yuan.(尾点) | 0.0 | 1.0 | 0.93 | 1.0 |
1,234 万元(中文单位) | 0.0 | 0.0 | 0.43 | 1.0 |
Revenue: 1,234 wan yuan(前缀) | 0.0 | 0.0 | 0.61 | 1.0 |
12,345 wan yuan(真错) | 0.0 | 0.0 | 0.80 | 0.0 |
exact在 8 个正确答案里给 7 个打 0 分——这不是模型差,是评分规则差。numeric match(抽取数字比较)对格式宽容、对幻觉数字严苛,是文档 VLM 的推荐规则。
同一模型、10 次运行的方差
模拟同一模型对同一图采样 10 次(仅表面形式变化),各评分规则的均值:
| 评分规则 | 10 次均值 |
|---|---|
| exact | 0.100(在 0 与 1 间剧烈摆动) |
| normalized exact | 0.500 |
| edit similarity | 0.794 |
| numeric match | 0.800(稳定) |
exact的摆动是纯噪声——这就是"重跑评测得到不同分数"的根因。缓解策略:用仍能捕捉真实错误的最宽松规则(此处 numeric match),并对 N≥3 次运行取均值。
二、必跑基准与评分规则
文档 VLM 的最小验证集为DocVQA + ChartQA + OCRBench + POPE,覆盖核心能力面(理解 + OCR + 抗幻觉):
| Benchmark | 测试内容 | 评分规则 | 文档相关度 |
|---|---|---|---|
| DocVQA | 文档 VQA | ANLS(0–1 软分,非 exact match) | ⭐⭐⭐⭐⭐ |
| ChartQA | 图表问答 | relaxed accuracy(±5% 容差) | ⭐⭐⭐⭐⭐ |
| InfoVQA | 信息密集文档 | ANLS | ⭐⭐⭐⭐⭐ |
| OCRBench | OCR 能力 | 专用 lookup table | ⭐⭐⭐⭐ |
| POPE | 物体存在幻觉 | F1 / accuracy | ⭐⭐ |
| HallusionBench | 综合幻觉 | 自定义 | ⭐⭐⭐ |
三条评分规则速记:
- ANLS(Average Normalized Levenshtein Similarity):
(1 − edit_distance / max_len),0–1 连续分,允许拼写小错。DocVQA / InfoVQA 采用。 - relaxed accuracy:数字答案在 GT ±5% 内即算对。ChartQA 采用。
- POPE F1:是/否二分类看 F1 而非 accuracy——因为"否"占多数会刷高 accuracy,掩盖保守倾向。
三、lmms-eval 实操
lmms-eval 一条命令跑多个 benchmark,自动支持 Qwen2-VL / LLaVA / InternVL,自动下载缓存数据集,输出标准 JSON 报告。code/05_eval_deploy/eval.sh的核心调用:
python-mlmms_eval\--modelqwen2_5_vl\--model_argspretrained=./merged_dpo_model,dtype=bfloat16\--tasksdocvqa_val,chartqa_test,ocrbench,textvqa_val,infovqa_val,pope,mmbench_dev_en\--batch_size1\# 高分图安全起见用 1--output_path./eval_results\--log_samples典型报告(建议同一 checkpoint 跑 3 次取 mean ± std):
| Task | Metric | Value | Std (3 runs) |
|---|---|---|---|
| docvqa_val | anls | 0.812 | ±0.004 |
| chartqa | relaxed_acc | 0.643 | ±0.011 |
| ocrbench | score | 547 | ±3 (/1000,500 及格) |
| pope | f1 | 0.871 | ±0.006 |
| pope | accuracy | 0.889 | ±0.005 |
报告解读要点:DocVQA ANLS 0.81 约对应 EM ≈ 70%(粗略换算ANLS×100−10 ≈ EM%);POPE F1 < accuracy 说明模型偏向答"否"(保守,常见于 DPO 后期),需回调 β。
四、部署:为何选 SGLang
| 选项 | 优点 | 缺点 |
|---|---|---|
| transformers | 简单稳定 | 慢,无 PagedAttention |
| vLLM | 快、生态大 | VLM 支持滞后 |
| SGLang | VLM 最先支持、RadixAttention | 较新,文档少 |
| TensorRT-LLM | 最快 | 部署复杂,VLM 模型少 |
文档 VLM 推荐 SGLang:原生支持 Qwen2-VL / LLaVA-OneVision,对动态分辨率友好。两个杀手锏:
- RadixAttention(前缀复用):多轮对话中前面的"图 + prompt"KV cache 可复用。同一张图第二次提问(如先问"有什么文字"再问"总营收"),前缀已在 cache,吞吐提升 3–5×。对应参数
--enable-radix-cache。 - 视觉 token 高效调度:把变长视觉 token 当"软"序列处理,不会因 AnyRes 多 tile 就 OOM,比 vLLM 在高分图上更稳定。
五、部署的五步生命周期
可上线的部署远不止launch_server:
步骤 1:合并 LoRA——推理不需要 adapter,必须先合并:
swiftexport--model_id_or_pathQwen/Qwen2.5-VL-7B-Instruct\--adapters./output_sft/checkpoint-xxx--merge_loratrue\--output_dir./merged_model步骤 2:起服务(code/05_eval_deploy/sglang_serve.sh):
python-msglang.launch_server\--model-path ./merged_model--port30000--host0.0.0.0\--dtypebfloat16 --mem-fraction-static0.85\--context-length8192--chat-template qwen2_5_vl\--enable-radix-cachemem-fraction-static 0.85留 15% 给 KV cache 增长与视觉 token buffer,调高易 OOM。
步骤 3:冒烟测试——file://本地路径与 base64 内嵌两种图像传法都要测,许多 bug 只在 base64 路径出现(如 MIME 推断错误)。
步骤 4:压测——VLM 压测不能只看 QPS,图像大小决定首 token 延迟。关键指标:TTFT(time to first token,1024×1024 图应 ≤ 1.5s)、TPOT(≤ 30ms)、显存峰值(跑满并发时 < 95% 上限)。A100 80GB 上 Qwen2.5-VL-7B 的可达成指标:
| 并发 | TTFT P50 | TPOT | 显存 |
|---|---|---|---|
| 1 | 0.8s | 18ms | 28 GB |
| 8 | 1.4s | 25ms | 52 GB |
| 16 | 2.6s | 40ms | 71 GB |
| 32 | OOM 风险 | — | — |
步骤 5:监控埋点——显存利用率(OOM 预警)、QPS/TTFT/TPOT、生成长度分布(VLM 易"无限描述",设 max_tokens 截断)、图像尺寸分布(异常大图往往是上游 bug)。
六、量化决策与完成标准
A100 80GB 无需量化;其他卡按延迟要求决策:极致延迟选 TensorRT-LLM INT8,平衡选 AWQ INT4(最常用),易部署选 GPTQ INT4(精度略低)。
VLM 量化的特殊风险:视觉编码器对量化更敏感(SigLIP 量化后 zero-shot 掉 3–5%),常见做法是只量化 LLM、视觉编码器保 bf16(SGLang 的--quantize llm_int8)。
文档 VLM 的完成标准
| 指标 | 及格线 |
|---|---|
| DocVQA ANLS | ≥ 0.80(约 EM ≥ 70%) |
| ChartQA relaxed-acc | ≥ 60% |
| OCRBench | ≥ 500 |
| POPE F1 | ≥ 85% |
| 接口 TTFT P50 | ≤ 2s(1024×1024 图) |
达不到 → 回阶段 3 调数据,而非无脑训练。诊断顺序:
- ANLS 低 + OCRBench 低 → 分辨率不够,开 AnyRes / 升到 1024+;
- ANLS 低 + OCRBench 高 → 指令对齐不够,加 SFT 数据多样性;
- POPE F1 低 → 幻觉,进阶段 4 做 DPO;
- 部署后掉点 → 99% 是 chat template / 预处理不一致。
📎 本篇涉及代码
| 文件 | 说明 |
|---|---|
code/05_eval_deploy/eval_stability_demo.py | 评测稳定性 / 评分规则对比(零依赖) |
code/05_eval_deploy/eval.sh | lmms-eval 多 benchmark 评测脚本 |
code/05_eval_deploy/sglang_serve.sh | SGLang 部署脚本 |
小结
| 主题 | 要点 |
|---|---|
| 评测不稳定 | 三因:CUDA 非确定性、batch 漂移、评分规则脆弱(最可控) |
| 评分规则 | exact 脆弱,文档任务用 numeric match / ANLS |
| 最小验证集 | DocVQA + ChartQA + OCRBench + POPE |
| 部署框架 | SGLang(RadixAttention 前缀复用 + 视觉 token 调度) |
| 部署 5 步 | 合并 LoRA → 起服务 → 冒烟 → 压测 → 监控 |
| 量化 | 仅量化 LLM,视觉编码器保 bf16 |
下一篇是专栏收尾的前沿方向:Chameleon / Emu3 / Llama 4 的 early fusion 路线——视觉也被离散化为 vocab、单模型单 loss,这是 VLM 的下一代范式。
专栏:《从 NLP 到 VLM:多模态大模型研发实战》
上一篇:⑦ 多模态 RL · DPO + GRPO | 本篇:⑧ 评测与部署 · lmms-eval + SGLang | 下一篇:⑨ 前沿方向 · Early Fusion