你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
输入关键字进行搜索
搜索:
没有找到相关结果
ruirui_ICT - 听大大们说,要“左手代码,右手公式”,喵~
赞同来自: attitude 、uestc_yang 、孙琳钧 、xinmiao 、李扬 、janemy 、pustar 、lady_he 、好兵兵啊 、李士林 、epleone 、hgzry812 、深度学习思考者 、贺思远 、tracycw更多 »
N_ = num_output; K_ = bottom[0]->count(axis); M_ = bottom[0]->count(0, axis);
weight_shape[0] = N_; weight_shape[1] = K_; vector<int> bias_shape(1, N_); this->blobs_[1].reset(new Blob<Dtype>(bias_shape));
caffe_cpu_gemm<Dtype>(CblasTrans, CblasNoTrans, N_, K_, M_, (Dtype)1., top_diff, bottom_data, (Dtype)0., this->blobs_[0]->mutable_cpu_diff());
caffe_cpu_gemv<Dtype>(CblasTrans, M_, N_, (Dtype)1., top_diff, bias_multiplier_.cpu_data(), (Dtype)0., this->blobs_[1]->mutable_cpu_diff());
caffe_cpu_gemm<Dtype>(CblasNoTrans, CblasNoTrans, M_, K_, N_, (Dtype)1., top_diff, this->blobs_[0]->cpu_data(), (Dtype)0., bottom[0]->mutable_cpu_diff());
cjwdeq
赞同来自: xinmiao 、uestc_yang 、ruirui_ICT 、shakevincent 、attitude 、yxyuni 、lady_he 、好兵兵啊 、popper0912更多 »
0布点0 - 图像处理新手
赞同来自:
王蒙蒙 - CaffeCN社区志愿者
要回复问题请先登录或注册
UESTC
4 个回复
ruirui_ICT - 听大大们说,要“左手代码,右手公式”,喵~
赞同来自: attitude 、uestc_yang 、孙琳钧 、xinmiao 、李扬 、janemy 、pustar 、lady_he 、好兵兵啊 、李士林 、epleone 、hgzry812 、深度学习思考者 、贺思远 、tracycw更多 »
至于实现BP的理论和推导,cjwdeq同学已经讲的非常清楚了。既然答题组的大大们总说要发扬“左手代码,右手公式”的精神,我就结合caffe的源码讲讲具体反向传播是怎么实现的。先从简单的全连接层入手:
打开Inner_product_layer.cpp,里面的Backward_cpu函数实现了反向传播的过程。(如果使用的是GPU,则会调用Inner_product_layer.cu文件里的Backward_gpu函数,实现过程是类似的)
先通过LayerSetUp函数明确几个变量: N_表示输出的特征维数,即输出的神经元的个数
K_表示输入的样本的特征维数,即输入的神经元的个数
M_表示样本个数
因此全连接层的W维数就是N_×K_,b维数就是N_×1 下面一行一行看Backward_cpu函数的代码,整个更新过程大概可以分成三步:(顺便盗几个cjwdeq同学贴的公式,哈哈)
1. 这一句是为了计算dW,对应公式就是
其中的bottom_data对应的是a,即输入的神经元激活值,维数为K_×N_,top_diff对应的是delta,维数是M_×N_,而caffe_cpu_gemm函数是对blas中的函数进行封装,实现了一个N_×M_的矩阵与一个M_×K_的矩阵相乘(注意此处乘之前对top_diff进行了转置)。相乘得到的结果保存于blobs_[0]->mutable_cpu_diff(),对应dW。
2. 这一句是为了计算db,对应公式为
caffe_cpu_gemv函数实现了一个M_×N_的矩阵与N_×1的向量进行乘积,其实主要实现的是对delta进行了一下转置,就得到了db的值,保存于blobs_[1]->mutable_cpu_diff()中。此处的与bias_multiplier_.cpu_data()相乘是实现对M_个样本求和,bias_multiplier_.cpu_data()是全1向量,从公式上看应该是取平均的,但是从loss传过来时已经取过平均了,此处直接求和即可。(感谢@孙琳钧和@辛淼同学的提醒)
3. 这一句是为了利用后面层传过来的delta_l+1计算本层的delta_l,对应公式为
主要Inner_product层里面并没有激活函数,因此没有乘f’,与f’的相乘写在ReLU层的Backward函数里了,因此这一句里只有W和delta_l+1相乘。blobs_[0]->cpu_data()对应W,维度是N_×K_,bottom[0]->mutable_cpu_diff()是本层的delta_l,维度是M_×K_。
写了这么多,Backward_cpu函数终于结束了。但是更新其实没结束,我最初看源码时就觉得奇怪,因为Backward_cpu函数里只计算了dW,db,delta,并没有对W和b进行更新呀?后来才发现,其实caffe里的反向传播过程只是计算每层的梯度的导,把所有层都计算完之后,在solver.cpp里面统一对整个网络进行了更新。具体是在step函数里先通过ComputeUpdateValue把learning rate、momentum、weight_decay什么的都算好,然后调用了Net.cpp的update函数逐层更新,对应公式就是:
以上基本就是整个全连接层的更新过程,卷积层的过程类似。(不过我当初看源码的时候偷懒,卷积层的没认真看,我打算借这个机会回去仔细看一下,过几天回来再写^_^)
如果哪些地方写的不对,欢迎同学们指正~
cjwdeq
赞同来自: xinmiao 、uestc_yang 、ruirui_ICT 、shakevincent 、attitude 、yxyuni 、lady_he 、好兵兵啊 、popper0912更多 »
参数符号说明如下:
1.进行前馈传导计算,利用前向传导公式,得到L2,L3直到输出层Lnl的激活值。
2.对于第nl层(输出层)的每个输出单元i,我们根据以下公式计算残差:
其中求和符号最后没有的原因是因为这一步是输出层的输出单元,输出层与标签只有一个连接,而不是像隐层一样每个神经元与后面隐层几个连接,所以反向回来时候输出层就算求和也是一个连接的传递。
3.对l = nl-1, nl-2, nl-3,的各个层,第l层的第i个节点的残差计算方法如下:
这里一步步求导就可以了,跟上面过程类似。
将上式中的nl-1与nl的关系替换为l与l+1的关系,就可以得到:
以上逐次从后向前求导的过程即为“反向传导”的本意所在。
4.计算我们需要的偏导数,计算方法如下:
随后按照如下公式对参数W和b进行更新:
所以批量梯度下降算法,一次迭代如下:
这大概就是整个过程。可以看成最后一层的残差反传其实是有监督的,而其它层的可以说是无监督的。我个人理解如上,不知道能不能解决你的问题。
0布点0 - 图像处理新手
赞同来自:
王蒙蒙 - CaffeCN社区志愿者
赞同来自: