Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

Caffe转ncnn后生成param与bin文件,param是网络结构文件bin是网络权重文件

下图是Alexnet的param文件。

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

第1行(版本信息)

7767517

代表此param文件的版本,magic number(幻数)。

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

第2行(layer与blob数量)

24 24

第一个数:层(layer)数量 ,即下面有24,每一行代表一层

第二个数:数据交换结构(blob)数量

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

根据Caffe官方文档,caffe大致可以分为三层结构:blob,layer,net。blob是caffe的标准数组结构,它提供了一个统一的内存接口。layer是caffe模型和计算的基本单元,net是一系列layers和其连接的集合。

2.1 Blob

Caffe内部数据存储和通讯都是通过Blob来完成,Blob提供统一的存储操作接口,可用来保存训练数据和模型参数等。

在数学上,一个blob就是一个N维的数组,按照c语言风格存储。Blob的维度可以表示为

(N, K, H, W),每个维度的意思分别是:

  • N:数据的个数,例如batch_size的大小
  • K:如果是图像,可以理解为通道数量;如果是网络中间,可以理解为feature map的数量
  • H:图像或者滤波器的高度
  • W:图像或者滤波器的宽度

以一张三通道480*640的图片为例子,如果转为Blob格式的数据,那么这个Blob可以表示为:(1*3*480*640)。

对于blob中的数据,我们关心的是values(值)和gradients(梯度),所以一个blob单元存储了两块数据——datadiff前者是我们在网络中传送的普通数据如图像像素数据之类的;后者指的是Back propagation运算得到的梯度数据。

2.2 Layer

Layer是Caffe模型的本质内容和执行计算的基本单元。Layer可以进行很多运算,如:

  • convolve(卷积)
  • pool(池化)
  • inner product(内积)
  • rectified-linear和sigmoid等非线性运算
  • 元素级的数据变换
  • normalize(归一化)
  • load data(数据加载)
  • softmax和hinge等losses(损失计算)

一个layer通过bottom(底部)连接层接收数据,通过top(顶部)连接层输出数据。

每一个layer都定义了3种重要的运算:setup(初始化设置)forward(前向传播), backward(反向传播)。

第3行(输入层data)

Input  data  0  1     data  0=227  1=227  2=3

前4个值的含义是固定的,分别是:

  • 层的类型:Input
  • 层的名称:data
  • 输入数据结构(blob)数量bottom:0
  • 输出数据结构(blob)数量top:1

Input层比较特殊,没有输入数据,所以bottom为0。输出是一个卷积层,所以top为1。

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

后面有三种类型的值(严格按照顺序排序): 

  • 第一种: 网络输入层名(一个层可能有多个输入,于是有多个网络输入层名) :没有
  • 第二种: 网络输出层名(一个层可能有多个输出,于是有多个网络输出层名) :data
  • 第三种(可能没有):特殊参数层,一是k=v的类型存在。二是k=len,v1,v2,v3….(数组类型)。此层在ncnn中是存放到paramDict结构中,不同类型层,各种参数意义不一样,需要具体分析。

0=227  1=227  2=3

特殊参数1(第一个k=v):w=227,输入图像的宽(w)

特殊参数2(第二个k=v):h=227,输入图像的高(h)

特殊参数3(第三个k=v):c=3,输入图像通道数(c)

Alexnet输入图像大小为227*227*3

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

第4行(卷积层conv1

Convolution conv1 1 1 data conv1      0=96 1=11 2=1 3=4 4=0 5=1 6=34848

前6个值

  • 层的类型:Convolution
  • 层的名称:conv1
  • 输入数据结构(blob)数量bottom:1
  • 输出数据结构(blob)数量top:1
  • 网络输入层名:data
  • 网络输出层名:conv1

0=96 1=11 2=1 3=4 4=0 5=1 6=34848

特殊参数1:0=96,num_output=96,卷积核的数量

特殊参数2:1=11, kernel_w=11,卷积核的宽

特殊参数3:2=1, dilation_w=1,卷积核rows方向的缩放系数,默认为1,一般不修改或配置

特殊参数4:3=4,stride_w=4,步长

特殊参数5:4=0,pad_w=0,填充大小

特殊参数6:5=1,bias_term=1,是否开启偏置项,默认为1, 开启

特殊参数7:6=34848,weight_data_size=34848,权重数据大小11 * 11 * 3 * 96=34848,如果加上bias的话,这一层的参数个数=(11 * 11 * 3 * 96)+96=34944

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

第5行(**层relu1

ReLU  relu1  1  1  conv1  conv1_relu1

5.1 什么是**函数

如下图,在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是**函数 Activation Function。

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

5.2 为什么要用**函数

如果不用**函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合。

如果使用的话,**函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。

例子:

这是一个单层的感知机,用它可以划出一条线, 把平面分割开

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

我们可以用多个感知机来进行组合, 获得更强的分类能力

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

但这样一个神经网络组合起来,输出的时候无论如何都还是一个线性方程,加上**函数的作用就是,输出变成了非线性函数,有了这样的非线性**函数以后, 神经网络的表达能力更加强大了。比如说,加上非线性**函数之后, 我们就有可能学习到这样的平滑分类平面。

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

5.3 有哪些**函数

在AlexNet之前,常见的**函数是用sigmoid和tanh两种,而AlexNet采用的是ReLU (Rectified Linear Unit,修正线性单元)。

5.3.1 sigmoid

公式:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

图像:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

能够把输入的连续实数值压缩到0和1之间

缺点:

  • sigmoid函数饱和使梯度消失,sigmoid神经元在值为0或1的时候接近饱和,梯度几乎为 0。因此在反向传播时,这个局部梯度会与整个代价函数关于该单元输出的梯度相乘,结果也会接近为0。这样,几乎就没有信号通过神经元传到权重再到数据了,因此这时梯度就对模型的更新没有任何贡献。
  • sigmoid函数不是关于原点中心对称因为在神经网络后面层中的神经元得到的数据将不是零中心的。这一情况将影响梯度下降的运作,因为如果输入神经元的数据总是正数(比如在Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析中每个元素都大于零),那么关于的梯度在反向传播的过程中,将会要么全部是正数,要么全部是负数(具体依整个表达式f而定)。这将会导致梯度下降权重更新时出现z字型的下降。然而,可以看到整个批量的数据的梯度被加起来后,对于权重的最终更新将会有不同的正负,这样就从一定程度上减轻了这个问题。因此,该问题相对于上面的神经元饱和问题来说只是个小麻烦,没有那么严重。
  • 计算量大,反向传播求误差梯度时,求导涉及除法。

5.3.2 tanh双切正切函数

公式:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

从下图看以看出tanh跟sigmoid很像,实际上,tanh 是sigmoid的变形:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

图像:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

tanh实数值压缩到[-1,1]之间,解决了sigmoid的输出不是零中心的问题,但仍然存在饱和问题。

5.3.3 ReLU

公式:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

图像:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

从图左可以看出,输入信号<0时,输出都是0,>0的情况下,输出等于输入。

优点:

  • 相较于sigmoid和tanh函数,ReLU对于SGD随机梯度下降的收敛有巨大的加速作用
  • sigmoid和tanh神经元含有指数运算等耗费计算资源的操作,而ReLU可以简单地通过对一个矩阵进行阈值计算得到

缺点:

在训练的时候,ReLU单元比较脆弱并且可能“死掉”。举例来说,当一个很大的梯度流过ReLU的神经元的时候,可能会导致梯度更新到一种特别的状态,在这种状态下神经元将无法被其他任何数据点再次**。如果这种情况发生,那么从此所以流过这个神经元的梯度将都变成0。也就是说,这个ReLU单元在训练中将不可逆转的死亡,因为这导致了数据多样化的丢失。例如,如果学习率设置得太高,可能会发现网络中40%的神经元都会死掉(在整个训练集中这些神经元都不会被**)。通过合理设置学习率,这种情况的发生概率会降低。

5.3.4 Leaky ReLU

公式:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

Leaky ReLU是给所有负值赋予一个非零斜率,比如0.01,Leaky ReLU非线性函数图像如下图所示。这样做目的是使负轴信息不会全部丢失,解决了ReLU神经元“死掉”的问题。更进一步的方法是PReLU,负值部分的斜率是根据数据来定的,而非预先定义的。

图像:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

5.3.5 Maxout

Maxout是对ReLU和leaky ReLU的一般化归纳,它的函数是:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

ReLU和Leaky ReLU都是这个公式的特殊情况(比如ReLU就是当Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析的时候)。这样Maxout神经元就拥有ReLU单元的所有优点(线性操作和不饱和),而没有它的缺点(死亡的ReLU单元)。然而和ReLU对比,它每个神经元的参数数量增加了一倍,这就导致整体参数的数量激增。

 

第6行(归一化层norm1

LRN  norm1  1  1  conv1_relu1  norm1          0=0 1=5 2=0.000100 3=0.750000

LRNLocal Response Normalization局部响应归一化,使用Sigmoid和tanh这类传统的饱和型**函数,我们往往都会做归一化处理,避免出现神经元对较大的输入值不敏感的情况(较大的输入值经过Sigmoid和tanh后的输出总是一个接近1的值)。

实际上ReLU这样的非饱和型函数不做归一化也是可以的,但作者还是给出了一个叫做LRN的方法,有利于增加泛化能力。(后期其他网络VGG、GOOGLENET、YOLO等放弃了这个LRN层,认为效果并不显著。)

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

0=0 1=5 2=0.000100 3=0.750000

特殊参数1:0=0,region_type

特殊参数2:1=5,local_size,对应上面公式中的n

特殊参数3:2=0.000100,alpha,对应上面公式中的特殊参数4:3=0.750000,beta,对应上面公式中的

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

第7行(池化层pool1

池化操作(Pooling)用于卷积操作之后,提取的是一小部分的代表性特征,减少冗余信息。其作用在于特征融合和降维降维改变的是图像的宽高,而不改变通道数。其实也是一种类似卷积的操作,只是池化层的所有参数都是超参数,都是不用学习得到的。

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

上面(来源见底层水印)这张图解释了最大池化(Max Pooling)的操作过程,核的尺寸为2×2,步长为2,最大池化的过程是将2×2尺寸内的所有像素值取最大值,作为输出通道的像素值。

除了最大池化外,还有平均池化(Average Pooling),也就是将取最大改为取平均。

AlexNet中的pooling操作是有重叠的。常见的pooling操作都没有重叠,即pooling单元的边长和步长是相同的,但AlexNet采用了3×3​的pooling单元,步长设置为2,相邻的两个pooling单元存在重叠。这样的设定使AlexNet的top-1和top-5错误率分别降低了0.4%和0.3%(和使用不重叠的池化相比)。

Pooling  pool1  1  1  norm1  pool1         0=0 1=3 2=2 3=0 4=0

特殊参数1:0=0,pooling_type池化类型,0=最大池化。1=平均池化,2=随机

特殊参数2:1=3,kernel_w=3,池化核大小

特殊参数3:2=2,stride_w=2,池化核步长

特殊参数4:3=0,pad_left=0,填充大小

特殊参数5:4=0,global_pooling=0,是否全区域池化(将整幅图像降采样为1*1

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

第8行(卷积层conv2

ConvolutionDepthWise conv2    1 1 pool1 conv2 0=256 1=5 2=1 3=1 4=2 5=1 6=307200 7=2

由于当时的GPU运算性能有限,alexnet将第2、4、5三个卷积层分为两部分,分配至两个GPU并行计算。所以这三层的层名都还是卷积层,但层的类型是ConvolutionDepthWise

前6个特殊参数跟普通的卷积层是一样的,不同的是最后多了一个参数7=2,表示分成两组。

特殊参数1:0=256,num_output=256,卷积核的数量

特殊参数2:1=5, kernel_w=5,卷积核的宽

特殊参数3:2=1, dilation_w=1,卷积核rows方向的缩放系数,默认为1,一般不修改或配置

特殊参数4:3=1,stride_w=1,步长

特殊参数5:4=2,pad_w=2,填充大小,Same padding,卷积后图像大小不变

特殊参数6:5=1,bias_term=1,是否开启偏置项,默认为1, 开启

特殊参数7:6=307200,weight_data_size=307200,权重数据大小5*5*48*128*2=307200

特殊参数8:7=2,group=2,分成两组在两个GPU上运行

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

第19行(全连接层fc6

卷积取的是局部特征,全连接就是把前面的局部特征重新通过权值矩阵组装成完整的图。因为用到了所有的局部特征,所以叫全连接。

输入数据是第5层的输出,尺寸为6×6×256=9216。本层共有4096个卷积核,每个卷积核的尺寸为6×6×256,由于卷积核的尺寸刚好与待处理特征图(输入)的尺寸相同,即卷积核中的每个系数只与特征图(输入)尺寸的一个像素值相乘,一一对应,因此,该层被称为全连接层。由于卷积核与特征图的尺寸相同,卷积运算后只有一个值,因此,卷积后的像素层尺寸为4096×1×1,即有4096个神经元。

InnerProduct  fc6  1  1  pool5  fc6      0=4096 1=1 2=37748736

特殊参数1:0=4096,num_output=4096,

特殊参数2:1=1,bias_term=1,是否开启偏置项,默认为1, 开启

特殊参数3:2=37748736,weight_data_size=37748736,(6*6*128*2)*4096=37748736

ncnn源码说明:

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

第21行(Dropout层drop6)

Dropout          drop6            1 1 fc6_relu6 fc6_drop6

Dropout以一定概率随机让某些神经元输出设置为0,既不参与前向传播也不参与反向传播。

Dropout主要是为了防止过拟合,为什么有助于防止过拟合呢?可以简单地这样解释,运用了dropout的训练过程,相当于训练了很多个只有半数隐层单元的神经网络(后面简称为“半数网络”),每一个这样的半数网络,都可以给出一个分类结果,这些结果有的是正确的,有的是错误的。随着训练的进行,大部分半数网络都可以给出正确的分类结果,那么少数的错误分类结果就不会对最终结果造成大的影响。

第26行(Softmax层prob

Softmax          prob             1 1 fc8 prob 0=0

softmax可以理解为归一化,如目前图片分类有一百种,那经过 softmax 层的输出就是一个一百维的向量。向量中的第一个值就是当前图片属于第一类的概率值,向量中的第二个值就是当前图片属于第二类的概率值...这一百维的向量之和为1.

softmax的输入层和输出层的维度是一样的,如果不一样,就在输入至 softmax 层之前通过一层全连接层。

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

 

全连接层将权重矩阵与输入向量相乘再加上偏置,将n个(−∞,+∞)的实数映射为K个(−∞,+∞)的实数(分数);Softmax将K个(−∞,+∞)的实数映射为K个(0,1)的实数(概率),同时保证它们之和为1。

 

AlexNet网络结构:

 

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析

Caffe模型转NCNN后生成param与bin文件及相关NCNN源码解析