caffe源碼解析之blob(1)

看過caffe官方文檔的話,應該會知道,它可以分爲三層:Blob、Layer、Net。Blob是一個四維的數組,用於存儲數據,包括輸入數據、輸出數據、權值等等;Layer層則是神經網絡中具體的各層結構,主要是計算的作用,在根據配置文件初始化結構後,前向計算結果,反向更新參數,都是它要做的,而它的輸入和輸出都是Blob數據;Net的話,就是多個Layer組合而成的有向無環圖結構,也就是具體的網絡了。Layer和Net的代碼有待深入,尤其是Layer的代碼,caffe實現了差不多40種不同的Layer層,裏面有不同的激活函數,這個要好好研究下。

Blob源碼解析

#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/syncedmem.hpp"
#include "caffe/util/math_functions.hpp"

blob.hpp包含的四個頭文件入手,其中caffe.pb.h是google protocol buffer根據caffe.proto自動生成的,可以到src/caffe/proto/caffe.proto裏看下caffe裏面用到的各個數據的定義,比如BlobProtoDatumNetParameter等。使用這個protocol buffer看起來確實方便,一方面可以用文本文件定義結構化的數據類型,另一方面可以生成查詢效率更高、佔空間更小的二進制文件,具體的教程可以看看這裏

caffe/common.hpp,主要singleton化Caffe類,並封裝了boost和CUDA隨機數生成的函數,提供了統一的接口。而在caffe/syncedmem.hpp中,定義了以下的接口:

inline void CaffeMallocHost(void** ptr, size_t size)
inline void CaffeFreeHost(void* ptr)

主要是分配內存和釋放內存的。而class SyncedMemory定義了內存分配管理和CPU與GPU之間同步的函數,也沒啥特別的。

比較重要的是caffe/util/math_functions.hpp,這裏面封裝了很多cblas矩陣運算,真是密密麻麻,看的我眼花繚亂、如癡如醉。比如:

void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA, const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const float alpha, const float* A, const float* B, const float beta, float* C)

封裝了cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B, ldb, beta, C, N),這個計算得到的結果爲C=alphaAB+beta*C,也即是A和B兩個矩陣的乘積。這裏有詳細的解釋。

void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,  const int N, const float alpha, const float* A, const float* x, const float beta, float* y)

是對cblas_sgemv的封裝,實現的矩陣與向量的乘積,結果爲y=alphaAx+beta*y。

void caffe_axpy<float>(const int N, const float alpha, const float* X, float* Y)

封裝了cblas_saxpy,實現的是Y=alpha*X+Y

裏面都是諸如此類的函數,基本是些矩陣和向量的一些處理函數。

回到blob類,裏面定義了data_(),diff_()指針,用於存放數據,而num_channel_,height_width_則主要用來做定位offsetreshape處理。對於輸入(n, c, h, w)位置的數據位置爲((n*channels_+c)*height_+h)*width_+w,可以依據位置取data_()diff_()中的數據。

對blob的理解還要結合caffe.proto裏面BlobProto的定義:

message BlobProto {
   optional int32 num = 1 [default = 0];
   optional int32 channels = 2 [default = 0];
   optional int32 height = 3 [default = 0];
   optional int32 width = 4 [default = 0];
   repeated float data = 5 [packed = true];
   repeated float diff = 6 [packed = true];
}

對於BlobProto,可以看到定義了四個optionalint32類型的名字(name)numchannelsheightwidthoptional意味着Blob可以有一個或者沒有這個參數,每個名字(name)後面都有一個數字,這個數字是其名字的一個標籤。這個數字就是用來在生成的二進制文件中搜索查詢的標籤(怪不得會快呢^_^)。關於這個數字,1到15會花費1byte的編碼空間,16到2047花費2byte。所以一般建議把那些頻繁使用的名字的標籤設爲1到15之間的值~而後面的repeated意味着float類型的datadiff可以重複任意次,而加上[packed = true]是爲了更高效的編碼。

到這裏基本上Blob就很清楚了,主要數據有兩個datadiff,用numchannelsheightwidth這四個維度來確定數據的具體位置,做一些數據查詢和Blobreshape的操作。

關於Blob就這麼多內容,畢竟就是一個統一的數據存取接口,後續會重點讀一下Layer的源碼,畢竟各層的輸入輸出和計算更新過程都在裏面,還需要補充一些相關的知識~~

目前的感受,是學到了一些封裝的手法,可以看看封裝cblas函數的那個文件,以及CPU和GPU一些接口的封裝上;另一方面是對於Protocol Buffer有了一些瞭解,目前看起來確實方便,以後如果遇到類似的場景可以試着用一下~~


轉載自http://www.shwley.com/index.php/archives/64/

發佈了17 篇原創文章 · 獲贊 57 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章