美文网首页Caffe有三AI学院
【caffe解读】 caffe从数学公式到代码实现2-基础函数类

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

作者: 有三AI | 来源:发表于2018-03-03 02:20 被阅读9次

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

    [caffe解读] caffe从数学公式到代码实现2-基础函数类​

    接着上一篇,本篇就开始读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在开源框架中,是比较占内存的了。

    1 absval_layer.cpp

    数学定义:

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

    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&

    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就是

    在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);

    就得到了

    没有对应的test文件,就不解析了。

    2 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 strictly positive.";

    }

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

    还记得指数函数求导吧;

    3 log_layer.cpp

    数学定义:

    4 power_layer.cpp

    数学定义:

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

    5 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) {

      FillerParameter filler_param;

      filler_param.set_std(filler_std);

      GaussianFiller filler(filler_param);

      filler.Fill(this->blob_bottom_);

      LayerParameter layer_param;

      TanHLayer 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 filler(filler_param);

      filler.Fill(this->blob_bottom_);

      LayerParameter layer_param;

      TanHLayer layer(layer_param);

      GradientChecker checker(1e-2, 1e-2, 1701);

      checker.CheckGradientEltwise(&layer, this->blob_bottom_vec_,

    this->blob_top_vec_);

    }

    其中GradientChecker(const Dtype stepsize, const Dtype threshold,

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

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

    6 sigmoid_layer.cpp

    数学定义:

    7 relu_layer.cpp

    数学定义:

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

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

    8 prelu_layer.cpp

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

    数学定义

    此处的a是个变量。

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

    代码如下

    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中的所有数据有关系。

    我们重新表述一下

    9 elu_layer.cpp

    数学定义:

    10 bnll_layer.cpp

    数学定义:

    知乎的公式编辑实在不行,严重打击了我写以后的文章的积极性

    更多请移步

    1,我的gitchat达人课

    龙鹏的达人课

    2,AI技术公众号,《与有三学AI》

    [caffe解读] caffe从数学公式到代码实现3-shape相关类​

    3,以及摄影号,《有三工作室》

    冯小刚说,“他懂我”​

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/muknxftx.html