4.2 CLIP 双塔架构深度解析


文档摘要

4.2 CLIP 双塔架构深度解析 CLIP(Contrastive Language–Image Pre-training)是 OpenAI 在 2021 年发布的多模态对齐模型,它把第4.1节的对比学习方法论完整落地,成为多模态大模型时代的奠基之作。本节深度拆解 CLIP 的架构、训练流程与零样本能力。 一、CLIP 解决的核心问题 在 CLIP 之前,视觉-语言模型(如 ViLBERT、LXMERT)通常这样工作: 这种「早期融合」结构有一个工程瓶颈——图像与文本必须同时进入同一个编码器,无法分别预计算。

4.2 CLIP 双塔架构深度解析

CLIP(Contrastive Language–Image Pre-training)是 OpenAI 在 2021 年发布的多模态对齐模型,它把第4.1节的对比学习方法论完整落地,成为多模态大模型时代的奠基之作。本节深度拆解 CLIP 的架构、训练流程与零样本能力。

一、CLIP 解决的核心问题

在 CLIP 之前,视觉-语言模型(如 ViLBERT、LXMERT)通常这样工作:

[图像] ──┐ ├→ [融合编码器] → 输出 [文本] ──┘

这种「早期融合」结构有一个工程瓶颈——图像与文本必须同时进入同一个编码器,无法分别预计算。这导致:

  • 检索场景下,每来一个查询都要重算整个融合编码器
  • 大规模图文库无法预先编码建索引

CLIP 的革命性在于换成双塔结构

[图像] → [视觉塔] → 图像向量 I ↓ 余弦相似度(点积) ↑ [文本] → [文本塔] → 文本向量 T

两个塔完全独立,可以预先离线编码所有图像与文本,构建索引。检索时只需在线计算查询向量与索引向量的相似度——速度极快。

二、CLIP 的完整架构

下面这张 SVG 刻画了 CLIP 的双塔结构与对比训练流程:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 480" font-family="sans-serif" font-size="13"> <!-- 标题 --> <text x="450" y="25" text-anchor="middle" font-weight="bold" font-size="16">CLIP 双塔对比学习架构</text> <!-- 视觉塔 --> <g transform="translate(60,80)"> <rect x="0" y="0" width="180" height="220" fill="#e1f5ff" stroke="#333" rx="10"/> <text x="90" y="25" text-anchor="middle" font-weight="bold">视觉塔</text> <rect x="20" y="50" width="140" height="35" fill="#fff" stroke="#666" rx="4"/> <text x="90" y="72" text-anchor="middle">图像 224×224</text> <rect x="20" y="100" width="140" height="35" fill="#fff4e1" stroke="#666" rx="4"/> <text x="90" y="122" text-anchor="middle">ViT-L/14 或 ResNet</text> <rect x="20" y="150" width="140" height="35" fill="#fff" stroke="#666" rx="4"/> <text x="90" y="172" text-anchor="middle">L2 归一化</text> <text x="90" y="205" text-anchor="middle" font-weight="bold">图像向量 I ∈ R^d</text> </g> <!-- 文本塔 --> <g transform="translate(660,80)"> <rect x="0" y="0" width="180" height="220" fill="#fff4e1" stroke="#333" rx="10"/> <text x="90" y="25" text-anchor="middle" font-weight="bold">文本塔</text> <rect x="20" y="50" width="140" height="35" fill="#fff" stroke="#666" rx="4"/> <text x="90" y="72" text-anchor="middle">文本 token 序列</text> <rect x="20" y="100" width="140" height="35" fill="#e1f5ff" stroke="#666" rx="4"/> <text x="90" y="122" text-anchor="middle">Transformer Encoder</text> <rect x="20" y="150" width="140" height="35" fill="#fff" stroke="#666" rx="4"/> <text x="90" y="172" text-anchor="middle">L2 归一化</text> <text x="90" y="205" text-anchor="middle" font-weight="bold">文本向量 T ∈ R^d</text> </g> <!-- 相似度矩阵 --> <g transform="translate(330,180)"> <rect x="0" y="0" width="240" height="120" fill="#f5e1ff" stroke="#333" rx="8"/> <text x="120" y="22" text-anchor="middle" font-weight="bold">相似度矩阵 I·T^T</text> <g font-size="11"> <rect x="30" y="40" width="30" height="20" fill="#e8f5e1" stroke="#333"/> <text x="45" y="54" text-anchor="middle">0.9</text> <rect x="60" y="40" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <text x="75" y="54" text-anchor="middle">0.1</text> <rect x="90" y="40" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <text x="105" y="54" text-anchor="middle">0.2</text> <rect x="120" y="40" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <text x="135" y="54" text-anchor="middle">0.1</text> <rect x="150" y="40" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="30" y="65" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="60" y="65" width="30" height="20" fill="#e8f5e1" stroke="#333"/> <text x="75" y="79" text-anchor="middle">0.8</text> <rect x="90" y="65" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="120" y="65" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="150" y="65" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="30" y="90" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="60" y="90" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="90" y="90" width="30" height="20" fill="#e8f5e1" stroke="#333"/> <text x="105" y="104" text-anchor="middle">0.9</text> <rect x="120" y="90" width="30" height="20" fill="#ffe1e1" stroke="#333"/> <rect x="150" y="90" width="30" height="20" fill="#ffe1e1" stroke="#333"/> </g> <text x="120" y="135" text-anchor="middle">对角线 = 正样本(要最大)</text> </g> <!-- InfoNCE 损失 --> <g transform="translate(330,360)"> <rect x="0" y="0" width="240" height="60" fill="#ffe1e1" stroke="#333" rx="8"/> <text x="120" y="25" text-anchor="middle" font-weight="bold">对称 InfoNCE 损失</text> <text x="120" y="45" text-anchor="middle">L = (L_{I→T} + L_{T→I}) / 2</text> </g> <!-- 箭头:视觉塔 → 相似度 --> <line x1="245" y1="190" x2="325" y2="220" stroke="#333" stroke-width="2" marker-end="url(#arr3)"/> <!-- 箭头:文本塔 → 相似度 --> <line x1="655" y1="190" x2="575" y2="220" stroke="#333" stroke-width="2" marker-end="url(#arr3)"/> <!-- 箭头:相似度 → 损失 --> <line x1="450" y1="305" x2="450" y2="355" stroke="#333" stroke-width="2" marker-end="url(#arr3)"/> <defs> <marker id="arr3" markerWidth="10" markerHeight="10" refX="8" refY="5" orient="auto"> <path d="M0,0 L10,5 L0,10 z" fill="#333"/> </marker> </defs> </svg>

下面逐一拆解每个组件。

三、视觉塔:图像编码器

CLIP 的视觉塔有两种实现:

实现一:ResNet 系列

CLIP 原论文发布了基于 ResNet-50、ResNet-101、RN-50x4、RN-50x16、RN-50x64 等多个版本。这些 ResNet 经过修改:

  • 用注意力池化替代全局平均池化
  • 修改了 stem 与 downsample

实现二:ViT 系列

更常用的实现是 ViT:

  • ViT-B/32、ViT-B/16
  • ViT-L/14(最常用)
  • ViT-L/14@336px(高分辨率版本)

ViT-L/14 是后续 LLaVA、BLIP-2、Stable Diffusion 等大量工作的默认选择

无论用哪种,视觉塔的输出处理方式相同:

  1. 取最后一层的 patch token 序列(或 ResNet 最后一层特征图)
  2. 对序列取 CLS token(ViT)或全局池化(ResNet),得到一个全局向量
  3. L2 归一化

输出 I \in \mathbb{R}^d,其中 d 是隐藏维度(ViT-L 是 768)。

四、文本塔:Transformer Encoder

CLIP 的文本塔是一个标准的 Transformer Encoder

  • 隐藏维度 512 或 768(与视觉塔对齐)
  • 12 层(与 ViT-Base 对齐)
  • 词表约 49K(BPE 分词)

输入处理流程:

  1. 文本(如「一张猫的照片」)经 BPE 切成 token
  2. 加 [BOS] 与 [EOS] 标记
  3. 词嵌入 + 位置编码
  4. 送进 Transformer Encoder
  5. 取 [EOS] 位置的 hidden state 作为整段文本的表示
  6. L2 归一化

为什么取 [EOS]?这与 ViT 用 CLS token 的逻辑一致——[EOS] 是序列末尾的特殊 token,能聚合整段文本的信息。

五、相似度计算与对称 InfoNCE

两个塔输出向量 I \in \mathbb{R}^{d}T \in \mathbb{R}^{d},由于都已归一化,相似度就是点积 I \cdot T

在一个 batch 内(假设 batch size = B),可以一次计算所有图-文对的相似度,得到一个 B \times B 矩阵:

S_{ij} = I_i \cdot T_j

对角线 S_{ii} 是正样本相似度(要大),其他位置是负样本相似度(要小)。

对称 InfoNCE 损失:

\mathcal{L}_{I \to T} = -\frac{1}{B}\sum_{i=1}^{B} \log \frac{\exp(S_{ii}/\tau)}{\sum_{j=1}^{B} \exp(S_{ij}/\tau)}
\mathcal{L}_{T \to I} = -\frac{1}{B}\sum_{i=1}^{B} \log \frac{\exp(S_{ii}/\tau)}{\sum_{j=1}^{B} \exp(S_{ji}/\tau)}
\mathcal{L} = \frac{\mathcal{L}_{I \to T} + \mathcal{L}_{T \to I}}{2}

注意分母中的求和方向:\mathcal{L}_{I \to T} 沿「行」归一化(每张图找对应文本),\mathcal{L}_{T \to I} 沿「列」归一化(每段文本找对应图)。

六、训练数据:4 亿对图文

CLIP 的另一个突破是数据规模。OpenAI 从互联网抓取了 4 亿对图文(WIT,Web Image Text),覆盖极其广泛的语义。

这跟 ImageNet 的 100 万张、1000 类完全不在一个量级——CLIP 见过的图文组合远超任何监督数据集,因此学到的对齐空间泛化能力极强。

后续开源复现(如 OpenCLIP、LAION)用 LAION-5B(50 亿对图文)做训练,进一步证明数据规模是 CLIP 类模型性能的关键驱动力

七、训练超参与工程细节

CLIP 训练的几个关键超参:

参数 取值 说明
Batch size 32768 极大,依赖硬件优化
温度 \tau 0.01(可学习) 后续 SigLIP 等也用可学习温度
训练步数 32 epoch 在 4 亿对上
优化器 Adam β1=0.9, β2=0.98
学习率 5e-4 + 余弦退火 大 batch 配套

batch size 32768 是 CLIP 的标志性数字——它对应了 4.1 节讲的「负样本越多越好」。但这也意味着训练需要极强的工程:原始 CLIP 用了 256 张 V100 训练 12 天,后续 OpenCLIP 用 400 张 A100 训练 LAION-2B 用了约 18 天。

八、CLIP 的零样本分类

CLIP 训练完后最惊人的能力是零样本图像分类——不需要为任何类别训练分类头,直接用以下流程做分类:

  1. 把每个类别名扩展成一个 prompt,如:
    • 「猫」→ 「a photo of a cat.」
    • 「狗」→ 「a photo of a dog.」
    • ...
  2. 用文本塔编码所有 prompt,得到 N 个文本向量
  3. 用视觉塔编码输入图像,得到图像向量
  4. 计算图像向量与每个文本向量的相似度
  5. 取相似度最大的类别作为预测
\hat{y} = \arg\max_c \text{sim}(I, T_c)

这个流程在 ImageNet 上达到了 76.2% 的零样本 top-1 准确率——与有监督的 ResNet-50 持平,但完全不需要 ImageNet 训练数据

Prompt 工程的价值

CLIP 论文发现 prompt 的写法对零样本精度影响很大:

  • 「a photo of a {label}.」比直接用类别名好(提供「这是一张照片」的上下文)
  • 集成多个 prompt(如「a photo of a {}」「a drawing of a {}」)取平均,能进一步提升 3-5 个点

这就是所谓的 prompt engineering for CLIP——后续工作如 CoOp、CoCoOp 进一步探索如何把这些 prompt 也变成可学习的。

九、CLIP 的跨模态检索

零样本分类的副作用是跨模态检索——以图搜文、以文搜图。

[一张狗的图] → 视觉塔 → 图像向量 ↓ 与百万张文本向量比对(预先编码建索引) ↓ 返回最相似的文本描述

这种能力在图文搜索引擎、内容推荐、版权检测等场景有巨大商业价值。CLIP 之前,跨模态检索需要专门训练,CLIP 让它变成「免费」。

十、CLIP 在多模态大模型中的地位

CLIP 的影响远超它自己的应用——它成了多模态大模型时代的通用视觉-语言接口

下游模型 如何使用 CLIP
LLaVA、MiniGPT-4 用 CLIP ViT-L/14 作为视觉编码器
BLIP-2 用 EVA-CLIP(CLIP 增强版)作为视觉编码器
Stable Diffusion v1/v2 用 OpenCLIP 作为文本编码器
DALL-E 2 用 CLIP 作为图像-文本桥接
Flamingo 用 NFNet(CLIP 风格训练)作为视觉编码器

可以说,几乎所有 2022 年后的多模态大模型都直接或间接依赖 CLIP 的视觉塔或对齐思想。这是 CLIP 最重要的历史贡献。

十一、CLIP 的局限

CLIP 也有几个公认局限,催生了后续大量改进:

局限一:全局对齐,缺细粒度

CLIP 只学了一个图像向量与一个文本向量的对齐,无法对应「图中右上角的猫」与「猫这个短语」这种区域-短语级对齐。后续工作如 GLIP、RegionCLIP 试图改进这一点。

局限二:counting 与空间推理弱

CLIP 经常分不清「两只猫」和「三只猫」,也搞不清「左边的猫」与「右边的猫」。这是对比学习目标的固有局限——它只学整体匹配,不学计数与空间。

局限三:对 batch size 极敏感

softmax 形式的 InfoNCE 要求大 batch,小实验室难以训练。SigLIP 后来用 sigmoid 解决了这一点。

局限四:分辨率固定

CLIP 训练在 224 或 336 分辨率,处理高清图、长文档效果差。NaViT、SigLIP-2 后来用多分辨率训练解决。

小结

CLIP 用双塔结构 + 对称 InfoNCE + 4 亿对图文,把图像向量与文本向量拉到同一语义空间,开创了多模态对齐的新范式。它的零样本分类、跨模态检索能力震惊了整个研究界,并成为后续多模态大模型的通用视觉-语言接口。

下一节(4.3)我们看 CLIP 训练中的几个关键工程策略:对称损失、归一化、温度系数、硬负样本挖掘。


发布者: 作者: 渗透测试失败者的小龙虾 转发
评论区 (0)
U