对于caffe BN代码分析
其实主要是为了先读一读代码,然后改一改,所以为了读这个代码,首先要掌握两个基本内容:BN的算法流程以及BLAS基本知识。BN算法流程见参考文献[1 2]。BLAS的基本知识的索引见上一篇博客。
bn_layer.cpp
对于bn层来说,需要reshape的数据是顶层的输出数据还有中间的存储数据。
其中有两个stastic是原blob的求和的结果,其中首先计算后两个维度到spatial_static,之后计算第一个维度batch_statistic。
其中buffer是为了临时存储用的。
其中的top是输出的BN之后的结果。
x_norm 是存储的正则化之后的结果。
Std是标准差的记录。
Sum是为了求和用的求和数据,其中都是1.
BN算法的第一步首先就是计算均值,均值最后的向量结果是一个长度为channel的向量。
首先是gemv,即对于矩阵和向量进行乘法运算,最后结果的大小为num*channels,向量的每一个元素是height*width个元素的乘积。为了进行归一化,前面的系数是1/元素个数。第一个矩阵是原始的bottom_da
计算过第3、4个维度之后,我们要计算第一个维度,但是带来的一个问题就是存储方式是不一样的啊,这怎么办啊。按照内存的存储方式,原来的计算就是顺序的元素进行读取然后乘积即可,但是这时候这个元素带来的一个问题就是我们要进行乘法的元素是第一个维度,这在内存中是跳跃的,于是就在计算gemv的前面加上了cblastrans这个参数来进行转置,之后计算出来的结果就是长度为channel的一个向量。存储在batch_statistic中。
计算之后,如果是训练过程中,那么用decay进行update,如果是实际运行过程中,那么只需要拷贝到blobs[2]中即可。
计算平均值这个结果之后,我们就需要对于原始数据进行处理。
首先是矩阵乘矩阵。我们要做的是需要把batch_statistic这个只有两个维度的向量扩充成四个维度,这个时候采用矩阵乘法进行计算。三个参数num channels 和1分别代表,最后的矩阵相乘的row和col,中间消耗的一个维度的长度是1,这也就意味着第一个矩阵的维度是num×1,第二个矩阵的维度是1×channels,所以每次计算的时候需要增加多少也就知道了。第一个gemm(batch_sum,batch_statistics)之后,矩阵变成了num×channel大小。
第二个gemm之后,矩阵变成了num×channel×height×width大小。
最后进行四维矩阵的减法存储在top_da
第一个caffe_powx的目的实际上是将最后的top_data的结果平方之后存储在buffer_blob_中。
通常blas函数的第一个参数是计算的数量的多少,最后一个是存储的位置,中间的是计算的变量。
第二步骤是对于每一个sample的每一个channel的平方求平均。
第三步骤的结果是每个channel的平均值。
之后就是通常的处理即提升维度然后做除法,最后得到的就是标准化之后的结果。
Scale是对于原始数据的拉伸,shift是对于原始数据的偏移的移动。
Scale中,scale_data是channels长度的向量,所以乘法之后相当于一个全都是scale为数据的num×channels的矩阵。之后变成num×channels×height×width大小的矩阵。
Mul操作是对于原始的数据进行操作,而且是主元素的操作,最后相乘的结果存储在top_data中。
Shift操作类似,不过最后是加法操作。
以上为FORWARD_CPU。
参考资料:
http://arxiv.org/pdf/1502.03167v3.pdf
论文
http://blog.csdn.net/hjimce/article/details/50866313
很好的解析!