第六章 视觉处理基础
6.1 卷积神经网络简介
卷积神经网络由一个或多个卷积层和顶端的全连通层(对应经典的神经网络)组成,同时也包括关联权重和池化层(Pooling Layer)等。图6-1就是一个卷积神经网络架构。
图6-1为卷积神经网络的一般结构,其中包括卷积神经网络的常用层, 如卷积层、池化层、全连接层和输出层;有些还包括其他层,如正则化层、 高级层等。用代码定义这个卷积神经网络。
1)导入相关模块
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
2)定义网络(对于图6-1)
class CNNNet01(nn.Module):
def __init__(self):
super(CNNNet,self).__init__()
self.conv1 = nn.Conv2d(in_channels=3,out_channels=16,kernel_size=5,stride=1)
self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)
self.conv2 = nn.Conv2d(in_channels=16,out_channels=36,kernel_size=5,stride=1)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.dense1 = nn.Linear(900,128)
self.dense2 = nn.Linear(128,10)
def forward(self,x):
x=self.pool1(F.relu(self.conv1(x)))
x=self.pool2(F.relu(self.conv2(x)))
x=x.view(-1,900)
x=F.relu(self.dense2(F.relu(self.dense1(x))))
return x
6.2 卷积层
卷积层是卷积神经网络的核心层,而卷积(Convolution)又是卷积层的核心。
参考:CNN中的卷积和反卷积_HHzdh的博客-CSDN博客
参考:卷积和反卷积输出的计算公式_HHzdh的博客-CSDN博客_反卷积计算公式
6.2.1 卷积核
卷积核是整个卷积过程的核心。 比较简单的卷积核或过滤器有Horizontalfilter、Verticalfilter、Sobel Filter 等。这些过滤器能够检测图像的水平边缘、垂直边缘、增强图像中心区域权重等。
1)垂直边缘检测。
这个过滤器是3×3矩阵(注,过滤器一般是奇数阶矩阵),其特点是有值的是第1列和第3列,第2列为0。经过这个过滤器作用后,就把原数据垂直边缘检测出来了,如图6-4所示。
2)水平边缘检测。
3×3矩阵,其特点是有值的是第1行和第3行,第2行为 0。经过这个过滤器作用后,就把原数据水平边缘检测出来了。
6.2.2 步幅
小窗口(实际上就是卷积核或过滤器)在左边窗口中每次移动的格数 (无论是自左向右移动,或自上向下移动)称为步幅(strides),在图像中就是跳过的像素个数。
6.2.3 填充
当输入图片与卷积核不匹配时或卷积核超过图片边界时,可以采用边界填充(Padding)的方法。即把图片尺寸进行扩展,扩展区域补零,如图6-10所示。当然也可不扩展。
一般选择Same方式,使用Same不会丢失信息 。
6.2.4 多通道上的卷积
3通道图片的卷积运算与单通道图片的卷积运算基本一致,对于3通道的RGB图片,其对应 的滤波器算子同样也是3通道的。例如一个图片是6×6×3,分别表示图片的 高度(Height)、宽度(Weight)和通道(Channel)。过程是将每个单通道 (R,G,B)与对应的filter进行卷积运算求和,然后再将3通道的和相加, 得到输出图片的一个像素值。
6.2.5 激活函数
卷积神经网络与标准的神经网络类似,为保证其非线性,也需要使用激活函数,即在卷积运算后,把输出值另加偏移量,输入到激活函数,然后作为下一层的输入,如图6-13所示。常用的激活函数有:nn.Sigmoid、nn.ReLU、nnLeakyReLU、nn.Tanh 等。
6.2.6 卷积函数
Conv2d(in_channels, out_channels, kernel_size, stride=1,padding=0, dilation=1, groups=1,bias=True, padding_mode=‘zeros’)
五个常用参数的含义如下:
- in_channels:输入的通道数目;
- out_channels:输出的通道数目;
- kernel_size:卷积核的大小;
- stride:卷积每次滑动的步长;
- padding:填充,设置在所有边界增加值为 0 的边距的大小。
卷积神将网络的计算公式:N=(W-F+2P)/S+1,其中N:输出大小,W:输入大小,F:卷积核大小,P:填充值的大小,S:步长大小
例子:
# batch norm 和leaky relu函数促进了健康的梯度流
class Discriminator(nn.Module):
def __init__(self, ngpu):
super(Discriminator, self).__init__()
self.ngpu = ngpu
self.main = nn.Sequential(
# input is (nc) x 64 x 64 128,3*64*64
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False), # 64-4+2/2+1=32
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf) x 32 x 32 64*32*32
nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False), # 32-4+2/2+1=16
nn.BatchNorm2d(ndf * 2),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*2) x 16 x 16 128*16*16
nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False), # 16-4+2/2+1=8
nn.BatchNorm2d(ndf * 4), # 256
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*4) x 8 x 8 256*8*8
nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False), # 8-4+2/2+1=4
nn.BatchNorm2d(ndf * 8),
nn.LeakyReLU(0.2, inplace=True),
# state size. (ndf*8) x 4 x 4 512*4*4
nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False), # 4-4/2+1=1
nn.Sigmoid() # 128,1*1024
)
def forward(self, input):
return self.main(input)
一些点:
- Conv1d:用于文本数据,只对宽度进行卷积,对高度不进行卷积,而Conv2d:用于图像数据,对宽度和高度都进行卷积
- Conv2d(输入通道数, 输出通道数, kernel_size(长和宽)),当卷积核为方形时,只写一个就可以,但是当卷积核不是方形时,长和宽都要写,如下nn.Conv2d(H,W,....)
6.2.7 转置卷积
转置卷积(Transposed Convolution)在一些文献中也称为反卷积 (Deconvolution)或部分跨越卷积(Fractionally-Strided Convolution)。转置卷积在生成式对抗网络(GAN)中使用很普遍。
pytorch中反卷积的函数为:
class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0,output_padding=0,groups=1,bias=True, dilation=1)
# 一般为以下
nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride, padding)
参数的含义如下:
- in_channels(int) – 输入信号的通道数
- out_channels(int) – 卷积产生的通道数
- kernel_size(int or tuple) - 卷积核的大小
- stride(int or tuple,optional) - 卷积步长,即要将输入扩大的倍数。
- padding(int or tuple, optional) - 输入的每一条边补充0的层数,高宽都增加2*padding
- output_padding(int or tuple, optional) - 输出边补充0的层数,高宽都增加padding
- groups(int, optional) – 从输入通道到输出通道的阻塞连接数
- bias(bool, optional) - 如果bias=True,添加偏置
- dilation(int or tuple, optional) – 卷积核元素之间的间距
而对于输入输出的计算,首先参数out_channels指定输出的通道数,即一定是output_size*output_size*out_channels,故主要计算输出的output_size,公式如下:
一般来说用以下公式:
例子:
# Generator Code
# 生成器 反卷积
class Generator(nn.Module):
def __init__(self, ngpu):
super(Generator, self).__init__()
self.ngpu = ngpu # Number of GPUs available. Use 0 for CPU mode. 可用的 GPUs 数量。
self.main = nn.Sequential(
# input is Z, going into a convolution 100维
nn.ConvTranspose2d( nz, ngf * 8, 4, 1, 0, bias=False), #(1-1)*1+4-2*0=4
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# state size. (ngf*8) x 4 x 4 4*4*512
'''
class torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0,
output_padding=0, groups=1, bias=True, dilation=1)
'''
nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False), # (4-1)*2-2*1+4=8
nn.BatchNorm2d(ngf * 4),
nn.ReLU(True),
# state size. (ngf*4) x 8 x 8 8*8*256
nn.ConvTranspose2d( ngf * 4, ngf * 2, 4, 2, 1, bias=False), # (8-1)*2-2*1+4=16
nn.BatchNorm2d(ngf * 2),
nn.ReLU(True),
# state size. (ngf*2) x 16 x 16 16*16*128
nn.ConvTranspose2d( ngf * 2, ngf, 4, 2, 1, bias=False), # (16-1)*2-2*1+4=32
nn.BatchNorm2d(ngf),
nn.ReLU(True),
# state size. (ngf) x 32 x 32 32*32*64
nn.ConvTranspose2d( ngf, nc, 4, 2, 1, bias=False), # (32-1)*2-2*1+4=64
nn.Tanh()
# state size. (nc) x 64 x 64 64*64*3
)
def forward(self, input):
return self.main(input)
6.3 池化层
常用的池化 方式通常有3种。
- 最大池化(Max Pooling):选择Pooling窗口中的最大值作为采样值。
- 均值池化(Mean Pooling):将Pooling窗口中的所有值相加取平均, 以平均值作为采样值。
- 全局最大(或均值)池化:与平常最大或最小池化相对而言,全局池化是对整个特征图的池化而不是在移动窗口范围内的池化。
6.3.1 局部池化
我们通常使用的最大或平均池化,是在特征图(Feature Map)上以窗口的形式进行滑动(类似卷积的窗口滑动),操作为取窗口内的平均值作为结果,经过操作后,特征图降采样,减少了过拟合现象。其中在移动窗口内的池化被称为局部池化。
最大池化常使用nn.MaxPool2d,平均池化使用 nn.AvgPool2d。
6.3.2 全局池化
以全局平均池化为例:全局平均池化(Global Average Pooling,GAP),不以窗口的形式取均值,而是以特征图为单位进行均值化,即一个特征图输出一个值。
6.4 现代经典网络
6.4.1 LeNet-5模型
(1)模型架构 LeNet-5模型结构为输入层-卷积层-池化层-卷积层-池化层-全连接层-全连接层-输出,为串联模式,如图6-19所示。
(2)模型特点 ·每个卷积层包含3个部分:卷积、池化和非线性激活函数。
- 使用卷积提取空间特征。
- 采用降采样(Subsample)的平均池化层(Average Pooling)。
- 使用双曲正切(Tanh)的激活函数。
- 最后用MLP作为分类器。
6.4.2 AlexNet模型
(1)模型架构
AlexNet为8层深度网络,其中5层卷积层和3层全连接层,不计LRN层和池化层,如图6-20所示。
(2)模型特点
- 由5层卷积和3层全连接组成,输入图像为3通道224×224大小,网络规模远大于LeNet。
- 使用ReLU激活函数。
- 使用Dropout,可以作为正则项防止过拟合,提升模型鲁棒性。
- 具备一些很好的训练技巧,包括数据增广、学习率策略、Weight Decay等。
6.4.3 VGG模型
(1)模型结构
(2)模型特点
- 更深的网络结构:网络层数由AlexNet的8层增至16和19层,更深的网络意味着更强大的网络能力,也意味着需要更强大的计算力,不过后来硬件发展也很快,显卡运算力也在快速增长,以此助推深度学习的快速发展。
- 使用较小的3×3的卷积核:模型中使用3×3的卷积核,因为两个3×3的感受野相当于一个5×5,同时参数量更少,之后的网络都基本遵循这个范式。
6.4.4 GoogleNet模型
VGG是增加网络的深度,但深度达到一个程度时,可能就成为瓶颈。 GoogLeNet则从另一个维度来增加网络能力,每单元有许多层并行计算,让网络更宽了,基本单元如图6-22所示。
(1)模型结构
网络总体结构如图6-23所示,包含多个图6-22所示的Inception模块,为便于训练添加了两个辅助分类分支补充梯度,如图6-23所示。
(2)模型特点
- 引入Inception结构,这是一种网中网(Network In Network)的结 构。 通过网络的水平排布,可以用较浅的网络得到较好的模型能力,并进行多特征融合,同时更容易训练。另外,为了减少计算量,使用了1×1卷积来 先对特征通道进行降维。堆叠Inception模块就叫作Inception网络,而 GoogLeNet就是一个精心设计的性能良好的Inception网络(Inception v1)的实例,即GoogLeNet是Inception v1网络的一种。
- 采用全局平均池化层。 将后面的全连接层全部替换为简单的全局平均池化,在最后参数会变得 更少。而在AlexNet中最后3层的全连接层参数差不多占总参数的90%,使用 大网络在宽度和深度上允许GoogleNet移除全连接层,但并不会影响到结果 的精度,在ImageNet中实现93.3%的精度,而且要比VGG还快。不过,网络 太深无法很好训练的问题还是没有得到解决,直到ResNet提出了Residual Connection。
6.4.5 ResNet模型
(1)模型结构
其完整网络结构如图6-25所示:通过引入残差,Identity恒等映射,相当于一个梯度高速通道,可以更容易地训练避免梯度消失的问题。
(2)模型特点
- 层数非常深,已经超过百层。
- 引入残差单元来解决退化问题。