Caffe构建神经网络模型

Caffe构建神经网络模型

Caffe是一个非常强大的深度学习框架,此处记录了博主在使用过程中的一些经验,没有覆盖到的内容查查官网和Github 上的issue问答应该能有帮助。另外附上Caffe的官方Tutorial。更新于2018.10.24。

更多内容,欢迎加入星球讨论。
在这里插入图片描述


Caffe层的使用

这里给出了一些博主使用过的Caffe层的范例。

基础知识

在具体说明Caffe各层的应用范例之前,先给出能够帮助理解或后续可能用到的一些基础知识。

Caffe中的数据存储格式

Caffe中数据存储在blob里,尺寸为N x C x H x W,含义从左到右分别为Batch Size、通道数(特征层数)、高、宽。

权重更新公式

下面是Caffe中更新权重所使用的公式,方便理解各层中设置的参数:

w i = w i − ( b a s e _ l r ∗ l r _ m u l t ) ∗ d w i − ( w e i g h t _ d e c a y ∗ d e c a y _ m u l t ) ∗ w i w_i=w_i-(base\_lr*lr\_mult)*dw_i - (weight\_decay*decay\_mult)*w_i wi=wi(base_lrlr_mult)dwi(weight_decaydecay_mult)wi

因此原则上当 l r _ m u l t lr\_mult lr_mult d e c a y _ m u l t decay\_mult decay_mult 都设置为0时,该层神经网络的参数就不会更新了。但是从博主在使用过程中的经验来看,不能过度依赖这个方法控制参数是否更新,因为在实践的过程中,即使博主将损失的权重、 l r _ m u l t lr\_mult lr_mult d e c a y _ m u l t decay\_mult decay_mult 都设置为0,该层的参数还是出现了变化。这个可能跟Caffe中更深层的代码实现方式有关。

参数初始化方法

Caffe中的filter.hpp提供了7种参数的初始化方法,分别为:常量初始化(constant)、高斯分布初始化(gaussian)、positive_unitball初始化、均匀分布初始化(uniform)、xavier初始化、msra初始化、双线性初始化(bilinear)。详细的说明可以参考参数初始化方法

划重点:

  1. 下面卷积层中用到的msra初始化方法是专门针对激活函数为ReLU的层设计的,是基于“均值为0,方差为2的高斯分布”产生的。
  2. 偏置初始化中用到的constant初始化方法是用常数作为初始值,具体的值可以自己确定,默认为0。

卷积层(Convolution)

关于卷积层的更多说明可以查看卷积层官方文档

layer {
  name: "conv_layer_name"    //为当前层取名,日后调用训练好的网络参数时,Caffe就通过这个区分应用哪一层的参数。因此,在构建deploy.prototxt时,也需要用相同的名字。
  type: "Convolution"    //定义当前层的属性,此处为卷积层。
  bottom: "input_blob_name"    //这里定义当前层的输入blob叫什么名字
  top: "output_blob_name"    //这里定义当前层的输出blob叫什么名字
  param {    //第一组param定义weights的参数
    lr_mult: 1    //确定weights学习率要乘以的系数
    decay_mult: 0    //确定weights衰减要乘以的系数
  }
  param {    //第二组param定义bias的参数
    lr_mult: 0
    decay_mult: 0
  }
  convolution_param {    //定义卷积层的参数
    num_output: 128    //输出通道数
    pad: 1    //padding尺寸
    kernel_size: 3    //卷积核尺寸
    stride: 1    //步长
    weight_filler {    //确定卷积层参数的初始化方法
      type: "msra"    //使用msra初始化
    }
    bias_filler {    //确定偏置初始化方法
      type: "constant"    //使用常量初始化
    }
    engine: CUDNN
  }
}

激活函数层(ReLU)

name、type、bottom、top等与卷积层相同,次数定义的层的类型为ReLU激活函数。关于激活函数的说明可以参考激活函数学习官方文档

layer {
  name: "ReLU_name"
  type: "ReLU"
  bottom: "input_blob_name"
  top: "output_blob_name"
  relu_param {
    negative_slope: 0.1    //定义激活函数在x负半轴的斜率
  }
}

按位计算层(Eltwise)

Caffe中也提供了求解按位计算的层Eltwise,其可以计算按位相乘PROD、按位相加SUM、求最大值。

layer {
  name: "Eltwise_layer_name"
  type: "Eltwise"
  bottom: "input_blob_name1"
  bottom: "input_blob_name2"    //Eltwise的两个输入
  top: "output_blob_name"
  eltwise_param {    //定义Eltwise的参数
    operation: SUM    //定义操作为求和
    coeff: 1    //第一个输入每个元素乘以的数
    coeff: -1    //第二个输入每个元素乘以的数
  }
}

级联层(Concat)

将各级输入按照通道级联。需要注意的是,所有要级联的层需要具有相同的h和w。

layer {
  name: "concat_layer_name"
  type: "Concat"
  bottom: "input_blob_name1"
  bottom: "input_blob_name2"
  bottom: "input_blob_name3"
  bottom: "input_blob_name4"
  bottom: "input_blob_name5"
  top: "output_blob_name"
}

消音层(Silence)

在Caffe中,如果一个层的输出没有被调用,该层的输出就会被默认为网络输出,print到终端上。为了防止中间产物被当作输出,需要用一个层“接住”这些输出,下面的层就是这个作用的,它不产生任何输出。

layer {
  name: "Silence_name"
  type: "Silence"
  bottom: "blob_name"
}

平铺扩展层(Tile)

在Caffe中,如果希望对某一个blob实现任意维度的平铺扩展(通过复制blob中的值实现)则需要用到Tile层。Caffe.help官网都有具体的信息。其中axis定义对哪一个维度进行平铺,而tiles则是在这个维度上平铺几倍

layer {
 name: "tile"
 type: "Tile"
 bottom: "blob1"
 top: "blob2"
  tile_param {
  axis: 2
  tiles: 32
  }
}

平铺层(Flatten)

作用:将维度为N x C x H x W的bottom平铺成指定维度(默认N x (C x H x W))。

layer {
	name: "Flatten"
	type: "Flatten"
	bottom: "input_blob"
	top: "output_blob"
	flatten_param {
		axis: 1		//指定开始维度
		end_axis: -1		//指定结束维度
	}
}

Reshape层

reshape层将输入blob按照指定方式重新排列。需要注意的是,Reshape层与Flatten层相同,不会对blob中的数据做改动,只是改变blob的维度。如下面的例子会将尺寸为N x C x H x W的输入变成N x (H x W) x 1 x 1的输出。

具体地,shape中可以指定尺寸,几个dim就是几维,dim后面的数字就是该维度上的维数。特别地,0表示继承输入blob这个维度上的维数;-1表示自动计算,补全维数,使得元素总数在变换前后相同。

axis和num_axis指定输入blob的哪一部分参与重新排布,默认是整个blob。具体示例可以参见官网的说明

layer {
	name: "Reshape"
	type: "Reshape"
	bottom: "disp_gt_aug"
	top: "disp_gt_aug_reshaped"
	propagate_down: false
	reshape_param {
		shape {
			dim: 0
			dim: -1
			dim: 1
			dim: 1
		}
	}
	axis: 1
	num_axis: 3
}

Pooling层

在进行pooling计算的时候,Caffe提供了两种方式定义卷积核的大小:第一种直接用kernel_size或kernel_height/kernel_width定义卷积核大小;第二种是用全局的方式实现,即设定global_pooling为真。pooling的种类有max(MAX)、average(AVE)等。

layer {
 name: "maxpool"
  type: "Pooling"
 bottom: "blob1"
 top: "blob2"
  pooling_param {
  pool: MAX
  pad: 0
  global_pooling: true
  }
}

Softmax层

首先明确Softmax函数的功能:Softmax将原始分类器的输出转化成不同类别之间的相对概率。比如,原始的输出为 V = [ − 3 , 2 , − 1 , 0 ] V=[-3,2,-1,0] V=[3,2,1,0],那么经过Softmax的处理后,数值转化为 S = [ 0.0057 , 0.8390 , 0.0418 , 0.1135 ] S=[0.0057, 0.8390, 0.0418, 0.1135] S=[0.0057,0.8390,0.0418,0.1135]。此时可以看到,被判断成第二类别的概率更大。Softmax函数定义为: S i = e V i ∑ i C e V i S_i=\frac{e^{V_i}}{\sum_i^Ce^{V_i}} Si=iCeVieVi

layer {
  name: "Softmax"
  type: "Softmax"
  bottom: "input_bottom"
  top: "output_bottom"
  softmax_param{
  	axis: 1		##用于指定对哪一维做处理,默认为1,即处理Channels。
  }
}

Argmax层

Argmax层用于找到某一维度上最大值所对应的位置,如按通道数找最大值,返回的值是每个位置 ( x , y ) (x,y) (x,y)上拥有最大值的Channel的位置 C C C

layer {
	name: "Argmax"
	type: "ArgMax"
	bottom: "input_blob"
	top: "output_blob"
	argmax_param{
		axis: 1
		out_max_val: 1
		top_k: 1
	}
}

损失计算层

L1Loss

name、type、bottom、top等与卷积层相同,type定义的层的类型为L1损失层。损失的种类和具体说明参考官方文档

layer {
  name: "loss_name"
  type: "L1Loss"
  bottom: "input_blob_name1"
  bottom: "input_blob_name2"    //损失层需要2个输入
  top: "output_blob_name"
  loss_weight: 1    //损失权重,取决于损失是否回传(不回传设为0),回传的话以多少比例影响参数(损失需要乘以的权重)
  l1_loss_param {    //定义L1损失的参数
    l2_per_location: false    //是否计算2维损失(我的理解)
    normalize_by_num_entries: true    //是否根据有效输入求平均(因为在计算时,有的位置上的数值是无效的,比如nan,因此在计算损失的时候需要将这些数值的影响去掉)
  }
}
Softmax with Loss

用于计算Softmax后的损失,其中前半部分的参数与Softmax相同,后半部分的参数见下面示例的注释。这个层特殊的地方在于:对于分类问题,通常是一个样本对应一个标量(label),然而softmax with loss则支持更高维度的类别标签。比如,当bottom[0]的维度为n x c x h x w,此时,如果将softmax的参数axis设为1,那么对应的label尺寸应为n x 1 x h x w,其中C的位置上存有一个数值范围在区间[0, c-1]的整数。至于后面损失的计算,则采用相同的处理。

layer {
	name: "SoftmaxWithLoss"
	type: "SoftmaxWithLoss"
	bottom: "prediction"
	bottom: "label"
	propagate_down: true 
	propagate_down: false
	top: "SoftmaxWithLoss"
	softmax_param {
		axis: 1		//将Channels这个维度视为互相独立的概率
	}
	loss_param {
		ignore_label: 0		//int类型,默认为空。如果指定值,则所有样本都参与反向计算;否则,给定label的样本不参与损失计算,反向传播时梯度直接设为0。
		normalize: 1		//bool类型。若设为1,损失会除以参与计算的样本总数,否则等于直接求和。
		normalization: FULL		//enum类型,默认为VALID,具体情况有FULL、VALID、BATCH_size和NONE四种,详细说明见下面的解释。
	}
}

关于Normalization参数的设置说明:

enum NormalizationMode {
    // Divide by the number of examples in the batch times spatial dimensions.
    // Outputs that receive the ignore label will NOT be ignored in computing the normalization factor.
    FULL = 0;

	// Divide by the total number of output locations that do not take the
	// ignore_label.  If ignore_label is not set, this behaves like FULL.
	VALID = 1;
	
	// Divide by the batch size.
	BATCH_SIZE = 2;
	
	// 
	NONE = 3;
}

需要注意的是:

  1. 若未设置normalization,但是设置了normalize,则有如下对应关系:
    normalize = 1 等同于 归一化方式为VALID;
    normalize = 0 等同于 归一化方式为BATCH_SIZE。
  2. 若设置了normalization,则归一化方式由normalization确定,不再考虑normalize。

实验中用到的自定义层

在实验过程中,除了Caffe官方提供的层以外,往往需要用到用户自定义的层以实现具体的功能。这一部分列举了一些博主在实验过程中用到的Caffe层。

输入层

layer {
  name: "CustomData_layer_name"
  type: "CustomData"
  top: "output_blob_name1"
  top: "output_blob_name2"
  top: "output_blob_name3"
  include {
    phase: TRAIN
  }
  data_param {    //定义参数
	    source: "path/to/your/data"
    batch_size: 4
    backend: LMDB    //定义输入数据类型
    rand_permute: true
    rand_permute_seed: 77
    slice_point: 3    //按通道切割成几部分,定义切割的位置
    slice_point: 6
    encoding: UINT8
    encoding: UINT8
    encoding: UINT16FLOW
    verbose: true
  }
}

数据扩张层

数据扩张层的参数过多,不在这里详细列出来了,如果需要可以参考DispNet中提供的代码。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值