登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Transformers处理多模态输入批次

来源:17golang原创

时间:2026-11-15 12:35:13 317浏览 收藏

在 Transformers 中处理多模态输入批次,最稳妥的做法是把每条样本先整理成统一的“文本 + 媒体 + ID”记录,再交给对应模型的 AutoProcessor 统一预处理。这样可以同时处理不同长度的文本、图片和部分模型支持的视频输入,并让生成结果按原始样本回填。

官方地址:https://huggingface.co/docs/transformers/

核心结论:不要手动把图片张量和文本张量拼在一起。批处理的边界应放在 processor:由它负责聊天模板、分词、图像或视频预处理、padding 和模型需要的字段;业务代码只负责样本结构、显存分批和结果顺序。

1. 先把每条多模态样本整理成同一种结构

批处理失败通常不是因为 batch 这个参数本身,而是不同样本的消息结构不一致。例如第一条样本只有文本,第二条有一张图片,第三条有两张图片或一段视频。如果业务层直接把它们混成一个列表,后面很难判断生成结果对应哪条记录。

建议在进入 Transformers 前保留一个稳定的样本 ID,并把媒体顺序写进 content 数组。对支持聊天模板的视觉语言模型,文本和媒体都作为内容块出现;对普通图文模型,则可以分别准备 text 和 images 列表。

多模态样本经过 AutoProcessor 形成 input_ids、attention_mask 和 pixel_values 批次字段的结构说明图
图1:多模态样本进入 AutoProcessor 并形成批次字段的结构示意图,不是真实运行截图。
from pathlib import Path

def build_record(sample_id, image_path, prompt):
    # 每条记录保留稳定 ID,后面回填生成结果时不依赖 batch 下标
    return {
        "id": sample_id,
        "image_path": str(Path(image_path)),
        "prompt": prompt,
    }

records = [
    build_record("A101", "/data/images/a101.jpg", "描述图片中的主要对象"),
    build_record("A102", "/data/images/a102.jpg", "判断图片是否适合归档"),
]

# 同一条记录的媒体与文本顺序固定,避免多模态占位符错位
conversations = [
    [{
        "role": "user",
        "content": [
            {"type": "image", "path": item["image_path"]},
            {"type": "text", "text": item["prompt"]},
        ],
    }]
    for item in records
]

上面的路径只是示例数据位置,不代表某个模型或机器上的真实目录。需要多张图片时,在同一个 content 列表中按固定顺序增加多个 image 块;不要把一张样本的图片拆到另一条样本里。

2. 用 AutoProcessor 一次生成批次输入

Processor 是多模态批处理的关键边界。官方文档中,processor 会把文本转换为模型需要的文本张量,把图片转换为像素值;不同模型还可能返回图像网格或其他视觉字段。调用时重点关注四个参数:

  • tokenize=True:把聊天内容转成模型可用的 token。
  • padding=True:把同一批不同长度的文本补齐到批次可计算的形状。
  • return_dict=True:返回带字段名的批次对象,不依赖位置猜字段。
  • return_tensors="pt":生成 PyTorch 张量;如果模型使用其他框架,要跟随对应配置。

支持聊天模板的视觉语言模型可以使用 apply_chat_template,它会把消息格式、媒体占位符和处理器预处理放到同一条路径中:

import torch
from transformers import AutoProcessor

model_id = "Qwen/Qwen2.5-VL-3B-Instruct"
processor = AutoProcessor.from_pretrained(model_id)

# 对话列表按样本排列;每个元素是一条完整对话
inputs = processor.apply_chat_template(
    conversations,
    add_generation_prompt=True,  # 给生成模型留下 assistant 的起始位置
    tokenize=True,
    return_dict=True,
    return_tensors="pt",
    padding=True,                # 文本长度不同时由 processor 补齐
)

# BatchFeature 的字段名由模型决定,不能假设只有 input_ids
print(list(inputs.keys()))
# 常见字段包括 input_ids、attention_mask、pixel_values,具体以模型为准

这里不建议先对每张图片手工 resize、归一化,再尝试和 token 张量拼接。视觉编码器的尺寸、均值、方差和图像网格规则属于模型处理器配置,手工覆盖容易让输入形状看似正确、语义却不匹配。

3. 普通图文模型与聊天模板要分开看

并非所有多模态 checkpoint 都使用同一种消息格式。普通的图文处理路径往往是“文本列表 + 图片列表”,而视觉聊天模型通常需要包含 role 和 content 的消息列表。不要只因为模型名称里有 vision 就照搬另一种写法,先看该 checkpoint 的 processor 和模型文档。

如果是普通图文输入,结构通常更接近下面这样:

from PIL import Image

texts = [item["prompt"] for item in records]
images = [Image.open(item["image_path"]).convert("RGB") for item in records]

# processor 同时完成文本编码、图像预处理和批次 padding
batch = processor(
    text=texts,
    images=images,
    padding=True,
    return_tensors="pt",
)

# 文件句柄不应长期占用;读取完成后主动关闭图片对象
for image in images:
    image.close()

如果是聊天模板,则要让每条样本的媒体占位符和真实媒体一一对应。对视频输入,只有在模型支持视频时才使用 type="video";较长视频可使用文档提供的固定 num_frames 或 FPS 抽样参数。抽样数量应与模型能力和显存预算一起确定,不能把“帧越多越好”当成通用规则。

4. 用 micro-batch 控制显存,再调用 generate

一批样本同时包含图片或视频时,显存消耗不只取决于文本 token 数,还和图片分辨率、视觉 patch 数、视频帧数以及模型结构有关。因此生产代码通常保留一个逻辑大批次,但按固定大小切成多个 micro-batch。

六条多模态记录拆成两个 micro-batch 并按样本 ID 合并为有序结果的结构说明图
图2:micro-batch 与稳定结果顺序的工程示意图,不是真实运行截图。
import torch
from transformers import AutoModelForImageTextToText

device = "cuda" if torch.cuda.is_available() else "cpu"
model = AutoModelForImageTextToText.from_pretrained(model_id).to(device)

def move_batch(batch, target_device):
    # BatchFeature 中的张量逐项搬运,非张量配置保持原值
    return {
        key: value.to(target_device) if hasattr(value, "to") else value
        for key, value in batch.items()
    }

def run_micro_batch(batch):
    # 推理阶段关闭梯度,降低不必要的显存占用
    batch = move_batch(batch, device)
    with torch.inference_mode():
        return model.generate(**batch, max_new_tokens=64)

# 实际项目中应让 processor 为每个 micro-batch 重新组装输入
outputs = run_micro_batch(inputs)
texts = processor.batch_decode(outputs, skip_special_tokens=True)
# 这里只展示解码接口;不要把示例文本当成模型真实输出

更稳妥的资源策略是先用很小的 micro_batch_size 跑通,再根据峰值显存逐步调大。视频场景还要单独限制帧数;同样的 batch size,在静态图片和视频输入上并不代表相同的显存压力。

5. 按原始 ID 拆回结果,避免顺序错位

只使用列表下标关联结果,在重试、分批或过滤失败样本后很容易错位。建议每次将记录 ID 与生成结果一起保存;如果某个 micro-batch 失败,只重试该 micro-batch,并保留其他批次已经完成的结果。

def attach_results(records, texts):
    # 生成结果数量不匹配时立即报错,避免静默错位
    if len(records) != len(texts):
        raise ValueError("样本数量与生成结果数量不一致")

    return [
        {"id": record["id"], "text": text}
        for record, text in zip(records, texts)
    ]

results = attach_results(records, texts)
# 后续写数据库或消息队列时使用 results 中的 id,而不是重新依赖位置

如果处理流程会过滤掉损坏图片或超长文本,应在过滤时同步维护记录列表,并把被跳过的 ID 记录为失败状态。这样重试任务仍然可以根据 ID 找回原始媒体和提示词。

6. 生产环境的配置、日志与发布检查

企业级批处理至少要把以下边界写进配置或运行日志:

边界建议记录目的
输入样本 ID、媒体数量、媒体类型、文本长度定位结构不一致与异常样本
处理器模型 ID、processor 配置版本、padding 策略解释字段形状和预处理差异
资源micro-batch 大小、视频帧数、峰值显存避免批次突然放大导致 OOM
输出成功 ID、失败 ID、重试次数、结果顺序支持幂等回填与问题追踪

日志中不要写入图片私有路径、访问令牌或原始敏感内容。对需要长期运行的服务,可以将处理器和模型在进程启动时加载一次,把每次请求只留在样本组装、推理和结果落库这三个边界内。

总结

Transformers 多模态批处理可以归纳为五步:统一样本结构,使用对应的 processor,按模型要求生成带字段名的 batch,在显存边界内拆成 micro-batch,最后按样本 ID 回填结果。图片、视频和聊天模板并不是可以互换的输入格式,真正稳定的实现依赖 checkpoint 的官方 processor 配置。

常见问题

为什么不能直接把图片张量和 input_ids 拼起来?

不同模型对图像预处理、视觉占位符和额外字段的要求不同。应让对应的 processor 生成完整输入,再把返回的字段交给模型。

padding=True 会不会把图片也补成相同内容?

padding 主要针对批次中的可变长度文本或处理器定义的序列字段。视觉字段如何组织由具体 processor 决定,不能用文本 padding 的直觉推断所有模型。

视频批处理为什么还要限制帧数?

帧数会直接影响视觉输入规模和显存消耗,而且模型通常有训练时的帧数能力范围。应该根据模型文档和资源预算选择固定帧数或 FPS 抽样。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>