【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks

【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks_第1张图片


文章目录

  • Abstract
  • 1 Introduction
  • 2 Proposed Method
    • 2.1 Network Architecture
    • 2.2 Residual in Residual (RIR)
    • 2.3 Channel Attention (CA)
    • 2.4 Residual Channel Attention Block (RCAB)
  • 3 Experiment
      • 【其他超分辨方向论文】



文章链接:(ECCV 2018)https://arxiv.org/abs/1807.02758
代码链接:https://github.com/yulunzhang/RCAN


Abstract

卷积神经网络(CNN)的深度是图像超分辨率(SR)的关键。现有SR网络面临以下问题:

  • 随着网络的加深,这些网络也会越来越难训练。
  • LR和低级特征中包含SR了丰富的低频信息,SR网络在不同通道中却采用了相同的方式对其进行处理,影响了CNN的表现。

为了解决这些问题,我们提出了深度残差通道注意网络(RCAN)。在RCAN中,我们提出了一种由几个具有长跳跃连接的残差组构成的residual in residual(RIR)结构来构建深度网络。每个残差组中包含一些具有短跳跃连接的残差块。整个RIR结构通过运用多个跳跃连接,让低频信息绕过网络,使主干网络只学习到高频信息。

此外,文章还提出了一种通道注意机制(CA)通过考虑通道间的相互依赖性来自适应地调整不同通道的权重

大量的实验表明RCAN在准确性和视觉效果上超越了现有的SOTA(2018)。



1 Introduction

现在基于深度学习的SR面临的问题:

  • 从EDSR和MDSR可以看出,网络的深度对图像SR起到很关键的作用,但是仅仅堆叠残差块构建出来的网络难以进一步提高性能了。
  • 现有的CNN-based网络对不同的通道特征都是一样看待的,而SR任务应该尽可能多的恢复一些高频信息(图像的高频信息就是灰度变化快的地方,比如边缘、角点等)。由于LR图像主要都是低频信息,最好将其直接输给最终的HR,没有必要浪费计算量。然而现有的EDSR等网络直接从LR中提取特征并对每个通道都做一样的处理,把计算量浪费在了低频信息上,阻碍了网络的性能提升。

本文的主要贡献:

  • 设计了一个残差通道注意力网络(RCAN),使网络可以变得更深并提高性能。
  • 提出了residual in residual(RIR)结构,即由多个残差组和长跳跃连接构建粗粒度的残差学习,在残差组内部再堆叠多个简化的残差块并采用短跳跃连接(大的残差内部再套娃小残差)。这种结构可以使低频信息绕过网络,从而提高信息处理的效率。
  • 提出了通道注意力(CA)机制,通过特征通道之间的相互依赖性来重新调整特征权重



2 Proposed Method

2.1 Network Architecture

网络结构如下所示:

【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks_第2张图片

网络由4个模块构成:

  • 浅层特征提取——由1个卷积层head构成:conv(args.n_colors, n_feats, kernel_size)

  • 深层特征提取——即文章提出的RIR结构;

  • 上采样——上采样用的是PixelShuffle,并且跟EDSR是一样的策略:

    • 2倍和3倍时先用卷积增加通道数到n_feat*scale^2,然后PixelShuffle;
    • 4倍时就相当于把2倍的上采样循环2次;
  • 重建——用1个卷积层把通道数恢复到输入图片的通道数:conv(n_feats, args.n_colors, kernel_size)

    (上采样和重建的代码通常放在一起,即tail里面)。


RCAN的部分主要代码如下:

class RCAN(nn.Module):
    def __init__(self, args, conv=common.default_conv):
        super(RCAN, self).__init__()
        
        n_resgroups = args.n_resgroups
        n_resblocks = args.n_resblocks
        n_feats = args.n_feats
        kernel_size = 3
        reduction = args.reduction 
        scale = args.scale[0]
        act = nn.ReLU(True)
        
        # RGB mean for DIV2K
        rgb_mean = (0.4488, 0.4371, 0.4040)
        rgb_std = (1.0, 1.0, 1.0)
        self.sub_mean = common.MeanShift(args.rgb_range, rgb_mean, rgb_std)
        
        # define head module
        modules_head = [conv(args.n_colors, n_feats, kernel_size)]

        # define body module
        modules_body = [
            ResidualGroup(
                conv, n_feats, kernel_size, reduction, act=act, res_scale=args.res_scale, n_resblocks=n_resblocks) \
            for _ in range(n_resgroups)]	# default=10

        modules_body.append(conv(n_feats, n_feats, kernel_size))

        # define tail module
        modules_tail = [
            common.Upsampler(conv, scale, n_feats, act=False),
            conv(n_feats, args.n_colors, kernel_size)]

        self.add_mean = common.MeanShift(args.rgb_range, rgb_mean, rgb_std, 1)

        self.head = nn.Sequential(*modules_head)
        self.body = nn.Sequential(*modules_body)
        self.tail = nn.Sequential(*modules_tail)

    def forward(self, x):
        x = self.sub_mean(x)
        x = self.head(x)

        res = self.body(x)
        res += x

        x = self.tail(res)
        x = self.add_mean(x)

        return x

上采样部分代码:

class Upsampler(nn.Sequential):
    def __init__(self, conv, scale, n_feat, bn=False, act=False, bias=True):

        m = []
        if (scale & (scale - 1)) == 0:    # Is scale = 2^n?
            for _ in range(int(math.log(scale, 2))):
                m.append(conv(n_feat, 4 * n_feat, 3, bias))
                m.append(nn.PixelShuffle(2))
                if bn: m.append(nn.BatchNorm2d(n_feat))
                if act: m.append(act())
        elif scale == 3:
            m.append(conv(n_feat, 9 * n_feat, 3, bias))
            m.append(nn.PixelShuffle(3))
            if bn: m.append(nn.BatchNorm2d(n_feat))
            if act: m.append(act())
        else:
            raise NotImplementedError

        super(Upsampler, self).__init__(*m)

2.2 Residual in Residual (RIR)

从上面的结构图中可以看出,residual in residual(RIR)结构的最外层由G个残差组以及一个长跳跃连接构成,从而形成了一个粗粒度的残差学习。在每一个残差组的内部,则是由B个残差通道注意力块(RCAB)以及一个小的跳跃连接构成。简单来说,这个residual in residual就是大残差内部再套娃小残差。

长跳跃连接可以使网络在更加粗粒度的层次上学习到残差信息。而短跳跃连接则是一种细粒度的identity-based的跳跃连接,使得大量网络不需要的低频信息得到过滤。

为了进一步实现自适应的辨别学习(discriminative learning),作者提出了通道注意力机制(CA)并在RCAB中进行了运用,其目的是给更有价值的通道更高的权重。


残差组Residual Group主要代码:

## Residual Group (RG)
class ResidualGroup(nn.Module):
    def __init__(self, conv, n_feat, kernel_size, reduction, act, res_scale, n_resblocks):
        super(ResidualGroup, self).__init__()
        modules_body = []
        modules_body = [
            RCAB(
                conv, n_feat, kernel_size, reduction, bias=True, bn=False, act=nn.ReLU(True), res_scale=1) \
            for _ in range(n_resblocks)]	# default=20 
        modules_body.append(conv(n_feat, n_feat, kernel_size))
        self.body = nn.Sequential(*modules_body)

    def forward(self, x):
        res = self.body(x)
        res += x
        return res

2.3 Channel Attention (CA)

为了使网络将重点放在有用信息多的特征上,作者利用特征通道之间的相互依赖性,提出了一种通道注意力(CA)机制(如下图所示)。

【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks_第3张图片

其中最关键的一步是:怎么给每个channel-wise feature生成不一样的attention,这里主要考虑两点:

  • LR中有丰富的低频和高频信息,但是其中的低频部分相对比较平坦,而高频部分通常是充满边缘、纹理等细节的区域。
  • 卷积层中的每个filter都有单独的局部感受野,因此卷积的输出无法利用局部区域之外的上下文信息。(?)

通道注意力机制(CA)的算法流程:

  • 首先对输入的特征图 X = [ x 1 , … , x c , x C ] X=[x_1,\dots,x_c,x_C] X=[x1,,xc,xC]全局平均池化,给每个通道都分别计算一个数据值 z c z_c zc
  • 继续处理这些数据值 z c z_c zc,【downscale–>Relu–>upscale–>Sigmoid】,其中下采样的reduction ratio为 r。处理得到最终的通道统计量 s s s
  • 最后,将通道统计量 s s s 与CA的输入特征图 x x x 做element-wise的相乘。

(可见,CA并不改变特征图的尺寸和通道数,只是给同一通道中的所有像素都乘上一个同一个值。如果某个通道比较重要,那么里面所有的元素的权重都被乘以一个比较高的系数,反之亦然。)


CA的代码如下:

class CALayer(nn.Module):
    def __init__(self, channel, reduction=16):
        super(CALayer, self).__init__()
        # global average pooling: feature --> point
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        # feature channel downscale and upscale --> channel weight
        self.conv_du = nn.Sequential(
                nn.Conv2d(channel, channel // reduction, 1, padding=0, bias=True),
                nn.ReLU(inplace=True),
                nn.Conv2d(channel // reduction, channel, 1, padding=0, bias=True),
                nn.Sigmoid()
        )

    def forward(self, x):
        y = self.avg_pool(x)
        y = self.conv_du(y)
        return x * y



2.4 Residual Channel Attention Block (RCAB)


【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks_第4张图片

从图可见,RCAB就是在EDSR提出的改进版残差块 R B RB RB(去掉了两个batch norm)的基础上,再往里面加入了通道注意力(CA)。


RCAB代码:

class RCAB(nn.Module):
    def __init__(
        self, conv, n_feat, kernel_size, reduction,
        bias=True, bn=False, act=nn.ReLU(True), res_scale=1):

        super(RCAB, self).__init__()
        modules_body = []
        for i in range(2):
            modules_body.append(conv(n_feat, n_feat, kernel_size, bias=bias))
            if bn: modules_body.append(nn.BatchNorm2d(n_feat))
            if i == 0: modules_body.append(act)
        modules_body.append(CALayer(n_feat, reduction))
        self.body = nn.Sequential(*modules_body)
        self.res_scale = res_scale

    def forward(self, x):
        res = self.body(x)
        res += x
        return res



3 Experiment

【论文笔记4_超分辨】(RCAN)Image Super-Resolution Using Very Deep Residual Channel Attention Networks_第5张图片

从结果看RCAN在这几个数据集上的测试效果还算比较不错的,作者也在消融实验中验证了RIR和CA的有效性。




【其他超分辨方向论文】

【1】(RDN)Residual Dense Network for Image Super-Resolution

【2】(IDN)Fast and Accurate Single Image Super-Resolution via Information Distillation Network

【3】(DRN)Closed-loop Matters: Dual Regression Networks for Single Image Super-Resolution

你可能感兴趣的:(超分辨论文笔记,ECCV,2018,深度学习,计算机视觉)