LeNet网络结构
1 LeNet全貌
LeNet-5是一个总共5层的卷积神经网络(一般输入层不计,两个卷积层 + 两个全连接层 + 输出层)。
2 超参数与计算量(FLOPs)
2.1 卷积层
对于卷积层,它的超参数就是对应kernel的值和偏置项。对于c1卷积层,输入为32x32x1,这里的1指的是输入的channel,记为C_in,输出为28x28x6,6是输出的channnel。kernel 为 5x5xC_in xc_out = 5x5x1x6。所以卷积的超参数量为:
超参数:
Para = (k_m * k_n * C_in + 1)* C_out = (5 * 5 *1+1)* 6 = 156
FLOPs(Floating Point of operations) 就是执行卷积操作的过程中的浮点数操作,非严格来说,就是乘法操作的次数。如果单看前向传播,对于输出的特征图中的每一个值,都对应了 k_m ∗ k_n ∗ C_i n 次乘法操作,所以总的 FLOPs 如下。其中 m,n是输出的特征图的 size。
FLOPs=k_m∗k_n∗C_in∗m∗n∗C_out =5∗5∗1∗28∗28∗6=117600
2.2 池化层
池化就是下采样(子抽样)过程,对图像进行子抽样,可以减少数据处理量同时保留有用信息。这里采用的是最大池化。对于最大池化操作来说,没有超参数。
2.3 全连接层
C5这层卷积层比较特殊,因为S4输出的featuremap的size为 5 ∗ 5, 正好和卷积的kernel size相等,因此卷积结果的size 1 ∗ 1, 所以这里实质上是一个全连接层。因为 kernal size 的 channel 是120, 因此输出为 120 ∗ 1 ∗ 1。
超参数:
(5∗5∗16+1)∗120=48120
FLOPs:
120∗1∗1*5∗5∗16=48000
3 总结
LeNet-5 是一个5层卷积神经网络,总共有约6万(60790)个超参数。随着网络越来越深,图像的高度和宽度在缩小,与此同时,图像的 channel 数量一直在增加。
4 LeNet 网络实现mnist
4.1 导入数据库
import argparse:使得我们能够手动输入命令行参数,就是让风格变得和Linux命令行差不多
argparse是python的一个包,用来解析输入的参数。
4.2 判断是否使用GPU
4.3 搭建网络
首先进行网络初始化,输入图像尺寸28*28*1,通过6个5*5的卷积核,步长stride=1进行卷积,并进行padding填充,保证输入输出尺寸相同。然后通过2*2的核,步长为2进行池化,得到14*14*6输出。
进入第二层卷积层,输入通道6(保证输入输出通道数相同),通过16个5*5的卷积核,得到输出为10*10*16,经过池化层。得到5*5*16的输出
然后进入两层全连接层,最后得到输出层。
定义全向传播过程,输入为x。
python mnist.py --outf model
(意思是将训练的模型保存到model文件夹下,当然,你也可以不加参数,那样的话代码最后一行torch.save()就需要注释掉了)
python mnist.py --net model/net_008.pth
(意思是加载之前训练好的网络模型,前提是训练使用的网络和测试使用的网络是同一个网络模型,保证权重参数矩阵相等)
设置超参数,定义数据预处理方式
定义损失函数loss function 和优化方式(采用SGD,误差梯度下降算法)
4.4 进入训练网络
首先,先对epoch里面所有的数据进行迭代,将输入和标签送人GPU中进行读取。然后将输入通入网络后,将输出经过交叉熵函数后,进行误差梯度下降,然后进行优化,最后将误差累计。然后没100个进行打印。
每跑完一轮epoch测试一下准确率,然后在测试集里面的数据迭代,然后经过网络输出,将得分最高的那个类取出来,将总共标签叠加,正确的预测叠加,然后求出正确率,最后输出。
5 实验结果
AlexNet 网络结构
它是一个8层的网络结构,5层卷积层和3层卷积层(这里不包括池化层和LRN(局部响应归一化))
注:局部响应归一化(LRN),对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。
conv1:
图像初始的输入是224*224*3(RGB图像),经过预处理之后变成了227*227*3。所以实际上conv1层的输入为227*227*3。
这个图像被11*11*3(3表示RGB三个通道)的卷积核进行卷积计算
(【input_size - kernel_size + 2*padding】/ stride + 1 = output_size)即227-11+0/4+1=55。所以输出特征图大小为55*55*96(用了96个卷积核分成两组,每组48个卷积核)——因为图中用双GPU进行计算,所以将数据分成了两组,所以深度48*2=96。这些像素层经过激活函数ReLU,生成激活像素层,尺寸保持不变
重叠pool池化层:经过池化运算,用3*3的卷积核,步长为2,进行池化。55-3+0/2+1=27。所以经过池化后的规格为27*27*96。
卷积层参数=卷积核大小x卷积核数量+偏置数量(卷积核的数量)=11x11x3x96+96=34848
conv2:
第二层输入数据为第一层输出的27x27x96的像素层,为便于后续处理,每幅像素层的左右两边和上下两边都要填充2个像素;27x27x96的像素数据分成27x27x48的两组像素数据,两组数据分别再两个不同的GPU中进行运算。每组像素数据被5548的卷积核进行卷积运算,共有256个5x5x48卷积核。卷积核沿原始图像的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。因此,卷积核在移动的过程中会生成(27-5+2x2)/1+1=27个像素。即会生成27x27x256个卷积后的像素层。
conv3:
第三层输入数据为第二层输出的2组13x13x128的像素层;为便于后续处理,每幅像素层的左右两边和上下两边都要填充1个像素;2组像素层数据都被送至2个不同的GPU中进行运算。每个GPU中都有192个卷积核,每个卷积核的尺寸是3x3x256。因此,每个GPU中的卷积核都能对2组13x13x128的像素层的所有数据进行卷积运算。
conv4:
第四层输入数据为第三层输出的2组13x13x192的像素层;为便于后续处理,每幅像素层的左右两边和上下两边都要填充1个像素;2组像素层数据都被送至2个不同的GPU中进行运算。每个GPU中都有192个卷积核,每个卷积核的尺寸是3x3x192。因此,每个GPU中的卷积核能对1组13x13x192的像素层的数据进行卷积运算。卷积核对每组数据的每次卷积都生成一个新的像素。卷积核沿像素层数据的x轴方向和y轴方向两个方向移动,移动的步长是1个像素。因此,运算后的卷积核的尺寸为(13-3+12)/1+1=13,2个GPU中共1313*384个卷积后的像素层
conv5:
FC6:
第六层输入数据的尺寸是6x6x256,采用6x6x256=9216尺寸的滤波器对第六层的输入数据进行卷积运算;每个6x6x256尺寸的滤波器对第六层的输入数据进行卷积运算生成一个运算结果,通过一个神经元输出这个运算结果;共有4096个6x6x256尺寸的滤波器对输入数据进行卷积运算,通过4096个神经元输出运算结果;
可以看到卷积神经网络的计算量其实主要集中在全连接层,这些层参数太多了,也最容易发生过拟合。DropOut通过训练时随机使得一部分结点失效,不贡献连接权重而减少过拟合风险。同时强迫这些神经元去学习互补的一些特征。因此最后通过dropout6运算后输出4096个本层的输出结果值。
FC7:
FC8:
AlexNet mnist
1 遇到的问题
实现结果
这里我在最开始找到代码的时候,他的产参数设置是:
最后的结果就是精度很低,只有71%左右。我觉得可能是epoch次数太多,导致过拟合,然后我又调整了epoch为5,精度更低,我发现不能一味的只调整epoch,还要顾及到学习率和batch_size的大小。batch size 太小会使训练速度很慢;太大会加快训练速度,但同时会导致内存占用过高,并有可能降低准确率。选择2的指数倍可能更好(因为计算机内存一般为 2 的指数倍,采用 2 进制编码)。所以32-256可能会更好,尤其是64和128。
但是结果仍然没有太好。我又重新更换了一个。
1 导入工具包
2 搭建网络
3 对图像进行旋转和变换
transforms.RandomHorizontalFlip(),依概率水平翻转
transforms.RandomGrayscale(),依概率转化为灰度图
transforms.ToTensor()转换为tensor格式,这个格式可以直接输入进神经网络了。
4 进入训练
然后,将训练的数据下载文件夹中,然后开始下载数据,划分为训练集和测试集。采用封装好的AlexNet网络,加载到GPU中。用交叉熵函数降低误差,另外用SGD实现随机梯度下降。学习率设为0.001,动量为0.9
这里是对待训练的数据trainloader进行迭代,然后分别将输入和标签分别加载到GPU中。将输入的数据通过我们之前搭好的网络进行训练,之后通过loss=criterion(outputs, labels)计算损失,这里调用了之前的交叉熵函数。optimizer.zero_grad()意思是把梯度置零,也就是把loss关于weight的导数变成0。我们使用backward函数之前必须进行梯度清零,不然在pytorch会将上次计算的梯度和本次计算的梯度累加。这样逻辑的好处是,当我们的硬件限制不能使用更大的bachsize时,使用多次计算较小的bachsize的梯度平均值来代替,更方便,坏处当然是每次都要清零梯度。然后optimizer.step()用于更新学习率。
5 进入测试阶段
这是输出结果,经过了20次epoch的训练,用于测试集的测试的精确度达到了97。