庫名稱 | 開發語言 | 支持接口 | 安裝難度(ubuntu) | 文檔風格 | 示例 | 支持模型 | 上手難易 |
---|---|---|---|---|---|---|---|
Caffe | c++/cuda | c++/python/matlab | *** | * | *** | CNN | ** |
MXNet | c++/cuda | python/R/Julia | ** | *** | ** | CNN/RNN | * |
TensorFlow | c++/cuda/python | c++/python | * | ** | * | CNN/RNN/… | *** |
- 安裝難度: (簡單) –> **(復雜)
- 文檔風格: (一般) –> **(好看、全面)
- 示例: (給的少) –> **(給的多、全)
- 上手難易: (易) –> **(難)
1.基本數據結構
庫名稱 | 數據結構名稱 | 設計方式 |
---|---|---|
Caffe | Blob | 存儲的數據可以看成N維的c數組,有(n,k,h,w)四個維數,一個blob里面有兩塊數據空間保存前向和后向求導數據 |
MXNet | NDArray | 提供cpu/gpu的矩陣和矢量計算,能夠自動並行 |
TensorFlow | tensor | 相當於N維的array或者list,維數可變,數據類型一旦定義不能改變 |
caffe的數據存儲類blob,當把數據可以看成是一個N維的c數組,它們的存儲空間連續。例如存儲圖片是4維(num, channel, height, width),變量(n,k,h,w)在數組中存儲位置為((n*K+k)*H+h)*W+w。blob有以下三個特征[4]:
- 兩塊數據,一個是原始data,一個是求導值diff
- 兩種內存分配方式,一種是分配在cpu上,一種是分配在gpu上,通過前綴cpu、gpu來區分
- 兩種訪問方式,一種是不能改變數據,一種能改變數據
Caffe最讓我覺得精妙的地方在於一個blob保存前向和后向的數據。雖然就代碼本身而言,前向數據是因為輸入數據不同而改變,后向求導是因為求導不同而改變,根據SRP原則,在一個類里面因為兩個原因而改變了數據這種是不合適的設計。但是從邏輯層面,前向數據的改變引起了反向求導的不同,它們實際上是一起在改變,本身應該是一個整體。所以我很喜歡這個設計,雖然基本上其他框架中都是將兩個數據給分離出來,caffe2也不知是否保留。
MXNet的NDArray類似numpy.ndarray,也支持把數據分配在gpu或者cpu上進行運算。但是與numpy和caffe不同的是,當在操作NDArray,它能自動的將需要執行的數據分配到多台gpu和cpu上進行計算,從而完成高速並行。在調用者的眼中代碼可能只是一個單線程的,數據只是分配到了一塊內存中,但是背后執行的過程實際上是並行的。MXnet在這分布式計算這點上做的比較好,可以充分利用多GPU資源。將指令(加減等)放入中間引擎,然后引擎來評估哪些數據有依賴關系,哪些能並行處理。定義好數據之后將它綁定到網絡中就能處理它了。
TensorFlow的tensor,它相當於N維的array或者list,與MXNet類似,都是采用了以python調用的形式展現出來。某個定義好的tensor的數據類型是不變的,但是維數可以動態改變。用tensor rank和TensorShape來表示它的維數(例如rank為2可以看成矩陣,rank為1可以看成向量)。tensor是個比較中規中矩的類型。唯一特別的地方在於在TensorFlow構成的網絡中,tensor是唯一能夠傳遞的類型,而類似於array、list這種不能當成輸入。
值得一提的是cuda-convnet采用的數據結構是NVMatrix,NV表示數據分配在gpu上,即將所有變量都當成矩陣來處理,它只有兩維,它算是最早用cuda實現的深度學習框架,而上面三種框架都采用了多維可變維的思想,這種可變維在用矩陣做卷積運算的時候是很有效的。
2.網絡實現方式
Caffe是典型的功能(過程)計算方式,它首先按照每一個大功能(可視化、損失函數、非線性激勵、數據層)將功能分類並針對部分功能實現相應的父類,再將具體的功能實現成子類,或者直接繼承Layer類,從而形成了XXXLayer的形式。然后將不同的layer組合起來就成了net。
MXNet是符號計算和過程計算混合[5],它設計了Symbol大類,提供了很多符號運算的接口,每個symbol定義了對數據進行怎樣的處理,symbol只是定義處理的方式,這步還並未真正的執行運算。其中一個需要注意的是symbol里面有Variable,它作為承載數據的符號,定義了需要傳遞什么樣的數據給某個Variable,並在后續的操作中將數據綁定到Variable上。下面的代碼是一個使用示例,它實現了將激勵函數連接到前面定義好的net后面,並給出了這一個symbol的名字和激勵函數類型,從而構造出net。下圖左邊部分是定義symbol的合集,中間將數據綁定到Variable上之后變成了右邊真正的執行流程圖。
net = mx.symbol.Activation(data=net, name='relu1', act_type="relu")
TensorFlow選擇的是符號計算方式,它的程序分為計算構造階段和執行階段,構造階段是構造出computation graph,computation graph就是包含一系列符號操作Operation和Tensor數據對象的流程圖,跟mxnet的symbol類似,它定義好了如何進行計算(加減乘除等)、數據通過不同計算的順序(也就是flow,數據在符號操作之間流動的感覺)。但是暫時並不讀取輸入來計算獲得輸出,而是由后面的執行階段啟動session的run來執行已經定義好的graph。這樣的方式跟mxnet很相似,應該都是借鑒了theano的想法。其中TensorFlow還引入了Variable類型,它不像mxnet的Variable屬於symbol(tf的operation類似mxnet的symbol),而是一個單獨的類型,主要作用是存儲網絡權重參數,從而能夠在運行過程中動態改變。tf將每一個操作抽象成了一個符號Operation,它能夠讀取0個或者多個Tensor對象作為輸入(輸出),操作內容包括基本的數學運算、支持reduce、segment(對tensor中部分進行運算。例如tensor長度為10,可以同時計算前5個,中間2個,后面三個的和)、對image的resize、pad、crop、filpping、transposing等。tf沒有像mxnet那樣給出很好的圖形解釋或者實例(可能因為我沒找到。。),按照自己的理解畫了一部分流程圖。有點疑惑的是,為什么要設計Variable,tf給出的一個alexnet的example源碼中,輸入數據和權重都設置成了Variable,每一層的輸出並未直接定義,按照tf的說法,只有tensor類型能夠在網絡中傳遞,輸出的類型應該是tensor,但是由於輸入和權重改變了,輸出應該也在隨着改變,既然如此,為何不只設計一個tensor,讓tensor也能動態改變。
就設計而言,TensorFlow相對於其他兩個更像是一種通用的機器學習框架,而不是只針對cnn或rnn,但就現在的性能而言,tf的速度比很多開源框架都要差一點[6]。
3.分布式訓練
Caffe和TensorFlow沒有給出分布式的版本,MXNet提供了多機分布式,因而前兩者只有如何控制使用多gpu。Caffe通過直接在執行指令后面加上-gpu 0,1來表示調用兩個gpu0和1,只實現了數據並行,也就是在不同的gpu上執行相同網絡和不同數據,caffe會實例化多個solver和net讓每次處理的batch_size加倍。TensorFlow則能夠自己定義某個操作執行在哪個gpu上,通過調用with tf.device(‘/gpu:2’)表示接下來的操作要在gpu2上處理,它也是數據並行。MXNet通過執行腳本時指定多機節點個數來確定在幾台主機上運行,也是數據並行。MXNet的多gpu分配和它們之間數據同步是通過MXNet的數據同步控制KVStore來完成的。
KVStore的使用首先要創建一個kv空間,這個空間用來在不同gpu不同主機間分享數據,最基本的操作是push和pull,push是把數據放入這個空間,pull是從這個空間取數據。這個空間內保存的是key-value([int, NDArray]),在push/pull的時候來指定到哪個key。下面的代碼將不同的設備上分配的b[i]通過key3在kv空間累加再輸出到a,從而完成了對多gpu的處理。這個是個非常棒的設計,提供了很大的自由度,並且為開發者減少了控制底層數據傳輸的麻煩。
gpus = [mx.gpu(i) for i in range(4)]
b = [mx.nd.ones(shape, gpu) for gpu in gpus]
kv.push(3, b)
kv.pull(3, out = a)
之前有看過一篇論文,如何將卷積網絡放在多gpu上訓練,論文中有兩種方法,一種是常用的數據並行,另一種是模型並行。模型並行指的是將一個完整的網絡切分成不同塊放在不同gpu上執行,每個gpu可能只處理某一張圖的四分之一。采用模型並行很大程度上是因為顯存不夠放不下整個網絡的數據,而現在gpu的功能性能提高,一個gpu已經能夠很好的解決顯存不夠的問題,再加上模型並行會有額外的通信開銷,因此開源框架采用了數據並行,用來提高並行度。
4.小結
上面針對三個框架的不同方面進行了一些分析與比較,可以看出TensorFlow和MXNet有一些相似的地方,都是想做成更加通用的深度學習框架,貌似caffe2也會采用符號計算[5],說明以后的框架會更加的偏向通用性和高效,個人最喜歡的是caffe,也仿造它和cuda-convnet的結構寫過卷積網絡,如果是想提高編程能力可以多看看這兩個框架的源碼。而MXNet給人的感覺是非常用心,更加注重高效,文檔也非常的詳細,不僅上手很容易,運用也非常的靈活。TensorFlow則是功能很齊全,能夠搭建的網絡更豐富而不是像caffe僅僅局限在CNN。總之框架都是各有千秋,如何選擇也僅憑個人的喜好,然而google這個大殺器一出現引起的關注度還是最大的,雖然現在單機性能還不夠好,但是看着長長的開發人員名單,也只能說大牛多就是任性。
參考:
[2] http://mxnet.readthedocs.org/en/latest/index.html
[3] http://caffe.berkeleyvision.org/
[6] Imagenet Winners Benchmarking