💡卷积神经网络由一个或多个卷积层和顶端的全连通层(对应经典的神经网络)组成,通过也包括关联权重和池化层(Pooling Layer)
-用于手写识别的一个比较简单的卷积神经网络,由卷积层(Conv2d)、池化层(MaxPool2d)和全连接层(Linear)叠加而成
import torch.nn as nn
import torch.nn.functional as F
device = torch.device("cuda:0" if torch.cuda.is_avaliable() else "cpu")
class CNNNet(nn.Module):
def __init__(self):
# super().__init__()表示可以调用父类nn.module的属性和方法,没有这句话只能调用方法
super(CNNNet, self).__init__()
# 卷积层 in_channels输入通道, out_channels输出通道,kernel_size卷积核大小,stride步长
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=3, stride=1)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.fc1 = nn.Linear(1296, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = self.pool1(F.relu(self.conv1(X)))
x = self.pool2(F.relu(self.conv2(x)))
# print(x.shape)
x = x.view(-1,36*6*6)
x = F.relu(self.fc2(F.relu(self.fc1(x))))
return x
net = CNNNet()
net = nec.to(device)
卷积层
卷积层是CNN是核心层,而卷积Convolution又是卷积层的核心。两个函数的运算就是卷积运算
卷积运算:用卷积核中每个元素分别乘以对应输入矩阵的对应元素
卷积核Kernel
卷积核又称权重过滤器,简称过滤器filter
- 类别:比较简单的卷积核有Horizontalfilter、Vecticalfilter、Sobel Filter等
- 作用:这些过滤器通常能够检测图像的水平边缘、垂直边缘、增强图像中心区域权重等
- 垂直边缘检测
- 水平边缘检测
- 效果图
- 垂直边缘检测
步幅Stride
小窗口(实际上就是卷积核或过滤器)在输入窗口中每次移动的格数(无论是自左自右移动,或自上自下移动)称为步幅(strides),在图像中就是跳过的像素个数
在小窗口移动过程中,其值始终是不变的,都是卷积核的值。也可以说,卷积核的值在整个过程中都是共享的,所以又把卷积核的值称为贡献变量。神经网络采用参数共享的方法大大降低了参数的数量
填充Padding
当输入图片与卷积核不匹配或者卷积核超过图片边界时,可以采用边界填充的方法。即把图片进行拓展,拓展区域补0。当然,也可以不拓展
根据是否拓展Padding又分为Same、Valid。采用Same方式时,对图片拓展为0。采用Valid时,不对图片进行拓展,即只对输入窗口与移动窗口(卷积核)重叠的部分进行计算。一般选用Same,Same一般不会丢失信息
设补0的圈数为p,输入数据的大小为n,过滤器的大小为f,步幅为s,则有
卷积后的大小位:
多通道上的卷积
之前讲述的都是二维卷积,放在图像上来说这就是单通道的图片是灰色,并没有考虑彩色的图片
但在实际应用中,输入数据一般是多通道的,如彩色图片若是RGB颜色模型的话就是三通道,即R、G、B通道,对于多通道的卷积,相应的卷积核(滤波器算子)也是多通道的。
例如下图,图片是6x6x3,分别表示高度(Height)、宽度(Width)和通道(Channel)。过程是将每个单通道(RGB)与相应的filter进行卷积运算,然后将三通道的和相加,得到输出图片的一个像素值
多维卷积和示意图
激活函数
CNN与标准的神经网络类似,为保证其非线性,也需要使用激活函数,即在卷积后,把输出值另加偏移值(bias),输入到激活函数,作为下一层的输入
常用的激活函数有:
nn.Sigmoid
nn.ReLU
nnLeakyReLU
nn.Tanh
卷积函数
卷积函数是构建神经网络的重要支架,通常Pytorch的卷积运算是通过nn.Conv2d
来完成的
- nn.Conv2d
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1,
groups=1, bias=True, padding_mode='zeros')
'''
param: in_channels(int):输入信号的通道
param: out_channels(int): 卷积产生的通道
param: kernel_size(int or tuple): 卷积核的尺寸
param: stride(int or tuple, optional): 卷积步长,可缺省
param: padding(int or tuple, optional): 输入的每一条边补充0的层数
param: dilation(int or tuple, optional): 卷积核元素之间的差距
param: groups(int, optional): 控制输入与输出的链接。group=1,输出是所有输入的卷积;group=2
此时相当于有并排的两个卷积层,每个卷积层计算输入通道的一半,并且产生的输出是输出通道的一半,随后将两个输出连接起来
param: bias(bool, optional): 如果bias=True,则添加偏置
'''
其中参数kernel_size、stride、padding、dilation也可以是一个int数据,此时卷积height和width值相同
也可以是tuple数组,tuple的第一维度表示height的值,第二维度表示width的值
- 输出形状
注意
必须是整数
转置卷积
转置卷积(Transposed Convolution)在一些文献里也叫反卷积(Deconvolution)或部分跨越卷积(Fractionally-Strided Convolution)
通过卷积的正向传播的图像一般越来越小,记为下采样(Downsampled)。卷积的方向传播实际上就是一种转置卷积,它是上采样(Up-Sampling)
那么,反向传播又会如何?首先从卷积的反向传播算法开始。假设损失函数为L,则反向传播时,对L关系求导,利用链式法则得到:
转置卷积在生成式对抗网络(GAN)中很常见
池化层
池化又称下采样,通过卷积层获取获取图像的特征后,理论上可以直接使用这些特征训练分类器(如softmax)。但是这样仍然会面积巨大的计算量挑战,造成过拟合现象。为了进一步降低网络训练参数以及模型的过拟合程度,就要对卷积层进行池化(Pooling)处理。
池化的三种方式
- 最大池化(Max Pooling):选择Pooling窗口中的最大值作为采样值
- 均值池化(Mean Pooling):选择Pooling窗口中的所有值相加取平均,以平均值作为采样值
- 全局最大(均值)池化:与平常最大或或最小池化相对而言,全局池化是对整个特征图的池化而不是在移动窗口内的池化
池化层在CNN中可以用来减小尺寸,提高运算速度以及减小噪音影响,让各特征更具健壮性。池化层比卷积层要简单,池化层没有卷积运算,只是在滤波器算子滑动区域内取最大值或平均值。而池化的作用则体现在降采样:保留显著特征、降低特征维度、增大感受野。深度网络越往后面越能捕捉到物体的语义信息,这种语义信息建立在比较大的感受野基础上
局部池化
- 定义:我们通常使用的最大或均值池化,是在特征图上(Feature Map)上以窗口的形式进行滑动(类似卷积的窗口滑动),操作为取窗口的最大或平均值作为结果,经过操作后,特征图降采样,减少了过拟合现象。其中在移动窗口内的池化称为局部池化
在pytorch中,最大池化常使用
nn.MaxPool2d
,平均池化使用nn.AvgPool2d
torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) ''' param kernel_size(int):池化窗口大小,取一个四维向量,一般是[height, width],如果两者相等,可以是一个数字,如kernel_size=3 param stride(int or tuple, optional): 窗口在每一个维度上滑动的步长,一般也是[stride_h,stride_w],如果两者相等,可以是一个数字,如stride=1 param padding(int or tuple, optional): 与卷积类似 param dilation(int or tuple, optional): 卷积对输入数据的空间间隔 param return_indices(bool, optional): 是否返回最大值对应的下标 param celi_mode(bool, optional): 使用一些方块代替层结构 '''
- 输入格式
#池化窗口为正方形,size=3x3,stride=2 m1 = nn.MaxPool2d(3, stride=2) #池化窗口为非正方形 m2 = nn.MaxPool2d((3,2), stride=(2,1)) input = torch.randn(20,16,50,32) output = m2(input) print(output.shape
全局池化
与局部池化相对的就是全局池化,全局池化也分为最大或平均池化。所谓的全局就是针对常用的平均池化而言,平均池化会有它的filter size,比如2x2,而全局池化就没有size,它针对的就是整个Feature Map
以全局平均池化(Global Average Pooling, GAP)为例阐释全局池化的原理
下图左边把四个特征图,先用一个全连接层展平为一个向量,然后通过一个全连接层输出为4个分类节点。GAP可以把这两步合二为一,四个特征图通过GAP层时,求出每张Feature Map所有像素的均值,输出一个数据值,这样4个特征图就会输出4个数据点,这些数据点就组成一个1*4的向量
GAP的优势在于:各个类别与Feature Map之间的联系更加直观(相比全连接层的黑箱),Feature Map被转化为分类概率也更加容易,因为再GAP中没有参数许愿调,所以避免了过拟合问题。GAP汇总了空间信息,因此对输入的空间转换鲁棒性更强。所以卷积网络中最后几个全连接层,大都用GAP替换
总结学习于:《Python深度学习:基于Pytorch》