【神经网络】学习笔记二—神经网络基本组成

本文参考自:神经网络基本组成 - 池化层、Dropout层、BN层、全连接层 13 - 赵家小伙儿 - 博客园 (cnblogs.com)

本篇简单说一下几个层,具体的详见本系列其他博文。

当前的物体检测算法虽然各不相同, 但第一步通常是利用卷积神经网络处理输入图像, 生成深层的特征图, 然后再利用各种算法完成区域生成与损失计算, 这部分卷积神经网络是整个检测算法的“骨架”, 也被称为Backbone。

Backbone是物体检测技术的基础, 其中也涌现出了多种经典的结构, 如VGGNet、 ResNet和DenseNet等, 如下图所示:

物体检测算法使用的通常是包含卷积计算且具有深度结构的前馈神经网络, 如卷积层、 池化层、 全连接层等不同的基本层, 这些层有着不同的作用。 本节将针对物体检测算法中常用的网络层进行一一介绍。

1. 池化层Pooling

池化层是当前卷积神经网络CNN中常用组件之一。池化层模仿人的视觉系统对数据进行降维,用更高层次的特征表示图像。

池化层以降低特征图的参数量来降低信息冗余,提升计算速度,增加感受野,以提升模型的尺度不变性,旋转不变性,是一种降采样操作。使得模型更关注全局特征而非局部出现的位置,这种降维的过程中可以保留一些重要的特征信息,提升容错能力,且在一定程度上起到防止过拟合的作用。

池化层的常见操作包含以下几种:最大值池化,均值池化,随机池化,中值池化,组合池化等。池化层有两个主要的输入参数:核尺寸kernel_size和步长stride。在物体检测过程中,常用的有最大值池化和均值池化。

(1)最大值池化

最大值池化是最常见的,应用最多的池化操作,实现方式简单,即挑出最大的数据即可。

如图为一个核尺寸和步长都为2的最大值池化过程,以左上角为例,9,20,15,26进行最大值池化,保留26。

在前向过程中,选择图像区域中的最大值作为该区域池化后的值,后向过程中,梯度通过前向过程时的最大值反向传播,其他位置的梯度为0。

最大值池化的优点在于它能学习到图像的边缘和纹理结构。

(2)均值池化

前向传播过程中,均值作为区域池化后的值;反向传播过程中,梯度特征均分到各个位置。

实际应用中,均值池化往往以全局均值池化的形式出现。常见于SE模块和分类模块中。极少见于作为下采样模块用于分类网络中。

均值池化的优点是减少估计均值的便宜,提升模型的鲁棒性。

下面是pytorch对池化层的实现:

import torch
from torch import nn

max_pooling = nn.MaxPool2d(2, stride=2)
aver_pooling = nn.AvgPool2d(2, stride=2)

input = torch.randn(1, 1, 4, 4)
print(input)
>> tensor([[[[ 1.2237, -0.8173, -0.2594,  0.1698],
          [-0.1023,  0.6973, -0.6429,  0.8561],
          [-0.3660,  0.1269,  0.2488,  0.0576],
          [ 0.0859,  0.1622, -0.0725, -0.0237]]]])

# 池化主要需要两个参数, 第一个参数代表池化区域大小, 第二个参数表示步长
out_max = max_pooling(input)
print(out_max)
>> tensor([[[[1.2237, 0.8561],
          [0.1622, 0.2488]]]])

# 调用最大值池化与平均值池化, 可以看到size从[1, 1, 4, 4]变为了[1, 1, 2, 2]
out_aver = aver_pooling(input)
print(out_aver)
>> tensor([[[[0.2503, 0.0309],
          [0.0023, 0.0525]]]])

2. dropout层

在深度学习中,当参数过多而训练样本又比较少时,模型容易产生过拟合现象。过拟合是很多深度学习乃至机器学习算法的通病,具体表现为训练集上预测准确率高,而测试集上准确率大幅下降。2012年,Hinton等人提出了dropout算法,可有效缓解过拟合现象的发生,起到一定的正则化效果。

基本思想如下图所示。训练时,每个神经元以概率p保留,即以概率1-p停止工作,每次前向传播保留下来的神经元都不同,这样可以使得模型不太依赖于某些局部特征,泛化性能更强。在测试时,为了保证相同的输出期望值,每个参数还要乘以p。有另外一种计算方式成为Inverted dropout,即在训练时将保留下的神经元乘以1/p,这样测试时不需要再改变权重。

dropout为什么可以防止过拟合的原因,可以从以下三个方面解释:

——多模型的平均:不同的固定神经网络会有不同的过拟合,多个取平均则可能让一些相反的拟合抵消掉。而dropout每次都是不同的神经元失活,可以看作是多个模型的平均,类似于多数投票取胜的策略。

——减少神经元之间的依赖:由于两个神经元不一定同时有效,因此减少了特征之间的依赖,迫使网络学习更有鲁棒性。因为神经网络不应该对特定的特征敏感,而应该从众多的特征中学习更为共同的规律,这也起到了正则化的效果。

——生物进化:dropout扮演类似于性别在生物进化中的角色,物种为了适应环境变化,繁衍时雌雄的基因各取一半,这样可以适应更复杂的新环境,避免单一基因的过拟合,当环境发生变化时不至于灭绝。

dropout被广泛应用到全连接层中,一般的保留概率设置为0.5,而在较为稀疏的卷积网络中则一般使用下面的BN层来正则化模型,使得训练更稳定。

实现如下:

import torch
from torch import nn

# PyTorch将元素置0来实现Dropout层, 第一个参数为置0概率, 第二个为是否原地操作
dropout = nn.Dropout(0.5, inplace=False)
input = torch.randn(2, 64, 7, 7)
output = dropout(input)

print(output.shape)

3. BN层

为了追求更高的性能,卷积网络被设计的越来越深,然而网络却变得难以训练收敛和调参。原因在于,浅层参数的微弱变化经过多层线性变化与激活函数后会被放大,改变了每一层的输入分布,造成深层的网络需要不断调整以适应这些分布变化,最总导致模型难以训练收敛。

由于网络中参数变化导致的内部节点数据分布发生变化的现象被称为ICS(Internal Covariate Shift)。ICS现象容易使训练过程陷入饱和区,减慢网络的收敛。前面提到的relu从激活函数的角度出发,在一定程度上解决梯度饱和的现象。而2015年提出的BN层,则从改变数据分布的角度避免了参数陷入饱和区。由于BN层优越的性能,其已经是当前卷积网络的标配。

BN层首先对每一个batch的输入特征进行白化操作,即去均值方差过程。假设一个batch的输入数据为x:B={x1,x2,…,xm},首先求该batch数据的均值与方差。求得均值和方差后,利用下式进行去均值方差操作:

白化操作可以使输入的特征分布具有相同的均值与方差,固定了每一层的输入分布,从而加速网络的收敛。然而,白化操作虽然从一定程度上避免了梯度饱和,但也限制了网络中数据的表达能力,浅层学到的参数信息会被白化操作屏蔽掉。因此,BN层在白化操作后又增加了一个线性变换操作,让数据尽可能恢复本身的表达能力,如下式:

式中,\gamma\beta为新引进的两个学习参数,最终的输出为yi。

BN层可以看作是增加了线性变换的白化操作,在实际工程中被证明了能够缓解神经网络难以训练的问题。其优点主要有以下三点:

——缓解梯度消失,加速网络收敛。BN层可以让激活函数的输入数据落在非饱和区,缓解了梯度消失问题。此外,由于每层数据的均值和方差都在一定范围内,深层网络不必去不断适应浅层网络输入的变化,实现了层间解耦,允许每一层独立学习,也加快了网络的收敛。

——简化调参,网络更稳定。在调参时,学习率调的过大容易出现震荡和不收敛,BN层则抑制了参数微小变化随网络加深而被放大的问题,因此对于参数变化的适应能力更强,更容易调参。

——防止过拟合。BN层将每一个batch的均值和方差引入到网络中,由于每个batch的这两个值都不同,可看作为训练过程增加了随即噪音,也可以起到一定的正则效果,防止过拟合。

在测试时,由于是对单个样本进行测试,没有batch的均值和方差,通常做法是在训练时将每一个batch的均值与方差都保留下来,在测试时使用所有训练样本均值与方差的平均值。

实现如下:

import torch
from torch import nn
# 使用BN层需要传入一个参数为num_features, 即特征的通道数
bn = nn.BatchNorm2d(64)
print(bn)
>> BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

# eps为公式中的є, momentum为均值方差的动量, affine为添加可学习参数
input = torch.randn(4, 64, 224, 224)
output = bn(input)
# BN层不改变输入、 输出的特征大小
print(output.shape)
>>  torch.Size([4, 64, 224, 224])

尽管BN层取得了巨大的成功,但仍有一定弊端:

——由于是在batch的维度进行归一化,BN层要求较大的batch才能有效地工作。而物体检测等任务由于占用内存较高,限制了batch的大小,这限制了BN层有效的发挥归一化功能。

——数据的batch大小在训练与测试时往往不一样。在训练时一般采用滑动来计算平均值与方差,在测试时直接拿训练集的平均值与方差来使用,这会导致测试机依赖于训练集,然而有时测试集和训练集分布并不一致。

——因此,我们能不能避开batch来进行归一化呢?答案是可以的。最新的工作GN从通道方向计算均值和方差,使用更为灵活有效,避开了batch大小对归一化的影响。

具体来说,GN首先将特征图的通道分为多个组,对每一个组内的参数作归一化,而不是batch。GN之所以能够工作的原因是因为在特征图中,不同的通道代表了不同的意义,例如形状,边缘和纹理等。这些不同的通道并不是完全独立的分布,而是可以放到一起进行归一化分析。

4. 全连接层

全连接层(Fully Connected Layers) 一般连接到卷积网络输出的特征图后边,特点是每一个节点都与上下层的所有节点相连,输入与输出都被延展成一维向量,因此从参数量来看,全连接层的参数量是最多的,如下图所示:

在物体检测算法中,卷积网络的主要作用是从局部到整体的提取图像的特征,而全连接层则用来将卷积抽象出来的特征图进一步映射到特定维度的标签空间,以求取损失或者输出预测结果。

pytorch使用全连接层徐娅指定输入与输出的维度:

import torch
from torch import nn
# 第一维表示一共有4个样本
input = torch.randn(4, 1024)
linear = nn.Linear(1024, 4096)
output = linear(input)

print(input.shape)
>> torch.Size([4, 1024])
print(output.shape)
>> torch.Size([4, 4096])

然而随着深度学习的发展,全连接层的缺点也逐渐暴露了出来,最致命的问题在于其参数量的庞大。再次以VGGNet为例说明。其第一个全连接层的输入特征为7*7*512=25088个节点,输出特征是大小为4096的一维向量,由于输出层的每一个点都来自于上一层所有点的权重相加,因为这一层的参数量为25088*4096≈108。相比之下,VGGNet最后一个卷积层的卷积核大小为3*3*512*512≈2.4×106, 全连接层的参数量是这一个卷积层的40多倍。

大量的参数会导致网络模型应用部署困难,并且其中存在着大量的参数冗余,也容易发生过拟合的现象。在跟多场景中,我们可以使用全局平均池化层(Global Average Pooling, GAP)来取代全连接层,这种思想最早见于NIN(Network in Network) 网络中, 总体上, 使用GAP有如下3点好处:

——利用池化实现了降维,极大的减少了网络的参数量;

——将特征提取与分类合二为一,一定程度上可以防止过拟合;

——由于去除了全连接层,可以实现任意图像尺度的输入。

5. 卷积层

卷积本事数学分析中的一种运算,在深度学习中使用的卷积通常是离散的。作为卷积神经网络CNN中最基础的组成成分,卷积的本质是用卷积核的参数来提取数据的特征,通过矩阵点乘运算与求和运算来得到结果。

下图所示为一个基本二维卷积的运算过程,公式为y=ωx+b。 这里的特征图(x)大小为1*5*5,即输入通道数为1,卷积核(ω) 大小为3*3, 偏置(b) 为1,为保证输出维度和输入特征维度一致, 还需要有填充(padding) , 这里使用zero-padding, 即用0来填充。

卷积核参数与对应位置像素逐位相乘后累加作为一次计算结果。 以图3.3左上角为例, 其计算过程为1×0+0×0+1×0+0×0+1×1+0×8+1×0+0×6+1×7+1=9, 然后在特征图上进行滑动, 即可得到所有的计算结果。

用pytorch实现如下:

import torch
from torch import nn

# 查看卷积核的基本信息, 本质上是一个Module
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1,
            padding=1, dilation=1, groups=1, bias=True)
print(conv)
# >> Conv2d(1, 1, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))

# 通过.weight与.bias查看卷积核的权重与偏置
print(conv.weight.shape)
# >> torch.Size([1])
print(conv.bias.shape)


# 输入特征图, 需要注意特征必须是四维, 第一维作为batch数, 即使是1也要保留
# batch*channels*W*H
input = torch.ones(1, 1, 5, 5)
output = conv(input)

# 当前配置的卷积核可以使输入和输出的大小一致
print(input.shape)
>> torch.Size([1, 1, 5, 5])
print(output.shape)
>> torch.Size([1, 1, 5, 5])

对于torch.nn.Conv2d()来说, 传入的参数含义如下:
——in_channels: 输入特征图的通道数, 如果是RGB图像, 则通道数为3。 卷积中的特征图通道数一般是2的整数次幂。

——out_channels: 输出特征图的通道数。

——kernel_size: 卷积核的尺寸, 常见的有1、 3、 5、 7。

——stride: 步长, 即卷积核在特征图上滑动的步长, 一般为1。 如果大于1, 则输出特征图的尺寸会小于输入特征图的尺寸。

——adding: 填充, 常见的有零填充、 边缘填充等, PyTorch默认为零填充。

——dilation: 空洞卷积, 当大于1时可以增大感受野的同时保持特征图的尺寸( 后面会细讲) , 默认为1。

——groups: 可实现组卷积, 即在卷积操作时不是逐点卷积, 而是将输入通道分为多个组, 稀疏连接达到降低计算量的目的( 后续会细讲) ,默认为1。

——bias: 是否需要偏置, 默认为True。在实际使用中, 特征图的维度通常都不是1, 假设输入特征图维度为m×win×hin, 输出特征图维度为n×wout×hout, 则卷积核的维度为n×m×k×k, 在此产生的乘法操作次数为n×wout×hout×m×k×k。

6. maxout层

放到下节细讲。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值