本文源于一道CNN作业中的题目。这道题涉及基本的CNN网络搭建,MNIST数据集上的分类结果,BatchNormalization的影响,Dropout的影响,volumekernel大小的影响,数据集的大小,不同部分的影响数据集,随机数种子的影响,不同激活单元的影响,可以让人对CNN有更全面的了解,所以想做。于是有了这篇文章。工具开源深度学习库:PyTorch数据集:MNIST实现需要初步建立基础BASE网络。在Pytorch中,有如下代码:classNet(nn.Module):def__init__(self):super(Net,self).__init__()self.conv1=nn.Conv2d(1,20,kernel_size=(5,5),stride=(1,1),padding=0)self.conv2=nn.Conv2d(20,50,kernel_size=(5,5),stride=(1,1),padding=0)self.fc1=nn.Linear(4*4*50,500)self.fc2=nn.Linear(500,10)defforward(self,x):x=F.max_pool2d(self.conv1(x),2)x=F.max_pool2d(self.conv2(x),2)x=x.view(-1,4*4*50)x=F.relu(self.fc1(x))x=self.fc2(x)returnF.log_softmax(x)这部分代码见base.py。问题A:预处理需要按照规则读取MNIST数据集,转换成适合处理的格式。这里读取的代码遵循BigDLPythonSupport的读取方法。不用说了,按照MNIST主页上的数据格式,可以快速阅读。关键块有读取32位的函数:def_read32(bytestream):dt=numpy.dtype(numpy.uint32).newbyteorder('>')#Bigendian模式读取,***bytefirst(MSBfirst)returnnumpy.frombuffer(bytestream.read(4),dtype=dt)[0]读出来后是一个(N,1,28,28)的张量,每个像素都是0-255的值,先做归一化,除所有的值乘以255,得到一个0-1的值,然后Normalize,训练集和测试集的均值方差已知,直接做就可以了。由于训练集和测试集的均值和方差是基于归一化的数据,一开始没有做归一化,所以forwardoutput和grad离谱,后来才发现这里有问题。这部分代码参见preprocessing.py。问题B:BASE模型将随机种子设为0,在前10000个训练样本上学习参数,最后看20个epoch后测试集的错误率。***结果为:Testset:Averageloss:0.0014,Accuracy:9732/10000(97.3%)可见BASE模型的准确率并没有那么高。问题C:BatchNormalizationv.sBASE在前三个block的卷积层之后增加了一个BatchNormalization层,简单修改网络结构如下:classNet(nn.Module):def__init__(self):super(Net,self)。__init__()self.conv1=nn.Conv2d(1,20,kernel_size=(5,5),stride=(1,1),padding=0)self.conv2=nn.Conv2d(20,50,kernel_size=(5,5),stride=(1,1),padding=0)self.fc1=nn.Linear(4*4*50,500)self.fc2=nn.Linear(500,10)self.bn1=nn.BatchNorm2d(20)self.bn2=nn.BatchNorm2d(50)self.bn3=nn.BatchNorm1d(500)defforward(self,x):x=self.conv1(x)x=F.max_pool2d(self.bn1(x),2)x=self.conv2(x)x=F.max_pool2d(self.bn2(x),2)x=x.view(-1,4*4*50)x=self.fc1(x)x=F.relu(self.bn3(x))x=self.fc2(x)returnF.log_softmax(x)相同参数运行,加入BN后结果为:Testset:Averageloss:0.0009,Accuracy:9817/10000(98.2%)可见效果明显提升。有关批量归一化的更多信息,请参阅[2]、[5]。问题D:DropoutLayer在最后一层fc2层后添加Dropout(p=0.5)后,BASE和BN上的结果为:BASE:Testset:Averageloss:0.0011,Accuracy:9769/10000(97.7%)BN:Testset:Averageloss:0.0014,Accuracy:9789/10000(97.9%)观察到dropout对BASE模型有一定的提升,但是对BN模型的影响不明显反而降低。原因可能是BN模型本身包含正则化的作用,加一层Dropout看似没必要但可能会影响结果。问题E:SK模型SK模型:堆叠两个3x3conv。层来替换5x5conv。layer经过这样的改动,构建的SK模型如下:classNet(nn.Module):def__init__(self):super(Net,self)。__init__()self.conv1_1=nn.Conv2d(1,20,kernel_size=(3,3),stride=(1,1),padding=0)self.conv1_2=nn.Conv2d(20,20,kernel_size=(3,3),stride=(1,1),padding=0)self.conv2=nn.Conv2d(20,50,kernel_size=(3,3),stride=(1,1),padding=0)self.fc1=nn.Linear(5*5*50,500)self.fc2=nn.Linear(500,10)self.bn1_1=nn.BatchNorm2d(20)self.bn1_2=nn.BatchNorm2d(20)self.bn2=nn.BatchNorm2d(50)self.bn3=nn.BatchNorm1d(500)self.drop=nn.Dropout(p=0.5)defforward(self,x):x=F.relu(self.bn1_1(self.conv1_1(x)))x=F.relu(self.bn1_2(self.conv1_2(x)))x=F.max_pool2d(x,2)x=self.conv2(x)x=F.max_pool2d(self.bn2(x),2)x=x.view(-1,5*5*50)x=self.fc1(x)x=F.relu(self.bn3(x))x=self.fc2(x)returnF.log_softmax(x)20个epochs后,结果如下,SK:Testset:Averageloss:0.0008,Accuracy:9848/10000(98.5%)测试集的准确率略有提升。这里用两个3x3的卷积核来代替大的5x5卷积核,参数个数由5x5=25变为2x3x3=18。实践表明,这使得计算速度更快,小卷积层之间的ReLU也有帮助。VGG中使用了这种方法。F题:ChangeNumberofchannels将featuremap的大小乘以一个倍数,然后通过shell程序执行得到如下结果:SK0.2:97.7%SK0.5:98.2%SK1:98.5%SK1。5:98.6%SK2:98.5%(max98.7%)当featuremaps为4、10、30、40时,最终准确率基本提升。这一定程度上说明在过拟合之前,增加featuremap的数量相当于提取了更多的特征,提取特征数量的增加有助于提高准确率。这部分代码见SK_s.py和runSK.sh。问题G:Usedifferenttrainingsetsizes也是跑脚本,添加参数parser.add_argument('--usedatasize',type=int,default=60000,metavar='SZ',help='usehowmanytrainingdatatotrainnetwork')表示大小使用的数据,从前到后取usebatchsize数据。这部分程序见SK_s.py和runTrainingSize.sh。运行结果如下:500:84.2%1000:92.0%2000:94.3%5000:95.5%10000:96.6%20000:98.4%60000:99.1%可以明显看出,数据越多,准确率越大结果。数据太少不能准确反映数据的整体分布,容易过拟合,数据太大效果不明显。但是,很多时候我们还是觉得数据太少了,获取的数据多了。也有一定的困难。问题H:使用不同的训练集使用脚本完成,这部分程序见SK_0.2.py和diffTrainingSets.sh。运行结果如下:0-10000:98.0%10000-20000:97.8%20000-30000:97.8%30000-40000:97.4%40000-50000:97.5%50000-60000:97.7%可以看出不同的训练samplesetsareused训练好的网络有一定的差异,虽然不是很大,但毕竟表现出不稳定的结果。问题一:RandomSeed的效果是使用runSeed.sh脚本完成的,使用全部60000个训练集。运行结果如下:Seed0:98.9%Seed1:99.0%Seed12:99.1%Seed123:99.0%Seed1234:99.1%Seed12345:99.0%Seed123456:98.9%其实在使用整个训练集的时候,seed随机数生成器的设置对***结果影响不大。问题J:ReLU还是Sigmoid?将所有ReLU替换为Sigmoid后,使用全部60000个训练集进行训练。对比结果如下:ReLUSK_0.2:99.0%igmoidSK_0.2:98.6%可以看出训练CNN时,使用ReLU激活单元优于Sigmoid激活单元。原因可能在于两种机制之间的差异。当sigmoid神经??元的输入值较大或较小时,输出值会接近于0或1,这使得很多地方的梯度几乎为0,权重几乎不更新。ReLU虽然增加了计算负担,但可以显着加快收敛过程,并且不存在梯度饱和问题。
