DeformableConv(可形变卷积)理论和代码分析

这篇具有很好参考价值的文章主要介绍了DeformableConv(可形变卷积)理论和代码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

DeformableConv(可形变卷积)理论和代码分析

  • 代码参考:DeformableConv代码
  • 理论参考:bilibili视频讲解

1.DeformableConv理论分析

可变形卷积代码,深度学习,神经网络,计算机视觉
以上图为例进行讲解,在普通卷积中:

  • input输入尺寸为[batch_size, channel, H, W]
  • output输入尺寸也为[batch_size, channel, H, W]
  • kernel尺寸为[kernel_szie, kernel_szie]
    因此output中任意一点p0,对应到input中的卷积采样区域大小为kernel_szie x kernel_szie,卷积操作用公式可表示为:
    y ( p 0 ) = ∑ p n ∈ R w ( p n ) ⋅ x ( p 0 + p n ) \mathbf{y}\left(\mathbf{p}_{0}\right)=\sum_{\mathbf{p}_{n} \in \mathcal{R}} \mathbf{w}\left(\mathbf{p}_{n}\right) \cdot \mathbf{x}\left(\mathbf{p}_{0}+\mathbf{p}_{n}\right) y(p0)=pnRw(pn)x(p0+pn)
    其中, p n \mathbf{p}_{n} pn代表卷积核中每一个点相对于中心点的偏移量,可用如下公式表示(3 x 3卷积核为例):
    R = { ( − 1 , − 1 ) , ( − 1 , 0 ) , … , ( 0 , 1 ) , ( 1 , 1 ) } \mathcal{R}=\{(-1,-1),(-1,0), \ldots,(0,1),(1,1)\} R={(1,1),(1,0),,(0,1),(1,1)}
    可变形卷积代码,深度学习,神经网络,计算机视觉

w ( p n ) \mathbf{w}\left(\mathbf{p}_{n}\right) w(pn)代表卷积核上对应位置的权重。 p 0 \mathbf{p}_{0} p0可以看做output上每一个点, y ( p 0 ) \mathbf{y}\left(\mathbf{p}_{0}\right) y(p0)为output上每一点的具体值。 x ( p 0 + p n ) \mathbf{x}\left(\mathbf{p}_{0}+\mathbf{p}_{n}\right) x(p0+pn)为output上每个点对应到input上的卷积采样区域的具体值。该公式整体意思就是卷积操作。

而DeformableConv的计算步骤就是在普通卷积的基础上,加一个模型自己学习的偏移量offset,公式为:
y ( p 0 ) = ∑ p n ∈ R w ( p n ) ⋅ x ( p 0 + p n + Δ p n ) \mathbf{y}\left(\mathbf{p}_{0}\right)=\sum_{\mathbf{p}_{n} \in \mathcal{R}} \mathbf{w}\left(\mathbf{p}_{n}\right) \cdot \mathbf{x}\left(\mathbf{p}_{0}+\mathbf{p}_{n}+\Delta \mathbf{p}_{n}\right) y(p0)=pnRw(pn)x(p0+pn+Δpn)
公式中用 Δ p n \Delta \mathbf{p}_{n} Δpn表示偏移量。需要注意的是,该偏移量是针对 x \mathbf{x} x的,也就是可变形卷积变的不是卷积核,而是input。
可变形卷积代码,深度学习,神经网络,计算机视觉
从上图可以看到,在input上按照普通卷积操作,output上的一点对应到input上的卷积采样区域是一个卷积核大小的正方形,而可变形卷积对应的卷积采样区域为一些蓝框表示的点,这就是可变形卷积与普通卷积的区别。

接下来说一下可变形卷积的具体细节,以N x N的卷积核为例。一个output上的点对应到input上的卷积采样区域大小为N x N,按照可变形卷积的操作,这N x N区域的每一个卷积采样点都要学习一个偏离量offset,而offset是用坐标表示的,所以一个output要学习2N x N个参数。一个output大小为H x W,所以一共要学习2NxN x H x W个参数。即上图的offset field,其维度为B x 2NxN x H x W,其中B代表batch_size,(上图是网络上找的,里面的N指卷积核的面积)。值得注意的两点细节是:

  • input(假设维度为B x C x H x W)一个batch内的所有通道上的特征图(一共C个)共用一个offset field,即一个batch内的每张特征图用到的偏移量是一样的。
  • 可变形卷积不改变input的尺寸,所以output也为H x W。

一个点加上offset之后,大概率不会得到一个整数坐标点,这时就应该进行线性插值操作,具体做法为找出和偏移后的点距离小于1且最近的四个点,将其值乘以权重(权重以距离来衡量)再进行加和操作,即为最终偏移后的点的值。

2.DeformableConv代码分析

依照上述理论,可将DeformableConv的代码实现拆解为如下流程图:
可变形卷积代码,深度学习,神经网络,计算机视觉文章来源地址https://www.toymoban.com/news/detail-596154.html

2.1初始化操作

class DeformConv2d(nn.Module):
    def __init__(self, 
                 inc, 
                 outc, 
                 kernel_size=3, 
                 padding=1, 
                 stride=1, 
                 bias=None, 
                 modulation=False):
        """
        Args:
            modulation (bool, optional): If True, Modulated Defomable 
            Convolution (Deformable ConvNets v2).
        """
        super(DeformConv2d, self).__init__()
        self.kernel_size = kernel_size
        self.padding = padding
        self.stride = stride
        self.zero_padding = nn.ZeroPad2d(padding)
        self.conv = nn.Conv2d(inc, #该卷积用于最终的卷积
                              outc, 
                              kernel_size=kernel_size, 
                              stride=kernel_size, 
                              bias=bias)

        self.p_conv = nn.Conv2d(inc, #该卷积用于从input中学习offset
                                2*kernel_size*kernel_size, 
                                kernel_size=3, 
                                padding=1, 
                                stride=stride)
        nn.init.constant_(self.p_conv.weight, 0)
        self.p_conv.register_backward_hook(self._set_lr)

        self.modulation = modulation #该部分是DeformableConv V2版本的,可以暂时不看
        if modulation:
            self.m_conv = nn.Conv2d(inc, 
                                    kernel_size*kernel_size, 
                                    kernel_size=3, 
                                    padding=1, 
                                    stride=stride)
            nn.init.constant_(self.m_conv.weight, 0)
            self.m_conv.register_backward_hook(self._set_lr)

    @staticmethod
    def _set_lr(module, grad_input, grad_output):
        grad_input = (grad_input[i] * 0.1 for i in range(len(grad_input)))
        grad_output = (grad_output[i] * 0.1 for i in range(len(grad_output)))

2.2执行

    def forward(self, x):
        offset = self.p_conv(x) #此处得到offset
        if self.modulation:
            m = torch.sigmoid(self.m_conv(x))

        dtype = offset.data.type()
        ks = self.kernel_size
        N = offset.size(1) // 2

        if self.padding:
            x = self.zero_padding(x)

        # (b, 2N, h, w)
        p = self._get_p(offset, dtype)

        # (b, h, w, 2N)
        p = p.contiguous().permute(0, 2, 3, 1)
        q_lt = p.detach().floor()
        q_rb = q_lt + 1

        q_lt = torch.cat([torch.clamp(q_lt[..., :N], 0, x.size(2)-1), torch.clamp(q_lt[..., N:], 0, x.size(3)-1)], dim=-1).long()
        q_rb = torch.cat([torch.clamp(q_rb[..., :N], 0, x.size(2)-1), torch.clamp(q_rb[..., N:], 0, x.size(3)-1)], dim=-1).long()
        q_lb = torch.cat([q_lt[..., :N], q_rb[..., N:]], dim=-1)
        q_rt = torch.cat([q_rb[..., :N], q_lt[..., N:]], dim=-1)

        # clip p
        p = torch.cat([torch.clamp(p[..., :N], 0, x.size(2)-1), torch.clamp(p[..., N:], 0, x.size(3)-1)], dim=-1)

        # bilinear kernel (b, h, w, N)
        g_lt = (1 + (q_lt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_lt[..., N:].type_as(p) - p[..., N:]))
        g_rb = (1 - (q_rb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_rb[..., N:].type_as(p) - p[..., N:]))
        g_lb = (1 + (q_lb[..., :N].type_as(p) - p[..., :N])) * (1 - (q_lb[..., N:].type_as(p) - p[..., N:]))
        g_rt = (1 - (q_rt[..., :N].type_as(p) - p[..., :N])) * (1 + (q_rt[..., N:].type_as(p) - p[..., N:]))

        # (b, c, h, w, N)
        x_q_lt = self._get_x_q(x, q_lt, N)
        x_q_rb = self._get_x_q(x, q_rb, N)
        x_q_lb = self._get_x_q(x, q_lb, N)
        x_q_rt = self._get_x_q(x, q_rt, N)

        # (b, c, h, w, N)
        x_offset = g_lt.unsqueeze(dim=1) * x_q_lt + \
                   g_rb.unsqueeze(dim=1) * x_q_rb + \
                   g_lb.unsqueeze(dim=1) * x_q_lb + \
                   g_rt.unsqueeze(dim=1) * x_q_rt

        # modulation
        if self.modulation:
            m = m.contiguous().permute(0, 2, 3, 1)
            m = m.unsqueeze(dim=1)
            m = torch.cat([m for _ in range(x_offset.size(1))], dim=1)
            x_offset *= m

        x_offset = self._reshape_x_offset(x_offset, ks)
        out = self.conv(x_offset)

        return out
        
    def _get_p_n(self, N, dtype): #求
        p_n_x, p_n_y = torch.meshgrid(
            torch.arange(-(self.kernel_size-1)//2, (self.kernel_size-1)//2+1),
            torch.arange(-(self.kernel_size-1)//2, (self.kernel_size-1)//2+1))
        # (2N, 1)
        p_n = torch.cat([torch.flatten(p_n_x), torch.flatten(p_n_y)], 0)
        p_n = p_n.view(1, 2*N, 1, 1).type(dtype)

        return p_n

    def _get_p_0(self, h, w, N, dtype):
        p_0_x, p_0_y = torch.meshgrid(
            torch.arange(1, h*self.stride+1, self.stride),
            torch.arange(1, w*self.stride+1, self.stride))
        p_0_x = torch.flatten(p_0_x).view(1, 1, h, w).repeat(1, N, 1, 1)
        p_0_y = torch.flatten(p_0_y).view(1, 1, h, w).repeat(1, N, 1, 1)
        p_0 = torch.cat([p_0_x, p_0_y], 1).type(dtype)

        return p_0

    def _get_p(self, offset, dtype):
        N, h, w = offset.size(1)//2, offset.size(2), offset.size(3)

        # (1, 2N, 1, 1)
        p_n = self._get_p_n(N, dtype)
        # (1, 2N, h, w)
        p_0 = self._get_p_0(h, w, N, dtype)
        p = p_0 + p_n + offset
        return p

    def _get_x_q(self, x, q, N):
        b, h, w, _ = q.size()
        padded_w = x.size(3)
        c = x.size(1)
        # (b, c, h*w)
        x = x.contiguous().view(b, c, -1)

        # (b, h, w, N)
        index = q[..., :N]*padded_w + q[..., N:]  # offset_x*w + offset_y
        # (b, c, h*w*N)
        index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1)

        x_offset = x.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N)

        return x_offset

    @staticmethod
    def _reshape_x_offset(x_offset, ks):
        b, c, h, w, N = x_offset.size()
        x_offset = torch.cat([x_offset[..., s:s+ks].contiguous().view(b, c, h, w*ks) for s in range(0, N, ks)], dim=-1)
        x_offset = x_offset.contiguous().view(b, c, h*ks, w*ks)

        return x_offset

到了这里,关于DeformableConv(可形变卷积)理论和代码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 论文解读 | KPConv——点云上的可形变卷积网络

    原创 | 文 BFT机器人  《KPConv: Flexible and Deformable Convolution for Point Clouds》是一篇发表于2019年的研究论文,作者为Hugues Thomas、Charles R. Qi、Jean-Emmanuel Deschaud、Beatriz Marcotegui和François Goulette。这篇论文关注于点云数据上的卷积操作,提出了一种名为KPConv的卷积方法,旨在解决点云

    2024年02月09日
    浏览(39)
  • 学习笔记:深度学习(3)——卷积神经网络(CNN)理论篇

    学习时间:2022.04.10~2022.04.12 CNN(Convolutional Neural Networks, ConvNets, 卷积神经网络)是神经网络的一种,是理解图像内容的最佳学习算法之一,并且在图像分割、分类、检测和检索相关任务中表现出色。 3.1.1 什么是CNN? CNN是一种带有卷积结构的前馈神经网络, 卷积结构 可以减少

    2024年02月03日
    浏览(91)
  • 深度学习卷积神经网络CNN之 VGGNet模型主vgg16和vgg19网络模型详解说明(理论篇)

    1.VGG背景 2. VGGNet模型结构 3. 特点(创新、优缺点及新知识点)    VGGNet是2014年ILSVRC(ImageNet Large Scale Visual Recognition Challenge 大规模视觉识别挑战赛 )竞赛的第二名,解决ImageNet中的 1000类图像分类和定位问题 ,第一名是GoogLeNet。    VGG全称是Visual Geometry Group,因为是由O

    2024年02月03日
    浏览(39)
  • DW卷积、PW卷积、转置卷积、膨胀卷积(空洞卷积)、可变形卷积一次看个够

    Depthwise Separable Convolution也就是深度可分离卷积,应该见过吧,它其实是由depthwise卷积和pointwise卷积组合而成,同样可以用来提取图像特征,但是它的计算量相对来说要小很多,所以一些轻量模型用了深度可分离卷积比如mobilenet。(以下无标注则均无padding,stride=1) Depthwise

    2024年02月11日
    浏览(37)
  • 基于PS-InSAR技术的形变监测分析流程

    1.1 PS-InSAR技术 ​ PS-InSAR技术是“永久散射体合成孔径雷达干涉测量”的多重嵌套缩写,即Persistent Scatterer Interferometric Synthetic Aperture Radar。其中PS(永久散射体)指对雷达波的后向散射较强,并且在时序上较稳定的各种地物目标,如建筑物与构筑物的顶角、桥梁、栏杆、裸露的

    2024年02月06日
    浏览(22)
  • 深度学习代码学习(一文真正看懂卷积层的代码定义)

    一维卷积: 将n行3列升维到n行6列。(原因:卷积核为6个) *表示点乘  Linear线性层: (通过矩阵计算改变输入输出特征向量的维度) Pytorch nn.Linear的基本用法与原理详解-CSDN博客  pytorch初学笔记(十二):神经网络基本结构之线性层-CSDN博客 二维卷积: torch学习 (十六):二维

    2024年01月16日
    浏览(66)
  • 动手学深度学习—卷积神经网络(原理解释+代码详解)

    多层感知机对图像处理是百万维度,模型不可实现。 如果要在图片中找到某个物体,寻找方法应该和物体位置无关。 适合 计算机视觉 的神经网络架构: 平移不变性 :不管检测对象出现在图像中的哪个位置,神经网络前几层应该对相同图像区域有相似的反应。 局部性 :神

    2024年02月14日
    浏览(55)
  • 深度学习——卷积层的输入输出多通道(笔记)+代码

    一 输入通道 1.多个输入通道 ①彩色图像有RGB(红绿蓝组成)三个通道 ②转换为灰度会丢失信息 灰度一个通道 2.多个通道输出的结果:只有一个输出 每个通道都有对应的卷积核,输出的结果是所有通道卷积核的和 【演示】二个通道的输出结果 输出结果某个值的计算:  3.输

    2024年02月07日
    浏览(49)
  • 深度学习4 -- 卷积神经网络(代码实现篇+付详细流程文件)

    本文是使用pytorch对卷积神经网络(Convolutional Neural Network, CNN)的代码实现,作为之前介绍CNN原理的一个代码补充。 本文代码相关介绍相对较为详细,也为自己的一个学习过程,有错误的地方欢迎指正。 本人介绍CNN原理的链接:CNN原理介绍1 CNN原理介绍2 为方便理解,如下图所示

    2024年02月06日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包