接着上一篇,本篇就开始读layers下面的cpp,先看一下layers下面都有哪些cpp。
absval_layer.cpp
其中,下面这些layer是不需要反向传播的,大部分都是io类,我们就不讲了,自己去看。
threshold_layer.cpp
剩下的就是要讲的,我们先从官方的开始看,后面再看自己写的以及一些开源的。这些layers大概有这么几大类,基础数学函数类,blob shape操作类,loss类。
本节先看一些基础函数类的layer,都只有一个输入,一个输出。注意其中有一些是容许inplace 的layer,有一些是不容许的。所谓inplace,输入输出共用一块内存,在layer的传播过程中,直接覆盖,省内存。Caffe在开源框架中,是比较占内存的了。
01
absval_layer.cpp
数学定义:
由于这是第一个例子,我们说的详细些;
Forward:
template <typename Dtype>
其中,count是blob的size,等于N*C*H*W,bottom[0]是输入x,top[0]是输出f(x),利用mutable_cpu_data来写入,cpu_data来读取。
Backward:
template <typename Dtype>
根据梯度下降法和链式法则,
一次标准的梯度更新过程如下,wt+1=wt+Δwt,对于sgd算法,其中 wt=−η⋅gt ,w为参数,t为时序,Δ为更新量,η为学习率,g为梯度
其中梯度g就是
在backward中,我们只需要计算出即可,至于上面的符号,学习率等在其他地方处理,其实就是
其中其实top_diff,就是对应:
const Dtype* top_diff = top[0]->cpu_diff();
在这里我们知道了,cpu_data就是bottom的data,而cpu_diff就是它存储的梯度,有疑问可以返回上一篇。
propagate_down是一个参数,用于控制是否容许梯度下传的,
caffe_cpu_sign(count, bottom_data, bottom_diff);实际上就是计算了梯度,
再利用caffe_mul(count, bottom_diff, top_diff, bottom_diff);
就OK了
没有对应的test文件,就不解析了。
02
exp_layer.cpp
看下caffe 关于其参数的定义:
数学定义:
// Message that stores parameters used by ExpLayer
从下面的setuplayer中可以看出,如果base不是-1,则必须是大于0的数,也就是-2,-3等是不支持的。
const Dtype base = this->layer_param_.exp_param().base();
当base=-1,也就是默认时f(x)=e^{αx+β},就是我们熟悉的指数函数了
还记得指数函数求导吧;
03
log_layer.cpp
数学定义:
同样base=-1是默认值,否则必须大于0
04
power_layer.cpp
数学定义:
梯度也是很简单的,不过为了提高计算效率,caffe尽可能的复用了中间结果,尤其是在反向传播的时候,分两种case,完整的计算大家还是去看代码,这里粘代码太难受了。
05
tanh_layer.cpp
数学定义:
这一次咱们遇到有test layer,仔细说说。
在caffe/test目录下test_tanh_layer.cpp
所谓测试,就是要验证网络的正向和反向。
这个文件是这样测试的:
先定义了个tanh_naïve函数,然后利用GaussianFille初始化一个bottom,将其通过forward函数,把出来的结果和tanh_naïve的结果进行比对,完整代码如下,感受一下:
void TestForward(Dtype filler_std) {
EXPECT_NEAR函数就会检查梯度是否正确,如果过不了,就得回去看forward函数是否有错了。
反向验证:
void TestBackward(Dtype filler_std) {
其中GradientChecker(const Dtype stepsize, const Dtype threshold,
const unsigned int seed = 1701, const Dtype kink = 0.,const Dtype kink_range = -1)
可以设置stepwise和误差阈值,CheckGradientEltwise是逐个像素检查。
06
sigmoid_layer.cpp
数学定义:
07
relu_layer.cpp
数学定义:
其中negative_slope a默认=0,退化为f(x)=max(x,0)
上面的relu其实包含了我们常说的relu和ReLU和LeakyReLU
08
prelu_layer.cpp
与LeakyReLU不同的是,负号部分的参数a是可学习的并不固定。所以,在反向传播时,该参数需要求导,默认a=0.25。
数学定义
此处的a是个变量。
首先,对x也就是bottom的求导
代码如下
if (propagate_down[0]) {
而scale参数的求导,则会稍微复杂些,如下
if (this->param_propagate_down_[0]) {
因为对于blob中第i个数据, 当i不等于k时,yi 与xk是没有关系的,但是a却与blob中的所有数据有关系。
我们重新表述一下
09
elu_layer.cpp
数学定义:
10
bnll_layer.cpp
数学定义:
这次就先这样。
同时,在我的知乎专栏也会开始同步更新这个模块,欢迎来交流
https://zhuanlan.zhihu.com/c_151876233
注:部分图片来自网络
—END—
标签:数学公式,layer,定义,bottom,Dtype,解读,caffe,cpp,diff From: https://blog.51cto.com/u_14122493/5751203