代码详解——可变形卷积(DCNv3)

本文围绕可变形卷积DCNv3展开,介绍其在DCNv2基础上的改进。重点讲解DCNv3的pytorch实现部分,包括dcnv3.py中各函数功能,如通道维度调整、归一化层构建等,还阐述了DCNv3在实现中的三个改进点,同时提及dcnv3_func.py中相关函数。

可变形卷积DCNv1 & DCNv2
✨✨✨论文及代码详解——可变形卷积(DCNv1)
✨✨✨论文及代码详解——可变形卷积(DCNv2)
DCNv3 是InternImage中提出的,DCNv3在DCNv2版本上进行了改进。
✨✨✨论文详解——《InternImage: Exploring Large-Scale Vision Foundation Models with Deformable Convolutions》
InterImage官方代码: https://github.com/OpenGVLab/InternImage

概述

如下图,首先下载InterImage官方代码,然后在segmentation、detection、classification文件夹下均可以找到ops_dcnv3文件夹,该文件夹下的内容就是实现DCNv3算子的核心代码。

在这里插入图片描述

  • modules
    如下图所示, modules文件夹中的dcnv3.py文件主要定义了DCNv3模块。
    其中DCNv3_pytorch是DCNv3的pytorch实现版本,DCNv3是DCNv3的C++实现版本。
    在这里插入图片描述
  • functions
    如下图所示,function文件夹中的dcnv3_func.py文件定义了DCNv3的一些核心操作。
    其中黄色部分的DCNv3Function类被c++版本的DCNv3 调用。
    其中红色部分的dcnv3_core_pytorch方法被pytorch版本的DCNv3_pytorch调用。
    在这里插入图片描述
  • src
    src下的代码是用C++来实现DCNv3中核心操作,其下的cpucuda分别表示cpu和cuda编程两种实现版本。c++实现的版本需要去编译,否则如上图所示,黄色箭头指向的import DCNv3 有红色波浪线,无法正常导入。
    如果想import DCNv3成功,有两种解决办法:
    (1)需要编译:DCNv3具体编译方法是直接运行make.sh文件(但是这种方法很容易编译失败,对于pytorch,cuda的版本以及c++编译器的配置都有要求)
    (2)不需要编译:去官网上下载轮子https://github.com/OpenGVLab/InternImage/releases/tag/whl_files (更推荐这种方法,但是也需要注意cuda和pytorch的版本)
    在这里插入图片描述
    本文重点介绍DCNv3的pytorch实现部分。

dcnv3.py

to_channels_first

  • 功能:将通道维度放在前面,输入(b,h,w,c),输出(b,c,h,w)
# 将通道维度放在前面:(b,h,w,c)->(b,c,h,w)
class to_channels_first(nn.Module):

    def __init__(self):
        super().__init__()

    def forward(self, x): # (b,h,w,c)
        return x.permute(0, 3, 1, 2) #(b,c,h,w)

to_channels_last

  • 功能:将通道维度放在后面,输入 (b,c,h,w),输出(b,h,w,c)
# 将通道维度放在后面 (b,c,h,w)->(b,h,w,c)
class to_channels_last(nn.Module):

    def __init__(self):
        super().__init__()

    def forward(self, x): # (b,c,h,w)
        return x.permute(0, 2, 3, 1) # (b,h,w,c)

build_norm_layer

  • 功能:构建归一化层,可以选择Batch Norm或者Layer Norm
def build_norm_layer(dim,
                     norm_layer,
                     in_format='channels_last',
                     out_format='channels_last',
                     eps=1e-6):
    layers = []
    if norm_layer == 'BN':
        if in_format == 'channels_last':
            layers.append(to_channels_first())
        layers.append(nn.BatchNorm2d(dim))
        if out_format == 'channels_last':
            layers.append(to_channels_last())
    elif norm_layer == 'LN':
        if in_format == 'channels_first':
            layers.append(to_channels_last()) # (b,c,h,w)->(b,h,w,c)
        layers.append(nn.LayerNorm(dim, eps=eps))
        if out_format == 'channels_first':
            layers.append(to_channels_first())
    else:
        raise NotImplementedError(
            f'build_norm_layer does not support {
     
     norm_layer}')
    return nn.Sequential(*layers)

build_act_layer

  • 功能:构建激活函数层,可选择RELU / SiLU / GELU激活函数
def build_act_layer(act_layer):
    if act_layer == 'ReLU':
        return nn.ReLU(inplace=True)
    elif act_layer == 'SiLU':
        return nn.SiLU(inplace=True)
    elif act_layer == 'GELU':
        return nn.GELU()

    raise NotImplementedError(f'build_act_layer does not support {
     
     act_layer}')

_is_power_of_2

  • 功能:检查n是否是2的某次方,且n不为0
    如果n & (n - 1)为0,则说明n是2的某次方。
def _is_power_of_2(n):
    if (not isinstance(n, int)) or (n < 0): # 如果n不为整数或者n小于0
        raise ValueError(
            "invalid input for _is_power_of_2: {} (type: {})".format(n, type(n)))

    return (n & (n - 1) == 0) and n != 0

CenterFeatureScaleModule

  • 功能:生成缩放系数。
    F.linear的输出取决于传入的实参weightbias,然后再通过sigmod函数归一化。
class CenterFeatureScaleModule(nn.Module):
    def forward(self,
                query,
                center_feature_scale_proj_weight, # (group,channels)
                center_feature_scale_proj_bias):
        center_feature_scale = F.linear(query,
                                        weight=center_feature_scale_proj_weight,
                                        bias=center_feature_scale_proj_bias).sigmoid() # 全连接层+sigmod
        # F.linear的输出取决于传入的实参weight和bias
        # 输入:(*,channels) -> 输出:(*,group)
        return center_feature_scale

DCNv3_pytorch

class DCNv3_pytorch 是分组可变形卷积DCNv3的pytorch的实现版。

'''分组可变形卷积 pytorch实现版'''
class DCNv3_pytorch(nn.Module):
    def __init__(
            self,
            channels=64, # 通道数
            kernel_size=3, # 卷积核大小
            dw_kernel_size=None, # 深度可分离卷积核大小
            stride=1, # 步长
            pad=1, # 填充
            dilation=1, #空洞率
            group=4,# 分组数
            offset_scale=1.0, 
            act_layer='GELU', # 激活函数
            norm_layer='LN', # 归一化层
            center_feature_scale=False):

        super().__init__()
        if channels % group != 0: # 分组卷积必须保证通道数可以被组数整除
            raise ValueError(
                f'channels must be divisible by group, but got {
     
     channels} and {
     
     group}')
        _d_per_group = channels // group # 每个组数拥有的通道数
        dw_kernel_size = dw_kernel_size if dw_kernel_size is not None else kernel_size # 设置深度可分离卷积核的大小
        # 最好把_d_per_group设置为2的某次方,方便cuda的具体实现
        if not _is_power_of_2(_d_per_group):
            warnings.warn(
                "You'd better set channels in DCNv3 to make the dimension of each attention head a power of 2 "
                "which is more efficient in our CUDA implementation.")

        self.offset_scale = offset_scale
        self.channels = channels
        self.kernel_size = kernel_size
        self.dw_kernel_size = dw_kernel_size
        self.stride = stride
        self.dilation = dilation
        self.pad = pad
        self.group = group
        self.group_channels =
### 可变形卷积 DCNv3 的算法原理 可变形卷积网络 (Deformable Convolutional Networks, DCNs) 是一种改进标准卷积操作的方法,旨在增强模型处理空间变换的能力。DCNv3 特别强调了对目标形状变化和尺度变化的鲁棒性。 在传统卷积中,感受野的位置是固定的网格结构;而在可变形卷积中,通过学习额外的偏移量来调整采样位置,使得感受野可以自适应地匹配不同形态的目标对象[^1]。具体来说,在 DCNv3 中: - **偏移预测模块**:引入了一个轻量级子网用于估计每个像素处的空间偏移向量。 - **特征聚合机制**:利用双线性插值方法根据计算得到的新坐标重新分配输入特征图上的权重分布。 这种设计允许更灵活的感受域配置,从而提高了对于姿态多样性以及视角差异等问题下的检测精度。 ```python import torch.nn as nn class DeformConv(nn.Module): def __init__(self, inc, outc, kernel_size=3, stride=1, padding=1, bias=False): super(DeformConv, self).__init__() # 偏移量生成器 self.offset_conv = nn.Conv2d( inc, 2 * kernel_size * kernel_size, kernel_size=kernel_size, stride=stride, padding=padding, bias=bias) # 主干卷积层 self.conv = nn.Conv2d( inc, outc, kernel_size=kernel_size, stride=stride, padding=padding, bias=bias) def forward(self, x): offset = self.offset_conv(x) output = deform_conv_function(input=x, offset=offset, weight=self.conv.weight) return output ``` ### 实现方式 为了有效实现上述功能,通常采用两种策略相结合的方式来进行优化: - **硬件加速支持**: 使用 GPU 或者其他专用硬件设备提供高效的矩阵运算性能支撑; - **软件框架集成**: 将相关算子融入主流深度学习库如 PyTorch 和 TensorFlow 等之中以便开发者能够方便调用并享受社区维护带来的便利性和稳定性。 此外,针对实际应用场景中的效率考量,研究者们还提出了多种简化版本或变体形式以平衡速度与效果之间的关系。 ### 应用案例 可变形卷积已经在多个计算机视觉任务取得了显著成果,特别是在那些涉及物体定位、跟踪及识别的任务里表现出色。例如,在自动驾驶领域内,车辆周围环境感知系统经常面临复杂的交通状况,而基于 DCN 构建的对象探测器可以帮助提高行人和其他障碍物检测准确性,进而保障行车安全。 另一个典型的应用场景是在医学影像分析方面,这里的数据往往存在较大个体间变异性和成像条件不确定性,因此使用具备更强泛化能力的可变形卷积有助于改善疾病诊断结果的质量。
评论 17
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zyw2002

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值