Hugging Face Transformers 中的 CLIP 模型:从零实现图像-文本跨模态相似度与零样本分类
2026/9/11 13:35:02 网站建设 项目流程

Hugging Face Transformers 中的 CLIP 模型:从零实现图像-文本跨模态相似度与零样本分类

【免费下载链接】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

CLIP(Contrastive Language-Image Pre-Training)是 OpenAI 提出的多模态视觉-语言模型,通过在海量(图像、文本)配对数据上联合训练,将视觉与文本特征投影到同一向量空间,从而无需针对任务微调即可实现图像-文本相似度计算与零样本图像分类。本文以当前仓库docs/source/ja/model_doc/clip.md为骨架,结合src/transformers/models/clip/下的完整源码实现,系统讲解 CLIP 的架构原理、配置体系、Tokenizer/ImageProcessor/Processor 配套组件,并给出可直接运行的实战示例与微调指引。

CLIP 是什么:从固定类别监督到自然语言监督

传统计算机视觉系统通常针对一组预先定义好的物体类别进行训练,这种受限的监督形式需要为每一个新的视觉概念额外标注数据,限制了模型的通用性与易用性。CLIP 的核心思路是:直接从关于图像的原始文本中学习,从而获得更广泛的监督来源。

论文(Learning Transferable Visual Models From Natural Language Supervision,发布于 2021-02-26)证明,仅通过"预测哪段描述对应哪张图"这一简单的预训练任务,就能从互联网收集的数以亿计的(图像、文本)配对数据中高效、可扩展地学习 SOTA 级图像表征。预训练完成后,可以使用自然语言去引用(或描述)学到的视觉概念,实现模型到下游任务的零样本迁移。作者在 30 多个既有计算机视觉数据集上进行了基准测试,覆盖 OCR、视频动作识别、地理位置定位以及多种细粒度物体分类等任务,发现模型大多能轻松迁移,且在很多任务上无需数据集专属训练即可与完全监督的基线竞争——例如在 ImageNet 零样本上达到与原始 ResNet-50 相当的准确率,却无需使用其 128 万条训练样本。

在 Transformers 仓库中,CLIP 的实现位于 src/transformers/models/clip/ 目录,包含配置(configuration_clip.py)、模型(modeling_clip.py)、图像处理器(image_processing_clip.pyimage_processing_pil_clip.py)、处理器(processing_clip.py)、分词器(tokenization_clip.py)以及原始权重转换脚本(convert_clip_original_pytorch_to_hf.py)。

架构原理:双编码器 + 共享投影空间

CLIP 是一个多模态视觉-语言模型,可以用于图像-文本相似度计算与零样本图像分类。其整体结构由两部分组成:

  • 图像编码器:使用 ViT 风格的 Transformer 获取视觉特征;
  • 文本编码器:使用因果语言模型(类似 GPT)获取文本特征。

两条分支的最终特征被投影到维度相同的潜在空间中,投影后的图像特征与文本特征之间的点积即作为两者的相似度得分。这一相似度计算逻辑在 modeling_clip.py 的CLIPModel.forward中实现:

# normalized features image_embeds = image_embeds / _get_vector_norm(image_embeds) text_embeds = text_embeds / _get_vector_norm(text_embeds) # cosine similarity as logits logits_per_text = torch.matmul(text_embeds, image_embeds.t().to(text_embeds.device)) logits_per_text = logits_per_text * self.logit_scale.exp().to(text_embeds.device) logits_per_image = logits_per_text.t()

即:先对图像、文本嵌入做 L2 归一化,再计算余弦相似度矩阵,最后乘上可学习的logit_scale的指数(logit_scale的初始值由配置项logit_scale_init_value控制,默认为 2.6592)。若开启return_loss=True,还会基于该相似度矩阵计算对称的对比损失(详见 contrastive_loss 与 image_text_contrastive_loss)。

图像分支的细节:Patch 化与 [CLS] 标记

为了把图像送入 Transformer 编码器,每张图像先被切分成一系列固定大小、互不重叠的 Patch,每个 Patch 被线性嵌入:

  • Patch 化与线性投影由CLIPEmbeddings.patch_embedding完成(Conv2d卷积实现),输出形状为[batch, seq_len, hidden_size]
  • 随后拼接一个可学习的[CLS]类标记(class_embedding),作为整张图的全局表示;
  • 再加上绝对位置编码position_embedding),把得到的向量序列送入标准 Transformer 编码器。

以上逻辑可在 modeling_clip.py 的CLIPEmbeddings.forward中看到。此外,该实现还提供了interpolate_pos_encoding能力(modeling_clip.py),当输入图像分辨率与预训练尺寸不同时,可以通过双三次插值(bicubic)调整位置编码,从而在更高分辨率图像上使用模型(调用时需传入interpolate_pos_encoding=True)。

文本分支与模型装配

CLIPModel.__init__(modeling_clip.py)将两分支装配在一起:

self.text_model = CLIPTextModel._from_config(text_config) self.vision_model = CLIPVisionModel._from_config(vision_config) self.visual_projection = nn.Linear(self.vision_embed_dim, self.projection_dim, bias=False) self.text_projection = nn.Linear(self.text_embed_dim, self.projection_dim, bias=False) self.logit_scale = nn.Parameter(torch.tensor(self.config.logit_scale_init_value))

其中两个nn.Linear投影层均不带 bias,将各自编码器的隐藏维度统一映射到projection_dim(默认 512)维的共享空间中。get_text_featuresget_image_features两个方法则分别返回经过投影后的文本/图像嵌入(modeling_clip.py),适合直接用于向量检索等场景。

快速上手:计算图像-文本相似度得分

使用 [CLIPProcessor] 包装 [CLIPImageProcessor] 与 [CLIPTokenizer] 于同一实例,可以同时完成文本编码与图像预处理。下面的例子展示了如何用 [CLIPProcessor] 与 [CLIPModel] 获取图像-文本相似度得分(与关联文档示例一致):

>>> from PIL import Image >>> import requests >>> from transformers import CLIPProcessor, CLIPModel >>> model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") >>> processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg" >>> image = Image.open(requests.get(url, stream=True).raw) >>> inputs = processor(text=["a photo of a cat", "a photo of a dog"], images=image, return_tensors="pt", padding=True) >>> outputs = model(**inputs) >>> logits_per_image = outputs.logits_per_image # this is the image-text similarity score >>> probs = logits_per_image.softmax(dim=1) # we can take the softmax to get the label probabilities

使用 Pipeline 与 AutoModel 的两种写法

除了显式实例化CLIPModel/CLIPProcessor,还可以借助更高层的封装。例如使用pipeline直接做零样本图像分类:

from transformers import pipeline clip = pipeline( task="zero-shot-image-classification", model="openai/clip-vit-base-patch32", device=0 ) labels = ["a photo of a cat", "a photo of a dog", "a photo of a car"] clip("http://images.cocodataset.org/val2017/000000039769.jpg", candidate_labels=labels)

或者使用AutoModel+AutoProcessor并配合 SDPA 注意力实现与自动设备映射:

import requests from PIL import Image from transformers import AutoModel, AutoProcessor model = AutoModel.from_pretrained("openai/clip-vit-base-patch32", attn_implementation="sdpa", device_map="auto") processor = AutoProcessor.from_pretrained("openai/clip-vit-base-patch32") url = "http://images.cocodataset.org/val2017/000000039769.jpg" image = Image.open(requests.get(url, stream=True).raw) labels = ["a photo of a cat", "a photo of a dog", "a photo of a car"] inputs = processor(text=labels, images=image, return_tensors="pt", padding=True).to(model.device) outputs = model(**inputs) logits_per_image = outputs.logits_per_image probs = logits_per_image.softmax(dim=1) most_likely_idx = probs.argmax(dim=1).item() most_likely_label = labels[most_likely_idx] print(f"Most likely label: {most_likely_label} with probability: {probs[0][most_likely_idx].item():.3f}")

CLIPModel.forward返回的 [CLIPOutput](定义于 modeling_clip.py)包含如下字段:

字段含义
loss对比损失(仅在return_loss=True时返回)
logits_per_image形状(image_batch_size, text_batch_size),图像-文本相似度得分
logits_per_text形状(text_batch_size, image_batch_size),文本-图像相似度得分
text_embeds经投影层得到的文本嵌入
image_embeds经投影层得到的图像嵌入
text_model_output文本编码器的完整输出
vision_model_output图像编码器的完整输出

配置体系:CLIPConfig、CLIPTextConfig 与 CLIPVisionConfig

CLIP 由三个配置类构成,均定义于 configuration_clip.py。

CLIPTextConfig:文本编码器配置

CLIPTextConfig用于初始化文本分支,默认参数对应openai/clip-vit-base-patch32风格:

参数默认值说明
vocab_size49408词表大小
hidden_size512隐藏层维度
intermediate_size2048FFN 中间层维度
projection_dim512投影到共享空间的维度
num_hidden_layers12Transformer 层数
num_attention_heads8注意力头数
max_position_embeddings77最大序列长度
hidden_act"quick_gelu"激活函数(CLIP 使用 QuickGELU)
layer_norm_eps1e-5LayerNorm epsilon
attention_dropout0.0注意力 dropout
initializer_range0.02权重初始化范围
pad_token_id/bos_token_id/eos_token_id1 / 49406 / 49407特殊 token id

该配置通过@strict装饰器启用架构校验:validate_architecture会检查hidden_size是否能被num_attention_heads整除,否则抛出ValueError(configuration_clip.py)。

CLIPVisionConfig:图像编码器配置

CLIPVisionConfig用于初始化图像分支:

参数默认值说明
hidden_size768隐藏层维度
intermediate_size3072FFN 中间层维度
projection_dim512投影到共享空间的维度
num_hidden_layers12Transformer 层数
num_attention_heads12注意力头数
num_channels3输入图像通道数(RGB)
image_size224输入图像尺寸
patch_size32Patch 尺寸
hidden_act"quick_gelu"激活函数
layer_norm_eps1e-5LayerNorm epsilon
attention_dropout0.0注意力 dropout
initializer_range0.02权重初始化范围

image_sizepatch_size在当前仓库实现中支持int | list | tuple类型,从而允许非方形的输入配置。

CLIPConfig:双分支总配置

CLIPConfig通过sub_configs = {"text_config": CLIPTextConfig, "vision_config": CLIPVisionConfig}聚合两个子配置,并额外提供:

  • text_config:用于初始化CLIPTextConfig的字典或实例;
  • vision_config:用于初始化CLIPVisionConfig的字典或实例;
  • projection_dim:投影维度(默认 512);
  • logit_scale_init_valuelogit_scale的初始值,默认 2.6592(与原始 CLIP 实现一致);
  • initializer_factor:初始化因子(默认 1.0)。

__post_init__还兼容了历史参数text_config_dict/vision_config_dict,当新旧参数同时出现且取值不一致时,以字典参数为准并打印提示日志(configuration_clip.py)。

从两个子配置构建总配置的方式如下:

>>> from transformers import CLIPTextConfig, CLIPVisionConfig, CLIPConfig >>> config_text = CLIPTextConfig() >>> config_vision = CLIPVisionConfig() >>> config = CLIPConfig(text_config=config_text, vision_config=config_vision)

预处理链路:CLIPTokenizer、CLIPImageProcessor 与 CLIPProcessor

CLIPTokenizer / CLIPTokenizerFast

CLIPTokenizer基于 HuggingFace tokenizers 库的byte-level BPE实现:

  • 特殊 token:bos_token="<|startoftext|>"eos_token="<|endoftext|>"pad_token="<|endoftext|>"unk_token="<|endoftext|>"
  • 归一化流程:NFCUnicode 规范化 → 将空白替换为单个空格 → 转小写(normalizers.Sequence([NFC, Replace(r"\s+", " "), Lowercase]));
  • 预分词采用基于正则的 Split 策略,词尾使用</w>后缀标记;
  • model_input_names = ["input_ids", "attention_mask"],可直接配合padding=True完成批内对齐。

由于默认max_position_embeddings=77,文本序列超过 77 个 token 时会被截断——这也是 CLIP 文本分支序列长度上限的来源(超出会触发 CLIPTextEmbeddings.forward 中的长度校验)。CLIPTokenizerFast则提供基于 Rust tokenizers 的快速版本,二者的get_special_tokens_masksave_vocabulary等方法在文档中均有 autodoc 条目。

CLIPImageProcessor / CLIPImageProcessorFast

CLIPImageProcessor负责图像的缩放(resize/rescale)与归一化,默认配置为:

  • resample:双三次插值(BICUBIC);
  • size{"shortest_edge": 224}(按最短边缩放到 224,保持宽高比);
  • crop_size{"height": 224, "width": 224}(中心裁剪到 224×224);
  • do_resize=Truedo_rescale=Truedo_normalize=True
  • image_mean/image_std:采用 OpenAI 官方 CLIP 的均值与标准差(OPENAI_CLIP_MEAN/OPENAI_CLIP_STD)。

同时支持use_square_size参数:传入后会把size改为{"height": 224, "width": 224}形式(见 image_processing_clip.py)。preprocess方法即按"resize → rescale → normalize → 返回 tensor"的链路处理输入。CLIPImageProcessorFast(以及仓库中新增的CLIPImageProcessorPil,见英文文档docs/source/en/model_doc/clip.md的 autodoc 条目)提供基于 torchvision 后端的加速实现。

CLIPProcessor

CLIPProcessor继承自ProcessorMixin,构造函数接收image_processortokenizer两个组件:

class CLIPProcessor(ProcessorMixin): def __init__(self, image_processor=None, tokenizer=None, **kwargs): super().__init__(image_processor, tokenizer)

调用processor(text=..., images=..., return_tensors="pt", padding=True)时,它会将文本分词结果与图像预处理结果打包为模型可直接消费的input_idsattention_maskpixel_values

模型类族一览

关联文档通过 autodoc 暴露了以下模型类(均实现在 modeling_clip.py):

  • CLIPModel:完整双塔模型,提供forward(返回CLIPOutput)、get_text_featuresget_image_features(modeling_clip.py);
  • CLIPTextModel:仅文本编码器(带池化),forward返回BaseModelOutputWithPooling
  • CLIPTextModelWithProjection:文本编码器 + 文本投影层,forward返回text_embeds(modeling_clip.py);
  • CLIPVisionModel:仅图像编码器(带池化);
  • CLIPVisionModelWithProjection:图像编码器 + 视觉投影层,forward返回image_embeds(modeling_clip.py)。

其中CLIPTextModelWithProjectionCLIPVisionModelWithProjection特别适合直接产出同一空间下的嵌入向量,用于图像检索、语义搜索等下游任务:

>>> import torch >>> from transformers import AutoTokenizer, CLIPTextModelWithProjection >>> model = CLIPTextModelWithProjection.from_pretrained("openai/clip-vit-base-patch32") >>> tokenizer = AutoTokenizer.from_pretrained("openai/clip-vit-base-patch32") >>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"], padding=True, return_tensors="pt") >>> with torch.inference_mode(): ... outputs = model(**inputs) >>> text_embeds = outputs.text_embeds

图像侧同理,使用CLIPVisionModelWithProjection+AutoProcessor即可得到outputs.image_embeds。在推理阶段建议包裹torch.inference_mode()以节省显存与加速。

微调与扩展资源

仓库内提供了完整的 CLIP 微调脚本:examples/pytorch/contrastive-image-text/run_clip.py,支持从预训练的图像/文本编码器继续训练,例如使用 COCO 数据集进行图文对比训练。脚本以 Hugging FaceTrainer为骨架,关键参数包括:

  • --model_name_or_path:预训练模型或配置路径(例如./clip-roberta,见 README.md);
  • --config_name/--tokenizer_name:当与模型名不一致时单独指定配置与分词器(run_clip.py);
  • --push_to_hub:训练结束后将模型推送到 Hub(run_clip.py)。

围绕 CLIP 的典型进阶用法还包括:

  • 图像检索:用预训练 CLIP 计算查询文本与候选图像库的相似度,并按得分排序,可进一步计算 MRR(平均倒数排名)评估检索质量;
  • 多语言图文对齐:使用多语言 CLIP 变体将不同语言的文本与图像映射到同一向量空间;
  • 可解释性分析:可视化输入 token 与图像区域(segment)之间的相似性,观察模型关注哪些图文对应关系;
  • 遥感(卫星)图文微调:在领域数据集(如 RSICD)上微调 CLIP 并对比不同数据增强策略对性能的影响。

小结

CLIP 在 Transformers 仓库中的实现完整覆盖了"配置 → 预处理 → 双塔编码 → 对比学习 → 下游迁移"的整条链路:CLIPConfig/CLIPTextConfig/CLIPVisionConfig管理双分支超参,CLIPTokenizerCLIPImageProcessor分别负责文本与图像预处理,CLIPProcessor将二者合一,而CLIPModel及四个派生模型类覆盖了相似度计算、特征提取与零样本分类的全部使用场景。阅读 modeling_clip.py 与 configuration_clip.py 的源码,可以进一步理解归一化点积、logit_scale缩放与对称对比损失的实现细节;配合 run_clip.py 训练脚本,即可在自有图文数据集上复现并扩展这一范式。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询