上代码;`
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(3,8,3,padding=1)
self.conv2 = nn.Conv2d(8,64,3,padding=1)
self.pool1 = nn.MaxPool2d(2,2)
self.bn1 = nn.BatchNorm2d(64)
self.relu1 = nn.ReLU()
self.conv3 = nn.Conv2d(64,128,3,padding=1)
self.conv4 = nn.Conv2d(128,128,1)
self.pool2 = nn.MaxPool2d(2,2)
self.bn2 = nn.BatchNorm2d(128)
self.relu2 = nn.ReLU()
self.conv5 = nn.Conv2d(128,512, 3,padding=1)
self.conv6 = nn.Conv2d(512,512, 1)
self.pool3 = nn.MaxPool2d(2, 2)
self.bn3 = nn.BatchNorm2d(512)
self.relu3 = nn.ReLU()
self.fc14 = nn.Linear(512*4*4,1024)
self.drop1 = nn.Dropout2d()
self.fc15 = nn.Linear(1024,512)
self.drop2 = nn.Dropout2d()
self.fc16 = nn.Linear(512,10)
`
采用卷积神经网络和全连接网络共同处理。进过多次实验和测试后发现该网络在30个循环后对测试集的正确率从能达到81%,训练机的正确率为99%。对其中代码简要说明:
self.conv1 = nn.Conv2d(3,8,3,padding=1)
该语句定义一次卷积运算其中第一个3表示输入为3通道对应到本次测试为图片的RGB三个通道,数字8的意思为8个卷积核,第二个3表示卷积核的大小为3*3,padding=1表示在图片的周围增加一层像素值用来保存图片的边缘信息。
可参考pytorch官网:
class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
in_channels(int) – 输入信号的通道
out_channels(int) – 卷积产生的通道
kerner_size(int or tuple) - 卷积核的尺寸
stride(int or tuple, optional) - 卷积步长
padding(int or tuple, optional) - 输入的每一条边补充0的层数
dilation(int or tuple, optional) – 卷积核元素之间的间距
groups(int, optional) – 从输入通道到输出通道的阻塞连接数
bias(bool, optional) - 如果bias=True,添加偏置
self.pool1 = nn.MaxPool2d(2,2)
该语句是表示神经网络中的池化MaxPool2d表示二维池化其中第一个2表示池化窗口的大小为2*2,第二个2表示窗口移动的步长。
官网给出的信息为:
class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
kernel_size(int or tuple) - max pooling的窗口大小
stride(int or tuple, optional) - max pooling的窗口移动的步长。默认值是kernel_size
padding(int or tuple, optional) - 输入的每一条边补充0的层数
dilation(int or tuple, optional) – 一个控制窗口中元素步幅的参数
return_indices - 如果等于True,会返回输出最大值的序号,对于上采样操作会有帮助
ceil_mode - 如果等于True,计算输出信号大小的时候,会使用向上取整,代替默认的向下取整的操作
self.bn1 = nn.BatchNorm2d(64)
函数介绍:torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
num_features: 来自期望输入的特征数,该期望输入的大小为’batch_size x num_features [x width]’
eps: 为保证数值稳定性(分母不能趋近或取0),给分母加上的值。默认为1e-5。
momentum: 动态均值和动态方差所使用的动量。默认为0.1。
affine: 布尔值,当设为true,给该层添加可学习的仿射变换参数。
track_running_stats:布尔值,当设为true,记录训练过程中的均值和方差;
在进行训练之前,一般要对数据做归一化,使其分布一致,但是在深度神经网络训练过程中,通常以送入网络的每一个batch训练,这样每个batch具有不同的分布;此外,为了解决internal covarivate shift问题,这个问题定义是随着batch normalizaiton这篇论文提出的,在训练过程中,数据分布会发生变化,对下一层网络的学习带来困难。
所以batch normalization就是强行将数据拉回到均值为0,方差为1的正太分布上,这样不仅数据分布一致,而且避免发生梯度消失。
self.relu1 = nn.ReLU()
ReLU激活函数
激活函数(Activation Function),就是在人工神经网络的神经元上运行的函数,负责将神经元的输入映射到输出端。引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。
如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机(Perceptron)。如果使用的话,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
Relu激活函数(The Rectified Linear Unit)修正线性单元,用于隐层神经元输出。
self.fc14 = nn.Linear(512*4*4,1024)
该语句表示全连层输入数据个数为一维数据512 * 4 * 4,输出个数为1024.
self.drop1 = nn.Dropout2d()
Dropout:删除掉隐藏层随机选取的一半神经元,然后在这个更改的神经元网络上正向和反向更新,然后再恢复之前删除过的神经元,重新选取一般神经元删除,正向反向,更新w,b.重复此过程,最后学习出来的神经网络中的每个神经元都是在一半神经元的基础上学习的,当所有神经元被恢复后,为了补偿,我们把隐藏层的所有权重减半。
为什么Dropout可以减少overfitting?
每次扔掉了一般隐藏层的神经元,相当于在不同的神经网络训练了,减少了神经元的依赖性,迫使神经网络去学习更加健硕的特征。
执行函数:
def forward(self,x):
x = self.conv1(x)
x = self.conv2(x)
x = self.pool1(x)
x = self.bn1(x)
x = self.relu1(x)
x = self.conv3(x)
x = self.conv4(x)
x = self.pool2(x)
x = self.bn2(x)
x = self.relu1(x)
x = self.conv5(x)
x = self.bn3(x)
x = self.conv6(x)
x = self.pool3(x)
x = self.bn4(x)
x = self.relu1(x)
x = x.view(-1,256*5*5)
x = F.relu(self.fc14(x))
x = self.drop1(x)
x = F.relu(self.fc15(x))
x = self.drop2(x)
x = self.fc16(x)
return x