Alec Radford等人提出Contrastive Language-Image Pre-training (CLIP), 突破了文本-图像之间的限制。CLIP使用大规模的文本-图像配对预训练,并且可以直接迁移到Imagenet上,完全不需要图像标签微调即可实现zero-shot分类。CLIP模型或许会引导CV的发展走向大规模预训练,文本-图像打通的时代。1

预训练方法

文本-图像对

常规的图像分类模型往往都基于有类别标签的图像数据集进行全监督训练,这往往对于数据需求非常高,需要大量人工标注;同时限制了模型的适用性和泛化能力,不适于任务迁移。

而在我们的互联网上,可以轻松获取大批量的文本-图像配对数据。Open AI团队通过收集4亿(400 million)文本-图像对((image, text) pairs),以用来训练其提出的CLIP模型。

CLIP系列-1

模型结构

CLIP的模型结构其实非常简单:包括两个部分,即文本编码器(Text Encoder)图像编码器(Image Encoder)。Text Encoder选择的是Text Transformer模型;Image Encoder选择了两种模型,一是基于CNN的ResNet(对比了不同层数的ResNet),二是基于Transformer的  ViT

CLIP系列-2

训练过程

CLIP在文本-图像对数据集上的训练过程如下:

  1. 假设DataLoader中的一个batch包含了 $N$ 个(文本-图像)对。将这 $N$ 个文本先通过Text Encoder进行文本编码,假设Text Encoder将每条文本编码为一个长度为 $d_t$ 的一维向量,那么这个batch的文本数据经Text Encoder的输出为$[T_{1}, T_{2}, \dots , T_{N}]$ ,维度为 $(N,d_{t})$ ; 同样的,将这 $N$ 个图像先通过Image Encoder进行图像编码,假设Image Encoder将每条文本编码为一个长度为 $d_{i}$ 的一维向量,那么这个batch的图像数据经Image Encoder的输出为$[I_{1}, I_{2}, \dots , I_{N}]$,维度为 $(N,d_{i})$ 。
  2. 得到的$[T_{1}, T_{2}, \dots , T_{N}]$和中$[I_{1}, I_{2}, \dots , I_{N}]$,文本-图像是一一对应的,例如 $T_{1}$ 与 $I_{1}$ 对应,$T_{2}$ 与 $I_{2}$ 对应,…,$T_{N}$与 $I_{N}$ 对应,这 $N$ 个对应关系我们记为正样本;而原本并不对应的文本-图像我们标记为负样本,例如 $T_1$ 与 $I_2$ 不对应,$T_N$ 与 $I_{N-1}$ 不对应。这么一来,我们就有个 $N$ 个正样本,$N^2-N$ 个负样本。这样正负样本就可以作为正负标签,用来训练Text Encoder和Image Encoder了。
  3. 我们通过计算 $I_i$ 与 $T_j$ ( $i,j \in [1,N]$ )之间的余弦相似度(cosine similarity), $I_{i} \cdot T_{j}$用来度量相应的文本与图像之间的对应关系。余弦相似度越大,表明$I_i$ 与$T_j$ 的对应关系越强,反之越弱。那么训练的任务便很清晰地变成了:通过训练Text Encoder和Image Encoder的参数,最大化$N$个正样本的余弦相似度,最小化 $N^2-N$个负样本的余弦相似度。按上图所示,即最大化对角线中蓝色的数值,最小化其它非对角线的数值。优化目标可以写为: $$ min(\sum\limits_{i=1}^{N} \sum\limits_{i=1}^{N} (I_{i} \cdot T_{j})_{(i \neq j)} - \sum\limits_{i=1}^{N}(I_{i} \cdot T_i)) $$ 原论文中的伪代码实现如下:
# image_encoder - ResNet or Vision Transformer
# text_encoder - CBOW or Text Transformer
# I[n, h, w, c] - minibatch of aligned images
# T[n, l] - minibatch of aligned texts
# W_i[d_i, d_e] - learned proj of image to embed
# W_t[d_t, d_e] - learned proj of text to embed
# t - learned temperature parameter

# 分别提取图像特征和文本特征
I_f = image_encoder(I) #[n, d_i]
T_f = text_encoder(T) #[n, d_t]

# 对两个特征进行线性投射,得到相同维度的特征,并进行l2归一化
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)

# 计算缩放的余弦相似度:[n, n]
logits = np.dot(I_e, T_e.T) * np.exp(t)

# 对称的对比学习损失:等价于N个类别的cross_entropy_loss
labels = np.arange(n) # 对角线元素的labels
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t)/2

损失函数

对比学习损失函数有多种,其中比较常用的一种是InfoNCE loss2,何恺明的论文MoCo提出,我们可以把对比学习看成是一个字典查询的任务,即训练一个编码器从而去做字典查询的任务。

假设已经有一个编码好的query $q$(一个特征),以及一系列编码好的样本$k_{0}, k_{1}, k_{2}, \dots$那么$k_{0}, k_{1}, k_{2}, \dots$可以看作是字典里的key。假设字典里只有一个key即 $k_+$ (称为 $k$ positive)是跟$q$是匹配的,那么 $q$$k_+$ 就互为正样本对,其余的key为q的负样本。一旦定义好了正负样本对,就需要一个对比学习的损失函数来指导模型来进行学习。这个损失函数需要满足这些要求,即当query 和 $q$ 唯一的正样本 $k_+$ 相似,并且和其他所有负样本key都不相似的时候,这个loss的值应该比较低。反之,如果 $q$$k_+$ 不相似,或者 $q$ 和其他负样本的key相似了,那么loss就应该大,从而惩罚模型,促使模型进行参数更新。

CLIP系列-4
MoCo采用的对比学习损失函数就是InfoNCE loss,以此来训练模型,公式如下: $$ L_{q} = - \log \frac{exp(q \cdot k_{+} /_\mathcal{T})}{\sum_{i=0}^{k}exp(q \cdot k_{i} /_{\mathcal{T}})} $$ CLIP 在两个方向上应用 InfoNCE Loss:

  • 对于每个图像 $I_i$ ,目标是让 $S_{i,i}$ (匹配的文本 $T_i$ )的得分高于其他 ($S_{i,j}(j \neq i)$) (不匹配的文本)
  • 对于每个文本 $T_i$ ,目标是让 $S_{i,i}$ (匹配的图像 $I_i$ )的得分高于其他 ($S_{i,j}(j \neq i)$) (不匹配的图像)

损失函数的形式为:

$$ \mathcal{L}_{image} = - \frac{1}{N}\sum\limits_{i=1}^{N} \log \frac{exp(S_{i,i}/\tau)}{\sum_{j=1}^{N} exp(S_{i,j}/\tau)} $$

$$ \mathcal{L}_{text} = - \frac{1}{N}\sum\limits_{i=1}^{N} \log \frac{exp(S_{i,i}/\tau)}{\sum_{j=1}^{N} exp(S_{j,i}/\tau)} $$

( $\tau$ ) 是一个可学习的温度参数,用于缩放相似度得分。

总损失是两个方向的平均: $$ \mathcal{L} = \frac{1}{2}(\mathcal{L}_{image} + \mathcal{L}_{text}) $$ 由于InfoNCE的形式与CrossEntropy的形式完全一致,因此对于每一行$S[i,:]$(图像$I_i$对所有文本的相似度),我们可以将其视为一个分类问题,目标是让模型正确预测第i个文本(正样本)

零样本图像分类

经过上一章的讲解,我们了解了CLIP如何在文本-图像对数据上训练,并且训练好的模型有能力判断给定的文本和图像是否匹配。这时CLIP已经完成了其全部训练过程,完全不需要Imagenet或其它数据集中的图像-类别标签,即可以直接做图像分类了,这也是CLIP这个模型最大的亮点:zero-shot图像分类。这是如何实现的呢,其实也非常简单:

CLIP系列-3
步骤如下:

  1. 根据所迁移的数据集将所有类别转换为文本。这里以Imagenet有1000类为例,我们得到了1000个文本:A photo of {label}。我们将这1000个文本全部输入 Text Encoder中,得到1000个编码后的向量 $T_{i}(i = 1,2, \dots , N)(N=1000)$,这被视作文本特征。
  2. 我们将需要分类的图像(单张图像)输入Image Encoder中,得到这张图像编码后的向量 $I_1$ 。将$I_1$与得到的1000个文本特征分别计算余弦相似度。找出1000个相似度中最大的那一个(上图中对应的为 $T_3$ ),那么评定要分类的图片与第三个文本标签(dog)最匹配,即可将其分类为狗。

  1. 详解CLIP (一) | 打通文本-图像预训练实现ImageNet的zero-shot分类,比肩全监督训练的ResNet50/101  https://zhuanlan.zhihu.com/p/521151393  ↩︎

  2. InfoNCE 在 CLIP 中的应用原理(代码实现):为什么用交叉熵F.cross_entropy来实现?  https://blog.csdn.net/shizheng_Li/article/details/146710821  ↩︎