Transformers 中的 SigLIP:图文相似度、零样本图像分类与低显存推理的完整指南
【免费下载链接】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
SigLIP 是 Hugging Face Transformers 中一个与 CLIP 同类的多模态图文模型,但其训练时采用成对 sigmoid 损失(pairwise sigmoid loss),摆脱了对 batch 内全局相似度矩阵的依赖,因此在小 batch 下也能保持优异性能。读完本文,你将掌握用pipeline和AutoModel两种方式使用 SigLIP 计算图文相似度、复现官方零样本分类效果(含 prompt 模板与padding="max_length"细节)、理解SiglipModel双塔结构及其 sigmoid 损失的源码实现,以及通过 bitsandbytes 4bit 量化降低显存负担的完整方法。
SigLIP 是什么:与 CLIP 的关键区别
SigLIP 使用独立的图像编码器和文本编码器,分别为两种模态生成表示。与 CLIP(参见 CLIP 文档)的核心差异在于训练损失函数:
- CLIP 风格:对比学习需要看到 batch 内所有图文对的全局相似度矩阵(softmax over batch),batch 越大负样本越多、效果越好,但对显存和 batch size 有较强依赖;
- SigLIP:训练时对图文对使用成对 sigmoid 损失,不需要 batch 内全局视角。这带来两个直接好处——可以高效扩展到大 batch,同时在小 batch 下也能取得更好的性能(引自官方文档 SigLIP 模型文档)。
所有官方 SigLIP 检查点均可通过google/siglip-*系列模型获取,例如本文示例使用的google/siglip-base-patch16-224(base 规模、16px patch、224 分辨率)。
快速上手:Pipeline 方式做零样本图像分类
最简路径是用pipeline加载zero-shot-image-classification任务:
from transformers import pipeline image = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" candidate_labels = ["a Pallas cat", "a lion", "a Siberian tiger"] pipeline = pipeline(task="zero-shot-image-classification", model="google/siglip-base-patch16-224", device=0) pipeline(image, candidate_labels=candidate_labels)pipeline内部会自动完成图像预处理、文本模板化(把每个候选标签包装为自然语言句子)、双向量相似度计算和概率归一化,适合快速验证模型效果。
AutoModel 方式:手动控制图文相似度计算
当需要自定义 prompt 模板、复用特征或接入下游逻辑时,使用AutoModel+AutoProcessor手动调用:
import requests import torch from PIL import Image from transformers import AutoModel, AutoProcessor model = AutoModel.from_pretrained("google/siglip-base-patch16-224", device_map="auto", attn_implementation="sdpa") processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224") url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" image = Image.open(requests.get(url, stream=True).raw) candidate_labels = ["a Pallas cat", "a lion", "a Siberian tiger"] texts = [f'This is a photo of {label}.' for label in candidate_labels] inputs = processor(text=texts, images=image, padding="max_length", return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model(**inputs) logits_per_image = outputs.logits_per_image probs = torch.sigmoid(logits_per_image) print(f"{probs[0][0]:.1%} that image 0 is '{candidate_labels[0]}'")这段代码里有四个关键点,与源码行为一一对应:
padding="max_length"必须显式传入。因为 SigLIP 文本编码器在训练时就采用最长长度 padding,单独使用 [SiglipTokenizer] 或 [SiglipProcessor] 时如果不加该参数,相似度结果会偏离训练分布;- prompt 模板
"This is a photo of {label}."。要得到与Pipeline一致的结果,必须给 processor 传入这种带模板的句子,而不是裸标签; logits_per_image不是 logits 概率,需要再过一层torch.sigmoid。这正对应 sigmoid 损失的设计:每个图文对独立输出一个"匹配/不匹配"的 sigmoid 概率,形状为(image_batch_size, text_batch_size);attn_implementation="sdpa"可切换到更省显存的注意力实现(详见下文"显存与注意力实现"一节)。
源码级剖析:SiglipModel 双塔结构与 Sigmoid 损失
SigLIP 的模型实现位于 modeling_siglip.py。从源码结构看,整个模型由以下几个部分组成:
双塔编码 + 可学习的尺度/偏置
SiglipModel.__init__中,模型分别实例化文本塔与视觉塔:
self.text_model = SiglipTextModel._from_config(text_config) self.vision_model = SiglipVisionModel._from_config(vision_config) self.logit_scale = nn.Parameter(torch.randn(1)) self.logit_bias = nn.Parameter(torch.randn(1))logit_scale和logit_bias是两个可学习的标量参数,分别对应温度系数与偏置,用于调节 sigmoid 匹配的决策边界。
前向流程:余弦相似度 × 温度
SiglipModel.forward的推理逻辑是:
image_embeds = vision_outputs.pooler_output text_embeds = text_outputs.pooler_output # normalized features image_embeds = image_embeds / image_embeds.norm(p=2, dim=-1, keepdim=True) text_embeds = text_embeds / text_embeds.norm(p=2, dim=-1, keepdim=True) # cosine similarity as logits logits_per_text = torch.matmul(text_embeds, image_embeds.t().to(text_embeds.device)) logit_scale, logit_bias = self.logit_scale.to(text_embeds.device), self.logit_bias.to(text_embeds.device) logits_per_text = logits_per_text * logit_scale.exp() + logit_bias logits_per_image = logits_per_text.t()即:两塔池化输出先做 L2 归一化,再点积得到余弦相似度矩阵,乘以exp(logit_scale)(温度取反方向)并加上logit_bias,最后转置得到logits_per_image。返回的SiglipOutput同时包含loss、logits_per_image、logits_per_text、text_embeds、image_embeds以及两个子塔的完整输出,方便直接取特征做检索。
Sigmoid 损失只在训练时使用
forward中通过return_loss参数触发训练损失(代码注释标明该实现改编自 Google big_vision 中的 SigLIP 官方实现):
if return_loss: # Adapted from .../big_vision/.../siglip.py#L287 eye = torch.eye(logits_per_text.size(0), device=logits_per_text.device) m1_diag1 = -torch.ones_like(logits_per_text) + 2 * eye loglik = torch.nn.functional.logsigmoid(m1_diag1 * logits_per_text) nll = -torch.sum(loglik, dim=-1) loss = nll.mean()这个公式把每个 batch 内样本拆成独立的二分类问题:对角线位置(正确图文对)标签为 1,其余位置标签为 0,对每行分别取logsigmoid再求和取负。它不依赖 batch 内其他样本的相似度值作为"竞争项"——这正是"成对 sigmoid 损失"消除全局视角依赖的数学体现。
文本塔:双向注意力 + 末位 token 池化
SiglipTextModel.forward有两个区别于因果语言模型的特征:
- 注释明确写道 "SigLIP's text model does not use a causal mask",实际调用的是
create_bidirectional_mask,即文本编码器是双向Transformer; - 池化策略是
pooled_output = last_hidden_state[:, -1, :]——取最后一个 token的隐藏状态(训练时因padding="max_length"恒为 EOS 位置),再经过线性投影头self.head映射到projection_size。
这也是为什么推理时必须padding="max_length":它保证"最后一个 token"始终是同一个语义位置(EOS),而非不同长度文本各自不同的最后一个实义 token。
视觉塔:Patch 卷积嵌入 + 多头注意力池化头
- 输入经
SiglipVisionEmbeddings用Conv2d(kernel_size=patch_size, stride=patch_size)切成 patch 序列,加上可学习的 2D 位置嵌入;还支持interpolate_pos_encoding(双三次插值位置编码),用于推理时输入更高分辨率图像; - 编码器结束后经过一次
post_layernorm,再由SiglipMultiheadAttentionPoolingHead完成池化:一个可学习的probe查询向量对 patch token 序列做 MultiheadAttention,输出即整图表示。这与 CLIP 的 mean pooling 不同,是 SigLIP 视觉侧的标志性设计。
配置体系:三个 Config 类与默认值
配置定义在 configuration_siglip.py。SiglipConfig由两个子配置组成,缺失时自动用默认值初始化:
| 配置类 | 关键默认值 |
|---|---|
SiglipTextConfig | vocab_size=32000、hidden_size=768、intermediate_size=3072、num_hidden_layers=12、num_attention_heads=12、max_position_embeddings=64、pad_token_id=1、bos_token_id=49406、eos_token_id=49407 |
SiglipVisionConfig | hidden_size=768、num_hidden_layers=12、num_attention_heads=12、image_size=224、patch_size=16、num_channels=3 |
SiglipConfig | text_config/vision_config双子配置 +initializer_factor=1.0 |
两个值得注意的实现细节:
- 文本侧的
pad_token_id默认是1(对应 SentencePiece 词表中的<unk>),注释中说明这与CLIPTokenizer的默认值不同——因为 SigLIP 的 EOS 同时也是 padding token(</s>),见下文 tokenizer 部分; SiglipTextConfig.projection_size若未显式设置会自动回退为hidden_size。
也可以显式组合两个子配置来构造:
from transformers import SiglipConfig, SiglipTextConfig, SiglipVisionConfig config_text = SiglipTextConfig() config_vision = SiglipVisionConfig() config = SiglipConfig(text_config=config_text, vision_config=config_vision)分词器与处理器:为什么 EOS 就是 PAD
SiglipTokenizer基于 SentencePiece,model_max_length默认为64,与文本塔max_position_embeddings=64对齐。其特殊 token 约定是:
eos_token="</s>", unk_token="<unk>", pad_token="</s>", # PAD 与 EOS 使用同一个 token从build_inputs_with_special_tokens可以看到输入序列只会在末尾追加</s>(_add_eos_if_not_present防止重复添加),batching 时短序列再用同一个</s>补齐到max_length——训练与推理位置分布因此保持一致。
另外,tokenizer 内置了canonicalize_text方法(改编自 Google big_vision 的 prompt 工程工具):小写化、去标点、压缩空白,用于评估时对文本做规范化,提升检索/分类一致性。
图像侧由SiglipImageProcessor负责,默认参数为:
| 参数 | 默认值 | 说明 |
|---|---|---|
size | {"height": 224, "width": 224} | 默认输入分辨率(与视觉配置一致) |
resample | PILImageResampling.BICUBIC | 双三次重采样 |
image_mean/image_std | IMAGENET_STANDARD_MEAN/IMAGENET_STANDARD_STD | ImageNet 标准归一化 |
do_resize/do_rescale/do_normalize/do_convert_rgb | 均为True | 完整预处理链 |
SiglipProcessor是这两个组件的组合入口,即上文示例中AutoProcessor.from_pretrained实际加载的类。仓库内还存在纯 PIL 后端版本SiglipImageProcessorPil(见 image_processing_pil_siglip.py),供不依赖 torchvision 的场景使用。
显存优化:SDPA、FlashAttention-2 与 bitsandbytes 4bit
切换注意力实现
官方文档提示,可将attn_implementation切换为"sdpa"或"flash_attention_2"以使用更省显存的注意力。从SiglipPreTrainedModel的类属性看,实现同时声明了:
_supports_flash_attn = True _supports_sdpa = True _supports_flex_attn = True _supports_attention_backend = True即 eager / sdpa / flash_attention_2 / flex_attention 均受支持。使用 FlashAttention-2 时需先安装对应版本:
# pip install -U flash-attn --no-build-isolation from transformers import SiglipModel model = SiglipModel.from_pretrained( "google/siglip-so400m-patch14-384", attn_implementation="flash_attention_2", device_map="auto", )注意 attention 实现会向下分发到text_model与vision_model两个子配置上(测试用例 test_sdpa_can_dispatch_composite_models 专门验证了这一行为)。
4bit 量化:只量化权重
量化通过以更低精度表示权重来降低显存开销,可用后端总览见 Quantization 概览。下面示例用 bitsandbytes 只把权重量化到 int4:
import requests import torch from PIL import Image from transformers import AutoModel, AutoProcessor, BitsAndBytesConfig bnb_config = BitsAndBytesConfig(load_in_4bit=True) model = AutoModel.from_pretrained("google/siglip-base-patch16-224", quantization_config=bnb_config, device_map="auto", attn_implementation="sdpa") processor = AutoProcessor.from_pretrained("google/siglip-base-patch16-224") url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/pipeline-cat-chonk.jpeg" image = Image.open(requests.get(url, stream=True).raw) candidate_labels = ["a Pallas cat", "a lion", "a Siberian tiger"] texts = [f'This is a photo of {label}.' for label in candidate_labels] inputs = processor(text=texts, images=image, padding="max_length", return_tensors="pt").to(model.device) with torch.no_grad(): outputs = model(**inputs) logits_per_image = outputs.logits_per_image probs = torch.sigmoid(logits_per_image) print(f"{probs[0][0]:.1%} that image 0 is '{candidate_labels[0]}'")除了模型加载行的quantization_config=bnb_config,其余流程与非量化版本完全相同——量化是"透明"的后端替换。
训练支持与注意事项
官方文档的 Notes 部分给出了几条重要使用约束,这里逐条结合实现说明:
- 训练支持 DDP 与 FSDP(单节点多 GPU),但不使用
torch.distributed工具链,batch size 的可扩展性可能受限; - 单独使用
SiglipTokenizer/SiglipProcessor时必须传padding="max_length",因为模型就是这样训练的(源码中取最后一个 token 池化的设计依赖这一点); - 要与
Pipeline结果对齐,需传入 prompt 模板"This is a photo of {label}."; attn_implementation可在"sdpa"与"flash_attention_2"之间切换以降低注意力显存占用。
此外,SigLIP 还支持仅用视觉塔做图像分类的封装SiglipForImageClassification:它在SiglipVisionModel之上直接挂一个线性分类头(hidden_size -> num_labels),num_labels=0时退化为nn.Identity,适合 ImageNet 风格的单标签分类任务。
测试与进一步阅读
SigLIP 的完整测试套件位于 tests/models/siglip/:
- test_modeling_siglip.py:模型通用测试(含 SDPA 分发、前向
torch.sigmoid(logits_per_image)概率计算路径等); - test_tokenization_siglip.py:分词行为验证;
- test_image_processing_siglip.py:图像预处理验证。
相关源码入口汇总:
| 组件 | 文件 |
|---|---|
模型实现(SiglipModel/ 文本塔 / 视觉塔 / sigmoid 损失) | modeling_siglip.py |
配置(SiglipConfig三件套) | configuration_siglip.py |
| 分词器(SentencePiece,EOS 即 PAD) | tokenization_siglip.py |
| 图像处理器(torchvision 后端 / PIL 后端) | image_processing_siglip.py / image_processing_pil_siglip.py |
| 处理器入口 | processing_siglip.py |
| 权重转换脚本 | convert_siglip_to_hf.py |
总结:在 Transformers 中使用 SigLIP,核心是记住"sigmoid 概率而非 softmax 相似度"、"padding 必须拉满到 max_length"、"零样本分类要用完整 prompt 模板"这三条约定,其余的量化、注意力后端、子塔单独使用都建立在标准的AutoModel/AutoProcessor接口之上。
【免费下载链接】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),仅供参考