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的后向传播结束。