博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Involution 笔记:
阅读量:4131 次
发布时间:2019-05-25

本文共 3209 字,大约阅读时间需要 10 分钟。

Involution 个人理解

传统卷积具有 :

​ ①空间不变性:卷积核共享。能有效地节省模型参数,且能维护平移的等变性。由于不能引入过多的参数,所以采用比较小的卷积核,但是小的卷积核(如3*3)不能获得广的感受野 (因此involution更容易检测出大物体

​ ② 通道特异性:能在不同的通道中包含不同的语义信息,在许多成功的深度神经网络上表现出比较多的冗余性

​ 原本卷积核需要的参数数量 = C0(输入通道)×Ci(输出通道)×K×K

所以传统卷积比较难去得到长距离的感知,所以提出involution。

involution具有与传统卷积相反的:

​ ①空间特异性:在不同位置上的卷积核是不同的。(kernel对应在一个空间位置上的参数值要与这个位置对应的输入特征向量相关(或者仅由其决定),incident可以自适应地为不同空间位置分配不同的权重)

​ ②通道共享: 每一个组内的通道共享一个卷积核。这样还可以减少计算复杂度

​ involution卷积需要的参数数量 = H×W×K×K×G (H和W是什么?feature map的大小,因为每一个pixel都会产生G个卷积核,所以H×W是feature map的面积,即含有的像素点的个数。将1个像素通过全连接层和激活函数生成K×K×G个维度,通过reshape编程矩阵形式,再通过broadcast生成同样的一定深度,多个通道共享一个卷积核)

卷积核生成

img

Xi,j代表位置(i,j)的特征向量。Wo和W1都是线性变换矩阵,前者可以使用1x1卷积用作压缩通道数,后者再将通道数扩展,类似于SE-Net的Sequeeze&Excitation过程。σ函数是BN和ReLU。而一个位置需要G个不同的卷积核,而应用conv2d卷积时,输出的shape最多只有4个维度,所以要把G和K×K参数放一起,最终输出的维度为(B,G×K×K,H,W)。最后要扩展多一个维度方便进行广播操作。

由于有了下述变换:Involution卷积比传统卷积的参数量要更少:第一层1×1卷积将通道从C压缩到C/r(r为压缩率),经过一个BN层和ReLu层后再由另一个1×1卷积扩张到G×K×K通道数。因此整个过程的参数量为:输入通道数×输出通道数×卷积核的大小为:1×1×C×C/r + 1×1×C/r×G×K×K =(C²+CGK²)/r; 而传统卷积为K²C²,因为C一般会比G少得多得多,因此Involution参数量更少。

卷积核的运作

preview

​ 首先根据特征图上的点生成一个将特征图上的每一个点都unfold扩展为其K×K领域的大小,再与K×K大小的kernel进行multiply-add操作(卷积操作),也就是进行聚合,变为一个空间位置的输出特征点。(上图为假设G=1的情况)

整个involution的代码(包括生成与执行)

import torchimport torch.nn as nnclass Involution(nn.Module):    def __init__(self, channels, kernel_size=7, stride=1, group_channels=16, reduction_ratio=4):        super().__init__()        assert not (channels % group_channels or channels % reduction_ratio)        # in_c=out_c        self.channels = channels        self.kernel_size = kernel_size        self.stride = stride        # 每组多少个通道        self.group_channels = group_channels        self.groups = channels // group_channels        # reduce channels        self.reduce = nn.Sequential(            nn.Conv2d(channels, channels // reduction_ratio, 1),            nn.BatchNorm2d(channels // reduction_ratio),            nn.ReLU()        )        # span channels        self.span = nn.Conv2d(            channels // reduction_ratio,            self.groups * kernel_size ** 2,            1        )        self.down_sample = nn.AvgPool2d(stride) if stride != 1 else nn.Identity()        self.unfold = nn.Unfold(kernel_size, padding=(kernel_size - 1) // 2, stride=stride)    def forward(self, x):        # Note that 'h', 'w' are height & width of the output feature.        # generate involution kernel: (b,G*K*K,h,w)        weight_matrix = self.span(self.reduce(self.down_sample(x)))        b, _, h, w = weight_matrix.shape        # unfold input: (b,C*K*K,h,w)        x_unfolded = self.unfold(x)        # (b,C*K*K,h,w)->(b,G,C//G,K*K,h,w)        x_unfolded = x_unfolded.view(b, self.groups, self.group_channels, self.kernel_size ** 2, h, w)        # (b,G*K*K,h,w) -> (b,G,1,K*K,h,w)        weight_matrix = weight_matrix.view(b, self.groups, 1, self.kernel_size ** 2, h, w)        # (b,G,C//G,h,w)        mul_add = (weight_matrix * x_unfolded).sum(dim=3)        # (b,C,h,w)        out = mul_add.view(b, self.channels, h, w)        return out

优势

​ 参数量和计算量更少

​ 更有效地再广泛的空间范围内进行信息交互(能使用更大的卷积核)

​ 在不同空间位置上仍具有知识共享/迁移能力:在生成Involution的时候用的是卷积,这实际上就潜在地引入了知识共享与迁移

​ 不需要位置编码

劣势

​ 无法灵活地改变通道数。需要改变通道数只能从外部通过池化技术进行

​ 通道间的内部信息交换在一定程度上受影响

有待探究的地方

​ 对kernel生成方式的进一步探索;

​ 结合NAS去搜索convolution-involution的混合结构

转载地址:http://ytfvi.baihongyu.com/

你可能感兴趣的文章
前阿里手淘前端负责人@winter:前端人如何保持竞争力?
查看>>
【JavaScript 教程】面向对象编程——实例对象与 new 命令
查看>>
我在网易做了6年前端,想给求职者4条建议
查看>>
SQL1015N The database is in an inconsistent state. SQLSTATE=55025
查看>>
RQP-DEF-0177
查看>>
Linux查看mac地址
查看>>
Linux修改ip
查看>>
MySQL字段类型的选择与MySQL的查询效率
查看>>
Java的Properties配置文件用法【续】
查看>>
JAVA操作properties文件的代码实例
查看>>
IPS开发手记【一】
查看>>
Java通用字符处理类
查看>>
文件上传时生成“日期+随机数”式文件名前缀的Java代码
查看>>
Java代码检查工具Checkstyle常见输出结果
查看>>
北京十大情人分手圣地
查看>>
Android自动关机代码
查看>>
Android中启动其他Activity并返回结果
查看>>
2009年33所高校被暂停或被限制招生
查看>>
GlassFish 部署及应用入门
查看>>
iWatch报错: Authorization request cancled
查看>>