深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算

这篇具有很好参考价值的文章主要介绍了深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算

1.计算机视觉与卷积神经网络

1.1计算机视觉综述

计算机视觉作为一门让机器学会如何去“看”的学科,具体的说,就是让机器去识别摄像机拍摄的图片或视频中的物体,检测出物体所在的位置,并对目标物体进行跟踪,从而理解并描述出图片或视频里的场景和故事,以此来模拟人脑视觉系统。因此,计算机视觉也通常被叫做机器视觉,其目的是建立能够从图像或者视频中“感知”信息的人工系统。

计算机视觉技术经过几十年的发展,已经在交通(车牌识别、道路违章抓拍)、安防(人脸闸机、小区监控)、金融(刷脸支付、柜台的自动票据识别)、医疗(医疗影像诊断)、工业生产(产品缺陷自动检测)等多个领域应用,影响或正在改变人们的日常生活和工业生产方式。未来,随着技术的不断演进,必将涌现出更多的产品和应用,为我们的生活创造更大的便利和更广阔的机会。

图1 计算机视觉技术在各领域的应用

1.2 计算机视觉的发展历程

计算机视觉的发展历程要从生物视觉讲起。对于生物视觉的起源,目前学术界尚没有形成定论。有研究者认为最早的生物视觉形成于距今约7亿年前的水母之中,也有研究者认为生物视觉产生于距今约5亿年前寒武纪。寒武纪生物大爆发的原因一直是个未解之谜,不过可以肯定的是在寒武纪动物具有了视觉能力,捕食者可以更容易地发现猎物,被捕食者也可以更早的发现天敌的位置。视觉系统的形成有力地推动了食物链的演化,加速了生物进化过程,是生物发展史上重要的里程碑。经过几亿年的演化,目前人类的视觉系统已经具备非常高的复杂度和强大的功能,人脑中神经元数目达到了1000亿个,这些神经元通过网络互相连接,这样庞大的视觉神经网络使得我们可以很轻松的观察周围的世界,如 图2 所示。

图2 人类视觉感知

对人类来说,识别猫和狗是件非常容易的事。但对计算机来说,即使是一个精通编程的高手,也很难轻松写出具有通用性的程序(比如:假设程序认为体型大的是狗,体型小的是猫,但由于拍摄角度不同,可能一张图片上猫占据的像素比狗还多)。那么,如何让计算机也能像人一样看懂周围的世界呢?研究者尝试着从不同的角度去解决这个问题,由此也发展出一系列的子任务,如 图3 所示。

图3 计算机视觉子任务示意图

  • (a) Image Classification: 图像分类,用于识别图像中物体的类别(如:bottle、cup、cube)。

  • (b) Object Localization: 目标检测,用于检测图像中每个物体的类别,并准确标出它们的位置。

  • (c) Semantic Segmentation: 语义分割,用于标出图像中每个像素点所属的类别,属于同一类别的像素点用一个颜色标识。

  • (d) Instance Segmentation: 实例分割,值得注意的是,目标检测任务只需要标注出物体位置,而实例分割任务不仅要标注出物体位置,还需要标注出物体的外形轮廓。

这里以图像分类任务为例,为大家介绍计算机视觉技术的发展历程。在早期的图像分类任务中,通常是先人工提取图像特征,再用机器学习算法对这些特征进行分类,分类的结果强依赖于特征提取方法,往往只有经验丰富的研究者才能完成,如 图4 所示。

图4 早期的图像分类任务

在这种背景下,基于神经网络的特征提取方法应运而生。Yann LeCun是最早将卷积神经网络应用到图像识别领域的,其主要逻辑是使用卷积神经网络提取图像特征,并对图像所属类别进行预测,通过训练数据不断调整网络参数,最终形成一套能自动提取图像特征并对这些特征进行分类的网络,如 图5 所示。

图5 早期的卷积神经网络处理图像任务示意

这一方法在手写数字识别任务上取得了极大的成功,但在接下来的时间里,却没有得到很好的发展。其主要原因一方面是数据集不完善,只能处理简单任务,在大尺寸的数据上容易发生过拟合;另一方面是硬件瓶颈,网络模型复杂时,计算速度会特别慢。

目前,随着互联网技术的不断进步,数据量呈现大规模的增长,越来越丰富的数据集不断涌现。另外,得益于硬件能力的提升,计算机的算力也越来越强大。不断有研究者将新的模型和算法应用到计算机视觉领域。由此催生了越来越丰富的模型结构和更加准确的精度,同时计算机视觉所处理的问题也越来越丰富,包括分类、检测、分割、场景描述、图像生成和风格变换等,甚至还不仅仅局限于2维图片,包括视频处理技术和3D视觉等。

1.3 卷积神经网络

卷积神经网络是目前计算机视觉中使用最普遍的模型结构。图6 是一个典型的卷积神经网络结构,多层卷积和池化层组合作用在输入图片上,在网络的最后通常会加入一系列全连接层,ReLU激活函数一般加在卷积或者全连接层的输出上,网络中通常还会加入Dropout来防止过拟合。

图6 卷积神经网络经典结构

  • 卷积层:卷积层用于对输入的图像进行特征提取。卷积的计算范围是在像素点的空间邻域内进行的,因此可以利用输入图像的空间信息。卷积核本身与输入图片大小无关,它代表了对空间邻域内某种特征模式的提取。比如,有些卷积核提取物体边缘特征,有些卷积核提取物体拐角处的特征,图像上不同区域共享同一个卷积核。当输入图片大小不一样时,仍然可以使用同一个卷积核进行操作。

  • 池化层:池化层通过对卷积层输出的特征图进行约减,实现了下采样。同时对感受域内的特征进行筛选,提取区域内最具代表性的特征,保留特征图中最主要的信息。

  • 激活函数:激活函数给神经元引入了非线性因素,对输入信息进行非线性变换,从而使得神经网络可以任意逼近任何非线性函数,然后将变换后的输出信息作为输入信息传给下一层神经元。

  • 全连接层:全连接层用于对卷积神经网络提取到的特征进行汇总,将多维的特征映射为二维的输出。其中,高维代表样本批次大小,低维代表分类或回归结果。

2. 池化

2.1 基础概念(平均池化和最大池化)

在图像处理中,由于图像中存在较多冗余信息,可用某一区域子块的统计信息(如最大值或均值等)来刻画该区域中所有像素点呈现的空间分布模式,以替代区域子块中所有像素点取值,这就是卷积神经网络中池化(pooling)操作。

池化操作对卷积结果特征图进行约减,实现了下采样,同时保留了特征图中主要信息。比如:当识别一张图像是否是人脸时,我们需要知道人脸左边有一只眼睛,右边也有一只眼睛,而不需要知道眼睛的精确位置,这时候通过池化某一片区域的像素点来得到总体统计特征会显得很有用。

池化的几种常见方法包括:平均池化、最大池化、K-max池化。其中平均池化和最大池化如 图1 所示,K-max池化如 图2 所示。

图1 平均池化和最大池化

  • 平均池化: 计算区域子块所包含所有像素点的均值,将均值作为平均池化结果。如 图1(a),这里使用大小为$2\times2$的池化窗口,每次移动的步幅为2,对池化窗口覆盖区域内的像素取平均值,得到相应的输出特征图的像素值。池化窗口的大小也称为池化大小,用$k_h \times k_w$表示。在卷积神经网络中用的比较多的是窗口大小为$2 \times 2$,步幅为2的池化。

  • 最大池化: 从输入特征图的某个区域子块中选择值最大的像素点作为最大池化结果。如 图1(b),对池化窗口覆盖区域内的像素取最大值,得到输出特征图的像素值。当池化窗口在图片上滑动时,会得到整张输出特征图。

图2 K-max池化

  • K-max池化: 对输入特征图的区域子块中像素点取前K个最大值,常用于自然语言处理中的文本特征提取。如图2,从包含了4个取值的每一列中选取前2个最大值就得到了K最大池化结果。

2.2池化特点

  1. 当输入数据做出少量平移时,经过池化后的大多数输出还能保持不变,因此,池化对微小的位置变化具有鲁棒性。例如 图3 中,输入矩阵向右平移一个像素值,使用最大池化后,结果与平移前依旧能保持不变。

图3 微小位置变化时的最大池化结果

  1. 由于池化之后特征图会变小,如果后面连接的是全连接层,能有效的减小神经元的个数,节省存储空间并提高计算效率

2.3 池化中填充的方式

在飞桨中,各种Pooling API中的Padding参数, 接收值类型都包括int、list、tuple和string。下面用代码和公式来介绍一下这些方式吧。

2.3.1 int输入

int输入即接收一个int类型的数字n,对图片的四周包裹n行n列的0来填充图片。如果要保持图片尺寸不变,n的值和池化窗口的大小是有关的。假如 $H_{in}, W_{in}$ 为图片输入的大小,$k_h, k_w$ 为池化窗口的大小,$H_{out}, H_{out}$ 为结果图的大小的话,他们之间有着这样的关系。 $$H_{out} = \frac{H_{in} + 2p_h - k_h}{s_h} + 1 \ W_{out} = \frac{W_{out} + 2p_w -k_w}{s_w} + 1$ $在使用3×3的池化窗口且步长为1的情况下,还要保持图片大小不变,则需要使用padding=1的填充。 那么,公式就变为了$ $H_{out} = \frac{6 - 3 + 21}{1} + 1 \ W_{out} = \frac{6 - 3 + 21}{1} + 1$ $另外,在stride不为1且不能被整除的情况下,整体结果向下取整。 关于Padding和K的公式如下$ $Padding = \frac{(k-1)}{2} \quad \quad (k % 2 != 0)$ $

关于上面的讲解,下面用飞桨的API来看看吧。

import paddle # No padding
x = paddle.rand((1, 1, 6, 6))
avgpool = paddle.nn.AvgPool2D(kernel_size=3, stride=1, padding=0)
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)

result: shape of x: [1, 1, 6, 6] shape of result: [1, 1, 4, 4]

这是池化中不padding,stride为1的结果,可以根据公式填入,$H_{out} = W_{out} = (6 + 0 - 3) + 1 = 4$,因此池化后的结果为4。如果填充呢?

import paddle # Padding 1
x = paddle.rand((1, 1, 6, 6))
avgpool = paddle.nn.AvgPool2D(kernel_size=3, stride=1, padding=1)
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)

result: shape of x: [1, 1, 6, 6] shape of result: [1, 1, 6, 6]

正如我们上面的公式,$H_{out} = W_{out} = (6 + 2 - 3) + 1 = 6$, 填充为1的时候图像保持为原大小。

2.3.2 list和tuple输入

因为图像有宽和高,所以list和tuple的长度应该为2,里面的两个值分别对应了高和宽,计算方式和上面int输入的一样,单独计算。一般用作输入图片宽高不一致,或者池化窗口大小不一的情况。我们直接用飞桨的API来看看吧。

import paddle # No padding and different kernel size
x = paddle.rand((1, 1, 12, 6)) # 12为高H, 6为宽W
avgpool = paddle.nn.AvgPool2D(kernel_size=(3, 5), stride=1, padding=0)
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)
result: shape of x: [1, 1, 12, 6] shape of result: [1, 1, 10, 2]

这里我们带入公式推理一下,$H_{out} = 12 - 3 + 1 = 10, W_{out} = 6 - 5 + 1 = 2$.与结果相符。下面是有填充的情况,且3的滑动窗口大小我们需要填充1,5的话则需要填充2了。下面来看看吧。

import paddle # No padding and different kernel size
x = paddle.rand((1, 1, 12, 6)) # 12为高H, 6为宽W
avgpool = paddle.nn.AvgPool2D(kernel_size=(3, 5), stride=1, padding=(1, 2))
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)
result: shape of x: [1, 1, 12, 6] shape of result: [1, 1, 12, 6]

这里的结果与我们的期望一致,大家试着带入公式算一下吧。

2.3.3 string输入

string输入有两个值,一个是SAME,一个是VALID。这两个的计算公式如下:

SAME:$H_{out} = \lceil \frac{H_{in}}{s_h} \rceil$, $W_{out} = \lceil\frac{W_{in}}{s_w}\rceil$

VALID:$H_{out} = \frac{H_{in} - k_h}{s_h} + 1$, $W_{out} = \frac{W_{in} - k_w}{s_w} + 1$

可以看到,VALID方式就是默认采用的不填充的方式,与上面不Padding的公式一样。而SAME则与池化窗口的大小无关,若$s_h$和$s_w$为1,无论池化窗口的大小,输出的特征图的大小都与原图保持一致。当任意一个大于1时,如果能整除,输出的尺寸就是整除的结果,如果不能整除,则通过padding的方式继续向上取整。理论过于难懂,我们直接用飞桨的API来看看吧。

import paddle # Padding SAME kernel_size 2
x = paddle.rand((1, 1, 6, 6))
avgpool = paddle.nn.AvgPool2D(kernel_size=2, padding='SAME')
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)
result: shape of x: [1, 1, 6, 6] shape of result: [1, 1, 3, 3]

代码的结果出来了,我们来直接带入公式来计算吧,$H_{out} = 6/2 = 3, W_{out} = 6/2 = 3$,结果一致。

import paddle # Padding SAME kernel_size 1
x = paddle.rand((1, 1, 6, 6))
avgpool = paddle.nn.AvgPool2D(kernel_size=1, padding='SAME')
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)
result: shape of x: [1, 1, 6, 6] shape of result: [1, 1, 6, 6]

这个呢,就和我们上面说的一致。下面来看看VALID填充方式吧。

import paddle # Padding VALID
x = paddle.rand((1, 1, 6, 6))
avgpool = paddle.nn.AvgPool2D(kernel_size=2, padding='VALID')
y = avgpool(x)
print('result:', 'shape of x:', x.shape, 'shape of result:', y.shape)
result: shape of x: [1, 1, 6, 6] shape of result: [1, 1, 3, 3]

这就是VALID的填充方式的结果啦。大家自己按照公式算算,看看你的答案和程序输出的对不对哦。

2.4 应用示例

与卷积核类似,池化窗口在图片上滑动时,每次移动的步长称为步幅,当宽和高方向的移动大小不一样时,分别用$s_w$和$s_h$表示。也可以对需要进行池化的图片进行填充,填充方式与卷积类似,假设在第一行之前填充$p_{h1}$行,在最后一行后面填充$p_{h2}$行。在第一列之前填充$p_{w1}$列,在最后一列之后填充$p_{w2}$列,则池化层的输出特征图大小为:

$$H_{out} = \frac{H + p_{h1} + p_{h2} - k_h}{s_h} + 1$$

$$W_{out} = \frac{W + p_{w1} + p_{w2} - k_w}{s_w} + 1$$

在卷积神经网络中,通常使用$2\times2$大小的池化窗口,步幅也使用2,填充为0,则输出特征图的尺寸为:

$$H_{out} = \frac{H}{2}$$

$$W_{out} = \frac{W}{2}$$

通过这种方式的池化,输出特征图的高和宽都减半,但通道数不会改变。

这里以 图1 中的2个池化运算为例,此时,输入大小是$4 \times 4$ ,使用大小为$2 \times 2$ 的池化窗口进行运算,步幅为2。此时,输出尺寸的计算方式为:

$$H_{out} = \frac{H + p_{h1} + p_{h2} - k_h}{s_h} + 1=\frac{4 + 0 + 0 - 2}{2} + 1=\frac{4}{2}=2$$

$$W_{out} = \frac{W + p_{w1} + p_{w2} - k_w}{s_w} + 1=\frac{4 + 0 + 0 - 2}{2} + 1=\frac{4}{2}=2$$

图1(a) 中,使用平均池化进行运算,则输出中的每一个像素均为池化窗口对应的 $2 \times 2$ 区域求均值得到。计算步骤如下:

  1. 池化窗口的初始位置为左上角,对应粉色区域,此时输出为 $3.5 = \frac{1 + 2 + 5 + 6}{4}$ ;

  2. 由于步幅为2,所以池化窗口向右移动两个像素,对应蓝色区域,此时输出为 $5.5 = \frac{3 + 4 + 7 + 8}{4}$ ;

  3. 遍历完第一行后,再从第三行开始遍历,对应绿色区域,此时输出为 $11.5 = \frac{9 + 10 + 13 + 14}{4}$ ;

  4. 池化窗口向右移动两个像素,对应黄色区域,此时输出为 $13.5 = \frac{11 + 12 + 15 + 16}{4}$ 。

图1(b) 中,使用最大池化进行运算,将上述过程的求均值改为求最大值即为最终结果。

3.CNN中模型的参数量与FLOPs计算

一个卷积神经网络的基本构成一般有卷积层、归一化层、激活层和线性层。这里我们就通过逐步计算这些层来计算一个CNN模型所需要的参数量和FLOPs吧. 另外,FLOPs的全程为floating point operations的缩写(小写s表复数),意指浮点运算数,理解为计算量。可以用来衡量算法/模型的复杂度。

3.1 卷积层

卷积层,最常用的是2D卷积,因此我们以飞桨中的Conv2D来表示。

3.1.1卷积层参数量计算

Conv2D的参数量计算较为简单,先看下列的代码,如果定义一个Conv2D,卷积层中的参数会随机初始化,如果打印其shape,就可以知道一个Conv2D里大致包含的参数量了,Conv2D的参数包括两部分,一个是用于卷积的weight,一个是用于调节网络拟合输入特征的bias。如下

import paddle
import numpy as np
cv2d   = paddle.nn.Conv2D(in_channels=2, out_channels=4, kernel_size=(3, 3), stride=1, padding=(1, 1))
params = cv2d.parameters()
print("shape of weight: ", np.array(params[0]).shape)
print("shape of bias: ", np.array(params[1]).shape)
shape of weight:  (4, 2, 3, 3)
shape of bias:  (4,)

这里解释一下上面的代码,我们先定义了一个卷积层cv2d,然后输出了这个卷积层的参数的形状,参数包含两部分,分别是weight和bias,这两部分相加才是整个卷积的参数量。因此,可以看到,我们定义的cv2d的参数量为:$423*3+4 = 76$, 4对应的是输出的通道数,2对应的是输入的通道数,两个3是卷积核的尺寸,最后的4就是bias的数量了, 值得注意的是, bias是数量与输出的通道数保持一致。因此,我们可以得出,一个卷积层的参数量的公式,如下: $ $Param_{conv2d} = C_{in} * C_{out} * K_h * K_w + C_{out}$ $其中,$C_{in} $表示输入的通道数,$C_{out} $表示输出的通道数,$ K_h$,$ K_w $表示卷积核的大小。当然了,有些卷积会将bias设置为False,那么我们不加最后的$C_{out}$即可。

3.1.2 卷积层FLOPs计算

参数量会计算了,那么FLOPs其实也是很简单的,就一个公式: $$FLOP_{conv2d} = Param_{conv2d} * M_{outh} * M_{outw}$ $这里,$M_{outh}$,$M_{outw}$ 为输出的特征图的高和宽,而不是输入的,这里需要注意一下。

3.1.3 卷积层参数计算示例

Paddle有提供计算FLOPs和参数量的API,paddle.flops, 这里我们用我们的方法和这个API的方法来测一下,看看一不一致吧。代码如下:

import paddle
from paddle import nn
from paddle.nn import functional as F


class TestNet(nn.Layer):
    def __init__(self):
        super(TestNet, self).__init__()
        self.conv2d = nn.Conv2D(in_channels=2, out_channels=4, kernel_size=(3, 3), stride=1, padding=1)

    def forward(self, x):
        x = self.conv2d(x)
        return x
        

if __name__ == "__main__":
    net = TestNet()
    paddle.flops(net, input_size=[1, 2, 320, 320])
    
    
Total GFlops: 0.00778     Total Params: 76.00

API得出的参数量为76,GFLOPs为0.00778,这里的GFLOPs就是FLOPs的10$^9$倍,我们的参数量求得的也是76,那么FLOPs呢?我们来算一下,输入的尺寸为320 * 320, 卷积核为3 * 3, 且padding为1,那么图片输入的大小和输出的大小一致,即输出也是320 * 320, 那么根据我们的公式可得: $76 * 320 * 320 = 7782400$, 与API的一致!因此大家计算卷积层的参数和FLOPs的时候就可以用上面的公式。

3.2 归一化层

最常用的归一化层为BatchNorm2D啦,我们这里就用BatchNorm2D来做例子介绍。在算参数量和FLOPs,先看看BatchNorm的算法流程吧!

输入为:Values of $x$ over a mini-batch:$B={x_1,...,m}$,

$\quad\quad\quad$Params to be learned: $\beta$, $\gamma$

输出为:{$y_i$=BN$_{\gamma}$,$\beta(x_i)$}

流程如下:

$\quad\quad\quad$$\mu_B\gets$ $\frac{1}{m}\sum_{1}^mx_i$

$\quad\quad\quad\sigma_{B}2\gets\frac{1}{m}\sum_{1}m(x_i-\mu_B)^2$

$\quad\quad\quad\hat{x}i\gets\frac{x_i-\mu_B}{\sqrt{\sigma^2+\epsilon}}$

$\quad\quad\quad y_i\gets\gamma\hat{x}i+\beta\equiv BN$,$\beta(x_i)$

在这个公式中,$B$ 为一个Batch的数据,$\beta$ 和 $\gamma$ 为可学习的参数,$\mu$ 和 $\sigma^2$ 为均值和方差,由输入的数据的值求得。该算法先求出整体数据集的均值和方差,然后根据第三行的更新公式求出新的x,最后根据可学习的$\beta$ 和 $\gamma$调整数据。第三行中的 $\epsilon$ 在飞桨中默认为 1e-5, 用于处理除法中的极端情况。

3.2.1 归一化层参数量计算

由于归一化层较为简单,这里直接写出公式: $$Param_{bn2d} = 4 * C_{out}$ $ 其中4表示四个参数值,每个特征图对应一组四个元素的参数组合;

beta_initializer $\beta$ 权重的初始值设定项。

gamma_initializer $\gamma$ 伽马权重的初始值设定项。

moving_mean_initializer $\mu$ 移动均值的初始值设定项。

moving_variance_initializer $\sigma^2$ 移动方差的初始值设定项。

3.2.2 归一化层FLOPs计算

因为只有两个可以学习的权重,$\beta$ 和 $\gamma$,所以FLOPs只需要2乘以输出通道数和输入的尺寸即可。 归一化的FLOPs计算公式则为: $ $FLOP_{bn2d} = 2 * C_{out} * M_{outh} * M_{outw}$ $ 与1.3相似,欢迎大家使用上面的代码进行验证。

3.3 线性层


线性层也是常用的分类层了,我们以飞桨的Linear为例来介绍。

3.3.1线性层参数量计算

其实线性层是比较简单的,它就是相当于卷积核为1的卷积层,线性层的每一个参数与对应的数据进行矩阵相乘,再加上偏置项bias,线性层没有类似于卷积层的“卷”的操作的,所以计算公式如下: $$Param_{linear} = C_{in} * C_{out} + C_{out}$ $。我们这里打印一下线性层参数的形状看看。

import paddle
import numpy as np
linear = paddle.nn.Linear(in_features=2, out_features=4)
params = linear.parameters()
print("shape of weight: ", np.array(params[0]).shape)
print("shape of bias: ", np.array(params[1]).shape)

shape of weight:  (2, 4)
shape of bias:  (4,)

可以看到,线性层相较于卷积层还是简单的,这里我们直接计算这个定义的线性层的参数量为 $2 * 4 + 4 = 12$。具体对不对,我们在下面的实例演示中检查。

3.3.2 线性层FLOPs计算

与卷积层不同的是,线性层没有”卷“的过程,所以线性层的FLOPs计算公式为: $ $FLOP_{linear} = C_{in} * C_{out}$$

3.4 实例演示


这里我们就以LeNet为例子,计算出LeNet的所有参数量和计算量。LeNet的结构如下。输入的图片大小为28 * 28

LeNet(
  (features): Sequential(
    (0): Conv2D(1, 6, kernel_size=[3, 3], padding=1, data_format=NCHW)
    (1): ReLU()
    (2): MaxPool2D(kernel_size=2, stride=2, padding=0)
    (3): Conv2D(6, 16, kernel_size=[5, 5], data_format=NCHW)
    (4): ReLU()
    (5): MaxPool2D(kernel_size=2, stride=2, padding=0)
  )
  (fc): Sequential(
    (0): Linear(in_features=400, out_features=120, dtype=float32)
    (1): Linear(in_features=120, out_features=84, dtype=float32)
    (2): Linear(in_features=84, out_features=10, dtype=float32)
  )
)
我们先来手动算一下参数量和FLOPs。


features[0] 参数量: $6 * 1 * 3 * 3 + 6 = 60$, FLOPs : $60 * 28 * 28 = 47040$

features[1] 参数量和FLOPs均为0

features[2] 参数量和FLOPs均为0, 输出尺寸变为14 * 14

features[3] 参数量: $16 * 6 * 5 * 5 + 16 = 2416$, FLOPs : $2416 * 10 * 10 = 241600$, 需要注意的是,这个卷积没有padding,所以输出特征图大小变为 10 * 10

features[4] 参数量和FLOPs均为0

features[5] 参数量和FLOPs均为0,输出尺寸变为5 * 5, 然后整个被拉伸为[1, 400]的尺寸,其中400为5 * 5 * 16。

fc[0] 参数量: $400 * 120 + 120 = 48120$, FLOPs : $400 * 120 = 48000$ (输出尺寸变为[1, 120])

fc[1] 参数量: $120 * 84 + 84 = 10164$, FLOPs : $120 * 84 = 10080$ (输出尺寸变为[1, 84])

fc[2] 参数量: $84 * 10 + 10 = 850$, FLOPs : $84 * 10 = 840$ (输出尺寸变为[1, 10])。

总参数量为: $60 + 2416 + 48120 + 10164 + 850 = 61610$

总FLOPs为:$47040 + 241600 + 48000 + 10080 + 840 = 347560$

下面我们用代码验证以下:

from paddle.vision.models import LeNet
net = LeNet()
print(net)
paddle.flops(net, input_size=[1, 1, 28, 28], print_detail=True)

+--------------+-----------------+-----------------+--------+--------+
|  Layer Name  |   Input Shape   |   Output Shape  | Params | Flops  |
+--------------+-----------------+-----------------+--------+--------+
|   conv2d_0   |  [1, 1, 28, 28] |  [1, 6, 28, 28] |   60   | 47040  |
|   re_lu_0    |  [1, 6, 28, 28] |  [1, 6, 28, 28] |   0    |   0    |
| max_pool2d_0 |  [1, 6, 28, 28] |  [1, 6, 14, 14] |   0    |   0    |
|   conv2d_1   |  [1, 6, 14, 14] | [1, 16, 10, 10] |  2416  | 241600 |
|   re_lu_1    | [1, 16, 10, 10] | [1, 16, 10, 10] |   0    |   0    |
| max_pool2d_1 | [1, 16, 10, 10] |  [1, 16, 5, 5]  |   0    |   0    |
|   linear_0   |     [1, 400]    |     [1, 120]    | 48120  | 48000  |
|   linear_1   |     [1, 120]    |     [1, 84]     | 10164  | 10080  |
|   linear_2   |     [1, 84]     |     [1, 10]     |  850   |  840   |
+--------------+-----------------+-----------------+--------+--------+
Total GFlops: 0.00034756     Total Params: 61610.00


可以看到,与我们的计算是一致的,大家可以自己把VGG-16的模型算一下参数量FLOPs,相较于LeNet, VGG-16只是模型深了点,并没有其余额外的结构。文章来源地址https://www.toymoban.com/news/detail-453687.html

到了这里,关于深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机竞赛 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别

    🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的动物识别算法研究与实现 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 目前,由于计算机能力和相关理论的发展获得了重大突破,基于深度学

    2024年02月09日
    浏览(86)
  • 计算机视觉与深度学习-卷积神经网络-卷积&图像去噪&边缘提取-图像去噪 [北邮鲁鹏]

    计算机视觉与深度学习-04-图像去噪卷积-北邮鲁鹏老师课程笔记 噪声点,其实在视觉上看上去让人感觉很难受,直观理解就是它跟周围的像素点差异比较大,显得比较突兀,视觉看起来很不舒服,这就是噪声点。 黑丝像素和白色像素随机出现 白色像素随机出现 使用高斯卷积

    2024年02月07日
    浏览(47)
  • 【计算机视觉|生成对抗】用深度卷积生成对抗网络进行无监督表示学习(DCGAN)

    本系列博文为深度学习/计算机视觉论文笔记,转载请注明出处 标题: Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks 链接:[1511.06434] Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks (arxiv.org) 近年来,卷积网络(CNNs)的监督学习

    2024年02月13日
    浏览(63)
  • 深度学习与计算机视觉的新技术:从卷积神经网络到Transformer

    深度学习是一种人工智能技术,它旨在模拟人类大脑中的神经网络,以解决复杂的问题。计算机视觉是人工智能的一个分支,旨在让计算机理解和解析人类视觉系统中的图像和视频。深度学习与计算机视觉的结合,使得计算机在处理图像和视频方面具有强大的能力。 在过去的

    2024年02月02日
    浏览(52)
  • 基于计算机视觉,深度学习、机器学习,OpenCV,图像分割,目标检测卷积神经网络计算机毕业设计选题题目大全选题指导

    随着深度学习、机器学习和神经网络技术的快速发展,计算机视觉领域的应用变得越来越广泛和有趣。本毕业设计旨在探索这一领域的前沿技术,将深度学习模型、神经网络架构、OpenCV图像处理工具,以及卷积神经网络(CNN)的强大能力结合起来,以解决实际图像处理问题。

    2024年02月08日
    浏览(79)
  • Keras-4-深度学习用于计算机视觉-卷积神经网络对 MNIST 数字进行分类:

    本篇学习记录主要包括:《Python深度学习》的第5章(深度学习用于计算机视觉)的第1节(卷积神经网络简介)内容。 相关知识点: 密集层 (Dense层、全连接层) 和 卷积层的区别在于: Dense层从输入特征空间中学到的是全局模式;而卷积层学到的是局部模式 (学到的是卷积核大

    2024年02月11日
    浏览(56)
  • 计算机视觉(五)深度学习基础

    深度学习与神经网络的区别 选择合适的目标函数 Softmax层 梯度消失的直观解释 激活函数 学习步长 SGD的问题 存在马鞍面,使我们的训练卡住,于是提出下面方法: Momentum动量 Nesterov Momentum 先利用“惯性”,“走”一步。避免一开始,就被当前梯度带偏。 Adagrad 为不同的参数

    2024年02月14日
    浏览(55)
  • PyTorch深度学习实战(5)——计算机视觉基础

    计算机视觉是指通过计算机系统对图像和视频进行处理和分析,利用计算机算法和方法,使计算机能够模拟和理解人类的视觉系统。通过计算机视觉技术,计算机可以从图像和视频中提取有用的信息,实现对环境的感知和理解,从而帮助人们解决各种问题和提高效率。本节中

    2024年02月16日
    浏览(41)
  • 计算机视觉基础(10)——深度学习与图像分类

    传统视觉算法采用手工设计特征与浅层模型 ,而手工设计特征依赖于专业知识,且泛化能力差。深度学习的出现改变了这一状况, 为视觉问题提供了端到端的解决方案 。在之前的课程中,我们已经学习了图像分类的传统知识。在本节课中,我们将学习到 图像分类融合深度学

    2024年02月04日
    浏览(86)
  • 计算机竞赛 题目:基于机器视觉opencv的手势检测 手势识别 算法 - 深度学习 卷积神经网络 opencv python

    🔥 优质竞赛项目系列,今天要分享的是 基于机器视觉opencv的手势检测 手势识别 算法 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 普通机器视觉手势检测的基本流程如下: 其中轮廓的提取,多边形

    2024年02月07日
    浏览(80)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包