资讯详情

资讯详情

一致性引导提示学习:提升CLIP下游泛化与少样本适配

视觉-语言模型这两年几乎成了多模态应用的默认底座但真到落地阶段绝大多数人卡在同一个地方模型本身够强可适配到自己的小数据集时效果上不去全量微调又烧不起卡。提示学习prompt learning本来是破解这个困局最轻的路子——只在文本侧学几个连续向量几十万参数就能把CLIP拉到下游任务上。可复现过CoOp一类方法的人都清楚基类准确率刷得很漂亮一换到新类或新数据集就明显掉队模型像是把训练集背答案背下来了。ICLR 2024上围绕**一致性引导的提示学习consistency-guided prompt learning**的一系列工作核心就是把一致性当成正则项塞进提示学习的训练目标里专门治这个过拟合基类的毛病。这篇文章不打算复述论文而是从我实际复现和调参的角度把这套方法的动机、三条技术路径、损失函数的写法、超参选择的经验以及踩过的坑掰开揉碎讲清楚。适合已经跑通过CoOp或CoCoOp、想进一步提升提示泛化能力的中级读者也适合刚入门多模态、想搞清楚提示学习为什么看着简单其实有坑的朋友。1. CLIP的提示学习为什么会在下游任务上翻车理解一致性引导之前得先把提示学习的问题定位准。很多教程只讲CoOp比手工prompt好却不讲它为什么在某些场景下反而不如手工prompt结果复现的人一脸懵。1.1 CoOp的甜与苦基类越准新类越慌CoOp的思路很直接手工prompta photo of a [CLASS]里的词是人挑的换数据集就不一定合适那我们把这部分上下文换成可学习的连续向量让模型自己在基类数据上学出一套最适配的上下文。训练目标就是标准的交叉熵图像编码器冻结只更新这几十个向量和类别词嵌入。在基类上CoOp的准确率通常能比手工prompt高出几个点few-shot比如16 shot下提升尤其明显。问题出在评测协议上——真正衡量泛化的方式是基类训练、基类新类联合测试。这时候你会发现一个很典型的现象基类准确率涨了新类准确率反而掉了两者的调和平均harmonic mean可能还不如手工prompt。这个现象我最早在复现时就吃过亏。当时用一个跨域分类Demo做验证训练集里只有猫狗鸟几类测试集加进了狐狸和兔子结果模型把所有长耳朵小动物都往鸟或猫上归。这不是bug是提示学习在少量基类上把上下文向量过拟合到了基类的视觉分布上。1.2 过拟合的根因prompt成了记忆基类的私钥为什么几个连续向量就能过拟合关键在于可学习参数太少、监督信号太单一。上下文向量是全局共享的所有基类图像都要通过它映射到文本空间模型会倾向于学出一组对基类判别性极强、但对分布外样本泛化差的向量。它就像一把只配了基类锁芯的钥匙换一把锁就插不进去。具体表现有两层。第一层是类别耦合基类之间的相似度关系被硬编码进prompt新类落在基类间隙里时就无法正确落位。第二层是领域偏移放大如果新类数据集和基类数据集在颜色、背景、拍摄角度上有差异prompt学到的偏置会被进一步放大性能断崖式下跌。更麻烦的是prompt学习通常只训50个epoch左右参数又少很多人觉得这么点参数不至于过拟合吧于是忽视了这个问题。实测下来恰恰是这种低参数、强监督的配置最容易把基类特征焊死在prompt里。1.3 一致性视角为什么约束比多加数据更划算治过拟合的常规思路是加数据、加增强、加正则。但这些在提示学习场景里性价比不高加数据要标注成本加增强在图像侧容易破坏语义加正则比如weight decay对prompt向量这种低维参数效果有限。一致性约束提供了另一条路不增加额外标注只要求模型对同一个语义的不同视角给出一致的预测。这里的不同视角可以是同一张图的不同增强、可以是图像侧和文本侧的表示、也可以是可学习prompt和手工prompt的预测分布。一旦引入这种约束模型就没法只盯着基类的判别边界而要维持一个在多种视角下都稳定的决策面泛化能力自然被逼出来。从信息论角度粗略理解一致性项相当于给损失函数加了一条流形平滑约束把基类周围的特征空间也纳入优化减少了模型在基类上的尖锐拟合。这也是为什么一致性引导在few-shot1/2/4/8/16 shot设置下收益最明显——样本少的时候过拟合最严重约束的价值最大。2. 一致性引导的三条实现路径一致性是个很宽的词落到提示学习上至少有三种主流实现方式它们不是互斥的ICLR 2024上不少工作会把两三种组合使用。理解每条路径解决什么、代价多少是选型的前提。2.1 视图级一致性让同一张图的两种增强殊途同归最直观的做法是对同一张图像做两次不同的数据增强分别送进图像编码器再各自和同一个文本prompt算相似度得到两个预测分布。然后强制这两个分布一致通常用对称KL散度或JS散度。这条路径的思想来源是半监督学习里的一致性正则consistency regularization本质是局部平滑假设输入的小扰动不应该改变语义预测。用在提示学习上有两个好处一是完全无监督不需要额外标签二是直接作用于预测层约束的是决策边界而不仅是特征。但要注意增强的强度。如果增强太强比如大幅裁剪、强颜色抖动两张图语义都变了强行一致反而引入噪声增强太弱又起不到平滑作用。我实测下来随机裁剪加轻度颜色抖动是个比较稳的组合强CutMix一类的做法在few-shot下很容易崩。2.2 模态级一致性图像prompt和文本prompt的握手CLIP是双塔结构图像侧和文本侧各自编码。传统的CoOp只动文本侧的prompt图像编码器完全冻结。模态级一致性主张两侧都要有可学习的prompt并且两侧的表示要对齐。具体做法通常是给图像编码器的浅层也插入可学习的prompt token类似视觉prompt然后约束图像侧prompt学到的表示和文本侧prompt学到的表示在共享空间里保持一致性——可以用对比损失也可以用简单的MSE对齐。这个思路在多模态提示学习里叫深度提示或双边提示。好处很明显只动文本侧模型的视觉表征是死的你无法让视觉侧也去适应下游任务两侧联动后视觉和语言的对齐被放到下游数据上再校准一遍few-shot下的判别力更强。代价是参数量翻倍虽然仍然很小训练也更慢而且两边的prompt层数、位置需要实验确定浅层还是深层、插几层都有讲究。2.3 prompt级一致性手工先验给可学习prompt当锚这条路径我最推荐新手先上手保留一个手工prompt比如a photo of a [CLASS]作为教师让可学习prompt学生去模仿它的预测分布。损失项就是标准的知识蒸馏KL散度用温度τ软化两边的logits让学生分布逼近教师分布。它的直觉是手工prompt虽然判别力不是最优但它是人类先验泛化性好、不容易过拟合基类。可学习prompt判别力强但容易偏用蒸馏把它拉向手工先验相当于给过拟合加了一个锚点。这比视图一致性更直接、更稳定因为它约束的不是输入扰动不变性而是不要偏离常识太远。工程上有个小技巧教师分支的前向可以缓存起来因为手工prompt和冻结的图像编码器都不变每个epoch只算学生分支能省不少算力。另外温度τ不要设太小0.5以下容易退化成硬标签蒸馏也不要太大5以上梯度太弱1到3之间是比较常见的区间。三种路径的对比关系整理如下方便选型时做取舍路径监督来源参数量影响主要解决的问题实现难度视图级一致性无监督增强无决策边界过拟合低模态级一致性有监督对齐损失约翻倍视觉表征不适配下游中prompt级一致性手工先验蒸馏无教师冻结偏离人类常识、基类偏置低实际工作里视图级和prompt级组合是最常见也最容易复现的方案模态级更适合算力充足、追求极限性能的场景。3. 把一致性写进损失函数公式与代码光知道路径不够落地时最容易出问题的是损失项的权重、温度、计算时机。这一节把总损失结构、各分项写法和代码骨架摊开讲。3.1 总损失的结构一致性引导提示学习的目标函数一般是主任务损失 若干一致性正则项的加权和$$ \mathcal{L}{total} \mathcal{L}{CE} \lambda_{view}\mathcal{L}{view} \lambda{prompt}\mathcal{L}{prompt} \lambda{modal}\mathcal{L}_{modal} $$其中$\mathcal{L}_{CE}$还是标准的图像特征与文本类别特征的相似度交叉熵作用在可学习prompt上后面三项分别是视图一致性、prompt蒸馏和模态一致性系数是超参。关键在于主任务损失负责判别力一致性项负责泛化两者是竞争关系权重没调好就会出现基类不涨、新类也不涨的尴尬局面。建议采用先只训主任务、再加一致性项的两阶段或warmup策略。一开始就让一致性项满权重介入模型连基本判别都没学会一致性约束就成了无效噪声。我自己的做法是前10个epoch只跑$\mathcal{L}_{CE}$之后按epoch线性加入一致性项收敛稳定性明显更好。3.2 视图一致性的实现细节与温度系数视图一致性作用在两个预测分布之间。设同一图像的两个增强视图$x_i$和$\tilde{x}_i$经过图像编码器得到特征再与文本类别嵌入算余弦相似度并缩放得到logits$z_i$和$\tilde{z}_i$分别softmax得到$p_i$和$\tilde{p}_i$。对称KL写法是$$ \mathcal{L}{view} \frac{1}{2N}\sum{i1}^{N}\left[\text{KL}(p_i | \tilde{p}_i) \text{KL}(\tilde{p}_i | p_i)\right] $$这里有个容易忽略的点是否对增强视图也做梯度。如果两个视图都带梯度相当于两倍显存更省的做法是让其中一个视图走no-grad分支充当教师只对另一个分支求梯度这就是所谓的非对称一致性。实测非对称版本训练更快、显存更省性能差距通常在0.3个点以内值得优先考虑。温度系数方面视图一致性里softmax前的缩放通常沿用CLIP的logit scale可学习但一致性项内部建议再额外加一个温度让分布平滑一点否则两个很尖的分布算KL梯度容易爆炸。我一般把一致性项内部温度设在2左右跟蒸馏的温度分开处理。3.3 prompt蒸馏的KL散度写法prompt级一致性本质是蒸馏设手工prompt分支的软化logits为$z^t/\tau$可学习prompt分支为$z^s/\tau$教师和学生都做softmax后算KL最后乘$\tau^2$恢梯度尺度$$ \mathcal{L}_{prompt} \tau^2 \cdot \text{KL}\left(\text{softmax}(z^t/\tau) | \text{softmax}(z^s/\tau)\right) $$工程细节有两个。一是教师分支必须no-grad否则会污染冻结的图像编码器虽然理论上不动但有人会误开requires_grad。二是类别词嵌入是否共享很多实现里手工prompt和可学习prompt共享同一套CLIP类别词嵌入这样蒸馏的是上下文的差异而不是类别词的差异更纯粹。如果分开要保证两边的类别顺序严格对齐否则KL就是在瞎算。3.4 代码骨架下面是一段简化的PyTorch实现骨架只保留核心逻辑方便对照理解import torch import torch.nn.functional as F def consistency_loss(student_logits, teacher_logits, tau2.0): # 蒸馏/一致性KL教师不带梯度 p_t F.softmax(teacher_logits / tau, dim-1) log_p_s F.log_softmax(student_logits / tau, dim-1) kl F.kl_div(log_p_s, p_t, reductionbatchmean) return tau * tau * kl def train_step(model, images, labels, lam_view, lam_prompt, tau2.0): # 1) 主任务可学习prompt 一个增强视图 logits_s model(images, use_learnableTrue) loss_ce F.cross_entropy(logits_s, labels) # 2) 视图一致性另一个增强视图走no-grad当教师 with torch.no_grad(): logits_aug model(augment(images), use_learnableTrue) loss_view consistency_loss(logits_s, logits_aug, tautau) # 3) prompt级一致性手工prompt当教师 with torch.no_grad(): logits_hand model(images, use_handcraftedTrue) loss_prompt consistency_loss(logits_s, logits_hand, tautau) loss loss_ce lam_view * loss_view lam_prompt * loss_prompt return loss这段骨架里augment和use_handcrafted是占位实际实现要在模型前向里区分两条文本分支。注意教师分支都在torch.no_grad()里这是省显存、防止梯度泄漏的关键。4. 训练配置与超参数哪些值不能乱调提示学习的超参不算多但每一个都对结果影响不小。一致性引导又把超参空间扩大了一层多了几个λ和温度没经验的话很容易调崩。4.1 数据集与基类/新类划分评测提示学习泛化能力标准协议是base-to-novel generalization一个数据集按类别划分为基类和新类训练只用基类样本测试时分基类、新类、调和平均三个指标报告。常见数据集划分有的人工设定也有的按类别名聚类自动分。无论哪种划分后要保证基类和新类在语义上不能太近否则测不出泛化差异也不能太远否则指标没意义。few-shot设置是评估重点通常报1、2、4、8、16 shot。shot数越低一致性引导的收益越明显shot高了以后基类数据足够多过拟合本身缓解一致性项的边际收益下降甚至可能因为约束过强拖慢收敛。所以我在16 shot以上时会把λ调小一半左右。4.2 优化器、学习率与epoch提示学习只训练prompt参数和可选的类别词嵌入优化器常用SGD或AdamW。学习率不能按全量微调的量级来通常是1e-3到4e-3prompt参数用大一点、类别词嵌入用十分之一左右。如果类别词嵌入也参与训练学习率太高会把预训练的语义空间拉崩。epoch数跟shot数挂钩。CoOp原文建议16 shot训50个epoch数据越少训得越多。加入一致性项后因为多了正则约束收敛会稍慢可以适当加5到10个epoch但不宜过多否则一致性项反而把模型拉向过度平滑判别力下降。一个实操细节余弦退火学习率配合warmup在一致性引导里比固定学习率稳定得多因为一致性项的梯度在训练早期方差较大。warmup步数可以设总步数的10%左右。4.3 一致性权重λ的调参经验这是最需要经验的部分。我踩过的最大坑就是λ设大了看到训练损失一直降但验证准确率不动排查半天发现是模型被一致性拉成了一团均匀分布。经验值如下表仅供参考具体还要看数据集和shot数超参建议范围说明λ_view0.5 ~ 1.0视图一致性shot低时取大值λ_prompt0.1 ~ 0.5蒸馏项太大易主导训练λ_modal0.05 ~ 0.2双边提示时用注意两侧对齐蒸馏温度τ1.0 ~ 3.0太小退化成硬标签warmup比例10% ~ 20%前期只训主任务调参顺序建议先固定λ_prompt0.2、λ_view0.5跑通看基类和新类是否都比不加一致性时更好如果基类掉了说明一致性太强先降λ如果新类没涨说明一致性没起作用可以试着提高λ_view或提高蒸馏温度。别一次动多个参数不然根本不知道是谁的功劳。5. 实验复现与踩坑记录从基类到新类的泛化落差理论和配置讲完真正有价值的是复现时那些文档里不会写的东西。这一节我把模拟项目里踩过的坑原样呈现包括排查链路。5.1 few-shot下的复现基准我在一个跨域分类Demo上复现时用16 shot设置基线是手工prompt和CoOp。结果手工prompt基类大概60出头、新类58左右调和平均比较均衡CoOp基类能到68新类掉到50调和平均反而更低加上视图一致性和prompt蒸馏后基类66、新类57调和平均反超手工prompt。这个结果说明一致性引导不是全面提升而是用一点基类准确率换新类泛化。如果你的下游任务基类和新类分布接近、不需要泛化那一致性引导可能不适合你加了反而降基类。这一点在论文里通常一笔带过但选型时必须想清楚。5.2 泛化测试的三个维度只测base-to-novel还不够。我通常额外测三个维度一是跨数据集基类一个数据集、测试换另一个相似但不同的数据集二是域偏移同类别、不同拍摄风格三是类名扰动类别名换成同义词。这三个维度最考验提示的鲁棒性也是最容易暴露一致性约束是否真起作用的地方。实测下来视图一致性对域偏移帮助最大因为它的平滑假设天然抗扰动prompt蒸馏对类名扰动帮助最大人类先验本身就抗同义词模态一致性对跨数据集帮助略好。这也是为什么组合使用往往优于单一路径。5.3 我踩过的四个坑坑一教师分支忘了no-grad。一开始我只对图像编码器设了requires_gradFalse但手工prompt分支的logits还是带着计算图结果显存直接翻倍训练速度砍半。排查时用torch.cuda.memory_allocated对比才发现。加torch.no_grad()后恢复正常。坑二两路类别顺序不一致。手工prompt和可学习prompt的类别词嵌入是分别构造的我一时手快把类别列表顺序搞反了KL散度算出来一直是0附近模型完全不学。排查了整整一下午最后打印两边logits的形状和argmax才发现类别对不上。这个坑很隐蔽因为损失值看起来正常。坑三一致性温度设太低。一开始温度设0.5训练前几个epoch损失正常下降但到20个epoch后突然震荡验证准确率崩盘。原因是温度低、分布尖KL的梯度方差大加上few-shot样本少batch内噪声被放大。把温度调到2以后稳定了。坑四λ在训练开始时满权重。前面提过一开始就让一致性项介入模型连判别边界都没学出来一致性项就是在给随机分布做平滑最终基类和新类都掉。加warmup后解决。排查这类问题我的通用思路是先看损失各分项的数值曲线判断是哪一项在异常再看梯度的范数和分布定位梯度问题最后做消融一次只开一项一致性确认收益来源。别一上来就怀疑数据集或模型结构90%的问题出在损失和梯度层面。6. 工程落地的取舍与延伸最后聊聊这套方法在实际项目里怎么用、什么情况下不用。6.1 什么场景值得引入一致性约束一句话判断你的基类和新类分布差异大、标注又少且能接受一点基类准确率的损失那就值得引入。典型场景是新品类识别、跨域商品分类、少样本医疗影像辅助分类这类新类跟训练类语义有距离的任务。反过来如果基类和新类几乎同分布比如只是按时间随机切分的同质数据一致性引导的收益很小不如把精力放在数据清洗和增强上。另外如果推理延迟卡得很死双边提示模态级一致性带来的额外视觉prompt会增加推理计算量要谨慎评估。6.2 与参数高效微调的对比跟LoRA一类参数高效微调比提示学习的参数量少一到两个数量级训练成本极低但性能上限也更低。一致性引导把提示学习的泛化短板补了一截但如果你有充足数据和算力LoRA在基类性能上仍然更强。我的经验判断是数据量在few-shot到几十shot之间、且需要频繁切换下游任务的场景一致性引导的提示学习性价比最高——一个模型、换套prompt就能适配新任务切换成本极低。数据量上去之后再考虑上参数高效微调。6.3 部署时的注意事项与延伸方向部署阶段最需要关注的是多条文本分支的显存占用。训练时手工prompt和可学习prompt两条分支并存推理时其实只需要可学习prompt那条手工分支可以完全丢掉所以推理开销跟普通CoOp一样。这一点在服务化时要注意别把训练代码原样搬上去。延伸方向有两个我觉得值得尝试。一是把一致性约束从预测层往特征层移约束中间层表示的一致性可能对细粒度任务更友好二是把视图一致性从图像增强扩展到文本增强比如类别名的不同模板做跨模态的多视图一致性理论上能进一步压过拟合。这两条我自己还在试目前看文本侧增强的增强强度不好控制容易引入噪声但方向应该是对的。如果非要说一条最实在的建议先把CoOp跑通、把base-to-novel的评测协议搭对再引入最简单的prompt蒸馏一致性确认新类指标有提升最后再往上叠视图一致性和模态一致性。一步到位堆所有技巧出了问题根本没法定位。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →