YOLO11 改进 – 主干网络_ RevCol可逆列网络:轻量级多列设计破解特征信息丢失难题,提升小目标与密集目标感知精度

前言

本文介绍了将可逆列网络(RevCol)与YOLOv11相结合的方法。RevCol由子网副本(列)组成,采用多级可逆连接,在前向传播时特征逐渐解耦且保留整体信息。该架构在图像分类、目标检测和语义分割等任务中表现出色,在多个数据集上取得优异成绩,还可引入到Transformer等网络中。我们将RevCol引入YOLOv11,对相关代码进行修改和注册,并配置了yolov11 - RevCol.yaml文件。通过实验脚本进行训练,结果显示该结合方式在目标检测任务中展现出一定效果。

文章目录: YOLOv11改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

专栏链接: YOLOv11改进专栏

文章目录

[TOC]

介绍

摘要

我们提出了一种新的神经网络设计范式——可逆列网络(Reversible Column Network,简称RevCol)。RevCol的主体由多个子网络的副本组成,分别称为列(columns),列之间采用多层级的可逆连接。这种架构使RevCol的行为与传统网络截然不同:在前向传播过程中,RevCol中的特征在通过每一列时逐渐被解耦,而整体信息得以保留,而不是像其他网络那样被压缩或丢弃。我们的实验表明,CNN风格的RevCol模型在多个计算机视觉任务中(如图像分类、目标检测和语义分割)表现出非常有竞争力的性能,尤其是在较大参数预算和大数据集条件下。例如,在ImageNet-22K上进行预训练后,RevCol-XL在ImageNet-1K上达到了88.2%的准确率。在使用更多预训练数据的情况下,我们最大的模型RevCol-H在ImageNet-1K上达到了90.0%的准确率,在COCO检测minival集上获得63.8%的APbox,在ADE20k分割任务上达到61.0%的mIoU。我们所知,这是纯CNN(静态)模型在COCO检测和ADE20k分割任务上的最佳结果。此外,作为一种通用的大架构模式,RevCol也可以引入到Transformer或其他神经网络中,证明其在计算机视觉和自然语言处理任务中均能提升性能。我们在 https://github.com/megvii-research/RevCol 发布了代码和模型。

文章链接

论文地址: 论文地址

代码地址: 代码地址

基本原理

RevCol 由子网的多个副本组成,分别命名为列,在这些副本之间采用多级可逆连接。RevCol coud 是计算机视觉中各种任务的基础模型主干,包括分类、检测和分割。

核心代码

class RevCol(BaseModule):
    def __init__(self, channels=[32, 64, 96, 128], layers=[2, 3, 6, 3], num_subnet=5, kernel_size = 3, num_classes=1000, drop_path = 0.0, save_memory=True, single_head=True, out_indices=[0, 1, 2, 3], init_cfg=None) -> None:
        super().__init__(init_cfg)
        self.num_subnet = num_subnet
        self.single_head = single_head
        self.out_indices = out_indices
        self.init_cfg = init_cfg

        self.stem = nn.Sequential(
            nn.Conv2d(3, channels[0], kernel_size=4, stride=4),
            LayerNorm(channels[0], eps=1e-6, data_format="channels_first")
        )

        # dp_rate = self.cal_dp_rate(sum(layers), num_subnet, drop_path)

        dp_rate = [x.item() for x in torch.linspace(0, drop_path, sum(layers))] 
        for i in range(num_subnet):
            first_col = True if i == 0 else False
            self.add_module(f'subnet{str(i)}', SubNet(
                channels,layers, kernel_size, first_col, dp_rates=dp_rate, save_memory=save_memory))

    def init_weights(self):
        logger = get_root_logger()
        if self.init_cfg is None:
            logger.warn(f'No pre-trained weights for '
                        f'{self.__class__.__name__}, '
                        f'training start from scratch')
            for m in self.modules():
                if isinstance(m, nn.Linear):
                    trunc_normal_init(m, std=.02, bias=0.)
                elif isinstance(m, nn.LayerNorm):
                    constant_init(m, 1.0)
        else:
            assert 'checkpoint' in self.init_cfg, f'Only support ' \
                                                  f'specify `Pretrained` in ' \
                                                  f'`init_cfg` in ' \
                                                  f'{self.__class__.__name__} '
            ckpt = _load_checkpoint(
                self.init_cfg.checkpoint, logger=logger, map_location='cpu')
            if 'state_dict' in ckpt:
                _state_dict = ckpt['state_dict']
            elif 'model' in ckpt:
                _state_dict = ckpt['model']
            else:
                _state_dict = ckpt

            state_dict = _state_dict
            # print(state_dict.keys())
            # strip prefix of state_dict
            if list(state_dict.keys())[0].startswith('module.'):
                state_dict = {k[7:]: v for k, v in state_dict.items()}

            # load state_dict
            self.load_state_dict(state_dict, False)

    def forward(self, x):
        x = self.stem(x)        
        c0, c1, c2, c3 = 0, 0, 0, 0
        for i in range(self.num_subnet):
            # c0, c1, c2, c3 = checkpoint(getattr(self, f'subnet{str(i)}'), x, c0, c1, c2, c3 )
            c0, c1, c2, c3 = getattr(self, f'subnet{str(i)}')(x, c0, c1, c2, c3)
        return c0, c1, c2, c3

    def cal_dp_rate(self, depth, num_subnet, drop_path):
        dp = np.zeros((depth, num_subnet))
        dp[:,0]=np.linspace(0, depth-1, depth)
        dp[0,:]=np.linspace(0, num_subnet-1, num_subnet)
        for i in range(1, depth):
            for j in range(1, num_subnet):
                dp[i][j] = min(dp[i][j-1], dp[i-1][j])+1
        ratio = dp[-1][-1]/drop_path
        dp_matrix = dp/ratio
        return dp_matrix

实验

脚本

import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO

if __name__ == '__main__':
#     修改为自己的配置文件地址
    model = YOLO('/root/ultralytics-main/ultralytics/cfg/models/11/yolov11-RevCol.yaml')
#     修改为自己的数据集地址
    model.train(data='/root/ultralytics-main/ultralytics/cfg/datasets/coco8.yaml',
                cache=False,
                imgsz=640,
                epochs=10,
                single_cls=False,  # 是否是单类别检测
                batch=8,
                close_mosaic=10,
                workers=0,
                optimizer='SGD',
                amp=True,
                project='runs/train',
                name='RevCol',
                )

结果

THE END