图像超分辨率模型记录:PAN — Efficient Image Super-Resolution Using Pixel Attention

前言

论文题目:Efficient Image Super-Resolution Using Pixel Attention —— 基于像素注意的高效图像超分辨率

论文地址Efficient Image Super-Resolution Using Pixel Attention

论文源码https://github.com/zhaohengyuan1/PAN



1. 模型介绍

这项工作旨在设计一种用于图像超分辨率(SR)的轻量级卷积神经网络。在不考虑简单性的情况下,我们使用新提出的像素注意力方案构建了一个非常简洁有效的网络。像素注意力(Pixel Attention,PA)类似于公式中的通道注意力和空间注意力。不同之处在于,PA生成3D注意力图而不是1D注意力矢量或2D图。该注意力策略引入了较少的附加参数,但生成了更好的SR结果。在PA的基础上,我们分别为主分支重建分支提出了两个构造块:第一个SC-PA块的结构与自校准卷积相同,但具有我们的PA层。由于其双分支架构和注意力方案,该模块块比常规的残差 / 密集块效率更高;第二个U-PA块结合了最近邻上采样、卷积和PA层。它以很少的参数成本提升了最终的重建质量。我们的最终模型PAN可以达到与轻量级网络SRResNet和CARN相似的性能,但是参数只有272K(SRResNet的17.92%和CARN的17.09%)。每个提出组件的有效性也通过消融研究得到验证。

2. 像素注意力(Pixel Attention)

像素注意力机制与通道 / 空间注意力机制的区别就是:

  1. 通道注意力生成1 × \times × 1 × \times × C维度的注意力权重
  2. 空间注意力生成H × \times × W × \times × 1维度的注意力权重
  3. 像素注意力生成H × \times × W × \times × C维度的注意力权重

像素注意力的网络结构也非常简单,就是1 × \times × 1卷积 + sigmoid激活函数。

在这里插入图片描述
具体来说,如图所示,通道注意通过空间全局池将前一个特征汇集到一个向量中,而空间注意通过通道池将特征汇集到单个特征映射中。我们发现这些方案在SR任务中效果较差,这需要像素级评估。另一方面,简单地删除池化操作可以显着提高性能。由于特征在像素级模型中相乘,我们将此修改后的注意力方案称为像素注意力,我们的网络称为像素注意力网络(PAN)。

3. 模型结构

模型结构主要是主分支的SC-PA模块(带有像素注意的自校准块)和重建分支的U-PA模块(像素注意的上采样块)。具体如下图所示:

在这里插入图片描述

SC-PA模块对输入先使用两个1 × \times × 1卷积进行分离,上层负责更高层次的特征操作且采用像素注意力PA,下层负责维护原始信息且采用标准卷积。
U-PA模块在卷积层之间引入PA,在以前的SR网络中,重建模块基本上由上采样和卷积层组成。 而且,很少有研究者调查过上采样阶段的注意力机制。 因此,在这项工作中,在重建模块中采用了PA层。 实验表明,引入PA可以显着提高最终性能,而参数成本却很少。 此外,本文还使用最近邻插值层(NN)作为上采样层以进一步节省参数。最终,U-PA块由NN、卷积层、PA、卷积层组成。

论文所提出的PAN专为高效的SR而设计,因此在网络架构中非常简洁。构建块SC-PA和U-PA也简单且易于实现。然而,当将此网络扩展到更大的规模(即 >50 个块)时,当前的结构将面临训练难度的问题。然后需要添加其他技术,如密集连接,以允许成功的训练。其次所提出的 PA方案还有另一个限制,通过实验发现,PA 对小型网络特别有用。但是随着网络规模的增加,有效性会降低。这主要是因为PA可以提高卷积的表达能力,这对于轻量级网络可能非常重要。相比之下,大规模网络是高度冗余的,它们的卷积没有得到充分利用,因此改进主要来自更好的训练策略

4. 模型实现

4.1 像素注意力PA代码实现

在这里插入图片描述

class PA(nn.Module):
    '''PA is pixel attention(像素注意力模块)
    通过1x1卷积生成注意力权重,然后与原图相乘实现像素注意力机制
    '''
    def __init__(self, nf):
        """初始化
        Args:
            nf (int): 输入特征图的通道数
        """
        super(PA, self).__init__()
        # 1x1卷积层,用于生成注意力权重(不改变通道数)
        self.conv = nn.Conv2d(nf, nf, 1)
        # Sigmoid激活函数,将权重压缩到[0,1]范围
        self.sigmoid = nn.Sigmoid()
 
    def forward(self, x):
        """前向传播
        Args:
            x (Tensor): 输入特征图,形状为[batch, nf, height, width]
        Returns:
            Tensor: 加权后的特征图,形状与输入相同
        """
        # 通过1x1卷积生成注意力权重
        y = self.conv(x)
        # 应用Sigmoid激活函数
        y = self.sigmoid(y)
        # 将原始特征图与注意力权重相乘(逐元素相乘)
        out = torch.mul(x, y)
 
        return out
 
 
# 给x和输出套conv(改进版的像素注意力模块)
class PAConv(nn.Module):
    """改进的像素注意力模块,增加了额外的卷积层
    结构:
    1. 使用1x1卷积生成注意力权重
    2. 注意力权重与3x3卷积后的特征图相乘
    3. 最后再通过一个3x3卷积
    """
    def __init__(self, nf, k_size=3):
        """初始化
        Args:
            nf (int): 输入特征图的通道数
            k_size (int, optional): 卷积核大小,默认为3
        """
        super(PAConv, self).__init__()
        # 1x1卷积层,用于生成注意力权重
        self.k2 = nn.Conv2d(nf, nf, 1)  # 1x1 convolution nf->nf
        # Sigmoid激活函数
        self.sigmoid = nn.Sigmoid()
        # 第一个3x3卷积层(保持空间维度不变)
        self.k3 = nn.Conv2d(nf, nf, kernel_size=k_size, 
                          padding=(k_size - 1) // 2, bias=False)  # 3x3 convolution
        # 第二个3x3卷积层(保持空间维度不变)
        self.k4 = nn.Conv2d(nf, nf, kernel_size=k_size, 
                          padding=(k_size - 1) // 2, bias=False)  # 3x3 convolution
 
    def forward(self, x):
        """前向传播
        Args:
            x (Tensor): 输入特征图,形状为[batch, nf, height, width]
        Returns:
            Tensor: 处理后的特征图,形状与输入相同
        """
        # 通过1x1卷积生成注意力权重
        y = self.k2(x)
        # 应用Sigmoid激活函数
        y = self.sigmoid(y)
        
        # 先对输入进行3x3卷积,然后与注意力权重相乘
        out = torch.mul(self.k3(x), y)
        # 对结果再进行一次3x3卷积
        out = self.k4(out)
 
        return out

4.2 SC-PA模块代码实现

在这里插入图片描述

class SCPA(nn.Module):
    """SCPA (Self-Calibrated Pixel Attention) 模块,改进自SCNet (Jiang-Jiang Liu et al. 2020)
    论文: Improving Convolutional Networks with Self-Calibrated Convolutions
    代码参考: https://github.com/MCG-NKU/SCNet
    
    该模块通过自校准机制增强特征表示,结合了通道分割、空间卷积和像素注意力
    """
 
    def __init__(self, nf, reduction=2, stride=1, dilation=1):
        """初始化
        Args:
            nf (int): 输入特征图的通道数
            reduction (int, optional): 通道缩减比例,默认为2
            stride (int, optional): 卷积步长,默认为1
            dilation (int, optional): 空洞卷积的膨胀率,默认为1
        """
        super(SCPA, self).__init__()
        group_width = nf // reduction  # 计算每组通道数
        
        # 分支a的1x1卷积,用于通道缩减
        self.conv1_a = nn.Conv2d(nf, group_width, kernel_size=1, bias=False)
        # 分支b的1x1卷积,用于通道缩减
        self.conv1_b = nn.Conv2d(nf, group_width, kernel_size=1, bias=False)
        
        # 分支a的处理路径:3x3卷积
        self.k1 = nn.Sequential(
                    nn.Conv2d(
                        group_width, group_width, kernel_size=3, stride=stride,
                        padding=dilation, dilation=dilation,
                        bias=False)
                    )
        
        # 分支b的处理路径:像素注意力卷积模块
        self.PAConv = PAConv(group_width)
        
        # 合并后的1x1卷积,用于通道恢复
        self.conv3 = nn.Conv2d(
            group_width * reduction, nf, kernel_size=1, bias=False)
        
        # LeakyReLU激活函数
        self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True)
 
    def forward(self, x):
        """前向传播
        Args:
            x (Tensor): 输入特征图,形状为[batch, nf, height, width]
        Returns:
            Tensor: 处理后的特征图,形状与输入相同
        """
        residual = x  # 保存残差连接
 
        # 分支a处理路径
        out_a = self.conv1_a(x)  # 1x1卷积通道缩减
        out_b = self.conv1_b(x)  # 1x1卷积通道缩减
        
        # 对两个分支都应用LeakyReLU激活
        out_a = self.lrelu(out_a)
        out_b = self.lrelu(out_b)
 
        # 分支a进一步处理:3x3卷积
        out_a = self.k1(out_a)
        # 分支b进一步处理:PAConv像素注意力
        out_b = self.PAConv(out_b)
        
        # 对两个分支的结果再次应用LeakyReLU激活
        out_a = self.lrelu(out_a)
        out_b = self.lrelu(out_b)
 
        # 合并两个分支的结果(沿通道维度拼接)
        out = self.conv3(torch.cat([out_a, out_b], dim=1))
        # 添加残差连接
        out += residual
 
        return out

4.3 PAN模型代码实现

class PAN(nn.Module):
    """Pixel Attention Network (PAN) 主网络结构
    该网络主要用于图像超分辨率任务,结合了SCPA模块和PA模块
    
    结构概述:
    1. 初始卷积层
    2. 由多个SCPA模块组成的主干网络
    3. 上采样部分(根据不同的放大倍数有不同的结构)
    4. 最后的卷积层和残差连接
    """
    
    def __init__(self, in_nc, out_nc, nf, unf, nb, scale=4):
        """初始化
        Args:
            in_nc (int): 输入通道数
            out_nc (int): 输出通道数
            nf (int): 主干网络的通道数
            unf (int): 上采样部分的通道数
            nb (int): 主干网络中SCPA模块的数量
            scale (int, optional): 上采样倍数,默认为4
        """
        super(PAN, self).__init__()
        # 定义SCPA模块的partial函数,固定nf和reduction参数
        SCPA_block_f = functools.partial(SCPA, nf=nf, reduction=2)
        self.scale = scale  # 上采样倍数
        
        ### 初始卷积层
        self.conv_first = nn.Conv2d(in_nc, nf, 3, 1, 1, bias=True)
        
        ### 主干网络部分
        # 由nb个SCPA模块组成的主干网络
        self.SCPA_trunk = arch_util.make_layer(SCPA_block_f, nb)    
        # 主干网络后的卷积层
        self.trunk_conv = nn.Conv2d(nf, nf, 3, 1, 1, bias=True)
        
        #### 上采样部分
        # 第一层上采样卷积
        self.upconv1 = nn.Conv2d(nf, unf, 3, 1, 1, bias=True)
        # 第一层像素注意力模块
        self.att1 = PA(unf)
        # 第一层高分辨率卷积
        self.HRconv1 = nn.Conv2d(unf, unf, 3, 1, 1, bias=True)
        
        # 针对scale=4的特殊处理(两层上采样)
        if self.scale == 4:
            # 第二层上采样卷积
            self.upconv2 = nn.Conv2d(unf, unf, 3, 1, 1, bias=True)
            # 第二层像素注意力模块
            self.att2 = PA(unf)
            # 第二层高分辨率卷积
            self.HRconv2 = nn.Conv2d(unf, unf, 3, 1, 1, bias=True)
            
        # 最后的输出卷积层
        self.conv_last = nn.Conv2d(unf, out_nc, 3, 1, 1, bias=True)
        # LeakyReLU激活函数
        self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True)
 
    def forward(self, x):
        """前向传播
        Args:
            x (Tensor): 输入图像,形状为[batch, in_nc, height, width]
        Returns:
            Tensor: 超分辨率输出图像,形状为[batch, out_nc, height*scale, width*scale]
        """
        # 初始特征提取
        fea = self.conv_first(x)
        # 通过主干网络
        trunk = self.trunk_conv(self.SCPA_trunk(fea))
        # 残差连接
        fea = fea + trunk
        
        # 上采样部分(根据不同的scale有不同的处理)
        if self.scale == 2 or self.scale == 3:
            # 单层上采样
            fea = self.upconv1(F.interpolate(fea, scale_factor=self.scale, mode='nearest'))
            fea = self.lrelu(self.att1(fea))  # 像素注意力
            fea = self.lrelu(self.HRconv1(fea))  # 高分辨率卷积
        elif self.scale == 4:
            # 两层上采样
            fea = self.upconv1(F.interpolate(fea, scale_factor=2, mode='nearest'))
            fea = self.lrelu(self.att1(fea))  # 第一层像素注意力
            fea = self.lrelu(self.HRconv1(fea))  # 第一层高分辨率卷积
            fea = self.upconv2(F.interpolate(fea, scale_factor=2, mode='nearest'))
            fea = self.lrelu(self.att2(fea))  # 第二层像素注意力
            fea = self.lrelu(self.HRconv2(fea))  # 第二层高分辨率卷积
        
        # 最终输出
        out = self.conv_last(fea)
        
        # 残差连接:添加原始输入的上采样结果(双三次插值)
        ILR = F.interpolate(x, scale_factor=self.scale, mode='bilinear', align_corners=False)
        out = out + ILR
        
        return out
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值