首页
/ 深入理解 Transformers 中的 CLIPSeg:基于冻结 CLIP 的零样本与一次性图像分割模型

深入理解 Transformers 中的 CLIPSeg:基于冻结 CLIP 的零样本与一次性图像分割模型

2026-09-06 19:04:51作者:凤尚柏Louis

导读

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_projectionself.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

CLIPSegTextModelinput_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=49408hidden_size=512intermediate_size=2048num_hidden_layers=12num_attention_heads=8max_position_embeddings=77、激活函数为 quick_gelupad_token_id=1bos_token_id=49406eos_token_id=49407

视觉子模型 CLIPSegVisionModel

CLIPSegVisionModelpixel_values 为输入,流程为:patch embedding(含 class token)→ pre LayerNorm → Transformer encoder → 取 CLS token 做 post LayerNorm 得到 pooler_output。默认视觉配置(CLIPSegVisionConfig):hidden_size=768image_size=224patch_size=32num_hidden_layers=12num_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 区分开的是 CLIPSegDecoderCLIPSegForImageSegmentationCLIPSegForImageSegmentation 的前向(见 modeling_clipseg.py)可归纳为严格的三步流水线:

  1. 冻结视觉塔前向查询图像:以 torch.no_grad() 调用 clip.get_image_features,同时要求 output_hidden_states=True,按 extract_layers(默认第 3、6、9 层)取出对应激活值,并记录 pooler_output
  2. 计算条件 embedding:优先使用调用方直接传入的 conditional_embeddings;否则根据 input_ids(文本提示)或 conditional_pixel_values(图像提示)调用 get_conditional_embeddings(见 modeling_clipseg.py)在 no_grad 下生成。代码里校验了提示数量必须等于查询图像 batch 数,且自定义 embedding 的维度必须等于 projection_dim
  3. 解码器产出 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_configvision_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_dimnum_attention_heads=decoder_num_attention_headsintermediate_size=decoder_intermediate_size,因此 extract_layersreduce_dim 与视觉塔 patch_size 的数值会直接影响最终 logits 的空间分辨率与感受野。

Processor:把文本与图像统一打包

CLIPSegProcessor(见 processing_clipseg.py)同时持有文本 tokenizer 与图像 image processor。__call__ 方法的组合逻辑覆盖了推理的全部四种场景:

  • text + images:文本提示分割查询图 → 输出 input_idsattention_maskpixel_values
  • visual_prompt + images:一次性(one-shot)提示分割 → 输出 pixel_valuesconditional_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])

几个关键点:

  1. 官方与社区最常用的检查点是 CIDAS/clipseg-rd64-refined("rd64"即 reduce_dim=64 架构的命名来源),CLIPSegForImageSegmentation 通过 AutoProcessor + 类名直接加载,无需手工拼装 tokenizer/image processor;
  2. 同一条 image 被重复 len(texts) 次后与各文本一一配对,模型为每个(图, 文本)对输出一张 352×352 的 logits 掩码(本尺寸来自检查点内置的图像预处理与解码器上采样配置,而非模型 docstring 中数值的臆测——集成测试 test_inference_image_segmentation 用同一检查点精确断言了 (3, 352, 352) 的输出形状与 logits 取值);
  3. 把 logits 过 Sigmoid 即得到 0~1 的软掩码,可继续做阈值化、叠加到原图、计算目标裁剪框等下游处理;
  4. 若把 processor(text=..., images=...) 换成 processor(visual_prompt=示例图, images=查询图, ...),同一套模型即可切换到一次性分割模式。

如果想先看清 CLIPSegForImageSegmentation 输出对象的完整字段,其返回类型 CLIPSegImageSegmentationOutputmodeling_clipseg.py)包含:loss(可选,传入 labels 时返回 BCE 损失)、logitsconditional_embeddingspooled_outputvision_model_outputdecoder_output。纯文本与纯视觉子模型也可独立加载——例如 CLIPSegTextModel 的 docstring 示例演示了如何仅用 tokenizer 提取 pooler_output

支持的注意力实现与质量保障

CLIPSegPreTrainedModelmodeling_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(CLIPSegVisionModelTesterCLIPSegTextModelTesterCLIPSegModelTester)对应文本/视觉/完整模型,以及继承自 ModelTesterMixin/PipelineTesterMixin 的通用能力测试(forward signature、gradient checkpointing、hidden states、SDPA 编译、feature-extraction pipeline 映射等)。两个 @slow 集成测试直接以真实权重做端到端数值对齐,可作为接入新输入前自行校验的参考基线。CLIPSegModel 同时也被注册为 feature-extraction pipeline 的支持模型,而图像分割任务通常更适合直接调用 CLIPSegForImageSegmentation 类以获得掩码输出。

总结

CLIPSeg 在 Transformers 中的落地形态可概括为一句话:冻结的 CLIP 双塔负责把任意"文本/图片提示"翻译成条件向量,一个后置归一化的轻量 Transformer 解码器用 FiLM 把这些条件调制进多尺度视觉特征,最终由转置卷积上采样为二值分割图。整个实现被井井有条地拆分为配置(CLIPSegConfig 及双子配置)、模型(CLIPSegModel/CLIPSegForImageSegmentation)与处理(CLIPSegProcessor)三部分,无论做零样本文本分割、一次性示例分割,还是自定义条件向量的特殊分割任务,都可以用同一套 API 完成,且无需任何针对新类别的重新训练。深入阅读时建议按 模型文档配置源码建模源码集成测试 的顺序,从"对外用法"逐步下沉到"数值如何产生",能获得对该模型最完整的理解。

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

项目优选

收起
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