前言
本文介绍了新型视觉适配器微调方法Mona,并将其集成到YOLO26中。传统全参数微调成本高、存储负担重且有过拟合风险,现有PEFT方法性能落后。Mona仅调整5%以内的骨干网络参数,在多个视觉任务中超越全参数微调。其核心亮点包括参数效率高、性能突破和即插即用。适配器模块包含降维、多认知视觉滤波器等单元,通过深度可分离卷积和多尺度卷积核处理视觉信号,还加入分布适配层优化输入分布。我们将Mona集成到YOLO26,经注册和配置yaml文件后进行实验,展现出良好效果。
文章目录: YOLO26改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总
专栏链接: YOLO26改进专栏
文章目录
- 前言
- 介绍
- 摘要
- 文章链接
- 基本原理
- 一、提出背景
- 二、核心亮点
- 三、方法架构与核心设计
- 1. 整体模块结构
- 2. 两大核心创新设计
- 核心代码
- YOLO26引入代码
- 注册
- 步骤1:
- 步骤2
- 配置yolo26-Mona.yaml
- 实验
- 脚本
- 结果
介绍
摘要
Mona(Multi-cognitive Visual Adapter)是一种新型视觉适配器微调方法,旨在打破传统全参数微调(full fine-tuning)在视觉识别任务中的性能瓶颈。Mona 方法通过引入多认知视觉滤波器和优化输入分布,仅调整 5% 的骨干网络参数,就能在实例分割、目标检测、旋转目标检测等多个经典视觉任务中超越全参数微调的效果,显著降低了适配和存储成本,为视觉模型的高效微调提供了新的思路。
文章链接
论文地址:论文地址
代码地址:代码地址
基本原理
Mona是由清华大学、国科大、上海交大、阿里巴巴联合提出的新型视觉适配器微调方法,相关论文《5%>100%: Breaking Performance Shackles of Full Fine-Tuning on Visual Recognition Tasks》已被CVPR2025收录,其核心目标是打破传统全参数微调的性能与成本枷锁,为视觉大模型高效微调提供新方案。
一、提出背景
- 传统全参数微调的痛点
- 成本极高:需更新模型全部参数,以早期BERT为例,单卡训练100万数据需5-7小时,万亿级参数模型的微调对硬件和时间要求更苛刻;
- 存储负担重:多任务场景下需为每个任务保存完整模型副本;
- 存在过拟合风险:大模型直接微调易因数据分布差异出现性能下降。
- 现有PEFT方法的局限
参数高效微调(PEFT)虽能仅调整少量参数实现模型适配,但视觉领域的多数PEFT方法性能仍落后于全参数微调,无法兼顾效率与效果。
二、核心亮点
- 参数效率极高:仅调整5%以内的骨干网络参数,远低于传统PEFT方法的参数改动占比,大幅降低适配和存储成本;
- 性能突破枷锁:在实例分割、目标检测、图像分类等多类视觉任务中,首次实现小于5%参数成本下超越全参数微调的性能;
- 即插即用特性:可便捷嵌入现有视觉模型架构(如Swin-Transformer),无需大规模重构模型。
三、方法架构与核心设计
1. 整体模块结构
Mona的适配器模块包含降维、多认知视觉滤波器、激活函数、升维等单元,并内置跳跃连接(Skip-Connections),可无缝集成至Swin-Transformer等主流视觉骨干网络,整体流程为:输入特征经LayerNorm处理后,先降维至内部维度,再通过多认知滤波器增强特征表达,经GELU激活和dropout后升维还原,最终与原始特征残差连接输出。
2. 两大核心创新设计
- 多认知视觉滤波器
这是Mona的核心模块,通过深度可分离卷积和**多尺度卷积核(3×3、5×5、7×7)**实现对二维视觉信号的精准处理,与传统线性适配器不同,其专门针对视觉任务设计,可完成多尺度特征融合,提升模型对视觉信息的理解能力,具体是将3种尺度卷积的输出均值后与原始特征残差连接,再经1×1卷积投影优化。 - 输入分布优化
在适配器前端加入分布适配层(Scaled LayerNorm),通过可学习参数gamma和gammax调整输入特征分布,使预训练特征更适配适配器的处理逻辑,提升微调效率。
核心代码
classMona(BaseModule):def__init__(self,in_dim,factor=4):super().__init__()self.project1=nn.Linear(in_dim,64)self.nonlinear=F.gelu self.project2=nn.Linear(64,in_dim)self.dropout=nn.Dropout(p=0.1)self.adapter_conv=MonaOp(64)self.norm=nn.LayerNorm(in_dim)self.gamma=nn.Parameter(torch.ones(in_dim)*1e-6)self.gammax=nn.Parameter(torch.ones(in_dim))defforward(self,x,hw_shapes=None):identity=x x=self.norm(x)*self.gamma+x*self.gammax project1=self.project1(x)b,n,c=project1.shape h,w=hw_shapes project1=project1.reshape(b,h,w,c).permute(0,3,1,2)project1=self.adapter_conv(project1)project1=project1.permute(0,2,3,1).reshape(b,n,c)nonlinear=self.nonlinear(project1)nonlinear=self.dropout(nonlinear)project2=self.project2(nonlinear)returnidentity+project2YOLO26引入代码
在根目录下的ultralytics/nn/目录,新建一个otherModules目录,然后新建一个以Mona为文件名的py文件, 把代码拷贝进去。
importtorchimporttorch.nnasnnimporttorch.nn.functionalasFclassMonaOp(nn.Module):def__init__(self,in_features):super().__init__()self.conv1=nn.Conv2d(in_features,in_features,kernel_size=3,padding=3//2,groups=in_features)self.conv2=nn.Conv2d(in_features,in_features,kernel_size=5,padding=5//2,groups=in_features)self.conv3=nn.Conv2d(in_features,in_features,kernel_size=7,padding=7//2,groups=in_features)self.projector=nn.Conv2d(in_features,in_features,kernel_size=1,)defforward(self,x):identity=x conv1_x=self.conv1(x)conv2_x=self.conv2(x)conv3_x=self.conv3(x)x=(conv1_x+conv2_x+conv3_x)/3.0+identity identity=x x=self.projector(x)returnidentity+xclassMona(nn.Module):def__init__(self,in_dim):super().__init__()self.project1=nn.Conv2d(in_dim,64,1)self.nonlinear=F.gelu self.project2=nn.Conv2d(64,in_dim,1)self.adapter_conv=MonaOp(64)self.norm=nn.LayerNorm(in_dim)self.dropout=nn.Dropout(p=0.1)self.gamma=nn.Parameter(torch.ones(in_dim,1,1)*1e-6)self.gammax=nn.Parameter(torch.ones(in_dim,1,1))defforward(self,x):identity=x x_norm=x.permute(0,2,3,1).contiguous()x_norm=self.norm(x_norm)x=x_norm.permute(0,3,1,2).contiguous()*self.gamma+x*self.gammax project1=self.project1(x)project1=self.adapter_conv(project1)nonlinear=self.nonlinear(project1)nonlinear=self.dropout(nonlinear)project2=self.project2(nonlinear)returnidentity+project2注册
在ultralytics/nn/tasks.py中进行如下操作:
步骤1:
fromultralytics.nn.otherModules.MonaimportMona步骤2
修改def parse_model(d, ch, verbose=True):
elifmisMona:args=[ch[f]]c2=ch[f]配置yolo26-Mona.yaml
ultralytics/cfg/models/26/yolo26-Mona.yaml
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license# Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs# Model docs: https://docs.ultralytics.com/models/yolo26# Task docs: https://docs.ultralytics.com/tasks/detect# Parametersnc:80# number of classesend2end:True# whether to use end-to-end modereg_max:1# DFL binsscales:# model compound scaling constants, i.e. 'model=yolo26n.yaml' will call yolo26.yaml with scale 'n'# [depth, width, max_channels]n:[0.50,0.25,1024]# summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPss:[0.50,0.50,1024]# summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPsm:[0.50,1.00,512]# summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPsl:[1.00,1.00,512]# summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPsx:[1.00,1.50,512]# summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs# YOLO26n backbonebackbone:# [from, repeats, module, args]-[-1,1,Conv,[64,3,2]]# 0-P1/2-[-1,1,Conv,[128,3,2]]# 1-P2/4-[-1,2,C3k2,[256,False,0.25]]-[-1,1,Conv,[256,3,2]]# 3-P3/8-[-1,2,C3k2,[512,False,0.25]]-[-1,1,Conv,[512,3,2]]# 5-P4/16-[-1,2,C3k2,[512,True]]-[-1,1,Conv,[1024,3,2]]# 7-P5/32-[-1,2,C3k2,[1024,True]]-[-1,1,Mona,[1024,5]]# 9-[-1,2,C2PSA,[1024]]# 10# YOLO26n headhead:-[-1,1,nn.Upsample,[None,2,"nearest"]]-[[-1,6],1,Concat,[1]]# cat backbone P4-[-1,2,C3k2,[512,True]]# 13-[-1,1,nn.Upsample,[None,2,"nearest"]]-[[-1,4],1,Concat,[1]]# cat backbone P3-[-1,2,C3k2,[256,True]]# 16 (P3/8-small)-[-1,1,Conv,[256,3,2]]-[[-1,13],1,Concat,[1]]# cat head P4-[-1,2,C3k2,[512,True]]# 19 (P4/16-medium)-[-1,1,Conv,[512,3,2]]-[[-1,10],1,Concat,[1]]# cat head P5-[-1,1,C3k2,[1024,True,0.5,True]]# 22 (P5/32-large)-[[16,19,22],1,Detect,[nc]]# Detect(P3, P4, P5)实验
脚本
importwarnings warnings.filterwarnings('ignore')fromultralyticsimportYOLOif__name__=='__main__':# 修改为自己的配置文件地址model=YOLO('./ultralytics/cfg/models/26/yolo26-Mona.yaml')# 修改为自己的数据集地址model.train(data='./ultralytics/cfg/datasets/coco8.yaml',cache=False,imgsz=640,epochs=10,single_cls=False,# 是否是单类别检测batch=8,close_mosaic=10,workers=0,# optimizer='MuSGD', # 使用MuSGD优化器,会报错optimizer='SGD',amp=False,project='runs/train',name='yolo26-Mona',)