书生-S2 的多模态输入怎么组织 / 图片和文本按什么顺序送进模型?

文章导读
图片和文本按什么顺序送进书生-S2,取决于模型加载时声明的输入形态:常见的一种是一条消息里分块排布,图文在同一个 content 列表里;另一种是图文成对字段分别传。顺序写错通常不会让模型加载失败,但会让对话模板套不上,表现为占位符数量对不上、图片被忽略,或者返回内容跟图片没什么关系。
📋 目录
  1. Ⅰ 先确认输入声明的形式:单条消息还是图文成对
  2. Ⅱ 用一张图加一句文本跑最小样例
  3. Ⅲ 把尺寸与格式处理放在组装之前
  4. Ⅳ 顺序或占位符写错时的现象归类
  5. Ⅴ 把跑通的输入组装写成可复用函数
A A

图片和文本按什么顺序送进书生-S2,取决于模型加载时声明的输入形态:常见的一种是一条消息里分块排布,图文在同一个 content 列表里;另一种是图文成对字段分别传。顺序写错通常不会让模型加载失败,但会让对话模板套不上,表现为占位符数量对不上、图片被忽略,或者返回内容跟图片没什么关系。

判断路径:先看加载日志确认输入形态是单条分块还是成对字段,再用一张图加一句话跑最小样例。尺寸、色彩模式、编码统一放在组装之前完成,这样报错时才能分清是预处理层还是模型层。占位符数量要与图片数量一致,排列顺序要与图片顺序一致。字段名一律以实际加载配置和日志为准,不要凭猜测拼。

先确认输入声明的形式:单条消息还是图文成对

先确定结构,再谈顺序。下面两段都是占位示意,字段名不代表官方接口,替换成你能在配置或加载日志里实际看到的名称即可。

形态 A,单条消息分块:

{
  'content': [
    {'type': 'image', 'image': PLACEHOLDER_IMG},
    {'type': 'text',  'text': '描述这张图'}
  ]
}

形态 B,图文成对字段:

书生-S2 的多模态输入怎么组织 / 图片和文本按什么顺序送进模型?
{
  'image': PLACEHOLDER_IMG,
  'text':  '描述这张图'
}

确认真实结构的方法主要看加载日志:启动时打印的处理器与分词器名称里,是否同时出现了图像处理相关组件。如果日志显示图像组件被跳过或加载失败,输入拼得再对也白搭,先解决加载。另一个办法是分别只送文本、只送图片各跑一次,观察报错出现在处理器阶段还是模型前向阶段,通常就能判断真实结构是按块还是按字段。

用一张图加一句文本跑最小样例

最小样例的目标只有一个:确认这条输入链路能返回结果,先不追求效果。下面代码是骨架,call_model 和 preprocess 都替换成你环境里的实际调用。

img = preprocess('one.png')
msg = {
    'content': [
        {'type': 'image', 'image': img},
        {'type': 'text',  'text': '这张图里有什么?'}
    ]
}
out = call_model(msg)   # 替换为实际推理调用
print(type(out), out)

运行后应观察到的输出形态:一段可读的自然语言,内容与这张图有关;不是把占位符原样回显,不是空字符串,也不是一段 traceback。如果返回的是空串、重复你的提问、或者明显在描述别的东西,先别改模型参数,回头核对图片对象是否真的进入了消息体。建议先固定为单图单句,跑通之后再考虑多图。

把尺寸与格式处理放在组装之前

预处理在整体流程里的位置是:读文件 → 解码 → 转 RGB → 缩放或裁剪到允许范围 → 归一化或编码 → 组装输入 → 送模型。也就是说,组装函数里拿到的应该已经是处理好的图像对象或编码,而不是原始文件路径。这样分工的好处是:预处理失败和模型失败会落在不同的报错点上。

书生-S2 的多模态输入怎么组织 / 图片和文本按什么顺序送进模型?

单独运行下面两步,不加载模型,只验证预处理:

file one.png
python prep_check.py one.png
# prep_check.py
import sys
from PIL import Image
im = Image.open(sys.argv[1])
print('size=', im.size, 'mode=', im.mode, 'format=', im.format)
im2 = im.convert('RGB')
print('rgb ok', im2.size)

如果这一步就报错,问题在文件本身、解码库或色彩模式,跟模型无关;如果这一步通过、送进模型才报错,再去查组装顺序和占位符。需要结合环境确认的一点是:某些部署会限制图片边长或通道数,超范围会在预处理阶段被静默裁剪,这种最好在预处理日志里显式打印处理前后的尺寸,避免后面排查时误判。

顺序或占位符写错时的现象归类

报错文本五花八门,但大致能归到三类。先看关键词落在哪一类,能省掉大量试错。

书生-S2 的多模态输入怎么组织 / 图片和文本按什么顺序送进模型?
现象关键词大致属于哪一层先检查什么
image token count、placeholder、expected N got M 之类的数量描述组装层的占位符与图片数量不匹配文本里手写的占位符个数是否等于图片张数,是否已被真正替换
unexpected keyword、missing required、expected str got dict/list、字段名找不到组装层的结构形态选错单条分块还是成对字段,字段名是否与加载配置一致
权重未找到、显存不足、dtype 不匹配、分词器加载失败模型加载或设备层,与输入顺序无关先把加载和设备问题修掉,再回来调输入顺序

三类里只有前两类跟本页主题直接相关。第三类出现时,改输入顺序不会有任何帮助,这是最容易浪费时间的地方。

把跑通的输入组装写成可复用函数

最小样例跑通后,立刻把组装方式固化成函数,后面所有实验都走同一个入口,避免每次手拼结构。

def build_input(image_path, question, prep):
    # image_path: 单张图片路径
    # question:   与这张图配对的文本
    # prep:       已在别处验证过的预处理函数,返回图像对象或编码
    # 返回结构中的字段名必须以实际加载配置为准
    # 顺序约定:占位符与图片按同一顺序排列,文本紧跟在配对图片之后
    img = prep(image_path)
    return {
        'content': [
            {'type': 'image', 'image': img},
            {'type': 'text',  'text': question}
        ]
    }

验证一致性用同一张图重复调用:同一句话连续跑两次,先比较返回结构是否一致;如果解码参数带随机采样,先把温度设为确定值,或者只比较结构不比较文本内容。再用同一张图换一个不同问题,返回内容应随问题变化,否则说明文本那一侧没被真正读进去。然后用两张不同图片配同一句话,确认返回随图片变化。三组都通过,再把函数扩展到多图场景:把图片列表与占位符按同一顺序展开,并在注释里写死这个约定,免得后面有人插队插入。