论文笔记——TVConv: Efficient Translation Variant Convolution for Layout-aware Visual Processing

论文笔记——TVConv: Efficient Translation Variant Convolution for Layout-aware Visual Processing

4月 1, 2022 阅读 2917 字数 2400 评论 0 喜欢 2

(CVPR2022论文)

以前存在的问题:静态和动态卷积要么与布局无关,要么计算量大,不适用于特定于布局的应用程序,例如人脸识别和医学图像分割。

创新点:
①作者观察到人脸识别的这类图像,图像内方差大,图像间方差小的特点,提出了TVConv用于布局感知的视觉处理
②TVConv由亲和力图(affinity maps)和权重生成块组成,亲和力图描绘了像素配对关系,权重生成块可以避免过度参数化
③与深度卷积相比,TV Conv 将计算成本降低了 3.1 倍,并将相应的吞吐量提高了 2.3 倍,同时保持了较高的精度。

与其他卷积的比较

首先指定可学习的亲和力图来区分局部特征,亲和力图隐式地捕获不同区域的语义关系(可看作一种注意力机制)。然后将亲和力图喂入权重生成块来产生权重,作为一种filter应用于输入。亲和力图训练完之后还可以调整以适应不同空间特征。

左边第一个图代表不同区域的卷积权重是可变的,左边第二个图代表普通的卷积不同区域权重相同。
右上角是一个表,x轴代表跨图像方差,y轴代表图像内方差。

整体结构

作者想要做一个全局卷积W,但是带来的问题就是参数量巨大而且容易过拟合,参数量是c×k×k×h×w,k为核的大小。于是把W分解为W’=B’A’,其中W’是W的reshape版本,尺寸为(c×k×k)×(h×w),然后B’是basis矩阵尺寸为(c×k×k)×c_A,然后A’是系数矩阵尺寸为c_A×(h×w)
经过这一操作,参数量就可以从 (ckkhw)减少到 (ckkc_A + c_Ahw),一般取c_A=1

A代表亲和力图,B代表权重生成块

CODE

class TVConv(nn.Module):
    def __init__(self,
                 channels,
                 TVConv_k=3,
                 stride=1,
                 TVConv_posi_chans=4,
                 TVConv_inter_chans=16,
                 TVConv_inter_layers=1,
                 TVConv_Bias=False,
                 h=3,
                 w=3,
                 **kwargs):
        super(TVConv, self).__init__()
        # 以下参数定义为常量,当模型保存的时候也保存
        self.register_buffer("TVConv_k", torch.as_tensor(TVConv_k))
        self.register_buffer("TVConv_k_square", torch.as_tensor(TVConv_k**2))
        self.register_buffer("stride", torch.as_tensor(stride))
        self.register_buffer("channels", torch.as_tensor(channels))
        self.register_buffer("h", torch.as_tensor(h))
        self.register_buffer("w", torch.as_tensor(w))

        self.bias_layers = None
        # 输出channel,代表图中的k^2c
        out_chans = self.TVConv_k_square * self.channels
        # 亲和力图初始化,默认大小为4*h*w
        self.posi_map = nn.Parameter(torch.Tensor(1, TVConv_posi_chans, h, w))
        # 亲和力图初始化值为1
        nn.init.ones_(self.posi_map)
        # 权重生成器,如上图所示,由多个(默认是一个)3*3conv、layernorm、relu三连构成,最后带上一个3*3conv收尾
        self.weight_layers = self._make_layers(TVConv_posi_chans, TVConv_inter_chans, out_chans, TVConv_inter_layers, h, w)
        # 权重bias层,通过权重生成器对亲和图卷,来产生一个输出channel跟输入channel一样的权重来做相加
        if TVConv_Bias:
            self.bias_layers = self._make_layers(TVConv_posi_chans, TVConv_inter_chans, channels, TVConv_inter_layers, h, w)

        self.unfold = nn.Unfold(TVConv_k, 1, (TVConv_k-1)//2, stride) # 只卷不积

    def _make_layers(self, in_chans, inter_chans, out_chans, num_inter_layers, h, w):
        ...

    def forward(self, x):
        # 权重生成器对亲和力图卷
        weight = self.weight_layers(self.posi_map)
        # reshape
        weight = weight.view(1, self.channels, self.TVConv_k_square, self.h, self.w)
        # 把输入reshape成跟上面权重的形状一样
        out = self.unfold(x).view(x.shape[0], self.channels, self.TVConv_k_square, self.h, self.w)
        # 相乘,并对需要卷的k*k方格累加
        out = (weight * out).sum(dim=2)
        # 加上权重bias层
        if self.bias_layers is not None:
            bias = self.bias_layers(self.posi_map)
            out = out + bias

        return out

发表评论

您的电子邮箱地址不会被公开。