深入理解 Transformers 中的 CLIPSeg:基于冻结 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
导读
CLIPSeg(CLIP Segmentation)是 Transformer 库中一个以“文本 / 图像提示驱动图像分割”为核心能力的模型家族。本文以 clipseg 模型文档 为骨架,结合 配置源码、建模源码、处理源码 与集成测试,系统讲解:为什么 CLIPSeg 能在不重新训练分割模型的情况下理解"任意提示";它的文本、视觉、解码器三大子模块如何组织;每个配置项的含义与默认值;以及如何仅凭数行代码完成一次零样本(zero-shot)或一次性(one-shot)分割推理。读完本文,你将能独立加载CIDAS/clipseg-rd64-refined这类检查点,并把它接入基于图像生成式文本查询的实际业务。
模型概览:一份提示、三类分割任务
CLIPSeg 由 Timo Lüddecke 与 Alexander Ecker 提出,论文题目为Image Segmentation Using Text and Image Prompts,于 2021 年 12 月 18 日在 Hugging Face Papers 发布,并在此后(2022 年 11 月 8 日)正式合入 Transformers 代码库。它的核心思想非常直接:在冻结的 CLIP 骨干之上添加一个极小的 Transformer 解码器,从而实现零样本与一次性图像分割。
传统图像分割通常针对固定的类别集合训练,想要加入新类别或更复杂的查询,就得在包含这些表达的数据集上重新训练。CLIPSeg 的方案是"一次训练、随时查询":推理阶段允许任意提示(prompt),提示可以是一段文本,也可以是一张图片。由此,同一个统一模型可覆盖三种常见分割任务:
- 指代表达分割(Referring Expression Segmentation):用自然语言指代图中某个对象并分割出来;
- 零样本分割(Zero-shot Segmentation):分割训练时从未见过的类别;
- 一次性分割(One-shot Segmentation):用一张查询图片作为示例,分割出同类对象。
该混合输入设计还支持"动态适应"任何能用文本或图片表达的二值分割任务,包括关于物体 affordance(可操作性)或属性(properties)的泛化查询。模型在扩展版 PhraseCut 数据集上训练后,能为图像基于自由文本提示或表达查询的附加图像生成二值分割图。
事实来源:上述信息直接出自 clipseg.md 中论文摘要的转述,仓库未对其训练数据集规模做更多声明,故本文不补充任何额外性能数字。
该模型由 nielsr 贡献到本仓库,源码级入口与实现集中在src/transformers/models/clipseg/目录下。
架构解读:冻结的 CLIP 骨干 + 轻量 Transformer 解码器
从源码结构看,src/transformers/models/clipseg/目录将 CLIPSeg 拆成了清晰的三层结构:
| 模块 | 文件 | 职责 |
|---|---|---|
| 配置类 | configuration_clipseg.py | 定义文本、视觉、整体三类 Config |
| 模型类 | modeling_clipseg.py | 双塔编码器、解码器与前向逻辑 |
| 处理器 | processing_clipseg.py | 将文本与图像统一打包成模型输入 |
| 转换脚本 | convert_clipseg_original_pytorch_to_hf.py | 官方原始权重转换工具 |
模型相关的模块化源文件为 modular_clipseg.py,该文件会自动生成上面的建模与配置文件(文件头注释明确提示不要手动编辑生成产物)。
双塔基础模型 CLIPSegModel
CLIPSegModel与标准CLIPModel结构一致,构成方式是文本塔 + 视觉塔 + 投影层。在 modeling_clipseg.py 的构造函数中可以看到:
self.text_model = CLIPSegTextModel(...):文本编码器,输出序列级 hidden states;self.vision_model = CLIPSegVisionModel(...):视觉编码器,ViT 风格 patch embedding + Transformer 编码器;self.visual_projection与self.text_projection:将两塔输出投影到共同的projection_dim;self.logit_scale:可学习的对比学习缩放参数,初始值取logit_scale_init_value(默认 2.6592)。
CLIPSegModel对外暴露三个主要方法:
forward:计算图像与文本的相似度 logits,可用于训练对比学习损失;get_text_features:返回文本侧特征(供条件 embedding 使用);get_image_features:返回图像侧特征(视觉塔前向的正式入口)。
当作为分割模型使用时,CLIP 双塔几乎全程处于torch.no_grad()冻结状态,只有新增的轻量解码器参与梯度计算,这是 CLIPSeg "最小解码器 + 冻结骨干"设计的直接体现。
文本子模型 CLIPSegTextModel
CLIPSegTextModel以input_ids为唯一必选输入,内部流程为:token embedding → causal mask 的 Transformer encoder → final LayerNorm → 取EOS token位置向量作为pooler_output。实现细节(见 modeling_clipseg.py)中有一处值得注意的历史处理:若配置中eos_token_id == 2(PR #24773 之前的旧行为),会退回到"取序列最大 token id"位置的老逻辑;否则按"第一个等于eos_token_id的位置"取值,以兼容 tokenizer 新增 token 的场景。
默认文本配置(CLIPSegTextConfig)对应 CLIP 风格:vocab_size=49408、hidden_size=512、intermediate_size=2048、num_hidden_layers=12、num_attention_heads=8、max_position_embeddings=77、激活函数为quick_gelu、pad_token_id=1、bos_token_id=49406、eos_token_id=49407。
视觉子模型 CLIPSegVisionModel
CLIPSegVisionModel以pixel_values为输入,流程为:patch embedding(含 class token)→ pre LayerNorm → Transformer encoder → 取 CLS token 做 post LayerNorm 得到pooler_output。默认视觉配置(CLIPSegVisionConfig):hidden_size=768、image_size=224、patch_size=32、num_hidden_layers=12、num_attention_heads=12,这也是 CLIP ViT-B/32 的规格。
视觉塔前向支持interpolate_pos_encoding参数(默认True),允许把预训练的位置编码插值到更高分辨率输入上使用——集成测试 test_inference_interpolate_pos_encoding 验证了 180×180 分辨率下该能力:关闭插值会因 patch 数量不匹配而抛出ValueError,开启后则正常输出(1, 26, 768)的 hidden states。
分割头 CLIPSegDecoder 与三步前向
真正把 CLIPSeg 与纯 CLIP 区分开的是CLIPSegDecoder与CLIPSegForImageSegmentation。CLIPSegForImageSegmentation的前向(见 modeling_clipseg.py)可归纳为严格的三步流水线:
- 冻结视觉塔前向查询图像:以
torch.no_grad()调用clip.get_image_features,同时要求output_hidden_states=True,按extract_layers(默认第 3、6、9 层)取出对应激活值,并记录pooler_output; - 计算条件 embedding:优先使用调用方直接传入的
conditional_embeddings;否则根据input_ids(文本提示)或conditional_pixel_values(图像提示)调用get_conditional_embeddings(见 modeling_clipseg.py)在no_grad下生成。代码里校验了提示数量必须等于查询图像 batch 数,且自定义 embedding 的维度必须等于projection_dim; - 解码器产出 logits:把多层视觉激活与条件 embedding 一起送入
CLIPSegDecoder,得到逐像素二值分割 logits。
条件 embedding 的三种来源(文档 Usage tips 明确列出)正好对应三种输入形态:
| 提示形式 | 传入字段 | 说明 |
|---|---|---|
| 文本提示 | input_ids | 文本经冻结文本塔编码为条件向量 |
| 图像提示 | conditional_pixel_values | 示例图片经冻结视觉塔编码为条件向量,用于一次性分割 |
| 自定义条件 | conditional_embeddings | 调用方预先算好的任意条件向量,可跳过模型内部计算 |
解码器内部:多尺度激活 + FiLM 调制 + 转置卷积上采样
CLIPSeg 之所以能输出精细的分割图,得益于解码器对"多尺度中间层激活"的利用。CLIPSegDecoder 的实现要点:
- 把
extract_layers指定的多层激活逆序处理(hidden_states[::-1]),每层先用一个线性层reduces[i]把hidden_size压缩到reduce_dim(默认 64),并逐层累加形成由粗到细的特征; - 在
conditional_layer(默认 0,含义是"若为 0 则使用最后一层")指定的那一层,通过FiLM(Feature-wise Linear Modulation)把条件向量注入特征:film_mul生成逐通道缩放、film_add生成逐通道偏移,两者与激活相乘相加,实现"条件调制主干特征"的效果; - 每步累加结果送入一个
CLIPSegDecoderLayer。该层与编码层唯一的区别是归一化放在残差之后(post-layer norm,见 modeling_clipseg.py),这是解码器在结构上区别于编码器的核心; - 最后移除 CLS token、把序列重排成
(batch, reduce_dim, h, w)的空间网格,再用转置卷积上采样回图像分辨率输出单通道 logits。
转置卷积有两档实现,由配置开关use_complex_transposed_convolution控制(默认False):简单版是一个reduce_dim → 1、kernel/stride 等于patch_size的单层ConvTranspose2d;复杂版则叠加了 3×3 卷积与两层 ReLU + 转置卷积的级联,kernel 尺寸取patch_size // 4,以支持更细粒度的分割上采样。当labels(二值掩码,shape(batch, height, width))存在时,模型用BCEWithLogitsLoss计算分割损失——因此同一模型既能推理也能在自有数据上做二值分割微调。
配置体系:从单 Config 到三 Config
CLIPSeg 遵循"复合 Config"设计:整体CLIPSegConfig内部持有text_config与vision_config两个子配置(源码见 configuration_clipseg.py)。构造时可以只给CLIPSegConfig()(两个子配置自动取默认值并打印日志),也可以显式传入两个子 Config 实例:
from transformers import CLIPSegConfig, CLIPSegTextConfig, CLIPSegVisionConfig config_text = CLIPSegTextConfig() config_vision = CLIPSegVisionConfig() config = CLIPSegConfig(text_config=config_text, vision_config=config_vision)向后兼容层面,__post_init__还会处理历史参数text_config_dict/vision_config_dict:若某个键同时出现在旧字典与新 Config 中且值不同,以旧字典为准并打印日志提示。
CLIPSegConfig独有的、与分割任务强相关的参数如下(configuration_clipseg.py):
| 参数 | 默认值 | 作用 |
|---|---|---|
extract_layers | [3, 6, 9] | 从冻结视觉骨干提取中间激活的层号列表,解码器据此获得多尺度信息 |
reduce_dim | 64 | 每层视觉 embedding 压缩后的维度 |
decoder_num_attention_heads | 4 | 解码器各层自注意力的头数 |
decoder_attention_dropout | 0.0 | 解码器注意力 dropout |
decoder_hidden_act | quick_gelu | 解码器 MLP 的激活函数配置(解码层内部层归一化配置还会将 MLP 激活硬编码为relu) |
decoder_intermediate_size | 2048 | 解码器 MLP 中间维度 |
conditional_layer | 0 | 应用 FiLM 调制的编码层序号,0 表示最后一层 |
use_complex_transposed_convolution | False | 是否使用级联的多级转置卷积做更细粒度上采样 |
projection_dim | 512 | 文本/视觉投影到的公共对比学习维度 |
logit_scale_init_value | 2.6592 | 对比学习 logit 缩放的初始化值 |
在 CLIPSegDecoder 构造中,解码器会深拷贝vision_config,再覆写hidden_size=reduce_dim、num_attention_heads=decoder_num_attention_heads、intermediate_size=decoder_intermediate_size,因此extract_layers、reduce_dim与视觉塔patch_size的数值会直接影响最终 logits 的空间分辨率与感受野。
Processor:把文本与图像统一打包
CLIPSegProcessor(见 processing_clipseg.py)同时持有文本 tokenizer 与图像 image processor。__call__方法的组合逻辑覆盖了推理的全部四种场景:
- text + images:文本提示分割查询图 → 输出
input_ids、attention_mask、pixel_values; - visual_prompt + images:一次性(one-shot)提示分割 → 输出
pixel_values与conditional_pixel_values; - 仅传 text 或 visual_prompt 时返回对应单项编码;
- 三者全空或 text 与 visual_prompt 同时给出都会抛出
ValueError,强制"文本提示与图像提示二选一"。
由于同一批图片会被图像处理器执行相同的尺寸归一化,文本提示与图像提示可以自由切换而无需改动下游模型代码。
开箱即用的推理示例
官方文档给出的端到端零样本分割示例如下(可在transformers安装环境中直接运行):
import torch from transformers import AutoProcessor, CLIPSegForImageSegmentation from transformers.image_utils import load_image processor = AutoProcessor.from_pretrained("CIDAS/clipseg-rd64-refined") model = CLIPSegForImageSegmentation.from_pretrained("CIDAS/clipseg-rd64-refined") url = "http://images.cocodataset.org/val2017/000000039769.jpg" image = load_image(url) texts = ["a cat", "a remote", "a blanket"] inputs = processor(text=texts, images=[image] * len(texts), padding=True, return_tensors="pt") with torch.inference_mode(): outputs = model(**inputs) logits = outputs.logits print(logits.shape) # torch.Size([3, 352, 352])几个关键点:
- 官方与社区最常用的检查点是
CIDAS/clipseg-rd64-refined("rd64"即reduce_dim=64架构的命名来源),CLIPSegForImageSegmentation通过AutoProcessor+ 类名直接加载,无需手工拼装 tokenizer/image processor; - 同一条
image被重复len(texts)次后与各文本一一配对,模型为每个(图, 文本)对输出一张352×352的 logits 掩码(本尺寸来自检查点内置的图像预处理与解码器上采样配置,而非模型 docstring 中数值的臆测——集成测试 test_inference_image_segmentation 用同一检查点精确断言了(3, 352, 352)的输出形状与 logits 取值); - 把 logits 过 Sigmoid 即得到 0~1 的软掩码,可继续做阈值化、叠加到原图、计算目标裁剪框等下游处理;
- 若把
processor(text=..., images=...)换成processor(visual_prompt=示例图, images=查询图, ...),同一套模型即可切换到一次性分割模式。
如果想先看清CLIPSegForImageSegmentation输出对象的完整字段,其返回类型CLIPSegImageSegmentationOutput(modeling_clipseg.py)包含:loss(可选,传入 labels 时返回 BCE 损失)、logits、conditional_embeddings、pooled_output、vision_model_output与decoder_output。纯文本与纯视觉子模型也可独立加载——例如 CLIPSegTextModel 的 docstring 示例演示了如何仅用 tokenizer 提取pooler_output。
支持的注意力实现与质量保障
CLIPSegPreTrainedModel(modeling_clipseg.py)继承自PreTrainedModel并声明了多项现代能力:支持 gradient checkpointing、SDPA(_supports_sdpa = True)、Flash Attention 与 Flex Attention(_supports_flash_attn/_supports_flex_attn),以及基于ALL_ATTENTION_FUNCTIONS的注意力后端选择;_no_split_modules保证了其在设备并行、TP 等场景下可以正确切分。模型自带的自定义初始化(_init_weights)遵循 OpenAI CLIP 风格的缩放规则。
模型的回归质量由 tests/models/clipseg/test_modeling_clipseg.py 覆盖,包括三套 Tester(CLIPSegVisionModelTester、CLIPSegTextModelTester、CLIPSegModelTester)对应文本/视觉/完整模型,以及继承自ModelTesterMixin/PipelineTesterMixin的通用能力测试(forward signature、gradient checkpointing、hidden states、SDPA 编译、feature-extractionpipeline 映射等)。两个@slow集成测试直接以真实权重做端到端数值对齐,可作为接入新输入前自行校验的参考基线。CLIPSegModel同时也被注册为feature-extractionpipeline 的支持模型,而图像分割任务通常更适合直接调用CLIPSegForImageSegmentation类以获得掩码输出。
总结
CLIPSeg 在 Transformers 中的落地形态可概括为一句话:冻结的 CLIP 双塔负责把任意"文本/图片提示"翻译成条件向量,一个后置归一化的轻量 Transformer 解码器用 FiLM 把这些条件调制进多尺度视觉特征,最终由转置卷积上采样为二值分割图。整个实现被井井有条地拆分为配置(CLIPSegConfig及双子配置)、模型(CLIPSegModel/CLIPSegForImageSegmentation)与处理(CLIPSegProcessor)三部分,无论做零样本文本分割、一次性示例分割,还是自定义条件向量的特殊分割任务,都可以用同一套 API 完成,且无需任何针对新类别的重新训练。深入阅读时建议按 模型文档 → 配置源码 → 建模源码 → 集成测试 的顺序,从"对外用法"逐步下沉到"数值如何产生",能获得对该模型最完整的理解。
【免费下载链接】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),仅供参考