首页 > 其他分享 >【caffe解读】 caffe从数学公式到代码实现2-基础函数类

【caffe解读】 caffe从数学公式到代码实现2-基础函数类

时间:2022-10-12 15:33:54浏览次数:39  
标签:数学公式 layer bottom top 解读 caffe cpp diff data


文章首发于微信公众号《与有三学AI》

接着上一篇,本篇就开始读layers下面的cpp,先看一下layers下面都有哪些cpp。

absval_layer.cpp
accuracy_layer.cpp
argmax_layer.cpp
base_conv_layer.cpp
base_data_layer.cpp
batch_norm_layer.cpp
batch_reindex_layer.cpp
bias_layer.cpp
bnll_layer.cpp
concat_layer.cpp
contrastive_loss_layer.cpp
conv_layer.cpp
crop_layer.cpp
cudnn_conv_layer.cpp
cudnn_lcn_layer.cpp
cudnn_lrn_layer.cpp
cudnn_pooling_layer.cpp
cudnn_relu_layer.cpp
cudnn_sigmoid_layer.cpp
cudnn_softmax_layer.cpp
cudnn_tanh_layer.cpp
data_layer.cpp
deconv_layer.cpp
dropout_layer.cpp
dummy_data_layer.cpp
eltwise_layer.cpp
elu_layer.cpp
embed_layer.cpp
euclidean_loss_layer.cpp
exp_layer.cpp
filter_layer.cpp
flatten_layer.cpp
hdf5_data_layer.cpp
hdf5_output_layer.cpp
hinge_loss_layer.cpp
im2col_layer.cpp
image_data_layer.cpp
infogain_loss_layer.cpp
inner_product_layer.cpp
input_layer.cpp
log_layer.cpp
loss_layer.cpp
lrn_layer.cpp
lstm_layer.cpp
lstm_unit_layer.cpp
memory_data_layer.cpp
multinomial_logistic_loss_layer.cpp
mvn_layer.cpp
neuron_layer.cpp
parameter_layer.cpp
pooling_layer.cpp
power_layer.cpp
prelu_layer.cpp
recurrent_layer.cpp
reduction_layer.cpp
relu_layer.cpp
reshape_layer.cpp
rnn_layer.cpp
scale_layer.cpp
sigmoid_cross_entropy_loss_layer.cpp
sigmoid_layer.cpp
silence_layer.cpp
slice_layer.cpp
softmax_layer.cpp
softmax_loss_layer.cpp
split_layer.cpp
spp_layer.cpp
tanh_layer.cpp
threshold_layer.cpp
tile_layer.cpp
window_data_layer.cpp

其中,下面这些layer是不需要反向传播的,大部分都是io类,我们就不讲了,自己去看。

threshold_layer.cpp
accuracy_layer.cpp
argmax_layer.cpp
data_layer.cpp
image_data_layer.cpp
input_layer.cpp
window_data_layer.cpp
parameter_layer.cpp
memory_data_layer.cpp
dummy_data_layer.cpp
hdf5_data_layer.cpp
hdf5_output_layer.cpp
neuron_layer.cpp
silence_layer.cpp
reshape_layer.cpp
rnn_layer.cpp
base_data_layer.cpp

剩下的就是要讲的,我们先从官方的开始看,后面再看自己写的以及一些开源的。这些layers大概有这么几大类,基础数学函数类,blob shape操作类,loss类。

本节先看一些基础函数类的layer,都只有一个输入,一个输出。注意其中有一些是容许inplace 的layer,有一些是不容许的。所谓inplace,输入输出共用一块内存,在layer的传播过程中,直接覆盖,省内存。Caffe在开源框架中,是比较占内存的了。

 

01 absval_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习

由于这是第一个例子,我们说的详细些;

Forward:

template <typename Dtype>
void AbsValLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>&
bottom, const vector<Blob<Dtype>*>& top) {
const int count = top[0]->count();
Dtype* top_data = top[0]->mutable_cpu_data();
caffe_abs(count, bottom[0]->cpu_data(),
top_data);
}

其中,count是blob的size,等于N*C*H*W,bottom[0]是输入x,top[0]是输出f(x),利用mutable_cpu_data来写入,cpu_data来读取。

Backward:

template <typename Dtype>
void AbsValLayer<Dtype>::Backward_cpu(const
vector<Blob<Dtype>*>& top,
const vector<bool>&
propagate_down, const vector<Blob<Dtype>*>&bottom) {
const int count = top[0]->count();
const Dtype* top_diff = top[0]->cpu_diff();
if (propagate_down[0]) {
const Dtype* bottom_data = bottom[0]->cpu_data();
 Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
 caffe_cpu_sign(count, bottom_data, bottom_diff);
 caffe_mul(count, bottom_diff, top_diff,
bottom_diff);
}
}

根据梯度下降法和链式法则,

一次标准的梯度更新过程如下,wt+1=wt+Δwt,对于sgd算法,其中 wt=−η⋅gt ,w为参数,t为时序,Δ为更新量,η为学习率,g为梯度

其中梯度g就是

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习_02

在backward中,我们只需要计算出即可,至于上面的符号,学习率等在其他地方处理,其实就是

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源软件_03

其中其实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
message ExpParameter {
// ExpLayer computes
outputs y = base ^ (shift + scale * x), for base > 0.
// Or if base is set to
the default (-1), base is set to e,
// so y = exp(shift + scale * x).
optional float base = 1
[default = -1.0];
optional float scale = 2
[default = 1.0];
optional float shift = 3
[default = 0.0];

从下面的setuplayer中可以看出,如果base不是-1,则必须是大于0的数,也就是-2,-3等是不支持的。

const Dtype base = this->layer_param_.exp_param().base();
if (base != Dtype(-1)) {
 CHECK_GT(base, 0) << "base must be strictlypositive.";
}

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习_04

当base=-1,也就是默认时f(x)=e^{αx+β},就是我们熟悉的指数函数了

还记得指数函数求导吧;

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习_05

 

03 log_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源框架_06

同样base=-1是默认值,否则必须大于0

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_图像处理_07

 

04 power_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源软件_08

梯度也是很简单的,不过为了提高计算效率,caffe尽可能的复用了中间结果,尤其是在反向传播的时候,分两种case,完整的计算大家还是去看代码,这里粘代码太难受了。

 

05 tanh_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源框架_09

这一次咱们遇到有test layer,仔细说说。

在caffe/test目录下test_tanh_layer.cpp

所谓测试,就是要验证网络的正向和反向。

这个文件是这样测试的:

先定义了个tanh_naïve函数,然后利用GaussianFille初始化一个bottom,将其通过forward函数,把出来的结果和tanh_naïve的结果进行比对,完整代码如下,感受一下:

void TestForward(Dtype filler_std) {
 FillerParameter filler_param;
 filler_param.set_std(filler_std);
 GaussianFiller<Dtype> filler(filler_param);
 filler.Fill(this->blob_bottom_);

 LayerParameter layer_param;
 TanHLayer<Dtype> layer(layer_param);
 layer.SetUp(this->blob_bottom_vec_,
this->blob_top_vec_);
 layer.Forward(this->blob_bottom_vec_,
this->blob_top_vec_);
// Now, check values
const Dtype* bottom_data = this->blob_bottom_->cpu_data();
const Dtype* top_data = this->blob_top_->cpu_data();
const Dtype min_precision = 1e-5;
for (int i = 0; i < this->blob_bottom_->count();
++i) {
   Dtype expected_value =
tanh_naive(bottom_data[i]);
   Dtype precision = std::max(
     Dtype(std::abs(expected_value * Dtype(1e-4))),
min_precision);
   EXPECT_NEAR(expected_value, top_data[i],
precision);
 }
}

EXPECT_NEAR函数就会检查梯度是否正确,如果过不了,就得回去看forward函数是否有错了。

反向验证:

void TestBackward(Dtype filler_std) {
 FillerParameter filler_param;
 filler_param.set_std(filler_std);
 GaussianFiller<Dtype> filler(filler_param);
 filler.Fill(this->blob_bottom_);

 LayerParameter layer_param;
 TanHLayer<Dtype> layer(layer_param);
 GradientChecker<Dtype> checker(1e-2, 1e-2, 1701);
 checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_,
this->blob_top_vec_);
}

其中GradientChecker(const Dtype stepsize, const Dtypethreshold,

const unsigned int seed = 1701, const Dtype kink =0.,const Dtype kink_range = -1)

可以设置stepwise和误差阈值,CheckGradientEltwise是逐个像素检查。

 

06 sigmoid_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_caffe_10

 

07 relu_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源框架_11

其中negative_slope a默认=0,退化为f(x)=max(x,0)

上面的relu其实包含了我们常说的relu和ReLU和LeakyReLU

 

08 prelu_layer.cpp

与LeakyReLU不同的是,负号部分的参数a是可学习的并不固定。所以,在反向传播时,该参数需要求导,默认a=0.25。

数学定义

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习_12

此处的a是个变量。

首先,对x也就是bottom的求导

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_深度学习_13

代码如下

if (propagate_down[0]) {
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
for (int i = 0; i < count; ++i) {
int c = (i / dim) % channels / div_factor;
bottom_diff[i] = top_diff[i] * ((bottom_data[i] > 0)
+ slope_data[c] * (bottom_data[i] <= 0));
}
}

而scale参数的求导,则会稍微复杂些,如下

if (this->param_propagate_down_[0]) {
 Dtype* slope_diff = this->blobs_[0]->mutable_cpu_diff();
for (int i = 0; i < count; ++i) {
int c = (i / dim) % channels / div_factor;
   slope_diff[c] += top_diff[i] * bottom_data[i]
* (bottom_data[i] <= 0);
 }
}

因为对于blob中第i个数据, 当i不等于k时,yi 与xk是没有关系的,但是a却与blob中的所有数据有关系。

我们重新表述一下

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_caffe_14

 

09 elu_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_开源框架_15

 

10 bnll_layer.cpp

数学定义:

【caffe解读】 caffe从数学公式到代码实现2-基础函数类_caffe_16

这次就先这样。


标签:数学公式,layer,bottom,top,解读,caffe,cpp,diff,data
From: https://blog.51cto.com/u_14122493/5750931

相关文章

  • 【caffe解读】 caffe从数学公式到代码实现1-导论
    ​真的很多年没有认真写csdn博客了,我回来了今天开一个新板块,目标是死磕现有的几大机器学习框架的代码,给想入门的小白们一些帮助。作为一个在图像行业战斗了几年的程序员,深知......
  • 【从caffe到Tensorflow 1】io 操作
    最近项目要频繁用到tensorflow,所以不得不认真研究下tensorflow而不是跟之前一样遇到了就搞一下了。首先我觉得所有这些框架里面caffe是最清晰的,所以就算是学习tensorflow,我......
  • 公司经营的困局解读——不是努力不够,而是不懂投放数据
    "IT有得聊”是机械工业出版社旗下IT专业资讯和服务平台,致力于帮助读者在广义的IT领域里,掌握更专业、实用的知识与技能,快速提升职场竞争力。 导Lead语有的人认为:“我已经很......
  • 炼丹感悟——超详细解读
    计算机视觉研究院专栏作者:Edison_G感谢大家一直支持“计算机视觉研究院”微信公众号,平时有没做好的地方望大家见谅,做的不好的希望您指出来,我们会修正不对之处,将做得更好,将最......
  • markdown数学公式
    title:markdown数学公式excerpt:反正latex也要学这些~tags:[markdown,latex,数学公式]categories:[life,software]index_img:https://picture-store-reposit......
  • 解读Vue3模板编译优化
    今天的文章打算学习下Vue3下的模板编译与Vue2下的差异,以及VDOM下Diff算法的优化。编译入口了解过Vue3的同学肯定知道Vue3引入了新的组合Api,在组件mount阶......
  • React生命周期深度完全解读
    在React中,对于每一次由状态改变导致页面视图的改变,都会经历两个阶段:render阶段、commit阶段。只有class组件才有生命周期,因为class组件会创建对应的实例,而函数组......
  • AQS源码深度解析之cancelAcquire方法解读
    1.背景2.源码解读调用该方法的地方 方法源码解读/***取消获取资源(异常处理时都需要用到)*方法主要功能:*1.处理当前取消节点的状态;......
  • 加解密模式之CBC模式和ECB模式解读
    1.CBC模式CBC模式的全称:CipherBlockChaining模式(密文分组链接模式),之所以叫这个名字,是因为密文分组像链条一样互相连接在一起。在CBC模式中,首先将明文分组和一个密文......
  • 负载均衡原理分析与源码解读
    上一篇文章一起学习了Resolver的原理和源码分析,本篇继续和大家一起学习下和Resolver关系密切的Balancer的相关内容。这里说的负载均衡主要指数据中心内的负载均衡,即RPC间的......