Octave Convolution详解
前言
Octave Convolution来自于这篇论文《Drop an Octave: Reducing Spatial Redundancy in Convolutional Neural Networks with Octave Convolution》这篇论文,该论文也被ICCV2019接收。
Octave表示的是音阶的八度,而本篇核心思想是通过对数据低频信息减半从而达到加速卷积运算的目的,而两个Octave之间也是声音频率减半【2】。
Octave Convolution(后面将以OctConv命名)主要有以下三个贡献:
- 将卷积特征图分成了两组,一组低频,一组高频,低频特征图的大小会减半,从而可以有效减少存储以及计算量,另外,由于特征图大小减小,卷积核大小不变,感受野就变大了,可以抓取更多的上下文信息;
- OctConv是一种即插即用的卷积块,可以直接替换传统的conv(也替换分组卷积以及深度可分离卷积等),减小内存和计算量;
- 当然,作者做了大量实验,使用OctConv可以得到更高性能,甚至可以媲美best Auto ML。
总的来说,OctConv是占用内存小,速度快,性能高,即插即用的conv块。
OctConv的特征表示
自然图像可被分解为低频分量以及高频分量,如下所示:

而卷积层的特征图也可以分为低频和高频分量,如下图(b)所示,OctConv卷积的做法是将低频分量的空间分辨率减半(如下图c所示),然后分两组进行conv,两组频率之间会通过上采样和下采样进行信息交互(见下图d),最后再合成原始特征图大小。

作者认为低频分量在一些特征图中是富集的,可以被压缩的,所以对低频分量进行了压缩,压缩的方式没有采用stride conv,而是使用了average pooling,因为stride conv会导致不对齐的行为。
OctConv的详细过程

如上图所示,OctConv的输入有两部分,一部分是高频\(X^H\),另一部分是低频\(X^L\),观察到\(X^L\)的大小是\(X^H\)的二分之一,这里通过两个参数\(\alpha_{in}\)和\(\alpha_{out}\)来控制低高频的输入通道和输出通道,一开始,输入只有一个\(X\),这时候的\(\alpha_{in}\)为0,然后通过两个卷积层(\(f\left(X^{H} ; W^{H \rightarrow H}\right)\)和\(f\left(p o o l\left(X^{H}, 2\right) ; W^{H \rightarrow L}\right)\))得到高频分量和低频分量,中间的OctConv就是有两个输入了和两个输出,最后需要从两个输入恢复出一个输出,此时\(\alpha_{out}\)为0,通过\(f\left(X^{H} ; W^{H \rightarrow H}\right)\)和\(upsample\left(f\left(X^{L} ; W^{L \rightarrow H}\right), 2\right)\)两个操作得到单独输出。
现在来讨论上面的四根线上的操作各代表什么。
\(f\left(X^{H} ; W^{H \rightarrow H}\right)\)是高频信息到高频信息,通过一个卷积层即可。
\(f\left(p o o l\left(X^{H}, 2\right) ; W^{H \rightarrow L}\right)\)是将高频信息汇合到低频信息中,先通过一个平均池化,然后通过一个卷积层。
\(upsample\left(f\left(X^{L} ; W^{L \rightarrow H}\right), 2\right)\)是将低频信息汇合到高频信息,先通过一个卷积层,然后通过平均池化层。
\(f\left(X^{L} ; W^{L \rightarrow L}\right)\)是将低频信息到低频信息,通过一个卷积层。
现在看看卷积核参数分配的问题,如下图所示:

上面的四个操作对应上图的四个部分,可以看到总的参数依然是\(c_{i n} \times c_{o u t} \times k \times k\),但由于低频分量的尺寸减半,所需要的存储空间变小,以及计算量缩减,达到加速卷积的过程。
Pytorch代码
下面的代码来自于OctaveConv_pytorch ,代码可读性很高,如果理解了上述过程,看起来会很容易。
第一层OctConv卷积,将特征图x分为高频和低频:
class FirstOctaveConv(nn.Module):
def __init__(self, in_channels, out_channels,kernel_size, alpha=0.5, stride=1, padding=1, dilation=1,
groups=1, bias=False):
super(FirstOctaveConv, self).__init__()
self.stride = stride
kernel_size = kernel_size[0]
self.h2g_pool = nn.AvgPool2d(kernel_size=(2, 2), stride=2)
self.h2l = torch.nn.Conv2d(in_channels, int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
self.h2h = torch.nn.Conv2d(in_channels, out_channels - int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
def forward(self, x):
if self.stride ==2:
x = self.h2g_pool(x)
X_h2l = self.h2g_pool(x)
X_h = x
X_h = self.h2h(X_h)
X_l = self.h2l(X_h2l)
return X_h, X_l
中间层的OctConv,低高频输入,低高频输出:
class OctaveConv(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, alpha=0.5, stride=1, padding=1, dilation=1,
groups=1, bias=False):
super(OctaveConv, self).__init__()
kernel_size = kernel_size[0]
self.h2g_pool = nn.AvgPool2d(kernel_size=(2, 2), stride=2)
self.upsample = torch.nn.Upsample(scale_factor=2, mode='nearest')
self.stride = stride
self.l2l = torch.nn.Conv2d(int(alpha * in_channels), int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
self.l2h = torch.nn.Conv2d(int(alpha * in_channels), out_channels - int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
self.h2l = torch.nn.Conv2d(in_channels - int(alpha * in_channels), int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
self.h2h = torch.nn.Conv2d(in_channels - int(alpha * in_channels),
out_channels - int(alpha * out_channels),
kernel_size, 1, padding, dilation, groups, bias)
def forward(self, x):
X_h, X_l = x
if self.stride ==2:
X_h, X_l = self.h2g_pool(X_h), self.h2g_pool(X_l)
X_h2l = self.h2g_pool(X_h)
X_h2h = self.h2h(X_h)
X_l2h = self.l2h(X_l)
X_l2l = self.l2l(X_l)
X_h2l = self.h2l(X_h2l)
X_l2h = self.upsample(X_l2h)
X_h = X_l2h + X_h2h
X_l = X_h2l + X_l2l
return X_h, X_l
最后一层的OctConv,将低高频汇合称输出。
class LastOctaveConv(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, alpha=0.5, stride=1, padding=1, dilation=1,
groups=1, bias=False):
super(LastOctaveConv, self).__init__()
self.stride = stride
kernel_size = kernel_size[0]
self.h2g_pool = nn.AvgPool2d(kernel_size=(2,2), stride=2)
self.l2h = torch.nn.Conv2d(int(alpha * in_channels), out_channels,
kernel_size, 1, padding, dilation, groups, bias)
self.h2h = torch.nn.Conv2d(in_channels - int(alpha * in_channels),
out_channels,
kernel_size, 1, padding, dilation, groups, bias)
self.upsample = torch.nn.Upsample(scale_factor=2, mode='nearest')
def forward(self, x):
X_h, X_l = x
if self.stride ==2:
X_h, X_l = self.h2g_pool(X_h), self.h2g_pool(X_l)
X_l2h = self.l2h(X_l)
X_h2h = self.h2h(X_h)
X_l2h = self.upsample(X_l2h)
X_h = X_h2h + X_l2h
return X_h
参考
【2】Pytorch代码
Octave Convolution详解的更多相关文章
- SIFT算法详解(转)
http://blog.csdn.net/zddblog/article/details/7521424 目录(?)[-] 尺度不变特征变换匹配算法详解 Scale Invariant Feature ...
- 【转】 SIFT算法详解
尺度不变特征变换匹配算法详解Scale Invariant Feature Transform(SIFT)Just For Fun zdd zddmail@gmail.com 对于初学者,从Davi ...
- 深度学习之卷积神经网络(CNN)详解与代码实现(一)
卷积神经网络(CNN)详解与代码实现 本文系作者原创,转载请注明出处:https://www.cnblogs.com/further-further-further/p/10430073.html 目 ...
- 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”
来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...
- 深度学习基础(CNN详解以及训练过程1)
深度学习是一个框架,包含多个重要算法: Convolutional Neural Networks(CNN)卷积神经网络 AutoEncoder自动编码器 Sparse Coding稀疏编码 Rest ...
- SIFT算法详解
尺度不变特征变换匹配算法详解Scale Invariant Feature Transform(SIFT)Just For Fun zdd zddmail@gmail.com or (zddhub@ ...
- sift拟合详解
1999年由David Lowe首先发表于计算机视觉国际会议(International Conference on Computer Vision,ICCV),2004年再次经David Lowe整 ...
- [转]CNN目标检测(一):Faster RCNN详解
https://blog.csdn.net/a8039974/article/details/77592389 Faster RCNN github : https://github.com/rbgi ...
- Attention is all you need 论文详解(转)
一.背景 自从Attention机制在提出之后,加入Attention的Seq2Seq模型在各个任务上都有了提升,所以现在的seq2seq模型指的都是结合rnn和attention的模型.传统的基于R ...
随机推荐
- MongoDB学习笔记(七、MongoDB总结)
1.为什么要NoSQL:nosql能解决sql中那些解决不了的问题 NoSQL是什么:Not Only SQL,本质上还是数据库,但它不会遵循传统数据库的规则(如:SQL标准.ACID属性[事务].表 ...
- js获取数组最大值或最小值
数组对象arr中属性num最大值最小值 // 最大值 Math.max.apply(Math,arr.map(item => { return item.num })) arr.sort((a, ...
- JS获取url请求参数
JS获取url请求参数,代码如下: // 获取url请求参数 function getQueryParams() { var query = location.search.substring(1) ...
- SSHD
SSH基本概述 SSH服务协议说明 SSH 是 Secure Shell Protocol 的简写,由 IETF 网络工作小组(Network Working Group )制定在进行数据传输之前,S ...
- JeeSite | 保存信息修改记录封装
前面写过两篇关于“保存信息修改记录”的内容,分别如下: JeeSite | 保存信息修改记录 JeeSite | 保存信息修改记录续 回顾 第一篇文章通过类字段的比较返回一个有字段值不 ...
- 巧妙利用label标签实现input file上传文件自定义样式
提到上传文件,一般会想到用input file属性来实现,简单便捷,一行代码即可 但input file原生提供的默认样式大多情况下都不符合需求,且在不同浏览器上呈现的样式也不尽相同 我们往 ...
- Java 面试宝典!并发编程 71 道题及答案全送上!
金九银十跳槽季已经开始,作为 Java 开发者你开始刷面试题了吗?别急,我整理了71道并发相关的面试题,看这一文就够了! 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程( ...
- Java描述设计模式(02):简单工厂模式
本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景简介 1.引入场景 订餐流程简单描述 1).食品抽象类,规定食品的基础属性操作 2).鱼类,鸡肉类食品类扩展 3).订餐流程类,根 ...
- Spring Boot 2 快速教程:WebFlux 集成 Thymeleaf(五)
号外:为读者持续整理了几份最新教程,覆盖了 Spring Boot.Spring Cloud.微服务架构等PDF.获取方式:关注右侧公众号"泥瓦匠BYSocket",来领取吧! 摘 ...
- VS2019 .Net Core 3.0 Web 项目启用动态编译
VS2019 中 .Net Core 3.0 项目默认没有启用动态编译, 这导致按F5调试的时候,修改了 HTML 代码,在浏览器上刷新没有效果. 启用动态编译方法如下: 1. 安装 Microsof ...