两种torch手动替换卷积权重方法

一. nn.Conv2d和F.conv2d权重替换方式分别如下

import torch
import torch.nn as nn
import torch.nn.functional as F

class net(nn.Module):
    def __init__(self):
        super(net, self).__init__()
        self.conv = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=(3, 3), bias=None)
        params = torch.tensor([-1, 0, 1, -2, 0, -2, -1, 0, 1]*9, dtype=torch.float32)
        kernel = nn.Parameter(params.view(3, 3, 3, 3), requires_grad=True)
        self.conv.weight = kernel

    def forward(self, x):
        out = self.conv(x)
        return out

class net2(nn.Module):
    def __init__(self):
        super(net2, self).__init__()
        params = torch.tensor([-1, 0, 1, -2, 0, -2, -1, 0, 1]*9, dtype=torch.float32)
        self.kernel = nn.Parameter(params.view(3, 3, 3, 3), requires_grad=True)

    def forward(self, x):
        out = F.conv2d(x, self.kernel)
        return out

if __name__ == '__main__':
    input = torch.rand(1, 3, 5, 5)
    model = net()
    print(model(input))

    model2 = net2()
    print(model2(input))

运行结果:
在这里插入图片描述

二. 对比总结:

  1. 在实际工程中,如果需要梯度反传,需要将requires_grad=True,反之设为False
  2. nn.Conv2d默认bias=True,而F.conv2d默认bias=False,这个需要特别注意,否者两个结果会不一样
  3. 手动设置卷积权重在一些特殊情况下很有用,一般的权重初始化有
    1) 均匀分布初始化(随机初始化权重在给定的范围内) nn.init.uniform_(m.weight, a=-0.1, b=0.1)
    2) 正态分布初始化 nn.init.normal_(m.weight, mean=0.0, std=0.01)
    3) 常数初始化 nn.init.constant_(m.weight, val=0.01)
    4) Xavier均匀分布初始化(适用于保持输入和输出的方差一致) nn.init.xavier_uniform_(m.weight, gain=1.0)
    5) Xavier正态分布初始化 nn.init.xavier_normal_(m.weight, gain=1.0)
    6) He均匀分布初始化(适用于ReLU激活函数的神经网络) nn.init.kaiming_uniform_(m.weight, mode=‘fan_in’, nonlinearity=‘relu’)
    7) He正态分布初始化(适用于ReLU激活函数的神经网络) nn.init.kaiming_normal_(m.weight, mode=‘fan_in’, nonlinearity=‘relu’)
<think>嗯,用户想了解可变形卷积的种类及其实现方式。首先需要回顾之前的对话历史,用户之前可能已经讨论过可变形卷积的基本概念,现在需要更深入的类型和实现细节。 根据引用[1]和[3],可变形卷积主要分为v1和v2两个版本。v1版本是基础结构,在ResNet的conv5阶段替换了3x3卷积层。而v2版本在更多层应用,比如conv3到conv5,共12层,并增加了调制机制。引用[3]提到DCN+FPN+Aligned Xception在COCO比赛中取得了好成绩,说明实际应用效果显著。 接下来需要区分两种类型的实现差异。v1的核心是偏移量学习,通过额外的卷积层生成偏移量,然后应用双线性插值采样。而v2添加了调制因子,通过另一个分支生成权重,调整每个采样点的重要性。这部分需要结合技术细节说明,比如偏移量分支的输出通道数,以及如何整合到标准卷积中。 关于实现方式,用户可能关心代码层面的具体操作。比如在PyTorch中,需要定义可变形卷积层,添加偏移量或调制因子的卷积层,并重写前向传播函数。双线性插值采样是关键步骤,确保梯度能正确回传。引用[2]中提到偏移量学习的自由性可能存在问题,但v2的调制机制部分解决了这个问题,通过权重约束偏移范围。 用户可能还关心实际应用中的性能差异。引用[1]指出在Pascal VOC上v2表现更好,但深层网络在复杂数据集可能过拟合。需要权衡深度和泛化能力,比如COCO数据集可能需要减少层数。 最后,生成的问题需要覆盖类型对比、实现关键点、应用场景和优化方向。这样用户可以根据兴趣深入某个方面,比如代码实现或调参策略。</think>好的,我们来详细探讨可变形卷积(Deformable Convolution)的主要类型及其实现方式。可变形卷积的核心思想是赋予标准卷积核采样点位置自适应的能力,使其能根据输入特征的内容动态调整采样位置,从而更好地适应目标形变、尺度变化等复杂几何变换。 **主要类型:** 目前,可变形卷积主要有两个代表性版本,通常称为 DCNv1 和 DCNv2: 1. **可变形卷积 v1 (DCNv1 - 基础版本):** * **核心思想:** 在标准卷积的规则网格采样点上,为每个点学习一个**偏移量**$(\Delta x, \Delta y)$。这些偏移量允许卷积核的采样点“移动”到特征图上更合适的位置。 * **实现关键:** * **偏移量学习:** 在输入特征图`input_feat`上,通过一个额外的并行卷积层(称为偏移量卷积层 `offset_conv`)来预测偏移量。`offset_conv`的卷积核大小通常与主卷积层相同(例如3x3)。 * **输出通道:** `offset_conv`的输出通道数为 `2 * N * kernel_size[0] * kernel_size[1]`。其中 `N` 是主卷积层的输出通道数(通常等于其分组数 `group`),`kernel_size[0] * kernel_size[1]` 是卷积核的大小(如3x3=9)。`2` 表示每个采样点的 (x, y) 偏移量。因此,对于输出通道数为 `N`、卷积核大小为 `KxK` 的主卷积层,偏移量张量 `offset` 的形状为 `(batch_size, 2*N*K*K, H, W)`。 * **可变形采样:** 在主卷积层执行卷积操作时,不再使用规则的网格位置 `p + p_n`(`p` 是输出位置,`p_n` 是核内相对位置),而是使用偏移后的位置 `p + p_n + \Delta p_n`(`\Delta p_n` 是从 `offset` 张量中对应位置取出的偏移量)。 * **双线性插值:** 由于偏移后的位置 `p + p_n + \Delta p_n` 通常是小数坐标,需要从输入特征图上通过**双线性插值**(Bilinear Interpolation)来获取该位置的像素值。这是实现可变形卷积的关键步骤,并且需要确保其是可导的,以便在反向传播中训练偏移量。 * **标准卷积计算:** 对每个输出位置 `p` 和每个输出通道 `n`,计算偏移后采样点值的加权和(权重即主卷积核的权重)。 2. **可变形卷积 v2 (DCNv2 - 增强版本):** * **核心改进:** 在 DCNv1 的基础上,引入了两个关键增强: * **调制机制 (Modulation Mechanism):** 不仅学习偏移量 $(\Delta x, \Delta y)$,还为每个采样点学习一个**调制因子** $\Delta m$(范围通常在 [0, 1])。这个因子可以看作是对该采样点重要性的权重。 * **更多层应用:** 研究表明,在网络的更早阶段(如 ResNet 的 conv3, conv4 阶段)应用可变形卷积,也能带来性能提升[^1]。 * **实现关键:** * **偏移量与调制因子学习:** 偏移量卷积层 `offset_conv` 的输出通道数变为 `3 * N * kernel_size[0] * kernel_size[1]`。其中 `3` 表示每个采样点的 (x偏移, y偏移, 调制因子)。 * **可变形调制采样:** 卷积计算时,采样位置变为 `p + p_n + \Delta p_n`(与 v1 相同)。但采样得到的值在加权求和之前,会先乘以对应的调制因子 $\Delta m_n$。公式可以表示为: $$Output(p) = \sum_{k=1}^{K} w_k \cdot Input(p + p_k + \Delta p_k) \cdot \Delta m_k$$ 其中 `K` 是卷积核大小(位置数),$w_k$ 是主卷积核在第 `k` 个位置的权重,$Input(\cdot)$ 是双线性插值得到的输入特征值,$\Delta p_k$ 是第 `k` 个位置的偏移量,$\Delta m_k$ 是第 `k` 个位置的调制因子。 * **作用:** 调制因子 $\Delta m$ 允许网络动态地调整每个采样点对输出的贡献程度。如果某个采样点偏移到了无关的背景区域或噪声点,网络可以通过学习一个接近 0 的 $\Delta m$ 来抑制该点的贡献,增强了模块的鲁棒性和表达能力[^1][^3]。 **实现方式 (以 PyTorch 为例):** 虽然可以手动实现双线性插值采样和梯度回传,但更常用的是利用现有的、优化过的库函数。PyTorch 提供了 `torchvision.ops.deform_conv2d` 函数,它封装了 DCNv1 的功能。对于 DCNv2,通常需要参考开源实现(如 mmdetection, detectron2 等框架中的模块)。 以下是概念性的伪代码,说明核心步骤: ```python import torch import torch.nn as nn import torch.nn.functional as F # 假设使用类似 torchvision.ops.deform_conv2d 的接口 class DeformConv2d(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, modulation=False): super(DeformConv2d, self).__init__() self.modulation = modulation # 是否是 DCNv2 (带调制) # 主卷积层的权重 (实际卷积操作由后面的 deform_conv 函数完成) self.weight = nn.Parameter(torch.Tensor(out_channels, in_channels // groups, kernel_size[0], kernel_size[1])) if bias: self.bias = nn.Parameter(torch.Tensor(out_channels)) else: self.register_parameter('bias', None) # 偏移量预测卷积层 # DCNv1: 输出通道数 = 2 * groups * kernel_size[0] * kernel_size[1] # DCNv2: 输出通道数 = 3 * groups * kernel_size[0] * kernel_size[1] offset_out_channels = (3 if modulation else 2) * groups * kernel_size[0] * kernel_size[1] self.conv_offset = nn.Conv2d(in_channels, offset_out_channels, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, bias=True) # 初始化偏移量卷积层的权重和偏置为零,初始偏移应为零 self.conv_offset.weight.data.zero_() if self.conv_offset.bias is not None: self.conv_offset.bias.data.zero_() self.kernel_size = kernel_size self.stride = stride self.padding = padding self.dilation = dilation self.groups = groups def forward(self, x): # 1. 通过偏移量卷积层预测偏移量 (和调制因子) offset = self.conv_offset(x) # shape: [batch, (2 or 3)*groups*kH*kW, H_out, W_out] # 2. 如果使用DCNv1 (无调制),调用 deform_conv2d 函数 if not self.modulation: output = deform_conv2d(x, offset, self.weight, bias=self.bias, stride=self.stride, padding=self.padding, dilation=self.dilation, groups=self.groups) # 3. 如果使用DCNv2 (带调制) else: # 将 offset 张量拆分为偏移量 (offset) 和调制因子 (mask) off_channels = 2 * self.groups * self.kernel_size[0] * self.kernel_size[1] mask_channels = self.groups * self.kernel_size[0] * self.kernel_size[1] # 假设 offset 的形状是 [batch, (2+1)*groups*kH*kW, H_out, W_out] offsets, mask = torch.split(offset, [off_channels, mask_channels], dim=1) # 对 mask 应用 sigmoid 激活函数将其限制在 [0, 1] 范围 mask = torch.sigmoid(mask) # 调用支持调制的 deform_conv2d 函数 (例如 mmdet 或 dcn 库中的实现) output = deform_conv2d(x, offsets, self.weight, mask=mask, bias=self.bias, stride=self.stride, padding=self.padding, dilation=self.dilation, groups=self.groups) return output ``` **关键点总结:** 1. **核心差异:** DCNv1 只学习偏移量 $(\Delta x, \Delta y)$;DCNv2 额外学习调制因子 $\Delta m$ 并支持在更多网络层应用。 2. **偏移量预测:** 通过一个并行的、从输入特征图学习的卷积层来预测偏移量(和调制因子)。 3. **双线性插值:** 是实现非网格位置采样的关键技术,必须是可导的。 4. **实现依赖:** 实际工程实现通常依赖优化过的底层算子(如 `deform_conv2d`),手动实现高效且正确的双线性插值采样和梯度回传较为复杂。 5. **应用位置:** 研究表明,替换骨干网络(如 ResNet)中 `conv3`, `conv4`, `conv5` 阶段的 3x3 卷积为可变形卷积(特别是 DCNv2)通常能取得较好的效果[^1],但需注意在复杂数据集上过多层可能导致过拟合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值