文章链接:(ECCV 2018)https://arxiv.org/abs/1807.02758
代码链接:https://github.com/yulunzhang/RCAN
卷积神经网络(CNN)的深度是图像超分辨率(SR)的关键。现有SR网络面临以下问题:
为了解决这些问题,我们提出了深度残差通道注意网络(RCAN)。在RCAN中,我们提出了一种由几个具有长跳跃连接的残差组构成的residual in residual(RIR)结构来构建深度网络。每个残差组中包含一些具有短跳跃连接的残差块。整个RIR结构通过运用多个跳跃连接,让低频信息绕过网络,使主干网络只学习到高频信息。
此外,文章还提出了一种通道注意机制(CA),通过考虑通道间的相互依赖性来自适应地调整不同通道的权重。
大量的实验表明RCAN在准确性和视觉效果上超越了现有的SOTA(2018)。
现在基于深度学习的SR面临的问题:
本文的主要贡献:
网络结构如下所示:
网络由4个模块构成:
浅层特征提取——由1个卷积层head
构成:conv(args.n_colors, n_feats, kernel_size)
;
深层特征提取——即文章提出的RIR结构;
上采样——上采样用的是PixelShuffle,并且跟EDSR是一样的策略:
重建——用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)
从上面的结构图中可以看出,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
为了使网络将重点放在有用信息多的特征上,作者利用特征通道之间的相互依赖性,提出了一种通道注意力(CA)机制(如下图所示)。
其中最关键的一步是:怎么给每个channel-wise feature生成不一样的attention,这里主要考虑两点:
通道注意力机制(CA)的算法流程:
(可见,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
从图可见,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
从结果看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