特征图上采样:CARAFE

CARAFE: Content-Aware ReAssembly of FEatures

CARAFE: 轻量级通用上采样算子

其他上采样方法的不足

  • 最近邻或者双线性上采样
    仅通过像素点的空间位置来决定上采样核,并没有利用到特征图的语义信息,可以看作是一种“均匀”的上采样,而且感知域通常都很小(最近邻 1x1,双线性 2x2);
  • Deconvolution
    上采样核并不是通过像素间的距离计算,而是通过网络学出来的,但对于特征图每个位置都是应用相同的上采样核,不能捕捉到特征图内容的信息,另外引入了大量参数和计算量,尤其是当上采样核尺寸较大的时候;
  • Dynamic filter
    对于特征图每个位置都会预测一组不同的上采样核,但是参数量和计算量更加爆炸,而且公认比较难学习

理想上采样算子的特性

  • Large receptive field:需要具有较大的感受野,这样才能更好地利用周围的信息;
  • Content-aware:上采样核应该和特征图的语义信息相关,基于输入内容进行上采样;
  • Lightweight:轻量化,不能引入过多的参数和计算量;

CARAFE

特征图上采样:CARAFE_第1张图片
CARAFE 分为两个主要模块,分别是上采样核预测模块特征重组模块。假设上采样倍率为 σ σ σ σσ \sigma σσσkencoder=3,kup=5,(性能与计算量的折中)
特征图上采样:CARAFE_第2张图片

  • 归一化的方法
    Softmax与Sigmoid Normalized 性能相同
    特征图上采样:CARAFE_第3张图片
  • pytorch 实现

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

class CARAFE(nn.Module):
def init(self, inC, outC, kernel_size=3, up_factor=2):
super(CARAFE, self).init()
self.kernel_size = kernel_size
self.up_factor = up_factor
self.down = nn.Conv2d(inC, inC // 4, 1)
self.encoder = nn.Conv2d(inC // 4, self.up_factor 2 * self.kernel_size 2,
self.kernel_size, 1, self.kernel_size // 2)
self.out = nn.Conv2d(inC, outC, 1)

def forward(self, in_tensor):
    N, C, H, W = in_tensor.size()

    # N,C,H,W -> N,C,delta*H,delta*W
    # kernel prediction module
    kernel_tensor = self.down(in_tensor)  # (N, Cm, H, W)
    kernel_tensor = self.encoder(kernel_tensor)  # (N, S^2 * Kup^2, H, W)
    kernel_tensor = F.pixel_shuffle(kernel_tensor, self.up_factor)  # (N, S^2 * Kup^2, H, W)->(N, Kup^2, S*H, S*W)
    kernel_tensor = F.softmax(kernel_tensor, dim=1)  # (N, Kup^2, S*H, S*W)
    kernel_tensor = kernel_tensor.unfold(2, self.up_factor, step=self.up_factor) # (N, Kup^2, H, W*S, S)
    kernel_tensor = kernel_tensor.unfold(3, self.up_factor, step=self.up_factor) # (N, Kup^2, H, W, S, S)
    kernel_tensor = kernel_tensor.reshape(N, self.kernel_size ** 2, H, W, self.up_factor ** 2) # (N, Kup^2, H, W, S^2)
    kernel_tensor = kernel_tensor.permute(0, 2, 3, 1, 4)  # (N, H, W, Kup^2, S^2)

    # content-aware reassembly module
    # tensor.unfold: dim, size, step
    in_tensor = F.pad(in_tensor, pad=(self.kernel_size // 2, self.kernel_size // 2,
                                      self.kernel_size // 2, self.kernel_size // 2),
                      mode='constant', value=0) # (N, C, H+Kup//2+Kup//2, W+Kup//2+Kup//2)
    in_tensor = in_tensor.unfold(2, self.kernel_size, step=1) # (N, C, H, W+Kup//2+Kup//2, Kup)
    in_tensor = in_tensor.unfold(3, self.kernel_size, step=1) # (N, C, H, W, Kup, Kup)
    in_tensor = in_tensor.reshape(N, C, H, W, -1) # (N, C, H, W, Kup^2)
    in_tensor = in_tensor.permute(0, 2, 3, 1, 4)  # (N, H, W, C, Kup^2)

    out_tensor = torch.matmul(in_tensor, kernel_tensor)  # (N, H, W, C, S^2)
    out_tensor = out_tensor.reshape(N, H, W, -1)
    out_tensor = out_tensor.permute(0, 3, 1, 2)
    out_tensor = F.pixel_shuffle(out_tensor, self.up_factor)
    out_tensor = self.out(out_tensor)
    return out_tensor

if name == main:
data = torch.rand(4, 20, 10, 10)
carafe = CARAFE(20, 10)
print(carafe(data).size())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
                                

你可能感兴趣的:(目标检测)