最近一直在研究caffe源码,将一些心得体会记录下来,以便以后查阅。
首先记录一个c++ template的知识,caffe大量使用了template,但貌似只支持float与double类型的数据。定义了template的函数,如果在.h文件中声明,在cpp文件中定义的话,在其他的文件include头文件之后再调用此函数,会出现无法解析的外部符号的错误,这是由于template的函数定义与声明应该放在一个文件中进行,不要像我们写其他代码的时候习惯性的建立两个文件,因为在你的调用文件中include了头文件,但头文件中只有template函数的声明,template比较特殊,只有实例化之后链接器才能找到其位置从而进行调用,所以我们只include头文件,链接器还是不知道这个函数在哪里,具体怎么执行。所以template函数定义与声明都放在一个文件下就可以了。这样链接器不需要去其他的c文件编译出来的obj中找函数,所以就不会出现错误。(因为这个函数就在你这个文件中,不需要链接其他的)。但是如果我们觉得写在一个文件中封装的感觉就没有了,还是想分别建立h与cpp文件,其实也是可以的,就是在cpp文件中对template函数显示实例化,具体就是给template一个具体的类型(float,int等等),而调用它。这样编译器在编译这个cpp的时候就知道template的函数的具体定义了,链接器也能找到它。
所以,记录的第一个知识点就是显示实例化,caffe中叫做//Explicit instantiation
第二个知识点是convlution_layer的实现过程,caffe中不是单纯的做卷积运算,而是先把要做卷积的图像执行im2col函数(应该是跟matlab学的),将图像转化为很大的一个矩阵,跟filter直接做内积,来得到卷积结果,这种方式虽然将本来占用空间不大的图像变成了很大的矩阵(因为这个矩阵有很多重复的数,卷积每走一步,还是会有很多重复的点参与运算,自己考虑),内存占用大了,但是代码看起来会比较清晰,速度也能提高(因为调用了矩阵乘法的加速库blas等等)。比如以下code:
先执行了im2col函数,然后执行caffe_cpu_gemm就是矩阵乘加,1.是alpha,0.是peta,这个函数的意义是最后一个参数等于alpha*weights*col_buff+peta*output,当然都是矩阵的首指针。weight就是卷积核的权重,col_buff就是将输入图像变形了的矩阵。cpu_data与mutable_cpu_data函数可以理解为把caffe中封装的blob类型的变量取址,赋给我们需要用的变量参与运算(如float*,double*等)。加个mutable就相当于我们要改变这个buffer中的值,比如output,不加就是只读取这个buffer的值让他参与运算,并不改变它的值,如input和filter weight。
以后还会有很多知识点的记录,争取将caffe中比较重要的知识都涵盖到