Transformers 中的 OWL-ViT 开放词汇目标检测:原理、源码解析与零样本/单样本实战
【免费下载链接】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
OWL-ViT(Vision Transformer for Open-World Localization)是 🤗 Transformers 中实现的开集(开放词汇)目标检测模型:它不再受固定类别标签集的约束,而是能够接收任意一段文字描述(如 "a photo of a cat")作为查询,在图像中定位并框出与文本语义匹配的目标,同时支持"用一张示例图去找相似物体"的单样本(image-guided)检测。本文以 docs/source/en/model_doc/owlvit.md 为主线,结合本仓库的模型源码、配置与处理类实现,系统讲解 OWL-ViT 的架构原理、OwlViTProcessor的预处理/后处理流程、三大实战用法(零样本文本条件检测、单样本图像条件检测、pipeline 快捷调用),并给出所有关键类与方法在仓库中的对应位置,供读者按需查阅和直接复现。
OWL-ViT 是什么:从"固定类别"到"开放词汇"的目标检测
传统目标检测模型(如 Faster R-CNN、DETR)的分类头只包含训练时见过的一组固定类别,遇到长尾类别或新类别时无法泛化。OWL-ViT 由 Matthias Minderer 等人在论文Simple Open-Vocabulary Object Detection with Vision Transformers中提出(2022-05-12 发表于 HF Papers,2022-07-22 由 adirik 贡献到 Transformers),目标是解决开放词汇目标检测问题:用任意自然语言查询目标物体。
论文摘要的核心观点是:将"简单的架构 + 大规模预训练"从图像分类迁移到目标检测。具体配方为三阶段:
- 对比式图文预训练(contrastive image-text pre-training)——在大量 (image, text) 对上训练图文对齐模型;
- 检测微调——以最小的结构改动将图文模型迁移为检测模型,进行端到端检测微调(end-to-end detection fine-tuning);
- 两种推理范式——零样本文本条件检测(zero-shot text-conditioned)与单样本图像条件检测(one-shot image-conditioned)。
论文同时指出:图像级预训练规模与模型尺寸的扩大能带来下游检测任务的一致提升。
架构与工作原理:从源码看 OWL-ViT 如何做检测
以 CLIP 为多模态骨干
OWL-ViT 以 CLIP 作为多模态骨干:
- 视觉侧:类 ViT 的 Transformer,从图像中提取视觉特征;
- 文本侧:因果语言模型风格的 Transformer,从文本查询中提取文本特征。
在 configuration_owlvit.py 中可以看到三个配置类,它们分别描述文本编码器、视觉编码器与完整模型。完整模型OwlViTConfig(第 101 行起)通过sub_configs组合文本与视觉子配置,同时定义了projection_dim = 512与logit_scale_init_value = 2.6592两个跨模态参数。
从图像级对比学习到逐 token 检测:改造的关键
把 CLIP 用于检测,核心改动发生在 modeling_owlvit.py:
- 普通 CLIP 会在视觉 Transformer 末端做 token pooling(把整张图压成一个向量,用于图文匹配);OWL-ViT去掉最终的 token pooling 层,改为给每一个 Transformer 输出 token(对应图像 patch)挂上轻量的分类头与框回归头;
- 普通分类器通常使用固定权重的分类层(fixed classification layer weights);OWL-ViT 的开放词汇分类通过把分类层权重替换为"类别名称文本经文本模型得到的 embedding"来实现,因此理论上任意文本短语都可作为检测类别。
完整模型OwlViTModel(modeling_owlvit.py)内部组合了OwlViTTextTransformer、OwlViTVisionTransformer、无偏置的visual_projection/text_projection(把各自 hidden size 投影到projection_dim)以及可学习的logit_scale参数。它的forward(modeling_owlvit.py)遵循标准的 CLIP 式对比学习流程:编码文本与图像 → L2 归一化 → 余弦相似度乘logit_scale.exp()得到logits_per_image/logits_per_text;当return_loss=True时还会调用image_text_contrastive_loss计算对称对比损失。这一部分是 OWL-ViT 训练时 CLIP 阶段的主体,对应论文中"作者先从头训练 CLIP,再端到端微调"的描述。
分类头与框预测头:如何从 patch token 得到"框 + 类别"
检测能力由OwlViTForObjectDetection(modeling_owlvit.py)实现,它把一个OwlViTModel与两个轻量检测头组合在一起:
OwlViTBoxPredictionHead(modeling_owlvit.py):一个Linear → GELU → Linear → GELU → Linear(4)的 MLP,对每个 patch token 输出 4 个值(cx, cy, w, h)(相对图像归一化到 [0,1])。OwlViTClassPredictionHead(modeling_owlvit.py):先把图像 patch 特征线性投影到文本维度,再与文本 query embedding 做点积(torch.einsum("...pd,...qd->...pq"))得到类别 logits;此外通过logit_shift与logit_scale两个线性层给 logits 施加可学习的平移与缩放(elu + 1),以改善零样本 logits 的校准。
前向路径中的两个关键细节值得留意:
- 图像表示成 patch 网格:在
image_text_embedder(modeling_owlvit.py)中,视觉模型输出的 token 序列被拆成"class token + patch token",OWL-ViT 将 class token 广播并逐元素乘到所有 patch token上,再经 LayerNorm 后 reshape 为(batch, num_patches_height, num_patches_width, hidden)的特征图——即OwlViTObjectDetectionOutput.image_embeds。这种"class 语义注入每个 patch"的操作是 OWL-ViT 区别于普通 ViT 的重要设计。 - 框中心带网格偏置(box bias):
compute_box_bias(modeling_owlvit.py)根据每个 patch 在特征网格上的归一化坐标生成中心偏置,并把框尺寸偏置到 patch 尺寸附近。这样box_predictor输出的初始框已经"落在各自 patch 附近",再经 sigmoid 得到最终归一化框,显著降低了回归难度。
一个前向过程发生了什么(零样本推理)
调用OwlViTForObjectDetection(**inputs)时(modeling_owlvit.py)依次发生:
image_text_embedder同时编码图像与全部文本 query,得到query_embeds与 patch 特征图;- 把
(batch * max_queries, hidden)的文本 embedding reshape 回(batch, max_queries, hidden); - 依据每个 query 的首个 token 是否为 0 构造
query_mask(input_ids[..., 0] > 0)——这是处理器内部用空串把"每条样本的 query 数量"补齐到 batch 最大值后,模型据此屏蔽 padding 文本的手段; class_predictor逐 patch 计算每个文本 query 的类别 logits;box_predictor输出逐 patch 的归一化框。
最终返回OwlViTObjectDetectionOutput,其核心字段包括:
logits:(batch, num_patches, num_queries)分类 logits;pred_boxes:(batch, num_patches, 4),格式为(center_x, center_y, width, height)且归一化到 [0,1];text_embeds、image_embeds、class_embeds等中间表征。
该输出类的完整字段说明见 modeling_owlvit.py。需要强调:pred_boxes是归一化的中心点坐标格式,后处理时必须交给处理器的post_process_*方法换算回原图尺寸的(xmin, ymin, xmax, ymax)格式。
单样本图像引导检测:image_guided_detection
OWL-ViT 还支持"用示例图代替文字"作为查询,即 one-shot image-conditioned detection。OwlViTForObjectDetection.image_guided_detection(modeling_owlvit.py)的实现思路是:
- 分别对目标图与 query 图提取特征图(
image_embedder); - 在
embed_image_query(modeling_owlvit.py)中,对 query 图上每个 patch 预测框并计算其与整图框([0,0,1,1])的 IoU,选取"与最佳 IoU 差距在 80% 以内"的 patch,用与全局平均 embedding 余弦相似度最小的那个 patch 的 class embedding 作为该 query 图的视觉查询特征; - 用该视觉 query embedding 走与零样本相同的
class_predictor+box_predictor,在目标图中找出所有相似的物体。
由于整个过程不依赖文本,"query 图可以是从任意目标图里裁剪出的一小块区域",这与文字查询互为补充(也正因如此,代码中OwlViTImagesKwargs.query_images的注释明确写着"query images override any text prompts")。输出类型为OwlViTImageGuidedObjectDetectionOutput,见 modeling_owlvit.py。
模型配置与可用检查点
三个配置类都支持直接实例化后构建随机模型,也可以随 checkpoint 一起从 Hub 加载(示例中模型基类 docstring 锚定google/owlvit-base-patch16风格配置)。核心默认值如下:
OwlViTTextConfig(configuration_owlvit.py)
| 参数 | 默认值 | 说明 |
|---|---|---|
vocab_size | 49408 | 词表大小(CLIP BPE) |
hidden_size | 512 | 隐藏维度 |
intermediate_size | 2048 | FFN 中间维度 |
num_hidden_layers | 12 | 层数 |
num_attention_heads | 8 | 注意力头数 |
max_position_embeddings | 16 | 文本最大长度(CLIP 风格短语编码) |
hidden_act | quick_gelu | 激活函数 |
layer_norm_eps | 1e-5 | LayerNorm epsilon |
pad/bos/eos_token_id | 0 / 49406 / 49407 | 特殊 token |
OwlViTVisionConfig(configuration_owlvit.py)
| 参数 | 默认值 | 说明 |
|---|---|---|
hidden_size | 768 | 隐藏维度 |
intermediate_size | 3072 | FFN 中间维度 |
num_hidden_layers | 12 | 层数 |
num_attention_heads | 12 | 注意力头数 |
image_size | 768 | 训练用输入分辨率(正方形) |
patch_size | 32 | patch 尺寸 |
num_channels | 3 | 输入通道 |
hidden_act | quick_gelu | 激活函数 |
OwlViTConfig(configuration_owlvit.py)聚合两个子配置并额外定义projection_dim = 512、logit_scale_init_value = 2.6592;__post_init__中若text_config/vision_config传了 dict 会自动转成对应的PreTrainedConfig。文末 API 一览中OwlViTModel的get_text_features/get_image_features也直接构建在这些配置之上。
官方文档与源码示例中反复出现的可直接加载检查点为google/owlvit-base-patch32与google/owlvit-base-patch16(patch32 的视觉分支将 768×768 输入切成 32×32 的 patch,patch16 版本 patch 更细、特征图更密)。
数据预处理:OwlViTImageProcessor、OwlViTImageProcessorPil与OwlViTProcessor
OWL-ViT 的输入侧同时涉及图像与文本,官方推荐使用OwlViTProcessor一站式完成两者。它内部组合了:
- 图像处理器(resize/rescale/normalize 图像),文档中对应
OwlViTImageProcessor与OwlViTImageProcessorPil两个类; CLIPTokenizer,用于把文本 query 编码成 token。
图像处理器
OwlViTImageProcessor(image_processing_owlvit.py)在类属性上直接声明了默认预处理参数:
resample = BICUBIC、do_resize = True;size = {"height": 768, "width": 768}、crop_size同为 768,default_to_square = True(图像会被统一到 768×768 的正方形输入);- 均值/方差沿用 CLIP 的
OPENAI_CLIP_MEAN/OPENAI_CLIP_STD,并默认do_rescale = True、do_normalize = True、do_convert_rgb = True。
它基于仓库的 Torchvision 后端(TorchvisionBackend)实现;另一个等价类OwlViTImageProcessorPil(image_processing_pil_owlvit.py)基于PilBackend实现,并承载了文档中列出的两个后处理方法:post_process_object_detection与post_process_image_guided_detection(即把模型输出的归一化(cx, cy, w, h)框换算到target_sizes指定尺寸下的(xmin, ymin, xmax, ymax)角点格式)。
OwlViTProcessor.__call__的输入编排
OwlViTProcessor的__call__(processing_owlvit.py)支持同时传入images、text(可叠加嵌套列表)与可选的query_images,并自动完成三项编排:
- 文本:
text必须是"字符串 / 字符串列表 / 嵌套字符串列表"。嵌套列表的语义是[batch 内的每张图, 该图的多条 query];多条 query 时处理器会先统计 batch 内最大 query 数,用空字符串" "把每条样本补齐到一致长度再统一 tokenize(默认padding="max_length"),输出input_ids与attention_mask。 - 查询图:传了
query_images时输出query_pixel_values,并优先于文本(代码中会以 query 图像结果直接覆盖数据)。 - 图像:图像经处理器得到
pixel_values。
OwlViTProcessorKwargs的默认行为是文本padding="max_length"、return_tensors="pt"(见 processing_owlvit.py),因此不传return_tensors时默认返回 PyTorch 张量。
实战一:零样本文本条件目标检测
官方文档(owlvit.md)给出的标准用法如下——加载处理器与检测模型,给出一张 COCO 图与多条文本查询,让模型找出图中的猫和狗:
import requests from PIL import Image import torch from transformers import OwlViTProcessor, OwlViTForObjectDetection processor = OwlViTProcessor.from_pretrained("google/owlvit-base-patch32") model = OwlViTForObjectDetection.from_pretrained("google/owlvit-base-patch32", device_map="auto") url = "http://images.cocodataset.org/val2017/000000039769.jpg" image = Image.open(requests.get(url, stream=True).raw) text_labels = [["a photo of a cat", "a photo of a dog"]] inputs = processor(text=text_labels, images=image, return_tensors="pt").to(model.device) outputs = model(**inputs) # Target image sizes (height, width) to rescale box predictions [batch_size, 2] target_sizes = torch.tensor([(image.height, image.width)]) # Convert outputs (bounding boxes and class logits) to Pascal VOC format (xmin, ymin, xmax, ymax) results = processor.post_process_grounded_object_detection( outputs=outputs, target_sizes=target_sizes, threshold=0.1, text_labels=text_labels ) # Retrieve predictions for the first image for the corresponding text queries result = results[0] boxes, scores, text_labels = result["boxes"], result["scores"], result["text_labels"] for box, score, text_label in zip(boxes, scores, text_labels): box = [round(i, 2) for i in box.tolist()] print(f"Detected {text_label} with confidence {round(score.item(), 3)} at location {box}") Detected a photo of a cat with confidence 0.707 at location [324.97, 20.44, 640.58, 373.29] Detected a photo of a cat with confidence 0.717 at location [1.46, 55.26, 315.55, 472.17]对这段流程做几点拆解:
post_process_grounded_object_detection(processing_owlvit.py)内部先调用图像处理器的post_process_object_detection完成:按threshold(默认 0.1)过滤低置信度预测、把模型输出的 logits 转成每个 patch 的分数并做必要的 NMS 处理、用target_sizes把归一化框缩放到原始图像尺寸;随后它把labels(预测命中的 query 下标)映射回你传入的text_labels,从而在返回结果中直接给出"命中的文本短语"。返回字典含scores、labels、boxes((xmin, ymin, xmax, ymax)格式)与text_labels四类键。- query 语义:这里文本写的是 "a photo of a cat" 这样的完整短语而不是裸类别名,这是 CLIP 系文本编码的常见范式——OWL-ViT 的类别 logits 本质是"图像 patch 与 query 短语 embedding 的余弦相似度",查询短语措辞会直接影响结果。
target_sizes必须给出:因为模型输出是相对 768×768 输入的归一化框,只有配合(height, width)才能得到原图坐标系下的真实框坐标。- 阈值经验:开放词汇场景下 logits 在不同措辞间天然缺乏可比性,官方默认
threshold=0.1,实践中常需按场景调低/调高。
实战二:单样本图像条件检测
把 query 从"文字"换成"示例图",需要用到image_guided_detection与post_process_image_guided_detection。下面这个完整示例改编自 modeling_owlvit.py 中image_guided_detection的 docstring,也是文档所述单样本能力最直接的验证:
import httpx from io import BytesIO from PIL import Image import torch from transformers import AutoProcessor, OwlViTForObjectDetection processor = AutoProcessor.from_pretrained("google/owlvit-base-patch16") model = OwlViTForObjectDetection.from_pretrained("google/owlvit-base-patch16") url = "http://images.cocodataset.org/val2017/000000039769.jpg" with httpx.stream("GET", url) as response: image = Image.open(BytesIO(response.read())) query_url = "http://images.cocodataset.org/val2017/000000001675.jpg" with httpx.stream("GET", query_url) as response: query_image = Image.open(BytesIO(response.read())) inputs = processor(images=image, query_images=query_image, return_tensors="pt") with torch.no_grad(): outputs = model.image_guided_detection(**inputs) # Target image sizes (height, width) to rescale box predictions [batch_size, 2] target_sizes = torch.Tensor([image.size[::-1]]) # Convert outputs (bounding boxes and class logits) to Pascal VOC format (xmin, ymin, xmax, ymax) results = processor.post_process_image_guided_detection( outputs=outputs, threshold=0.6, nms_threshold=0.3, target_sizes=target_sizes ) i = 0 # Retrieve predictions for the first image boxes, scores = results[i]["boxes"], results[i]["scores"] for box, score in zip(boxes, scores): box = [round(i, 2) for i in box.tolist()] print(f"Detected similar object with confidence {round(score.item(), 3)} at location {box}") Detected similar object with confidence 0.856 at location [10.94, 50.4, 315.8, 471.39] Detected similar object with confidence 1.0 at location [334.84, 25.33, 636.16, 374.71]与文本条件版本相比,这里有两处显著差异:
- 模型侧调用
model.image_guided_detection(**inputs)而非直接model(**inputs),因为单样本模式必须让 query 图先经过embed_image_query提取视觉查询特征; - 后处理换用
post_process_image_guided_detection(processing_owlvit.py),其签名额外提供threshold(默认 0.0)与nms_threshold(默认 0.3):视觉相似度模式下冗余框更多,需要靠 NMS 阈值压制互相重叠的重复检测,此输出中labels字段恒为None(因为没有类别语义)。
实战三:pipeline 一键零样本检测
如果不关心底层 API,仓库为 OWL-ViT 提供了开箱即用的"zero-shot-object-detection"pipeline(zero_shot_object_detection.py),把"图像加载 → 预处理 → 推理 → 画框结果整理"全部封装好:
from transformers import pipeline detector = pipeline(model="google/owlvit-base-patch32", task="zero-shot-object-detection") detector( "http://images.cocodataset.org/val2017/000000039769.jpg", candidate_labels=["cat", "couch"], ) [{'score': 0.287, 'label': 'cat', 'box': {'xmin': 324, 'ymin': 20, 'xmax': 640, 'ymax': 373}}, {'score': 0.254, 'label': 'cat', 'box': {'xmin': 1, 'ymin': 55, 'xmax': 315, 'ymax': 472}}, {'score': 0.121, 'label': 'couch', 'box': {'xmin': 4, 'ymin': 0, 'xmax': 642, 'ymax': 476}}]ZeroShotObjectDetectionPipeline(由OwlViTForObjectDetection驱动)接受的关键参数:candidate_labels(字符串或字符串列表,对应文本 query)、threshold(默认 0.1)、top_k(默认返回全部)、timeout(抓取网络图片的超时秒数)。输出是每张图一个字典列表,每个字典含label、score、box(xmin/xmax/ymin/ymax)。它也支持一次传入多个 dict(各自带image与candidate_labels)批量处理。注意候选类别最好也采用与图文预训练一致的自然语言措辞,以便得到合理分数。
仓库中的实现文件与测试
若需深入源码或在源码上做二次开发,OWL-ViT 的实现集中在以下文件:
| 关注点 | 仓库路径 |
|---|---|
| 完整 PyTorch 模型(Text/Vision/联合/检测头/单样本检测) | src/transformers/models/owlvit/modeling_owlvit.py |
| 三个配置类及全部默认超参 | src/transformers/models/owlvit/configuration_owlvit.py |
| Processor(文本+图像编排、后处理转发) | src/transformers/models/owlvit/processing_owlvit.py |
| 图像预处理与后处理 | src/transformers/models/owlvit/image_processing_owlvit.py 与 src/transformers/models/owlvit/image_processing_pil_owlvit.py |
| 原版(Google Scenic/Flax)权重转 HF 脚本 | src/transformers/models/owlvit/convert_owlvit_original_flax_to_hf.py |
| 零样本检测 pipeline | src/transformers/pipelines/zero_shot_object_detection.py |
| 模型/处理器/图像处理测试 | tests/models/owlvit/test_modeling_owlvit.py、tests/models/owlvit/test_processing_owlvit.py、tests/models/owlvit/test_image_processing_owlvit.py |
这些测试是对本文所述用法最直接的机器验证:例如test_modeling_owlvit.py中既覆盖了文本条件检测的前向/后处理,也覆盖了image_guided_detection的单样本路径与输出结构一致性;test_processing_owlvit.py则覆盖了文本列表 padding、query_images覆盖文本等边界行为。
API 速查
下表汇总了模型文档(owlvit.md)中列出并通过 autodoc 生成的全部公开类及其核心方法,方便按需查阅:
| 类 | 核心方法 | 职责 |
|---|---|---|
OwlViTConfig | — | 顶层配置,聚合 text/vision 子配置 |
OwlViTTextConfig | — | 文本编码器配置 |
OwlViTVisionConfig | — | 视觉编码器配置 |
OwlViTImageProcessor | preprocess | 图像 resize/rescale/normalize |
OwlViTImageProcessorPil | preprocess、post_process_object_detection、post_process_image_guided_detection | PIL 后端的预处理与检测结果后处理 |
OwlViTProcessor | __call__、post_process_grounded_object_detection、post_process_image_guided_detection | 一站式图文预处理与后处理 |
OwlViTModel | forward、get_text_features、get_image_features | CLIP 式图文联合模型(用于对比特征/作为检测骨干) |
OwlViTTextModel | forward | 文本编码 |
OwlViTVisionModel | forward | 视觉编码(逐 patch 特征,保留位置信息) |
OwlViTForObjectDetection | forward、image_guided_detection | 开放词汇检测模型(文本条件 + 单样本图像条件) |
常见疑问与注意事项
- 为什么文本查询要用嵌套列表?OWL-ViT 支持"一张图多条 query",因此
text用[image1 的 queries, image2 的 queries, ...]表达;每条样本的 query 数量可以不同,处理器会自动用空串补到 batch 内最大长度,模型侧再通过首 token 是否为 0 的query_mask屏蔽填充项(modeling_owlvit.py)。 pred_boxes是哪种格式?模型的原始输出是归一化的(center_x, center_y, width, height)(cxcywh,相对 768×768 的输入分辨率),必须经post_process_grounded_object_detection(传target_sizes)才会换算成原图像素坐标的 Pascal VOC 角点格式(xmin, ymin, xmax, ymax)。- 文本条件与图像条件检测的输出差异:文本条件结果含
labels与text_labels;图像条件结果(image_guided_detection)不含类别语义,labels为None,且默认threshold=0.0、nms_threshold=0.3,需要靠 NMS 清理相似物体带来的重复框。 - 分辨率与位置编码插值:视觉分支的
image_size默认 768。若输入不同分辨率,interpolate_pos_encoding=True会对预训练位置编码插值,同时box_predictor/image_embedder会按实际 patch 数量动态重算网格偏置(modeling_owlvit.py),因此 OWL-ViT 对输入尺寸并不严格限定。 - 模型是否可做特征提取?可以。
OwlViTModel的get_text_features/get_image_features(modeling_owlvit.py)直接返回投影后的文本/图像特征,可用于图文检索或作为下游任务的 embedding 来源。 - 后续模型:若需要更强或分辨率更高的开放词汇检测能力,可在仓库中继续查看同思路演进的 docs/source/en/model_doc/owlv2.md 与
src/transformers/models/owlv2/目录(与 OWL-ViT 同源但架构与训练细节不同),不过本文所有 API 均以 OWL-ViT(owlvit)为准。
综上,OWL-ViT 的价值在于把"图像级图文对齐"的能力低成本地迁移到"逐区域开放词汇定位"上。从OwlViTProcessor的图文编排,到OwlViTForObjectDetection的逐 patch 分类/回归头,再到image_guided_detection的单样本视觉查询,你可以用不到 20 行代码完成对任意自然语言短语的图像目标搜索——这正是开放世界目标检测从论文走向工程实用的最小闭环。
【免费下载链接】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),仅供参考