学习内容
本文对caffe 相关配置网络结构的各层代码进行了详细的介绍,并对solver超参数配置详解。
开发环境
ubantu 16.04、VMwave
网络结构相关知识
一般来说,对于卷积神经网络,涉及到神经元、卷积、池化、激活函数等相关概念。这里里简单的进行说明介绍。
神经元: 类似于大脑的神经网络,各个神经元之间连接方式组成不同的网络结构。如下图所示。
在日常生活中,当我们要思考或者回想一些之前记忆过的内容时,脑子里会出现与之相关的信息,这些在脑子里预留的和该事件有关的信息可以说对于的是在神经网络中的权重(weights)和偏置(bias)。这两个是神经网络中的重要参数。
由抽象的概念对应出一个数学关系可以表示为最简单的线型关系,
这里a代表输入,w代表权重值,b代表偏置值。而对输入和权重值相乘再相加的过程表示为卷积。在上述的公式中的求和后得到的z是传递到下个神经元的响应。一般在神经网络中经过卷积层后要进行池化操作,这样的主要目的是为了缩小数据的尺度,加快训练的速度,防止过拟合。传送到下一个神经元后,神经元经过激活函数判断是否是有效输出。常用的激活函数如下所示:
综上所述,以一个实际例子进行说明,在我们进行回想事物或者进行记忆时,要在大脑中使用相关神经元进行建立记忆的网络,并在反复记忆后在脑子里留下相关的记忆信息。对应卷积神经网络,即为:
- 初始化网络结构
- 初始化weights和biases参数同时在神经网络训练中和实际记忆训练中,我们总是希望自己的准确度越高越好,这里在神经网络中实际上输出的准确度和标准情况的偏差值称为loss。所以在实际记忆中和神经网络训练中,我们总是想保持loss尽可能小,所以要选用特殊的记忆结构(在神经网络中也就是对应选取特定函数,选取最优的weights和biases参数),使得得到的参数尽可以能满足自己的高准确度的期望。
网络配置详解
Caffe层的定义由2部分组成:层属性与层参数。在一个神经网络中首先输入整个网络的是输入层,所以首先介绍关于输入层的相关配置信息。
输入层
对于一个网络的输入层来说,输入数据类型,输入的相关数据信息,以及相关输入的处理的配置信息。这里举出一个例子进行简单参考(主要参考example里面的cifar10的例子)。并对网络的输入层的相关参数进行说明。
layer { name: "cifar"#层名字 type: "Data"#数据类型 ##一般用bottom表示输入,top表示输出,多个top表示有多个输出 top: "data" top: "label" ##训练网络中分为训练阶段和自测试阶段,如果没有include则表示该层即在测试中,又在训练中 include { phase: TRAIN } ##用一个配置文件来进行均值操作 transform_param { mean_file: "examples/cifar10/mean.binaryproto" transform_param { ##归一化参数 scale: 0.00390625 ##1表示开启镜像,0表示关闭镜像 (也可以用ture和false表示) mirror: 1 ##剪切一个227×227的图块,在训练阶段随机裁剪,在测试阶段从中间裁剪 crop_size:227 } } data_param { source: "examples/cifar10/cifar10_train_lmdb" #数据库来源 batch_size: 100 #每批次处理的个数 backend: LMDB #选用数据的名称 } } ##HDF5的数据源 layer { name: "data" type: "HDF5Data" top: "data" top: "label" include { phase: TRAIN } hdf5_data_param { source: "examples/hdf5_classification/data/train.txt" batch_size: 10 } }
在网络层配置中,首先命名层级名字,数据类型,然后bottom表示输入,top表示输出。使用include{}进行指向该层存在于训练中还是测试中,使用transform_param {}对输入层的数据进行处理进行操作配置,包括输入数据镜像功能,随机裁剪,均值处理,归一化等操作。最后在data_param {}中对数据的来源,输入的批次大小以及选取的数据名称进行配置。
卷积层
卷积层例子如下:
layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" #设置学习率的系数 #最终的学习率是该系数乘solver.prototext中配置文件的学习率参数, #比如bias的学习率在solver.prototext中为0.4,这里的学习率系数为2, #所以最后的bias学习率为0.8。 ##如果lr_mult有两个,则第一个表示权值的学习率,第二个表示偏置值的学习率, ##一般偏置项的学习率是权值学习率的两倍。 param { lr_mult: 1 } param { lr_mult: 2 } #卷积相关参数 convolution_param { num_output: 32 #卷积核(filter)的个数 pad: 2 #扩充边缘,默认为0,不填充,这里为2是填充两圈 kernel_size: 5 #卷积核的大小5X5,深度由上一层决定 stride: 1 #卷积核的步长,默认为1 weight_filler { type: "gaussian" #权值初始化,默认为constant,值为全0,很多时候可以用xavier,或者gaussian进行初始化 #type: "xavier" } bias_filler { type: "constant" #偏置初始化,默认为constant,值为全0 } } }
在卷积层需要设置的是学习率的相关参数,使用param { lr_mult: 1 }
进行配置权值和偏置值的相关学习率系数。 convolution_param {}进行配置卷积的相关参数,包括是否填充、卷积核的大小,卷积的步长等,并加载权重值和偏置值的初始化设置。在经过一层卷积后数据的输入输出会发生如下变化:假设输入数据为n X channel X width X height,设经过卷积层后的conv_width 和 conv_height 为(width + 2 X pad - kernel_size)/stride +1;对于输出的channel为num_output。
池化层
池化层例子如下:
layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { #选择池化的模式:MAX 常用的是MAX AVE;(最大池化、平均池化) pool: MAX #池化的核大小 kernel_size: 3 #池化步长大小 默认为1,可以设置为2,也即不重叠 stride: 2 } }
经过池化层后的数据变化和卷积层类似假设输入数据为n X channel X width X height
,设经过池化层后的conv_width
和conv_height
为(width + 2 X pad - kernel_size)/stride +1
;对于输出的channel为上一层的输出的channel数。因为池化大多数情况是用于进行减小数据尺度的,所以在池化的步长通常设置为2,或者别的大于1的数值,使得数据尺度变小。
激活函数层
激活函数层,对输入的数据进行激活操作。在每一个卷积池化层后连接一层激活函数。例子和讲解如下:
#该操作是逐元素进行运算的,在运算过程中,没有改变数据大小的,输入和输出的大小是相等的 #常用的relu激活函数,因为收敛快,效果和其他相似。 layer { name: "relu1" type: "ReLU" bottom: "pool1" top: "pool1" }
全连接层
全连接层的输出是一个简单的向量,参数和卷积层一样,或者换句话说全连接是特殊的卷积层。如果该层为整个网络的最后一层的话,num_output的值就是输出的分类的各个种类的预测值,例子如下:
layer { name: "ip1" type: "InnerProduct" bottom: "pool3" top: "ip1" param { lr_mult: 1 } param { lr_mult: 2 } inner_product_param { num_output: 64 weight_filler { type: "gaussian" std: 0.1 } bias_filler { type: "constant" } } }
在输出测试时,可以进行检测输入的准确率。
layer { name: "accuracy" type: "Accuracy" bottom: "ip2" bottom: "label" top: "accuracy" include { phase: TEST } }