学习唐进民的《深度学习之PyTorch实战计算机视觉》的理论部分(前四章)的笔记以及部分PyTorch知识。

学习完此部分理论内容之后就可以开始学习 Python(建议
Python初学小知识 专栏)和 PyTorch(建议
深度学习专栏里面的前面几篇深度学习PyTorch笔记)知识,写代码了!

如果已经掌握了基础的 Python 和 PyTorch 知识就可以开始进行实例操作(
深度学习专栏
人工智能实例专栏有一些例子,持续更新)了!


1 神经网络与cv


在使用深度学习方法解决计算机视觉问题的过程中,用得最多的网络架构是一种叫作卷积神经网络(Convolutional Neural Network)的模型。卷积神经网络是人工神经网络的变化和升级,是科学家通过模拟人类大脑的工作原理而发明的。


1.1 经典的人工神经元模型:M-P 模型


在这里插入图片描述





y


j



=


f



(







i


=


1



n




w



i


j





x


i







θ


j



)




y_{j}=f\left(\sum_{i=1}^{n} w_{i j} x_{i}-\theta_{j}\right)


yj=f(i=1nwijxiθj)








  • x


    i




    x_i


    xi
    相当于输入的信息,





    w



    i


    j





    w_{i j}


    wij
    是权重
  • 圆圈等价于在生物神经元中判断是否对输入的信息进行激活、输出的部分
  • 信息×权重,再求和数,然后将求和的结果传送给函数




    f



    f


    f





  • f



    f


    f
    定义了目标阈值的激活函数,这个激活函数只有在满足目标阈值时才能将信息激活及输出






  • θ


    j




    \theta_{j}


    θj
    相当于阈值
  • 可以对权重及激活条件进行设定,来构造与、或、非门

1.2 感知机(Perceptron)的诞生


  • 是一种具有单层计算单元的神经网络模型
  • 是一种能够进行二分类的线性模型,初衷是解决数据的分类问题
  • 只要被处理的数据线性可分,就能使用感知机模型不断地进行模型训练和参数优化,最后得到一个能够对数据进行二分类的模型





    f


    (


    x


    )


    =


    sign





    (


    w





    x


    +


    b


    )



    f(x)=\operatorname{sign}(w \cdot x+b)


    f(x)=sign(wx+b)






    sign





    (


    w





    x


    +


    b


    )


    =



    {







    +


    1








    w





    x


    +


    b


    >


    0













    1








    w





    x


    +


    b


    <


    0









    \operatorname{sign}(w \cdot x+b)=
    {+1wx+b>01wx+b<0



    sign(wx+b)={+11wx+b>0wx+b<0

如果处于二维空间中,那么




w





x


+


b


=


0



w \cdot x+b= 0


wx+b=0
对应的就是对输入数据进行二分类的那条直线,在感知机中我们也把这条直线叫作分割超平面(Separating Hyperplane)。


不过感知机也存在极为明显的优缺点,优点是很容易处理线性可分问题,缺点是不能处理异或问题,也就是说不能处理非线性问题。 所以,之后出现了能够处理非线性问题的多层感知机模型:
在这里插入图片描述
多层感知机和单层感知机的最大区别是多层感知机在它的输入层(Input Layer)和输出层(Output Layer)之间加入了新的网络层次→隐藏层(Hidden Layer)(一层或多层)。同时,多层感知机具备了一种后向传播能力,我们可以暂时将后向传播理解为多层感知机模型进行自我学习和优化的一种方法。


不能只对网络层次进行机械性累加,否则在进行后向传播的过程中会出现梯度消失的问题。对于深层次神经网络模型,我们必须有特别的优化和控制手段。


有一种一种有效的解决方案,是通过无监督预训练对权值进行初始化和有监督训练微调模型(利用现有的样本数据,通过科学的方法不断微调模型参数,使模型的预测结果和真实结果之间的误差值不断减小)。


1.3 计算机视觉


传统的计算机视觉大致分为信息的收集、信息的分析和信息的处理三部分内容。


图像的目标识别 Object Recognition:对识别出的类别对象用长方形进行框选并在框上打上标签名)和语义分割(Semantic Segmentation:对识别出的类别使用同一种像素进行标识并打上标签)可以说是图片分类的升级版本。


2 深度神经网络基础


2.1 监督学习和无监督学习


2.1.1 监督学习(Supervised Learning)


监督学习是有训练的,过程是:


  • 输入数据 datas 和其对应的标签数据 labels;
  • 然后搭建一个模型,让模型在通过训练后准确地找到输入数据和标签数据之间的最优映射关系;
  • 在输入新的数据后,模型能够通过之前学到的最优映射关系,快速地预测出这组新数据的标签。

(1)回归问题
线性回归的使用场景是我们已经获得一部分有对应关系的原始数据,并且问题的最终答案是得到一个连续的线性映射关系,其过程就是使用原始数据对建立的初始模型不断地进行训练,让模型不断拟合和修正,最后得到我们想要的线性模型,这个线性模型能够对我们之后输入的新数据准确地进行预测。
在这里插入图片描述
(2)分类问题
建立的是一个离散的映射关系。
在这里插入图片描述
图片描述的是二分类模型。也可以有多分类模型。


2.1.2 无监督学习(Unsupervised Learning)


无监督学习是没有训练,靠直觉的,过程如下:


  • 提供一组没有任何标签的输入数据;
  • 将其在我们搭建好的模型中进行训练,对整个训练过程不做任何干涉;
  • 最后得到一个能够发现数据之间隐藏特征的映射模型,使用这个映射模型能够实现对新数据的分类
    在这里插入图片描述
    左图显示的是监督学习中的一个二分类模型(两个标签),右图显示的就是无监督学习的过程(没有标签→聚类)。

在无监督训练的整个过程中,我们需要做的仅仅是将训练数据提供给我们的无监督模型,让它自己挖掘数据中的特征和关系。


2.1.3 小结


  • 监督学习需要投入大量精力,更符合设计者的需求和初衷
  • 更具有创造性,不过最后的结果也可能会向不好的方向发展

2.2 欠拟合和过拟合


可以将搭建的模型是否发生欠拟合或者过拟合作为评价模型的拟合程度好坏的指标。


2.2.1 欠拟合


在这里插入图片描述
(a)是已获得的房屋的大小和价格的关系数据;(b)是一个欠拟合模型;(c)所示的是一个较好的拟合模型。


解决问题的方式:


  • 增加特征项
  • 构造复杂的多项式
  • 减少正则化参数

2.2.2 过拟合


在这里插入图片描述
解决问题的方式:


  • 增大训练的数据量
  • 采用正则化方法:在目标函数之后加上范数,用来防止模型过拟合的发生,在实践中最常用到的正则化方法有 L0 正则、L1 正则和 L2 正则)
  • Dropout 方法:在神经网络模型进行前向传播的过程中,随机选取和丢弃指定层次之间的部分神经连接,因为整个过程是随机的,所以能有效防止过拟合的发生

2.3 损失和优化


深度神经网络中的损失 = 预测值 - 真实值,是衡量我们训练出来的模型泛化能力(机器学习算法对新鲜样本的适应能力)好坏的重要指标。预测值和真实值的差距越小,则说明我们训练的模型预测越准确,具有更好的泛化能力。


对模型进行优化的最终目的是尽可能地在不过拟合的情况下降低损失值


2.3.1 损失函数


(1)均方误差(Mean Square Error,简称 MSE)





M


S


E


=



1


N








i


=


1



N





(



y


true 


i







y


pred 


i



)



2




M S E=\frac{1}{N} \sum_{i=1}^{N}\left(y_{\text {true }}^{i}-y_{\text {pred }}^{i}\right)^{2}


MSE=N1i=1N(ytrue iypred i)2

就是方差。
(2)均方根误差(Root Mean Square Error,简称 RMSE)





R


M


S


E


=





1


N








i


=


1



N





(



y


true 


i







y


pred 


i



)



2






R M S E=\sqrt{\frac{1}{N} \sum_{i=1}^{N}\left(y_{\text {true }}^{i}-y_{\text {pred }}^{i}\right)^{2}}


RMSE=N1i=1N(ytrue iypred i)2



(3)平均绝对误差(Mean Absolute Error,MAE)





M


A


E


=



1


N








i


=


1



N








(



y


true 


i







y


pred 


i



)








M A E=\frac{1}{N} \sum_{i=1}^{N}\left|\left(y_{\text {true }}^{i}-y_{\text {pred }}^{i}\right)\right|


MAE=N1i=1N(ytrue iypred i)


2.3.2 优化函数


在实践操作中最常用到的是一阶优化函数,一阶优化函数在优化过程中求解的是参数的一阶导数,这些一阶导数的值就是模型中参数的微调值。典型的一阶优化函数包括 GD、SGD、Momentum、Adagrad、Adam,等等。


(1)全局梯度下降


梯度下降(Gradient Descent,简称 GD)是参数优化的基础方法。虽然梯度下降已被广泛应用,但是其自身纯在许多不足,所以在其基础上改进的优化函数也非常多。


全局梯度下降的参数更新公式如下:






θ


j



=



θ


j






η


×







J



(



θ


j



)









θ


j






\theta_{j}=\theta_{j}-\eta \times \frac{\partial J\left(\theta_{j}\right)}{\partial \theta_{j}}


θj=θjη×θjJ(θj)

学习速率




η



\eta


η
用于控制梯度更新的快慢。


因为模型的训练依赖于整个数据集,所以增加了计算损失值的时间成本和模型训练过程中的复杂度。


(2)批量梯度下降(Batch Gradient Descent,简称 BGD)


每次用一个批量的数据来对模型进行训练,并以这个批量计算得到的损失值为基准来对模型中的全部参数进行梯度更新,默认这个批量只使用一次。






θ


j



=



θ


j






η


×








J



b


a


t


c


h





(



θ


j



)









θ


j






\theta_{j}=\theta_{j}-\eta \times \frac{\partial J_{batch}\left(\theta_{j}\right)}{\partial \theta_{j}}


θj=θjη×θjJbatch(θj)

如果我们将批量划分得足够好,则计算损失函数的时间成本和模型训练的复杂度将会大大降低,不过选择批量梯度下降很容易导致优化函数的最终结果是局部最优解。


(3)随机梯度下降(Stochastic Gradient Descent,简称 SGD)


通过随机的方式从整个参与训练的数据集中选取一部分来参与模型的训练。只要随机选取的数据集大小合适,就不用担心计算损失函数的时间成本和模型训练的复杂度,而且与整个参与训练的数据集的大小没有关系。








θ


j



=



θ


j






η


×








J



s


t


o


c


h


a


s


t


i


c





(



θ


j



)









θ


j






\theta_{j}=\theta_{j}-\eta \times \frac{\partial J_{stochastic}\left(\theta_{j}\right)}{\partial \theta_{j}}


θj=θjη×θjJstochastic(θj)


模型会受到随机训练数据集中噪声数据的影响,又因为有随机的因素,所以也容易导致模型最终得到局部最优解。


(4)自适应时刻估计方法(Adaptive Moment Estimation,简称 Adam)


Adam 在模型训练优化的过程中通过让每个参数获得自适应的学习率,来达到优化质量和速度的双重提升
例如:假设我们在一开始进行模型参数的训练时损失值比较大,则这时需要使用较大的学习速率让模型参数进行较大的梯度更新,但是到了后期我们的损失值已经趋近于最小了,这时就需要使用较小的学习速率让模型参数进行较小的梯度更新,以防止在优化过程中出现局部最优解。


Adam 收敛速度快、学习效果好的优点脱不了干系,而且对于在优化过程中出现的学习速率消失、收敛过慢、高方差的参数更新等导致损失值波动等问题,Adam 都有很好的解决方案。


2.4 激活函数


没有激活函数的单层神经网络模型:





f


(


x


)


=


W





X



f(x) = W\cdot X


f(x)=WX
其中的大写字母代表矩阵或者张量。


加入偏置的单层神经网络模型:





f


(


x


)


=


W





X


+


b



f(x) = W\cdot X + b


f(x)=WX+b


加入激活函数的二层神经网络:





f


(


x


)


=


max






(



W


2






max






(



W


1






X


+



b


1



,


0


)



+



b


2



,


0


)




f(x)=\max \left(W_{2} \cdot \max \left(W_{1} \cdot X+b_{1}, 0\right)+b_{2}, 0\right)


f(x)=max(W2max(W1X+b1,0)+b2,0)
激活条件是比较 0 和输入值中的最大值。


三层神经网络模型,并且每层的神经输出都使用同样的激活函数:





f


(


x


)


=


max






(



W


3






max






(



W


2






max






(



W


1






X


+



b


1



,


0


)



+



b


2



,


0


)



+



b


3



,


0


)




f(x)=\max \left(W_{3} \cdot \max \left(W_{2} \cdot \max \left(W_{1} \cdot X+b_{1}, 0\right)+b_{2}, 0\right)+b_{3}, 0\right)


f(x)=max(W3max(W2max(W1X+b1,0)+b2,0)+b3,0)


如果没有激活函数,而我们只是一味地加深模型层次,则搭建出来的神经网络数学表示如下:





f


(


x


)


=



W


3







(



W


2







(



W


1






X


+



b


1



)



+



b


2



)



+



b


3




f(x)=W_{3} \cdot\left(W_{2} \cdot\left(W_{1} \cdot X+b_{1}\right)+b_{2}\right)+b_{3}


f(x)=W3(W2(W1X+b1)+b2)+b3


可以看出,上面的模型仍然是一个线性模型,如果不引入激活函数,则无论我们加深多少层,其结果都一样,线性模型在应对非线性问题时会存在很大的局限性。激活函数的引入给我们搭建的模型带来了非线性因素。


2.4.1 Sigmoid







f


(


x


)


=



1



1


+



e






x







f(x)=\frac{1}{1+e^{-x}}


f(x)=1+ex1

在这里插入图片描述
缺点:


  • 使用 Sigmoid 作为激活函数会导致模型的梯度消失,因为Sigmoid 导数的取值区间为 0~0.25。在后向传播的过程中每逆向经过一个节点,梯度值的大小就会变成原来的四分之一,如果模型层次达到了一定深度,那么后向传播会导致梯度值越来越小,直到梯度消失。
  • Sigmoid 函数的输出值恒大于 0,这会导致模型在优化的过程中收敛速度变慢,会增加时间成本。因此,在选取参与模型中相关计算的数据时,要尽量使用零中心(Zero-Centered)数据;而且要尽量保证计算得到的输出结果是零中心数据。

2.4.2 tanh







f


(


x


)


=





e


x







e






x







e


x



+



e






x







f(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}


f(x)=ex+exexex

在这里插入图片描述
tanh 函数的输出结果是零中心数据,所以解决了激活函数在模型优化过程中收敛速度变慢的问题。而 tanh 函数的导数取值区间为 0~1,仍然不够大,所以,在深度神经网络模型的后向传播过程中仍有可能出现梯度消失的情况。


2.4.3 ReLU(Rectified Linear Unit,修正线性单元)


是目前在深度神经网络模型中使用率最高的激活函数:





f


(


x


)


=


m


a


x


(


0


,


x


)



f(x) = max(0, x)


f(x)=max(0,x)

在这里插入图片描述
ReLU 函数的收敛速度非常快,其计算效率远远高于 Sigmoid 和 tanh。


缺点是ReLU 的输出并不是零中心数据,这可能会导致某些神经元永远不会被激活,并且这些神经元相对应的参数不能被更新。这一般是由于模型参数在初始化时使用了全正或者全负的值,或者在后向传播过程中设置的学习速率太快导致的。
其解决方法是对模型参数使用更高级的初始化方法如Xavier,以及设置合理的后向传播学习速率,推荐使用自适应的算法如 Adam。


3 卷积神经网络(Convolutional Neural Networks,简称 CNN)


一个标准的卷积神经网络架构主要由卷积层、池化层和全连接层等核心层次构成。


3.1 基础知识


3.1.1 卷积层(Convolution Layer)


主要作用是对输入的数据进行特征提取,而完成该功能的是卷积层中的卷积核(Filter)。我们可以将卷积核看作一个指定窗口大小的扫描器,扫描器通过一次又一次地扫描输入的数据,来提取数据中的特征。如果我们输入的是图像数据,那么在通过卷积核的处理后,就可以识别出图像中的重要特征了。


在卷积层中是如何定义这个卷积核?
输入图像:高度×宽度×深度 = 32×32×3,3指图像具有 R、G、B 三个色彩通道。
卷积核:高度×宽度×深度 = 5×5×3,深度要保证与输入图像的色彩通道一致。
如果输入图像是单色彩通道的,那么卷积核的深度就是 1。
在这里插入图片描述
−8 = 0×4 + 0×0 + 0×0 + 0×0 + 1×0 + 1×0 + 0×0 + 1×0 + 2×(−4)


下面,根据我们定义的卷积核步长对卷积核窗口进行滑动。卷积核的步长其实就是卷积核窗口每次滑动经过的图像上的像素点数量。
用于提升卷积效果的边界像素填充方式:Valid 方式就是直接对输入图像进行卷积,不对输入图像进行任何前期处理和像素填充,这种方式的缺点是可能会导致图像中的部分像素点不能被滑动窗口捕捉;Same 方式是在输入图像的最外层加上指定层数的值全为 0 的像素边界,这样做是为了让输入图像的全部像素都能被滑动窗口捕捉。


卷积通用公式:用于计算输入图像经过一轮卷积操作后的输出图像的宽度和高度的参数










W


output 



=





W


input 







W


filter 



+


2


P



S



+


1











H


output 



=





H


input 







H


filter 



+


2


P



S



+


1







Woutput =Winput Wfilter +2PS+1Houtput =Hinput Hfilter +2PS+1



Woutput =SWinput Wfilter +2P+1Houtput =SHinput Hfilter +2P+1


  • W 和 H 分别表示图像的宽度(Weight)和高度(Height)的值
  • 下标input、output、filter分别表示输入特征图、输出特征图、滑动窗口的相关参数
  • S 表示卷积核的步长
  • P (是 Padding 的缩写)表示在图像边缘增加的边界像素层数,如果图像边界像素填充方式选择的是 Same 模式,那么 P 的值就等于图像增加的边界层数;如果选择的是 Valid 模式,那么 P = 0

举例:三个色彩通道的输入图像的卷积过程
在这里插入图片描述


3.1.2 池化层


一种提取输入数据的核心特征的方式,不仅实现了对原始数据的压缩,还大量减少了参与模型计算的参数,从某种意义上提升了计算效率。其中,最常被用到的池化层方法是平均池化层和最大池化层,池化层处理的输入数据在一般情况下是经过卷积操作之后生成的特征图。


池化层也需要定义一个类似卷积层中卷积核的滑动窗口,但是这个滑动窗口仅用来提取特征图中的重要特征,本身并没有参数。下面是平均池化层和最大池化层。
在这里插入图片描述


在这里插入图片描述
上面两张图是对单层特征图进行的操作,并且滑动窗口的步长为 2。高度×宽度是 2×2,滑动窗口的深度和特征图的深度保持一致。


池化通用公式:计算输入的特征图经过一轮池化操作后输出的特征图的宽度和高度
















W


output 



=





W


input 







W


filter 




S



+


1

















H


output 



=





H


input 







H


filter 




S



+


1







Woutput =Winput Wfilter S+1Houtput =Hinput Hfilter S+1



Woutput =SWinput Wfilter +1Houtput =SHinput Hfilter +1
池化层不仅能够最大限度地提取输入的特征图的核心特征,还能够对输入的特征图进行压缩。


值得一提的是,当时使用Pooling(池化)操作时,是为了简化计算,但是现在的计算量足够,为了避免丢失信息。因此现在的学术界已经倾向于不用Pooling了。


3.1.3 全连接层


全连接层的主要作用是将输入图像在经过卷积和池化操作后提取的特征进行压缩,并且根据压缩的特征完成模型的分类功能。
在这里插入图片描述


  • 前面通过卷积层和池化层提取的输入图像的核心特征,与全连接层中定义的权重参数相乘
  • 最后被压缩成仅有的 10 个输出参数(已经是一个分类的结果)
  • 再经过激活函数的进一步处理,就能让我们的分类预测结果更明显
  • 将 10 个参数输入到 Softmax 激活函数中,激活函数的输出结果就是模型预测的输入图像对应各个类别的可能性值。

3.2 模型实例


3.2.1 LeNet 模型


在这里插入图片描述


  • C1、C3、C5是卷积层
  • S2、S4是下采样层,下采样要完成的功能是缩减输入的特征图的大小,这里都使用了最大池化层来进行下采样
  • F6是全连接层

3.2.2 AlexNet 模型


在这里插入图片描述
Conv是卷积层、MaxPool是最大池化层、FC是全连接层


3.2.3 VGGNet 模型


相对 AlexNet 而言,在 VGGNet 模型中统一了卷积中使用的参数,比如卷积核滑动窗口的高度和宽度统一为 3×3,卷积核步长统一为 1,Padding 统一为 1,等等;而且增加了卷积神经网络模型架构的深度,分别定义了 16 层的 VGG16 模型和 19 层的 VGG19 模型,与 AlexNet 的 8 层结构相比,深度更深。


这两个重要的改变对于人们重新定义卷积神经网络模型架构也有不小的帮助,至少证明使用更小的卷积核并且增加卷积神经网络的深度,可以更有效地提升模型的性能。
在这里插入图片描述


3.2.4 GoogleNet


与 VGGNet 模型相比较,GoogleNet 模型的网络深度已经达到了 22 层,而且在网络架构中引入了 Inception 单元。这两个重要的改变证明,通过使用 Inception 单元构造的深层次卷积神经网络模型,能进一步提升模型整体的性能。
在这里插入图片描述
(1)Naive Inception 单元的结构:
在这里插入图片描述
前一层(Previous Layer)是 Naive Inception 单元的数据输入层,之后被分成了 4 个部分,这 4 个部分分别对应滑动窗口的高度和宽度为 1×1 的卷积层、3×3 的卷积层、5×5 的卷积层和 3×3 的最大池化层,然后将各层计算的结果汇聚至合并层(Filter Concatenation),在完成合并后将结果输出。


【实例:】
输入一个32×32×256的特征图,该特征图先被复制成 4 份并分别被传至接下来的 4 个部分(设这 4 个部分对应的滑动窗口的步长均为 1),其中:


  • 1×1 卷积层的 Padding 为 0,滑动窗口维度为 1×1×256,要求输出的特征图深度为 128
  • 3×3 卷积层的 Padding 为 1,滑动窗口维度为 3×3×256,要求输出的特征图深度为 192
  • 5×5 卷积层的 Padding 为 2,滑动窗口维度为 5×5×256,要求输出的特征图深度为 96
  • 3×3 最大池化层的 Padding 为 1,滑动窗口维度为 3×3×256。

之后通过计算,分别得到这 4 部分输出的特征图为 32×32×128、32×32×192、32×32×96 和 32×32×256,最后在合并层进行合并(相加),得到32×32×672 的特征图。


缺点:


  • 所有卷积层直接和前一层输入的数据对接,所以卷积层中的计算量会很大
  • 在这个单元中使用的最大池化层保留了输入数据的特征图的深度,所以在最后进行合并时,总的输出的特征图的深度只会增加,这样增加了该单元之后的网络结构的计算量。

(2)GoogleNet 模型中的 Inception 单元结构


NIN(Network in Network)中 1×1 卷积层的作用:能够完成特征图通道的聚合或发散。
【举例说明:】
现在有一个维度为 50×50×100 的特征图,将之输入 1×1 的卷积层中,卷积核的滑动窗口维度为 1×1×100。
特征图通道的聚合:如果我们想要输出一个深度为 90 的特征图,则卷积 90 次,就变成了维度为 50×50×90的特征图。
特征图通道的发散:如果我们想要输出一个深度为 100 的特征图,则卷积 100 次,就变成了维度为 50×50×100的特征图。


通过 1×1 卷积层来控制特征图最后输出的深度,从而间接影响了与其相关联的层的卷积参数数量。
比如将一个 32×32×10 的特征图输入 3×3 的卷积层中,要求最后输出的特征图深度为 20,那么在这个过程中需要用到的卷积参数为 10×3×3×20 = 1800 个。
如果将 32×32×10 的特征图先输入 1×1 的卷积层中,使其变成 32×32×5 的特征图,再将其输入 3x3 的卷积层中,那么在这个过程中需要用到的卷积参数减少至 1×1×10×5 + 3×3×5×20 = 950 个。


总结:使用 1×1 的卷积层使卷积参数几乎减少了一半,极大提升了模型的性能。


GoogleNet 在 Naive Inception 单元的基础上对单元结构进行了改进:
在这里插入图片描述
GoogleNet 中的 Inception 单元与 Naive Inception 单元的结构相比,就是在上图的相应位置增加了 1×1 的卷积层。
假设新增加的 1×1 的卷积的输出深度为 64,步长为 1,Padding 为 0,其他卷积和池化的输出深度、步长都和之前在 Naive Inception 单元中定义的一样,前一层输入的数据仍然使用同之前一样的维度为 32×32×256 的特征图,通过计算,分别得到这 4 部分输出的特征图维度为 32×32×128、32×32×192、32×32×96 和 32×32×64,将其合并后得到维度为 32×32×480 的特征图,将这 4 部分输出的特征图进行相加,最后 Inception 单元输出的特征图维度是 32×32×480。
在输出的结果中,32×32×128、32×32×192、32×32×96 和之前的 Naive Inception 单元是一样的,但其实这三部分因为 1×1 卷积层的加入,总的卷积参数数量已经大大低于之前的 Naive Inception 单元,而且因为在最大池化层之前也加入了 1×1 的卷积层,所以最终输出的特征图的深度也降低了。


(3)GoogleNet 的网络架构


起始部分:
在这里插入图片描述
Local Response Normalization 是在模型中使用的局部响应归一化层。这个起始部分的输出结果作为Inception 单元堆叠部分的输入。


分类输出部分:
在这里插入图片描述
最后分类输出部分的输入数据来自 Inception 单元堆叠部分最后一个Inception 单元的合并输出,AveragePool 层对应模型中的平均池化层(Average pooling),FC 层对应模型中的全连接层,Softmax 对应模型最后进行分类使用的 Softmax 激活函数。


小结:在 GoogLeNet 模型中使用 Inception 单元,使卷积神经网络模型的搭建实
现了模块化,如果我们想要增加或者减少 GoogLeNet 模型的深度,则只需增添或者减少相应的 Inception 单元就可以了,非常方便。另外,为了避免出现深层次模型中的梯度消失问题,在 GoogLeNet 模型结构中还增加了两个额外的辅助 Softmax 激活函数,用于向前传导梯度。


更详细的讲解可以参考这里


3.2.5 ResNet


ResNet 模型中引入了一种残差网络(Residual Network)结构,通过使用残差网络结构,深层次的卷积神经网络模型不仅避免了出现模型性能退化的问题,反而取得了更好的性能。下面是一个具有 34 层网络结构的 ResNet 模型:
在这里插入图片描述
虽然 ResNet 模型的深度达到了 34 层,但是其实在 ResNet 模型中大部分结构都是残差网络结构,所以同样具备了模块化的性质。


在 ResNet 模型中大量使用了一些相同的模块来搭建深度更深的网络,却没有出现梯度消失、极易过拟合等模型性能退化的问题,其中一个非常关键的因素就是模型累加的模块并非简单的单输入单输出的结构,而是一种设置了附加关系(恒等映射:Identity Mapping)的新结构(残差网络结构


【没有设置附加关系的单输入单输出模块:】
在这里插入图片描述
【设置附加的恒等映射关系的残差网络结构:】
在这里插入图片描述
上面两个结构唯一不同的是残差模块的最终输出结果 = 输入数据 X 经过两个卷积之后的输出 F(X) + 输入数据的恒等映射。
这个简单的加法并不会给整个 ResNet 模型增加额外的参数和计算量,却能加快模型的训练速度,提升模型的训练效果;另外,在我们搭建的 ResNet 模型的深度加深时,使用残差模块的网络结构不仅不会出现模型退化问题,性能反而有所提升。
这里需要注意附加的恒等映射关系的两种不同的使用情况,残差模块的输入数据若和输出结果的维度一致,则直接相加;若维度不一致,则先进行线性投影,在得到一致的维度后,再进行相加或者对维度不一致的部分使用 0 填充。


【让网络结构更深的残差模块:】
在这里插入图片描述
在之前的残差模块的基础上引入了 NIN,使用 1×1 的卷积层来减少模型训练的参数量,同时减少整个模型的计算量,使得拓展更深的模型结构成为可能。于是出现了拥有 50层、101 层、152 层的 ResNet 模型,这不仅没有出现模型性能退化的问题,而且错误率和计算复杂度都保持在很低的程度。


4 深度学习框架(Deep Learning Frameworks)


  • GPU单个核计算能力比CPU差,但是GPU的核是CPU的几百倍
  • GPU更适合并行计算(矩阵乘),CPU更适合串行计算
  • 深度学习中就是一层一层的矩阵乘法

TensorFlow、Caffe、PyTorch都是在cuDNN的基础上的框架


  • TensorFlow是静态的,首先会用一大段代码定义计算图的样子,然后给新的数据(只能在同一个计算图上运行)

  • TensorFlow构建好的图是不变的,只是在同一张图上跑不同的数据

  • TensorFlow支持分布式的GPU,更方便企业

  • PyTorch是动态的,每个数据可以有自己不同的地方

  • PyTorch的网络结构可以依赖于数据


5 PyTorch介绍


主要是下面这三个部分。


5.1 Tensor


张量,多维数组,相当于numpy中的ndarray,不过Tensor可以在GPU上跑。


5.2 Variable


可以记住每一个Tensor在计算图中的位置,可以计算出当前Variable和之前的Variable在逻辑上的关系。


记住当前变量是依赖于哪一些变量,是怎样依赖的,这样,就可以用一句loss.backward()来求导。


所以不用Variable是不能求导的,必须将感兴趣的变量用 Variable 初始化封装一下,才可以自动求导,不过有一些变量我们对它们的导数不感兴趣,就可以用requires_grad=False取消求导。 一般对输入不感兴趣,对权重感兴趣。


前向传播:给一个值,算出输出;后向传播:给一个值,输出梯度。


5.3 Module


Variable之上,一个最顶层的抽象。对应神经网络中不同的层layer。


PyTorch中,nn这个库提供了Module,可以非常简洁地实现神经网络模型和损失函数的构造,直接调用即可。


optim这个包里面包含了很多常见的优化算法,权值更新时就调用optimizer.step()就可以更新模型中的所有参数**