前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Octave Convolution原理与Caffe实现

Octave Convolution原理与Caffe实现

作者头像
chaibubble
发布2019-08-01 15:08:47
8330
发布2019-08-01 15:08:47
举报

前言

OctaveNet网络paper是《Drop an Octave: Reducing Spatial Redundancy in Convolutional Neural Networks with Octave Convolution》,是CVPR2019中的一篇论文。 OctaveNet是一个用于ImageNet Classfication任务的backbone结构。这篇论文提出了一种新型的卷积结构,或者叫做卷积模块,叫做 Octave Convolution。 Octave Convolution号称是一种可以无缝嵌入到任何已有backbone中的模块,简单好用,能有效降低已有模型的计算量并带来小幅的性能提升,听起来还是让人非常兴奋的。

从频域的角度理解图像

我们都知道,一副图像从空间域的角度看,它一般情况下是一个3×W×H3 \times W \times H3×W×H的矩阵,矩阵中每一个位置都有一个[0,255]的值,而从频域的角度出发的话,一副图像都可以被分解为描述平稳变化结构的低空间频率分量(低频域、low-frequency)和描述快速变化的精细细节的高空间频率分量(高频域、high-frequency),就像下面这幅图:

最左侧为原始图像,中间为低频的部分,它比较多的反应的是图像的整体信息,最右侧为高频部分,它更多的反应图像的细节信息,比如边缘。这就好比空间域下的梯度,图像中存在边缘的地方,往往就是梯度大的地方。

特征图的高频与低频表示

既然对于图像来说可以区分高频与低频,那么对于特征图也是这样,特征图无非就是一个channel更多的矩阵而已,但是对于一个端对端的CNN模型,总不能在网络中引入一种频域计算,所以Octave Convolution显示的定义了“下采样”操作后的特征图叫做“低频域”,而不做下采样的原始尺寸叫做“高频域”。这样一来由于下采样带来的特征图尺寸减小,从而使得Octave Convolution计算量降低,此外网络有了不同尺度的信息(两个频域),并且两个频域的信息会在卷积完成后聚合,这个特性使得Octave Convolution具有比之前更好的性能。 “下采样”的scale,采用的是2的幂次,而目前文章只讨论了212^{1}21次幂的情况,说白了就是特征图的长宽都缩小了2,就像下面这张图:

在这里插入图片描述
在这里插入图片描述

图(b)是一个原始的特征图,并人为的切分特征图为Low Frequency和High Frequency,切分的标准是0.25,0.5,0.75三个系数,比如一个channel=64的特征图,系数为0.5的情况下,那么32个通道为低频,另外32个为高频。图©是用下采样操作实现低频域,就是上面说到缩小2倍。图(d)想要说明这个低频和高频要通过卷积做update,然后还有聚合交换的部分,反正只看(d)是看不出来,后面再具体介绍。

在这里不得不吐槽一点,论文由图像引出了高频和低频,但是到了卷积的地方直接过渡到了“下采样”,此后low-frequency和high-frequency还一直贯穿全文,这给人一种写论文写的过劲的感觉,毕竟Low Frequency、High Frequency和Octave 要比upsample和subsample好听,但是其实就是下采样完了上采样,尤其是我们要去实现它的时候。 ≡(▔﹏▔)≡

Octave Convolution

Octave Convolution原理

既然我们知道了Octave Convolution是一种下采样和上采样的组合,那么它的实现也就好理解了:

在这里插入图片描述
在这里插入图片描述

一个特征图的通道数cinc_{in}cin​根据预设系数aina_{in}ain​切分为高频(1−ain)cin\left ( 1- a_{in}\right )c_{in}(1−ain​)cin​与低频aincina_{in}c_{in}ain​cin​的部分,低频部分的宽高都缩小为原来的一半。然后Octave Convolution会做下面四个部分: (1)高频部分直接卷积:f(XH)f\left ( X^{H}\right )f(XH),即高频到高频的卷积,输出通道数(1−aout)cout\left ( 1- a_{out}\right )c_{out}(1−aout​)cout​; (2)高频部分先做下采样再卷积,这里的下采样是pool(XH,2)pool\left ( X^{H},2\right )pool(XH,2),然后f(pool(XH,2))f\left ( pool\left ( X^{H},2\right )\right)f(pool(XH,2)),即高频到低频的卷积,输出通道数aoutcouta_{out}c_{out}aout​cout​; (3)低频部分直接卷积后做上采样:f(XL)f\left ( X^{L}\right )f(XL),这里的upsample(f(XL))upsample\left ( f\left ( X^{L}\right )\right )upsample(f(XL))所用的上采样方法我们后面再说,即低频到高频的卷积,输出通道数(1−aout)cout\left ( 1- a_{out}\right )c_{out}(1−aout​)cout​; (4)低频部分直接卷积:f(XL)f\left ( X^{L}\right )f(XL),即低频到低频的卷积,输出通道数aoutcouta_{out}c_{out}aout​cout​。 这四个部分完成之后,接下来就要做信息的聚合,也就是(1)和(3)的结果做一个对应位置的按位加操作,(2)和(4)的结果做一个对应位置的按位加操作。 这样Octave Convolution就完成了,它其实在做的就是把原来的一个卷积操作,拆成了4个,而这4个中有三个处理的输入都是原来特征图w,h的一半,所以计算量就下来了。 所以一个one-stream的网络,在使用Octave Convolution之后,其实会变成two-stream结果,也就是高频流和低频流,在每一次的卷积结束之后,两个stream的信息会聚合一次。既然是中间是two-stream结构,那原有的网络怎么开始和收尾呢? 假如最开始时的输入只有一个,通道数还用cinc_{in}cin​表示,那么要分出高频和低频两个流,就是只做(1)和(2),但是区别在于,输入特征图的通道数就是cinc_{in}cin​。而最后就是只做(1)和(3),区别是输出特征图的通道数就是coutc_{out}cout​。

上采样和下采样

下采样: Octave Convolution的低频域输出可以用下面的公式表示: Yp,qL=Yp,qL→L+Yp,qH→L Y_{p,q}^{L}=Y_{p,q}^{L\rightarrow L}+Y_{p,q}^{H\rightarrow L} Yp,qL​=Yp,qL→L​+Yp,qH→L​ Yp,qL→L=∑i,j∈NkWi+k−12,j+k−12L→LXp+i,q+jL Y_{p,q}^{L\rightarrow L}=\sum_{i,j\in N{_{k}}}W_{i+\frac{k-1}{2},j+\frac{k-1}{2}}^{L\rightarrow L}X_{p+i,q+j}^{L} Yp,qL→L​=i,j∈Nk​∑​Wi+2k−1​,j+2k−1​L→L​Xp+i,q+jL​ Yp,qH→L=∑i,j∈NkWi+k−12,j+k−12H→LX2∗p+i,2∗q+jH Y_{p,q}^{H\rightarrow L}=\sum_{i,j\in N{_{k}}}W_{i+\frac{k-1}{2},j+\frac{k-1}{2}}^{H\rightarrow L}X_{2*p+i, 2*q+j}^{H} Yp,qH→L​=i,j∈Nk​∑​Wi+2k−1​,j+2k−1​H→L​X2∗p+i,2∗q+jH​ 其中Yp,qL→LY_{p,q}^{L\rightarrow L}Yp,qL→L​就是(4)的结果,Yp,qH→LY_{p,q}^{H\rightarrow L}Yp,qH→L​就是(2)的结果,Yp,qH→LY_{p,q}^{H\rightarrow L}Yp,qH→L​的公式就解释了下采样的过程,Wi+k−12,j+k−12H→LW_{i+\frac{k-1}{2},j+\frac{k-1}{2}}^{H\rightarrow L}Wi+2k−1​,j+2k−1​H→L​是一个对卷积核的遍历操作,当遍历到某一个点(i,j)\left ( i,j \right )(i,j)之后,去对应特征图上X2∗p+i,2∗q+jHX_{2*p+i, 2*q+j}^{H}X2∗p+i,2∗q+jH​的点,由于特征图上(p,q)\left ( p,q \right )(p,q)的遍历是以2倍的系数走的,所以这可以理解为,每一次都选择一个四方格的左上角的点,从而跨过其余三个点,开始卷积。这样一来,就下采样了,这也其实相当于一个跨步的卷积。 此外,这个公式还可以改写成X2∗p+0.5+i,2∗q+0.5+jHX_{2*p+0.5+i, 2*q+0.5+j}^{H}X2∗p+0.5+i,2∗q+0.5+jH​,但是0.5在特征图上是没有值的,所以它想表达的意思就是要聚合那个四方格,那就是平均池化喽。 于是,Octave Convolution的下采样策略就清楚了,要么做跨步卷积,要么先平均池化然后做步长为1的卷积。 当然论文中推荐了后者,因为如果原来网络中就做步长为2的卷积,为了下采样,难道要做步长为4吗?显然这不合理。

上采样: Octave Convolution的高频域输出可以用下面的公式表示: Yp,qH=Yp,qH→H+Yp,qL→H Y_{p,q}^{H}=Y_{p,q}^{H\rightarrow H}+Y_{p,q}^{L\rightarrow H} Yp,qH​=Yp,qH→H​+Yp,qL→H​ Yp,qH→H=∑i,j∈NkWi+k−12,j+k−12H→HXp+i,q+jH Y_{p,q}^{H\rightarrow H}=\sum_{i,j\in N{_{k}}}W_{i+\frac{k-1}{2},j+\frac{k-1}{2}}^{H\rightarrow H}X_{p+i,q+j}^{H} Yp,qH→H​=i,j∈Nk​∑​Wi+2k−1​,j+2k−1​H→H​Xp+i,q+jH​ Yp,qL→H=∑i,j∈NkWi+k−12,j+k−12H→LXp2+i,q2+jL Y_{p,q}^{L\rightarrow H}=\sum_{i,j\in N{_{k}}}W_{i+\frac{k-1}{2},j+\frac{k-1}{2}}^{H\rightarrow L}X_{\frac{p}{2}+i, \frac{q}{2}+j}^{L} Yp,qL→H​=i,j∈Nk​∑​Wi+2k−1​,j+2k−1​H→L​X2p​+i,2q​+jL​ 有了上面的介绍,这个就可以简单点说了,应特征图上而Xp2+i,q2+jLX_{\frac{p}{2}+i, \frac{q}{2}+j}^{L}X2p​+i,2q​+jL​的点其实是一个点复制成了一个四方格,或者说,这分明是一个最邻近插值。

在这里插入图片描述
在这里插入图片描述

OctaveConv如何减低计算量

在这里插入图片描述
在这里插入图片描述

假如我们有一个这样的卷积操作,那么它的计算量应该是: 其中一次卷积的计算量为: Componce=(3×3+8)×c+c−1 Comp_{once} = \left ( 3\times3+8\right )\times c + c-1 Componce​=(3×3+8)×c+c−1 那么完成所有的运算的计算量就应该是: Compb=Compo×c×w×h Comp_{b} =Comp_{o}\times c \times w \times h Compb​=Compo​×c×w×h 为了让后续的约分方便,我们把这个计算近似一下,忽略单次卷积里面的逐通道相加操作: Compb=(3×3+8)×c×c×w×h Comp_{b} = \left ( 3\times3+8\right )\times c \times c \times w \times h Compb​=(3×3+8)×c×c×w×h

那么这样的一个操作按照上面提到的Octave Convolution实现,应该怎么计算呢? 假设系数ain=aout=0.5a_{in}=a_{out}=0.5ain​=aout​=0.5 步骤(1): Compo1=(3×3+8)×12c×12c×w×h=14Compa Comp_{o1} = \left ( 3\times3+8\right )\times \frac{1}{2}c \times \frac{1}{2}c \times w \times h = \frac{1}{4}Comp_{a} Compo1​=(3×3+8)×21​c×21​c×w×h=41​Compa​ 步骤(2)忽略下采样: Compo2=(3×3+8)×14c×14c×14w×14h=116Compa Comp_{o2} = \left ( 3\times3+8\right )\times \frac{1}{4}c \times \frac{1}{4}c \times \frac{1}{4}w \times \frac{1}{4}h = \frac{1}{16}Comp_{a} Compo2​=(3×3+8)×41​c×41​c×41​w×41​h=161​Compa​ 步骤(3): Compo3=(3×3+8)×14c×14c×14w×14h=116Compa Comp_{o3} = \left ( 3\times3+8\right )\times \frac{1}{4}c \times \frac{1}{4}c \times \frac{1}{4}w \times \frac{1}{4}h = \frac{1}{16}Comp_{a} Compo3​=(3×3+8)×41​c×41​c×41​w×41​h=161​Compa​ 步骤(4)忽略上采样: Compo4=(3×3+8)×14c×14c×14w×14h=116Compa Comp_{o4} = \left ( 3\times3+8\right )\times \frac{1}{4}c \times \frac{1}{4}c \times \frac{1}{4}w \times \frac{1}{4}h = \frac{1}{16}Comp_{a} Compo4​=(3×3+8)×41​c×41​c×41​w×41​h=161​Compa​ 最后加起来: Compo==716Compa Comp_{o} = = \frac{7}{16}Comp_{a} Compo​==167​Compa​

OctaveConv的Caffe实现

octave_upsample_layer

在此之前,OctaveConv已经有了MXNet和Pytorch版本的实现,分别是OctaveConvOctaveConv_pytorch,但是还没有Caffe版本的实现,按照OctaveConv的原理,这个网络用Caffe是可以搭的,Caffe的已有上采样方式都不适用。 所以我添加了一个自定义层:octave_upsample_layer,以支持OctaveConv的上采样操作。 在这个层中,forward的部分就是上面提到的,而backward的部分,实现方式是这样:

在这里插入图片描述
在这里插入图片描述

自定义层注册

要把这个层添加到Caffe中需要

代码语言:javascript
复制
message LayerParameter {
optional OctaveUpsampleParameter octaveupsample_param = 最后一个ID;
}

并且它有下面几个参数:

代码语言:javascript
复制
message OctaveUpsampleParameter {
  // DEPRECATED. No need to specify upsampling scale factors when
  // exact output shape is given by upsample_h, upsample_w parameters.
  optional uint32 scale = 1 [default = 2];
  // DEPRECATED. No need to specify upsampling scale factors when
  // exact output shape is given by upsample_h, upsample_w parameters.
  optional uint32 scale_h = 2;
  // DEPRECATED. No need to specify upsampling scale factors when
  // exact output shape is given by upsample_h, upsample_w parameters.
  optional uint32 scale_w = 3;
  // DEPRECATED. Specify exact output height using upsample_h. This
  // parameter only works when scale is 2
  optional bool pad_out_h = 4 [default = false];
  // DEPRECATED. Specify exact output width using upsample_w. This
  // parameter only works when scale is 2
  optional bool pad_out_w = 5 [default = false];
  optional uint32 upsample_h = 6;
  optional uint32 upsample_w = 7;
}

重新Build就好了。

Example

已有的主干网络添加OctaveConv的例子已经上传到了OctaveConv_Caffe项目。

OctaveConv一定能让网络变快吗?

这个答案是否定的,因为对于一个模型,在输入图像尺寸固定的情况下,它的计算量就是确定的,但是对于一个模型forward的速度,却和平台有关。所以OctaveConv一定能降低已有模型的计算量,这个是公式可证的。 但是它不一定能让已有模型的速度变得更快,这是因为,OctaveConv把原来一次就能完成的卷积分开了四次完成,这里就会增加额外的数据传输时的消耗,此外还有额外的上采样、下采样、按位加操作。 所以,比如我能在一个高性能的处理器上forward一个模型,它处理卷积操作的速度很快,那么如果Octaveconv节省下来的卷积操作耗时无法弥补这些额外的开销的话,网络就不会变得更快。 相反的,如果卷积操作的耗时很大,利用Octaveconv可以节省下的时间比其余的开销要大,那么网络就会变快。

下面是一个resnet18resnet18_octave_0.5的网络耗时对比,平台是i7cpu: resnet18的conv19耗时

代码语言:javascript
复制
conv19   forward: 6.9 ms.

resnet18_octave_0.5conv19耗时6.9ms

代码语言:javascript
复制
 conv19_hf                forward: 3 ms.
 conv19_hf_add        forward: 1.14 ms.
 conv19_lf                 forward: 1.24 ms.
 conv19_lf_add         forward: 1.26 ms.

最后他们相差并不多,下面四个卷积操作并没有按照14\frac{1}{4}41​、116\frac{1}{16}161​、116\frac{1}{16}161​、116\frac{1}{16}161​的比例。一方面是因为相比于很多移动平台,i7-7700的性能还是比较强的,此外,resnet18的conv19卷积,本身计算量也不太大。 当然,这并不能说方法不work,paper本身提供的是一个思路,我们需要在合适的结构和平台下做更多的验证。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年07月27日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 从频域的角度理解图像
  • 特征图的高频与低频表示
  • Octave Convolution
    • Octave Convolution原理
      • 上采样和下采样
        • OctaveConv如何减低计算量
        • OctaveConv的Caffe实现
          • octave_upsample_layer
            • 自定义层注册
              • Example
              • OctaveConv一定能让网络变快吗?
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档