CLIP:图像和文字如何进入同一个空间?

多模态第三课,从配对游戏到对比学习与零样本分类

目录

  第二课区分了分类、图文对齐和视觉自监督,这一课把图文对齐单独展开:一张猫的照片和一句“a photo of a cat”,原本分别是像素与字符,怎样才能计算它们是否匹配?

  CLIP 不需要先把图片翻译成一句话,而是分别编码图片与文字,将它们变成可以比较的向量,再通过大量图文配对学习这种比较方式。[1]

1、先把训练想成一个配对游戏

  桌上有三张图片:猫、狗、自行车;旁边放着三段对应描述。每次训练,模型要给每张图片选描述,也给每段描述选图片,选错时调整参数,下次再试。

  如果按配对顺序排列,正确答案就落在下表的对角线上。其他格子不是人类逐个标注的错误描述,而是从同一批数据中构造出来的竞争候选。

图片 \ 描述 猫的照片 狗的照片 自行车的照片
猫 正确配对 候选 候选
狗 候选 正确配对 候选
自行车 候选 候选 正确配对

训练希望正确配对的分数比竞争候选更高,并不要求每一对不匹配图文的相似度都变成零。

  现实里可能有两张猫图、重复描述或不完整的文字,因此同批的“负例”不一定都在语义上错误。这种噪声和假负例也是理解对比学习时需要记住的边界。

2、共同空间怎样建立?

2.1 两个编码器,各自提取特征

  图片经过视觉编码器得到全图特征,文字经过文本编码器得到句子特征,再由各自的投影层映射到相同宽度。图中的长度为教学示意,实际维度取决于模型配置。

CLIP 双编码器、共享空间与配对矩阵

  图片编码器和文字编码器不需要共享参数;“共同空间”指它们的最终表示可以按同一规则比较,而不是内部结构或每个隐藏维度都相同。

2.2 相同维度只是接口条件

  两位同学各写一张六列的表,并不意味着每一列记录同一种东西。类似地,把图像与文字都投影到 $d$ 维,只解决了形状问题,训练中的配对信号才逐渐让它们形成有用的对应关系。

  设图像编码器与投影得到 $u_i$,文本编码器与投影得到 $w_j$,先把向量归一化:

\[v_i=\frac{u_i}{\|u_i\|_2},\qquad t_j=\frac{w_j}{\|w_j\|_2}\]

  归一化后长度都是 1,点积 $v_i^Tt_j$ 就是余弦相似度,比较的是方向而不是向量长度。“接近”因此是这套表示中的相似,不代表所有人类理解的语义关系都被完整保留。

3、配对关系怎样变成损失?

3.1 先算一张分数表

  一批有 $B$ 对图文,图像和文字特征的形状均为 [B,d],将两者相乘得到 [B,B] 的分数矩阵:

\[S_{ij}=\frac{v_i^Tt_j}{\tau}\]

  第 $i$ 行表示“这张图与各段描述有多匹配”,第 $j$ 列则表示“这段描述与各张图有多匹配”;$\tau>0$ 是温度,官方实现通过可学习的对数尺度参数构造它的倒数。[2]

3.2 对每一行选正确描述,再对每一列选正确图片

  以一行示意 logits [2,1,0] 为例,softmax 后约为 [0.665,0.245,0.090]。如果第一项是正确配对,损失为 $-\log(0.665)\approx0.408$;正确项概率越大,这个损失越小。

\[L_{image}=-\frac1B\sum_{i=1}^{B}\log\frac{\exp(S_{ii})}{\sum_{j=1}^{B}\exp(S_{ij})}\]

  这就是常见的 InfoNCE 风格对比目标在图像找文字方向上的形式:分子是正确配对,分母包含全部候选。转置分数表后,对文字找图片再算一次交叉熵,最后取平均:

\[L_{text}=-\frac1B\sum_{j=1}^{B}\log\frac{\exp(S_{jj})}{\sum_{i=1}^{B}\exp(S_{ij})},\qquad L=\frac{L_{image}+L_{text}}2\]

  梯度会沿分数计算、投影层和编码器往回传,使模型逐渐改善配对结果。两个编码器不是先各自训练完,再靠一个固定规则自动获得语义对齐。

3.3 温度改变什么?

  对同一组相似度,较小的温度会放大 logits 差异,让 softmax 更尖锐。例如 [0.6,0.3,0] 在 $\tau=1$ 时约为 [0.437,0.324,0.240],在 $\tau=0.1$ 时约为 [0.950,0.047,0.002]。

  固定特征时,这种正比例缩放不会改变候选排序,却会改变概率分布与训练梯度;如果最高分恰好是错误答案,也会让模型更加确信错误,所以温度不是越小越好。

4、用最小代码算一次 CLIP 损失

  下面用手工向量演示归一化、分数矩阵和双向交叉熵,省去图片加载与编码器。它能在安装了 PyTorch 的环境运行,但不是完整的 CLIP 训练,也不代表真实特征。

import torch
import torch.nn.functional as F

image_features = torch.tensor([
    [1.0, 0.0], [0.0, 1.0], [-1.0, 0.0]
], requires_grad=True)
text_features = torch.tensor([
    [0.9, 0.1], [0.1, 0.9], [-0.9, 0.1]
], requires_grad=True)

v = F.normalize(image_features, dim=-1)
t = F.normalize(text_features, dim=-1)
tau = 0.1
logits = v @ t.T / tau          # [3, 3]
labels = torch.arange(3)       # 正确配对位于对角线
loss_image = F.cross_entropy(logits, labels)
loss_text = F.cross_entropy(logits.T, labels)
loss = (loss_image + loss_text) / 2
loss.backward()
print(logits.detach().round(decimals=2))
print(round(loss.item(), 4))   # 约 0.0002

  这里特意使用相互匹配的方向,所以损失很小。可以交换两行文字向量再计算,观察配对顺序错误如何增大损失;真实训练中还需要优化器更新编码器、投影层与尺度参数。

  有两个常见错误:不做归一化会把向量长度混入打分;先 softmax 再传入 cross_entropy 会重复处理,因为这个函数期望接收 logits,内部已经包含 log-softmax。

5、零样本分类为什么不需要另训分类头?

  训练时学会图文比较后,可以把类别写成描述,让文字编码器为每个类别生成一个向量,再拿待分类图片与这些向量逐一比较。比如候选类别是猫、狗和自行车,分别构造 “a photo of a cat”、 “a photo of a dog”、 “a photo of a bicycle”。

\[\widehat y=\arg\max_c v_{image}^Tt_{prompt(c)}\]

  这相当于用文本特征临时构造分类器,不需要在当前目标数据集上额外训练一个类别头。“零样本”指这次迁移没有用目标任务标签做训练,并不说明模型从未见过相关概念,也不意味着能识别所有新类别。

  官方实现提供了图像预处理、文本分词与特征提取接口,使用时应成套使用同一个模型的两路编码器。[2]

  提示语会影响文本表示,换描述、补上下文或对多个模板的特征做聚合,都可能改变效果;需要在固定评价数据上验证,而不是只选一个看起来效果好的示例。

6、容易混淆的事与一个小实验

容易误解的地方 更准确的理解
CLIP 会给图片生成描述 基础 CLIP 比较图文向量,没有自回归文本生成器
相同维度就能比较语义 还要有共同训练、正确投影和归一化
softmax 得到的是客观置信度 它依赖候选集合与尺度,不是自动校准的可信度
同批其他图文都是错误配对 重复主体与描述会产生假负例
图文相似度高就证明细节正确 整体语义匹配不能替代定位、计数或时间证据

  可以挑一组图片,先做固定候选的零样本分类,再将某个类别提示改得更具体,记录结果变化。接着加入一个新的候选类别,观察原有候选的 softmax 概率变化;这个实验能直观看到“相对候选得分”与“客观置信度”的区别。

  游戏截图中的遮挡、小目标和准星关系,往往超出一句整体描述能够表达的范围。CLIP 可作为检索或表示学习的起点,具体审核判断仍要用任务数据验证局部与时间证据。

7、把整条流程串起来

  图像与文字分别编码,经过投影和归一化后进入可比较的空间;训练时,用一批图文构造分数表,提高正确配对的相对得分;使用时,再通过候选文本完成检索或零样本分类。

  读完后,试着回答:为什么损失要算两个方向?减小温度为什么不改变固定特征的排序?新增候选类别时,原有类别的 softmax 概率为什么会变化?

  下一课看 SigLIP:如果不在整批候选之间做 softmax,而是逐对判断是否匹配,训练目标会怎样变化?

8、参考文献(References)

  1. Radford, A., Kim, J. W., Hallacy, C., et al. “Learning Transferable Visual Models From Natural Language Supervision.” ICML, 2021. arXiv:2103.00020.↩
  2. OpenAI. “CLIP: Official Implementation.” GitHub repository, 2021. Accessed 2026-10-07.↩