先固定输入尺寸再谈速度——MOSS-VL-Realtime 实时推理的取舍

文章导读
调 MOSS-VL-Realtime 的推理速度时,最常见的错误是把输入尺寸和批大小一起改:改完发现变快了,但说不清快在哪儿;下一次换个尺寸又变慢,此前的对比记录全部作废。原因是输入尺寸决定了送进视觉编码器的像素量,它同时影响耗时和模型能读到的细节,是速度与可读信息量的总闸门。比较合理的顺序是:先把输入尺寸明确并验证它确实传给了模型,在这个尺寸下完成其他变量的对比,最后把尺寸写进配置再复测一次。
📋 目录
  1. 壹 在配置或调用参数里找到控制输入尺寸的入口并确认生效
  2. 贰 同一批图片做尺寸梯度对比,记录耗时与描述细节
  3. 叁 找出小字和远距离目标开始丢失的那一档
  4. 肆 尺寸固定后,再单独比较批大小、精度、线程数的影响
  5. 伍 把选定的尺寸写进配置并复测一次
A A

调 MOSS-VL-Realtime 的推理速度时,最常见的错误是把输入尺寸和批大小一起改:改完发现变快了,但说不清快在哪儿;下一次换个尺寸又变慢,此前的对比记录全部作废。原因是输入尺寸决定了送进视觉编码器的像素量,它同时影响耗时和模型能读到的细节,是速度与可读信息量的总闸门。比较合理的顺序是:先把输入尺寸明确并验证它确实传给了模型,在这个尺寸下完成其他变量的对比,最后把尺寸写进配置再复测一次。

输入尺寸既是算力开销的放大器,也是小字和远距离目标能否被描述出来的下限。建议先在同一批图片上做尺寸梯度对照,每档跑多次取中位数,同时记录耗时和模型对细节的表述,找出小字开始丢失的那一档并回退一到两档作为工作尺寸;确认配置生效并复测一致后,再单独比较批大小、精度、线程数。尺寸未固定前得到的任何速度结论,都可能把尺寸变化的影响误记到其他参数上;这套做法适合单机调试和容量评估,不适合直接当作线上容量承诺。

在配置或调用参数里找到控制输入尺寸的入口并确认生效

“改了参数但没传给模型”是这类排查里最常见的无效劳动。MOSS-VL-Realtime 的尺寸入口通常分布在三处:服务端配置文件里与图像预处理相关的字段、图像处理器(processor)调用时传入的尺寸参数、以及请求体中可能存在的覆盖字段。具体字段名以本地代码与配置文件为准,先搜索出候选位置再动手:

grep -rn "image_size\|max_pixels\|min_pixels\|resize\|input_size" configs/ src/ | head -50

比读配置更可靠的做法是在真正调用模型的地方打印张量形状。下面的代码为通用接入骨架,加载方式按你本地的实际写法替换:

import torch
from transformers import AutoProcessor  # 以本地实际加载方式为准

processor = AutoProcessor.from_pretrained(model_dir)
inputs = processor(images=img, text=prompt, return_tensors="pt")
print("pixel_values.shape =", inputs["pixel_values"].shape)
print("input_ids.shape    =", inputs["input_ids"].shape)

# 如果想确认模型侧真正拿到的尺寸,可在视觉编码器首个卷积层前挂 hook
# model.vision_tower.encoder.layers[0].register_forward_pre_hook(
#     lambda m, args: print("vision input", args[0].shape))

生效前后应能看到形状变化,下面的日志仅为格式示例,真实数值以你的输出为准:

# 改配置前
pixel_values.shape = torch.Size([1, 3, 336, 336])
# 改配置后(同一张图、同一提示词)
pixel_values.shape = torch.Size([1, 3, 672, 672])

如果打印出来的形状没有变化,先怀疑三件事:配置段没被读入、请求体字段覆盖了配置、或者预处理在别处又做了一次缩放。形状对不上之前,不要开始记录耗时。

先固定输入尺寸再谈速度——MOSS-VL-Realtime 实时推理的取舍

同一批图片做尺寸梯度对比,记录耗时与描述细节

梯度取值不必很细,能看出趋势即可。常见做法是从最小可跑尺寸起按固定步长递增,例如短边 336 → 448 → 560 → 672 → 896,或最长边 512 → 768 → 1024 → 1280。无论选哪种,缩放方式(等比缩放加 padding 还是中心裁剪)在整个对照过程中必须保持不变,否则读到的差异里混进了裁剪策略的影响。

每档建议跑 3 次以上取中位数,第一次计入预热后丢弃;同一进程内连续跑,避免反复加载模型带来的额外波动。对照表字段可以按下面这张表来定,空表先搭好,逐档填:

尺寸档位实际输入形状耗时中位数(ms)3次耗时原始值显存/内存峰值输出描述字数与关键细节
短边 336
短边 448
短边 672

耗时只记一个平均数是容易误导自己的,尤其在实时场景下抖动比均值更值得关心,所以原始值一并留下。输出描述建议原样保存文本,而不是只记“好/不好”,后面判断丢失档位时要回头看原话。

找出小字和远距离目标开始丢失的那一档

这一步决定的是信息量下限,靠肉眼比较输出文本即可,不需要额外模型。测试图建议准备 4 到 6 张:一张含小字标牌的近景、一张远距离目标(远处的人、车辆或文字)、一张多目标场景、一张正常近景作为对照,再加一张你业务里最典型的图。图片不要预先压缩到很小,否则尺寸梯度会被上游先砍掉一段。

先固定输入尺寸再谈速度——MOSS-VL-Realtime 实时推理的取舍

判定标准尽量用二值判断,避免主观打分:模型能否说出目标名称(如“路牌”“车牌”“消防栓”),能否说出大致位置(“画面左下”“远处中间”)。两项任一从“能”变“不能”,就把该档标记为丢失起点。记录方式按图逐档填,一行一张图、一列一档:

  • 能说出名称且能说出位置:记为 2
  • 只说出名称、位置含糊:记为 1
  • 名称也不对或直接没提:记为 0

把每张图变成 0 的那一档找出来,取其中最早丢失的那一档,然后回退一到两档作为候选工作尺寸。回退的理由是:梯度测试用的是你挑过的图,真实输入往往更杂,留一点余量比贴着边界跑更稳。如果最早丢失档已经是最小档,说明这批图对当前模型偏难,需要改的是图源或提示词,而不是继续压尺寸。

尺寸固定后,再单独比较批大小、精度、线程数的影响

尺寸一固定,后面的变量才有可比性。原则只有一条:每次只改一个变量,其他全部保持与基线一致。基线要先重复跑 3 次,算出自身的波动范围,这个范围就是后面的噪声底——差异小于噪声底的,不要写成“有提升”。

先固定输入尺寸再谈速度——MOSS-VL-Realtime 实时推理的取舍
变量基线值改动值耗时中位数显存峰值输出是否变化备注
batch_size1
dtypefp16
num_threads

判读时看三点:耗时差异是否超出基线噪声底;输出文本是否仍然说得出同一批目标(精度和批大小都可能轻微改变输出,实时场景下这个代价要提前认可);显存峰值是否留有余量。批大小在单请求实时场景里通常收益有限,反而会增加首字延迟,需要结合你的请求模式确认,不要因为一次对照就默认调大。

把选定的尺寸写进配置并复测一次

选定尺寸后,把它从命令行参数挪进配置文件,避免下次启动又被默认值盖掉。下面只是结构示例,字段名以你的仓库为准:

vision:
  input_size: [672, 672]     # 或写成 short_edge: 672 / max_pixels: 602112
  keep_aspect_ratio: true
  resize_mode: bilinear
runtime:
  batch_size: 1
  dtype: fp16
  num_threads: 8

复测步骤:重启进程清掉上一次的状态;用与初测完全相同的那批图片和提示词;每档跑 3 次取中位数;再次打印 pixel_values.shape,确认它等于配置里的值;把结果填回梯度表同一行,与初测对照。

差异的允许范围这样定:与初测同一档的差异落在初测三次运行的波动范围内,就认为结论可复现;超出范围时,先查形状是否真的变了,再查是否有别的进程在抢资源,最后才怀疑模型本身的随机性。只有复测通过,这份尺寸才算固化下来,后续再谈批大小、精度、线程数的优化才有共同的比较基准。