CLIP探索笔记(Learning Transferable Visual Models From Natural Language Supervision Alec)
记录CLIP的流水账,训练和推理是如何完成的?
每一次阅读都有不同的领悟和发现,一些简单的想法。
官方信息
相关的文章
-
Blog | CLIP Model and The Importance of Multimodal Embeddings
- Code | https://github.com/RustamyF/clip-multimodal-ml
-
Blog | Simple Implementation of OpenAI CLIP model: A Tutorial
- Code | https://github.com/moein-shariatnia/OpenAI-CLIP
它想干嘛?
他想做一个分类任务,一个模糊分类的任务;
他还可以做图文匹配等;
之前看过这篇论文,但是有些步骤或者工作原理没想明白,看到后面越来越迷糊,越来越困惑,所以进一步深挖。
训练阶段
-
Text Encoder不需要训练,直接拿现成的文本模型来用就可以了,比如GPT,提取文本特征
- Text Encoder可以是Bert, GPT
- 作者统一采用GPT-2里的Transformer结构; 对于base size model,使用63M-parameter 12-layer 512-width model with 8 attention heads; model width则随着image encoder的size增加而增加。 输入句子的最大长度为76。
-
Image Encoder需要训练,提取图片特征
- Image Encoder模型可以是vgg,resnet,vit等
- 代码中用的是CNN,resnet结构
-
计算余弦相似度
- 论文中给出了伪代码,文本特征和图像特征,分别和真实标签特征做相似度计算,然后再求平均
-
对角线是正样本
重点来看一下,LOSS的计算
假设一个batch中共有N对<图像,文字>对,经过Text Encoder和Image Encoder 后,就会分别产生:
- N条文字向量: [ T 1 T_1 T1, T 2 T_2 T2,…, T n T_n Tn], N条特征对应上图中红色矩形框,维度是(N=128,D=512), 注意 T 1 T_1 T1就对应一条文本的特征,维度是D=512或者768,
- N张图片向量: [ I 1 I_1 I1, I 2 I_2 I2,…, I n I_n In],N张特征对应上图中绿色矩形框,维度是(N=128,D=512),注意 I 1 I_1 I1就对应一张图的特征,维度是D=512或者768, 与文本的特征维度一致
得到文本特征和图像特征后,我们要干什么事?
T 1 T_1 T1和 I 1 I_1 I1描述的同一个事,一句文本,一张图经过一顿操作后,得到了两个维度相等的向量,然后我们期望是什么呢?
- T 1 T_1 T1和 I 1 I_1 I1,这两个向量是相等,最大化相等,也就是相似度最大,值为1。
- 同时,期望图像 I 1 I_1 I1和其他的文本,不要相似,相似度值为0;
- 还有,期望文本 T 1 T_1 T1和其他图像,也不要相似,相似度值为0。
这样,根据上面的3条件,就可以去计算梯度了。
接下来看一下, 红色框和绿色框做点积运算,得到相似度矩阵,也就是上图中的橙色矩形框;论文中给出的伪代码,对Text Encoder和Image Encoder输出的向量,先做了归一化,再做点积求相似度, 整个过程也就是余弦相似度。
在橙色矩形框中,它就是N个文本和N张图像,两两的做相似度计算,什么意思呢?
- 按行计算loss的时候, 也就是拿一张图像,去和N个文本做相似度计算,找到相似度最大的,也就是期望蓝色小块,相似度值接近1,同时,其他的白色小块相似度值接近0。
- 按列计算loss的时候,也就是拿一条文本,去和N张图像做相似度计算,找到相似度最大的,也就是期望蓝色小块,相似度值接近1,同时,其他的白色小块相似度值接近0。
结合上面说的3个期望条件,我们希望一个训练CLIP模型(Text Encoder和Image Encoder),让他输出的橙色矩形框:
- 蓝色小块的值接近1
- 白色小块的值接近0
调试代码
similarity就是橙色矩形框,也就是相似度矩阵,维度是(128,128)。
往下看计算loss,本来思路很清晰,看到这里,迷糊了:
labels = np.arange(n)
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
受分类loss的影响,绕不过来,是有n类嘛,可是这里没有分类的概念啊!
想了半天,原来这里的labels,不是类别标签, 而是制作GT标签,
labels = np.arange(n),做了one-hot后,对应GT标签,也就是橙色矩形框的GT标签类似这样
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0 1
1为蓝色小块,0为白色小块,作为GT标签,不要往分类标签去想,和分类没半毛钱关系。
GT标签和预测值,按行,按列做交叉熵损失,相加求均值,就可以得到梯度反馈了。
下面是辅助理解的代码:
例子1:
矩阵乘法,也就是点积运算
import numpy as np
import torch
# numpy 矩阵乘法为 A@B 或 np.dot(A,B)
A = np.array([
[1,2],
[3,4]
])
B = np.array([
[1,2],
[3,4]
])
C1 = A @ B
C2 = np.dot(A,B)
print(C1)
print('---------')
print(C2)
# pytoch 矩阵乘法为 A@B 或 torch.matmul(A,B)
PA = torch.tensor(A)
PB = torch.tensor(B)
PC1 = PA @ PB
PC2 = torch.matmul(PA, PB)
print(PC1)
print('---------')
print(PC2)
例子2:
两个向量,做点积运算,
# A simple Example
batch_size = 4
dim = 256
embeddings = torch.randn(batch_size, dim)
out = embeddings @ embeddings.T
print(out)
print(F.softmax(out, dim=-1))
output:
tensor([[278.7493, -25.9693, 14.6406, 9.5813],
[-25.9693, 237.4993, 25.0445, -1.2630],
[ 14.6406, 25.0445, 314.3572, -20.8825],
[ 9.5813, -1.2630, -20.8825, 261.9082]])
tensor(
[[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
最后,拓展一下,你会发现什么问题?
假设在一个batch里面,出现2个相似的样本,loss收敛就会有问题,所以,一个batch里面的样本,最好是什么状态呢? 最好是都不相关的样本,两两之间不要相似,这样我们的loss梯度,working效果最好。
LOSS的计算,到这里就结束了。
推理阶段
- 对一张图,给出预设文本提示(任意n个),预设答案n个
- 预设的文本提示很重要,影响特征值对比
- 文本的预测是模糊预测的,比如:一只猪 或 有一头猪,和之前的VGG,resnet直接输出label值不同;
- 以前咱们是直接预测是猫或是狗,CLIP说 我们不这样干,不做精准的文本预测,我们考虑做相关性预测
- 计算图像数据的特征
- 计算图像特征和文本特征的相似度,相似度最高的就是答案