Transformers 零样本图像分类(Zero-shot Image Classification)实战指南:Pipeline 与手动推理全解析
零样本图像分类(Zero-shot Image Classification)允许模型在未经针对特定类别训练的情况下,直接通过自由文本描述(如 "owl"、"This is a photo of a car.")对图像进行分类,是 🤗 Transformers 中典型的开放词表(open vocabulary)多模态任务。本文基于本仓库 docs/source/en/tasks/zero_shot_image_classification.md 展开,结合仓库内 Pipeline 实现源码与测试用例,带你掌握两条完整路径:一行代码接入 pipeline 完成推理,以及脱离 Pipeline、手动走完「图像预处理 → 文本编码 → 相似度计算 → 概率后处理」全流程,并理解其背后的 CLIP 类对比学习原理。
一、什么是零样本图像分类:从"闭集分类"到"开放词表"
传统图像分类是一个**闭集(closed-set)**问题:模型在一个固定的、带标签的数据集上训练,学习把特定图像特征"映射"到固定标签集合。一旦任务引入一批新标签(例如从"猫/狗"换到"狐狸/熊/海鸥/猫头鹰"),就必须收集新数据、重新微调(fine-tune)来"重新校准"模型,成本高昂且难以扩展。
零样本 / 开放词表图像分类模型则完全不同。它们通常是多模态模型,在大规模"图像 + 描述文本"配对数据上训练,学习对齐的视觉-语言表征(aligned vision-language representations)。这类表征可以直接迁移到多种下游任务,零样本图像分类正是其中之一:
- 无需为每个新类别准备训练样本;
- 用户可以直接用自由形式的自然语言描述目标对象(可以是单词,也可以是完整句子);
- 模型天然具备对从未见过类别的泛化能力。
从本仓库的 Auto 映射表 src/transformers/models/auto/modeling_auto.py#L1883-L1899 可以看到,MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES 登记了支持该任务的架构族,包括 CLIP、SigLIP、SigLIP2、AltCLIP、ChineseCLIP、ALIGN、BLIP、BLIP-2、CLIPSeg、MetaClip2、Tipsv2、VideoPrism 等,即这些模型的 checkpoint 均可直接用于本任务的 Pipeline 与 Auto 类加载。
开始前的环境准备
在动手之前,请确保已安装必要依赖(PyTorch 后端 + PIL 图像库):
pip install -q "transformers[torch]" pillow
transformers[torch] 会安装 torch 与 transformers 的 torch 相关依赖;pillow 用于图像的加载与处理。若本地已有 GPU 环境,PyTorch 会自动利用 CUDA 加速。
二、快速上手:Zero-shot Image Classification Pipeline
2.1 实例化 Pipeline
最简单的推理方式是利用 [pipeline] 工厂函数,传入 Hub 上的 checkpoint 与任务标识符 "zero-shot-image-classification":
>>> from transformers import pipeline
>>> checkpoint = "openai/clip-vit-large-patch14"
>>> detector = pipeline(model=checkpoint, task="zero-shot-image-classification")
任务标识符与 Pipeline 类的对应关系定义在 src/transformers/pipelines/init.py 中;"zero-shot-image-classification" 会实例化 src/transformers/pipelines/zero_shot_image_classification.py 中的 ZeroShotImageClassificationPipeline。从源码可以看出(同文件 L62-L65),该 Pipeline 会加载 image processor(负责图像预处理)与 tokenizer(负责文本编码)两个组件。
2.2 准备输入图像
Pipeline 的 __call__ 签名(源码 L83-L88)支持三种图像输入形式:HTTP 链接字符串、本地路径字符串、直接传入 PIL.Image 对象。下面通过 URL 加载一张示例图片:
>>> from PIL import Image
>>> import requests
>>> url = "https://unsplash.com/photos/g8oS8-82DxI/download?ixid=MnwxMjA3fDB8MXx0b3BpY3x8SnBnNktpZGwtSGt8fHx8fDJ8fDE2NzgxMDYwODc&force=true&w=640"
>>> image = Image.open(requests.get(url, stream=True).raw)
2.3 传入候选标签并获取预测
将图像与**候选标签(candidate labels)**一起交给 Pipeline。候选标签可以是 "fox" 这样的简单单词,也可以是更描述性的短语:
>>> predictions = detector(image, candidate_labels=["fox", "bear", "seagull", "owl"])
>>> predictions
[{'score': 0.9996670484542847, 'label': 'owl'},
{'score': 0.000199399160919711, 'label': 'seagull'},
{'score': 7.392891711788252e-05, 'label': 'fox'},
{'score': 5.96074532950297e-05, 'label': 'bear'}]
输出是一个字典列表,每个字典含 label(候选标签原文)与 score(模型对该标签的置信度,取值 0~1),且已按分数从高到低排序。本例中模型以约 99.97% 的置信度将图片判为 owl(猫头鹰)。
2.4 Pipeline 的关键参数与底层行为
结合 src/transformers/pipelines/zero_shot_image_classification.py 源码,Pipeline 的若干行为值得深入理解:
hypothesis_template(默认 "This is a photo of {}.")
源码 preprocess(L137-L158)会用该模板对每个候选标签做格式化:sequences = [hypothesis_template.format(x) for x in candidate_labels]。即 Pipeline 内部会把 "owl" 变成 "This is a photo of owl." 再送入文本编码器。若你的候选标签已经是完整句子,可传 hypothesis_template="{}" 跳过格式化。
timeout
控制从网络抓取图片的最大等待秒数(源码 L108-L110、L147)。默认 None 表示不设超时,可能阻塞较久;生产环境建议显式设置。
SigLIP 等模型的特殊文本处理
源码 L153-L155 有一个针对 siglip 架构的特判:会额外设置 padding="max_length", max_length=64, truncation=True,强制把文本序列 pad 到固定长度。这是因为 SigLIP 使用 sigmoid 二元交叉熵目标而非 softmax,其文本编码对 padding 位置更敏感。这也直接影响了后处理逻辑(见下文 3.3 节)。
批处理与多图输入
__call__ 同时接受图像列表(源码 L80-L81),并支持 batch_size 参数。仓库测试 tests/pipelines/test_pipelines_zero_shot_image_classification.py#L94-L134 验证了 image_classifier([image] * 5, candidate_labels=["A", "B", "C"], batch_size=2) 的批量推理行为,并对每个输出元素调用 compare_pipeline_output_to_hub_spec 校验其与 Hub 输出规范一致。
三、手动推理:亲手实现零样本分类的完整数据流
Pipeline 封装了全部细节;若要深入理解原理、或在自定义训练/评估循环中复用,可以手动复现整个流程。下面的步骤与 Pipeline 内部实现一一对应。
3.1 加载模型与处理器
使用 Auto 类加载模型与处理器。**处理器(processor)**是一个组合组件:内部包含 image processor(负责图像的 resize、归一化)与 tokenizer(负责文本的分词),这与 Pipeline 源码中同时加载两者的行为一致:
>>> from transformers import AutoProcessor, AutoModelForZeroShotImageClassification
>>> model = AutoModelForZeroShotImageClassification.from_pretrained(checkpoint)
>>> processor = AutoProcessor.from_pretrained(checkpoint)
AutoModelForZeroShotImageClassification 依据上文提到的 Auto 映射表(src/transformers/models/auto/modeling_auto.py#L1883-L1899)自动选择正确的模型类,例如 CLIP 架构会加载 CLIPModel。
换一张图片继续演示(一张汽车照片):
>>> from PIL import Image
>>> import requests
>>> url = "https://unsplash.com/photos/xBRQfR2bqNI/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc4Mzg4ODEx&force=true&w=640"
>>> image = Image.open(requests.get(url, stream=True).raw)
3.2 用 Processor 构造模型输入
与 Pipeline 内部逻辑(源码 L151)保持一致,先把候选标签套上提示模板,再一次性把图像与文本送入 processor:
>>> candidate_labels = ["tree", "car", "bike", "cat"]
# 遵循 pipeline 的 prompt 模板以获得一致结果
>>> candidate_labels = [f'This is a photo of {label}.' for label in candidate_labels]
>>> inputs = processor(images=image, text=candidate_labels, return_tensors="pt", padding=True)
这一步做了什么?
- 图像侧:image processor 将 PIL 图像 resize 到模型要求的输入尺寸(如 224×224 或 384×384),做归一化,得到
pixel_values; - 文本侧:tokenizer 将 4 条模板化文本分词并 padding 到等长,得到
input_ids与attention_mask; return_tensors="pt"指定返回 PyTorch 张量(Pipeline 源码中同样使用return_tensors="pt",见 src/transformers/pipelines/zero_shot_image_classification.py#L148-L156)。
3.3 前向传播与概率后处理
将输入送入模型,取出图像-文本相似度 logits,再做 softmax 归一化:
>>> import torch
>>> with torch.no_grad():
... outputs = model(**inputs)
>>> logits = outputs.logits_per_image[0]
>>> probs = logits.softmax(dim=-1).numpy()
>>> scores = probs.tolist()
>>> result = [
... {"score": score, "label": candidate_label}
... for score, candidate_label in sorted(zip(probs, candidate_labels), key=lambda x: -x[0])
... ]
>>> result
[{'score': 0.998572, 'label': 'car'},
{'score': 0.0010570387, 'label': 'bike'},
{'score': 0.0003393686, 'label': 'tree'},
{'score': 3.1572064e-05, 'label': 'cat'}]
logits_per_image 是什么? 以 CLIP 架构为例(src/transformers/models/clip/modeling_clip.py),模型分别通过图像编码器(get_image_features,L710)与文本编码器(get_text_features,L674)得到视觉与文本表征,计算二者相似度矩阵,输出形状为 (图像数, 文本数) 的 logits_per_image(L808 附近),再对其转置得到 logits_per_text。对本任务而言,每一行就是"这张图像与每个候选标签文本"的相似度得分,对其做 softmax(dim=-1) 即可得到归一化的类别概率分布。
结果为何与 Pipeline 一致? 因为 Pipeline 的 postprocess(源码 L177-L195)执行的是完全相同的操作:对 logits 做 softmax(SigLIP 架构则用 torch.sigmoid,见 L180-L184,这是其训练目标为逐标签二元分类所致),随后按分数降序 zip 标签与分数。
四、理解 Pipeline 内部的完整调用链
ZeroShotImageClassificationPipeline 继承自基类 Pipeline(src/transformers/pipelines/base.py),遵循标准的 preprocess → _forward → postprocess 三段式管线:
- preprocess(L137-L158):
load_image加载图像(支持 URL/本地路径/PIL,含timeout控制)→ image processor 产出pixel_values→ 用hypothesis_template格式化候选标签 → tokenizer 产出文本输入 → 打包为model_inputs; - _forward(L160-L175):把文本输入与图像张量一起喂给模型,取出
outputs.logits_per_image; - postprocess(L177-L195):softmax/sigmoid 归一化 → 按分数排序 → 输出
{"score", "label"}字典列表。
同时,Pipeline 在初始化时通过 self.check_model_type(MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES)(L71)校验所加载模型是否属于支持该任务的架构族,避免错误搭配。
五、模型选择与测试佐证
- CLIP 系:
openai/clip-vit-large-patch14(本文示例)、openai/clip-vit-base-patch32等,使用 softmax 概率;仓库慢测试 tests/pipelines/test_pipelines_zero_shot_image_classification.py#L140-L171 用一张包含"两只猫与遥控器"的 COCO 测试图片验证了openai/clip-vit-base-patch32的输出(remote: 0.511, cat: 0.485, plane: 0.004)。 - SigLIP 系:
google/siglip-base-patch16-224等,文本侧强制 padding 到 64 长度,后处理改用 sigmoid;对应慢测试见同文件 L173-L205。 - BLIP-2 系:
Salesforce/blip2-itm-vit-g,其 tokenizer 需关闭 token_type_ids,Pipeline 测试通过tokenizer_kwargs={"return_token_type_ids": False}传参(L207-L229)——这也说明tokenizer_kwargs是 Pipeline 暴露给用户的关键透传参数。 - 快速验证:测试中使用了
hf-internal-testing/tiny-random-clip-zero-shot-image-classification小模型(L76-L79),适合无 GPU 环境下跑通流程。
六、小结
零样本图像分类把图像分类从"固定标签集合 + 重新微调"的模式中解放出来。通过本仓库的 pipeline(task="zero-shot-image-classification") 一行代码即可完成推理;手动路径则揭示了其本质——CLIP 类多模态模型学习到的视觉-语言对齐表征,配合 hypothesis_template 文本模板与 softmax 概率归一化,即可对任意自然语言描述的类别进行打分排序。想进一步阅读 Pipeline 通用用法,可查看仓库文档 docs/source/en/pipeline_tutorial.md;想深入实现细节,可直接阅读 src/transformers/pipelines/zero_shot_image_classification.py 与 tests/pipelines/test_pipelines_zero_shot_image_classification.py。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00