对比学习中的prompt-engineering
- 写在前面:为什么要做promt engineering?
- CoOp:Learning to Prompt for Vision-Language Models
- CoCoOp:Conditional Prompt Learning
- 最新研究工作:MaPLe(CVPR 23), ALIGN(NIPS 23)
- PLOT: Prompt learning with optimal transport for vision-language models(ICLR 2023)
- Tuning Multi-mode Token-level Prompt Alignment across Modalities (NIPS 2023)
写在前面:为什么要做promt engineering?
什么是对比学习中的prompt-learning?在跨模态学习中,跨模态预训练模型如CLIP,BLIP等是非常好用的工具,而早在CLIP的提出时,就是通过prompt来实现一个zero-shot的性能。zero-shot的概念是,训练过程中只需要对于两个模态数据进行不特定任务的训练,在测试的过程中却可以在多个任务上都有比较好的效果。
Full fine-tuning导致降低之前学习的joint V-L representation,而linear-probe(加一个线性层)限制了CLIP的zero-shot能力。为此,受到NLP中的提示学习的启发,许多工作提出了通过在端到端训练中学习prompt token来适应V-L模型。
在"zero-shot"这个术语中,"shot"指的是模型在特定任务上进行过多少次学习的经验。具体来说,"shot"表示模型在观察(或学习)任务示例(样本)的数量。
举个例子:在CLIP中,zero shot 通过预先设定好的promt实现多个下游任务,如在分类任务上,加一个如“a photo of”的promt就可以做到分类。但是如果是在较为细化的任务上,这种promt并不一定好,如下图所示,如果在一些具体的分类数据集上进行训练,用原本的promt的效果就差了。如(a),a photo of xxx的promt甚至不如a xxx,但a photo of a的promt又能带来非常明显的提升,不仅仅是promt的长度和所带的信息有影响,一些表述的变化也会导致效果的变化。但是直接找到最优的promt是很不现实的,尤其是在多任务,多场景,多数据集的情况下,所以有了一个基本的思想:能否找到一种方法来学习promt呢?
CoOp:Learning to Prompt for Vision-Language Models
CoOp和CoCoOp的github代码:https://github.com/KaiyangZhou/CoOp
Contribution
顺承上面的motivation,CoOp作为第一篇通过在文本模态上添加promt,利用多模态预训练模型来进行视觉下游任务框架中的promt learning策略被提出了。在CoOp的框架下,多模态预训练模型不能直接利用zero shot性能来进行应用,而是添加了一个额外的学习过程,变为一个few shot learning的过程,即仅在小部分数据上进行学习。本文提出了,哪怕是1 shot或者2 shot(每个类别有一个或者两个标注数据进行训练)都能够有较好的提升,且当shot变多(如16shot)的时候,效果会显著变好(平均提升15个点,甚至有能超过45个点的),更重要的是,CoOp在多种领域中体现出了较好的鲁棒性。
总结有三点:
- 提出了多模态下的prompt engineering的概念。
- 专门为VLP模型设计了一个prompt engineering的框架,并且给了一个很简单的实现
- 用实验证明了方法的有效性以及在不同领域的鲁棒性
Methodology
基本的框架如下:
本文需要优化的目标是左上角的生成器(CoOp是Context Optimization的简称)
基本的框架就是一个CLIP,文章先大量介绍了一下CLIP的基本架构,但是主要的内容是有关CoOp的所以是直接进入3.2部分来学习上下文优化策略。有关分类,通过输入不同的类别+promt,计算出来的embedding可以通过softmax的余弦相似度来进行表示:
p
(
y
=
i
∣
x
)
=
exp
(
c
o
s
(
w
i
,
f
)
/
τ
)
∑
j
=
1
K
exp
(
cos
(
w
j
,
f
)
/
τ
)
p(y=i|\boldsymbol x)=\frac{\exp (cos(\boldsymbol w_i,\boldsymbol f)/\tau)}{\sum_{j=1}^K\exp(\cos(\boldsymbol w_j,\boldsymbol f)/\tau)}
p(y=i∣x)=∑j=1Kexp(cos(wj,f)/τ)exp(cos(wi,f)/τ)
CoOp
CoOp的学习是直接从数据端到端的进行学习的,而多模态预训练模型的参数是frozen的。本工作提出的优化的context有两种策略:一是对所有任务和数据集学习一个公共的prompt;二是对具体的训练任务训练一个具体的prompt。
关于范式,本文也有两种构造方法,一种是放在串的最后,另一种是放在串的前面。
CSC:本文还有一种设计,是每一个类学习一个对应的Context
训练
本文的学习逻辑异常简单,就是直接在分类交叉熵损失上进行训练就行。
代码实现
自己后续稍微跑了一下这个的代码,也搞清楚了这个代码的逻辑。CoOp和CoCoOp都是在作者本身搭建的深度学习实验框架dassl上进行的,所以直接看懂代码难度还挺大(因为封装的比较好)。当然这也不是很有必要,主要是看看这个prompt是怎么学并且怎么用的。首先,这个prompt是包含在一个CustomCLIP类中的,在初始化的时候会根据当前任务的总共类别来初始化相同数量的prompt(如果是共用一个prompt那就只用初始化一个)。然后直接用tokenizer来进行转化。注意,这个学习的过程不是在自然语言空间下学的,而是在tokenized的embedding下进行学习的,所以很难转换回到自然语言的语义下
在本文中,prompts是由ctx进行直接学习的,根据不同的类别进行切分。
CoCoOp:Conditional Prompt Learning
motivation
之前的CoOp方法有一个明显弊端:泛化性能还是较差,在一些未知类中,CoOp的效果反而会下降(即如果训练一个全局的prompt,在遇到训练以外的类时效果会变差)在CoCoOp中,提出了一种基于本身的类别进行自适应生成Context的策略,具体的采用了元学习的方法。
CoCoOp方法提出
CoOp的不足
其实CoCoOp的few shot learning的要求比CoOp还要苛刻一些,CoCoOp的目标是在不完全的类中也能先进行一个自适应,如果类别特别多,完全的覆盖的训练代价也是很高的。这中情况下,CoOp的“Class Specific Context”就没有办法进行实际的应用因为这个方法是预先学习了所有类的prompt,作者也在文中标明了,仅仅跟CoOp中学习所有类公共prompt的方法进行比较。
文章换了个说法,听起来似乎问题更严重了:CoOp对于unseen class有很差的泛化能力!
基本框架
本文借鉴了(一丢丢?)元学习的思想,提出了一个基于自适应学习的Meta Net(但并不是上面的MAML,笔者之前错误的把这两个混淆了)
其实最开始有一个很基本的思路:我直接学习M个神经网络来生成M个token来直接构造prompt不就行了吗?但是这么做有个很麻烦的问题:模型会做得很大,而且训练起来也需要训练这所有的神经网络。
所以提出Meta-Net的一个关键的出发点是节省参数,是一个“轻量化神经网络”。这个网络会用来生成一个附加的conditional token (vector),和context vector结合形成最终的prompt,而这些内容是根据视觉信息学习到的。和CoOp的最大差别“Condition”就体现在利用了视觉信息作为条件。
CoOp的改变可以直接用这两段代码来表示,其余内容和CoOp的逻辑差不多
此时笔者在作者的仓库上进行了一些实验,挺推荐这个仓库的实验框架的,非常的完整,配置好了用的也很顺手。
实验结果
这个结果十分的客观且有趣:
这个实验是挺有意思的,所有的模型都只在训练集中的Base类中进行学习,但是测试的时候,不仅仅在Base中测试,还会在训练集中没有见过的类New中进行测试。这说明了一个问题:CoOp会带来过拟合,CoCoOp也会有过拟合情况,但是影响被大大减小了。
我是这么理解这个现象的,因为CLIP是在大规模的多模态数据集上进行了预训练,所以本身泛化性能就是非常强的。而CoOp学习完了propmt之后,归纳偏置变大了,prompt几乎就是在这一些类上学到的。CoCoOp由于有了图像的信息,并且作为一个bias加到基础的prompt上,就相对灵活一些了,但是因为这个prompt还是在一个不完全的分布下学习到的,所以还是可能有过拟合现象,在部份数据集上效果比CLIP差,整体上也不如CLIP的泛化能力。
综合的来说,这个方法并不是在Base或者是New上有更好的效果,而是在其中间找到了一个平衡,使模型既能够通过简单的fewshot训练就获得很好的性能,有能够不受到过拟合的严重影响
最新研究工作:MaPLe(CVPR 23), ALIGN(NIPS 23)
这两个工作也很全乎,都有代码:
MaPLe的源码
ALIGN的源码
MaPLe:
Motivation & Contribution
对于CoOp和CoCoOp,这种基于prompt Learning来提高具体任务上的性能的方式是值得借鉴的,但是这些方法仅仅是在对文本模态上的prompt进行学习,忽略了CLIP本身多模态的性质,虽然CoCoOp用到了视觉特征来做一个condition,但是这也是不够充分的。MaPLe的想法就来源于做一个视觉语言模态对齐且对称的prompt learning的框架,基于此也取得了一些不错的效果。
如上图所示,MaPLe和标准的但茅台的prompt learning相比,前者的做法是通过text+prompt(tokenized)来和原本的image来一起送到encoder中来做,而现在的做法是同时在两个模态中都加了prompt。MaPLe引入了一种层次结构的prompt来适应不同模态以及进行了多层的prompt添加。
主要的Contribution如下:
- 我们提出了CLIP中的多模态prompt学习,以更好地对齐其视觉-语言表示。这是用于微调CLIP的第一个多模态prompt方法。
- 为了将文本和图像编码器中学习到的prompt联系起来,提出了一个耦合函数,连接两种模态。
- 我们的多模态prompt是在视觉和语言分支的多个transformer块中学习的,这种深度prompt策略能独立地对上下文关系建模,逐步学习两种模式的协同行为,也就是说和以前的单层的prompt不同,多模态prompt是有层次结构的。
方法提出
模型的基本结构如下
这个的架构就和之前的工作差别很大了。直接看图说话的讲,就是首先会学习一个公共的“上下文提示词”,即红色区域,也仅仅有这一部分是可学习的,其他部分全部都是frozen。红色区域不仅仅有一个context prompt,还有一个耦合函数(V-L coupling function),通过文本prompt来获得视觉prompt,从而构建跨模态联系,而且这个函数是多层次的,学习的过程就是学习每一层中的
P
P
P以及两个模态的耦合函数
F
\mathcal F
F,本文发现在多层次构建prompt是必要的。
所以构建了如上的多模态,多层次的的prompt学习框架。构建prompt可以从语言,视觉两个branch来讲。
deep language prompting
在CLIP的语言部分构建prompt ,有
[
_
,
W
i
]
=
L
i
(
[
P
i
−
1
,
W
i
−
1
]
)
[\_,W_i]=\mathcal L_i([P_{i-1},W_{i-1}])
[_,Wi]=Li([Pi−1,Wi−1]),其中 i 取值为1~J
解释一下这些符号,首先
P
i
∈
R
d
l
i
=
1
,
2
,
…
,
b
P^i\in\mathbb R^{d_l}\qquad i=1,2,\dots,b
Pi∈Rdli=1,2,…,b是b个可学习的prompt,输入的embedding是
[
P
1
,
P
2
,
…
,
P
b
,
W
0
]
[P^1,P^2,\dots,P^b,W_0]
[P1,P2,…,Pb,W0],
W
0
=
[
w
1
,
w
2
,
…
,
w
N
]
W_0=[w^1,w^2,\dots,w^N]
W0=[w1,w2,…,wN]是固定的token,总共的深度是J,中括号是连接操作
后续层有(我的上下标没有打错,所以这里还需要梳理一下…):
deep vision prompting
视觉的处理方式如下,其中
E
E
E代表vision branch中的embedding,这里的
L
,
V
\mathcal{L,V}
L,V指的都是在CLIP中的Transformer层,而
P
~
\tilde P
P~代表的是视觉branch上的可学习prompt。
视觉语言耦合
为了将两个模态的信息进行交互,加了一个线性层,这样能够学习到一个模态一致的关系。
关于这一步,原文的解释是“独立的学习两个模态的prompt,缺少V与L分支的协同作用。”
我的理解是:不会让不同模态过度拟合到本身任务的分布上,从而导致泛化能力差
即改写为
实验细节与结果
本方法主要对比CoCoOp有了很明显的全面提升,因为引入了多模态信息和多层次prompt
此外还进行了复杂度的实验,实验发现,不考虑层次,将整个的耦合函数变为共享的函数,不会太大的影响性能,但是能够节省不少参数。CoCoOp由于生成的时候需要再额外通过一个视觉特征和meta Net,推理的速度当batchsize变大的时候会变慢。
week13也继续提示词学习的调研,所以不另开一个文章
PLOT: Prompt learning with optimal transport for vision-language models(ICLR 2023)
自从CoOp出来之后,这个领域的工作开始大量的跟进,在MaPLe前,也已经有了一些不错的新的方法。
motivation
本文最重点的改进是,认为将PL的方法引入到多模态的时候,仅仅用一个prompt来表述一个类是不够的,所以提出了用多个prompt来关联一个类(什么意思呢?)如下入所示,一个类的内容是可以被多句话来进行表示的。
最自然的解决方案是通过将每个prompt与视觉特征分别匹配,直接学习多个prompt。然而,这种解决方案是有问题的,因为这会鼓励所有prompt都更接近于一个单一点,用人话讲就是,我仅有一个视觉特征,没办法学习多个prompt的特征,因为这只有一个点,没办法去做对齐/匹配。然后作者又尝试了添加一些约束来使得学到的prompt的点分散,但这学出来的prompt的效果依旧不好,因为还是将视觉特征视为了一个点,但在motivation中我们会发现,局部的对图像进行描述的时候,关注的更多的是图像的局部信息,也就是说可能是特征的一个子集
为了解决这个问题,提出了PLOT的方法, 其中PL带表“prompt learning”,而OT是一类问题,指Optimal Transport最优传输问题。OT问题熟悉又陌生,因为在week4的笔记中,其实提到过一点有关最优传输算法的问题,其中代表的算法Sinkhorn算法,在多粒度视频信息检索的工作中,也用来进行多粒度的不同维度特征对齐的操作。OT问题中,对于分布之间的距离可以通过采样后用Wasserstein距离来表示。在本文的做法中,视觉的特征和多个的prompt被视为两个离散分布,这样能够做到更加细粒度的跨模态匹配。
具体方法
本文基本的框架是基于CoOp来做的,基本上也是在CLIP上的。
PLOT基本的框架如上,其中,不同prompt对应多个文本模态的特征,而图像的局部信息被编码到一个图像的特征集中。本文做的就是将这两个不同的表示对齐,所以重点就是这个OT应该如何去做。
Optimal Transport
OT距离(wasserstein距离)被广泛用来作为度量两个分布距离。这里用公式的方法表示整个问题,原始的视觉特征维
f
f
f,文本特征为
g
g
g,则总共可以表示为
其中
δ
f
\delta_f
δf表示的是在f点上为1,其余点为0的delta函数,而
u
,
v
u,v
u,v表示的是一个离散的概率向量,所有位的和为1。最终的距离可以写成
其中C为代价矩阵有
C
m
,
n
=
1
−
sim
(
f
m
,
g
n
)
\boldsymbol C_{m,n}=1-\text{sim}(\boldsymbol f_m,\boldsymbol g_n)
Cm,n=1−sim(fm,gn),T为运输计划,这个是需要学习的,能够最小化代价。
下面的约束就是说,T是一个概率矩阵,因为行列和都是概率分布。
解决这个问题,的优化目标转化为
然后就可以用sinkhorn算法来进行求解。这篇工作比较有意思的点是采用的不同尺度的特征通过sinkhorn算法进行匹配的学习策略。
具体学习过程
在CLIP的ViT编码器编码后,会有一个feature set
(
H
×
W
+
1
)
×
C
(H\times W+1)\times C
(H×W+1)×C,令
M
=
H
×
W
M=H\times W
M=H×W,对应的,设定
N
N
N个prompt,这样就会有一个从
M
×
C
M\times C
M×C到
N
×
n
_
c
l
s
N\times n\_cls
N×n_cls的对应关系,分别是图像特征和类名构成的prompt组(每组N个),然后构建相似度矩阵,基于相似度矩阵构建代价矩阵,作为sinkhorn的优化目标:
然后用sinkhorn算法算一个transport矩阵
T
∗
\bf T^*
T∗
Tuning Multi-mode Token-level Prompt Alignment across Modalities (NIPS 2023)
这是领域内最新的研究工作,首先也对之前的若干工作进行了一个总结和阐述,然后综合了前面方法的优劣,提出了一个更加完整的PL的方法
不同的Prompt Learning的分类
上图是一个对于不同的Prompt tuning的方法进行的总结:
TPT:这是在文本模态上面进行学习的一个方法,代表有CoOp和CoCoOp
VPT:这篇没有细讲,这个工作是将图像看做一个patch序列,然后通过visual prompts来增强图像表征,同样的可以表示为“ X X X X { i m a g e } X\ X\ X\ X\ \{image\} X X X X {image}”。这种的方法可以用在更多的下游任务如视频理解,域适应(迁移学习),图像分割等。
MPT:是将TPT和VPT结合的代表,MaPLe这个工作就是MPT。
PLOT:采用最优传输的方法来解决,下文会从prompt learning的角度进行更加全面的描述
motivation & contribution
在TPT,VPT和MPT的工作中,都采用的是“single-mode prompt”,也就是说是一个prompt对应一个实例,但是PLOT提出了这是不够的,而在multi-modal的框架中,这个问题更加显著,因为视觉和文本的概念以及其对齐都需要被推理。此外,如果仅仅用全局的特征来表示图像和标签会不够理想,因为会导致局部特征的缺失,得到的是次优的效果。概括的将,motivation和PLOT是类似的(1.局部;2.多个prompt),主要是还受到了MPT工作即MaPLe的启发,将OT方法引入到了多模态的prompt learning/tuning中。
本文将视觉的prompt空间看做一个离散分布 P P P,然后将标签看做在文本模态的prompt空间的离散分布 Q Q Q,然后就可以在上面去做一个OT。和PLOT不同的是,PLOT只做了token-level的OT,而本文同时做了prompt-level的OT,也就是成了一个多层次的OT学习方法
总结下contribution有下:
- 提出了多种模式的token-level对齐的框架,其中会学习多个prompt,此外本工作可以在很多先前的工作基础上完成
- 讲prompt tuning的任务给构建成了一个分布对齐的任务,并且通过prompt和token level的OT来实现这个任务
- 不仅仅本工作应用在了few-shot分类任务上,还在数据集迁移学习以及域泛化上进行了实验。
方法提出
总述
总的来说,本文的方法主要还是在OT层面上更进一步,通过一个新的对齐模块(multi-mode token-level alignment module),可以将本文的方法应用到现有的大部分工作上,因为这个对齐的损失是加在原有loss上的
这个和之前的PLOT比较像,PLOT就是可以直接在CoOp上实现的,因为相当于在原本的CLIP的返回logits上额外加了一个新的loss
上图是PLOT的核心代码,可以看到主要就是通过一个新的相似性矩阵sim_op来进行一个新的logits的学习,从而是的原本的logits效果更好。
具体来说,首先引入了prompt level 的OT,和PLOT一样,将图像和label当做视觉和图像空间上的离散分布,其中视觉分布
P
P
P是由
M
M
M个token来构成,图像分布
Q
Q
Q是由
N
N
N个token构成。这样的话,prompt层面的的OT是将这两个分布学习一个最优传输的关系,从而进行对齐。此外,本工作在此基础上也进行了层级关系的学习,在此首先可以看一下MPT中的每一层output的表示有
其中
e
,
v
,
t
,
w
\boldsymbol e,\boldsymbol v, \boldsymbol t, \boldsymbol w
e,v,t,w 分别表示视觉patch嵌入,视觉prompt,文本prompt和文本嵌入。文本的下标
k
k
k表示这个是第k类的内容。第
n
n
n层的第
k
k
k文本prompt输出有
b
+
k
l
b+k_l
b+kl个token,相应的,视觉上也有
b
+
O
b+\mathcal O
b+O个“token”(patch embedding)。这在Transformer结构中,表示的都是局部的信息,所以如果能在token的层面(指深度的token)进行对齐,就能达到细粒度对齐的效果,所以有了token-level OT。更具体的,
P
P
P分布中的第
m
m
m个点以及
Q
Q
Q分布中的第
n
n
n个点本身又会被建模成离散分布,并且通过OT的方法来进行对齐。最后学到的Transport矩阵可以一方面捕获prompt的信息,另一方面捕获token的信息。
具体方法
首先和PLOT一样,学得的prompt的两个特征集可以构建两个离散分布,然后用sinkhorn算法来解出一个prompt-level的OT的传输矩阵。视觉prompts为
{
v
m
}
m
=
1
M
\{\boldsymbol v^m\}_{m=1}^M
{vm}m=1M,文本prompts为
{
t
n
}
n
=
1
N
\{\boldsymbol t^n\}_{n=1}^N
{tn}n=1N,长度均为b,维度分别为
d
v
,
d
l
d_v,d_l
dv,dl
于是可以构造如下离散分布:
其中
x
m
\boldsymbol x_m
xm和
y
n
\boldsymbol y_n
yn视觉或文本模态的第m或n个输出(多prompts对应的输出),于是可以在多prompts的层面上通过OT问题进行对齐的方法,进行更全面的相似性度量,而加速匹配。
这个式子估计的是从分布
P
P
P到
Q
Q
Q的传输矩阵,可以用Sinkhorn算法求解。
T
m
n
T_{mn}
Tmn代表着第m个视觉prompt到第n个文本prompt的一个传输概率。也就是说,相当于进行了不同数量不同模态不同维度的对齐。这个的学习效果,很大一部分取决于代价矩阵
C
\bf C
C的构建。
对于token-level对齐,首先先将原本的输出做成一个离散分布,有
其中
r
r
r和
s
s
s均为输出层的token。然后同样可以构成一个OT:
上述公式可以计算一个从visual patch到 token feature的一个传输矩阵。这直接将两个不同层次的特征直接整合到一起,构建了一个新的代价矩阵。其中又有
C
^
j
l
=
1
−
sim
(
r
j
,
s
l
)
\hat{C}_{jl}=1-\text{sim}(\boldsymbol r_j,\boldsymbol s_l)
C^jl=1−sim(rj,sl)于是可以构建如下代价矩阵:
这样就将两个尺度的特征进行了整合,注意每个prompt是对应多个尺度的,所以也就是说,prompt的OT用的不再是简单的prompt之间的similarity计算,还考量了不同模态prompt之间本身的对齐效果,这是一个token-level的对齐,实际上,先进行了token-level的对齐,然后再进行了prompt-level的对齐,这是一个套接的关系。