MOSS-VL-Realtime 输出总在描述背景——是提示词没约束还是输入太大?

文章导读
MOSS-VL-Realtime 回答总在描述背景,通常不是模型“不会看”,而是两件事叠在一起:提问没有把注意力约束到具体目标上,同时送进视觉编码器的图太大,画面被切成大量图块后,模型倾向于先覆盖整体再谈细节。要分清主因,别同时改提示词和分辨率——先固定一切,只动一个变量,看输出粒度往哪边动。
📋 目录
  1. 一 把问题改成明确指向,做同一张图的提示词对照
  2. 二 固定提示词只改输入尺寸,观察输出粒度变化
  3. 三 调整最大输出长度与停止条件,看是否为截断或跑题
  4. 四 用一组固定样例做交叉对照,判断主因
  5. 五 把选定的提问模板与输入尺寸写进配置并复跑
A A

MOSS-VL-Realtime 回答总在描述背景,通常不是模型“不会看”,而是两件事叠在一起:提问没有把注意力约束到具体目标上,同时送进视觉编码器的图太大,画面被切成大量图块后,模型倾向于先覆盖整体再谈细节。要分清主因,别同时改提示词和分辨率——先固定一切,只动一个变量,看输出粒度往哪边动。

用同一张图做三组单变量对照:只改提问指向、只改输入尺寸、只改最大输出长度与停止条件。适用场景是图片问答输出泛化、答非所问、描述背景。操作上先跑提示词对照,再跑尺寸梯度,最后排除截断。判断依据是输出首个分句是否落到目标实体、位置或数量。若三者都无效,多半是目标在画面中占比过小或模型本身能力边界,需要改裁切策略而不是继续调提示词。

把问题改成明确指向,做同一张图的提示词对照

先选一张你手上有明确答案的图,比如桌面上一个白色杯子。把提问拆成三版,每版只要求一种粒度,并显式禁止描述背景:

版本A(只答目标名称)
图里左侧桌面上那个白色容器是什么?只回一个名词。
不要描述背景、光线、场景,不要解释。

版本B(只答位置)
白色杯子在画面的哪个区域?只从“左上/中上/右上/左下/居中/右下”里选一个回答。

版本C(只答数量)
画面中有几个白色杯子?只回一个阿拉伯数字,不要其他文字。

三版共用同一张图、同一组生成参数,逐条记录。记录表按下面字段建,输出原文照抄,不要摘要:

图片ID提示词版本输出原文首分句是否直指目标是否出现背景描述是否符合要求粒度
img-01A待填是/否是/否是/否
img-01B待填是/否是/否是/否
img-01C待填是/否是/否是/否

对比方法是看“首个分句”。A 版首分句是不是名词、B 版首分句是不是方位词、C 版首分句是不是数字。如果三版都从“这是一张……的图片”起手,说明提示词还没起约束作用,问题更可能出在输入侧,接着做尺寸对照。如果 A、C 版本明显收敛而 B 版仍发散,说明约束写法本身需要更硬的格式限定,而不是图太大。

固定提示词只改输入尺寸,观察输出粒度变化

提示词统一切到上一节表现最好的一版,生成参数全部锁死,只改送进模型的图像尺寸。梯度可以先取短边 512、768、1024、1280 和原图五档,长边按比例缩放,超过上限再走裁切。

预处理通常发生在 processor 或 image_processor 里,缩放和裁切就在那一步。要先确认本地实现走的是哪条路径:是按最短边缩放、按最长边缩放,还是先缩放再中心裁切。可以在预处理前后各打一行日志,记录原始尺寸和实际传入的张量形状:

img = Image.open(path).convert('RGB')
print('origin', img.size)
inputs = processor(text=prompt, images=img, return_tensors='pt')
print({k: tuple(v.shape) for k, v in inputs.items() if hasattr(v, 'shape')})

字段名以本地实现返回为准,有的实现返回 pixel_values,有的还会返回网格信息,两者都能拿来核对实际图块数量。对照记录只记三项:档位、实际送入的张量形状、输出文本原文。比较时看同一档位下输出是否开始出现具体物体细节,而不是只复述“桌面上有物品、背景是房间”。若尺寸下降后描述明显变具体,输入尺寸就是主因之一;若各档输出几乎同构,尺寸不是关键,回到提示词和生成参数。

调整最大输出长度与停止条件,看是否为截断或跑题

有些“总在描述背景”只是前几十个 token 被背景铺陈占满,真正答案还没轮到就停了;也有相反情况,长度放太宽后模型自由发挥。参数项按本地实现支持的来,常见的有:

  • max_new_tokens:生成长度上限,先取 64、96、128、256 四档。
  • temperature / top_p:想复现对照就压低随机性,比如温度取接近 0 的确定性解码。
  • repetition_penalty 或 no_repeat_ngram_size:抑制反复描述同一片背景。
  • stop / stop_strings:把句号、两个换行或固定结束标记设为停止条件,让模型答完即止。
  • eos 处理:确认结束符没有被配置过滤掉,否则模型会一直写下去。

逐档跑完记录输出长度与结尾形态。截断的判定特征比较固定:输出在半个短语或半个括号处结束、结尾没有句读、生成 token 数正好贴近上限、日志里的结束原因显示为长度而非正常结束。出现这些特征,说明前面几节的对照都被截断污染了,需要先放宽上限重跑,再谈提示词和尺寸的影响。

用一组固定样例做交叉对照,判断主因

单张图的现象不能当结论。样例建议选 3 到 5 张,覆盖这几种情况:目标占画面比例很小、目标占比较大;背景干净、背景杂乱;单目标、同类多目标。这样交叉之后才看得出是“图太大导致忽略小目标”,还是“提问没指向导致一律铺陈”。

对照矩阵按行放“图片 × 提示词版本”,列放输入尺寸档位,格子里填输出是否符合要求粒度(符合/部分符合/不符合)并附一句原文摘录。同一矩阵再叠一列“放宽长度后是否改变”,用来标出被截断干扰的格子。判定规则可以按下面走:

  1. 只有提示词版本改变结果、尺寸各档一致:提示词约束不足是主因。
  2. 只有尺寸档位改变结果、提示词版本一致:输入过大是主因,重点改缩放与裁切。
  3. 两者都改变且方向一致:两个因素叠加,先降尺寸再收紧提示词。
  4. 两者都不改变:先查生成参数与结束符,再考虑目标占比过小需要单独裁切区域送图。

把选定的提问模板与输入尺寸写进配置并复跑

把验证过的一组组合固化成配置,避免每次手写提示词和临时改尺寸。示例结构如下,字段名按你的调用方式替换:

prompt_template: '图中{target}位于画面的哪个区域?只从“左上/中上/右上/左下/居中/右下”里选一个回答,不要描述背景。'
preprocess:
  shortest_edge: 1024
  keep_aspect: true
generate:
  max_new_tokens: 96
  temperature: 0.0
  stop:
    - '。'
log_fields:
  - image_shape
  - output_text
  - finish_reason

复跑步骤:清掉上一次的缓存输出,用同一批样例图、同一模板、同一尺寸档位重跑一遍,把输出写入同一张对照表的新列。一致性检查看三点:输出粒度是否与选定档位一致、finish_reason 是否都为正常结束、实际送入的张量形状是否与配置里的目标尺寸吻合。如果形状对不上,说明预处理分支没生效,回去查缩放和裁切的调用位置,而不是再改提示词。