🤗 Transformers Processors 完全指南:面向多模态输入的预处理处理器设计与实战
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
多模态模型(视觉-语言、语音-文本、音视频模型等)需要同时把不同模态的原始输入(文本、图像、音频、视频)统一转换成模型可消费的张量输入,这正是ProcessorMixin与AutoProcessor这套"处理器(Processor)"体系所解决的问题。本文以 Processors 官方文档 为主体,结合本仓库(transformers)源码深入讲解处理器的设计原理、加载方式、预处理流程与保存共享机制,读完你既可以熟练为 PaliGemma、Whisper 等多模态模型编写预处理管线,也能理解处理器内部"一拆多合"的组合式架构与占位符替换等底层实现。
为什么多模态模型需要 Processor
单模态预处理器只擅长一种输入:分词器(Tokenizer)把文本变成 token 序列与input_ids,图像处理器(Image Processor)把图片变成pixel_values,特征提取器(Feature Extractor)把音频波形变成带有正确采样率的张量。而多模态模型的输入是混合形态的,例如:
- 视觉-语言模型 PaliGemma:使用 SigLIP 图像处理器 + Llama 分词器;
- 语音识别模型 Whisper:使用特征提取器(处理 16kHz 音频)+ 快速分词器(处理文本转录);
- 视频-语言模型:同时需要视频处理器与分词器。
如果每次推理都要用户手动去分别调用 image processor 和 tokenizer,再手工拼装字典,既繁琐又易错。Processor(处理器)就是在这些底层预处理器之上再加一层薄封装:把多个子预处理器聚合为一个统一类,对外只暴露一个调用入口,输入"文字 + 图片"就自动产出input_ids与pixel_values,输入"音频 + 文本"就产出input_features与labels。
处理器类体系:一切从 ProcessorMixin 开始
仓库中所有处理器都继承自ProcessorMixin,该 mixin 与PushToHubMixin组合,统一提供三大能力(见源码 docstring 与各方法定义):
ProcessorMixin.from_pretrained:从 Hub 模型仓库或本地目录加载处理器;ProcessorMixin.save_pretrained:把处理器及其全部子预处理器保存到本地目录;push_to_hub:把处理器一键推送/共享到 Hub。
ProcessorMixin在内部维护一组子处理器属性,动态挂载在实例上(processing_utils.py):
tokenizer: Any # 文本分词器 feature_extractor: Any # 音频特征提取器(旧称) image_processor: Any # 图像处理器 video_processor: Any # 视频处理器 chat_template: str | dict[str, str] | None注意__init__(processing_utils.py)会做严格的参数校验:传入的关键字参数必须落在get_attributes()声明的子处理器名单内,否则抛TypeError;每个参数还会通过check_argument_for_proper_class校验类型(比如把 feature extractor 误传给 tokenizer 槽位会被立刻拦截),避免"顺序传错"这类低级错误。
组合式设计的直观例证
PaliGemmaProcessor的构造函数说明一个视觉-语言处理器通常由image_processor + tokenizer组合而成,并且可以扩展模型专属能力:
def __init__(self, image_processor=None, tokenizer=None, chat_template=None, **kwargs): # 要求图像处理器携带 image_seq_length(用于把图像展开成固定长度的视觉 token 序列) if not hasattr(image_processor, "image_seq_length"): raise ValueError("Image processor is missing an `image_seq_length` attribute.") ...类似地,WhisperProcessor的组合更简单:__init__(self, feature_extractor, tokenizer)。音频类处理器的实现也很直白——若同时给出audio与text,则分别调用feature_extractor(audio, sampling_rate=...)与tokenizer(text),并把 tokenizer 输出的input_ids重命名为labels合入结果(processing_whisper.py)。
加载处理器:AutoProcessor 与模型专属类两种路径
加载处理器有两种等价方式,对应ProcessorMixin.from_pretrained的两种调用入口。
方式一:AutoProcessor(推荐,无需关心具体类名)
AutoProcessor是 AutoClass 体系的一部分,提供"根据 checkpoint 自动推断处理器类型"的免指定接口。它不能被直接__init__()实例化(会抛OSError),只能通过类方法from_pretrained使用:
from transformers import AutoProcessor processor = AutoProcessor.from_pretrained("google/paligemma-3b-pt-224")从源码可以看到其解析流程(processing_auto.py):AutoProcessor.from_pretrained会按优先级探测仓库根目录下的配置文件,依次尝试preprocessor_config.json(处理器配置)→ image processor 配置 → video processor 配置 → feature extractor 配置,从配置里的processor_class/auto_map字段解析出真正的处理器类再实例化。这也解释了为什么手动save_pretrained的目录也能被AutoProcessor无缝加载。
方式二:模型专属处理器类
处理器通常与某个预训练模型类绑定,因此也可直接从模型类身上加载:
from transformers import WhisperProcessor processor = WhisperProcessor.from_pretrained("openai/whisper-tiny")这种方式的好处是显式、类型明确,还能调用模型专属方法(如WhisperProcessor.get_decoder_prompt_ids、get_prompt_ids)。
进阶:手动拆装两个子预处理器
from_pretrained本质上是在帮你"分别加载子预处理器再组合"。你完全可以自己复现这一过程:先单独加载WhisperTokenizerFast与WhisperFeatureExtractor,再手动实例化处理器:
from transformers import WhisperTokenizerFast, WhisperFeatureExtractor, WhisperProcessor tokenizer = WhisperTokenizerFast.from_pretrained("openai/whisper-tiny") feature_extractor = WhisperFeatureExtractor.from_pretrained("openai/whisper-tiny") processor = WhisperProcessor(feature_extractor=feature_extractor, tokenizer=tokenizer)这一模式在给 checkpoint 更换/微调某个子预处理器(例如替换 tokenizer 词汇)时非常实用,因为 ProcessorMixin 的构造器 支持位置参数,只要保持子处理器顺序与声明的attributes一致即可组合任意配对。
预处理:把异构输入路由到正确的子预处理器
调用处理器(processor(text=..., images=..., audio=..., ...))会返回一个标准的BatchFeature对象,内部按return_tensors指定类型("pt"/"tf"/"np")张量化。
处理器call的分发逻辑
基类的__call__(processing_utils.py)实现了一条清晰的流水线:
prepare_inputs_layout:做输入规范化——把文本包成 batch 列表、通过image_processor.fetch_images拉取并解码 URL 图片、自动抓取/重采样音频(processing_utils.py);validate_inputs:校验"四种模态至少提供一种",否则抛ValueError(processing_utils.py);_merge_kwargs:把调用方 kwargs 与valid_processor_kwargs(即 ProcessingKwargs TypedDict,内含text_kwargs/images_kwargs/videos_kwargs/audio_kwargs分组默认值)合并,保证子处理器收到的是经过白名单校验的参数;- 按需分发:
if images is not None and hasattr(self, "image_processor")才调用图像处理器;音频只在该实例存在_audio_processor(feature_extractor 或 audio_processor)时才处理; - 汇总:
data = {**text_inputs, **processed_images, **processed_videos, **processed_audio},把文本输出与各模态输出合并为一个字典交给BatchFeature。
关键点是模态可选:processor 既能处理"text + image",也能只处理"text + audio",具体看这个多模态模型的子预处理器组合。
实战:用 Whisper 处理器准备 ASR 训练数据
自动语音识别(ASR)需要处理器同时处理文本与音频。以keithito/lj_speech数据集为例,先加载数据集并只保留audio与text两列(可删除不需要的file、id、normalized_text列):
from datasets import load_dataset dataset = load_dataset("keithito/lj_speech", split="train") dataset = dataset.map(remove_columns=["file", "id", "normalized_text"]) dataset[0]["audio"] {'array': array([-7.3242188e-04, -7.6293945e-04, -6.4086914e-04, ..., 7.3242188e-04, 2.1362305e-04, 6.1035156e-05], dtype=float32), 'path': '/root/.cache/huggingface/datasets/downloads/extracted/917ece08c95cf0c4115e45294e3cd0dee724a1165b7fc11798369308a465bd26/LJSpeech-1.1/wavs/LJ001-0001.wav', 'sampling_rate': 22050} dataset[0]["text"] 'Printing, in the only sense with which we are at present concerned, differs from most if not from all the arts and crafts represented in the Exhibition'务必重采样:LJSpeech 原始采样率为 22050 Hz,而 Whisper 预训练模型要求 16000 Hz,采样率不匹配会直接破坏input_features的频谱计算,因此先用datasets的Audio特性把audio列统一重采样:
from datasets import Audio dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))然后加载处理器并编写prepare_dataset映射函数。处理器把音频array转成input_features,把text转成labels,一次调用即产出训练所需全部模型输入:
from transformers import AutoProcessor processor = AutoProcessor.from_pretrained("openai/whisper-tiny") def prepare_dataset(example): audio = example["audio"] example.update(processor(audio=audio["array"], text=example["text"], sampling_rate=16000)) return example prepare_dataset(dataset[0])需要强调:传给处理器的sampling_rate=16000必须与cast_column后的音频实际采样率一致,这是音频类处理器能否正确计算梅尔频谱特征的前提。此模式可直接配合dataset.map(prepare_dataset)对全量数据做离线预处理,供Seq2SeqTrainer/DataCollatorForSeq2Seq使用。
视觉-语言示例:PaliGemma 的图像 + 文本输入
回到文档开头的 PaliGemma 例子,处理器把"提示文本 + PIL 图像"组合成模型需要的input_ids与pixel_values:
from transformers import AutoProcessor, PaliGemmaForConditionalGeneration from PIL import Image import requests processor = AutoProcessor.from_pretrained("google/paligemma-3b-pt-224") prompt = "answer en Where is the cat standing?" url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" image = Image.open(requests.get(url, stream=True).raw) inputs = processor(text=prompt, images=image, return_tensors="pt") inputs在 PaliGemmaProcessor.call的源码里还能看到视觉-语言处理器常见的几个细节:支持suffix关键字构建"前缀 + 后缀"式的 VQA 样本(后缀会自动追加 EOS token);构造时强制开启return_token_type_ids=True以便区分图像 token 与文本 token;若结果里含token_type_ids,会据此把非文本位置置为-100生成labels,让推理与训练共用同一入口。
多模态占位符(Placeholder Token)机制
现代视觉-语言模型(如 PaliGemma、LLaVA、Qwen-VL 系列)的文本提示中常包含图片/视频/音频占位符(如 PaliGemma 的{"image"}token)。处理器需要把这些占位符原地展开成与多模态输入一一对应的完整 token 串。基类在__call__中通过get_text_with_replacements(processing_utils.py)统一完成:
- 每个模态子处理器处理完后,若处理器声明了
image_token/video_token/audio_token属性,就调用子类实现的replace_image_token/replace_video_token/replace_audio_token(processing_utils.py,基类默认NotImplementedError)为每个图像/视频/音频生成替换字符串; - 随后对文本按
re.finditer扫描占位符,用"第 i 个图片占位符替换第 i 张图的替换串"的顺序消费替换列表; - 同时返回
replacement_offsets(每个占位符在原文与展开后文本中的(start, end)偏移),用于后端(如 vLLM 类推理引擎)把多模态数据与文本 token 精确对齐。
自定义多模态处理器的开发者只需在子类中定义好与文本中一致的image_token常量、重写对应的replace_image_token,即可接入这套统一的占位符机制。
保存、共享与 Chat Template
save_pretrained(processing_utils.py)会把一个组合处理器拆回多个标准文件落盘:
- 对每个子处理器调用其各自的
save_pretrained:分词器保存词汇文件并写入tokenizer_config.json等;若模型有多个分词器(如 encoder/decoder 双分词器),额外的分词器会存进以属性名命名的子目录; chat_template(Jinja 模板)单独存为chat_template.jinja,多套模板则放入chat_templates/目录(注意源码对模板名做了路径穿越防护,见 CWE-22 检查,processing_utils.py);- 最后把所有非 tokenizer 子预处理器的配置汇总成一份统一的
preprocessor_config.json(即PROCESSOR_NAME),其中的processor_class/auto_map字段正是AutoProcessor.from_pretrained反查处理器类型的依据。
因此,一个完整的处理器本地目录通常包含preprocessor_config.json、tokenizer_config.json、vocab.json、merges.txt、chat_template.jinja等文件;保存后再用AutoProcessor.from_pretrained("./my_processor_dir")即可原样还原,也支持push_to_hub=True一键发布到 Hub 与他人共享。
小结
Processor 是transformers多模态生态的"总装车间":它以ProcessorMixin为基类,将分词器、图像处理器、特征提取器等子预处理器组合成统一入口,通过AutoProcessor.from_pretrained免指定加载、通过__call__按模态自动路由、通过占位符替换机制打通"文本模板 ↔ 图像/音频张量"的对齐,并通过save_pretrained/push_to_hub实现配置的落盘与分享。掌握了这一套机制,无论为 Whisper 准备 ASR 训练集、为 PaliGemma 构造 VQA 样本,还是为新多模态模型编写自定义处理器,都能做到"一次预处理,处处可复用"。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考