【学习周记week15】基于知识引导的prompt learning方法

前言

上周因为准备毕设以及ECCV开题所以gap了一周,主要做了一些相关的代码阅读和复现(还有摸鱼)。本周会继续进行Multi-Modal Prompt Engineering领域的调研。

什么是Knowledge Guidance

其实这种guidance的做法不是第一次见到了,在之前做生成任务的调研时,文生图就很常用guidance的方法,来将生成器的生成内容引导向一个正确的目标。在文生图中,有通过classifier+gradient的方法来进行约束,还有一些其他的做法不赘述,也就是说,可以通过一个先验的方向来对后续的学习进行一个引导,这当然也可以用于提示词学习!

Prompt-aligned Gradient for Prompt Tuning(ICCV 23/CORR 22)

这是一个2022年的工作,但是在2023年又中了ICCV

Motivation

本文的Motivation主要还是基于CoOp来的,主要有两点。首先看ab图,CoOp存在随着epoch增加严重掉点的问题(本质上应该还是过拟合),然后看cd图,如果不进行数据增强或提供足够的样本(更多shots),CoOp可能无法改进CLIP

在这里插入图片描述
此外,还有一个有趣的Motivation是,通过Grad-CAM来可视化时发现,经过CoOp微调过的prompt会导致模型更关注背景而不是前景对象,这和分类任务是背道而驰的(值得注意的是,这篇工作已经默认在分类任务下去做了)
在这里插入图片描述
也就是上图中的(b)和(c)的区别。在CLIP中,本身更集中在foreground上的注意力被分散了。

这会导致过拟合现象严重,而且在样本数特别少的时候过拟合的更严重(因为分布偏离的更多)

Contribution

本文提出了一种基于prompt对齐的梯度的引导方法,叫做ProGrad,来应对prompt学习中添加的不正确偏置。ProGrad的主旨是在tuning的过程中进行一种正则化,来确保这一步的tuning不应该和原本的知识产生冲突。对于“原本的知识”,本文给出的例子是zero-shot CLIP给出的预测。具体的,本文通过一个KL散度来将ZSCLIP和本身正在做prompt tuning的预测进行对齐,学习到的方向叫做一般方向(general direction),还有一个是基于交叉熵的方向,成为域特殊方向(domain-specific direction),然后将域特殊方向解构到一个垂直的向量 G ⊥ G_{\perp} G和一个平行的向量 G ∥ G_\Vert G。垂直向量并不能覆盖原本的向量,而平行向量要么是和一般方向是相同的,要么是和一般方向相反的。如上面的gradcam图显示,不同方向的的direction都能够起到正则化的作用,而综合起来能够有更好的效果。

综合的Contribution有以下三点:

  1. ProGrad能够做到很好的精度效果提升与CoOp比
  2. 能够提升泛化能力与CoCoOp比
  3. 在域泛化下有很好的效果

具体方法

这部分草稿被删了,现在省略的重新讲一下。最基本的思路就是,将学习的时候的梯度进行结构,首先是由CoOp进行学习的域特殊方向,这个方向是能够加强其在当前数据下的精度的优化方向,但是这可能导致过拟合,所以用一个一般普通的prompt和zero-shot CLIP 学习的logits和原本的logits计算一个KL散度,这个KL散度回传的梯度作为一般方向。公式化的表述如下:
首先有交叉熵损失:
在这里插入图片描述
和两个模型的logits的KL散度:
在这里插入图片描述
然后将这两个损失转化成相应的回传的梯度,前者对应 G g G_g Gg,后者对应 G d G_d Gd
然后将两个梯度的方向进行比较,这里会分成两种情况,可以参考下图共同理解。第一种情况是夹角小于九十度,也就是余弦相似度为正,此时直接用 G g G_g Gg即CoOp的交叉熵损失进行优化。第二种情况下则是将梯度映射到和KL散度垂直的角度,这样可以保证优化方向不会在冲突的方向上进行。
在这里插入图片描述
公式化描述则为

在这里插入图片描述
注意其中的 λ \lambda λ使得这个修正的程度可调整, λ = 1 \lambda=1 λ=1代表映射到垂直方向梯度, λ = 0 \lambda=0 λ=0则本方法退化到CoOp

实验部分

主要的核心代码是梯度回传部分

def prograd_backward_and_update(
        self, loss_a, loss_b, lambda_=1, names=None
    ):
        # loss_b not increase is okay
        # loss_a has to decline
        self.model_zero_grad(names)
        # get name of the model parameters
        names = self.get_model_names(names)
        # backward loss_a
        self.detect_anomaly(loss_b)
        loss_b.backward(retain_graph=True)
        # normalize gradient
        b_grads = []
        for name in names:
            for p in self._models[name].parameters():
                b_grads.append(p.grad.clone())

        # optimizer don't step
        for name in names:
            self._optims[name].zero_grad()

        # backward loss_a
        self.detect_anomaly(loss_a)
        loss_a.backward()
        for name in names:
            for p, b_grad in zip(self._models[name].parameters(), b_grads):
                # calculate cosine distance
                b_grad_norm = b_grad / torch.linalg.norm(b_grad)
                a_grad = p.grad.clone()
                a_grad_norm = a_grad / torch.linalg.norm(a_grad)

                if torch.dot(a_grad_norm.flatten(), b_grad_norm.flatten()) < 0:
                    p.grad = a_grad - lambda_ * torch.dot(
                        a_grad.flatten(), b_grad_norm.flatten()
                    ) * b_grad_norm

        # optimizer
        for name in names:
            self._optims[name].step()


kgCoOp

这篇工作的motivation和ProGrad是接近的,主要的工作会在这两者的对比下进行描述

ProGrad中的不足

prograd的方法有个核心的点,是对于冲突的部分采取了丢弃的处理,这会使得在学习的过程中会产生很多无用的信息(其实我个人在理解ProGrad的时候,觉得最主要的问题是,ProGrad的学习是通过和预先构建好prompt的CLIP去做KL散度的,那和coop的motivation提到的一样,既然先验的prompt会导致学习的梯度方向变化,那prompt的设计对于结果是很敏感的,如果是一个很差的prompt,会导致其效果甚至整体变差。
于是KgCoOp的核心思想就是不舍弃原本知识的情况下确保和general knowledge不偏离太多

提出的方法

首先基本的设计还是在CoOp的架构上来进行的,所以这部分内容不再赘述。而在KgCoOp中,主要也是解决了CoOp的过拟合问题。该部分首先先详细的讲了一下CoOp的过拟合现象,在base2new问题上效果很差。然后提出了KgCoOp,基本的思路和ProGrad是类似的
在这里插入图片描述
做法出奇的简单,就是在原本的交叉熵损失基础上,加入了一个新的l2范数损失,有


最终有 L = L c e + λ L k g \mathcal L=\mathcal L_{ce}+\lambda\mathcal L_{kg} L=Lce+λLkg
注意,这里只是对文本模态的编码,基于zsclip和实际文本输出进行的l2损失。
在kgcoop代码中,其实现直接在CLIP中实现:

# prompt learner: promptsa之后用于构建old_text_features
        #prompts_ = [prompt_prefix + " " + name + "." for name in classnames]        
        temp = CUSTOM_TEMPLATES[cfg.DATASET.NAME]
        prompts_ = [temp.format(c.replace("_", " ")) for c in classnames]       # choose prompts from temp
        print(f"Prompts: {prompts_}")
        prompts_ = torch.cat([clip.tokenize(p) for p in prompts_])
        prompts_ = prompts_.cuda()

# CustomCLIP: 最终的输出是logits,但是kgCoOp会对这个logits的计算进行修正
    def forward(self, image):
        prompts = self.prompt_learner()
        image_features = self.image_encoder(image.type(self.dtype))

        tokenized_prompts = self.tokenized_prompts
        text_features = self.text_encoder(prompts, tokenized_prompts) 
        text_features_old = self.ori_embedding


        image_features = image_features / image_features.norm(dim=-1, keepdim=True)
        text_features = text_features / text_features.norm(dim=-1, keepdim=True)
        logit_scale = self.logit_scale.exp()

        logits = logit_scale * image_features @ text_features.t()

        cos = torch.nn.CosineSimilarity(dim=1,eps=1e-07)
        text_features_old = text_features_old / text_features_old.norm(dim=-1, keepdim=True)
        score = cos(text_features,text_features_old)
        # calculate the mean of the cosine similarity 
        # between text_feature and text_feature_old
        score = 1.0-torch.mean(score)

        return logits, score
# kgcoop.forward_backward(): 基于logits和score进行整体计算
	        output,score = self.model(image)
            loss = F.cross_entropy(output, label)+self.w*score
            self.model_backward_and_update(loss)

Self-regulating Prompts: Foundational Model Adaptation without Forgetting (ICCV23)

abstract

这一篇工作是在MaPLe基础上进行推进的,但是整个方法做的就比较复杂了。基本的问题还是一致的,即模型在提高特定任务性能的情况下不损失泛化性。基于此本文提出的自正则化的prompt,本方法主要分为了三个分支

a. 基于Mutual Agreement Maximization的正则化:通过最大化prompt的特征和frozen VLM的特征来实现
b. 基于self-ensemble(自集成)的方法:采用一个基于加权的提示词聚合方法来实现
c. 通过文本多样性来实现正则化:这个Motivation很有意思,作者注意到标签和图像是不对等的,因为一个分类有很多的图像与之对应,但是一个图像不会有很多的label(分类任务中),所以在文本label会欠缺多样性

Contribution

  • 通过self-regularization的方法防止了过拟合,提升了泛化性。通过最大化mutual-agreement,同时学习到了task-specific和任务无关的泛化知识(这里我觉得很奇怪,我不认为用于微调的数据能够让模型学到更多的“泛化知识”,而是能够在当前领域下能够不集中在)
  • 提出了一种基于加权的self-ensembling的策略,来捕捉互补的特征,同样是能够提高泛化性能

相关工作

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值