caffe:debug之反向传播(以Lenet为例,接上篇前向传播)
本文接第一次处理TestAll,在退出TestAll后,即开始正式迭代训练网络。
step中继续执行其他过程 然后进入ForwardBackward()函数
上述函数定义在net.hpp中
其中Forward函数即在testAll中提到的net.cpp的Forward函数
Forward详细说明以及步骤都在TestAll中讲解完毕。
其一次结果为:
接下来开始执行Backword函数执行后向传播
Backword(net.cpp)
---------->BackwordFromTo(net.cpp)
在BackwordFromTo中执行Backward函数开始后向传播
---------->Backward(layer.hpp)
---------->Backward_cpu(softmax_loss_layer.cpp)
其中对于
求导公式解释如下
/*
softmaxLoss的梯度计算结果最后可以化简为qi - pi (qi是softmax对zi的偏导,pi是label的值 取 0 或 1)
详见Deep Learning P221 6.5.9 Differentiation outside the Deep Learning Community
For example, suppose we have variables p1,p2,...,pn representing probabilities and variables z1 , z2 , . . . , zn representing unnormalized log probabilities. Suppose
...
construct a cross-entropy loss J = − sum( pi log qi). A human
mathematician can observe that the derivative of J with respect to zi takes a very
simple form: qi − pi .
知道这个trick之后 红框的表达式就不难理解了
bottom_diff[i * dim + label_value * inner_num + j] -= 1 前面还有一句 caffe_copy(prob_.count(), prob_data, bottom_diff);
首先向bottom_diff拷入prob_data, 也就是softmax输出的qi
dim : 表示一个num/batch里一个样本的大小,为 channel * Height * Width
inner_num:表示一个特征图/channel/一个label的大小,为Height * Width,这里就是1*1了
label_val = label[i * inner_num_ + j] 就是 p[label_val] = 1 (只有一个) p[others_label_val] = 0
i * dim + label_value * inner_num + j 就是一个样本里,对应label_val的那个channel的index
对这个index执行 q[index] -= p[label_val]
对这个样本里的其它channel的index默认执行q[index] -= p[other_label_val]
*/
言归正传,执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续
继续后向传播,计算前一层。进入Backward函数中
---------->Backward_cpu(inner_product_layer.cpp)
此函数进行一系列计算,计算内积,即相乘,使用BLAS,最终求出梯度
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
---------->Backward_cpu(relu_layer.cpp)
relu计算误差
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
-----------> Backward_cpu(inner_product_layer.cpp)
再次全连接层做内积
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
-----------> Backward_cpu(pooling_layer.cpp)
继续传播误差,传播梯度
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
-----------> Backward_cpu(conv_layer.cpp)
在conv_layer中实现了权重偏置数据的反传,同样还是对矩阵进行相乘操作来求导。
同样,其中的实现函数都被封装在了base_conv_layer.cpp中。
如
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
-----------> Backward_cpu(pooling_layer.cpp)
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
-----------> Backward_cpu(conv_layer.cpp)
执行完此函数后回到net.cpp的BackwardFromTo函数,然后继续后向传播,计算前一层。进入Backward函数中
至此 backward(layer.cpp)函数结束
回到net.cpp的Backward函数中
并返回损失。
至此,一次LeNet的后向传播结束。