Caffe各层参数详解

在之前的文章中,整理了 ubuntu18安装和caffe-cpu安装问题汇总(含详细流程),这篇文章则对caffe的各个层进行一个剖析。文章篇幅较长,可根据目录按层选择阅读。

简述

Net是由Layer层层组成的

Net是由Solver文件支配的,Net的很多的hyper parameters都是由solver支配的。

如何学习caffe

  1. 第一步,用一个卷积网络,要有数据才行,首先要对数据进行一些预处理.比如你需要把数据做成(caffe支持的文件/夹格式)数据源的格式, 然后你再把数据集交给caffe, caffe再帮你做预测分类等

  2. 第二步, 需要定义一个网络,卷积神经网络是有各种各样的,如何定义各个层结构呢? 不需要!只需要去修改配置文件,按照一定流程去做。

    1. 所有的层都是按照流程图的格式,一层一层往下写的,每一层里都有很多可选项,参数项

  3. 第三步,由于net会受到一些hyper parameters的控制,所以我们需要定义一些solver控制hyper parameters

    1. solver就是专门配置你的caffe网络参数和超参数的文件,也是个prototxt文件

  4. 第四步,训练网络,只运行一个脚本文件,如果是linux系统,那一行命令就ok

看一下caffe的网络配置文件,我们该如何从头到尾去写这个东西呢??每一层的每个参数都表达什么意思呢?

下载好caffe后,可以去examples文件夹查看一些示例

数据层

关于LMDB相关内容,参考文章:数据集转换成LMDB格式

layer {
  name: "cifar" # 就是你想给这个层叫什么名字你自己定
  type: "Data" # 写成data告诉caffe这个层是数据层
  top: "data"
  top: "label"
  # 每一层既有输入又有输出, 我们用bottom表示输入,top表述输出,多个top表示多个输出
  # 这是数据层,所以不存在输入的情况,只有输出
  # 数据层输出两个东西,一个是数据data, 一个是标签label

  include {
    phase: TRAIN
  }
  }

# 一般一边训练一边测试,训练几千次之后测试一下,这样可以提前终止, 
# 这个实现方法就在include里边phase(阶段):
# 写了个TRAIN 意思就是这层的数据,只有在我们训练层的时候才会用到,测试的时候不会用到TEST同理,测试层才用

  transform_param {
    mean_file: "examples/cifar10/mean.binaryproto"
    transform_param{
        scale:0.00390625
        mirror: 1
        crop_size: 227
    }
  }

#   mean_file:这里求均值是为了对数据初始化操作, 这里可以思考正态分布转变成标准正态分布,当然是每个通道的像素减去这个通道的均值. 这里是一个配置文件, 通过这个配置文件来实现的,至于怎么做出来的,等会你就知道了
#   scale是用来归一化的, 就是0-255到0-1,为什么是0.00396625呢?是通过1/255=0.0039得到的
#   mirror镜像, 意思就是数据增强,把每个图片都镜面对称一下, 你不就有2倍的数据了吗, 1就是开启,0就是关闭
#   crop剪裁:如果你觉得数据比较少,那你可以通过随机剪裁,进行数据增强, 一个图给你剪成好多个图,你不多了好多倍 的数据了吗


  data_param {
    source: "examples/cifar10/cifar10_train_lmdb"
    batch_size: 111
    backend: LMDB
  }

# data_param 数据参数
#    一般情况下caffe要求我们的数据集格式是lmdb, 我们要把数据搞成数据库的形式,然后把数据库的源交给caffe,就是根目录,做成数据库的话方便caffe读取,你想想用规则读取快还是不用规则读取快呢????另外,你读取数据是cpu来搞的, 你gpu是用来计算数据的, 所以你读取数据也得很快才行,
#    source:就是你创建好的LMDB格式数据集的根目录所在位置
#    batch_size是每次喂给net的样本个数,一般是2的n次幂,一般是越大越好,但是你要考虑你的硬件限制,一般GPU是8G的,1080是8G, TitanX12G, 显存决定了你的batch_size
#    backend:数据类型的名称 比如LMDB


LMDB格式数据层

layer {
  name: "cifar"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    mean_file: "examples/cifar10/mean.binaryproto"
  }
  data_param {
    source: "examples/cifar10/cifar10_test_lmdb"
    batch_size: 1000
    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" 
#这个txt文件里写了每个小数据库的具体路径,这里后续还会介绍
    batch_size: 10
  }
}

直接输入图片层

  1. 文件夹下有好多图片,同样的道理,我们把source的路径搞成.txt文件, 这个txt文件包括了所有图片的路径 但是要注意这里txt内容的格式, 是图片路径<空格>类别
  2. 看到new_height了吗? 你的图片可能是千奇百怪的大小,没有预处理过, 所以你需要先进行预处理resize, 这里是resize成了256*256的分辨率
layer {
  name: "data"
  type: "HDF5Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  image_data_param {
    source: "examples/xjh_images/data/train.txt"
    batch_size: 10
    new_height: 256
    new_width:256
  }
}

LMDB文件常用来做分类,HDF5常用来做回归

卷积层

数据层介绍了三种数据层:HDF5、LMDB和直接输入图片。这里解释卷积层的相关信息。

layer {
  name: "conv1"         #该层的名字
  type: "Convolution"   #该层的类型,即卷积
  bottom: "data"        #输入层的名字
  top: "conv1"          #输出层的名字,注意这里就是本层
  
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
#param参数解释
#lr_mult    本层的学习率,并不是模型的学习率,模型的学习率是通过solver文件控制的
#           其实lr_mult表示的是模型学习率的系数
#如果该层有两个lr_mult参数,那么第一个表示模型学习率在本层的权值,第二个则表示对应偏置项,一般偏置=2*权值


  convolution_param {    #卷积参数
    num_output: 32       #输出的通道数channels,即卷积核的个数,如果有c个核,那么得到c个featuremaps一般来说channels=c

    pad: 2               #边缘处理方式,0表示不扩充
    kernel_size: 5       #卷积核大小
    stride: 1            #卷积核滑动步长

    weight_filter {      #卷积核的权值
        type:"xvaier"    #type表示卷积核的类型,即gauss核还是全0核等
                                #全0核则默认type:"constant"
                                #常用xvaier算法来初始化gaussian
    }
    bias_filter {        #偏置项初始化
        type:"constant"  #默认则偏置项=0
    }
  }

}

输入如果是width=w, height=h

那么输出w' = [(w-k+2p)/s] +1

h'=[(h-k+2p)/s] +1

p就是padding边缘处理

stride就是步长

池化(合并)层

pool翻译成合并更能表示出pool层的作用:把kernel内覆盖的多个像素,合并成一个。

layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX        #池化方法,有取最值max,也有取覆盖点均值等方法
    kernel_size: 3   #核大小
    stride: 2        #步长
  }
}

激活函数

上述设定了卷积层,但是卷积层需要输入给激活函数,将激活函数的结果作为输出。

激活函数的输入层一般是池化后的卷积层,即卷积层-池化层。

layer {
  name: "relu1"
  type: "ReLU"    #使用的函数是ReLU函数
  bottom: "pool1" #池化层
  top: "pool1"
}

相同名字的bottom和top这些blob就是同一个blob,占用的是同一个空间。
简单来解释就是:
int a;
a = 0;
a = 1;
你可以无数次对这个a进行改变。
对于blob来说也是一样。

至于谁先谁后,那就是看你的网络定义哪个layer在前,它就先计算。
如果有两个layer输出的blob名称是一样的,那么它们的输入blob也一定会有这个blob,也就是,如果layer不是对输入blob本身操作,就不允许输出blob同名。比如:
layer1和layer2的输入和输出blob都是blob1,它们都是对blob1进行操作,这是允许的,直接按顺序计算就可以了。
layer1的输入blob是blob1,输出blob是blob_out,layer2的输入blob是blob2,输出blob也是blob_out,那么这就是不允许的。因为它们不是对它们的输入blob本身进行操作,假设你允许这样的操作,那么后运算的layer会将blob_out覆盖成后运算的结果,前面运算的blob_out的结果就消失了。

当然,layer1和layer2的输入和输出blob都是blob1,它们都是对blob1进行操作,比如layer1先计算,然后layer2后计算,计算layer2的结果也是会把layer1的结果给覆盖,只不过是网络已经不需要这个layer1的结果而已,因为它已经前向传播过去了...

全链接FC层

FC层即全链接层,下文type:“InnerProduct”。

全链接层就是把之前提取到的特征,进行一个简单的组合,包装成一个向量的形式,根据这个向量就可以对其特征向量进行分析。

layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool3"
  top: "ip1"
  param {
    lr_mult: 1
    decay_mult: 250
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 500
    weight_filter {
        type:"xavier"
    }
    bias_filter {
        type:"constant"
    }
  }
}

layer {
    name:"accuracy"
    type:"Accuracy"
    bottom:"ip2"
    bottom:"label"
    top:"accuracy"
    include {
        phase:TEST
    }
}

LOSS层

layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip1"
  bottom: "label"
  top: "loss"
}

首先要把你预测的结果归一化,就是值域在0-1,相当于分类的概率值;

loss的计算方法你说了算,可以是softmax的loss数值也可以是softmax概率值;

loss层的输入是全连接层,和真实值层。

Reshape层

为什么存在reshape层,因为想要在不改变数据的情况下,改变维度

    比如[3, 15, 28, 28] 变成[3*15, 28*28]

layer {
    name:"reshape"
    type:"Reshape"
    bottom:"input"
    top:"output"
    reshape_param {
        shape {
            dim : 0
            dim : 2
            dim : 3
            dim : -1
        }
    }     
}

reshape_param参数:

    上述shape{}中的竖排:

        dim:a

        dim:b    

        dim:c        

        dim:d

是常见写法的:[a, b, c, d]

dim后的数字表示:示例dim:x

  • x=0时,就是保持abcd的值不变
  • x=-1时就是让caffe自己去推断数值

如果是具体的数值,比如把b换成23,就是你制定了b原来的数改成23

dropout层

还记得dropout吗?防止过拟合的,就是随机断开网络连接线,迭代一次

layer {
  name: "drop7"
  type: "Dropout"
  bottom: "fc7-conv"
  top: "fc7-conv"
  dropout_param {
    dropout_ratio: 0.5 #迭代一次断开的线数,0.5即50%
  }
}

完整的网络配置文件(mnist数据)

环境:ubuntu18.04,caffe-cpu

进入下载的caffe/examples/mnist/,选择lenet_train_test.prototxt,可以自行查看学习。

示例

在下面代码中标注了,Net中哪个部分设置了网络输出的纬度。(FC层)

name: "LeNet"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_train_lmdb"
    batch_size: 64
    backend: LMDB
  }
}
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "examples/mnist/mnist_test_lmdb"
    batch_size: 100
    backend: LMDB
  }
}
layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 20
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool1"
  type: "Pooling"
  bottom: "conv1"
  top: "pool1"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "conv2"
  type: "Convolution"
  bottom: "pool1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  convolution_param {
    num_output: 50
    kernel_size: 5
    stride: 1
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "pool2"
  type: "Pooling"
  bottom: "conv2"
  top: "pool2"
  pooling_param {
    pool: MAX
    kernel_size: 2
    stride: 2
  }
}
layer {
  name: "ip1"
  type: "InnerProduct"
  bottom: "pool2"
  top: "ip1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 500
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "relu1"
  type: "ReLU"
  bottom: "ip1"
  top: "ip1"
}
layer {
  name: "ip2"
  type: "InnerProduct"
  bottom: "ip1"
  top: "ip2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 2
  }
  inner_product_param {
    num_output: 10       #注意:这里就是输出结果的纬度,即10个分类
    weight_filler {
      type: "xavier"
    }
    bias_filler {
      type: "constant"
    }
  }
}
layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "ip2"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "ip2"
  bottom: "label"
  top: "loss"
}

超参数solver配置文件

caffe提供了一个solver文件,根据这个solver进行修改,然后保存到项目根目录下即可。

solver是个.prototxt文件,通过mnist文件夹内的文件来看一下solver是如何控制haper parameters的。

# lenet_solver.prototxt文件一览
# The train/test net protocol buffer definition
net: "examples/mnist/lenet_train_test.prototxt"
# test_iter specifies how many forward passes the test should carry out.
# In the case of MNIST, we have test batch size 100 and 100 test iterations,
# covering the full 10,000 testing images.
test_iter: 100
# Carry out testing every 500 training iterations.
test_interval: 500
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
# The learning rate policy
lr_policy: "inv"
gamma: 0.0001
power: 0.75
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 10000
# snapshot intermediate results
snapshot: 5000
snapshot_prefix: "examples/mnist/lenet"
# solver mode: CPU or GPU
solver_mode: GPU

自定义的solver文件

loss fucntion的优化算法

往往loss function是非凸函数,没有解析解,需要通过优化方法来求解析解

caffe提供了六种常用的优化算法, 需要在solver文件中进行配置,即通过type关键字来选择

    Stochastic Gradient Descent (type:"SGD")

    AdaDelta (type:"AdaDelta")

    Adaptive Gradient (type:"AdaGrad")

    Adam(type:"Adam")

    Nesterov's Accelerated Gradient (type:"Nesterov")

    RMSprop(type:"RMSProp")

上述就是六种算法名字及在solver文件中type关键字对应的参数名

自定义solver

# 自己写一个solver.prototxt文件

net: "examples/mnist/lenet_train_test.prototxt"
# net就是网络配置文件的路径

test_iter: 100
# 一次迭代100个测试batch,
# 测试和训练时,都是一个batch一个batch喂给net的,test_iter表示了一次给net是100个batch
# test_iter的大小 = (样本个数)/(batch包含样本数)

test_interval: 500
# 表示每训练500次,测试一次

base_lr: 0.01
# 基础学习率,就是上文提到的网络模型的学习率
# 实习学习率 = 每个conv层设置的学习率权值数 * base_lr + 每个conv层设置的偏置项
# 若想修改学习率,最好只修改此处。

momentum: 0.9
# 动量值,一般设置为0.9且常不修改

weight_decay: 0.0005
# 不知道是什么

lr_policy: "inv"
# 学习策略

gamma: 0.0001
# 不知道是什么

power: 0.75
# 不知道是什么

display: 100
# 每训练100次打印一次结果,display:0时不显示

max_iter: 10000
# 最大迭代次数,即迭代10000次时停止训练

snapshot: 5000
# 快照保存模型,即当迭代5000次时保存一下模型

snapshot_prefix: "examples/mnist/lenet"
# 快照存储位置

solver_mode: GPU
# net的运行模,CPU/GPU

 

 

 

 

 

 

 

你可能感兴趣的:(caffe)