MaPLe:Multi-model Prompt Learning

多模态提示学习

  • 来源:[CVPR 2023] Official repository of paper titled “MaPLe: Multi-modal Prompt Learning”.
  • 作者是印度人
  • 代码:https://github.com/muzairkhattak/multimodal-prompt-learning

背景

预训练的视觉语言模型如CLIP在下游任务中表现出优异的泛化能力,然而,它们对输入文本的Prompt非常敏感,需要仔细选择Prompt templates提示模板才能表现良好。

受到自然语言处理文献的启发,最近的CLIP适应方法将提示作为文本输入来微调CLIP应对下游任务。在CLIP的一个分支(文本-图像)中使用Prompt是次优的,因为不允许在下游任务中动态调整两个表示空间。

在少样本的情况下对模型进行微调是不现实的,甚至会导致模型以往预训练的信息。Prompt方法可以避免手动调整templates,并不需要调整原始参数。这对于CLIP是未被充分研究的课题。

motivation

来源于CLIP的多模态特性,文本和图像编码器共存的双塔结构,两者都对正确对齐视觉-语言模态起作用。仅针对CLIP中的文本模态进行Prompt Learning是不够的。

具体实施方法

image-20260327164347205

如上图,作者认为,以往方法中只给Text分支Prompts是不够的,只有通过一个函数,把生成的Prompt同时给到两个分支,才是合理的,输入的Prompt还是原本的文字信息。其实不是什么很复杂的东西,作者写的很复杂。

image-20260327164656008

MaPLe (Multi-modal Prompt Learning) 是一种针对视觉-语言模型(如CLIP)的高效微调方法,旨在通过多模态提示学习来更好地适应下游任务。

它的核心思想是:同时在视觉和语言两个分支中引入可学习的提示(Prompts),并通过一个耦合函数将它们紧密联系起来,以实现两种模态的协同优化。

🎯 动机:为什么需要 MaPLe?

以CLIP为代表的视觉-语言模型虽然在下游任务上展现出强大的泛化能力,但其性能高度依赖于输入文本提示(Prompt)的设计。

  • 现有方法的局限:早期的提示学习方法(如CoOp)仅在文本分支引入可学习的参数来替代人工设计的提示。然而,作者认为这种单模态的调整是次优的,因为它无法灵活地动态调整视觉和语言两个表示空间,限制了模态间的交互和对齐。
  • MaPLe的解决方案:为了让模型在下游任务上达到最佳对齐,提示学习应该更完整地适应整个模型。因此,MaPLe提出同时在视觉和语言分支进行提示学习,并通过一个耦合机制确保两者协同工作。

⚙️ 方法:MaPLe 如何工作?

MaPLe 的方法可以分解为三个关键部分:

  1. 深度语言提示 (Deep Language Prompting) 在文本编码器的前 J 层Transformer模块中,分别引入可学习的提示标记(learnable tokens)。这些提示会与原始的文本输入嵌入(如 “a photo of a [CLASS]”)拼接在一起,逐层输入。这使得模型能够学习到更深层次的、与上下文相关的语言特征。
  2. 深度视觉提示 (Deep Vision Prompting) 与语言分支类似,在图像编码器的前 J 层Transformer模块中,也分别引入可学习的提示标记。这些提示会与图像的块嵌入(patch embeddings)拼接,从而在视觉特征提取的早期阶段就注入可学习的上下文信息。
  3. 视觉-语言提示耦合 (Vision-Language Prompt Coupling) 这是 MaPLe 的核心创新。为了让两个分支的提示产生协同效应,而不是独立学习,作者设计了一个耦合函数 (Coupling Function)
    • 具体实现:该函数通常是一个简单的线性层。它将语言分支的提示作为输入,通过映射来生成视觉分支的提示。
    • 作用:这个耦合函数充当了两种模态之间的桥梁,使得梯度可以在视觉和语言提示之间相互传播,从而鼓励它们共同学习,实现对两种模态表示的协同调整。

在整个微调过程中,只有这些可学习的提示参数和耦合函数会被更新,而预训练的CLIP模型主体参数则被冻结,这使其成为一种参数高效的微调方法。

具体代码实现片段

image-20260330114501971

这一张图看的比较清晰一些。可见,原CLIP的基座模型Text Encoder以及Image Encoder均为不可学习参数,不参与参数优化步骤。只有Prompts块中的参数进行学习优化。

1
2
3
prompts, shared_ctx, deep_compound_prompts_text, deep_compound_prompts_vision = self.prompt_learner()
text_features = self.text_encoder(prompts, tokenized_prompts, deep_compound_prompts_text)
image_features = self.image_encoder(image.type(self.dtype), shared_ctx, deep_compound_prompts_vision)

通过代码段理解这个算法,就是

  • 先通过prompt_learner函数得到4个Prompt分量。

  • TEXT部分原输入tokenized_prompts,加入两个输入tokenized_prompts, deep_compound_prompts_text

  • VISION部分原输入image,加入两个输入shared_ctx, deep_compound_prompts_vision

  • 只不过是在过程中进行了一些堆叠,进行融合了而已。从代码看,只有输入的这一块有Prompts输入,中间部分的Text Encoder以及Image Encoder之间的Prompts是没有的。

  • Coupling模块似乎没有找到,通过self.prompt_learner()函数同时经过某些操作一起生成的这个步骤就是Coupling。强制了视觉提示必须包含语言提示的信息。

image-20260330144743615