卷积神经网络的复杂度分析

点击上方“小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达5cf330a6c7a70d335a5289fd319d6452.jpeg

作者 | Michael Yuan@知乎(已授权)
来源 | https://zhuanlan.zhihu.com/p/31575074

导读

 

在梳理CNN经典模型的过程中,作者理解到其实经典模型演进中的很多创新点都与改善模型计算复杂度紧密相关,因此今天就让我们对卷积神经网络的复杂度分析简单总结一下。

在梳理CNN经典模型的过程中,我理解到其实经典模型演进中的很多创新点都与改善模型计算复杂度紧密相关,因此今天就让我们对卷积神经网络的复杂度分析简单总结一下下。

本文主要关注的是 针对模型本身的复杂度分析(其实并不是很复杂啦~)。如果想要进一步评估模型 在计算平台上的理论计算性能,则需要了解 Roofline Model 的相关理论
731ba079e1566dfea1d320e9d61242eb.jpeg
“复杂度分析”其实没有那么复杂啦~

时间复杂度

即模型的运算次数,可用900f1a2e06f1def7a416e917a99eb71a.png衡量,也就是浮点运算次数(FLoating-point OPerations)。

1.1 单个卷积层的时间复杂度
  • outside_default.png每个卷积核输出特征图 outside_default.png 的边长

  • outside_default.png每个卷积核 outside_default.png的边长

  • outside_default.png每个卷积核的通道数,也即输入通道数,也即上一层的输出通道数。

  • 6ea8b6c504561b3fda3cb0584d438b55.png本卷积层具有的卷积核个数,也即输出通道数。

  • 可见,每个卷积层的时间复杂度由输出特征图面积 3413438ad2914703cb62b14fef00dafc.png、卷积核面9914090fdd2638c4b42cb6958c084168.png、输入a50aecdd608dcf4086527330e158d8e5.png 和输出通道数94b1b7878674c2b0218554d4a5b63985.png完全决定。

  • 其中,输出特征图尺寸本身又由输入矩阵尺寸a5c6fa1e2004db51c0b5f8b1ffc8971d.png 、卷积核尺寸outside_default.png 、ef17bacfbb6f4042dffd5977aa90031f.png这四个参数所决定,表示如下:

9625af551d0bc7c6f03ca17253466ebc.png

  • 注1:为了简化表达式中的变量个数,这里统一假设输入和卷积核的形状都是正方形。

  • 注2:严格来讲每层应该还包含 1 个 badfdfe94c52f9b335b39b5f7a98981f.png参数,这里为了简洁就省略了。

1.2 卷积神经网络整体的时间复杂度

4c4838fd3f0189bb2928be7469afe4e3.png

  • 87872971798a2bd3649da33ca0dd3f43.png神经网络所具有的卷积层数,也即网络的深度。

  • 2c4ebe8a8a49d7f921eafa0903bb327d.png神经网络第9fbf3f8e5e4b9b3bcd73f0fdb73bc56a.png个卷积层

  •  5ffe3f88e3f316f161f54fe330456900.png神经网络第13b50fa6ec1d1dbe0ad12d5965c8ff41.png个卷积层的输出通道数49e8b1273768da27347dbace773f7615.png,也即该层的卷积核个数。

  • 对于第df6e3b4c830111d0010863f4a336e0a9.png个卷积层而言,其输入通道数001578de49a8cf501a8729e938dd291f.png就是第f786bf9c77a602ca69ebcfbc475d12a7.png个卷积层的输出通道数。

  • 可见,CNN整体的时间复杂度并不神秘,只是所有卷积层的时间复杂度累加而已。

  • 简而言之,层内连乘,层间累加。

示例: 用 Numpy 手动简单实现二维卷积

假设 Stride = 1, Padding = 0, img 和 kernel 都是 np.ndarray.

def conv2d(img, kernel):
    height, width, in_channels = img.shape
    kernel_height, kernel_width, in_channels, out_channels = kernel.shape
    out_height = height - kernel_height + 1
    out_width = width - kernel_width + 1
    feature_maps = np.zeros(shape=(out_height, out_width, out_channels))
    for oc in range(out_channels):              # Iterate out_channels (# of kernels)
        for h in range(out_height):             # Iterate out_height
            for w in range(out_width):          # Iterate out_width
                for ic in range(in_channels):   # Iterate in_channels
                    patch = img[h: h + kernel_height, w: w + kernel_width, ic]
                    feature_maps[h, w, oc] += np.sum(patch * kernel[:, :, ic, oc])


    return feature_maps

空间复杂度

空间复杂度(访存量),严格来讲包括两部分:总参数量 + 各层输出特征图。

  • 参数量:模型所有带参数的层的权重参数总量(即模型体积,下式第一个求和表达式)

  • 特征图:模型在实时运行过程中每层所计算出的输出特征图大小(下式第二个求和表达式)

outside_default.png

  • 总参数量只与卷积核的尺寸 3c8b43bc357257252be63508376259a8.png、通道数e541127a6d8ab7452586c555d86568f5.png、层数bed113650bed6ef509d440d07856e6a8.png相关,而与输入数据的大小无关。

  • 输出特征图的空间占用比较容易,就是其空间尺寸9eba40c61581379bdb237bb521e9f947.png和通道数3d5b716432035041c410cd80019d8650.png的连乘。

  • 注:实际上有些层(例如 ReLU)其实是可以通过原位运算完成的,此时就不用统计输出特征图这一项了。

复杂度对模型的影响

  • 时间复杂度决定了模型的训练/预测时间。如果复杂度过高,则会导致模型训练和预测耗费大量时间,既无法快速的验证想法和改善模型,也无法做到快速的预测。

  • 空间复杂度决定了模型的参数数量。由于维度诅咒的限制,模型的参数越多,训练模型所需的数据量就越大,而现实生活中的数据集通常不会太大,这会导致模型的训练更容易过拟合。

  • 当我们需要裁剪模型时,由于卷积核的空间尺寸通常已经很小(3x3),而网络的深度又与模型的表征能力紧密相关,不宜过多削减,因此模型裁剪通常最先下手的地方就是通道数。

Inception 系列模型是如何优化复杂度的

通过五个小例子说明模型的演进过程中是如何优化复杂度的。

4.1  5ebb7064054493cdfdc6e841d4d2ecab.png 中的 7b0796809df4d54875688d6141a3a1e0.png 卷积降维同时优化时间复杂度和空间复杂度
a8807ba9582bbeec0a7bb98fa0a9c5a1.jpeg
(图像被压缩的惨不忍睹...)
  • InceptionV1 借鉴了 Network in Network 的思想,在一个 Inception Module 中构造了四个并行的不同尺寸的卷积/池化模块(上图左),有效的提升了网络的宽度。但是这么做也造成了网络的时间和空间复杂度的激增。对策就是添加 1 x 1 卷积(上图右红色模块)将输入通道数先降到一个较低的值,再进行真正的卷积。

  • 以 InceptionV1 论文中的 (3b) 模块为例(可以点击上图看超级精美的大图),输入尺寸为f5f7f5a62040f67f6bee1f6ce977021d.png, 046047e01be7d48721fbf51670dcca06.png卷积核 outside_default.png 个,e9cb785d0276b8344066e0b49d28fec2.png卷积核 outside_default.png 个,d4de69c58906ca0ca2faedcab567d99d.png卷积核 outside_default.png 个,卷积核一律采用 Same Padding 确保输出不改变尺寸。

  • a54e54e6a7f92999ea6785c4730e1b06.png卷积分支上加入f9f7ad311366d04183063d645e386cf2.pnga09186df1a74f9e2fa8987e85dbac965.png卷积前后的时间复杂度对比如下式:

    f6c423913175dba27853010798df6faa.png

  • 同理,在af72b5daebf3d50c8bb5c2e2f4743051.png卷积分支上加入3eab505470caf298f090e64f6f600426.png0980a41d23e8675c2fe232d14a37a9b9.png卷积前后的时间复杂度对比如下式:

     d81f674b168108cc47d7eb2cb656f344.png

  • 可见,使用8be18499be8bdcd30c738d90336da245.png卷积降维可以降低时间复杂度3倍以上。该层完整的运算量可以在论文中查到,为 300 M,即fd96270b43fa588fedba1fc86c637999.png

  • 另一方面,我们同样可以简单分析一下这一层参数量在使用 1 x 1 卷积前后的变化。可以看到,由于 1 x 1 卷积的添加,3 x 3 和 5 x 5 卷积核的参数量得以降低 4 倍,因此本层的参数量从 1000 K 降低到 300 K 左右。

255a8903d2e7e25f4a076bcd71d29b66.png

4.2  ef60ef2cae337c55803cd9e0aa995d43.png 中使用  8be290ae48211fee5294a35e5d2c8759.png 代替 7dcf3c15e7b1806b7136b554c870f8e0.png
  • 全连接层可以视为一种特殊的卷积层,其卷积核尺寸 outside_default.png 与输入矩阵尺寸 一模一样。每个卷积核的输出特征图是一个标量点,即 。复杂度分析如下:

    dcc7b2235c1be10042fa98b3fa8376d1.png

  • 可见,与真正的卷积层不同,全连接层的空间复杂度与输入数据的尺寸密切相关。因此如果输入图像尺寸越大,模型的体积也就会越大,这显然是不可接受的。例如早期的VGG系列模型,其 90% 的参数都耗费在全连接层上。

  • InceptionV1 中使用的全局平均池化 GAP 改善了这个问题。由于每个卷积核输出的特征图在经过全局平均池化后都会直接精炼成一个标量点,因此全连接层的复杂度不再与输入图像尺寸有关,运算量和参数数量都得以大规模削减。复杂度分析如下:

    ccda237242c6d89e3a3b8be362acb9a0.png

4.3  4148640c28845d1d51a291c31fdf373e.png 中使用两个 05e22f581631355832f8e6cb60e5350a.png 卷积级联替代 1310fdf6ea376dd80ab721c87c549b92.png 卷积分支
2f6dfae76bbb4802083b9a4f9e20cc7f.jpeg
感受野不变
  • 根据上面提到的二维卷积输入输出尺寸关系公式,可知:对于同一个输入尺寸,单个3e4c8497d95d5f3d16ea78cd75fa0cae.png卷积的输出与两个f64257e3e5571a018c9e6bb9cb9d3db4.png卷积级联输出的尺寸完全一样,即感受野相同。

  • 同样根据上面提到的复杂度分析公式,可知:这种替换能够非常有效的降低时间和空间复杂度。我们可以把辛辛苦苦省出来的这些复杂度用来提升模型的深度和宽度,使得我们的模型能够在复杂度不变的前提下,具有更大的容量,爽爽的。

  • 同样以 InceptionV1 里的 (3b) 模块为例,替换前后的0f8fba3628094116c6851eb2a2dec158.png卷积分支复杂度如下:

    088919bad1ae94bc990da701be5d2027.png

4.4  b2fe581afc645641970e85082ced1bda.png 中使用 outside_default.png 与 c2be10fb9d822586aee95a568b699bf0.png 卷积级联替代  cff9b86562be9fd5a07a08a212f6623a.png 卷积
9dff655c7532506ca8fc5fa0e8c3da59.jpeg
  • InceptionV3 中提出了卷积的 Factorization,在确保感受野不变的前提下进一步简化。

  • 复杂度的改善同理可得,不再赘述。

4.5  135aa0ffa2d5279e356be238d99b0a46.png 中使用 outside_default.png
3595b888769da85095ce211560b435be.jpeg
  • 我们之前讨论的都是标准卷积运算,每个卷积核都对输入的所有通道进行卷积。

  • Xception 模型挑战了这个思维定势,它让每个卷积核只负责输入的某一个通道,这就是所谓的 Depth-wise Separable Convolution。

  • 从输入通道的视角看,标准卷积中每个输入通道都会被所有卷积核蹂躏一遍,而 Xception 中每个输入通道只会被对应的一个卷积核扫描,降低了模型的冗余度。

  • 标准卷积与可分离卷积的时间复杂度对比:可以看到本质上是把连乘转化成为相加。

    3760e37176f8cdbf24e3d6dc672b98cc.png

总结

通过上面的推导和经典模型的案例分析,我们可以清楚的看到其实很多创新点都是围绕模型复杂度的优化展开的,其基本逻辑就是乘变加。模型的优化换来了更少的运算次数和更少的参数数量,一方面促使我们能够构建更轻更快的模型(例如MobileNet),一方面促使我们能够构建更深更宽的网络(例如Xception),提升模型的容量,打败各种大怪兽,欧耶~

b13d933de088c10c9ee23ba42d0ec04e.jpeg

好消息!

小白学视觉知识星球

开始面向外开放啦👇👇👇

 
 

a43cb0ede2f5b381da67f135605d9233.jpeg

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。


下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。


下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。


交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值