您好,欢迎来到12图资源库!分享精神,快乐你我!我们只是素材的搬运工!!
  • 首 页
  • 当前位置:首页 > 开发 > WEB开发 >
    应用Pytorch停止CNN详细剖析
    时间:2017-08-16 12:41 来源:网络整理 作者:网络 浏览:收藏 挑错 推荐 打印

    【51CTO活动】8.26 带你深度了解清华大学、搜狗基于算法的IT运维实际与探求

    本文缘起于一次CNN作业中的一道题,这道题触及到了基本的CNN网络搭建,在MNIST数据集上的分类结果,Batch Normalization的影响,Dropout的影响,卷积核大小的影响,数据集大小的影响,不同部分数据集的影响,随机数种子的影响,以及不同激活单元的影响等,可以让人比较片面地对CNN有一个了解,所以想做一下,于是有了本文。

    工具

    开源深度学习库: PyTorch

    数据集: MNIST

    完成

    初始要求

    应用Pytorch停止CNN详细剖析

    首先树立基本的BASE网络,在Pytorch中有如下code:

    class Net(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) 

     

        def forward(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) 

            return F.log_softmax(x) 

    这部分代码见 base.py 。

    成绩A:预处置

    即要求将MNIST数据集按照规则读取并且tranform到适宜处置的格式。这里读取的代码沿用了BigDL Python Support的读取方式,无需细说,依据MNIST主页上的数据格式可以很快读出,关键block有读取32位比特的函数:

    def _read32(bytestream): 

        dt = numpy.dtype(numpy.uint32).newbyteorder('>')    # 大端形式读取,最高字节在前(MSB first

        return numpy.frombuffer(bytestream.read(4), dtype=dt)[0] 

    读出后是(N, 1, 28, 28)的tensor,每个像素是0-255的值,首先做一下归一化,将一切值除以255,失掉一个0-1的值,然后再Normalize,训练集和测试集的均值方差都已知,直接做即可。由于训练集和测试集的均值方差都是针对归一化后的数据来说的,所以刚末尾没做归一化,所以forward输入和grad很离谱,后来才发现是这里出了成绩。

    这部分代码见 preprocessing.py 。

    成绩B:BASE模型

    应用Pytorch停止CNN详细剖析

    将random seed设置为0,在前10000个训练样本上学习参数,最后看20个epochs之后的测试集错误率。最后结果为:

    Test set: Average loss: 0.0014, Accuracy: 9732/10000 (97.3%) 

    可以看到,BASE模型准确率并不是那么的高。

    成绩C:Batch Normalization v.s BASE

    应用Pytorch停止CNN详细剖析

    在前三个block的卷积层之后加上Batch Normalization层,复杂修正网络结构如下即可:

    class Net(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) 

     

        def forward(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) 

            return F.log_softmax(x) 

    异样的参数run一下,得出加了BN的结果为:

    Test set: Average loss: 0.0009, Accuracy: 9817/10000 (98.2%) 

    由此可见,有清楚的效果提升。

    关于Batch Normalization的更多材料参见[2],[5]。

    成绩D: Dropout Layer

    在最后一层即 fc2 层后加一个 Dropout(p=0.5) 后,在BASE和BN上的结果辨别为:

    BASE:Test set: Average loss: 0.0011, Accuracy: 9769/10000 (97.7%) 

    BN:  Test set: Average loss: 0.0014, Accuracy: 9789/10000 (97.9%) 

    察看得知,dropout可以对BASE模型起到一定提升作用,但是对BN模型却效果不清楚反而降低了。

    缘由能够在于,BN模型中本身即包含了正则化的效果,再加一层Dropout显得没有必要反而能够影响结果。

    成绩E:SK model

    SK model: Stacking two 3x3 conv. layers to replace 5x5 conv. layer

    如此一番改动后,搭建的SK模型如下:

    class Net(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) 

     

        def forward(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) 

            return F.log_softmax(x) 

    在20个epoch后,结果如下,

    SK: Test set: Average loss: 0.0008, Accuracy: 9848/10000 (98.5%) 

    测试集准确率失掉了少许的提高。

    这里应用2个3x3的卷积核来替代大的5x5卷积核,参数个数由5x5=25变为了2x3x3=18。实际表明,这样使得计算更快了,并且小的卷积层之间的ReLU也很有协助。

    VGG中就运用了这种办法。

    成绩F:Change Number of channels

    应用Pytorch停止CNN详细剖析

    经过将特征图大小乘上一个倍数,再经过shell顺序执行,失掉如下结果:

    SK0.2:  97.7% 

    SK0.5:  98.2% 

    SK1:    98.5% 

    SK1.5:  98.6% 

    SK2:    98.5%  (max 98.7%) 

    在特征图辨别为4,10, 30, 40时,最终的准确度基本是往上提升的。这在一定水平上阐明,在没有到达过拟合前,增大特征图的个数,即相当于提取了更多的特征,提取特征数的添加有助于精度的提高。

    这部分代码见 SK_s.py 和 runSK.sh 。

    成绩G:Use different training set sizes

    异样经过脚本运转,添加参数

    (责任编辑:admin)