這篇其實跟使用MXnet的關系不大,但對於我們理解深度學習的框架設計還是很有幫助的。
首先還是對promgramming models的一個簡單介紹,這個東西實際上是在編譯里面經常出現的東西,我們在編譯我們的程序的時候,可以對變量構建出一個計算圖,然后可以對這個圖進行相應的優化來提高速度或者節省內存。到了DL框架上,這些用處就更加重要了,但是也不是所有的DL框架都有計算圖的,因為這其中存在一個research和engineering的權衡。計算圖的簡單理解就是下圖:

一、Symbolic vs. Imperative Programs
首先要說的就是符號式程序和命令式程序等區別了,類似於程序語言設計中的區別,能否在程序代碼執行的過程中,能否方便直接的進行修改、分支選擇或者循環以及得到中間的輸出結果等操作,是區分符號式和命令式的關鍵,當然這些只是我個人的直觀理解。。。。稍微正式點就是:Most symbolic-style programs contain, either explicitly or implicitly, a compile step. This converts the computation graph into a function that can be called. Computation occurs in the last step in the code. The major characteristic of symbolic programs is the clear separation between defining the computation graph and compiling.然后imperative就是你怎么寫它怎么跑,你什么時候寫好了讓他跑,他就什么時候開始跑。
符號式的設計方法有很多好處,tenserflow和MXnet都是基於這種方法編寫的,首先因為它們都可以構建出計算圖,這樣以來就可以對計算圖進行優化,通過dependency的分析等等,可以大大的提高算法訓練的速度和減少內存的需要,甚至於TQ最新的那篇paper,通過手動置頂一些mirror來保存forward的時候的部分feature map而不是全部,最終resnet152訓練要求的48G顯存降到了6G,而且速度上並沒有掉太多(我個人實際使用的時候,可能是因為miroor設置的不好,降了很多速度。。),但是符號式的缺點也很明顯,它給使用者的權限太少了,因為算法在訓練的時候,是在定義好的symbil的基礎上進行自己的優化,然后傳入data開始訓練,然后你想在里面做一些奇葩的操作例如循環,if之類的,基本是不可能的。。
所以在我們做research的時候,專注點在於算法的性能而不是效率低時候,可能torch和chaniner這種 imperative-style program是個不錯的選擇,在torch框架上,你可以很容易的做出一些mxnet很難進行的動作,但是因為我個人暫時也不是很熟悉它們,而且我們的重點還是mxnet,所以我也不強行講了。下面是一個簡單的例子,我們在imperative program可以容易的寫出來,但是在符號式的卻很難。
a = 2 b = a + 1 d = np.zeros(10) for i in range(d): d += np.zeros(10)
二、粗細力度,自動求導
框架的粗粒度、細粒度和自動求導的概念之前聽過很多次了,前兩者以前是完全不懂並且覺得很高端,后者是以為是字面意思然后覺得太可怕。。。實際上粗細粒度是對框架提供的一個操作大小的描述,粗粒度操作如:FC,BN,細粒度操作如:elemenwise的sum、mul之類的。
還是拿Tenserflow和MXnet來說,前者提供了很多的底層小操作,所以它是一個細粒度的框架,這樣的好處是,使用者要想定義自己的操作的時候,就可以通過組合tenserflow里面的op來實現,大大的減小了難度,但這樣帶來的壞處就是,最終的計算圖會又大又復雜,很難去優化。這也是google背書的Tenserflow之前在性能上反而不是MXnet的原因之一的吧。 MXnet我們就很熟悉它的那些OP了,基本上都是一些粗粒度的操作,這樣我們在定義自己的OP的時候,只能去默默的寫C++代碼,個中滋味,就不多說了。。。粗粒度的操作帶來的好處當然就是性能的提升。
自動求導這個概念,簡單的理解就是,框架給你提高的OP,你只要用他們做fwd,然后backward會自動定義好,因為這些OP的BP已經有人寫好了,並且整個BP的流程也可以推到出來然后建立好。並不是你在定義自己的操作的時候,代碼能自動幫你寫這個操作的BP。
總結
我發現做系統設計這些真的很interesting,要不是我的代碼水平太弱雞,真是分分鍾鍾想去搞系統啊。。
