note
- CLIP使用了对比学习的方法,即通过正样本(匹配的图像-文本对)和负样本(不匹配的图像-文本对)来训练模型。在训练过程中,模型会尝试最大化正样本对的相似度(比如通过计算余弦相似度),同时最小化负样本对的相似度。
- CLIP模型在zero-shot学习中表现强大,可以直接用于zero-shot推理,比如将猫的图片emb后,将猪狗猫等类的文本描述也分别emb,计算图片和类别emb之间的相似度,从而进行分类。
- CLIP-ViT-L/14模型的14表示每个patch的分辨率为14X14,比如在224x224像素的图像上,总共有(224 / 14) x (224 / 14) = 16 x 16 = 256个patch。
- LLaVA的模型结构非常简单,就是CLIP+LLM(Vicuna,LLaMA结构),利用Vison Encoder将图片转换为[N=1, grid_H x grid_W, hidden_dim]的feature map,然后接一个插值层Projection W,将图像特征和文本特征进行维度对齐。经过Projection后,得到[N=1, grid_H x grid_W=image_seqlen, emb_dim]。然后将 image token embedding和text token embedding合并到一起,作为语言模型的输入,生成描述的文本。
- 与InstructBLIP或Qwen-VL在数亿甚至数十几亿的图像文本配对数据上训练的、专门设计的视觉重新采样器相比,LLaVA用的是最简单的LMM架构设计,只需要在600K个图像-文本对上,训练一个简单的完全连接映射层即可。
文章目录
- note
- CLIP模型
- CLIP 损失函数
- CLIP实践
- LLava模型
- 论文贡献
- 模型结构
- LLaVA两阶段训练
- LLava 1.5模型
- LLava 1.6模型
- Reference
CLIP模型
链接:https://arxiv.org/pdf/2103.00020.pdf
CLIP模型是一个双塔结构,包括一个文本编码器Text Encoder和一个图像编码器Image Encoder。训练数据集的形式为(image, text),对于每个正确匹配的image和text,text是对image的一句正确描述。CLIP模型需要对(image, text)的数据对进行预测,即(image, text)匹配的为1,不匹配的为0。
- Text Encoder: 对于每个句子, 将其编码成一个隐向量, T i T_i Ti 维度 ( 1 , 512 ) ; N (1,512) ; \mathrm{N} (1,512);N 个句子, 因此有 T 1 T_1 T1 T N T_N TN ,即[N, 512]
- Image Encoder: 对于每张img, 将其编码成一个隐向量, l i l_i li 维度 ( 1 , 512 ) ; N (1,512) ; \mathrm{N} (1,512);N 张图, 因此有 l 1 l_1 l1 l N l_N lN, 即 [ N , 512 ] [\mathrm{N}, 512] [N,512]
由于Text Encoder和Image Encoder最后都是输出[N,512]的Tensor,因此可以很方便地计算images和texts两两之间的相似度。CLIP可以选在ResNet或ViT作为Backbone。实验表明,ViT的效果要好于ResNet。
CLIP 损失函数
CLIP采用对称损失函数,简单来说,就是对相似度矩阵,分别从行方向和列方向计算loss,最后取两者的平均。
-
图像到文本(Image-to-Text):
- 对于每一个图像,模型尝试找出与之对应的文本描述。模型计算该图像特征向量与所有文本特征向量的相似度(通常使用点积),并通过softmax函数将这些相似度转换为概率分布。模型的目标是使得与当前图像真实对应的文本的概率最大化。这通常通过最小化交叉熵损失来实现,其中正样本是图像对应的真实文本。
-
文本到图像(Text-to-Image):
- 对于每一个文本描述,模型尝试找出与之对应的图像。这个过程与图像到文本非常相似,但方向相反。模型计算该文本特征向量与所有图像特征向量的相似度,并通过softmax函数转换为概率分布。模型的目标是使得与当前文本真实对应的图像的概率最大化。这也是通过最小化交叉熵损失来实现,其中正样本是文本对应的真实图像。
数学表示和损失函数实现:
给定批量中有
N
N
N 个图像和文本对, 损失函数由两部分交叉嫡组成:
L
=
1
2
N
(
∑
i
=
1
N
−
log
e
s
i
,
i
/
τ
∑
j
=
1
N
e
s
i
,
j
/
τ
+
∑
i
=
1
N
−
log
e
s
i
,
i
/
τ
∑
j
=
1
N
e
s
j
,
i
/
τ
)
L=\frac{1}{2 N}\left(\sum_{i=1}^N-\log \frac{e^{s_{i, i} / \tau}}{\sum_{j=1}^N e^{s_{i, j} / \tau}}+\sum_{i=1}^N-\log \frac{e^{s_{i, i} / \tau}}{\sum_{j=1}^N e^{s_{j, i} / \tau}}\right)
L=2N1(i=1∑N−log∑j=1Nesi,j/τesi,i/τ+i=1∑N−log∑j=1Nesj,i/τesi,i/τ)
其中, s i , j s_{i, j} si,j 是图像 i i i 和文本 j j j 的特征向量的点积, τ \tau τ 是一个温度参数。
伪代码:
# 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
# extract feature representations of each modality
I_f = image_encoder(I) #[n, d_i]
T_f = text_encoder(T) #[n, d_t]
# joint multimodal embedding [n, d_e]
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)
# scaled pairwise cosine similarities [n, n]
logits = np.dot(I_e, T_e.T) * np.exp(t)
# symmetric loss function
labels = np.arange(n)
# 图像到文本的损失函数,第0维度即图片的行维度
loss_i = cross_entropy_loss(logits, labels, axis=0)
# 文本到图像的损失函数
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t)/2
CLIP实践
$ conda install --yes -c pytorch pytorch torchvision cudatoolkit
$ pip install ftfy regex tqdm
$ pip install git+https://github.com/openai/CLIP.git
import torch
import clip
from PIL import Image
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
image = preprocess(Image.open("CLIP.png")).unsqueeze(0).to(device)
text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)
with torch.no_grad():
image_features = model.encode_image(image)
print("image_features shape:", image_features.shape) # [1, 512]
text_features = model.encode_text(text)
print("text_features shape:", text_features.shape) # [3, 512]
logits_per_image, logits_per_text = model(image, text)
print("logits_per_image shape:", logits_per_image.shape) # [1, 3]
print("logits_per_text shape:", logits_per_text.shape) # [3, 1]
probs = logits_per_image.softmax(dim=-1).cpu().numpy()
print("Label probs:", probs) # prints: [[0.9927937 0.00421068 0.00299572]]
print(" Label: {}".format(["a diagram", "a dog", "a cat"]))
LLava模型
论文:https://arxiv.org/pdf/2304.08485.pdf
项目:https://llava-vl.github.io/
论文贡献
- 多模态指令数据。当下关键的挑战之一是缺乏视觉与语言组成的指令数据。本文提出了一个数据重组方式,使用 ChatGPT/GPT-4 将图像 - 文本对转换为适当的指令格式;
- 大型多模态模型。研究者通过连接 CLIP 的开源视觉编码器和语言解码器 LLaMA,开发了一个大型多模态模型(LMM)—— LLaVA,并在生成的视觉 - 语言指令数据上进行端到端微调。实证研究验证了将生成的数据用于 LMM 进行 instruction-tuning 的有效性,并为构建遵循视觉 agent 的通用指令提供了较为实用的技巧。使用 GPT-4,本文在 Science QA 这个多模态推理数据集上实现了最先进的性能。
- 开源。研究者向公众发布了以下资产:生成的多模式指令数据、用于数据生成和模型训练的代码库、模型检查点和可视化聊天演示。
模型结构
LLaVA模型的架构,是将一个预训练的视觉编码器(CLIP ViT-L/14)与一个大规模语言模型(Vicuna)连接在一起。
这两个模型通过一个简单的映射矩阵连接,这个矩阵负责将视觉和语言特征对齐或转换,以便在一个统一的空间内对它们进行操作。在多模态指令跟随数据集上,LLaVA表现出色,跟GPT-4相比,分数达到了85.1%。在Science QA上,LLaVA的准确率刷新了纪录,达到92.53%。
与InstructBLIP或Qwen-VL在数亿甚至数十几亿的图像文本配对数据上训练的、专门设计的视觉重新采样器相比,LLaVA用的是最简单的LMM架构设计,只需要在600K个图像-文本对上,训练一个简单的完全连接映射层即可。
对于输入图像 X_v,本文使用预训练的 CLIP 视觉编码器 ViT-L/14 进行处理,得到视觉特征 Z_v=g (X_v)。实验中使用的是最后一个 Transformer 层之前和之后的网格特征。本文使用一个简单的线性层来将图像特征连接到单词嵌入空间中。具体而言,应用可训练投影矩阵 W 将 Z_v 转换为语言嵌入标记 H_v,H_v 具有与语言模型中的单词嵌入空间相同的维度:
H
v
=
W
⋅
Z
v
, with
Z
v
=
g
(
X
v
)
\mathbf{H}_{\mathrm{v}}=\mathbf{W} \cdot \mathbf{Z}_{\mathrm{v}} \text {, with } \mathbf{Z}_{\mathrm{v}}=g\left(\mathbf{X}_{\mathrm{v}}\right)
Hv=W⋅Zv, with Zv=g(Xv)
在LLaVA中,Vision Encoder使用的是CLIP-ViT-L/14,并且,需要注意的是,LLaVA使用最后一层Transformer之前或之后的grid features作为图像表示,而不是CLIP最后的输出层。
总结:LLaVA的模型结构非常简单,就是CLIP+LLM(Vicuna,LLaMA结构),利用Vison Encoder将图片转换为[N=1, grid_H x grid_W, hidden_dim]的feature map,然后接一个插值层Projection W,将图像特征和文本特征进行维度对齐。经过Projection后,得到[N=1, grid_H x grid_W=image_seqlen, emb_dim]。然后将 image token embedding和text token embedding合并到一起,作为语言模型的输入,生成描述的文本。
LLaVA两阶段训练
阶段一:特征对齐预训练。由于从CLIP提取的特征与word embedding不在同一个语义表达空间,因此,需要通过预训练,将image token embedding对齐到text word embedding的语义表达空间。这个阶段冻结Vision Encoder和LLM模型的权重参数,只训练插值层Projection W的权重。
阶段二:端到端训练。这个阶段,依然冻结Vision Encoder的权重,训练过程中同时更新插值层Projection W和LLM语言模型的权重,训练考虑Multimodal Chatbot和Science QA两种典型的任务。
LLava 1.5模型
论文:https://arxiv.org/pdf/2310.03744.pdf
LLaVA 1.5和LLaVA在模型架构上基本一致,对LLM模型和插值层做了修改,但是模型效果逐渐开始炸裂:
- LLM模型:LLM语言模型升级为Vicuna v1.5 13B,语言模型参数量更大,效果更好
- Connector:也就是插值层,由原来的单个线性层替换为MLP层(多层线性层叠加)
- Vision Encoder: 输入图像分辨率由224增大为336,使用CLIP ViT-L/336px,对图像细节理解能力更强
- 更高质量的数据
LLava 1.6模型
链接:https://llava-vl.github.io/blog/2024-01-30-llava-next/
2024年1月30日,LLaVA发布了1.6版本,模型效果又得到了进一步提升,增强了推理,OCR和世界知识的能力。模型参数量来到了34B,比1.5版本的13B有了巨大的提升,同时模型效果在各项指标都直接有10个点以上的提升。主要改变是:
- Vision Encoder分辨率:支持更大的分辨率,包括672x672, 336x1344, 1344x336 几种分辨率的输入,并且支持通过图片裁切,编码,合并来实现。
- LLM模型参数量大升级:由LLaVA 1.5的13B参数,增加到最多34B参数。
- OCR能力提升:更好的推理和OCR能力:通过修改指令数据集实现
- 更好的视觉对话:在一些场景下,拥有更好的世界知识
Reference
[1] 浙大校友开源多模态大模型LLaVA-1.5,130亿参数8个A100一天训完
[2] [LLaVA系列]CLIP/LLaVA/LLaVA1.5/VILA笔记
[3] 从零实现CLIP模型