首页
/ Transformers 零样本图像分类(Zero-shot Image Classification)实战指南:Pipeline 与手动推理全解析

Transformers 零样本图像分类(Zero-shot Image Classification)实战指南:Pipeline 与手动推理全解析

2026-09-08 19:46:53作者:冯爽妲Honey

零样本图像分类(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_idsattention_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 继承自基类 Pipelinesrc/transformers/pipelines/base.py),遵循标准的 preprocess → _forward → postprocess 三段式管线:

  1. preprocess(L137-L158)load_image 加载图像(支持 URL/本地路径/PIL,含 timeout 控制)→ image processor 产出 pixel_values → 用 hypothesis_template 格式化候选标签 → tokenizer 产出文本输入 → 打包为 model_inputs
  2. _forward(L160-L175):把文本输入与图像张量一起喂给模型,取出 outputs.logits_per_image
  3. 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.pytests/pipelines/test_pipelines_zero_shot_image_classification.py

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391