機器學習代碼心得之模板和張量庫
寫機器學習程序有兩類心態,一種是最大化代碼運行效率的程序員,另外一種是最求實現簡單少寫代碼的程序員。比較常見的是前者寫C++,CUDA, 后者寫matlab, python。我個人屬于前者,希望可以對代碼有最大的控制,提前分配好內存,運行中不進行內存的動態分配,如果用GPU的時候讓所有的數據都呆在顯卡上面不要出來等等。
當然,提到優化,最需要注意的是阿姆達法則(Amdahl'slaw),即如果你優化了占運行效率的1%的部分,再怎么優化也無濟于事。很多時候數據邊界檢查和必要的assert盡量加上其實很多時候不會影響效率。
現在的問題是,對于極度最求效率的C++程序員,是否可以寫出最求簡潔明快的matlab python程序員一樣的代碼呢。答案是肯定的,常見的c++庫Eigen和支持CUDA的mshadow就一定程度上提供了這樣的功能。使用張量矩陣庫的好處是可以寫出簡潔的代碼,但是這樣一來,和寫pythonmatlab又有什么樣的區別呢?
區別是存在的,利用c++ 的模板技術,寫矩陣向量代碼可以和手寫C++的函數來的一樣高效。這一類模板編程技術稱作expressiontemplate。
具體的原因可以考慮如下問題,假設A,B,C是三個向量。 應該如何實現A = B + C這樣一個操作。對于一般的矩陣庫,常見的實現是重載+這個操作,分配一個新的臨時空間用以儲存結果,然后把B+ C的結果存到那個臨時的儲存空間里面去。同樣的情況可以對應于其它各類操作如矩陣乘法。如果是在乎效率的C++程序員不會希望有這樣的臨時空間的分配,因此會直接寫類似以下代碼來解決問題:
for(int i = 0; i < A.len; ++i) {
A[ i ] = B[ i ]+ C[ i ];
}
問題是我們需要寫很多這樣的函數來對付各種的情況。而且代碼也不再像A=B+C這樣簡潔了。利用expressiontemplate的技術,可以解決這一問題。方案如下,我們還是重載operator+這個操作,但是這個操作不進行計算,而是返回一個抽象語法表達式BinaryAddExp。然后再重載等號這個操作operator=,在等號操作的似乎我們已經拿到了目標A以及B, C。可以把實現直接寫成上面的樣子。這樣我們可以高效地寫A=B+C, 并且還是對我們的內存分配有最大的控制。樣例代碼見https://github.com/tqchen/mshadow/blob/master/example/exp-template/exp_lazy.cpp
當然這樣的方案只能解決A=B+C,還有其它的問題如A = B+C+D或者W =gradient * eta + W 這樣的更新公式。這一類更長的表達式需要抽象語法表達式的嵌套,也可以通過模板來實現。具體教程見https://github.com/tqchen/mshadow/wiki/Expression-Template。
總結下來,最重要的一點是因為這些模板展開都是在編譯時執行的,使得這些復雜的組合實際上沒有消耗運行時的效率。機器學習代碼也可以既簡潔有高效,這樣的特性也只有C++模板才可以做到。對于其他語言,相對的反而不是那么自然了。
當然底層的模板庫也有一些自身的缺陷,比如不可以做遠距離的依賴分析,以及并行優化等。更加高層的張量庫如Minerva就可以完成這些事情。但是作為一個在極度追求效率和對于資源控制的C++機器學習程序里面,基于模板的矩陣庫依然是一個必不可少的組成部分。即使是最最求效率可以寫公式而不是直接調用blas函數。并使得C++的機器學習代碼也可以優雅高效。這相比于java也是一個重要的優勢。
來自:http://weibo.com/p/1001603795728785912771