函数求值需要运行所有线程_精读《深度学习 - 函数式之美》
1 引言
函數式語言在深度學習領域應用很廣泛,因為函數式與深度學習模型的契合度很高,The Beauty of Functional Languages in Deep Learning?—?Clojure and Haskell 就很好的詮釋了這個道理。
通過這篇文章可以加深我們對深度學習與函數式編程的理解。
2 概述與精讀
深度學習是機器學習中基于人工神經網絡模型的一個分支,通過模擬多層神經元的自編碼神經網絡,將特征逐步抽象化,這需要多維度、大數據量的輸入。TensorFlow 和 PyTorch 是比較著名的 Python 深度學習框架,同樣 Keras 在 R 語言中也很著名。然而在生產環境中,基于 性能和安全性 的考慮,一般會使用函數式語言 Clojure 或 Haskell。
在生產環境中,可能要并發出里幾百萬個參數,因此面臨的挑戰是:如何高效、安全的執行這些運算。
所以為什么函數式編程語言可以勝任深度學習的計算要求呢? 深度學習的計算模型本質上是數學模型,而數學模型本質上和函數式編程思路是一致的:數據不可變且函數間可以任意組合。這意味著使用函數式編程語言可以更好的表達深度學習的計算過程,因此更容易理解與維護,同時函數式語言內置的 Immutable 數據結構也保障了并發的安全性。
另外函數式語言的函數之間都是相互隔離的,即便在多線程環境下也不會發生競爭和死鎖的情況,函數式編程語言會自動處理這些情況。
比如說 Clojure,它甚至可在兩個同時修改同一引用的程序并發運行時,自動重試其中之一,而不需要手動加鎖:
(import ‘(java.util.concurrent Executors)) (defn test-stm [nitems nthreads niters](let [refs (map ref (repeat nitems 0))pool (Executors/newFixedThreadPool nthreads)tasks (map (fn [t](fn [](dotimes [n niters](dosync(doseq [r refs](alter r + 1 t))))))(range nthreads))](doseq [future (.invokeAll pool tasks)](.get future))(.shutdown pool)(map deref refs))) (test-stm 10 10 10000) -> (550000 550000 550000 550000 550000 550000 550000 550000 550000 550000)上面的代碼創建了引用(refs),同時創建了多個線程自增這個引用對象,按理說每個線程都修改這個引用會導致競爭狀態出現,但從結果來看是正常的,說明 Clojure 引擎在執行時會自動解決這個問題。實際上當兩個線程出現競爭而失敗時,Clojure 會自動重試其中之一。
原文介紹Clojure 的另一個優勢是并行效率高:
(defn calculate-pixels-2 [](let [n (* *width* *height*)work (partition (/ n 16) (range 0 n))result (pmap (fn [x](doall (map(fn [p](let [row (rem p *width*) col (int (/ p *height*))](get-color (process-pixel (/ row (double *width*)) (/ col (double *height*))))))x)))work)](doall (apply concat result))))使用 partition 結合 pmap 可以使并發效率達到最大化,也就是 CPU 幾乎都消耗在實際計算上,而不是并行的任務管理與上下文切換。Clojure 憑借 partition 對計算進行分區,采取分而治之并對分區計算結果進行合并的思路優化了并發性能。
原文介紹Clojure 另一個特性是函數鏈式調用:
;; pipe arg to function (-> "x" f1) ; "x1";; pipe. function chaining (-> "x" f1 f2) ; "x12"其中 (-> "x" f1 f2) 等價于 f2(f1("x")),這種描述不僅更簡潔清晰,也更接近于實際數學模型。
原文介紹最后,Clojure 還具備計算安全性,計算過程不會修改已有的數據,因此在神經網絡的任何一層的原始值都會保留,每層計算都可以獨立運行且函數永遠冪等。
Haskell 也有獨特的優勢,它具有類型推斷、惰性求值等特性,被認為更適合用于機器學習。
類型推斷即 Haskell 類型都是靜態的,如果試圖賦予錯誤的類型會報錯。
Haskell 的另一個優勢是可以非常清晰的描述數學模型。
想想一般數學模型是怎么描述函數的:
fn =>f1 = 1f2 = 9f3 = 16n > 2, fn = 3fn-3 + 2fn-2 + fn-1一般語言用 if-else 描述等價關系,但 Haskell 可以幾乎原汁原味的還原函數定義過程:
solve :: Int -> Interger solve 1 = 1 solve 2 = 9 solve 3 = 16 solve n = 3 * solve (n - 3) + 2 * solve (n - 2) + solve (n - 1)這使得閱讀 Haskell 代碼和閱讀數學公式一樣輕松。
原文Haskell 另一個優勢是惰性求值,即計算會在真正用到時才進行,而不會在計算前提前消費掉,比如:
let x = [1..] let y = [2,4 ..] head (tail tail( (zip x y)))可以看到,x 與 y 分別是 1,2,3,4,5,6... 與 2,4,6,8... 的無限數組,而 zip 函數將其整合為一個新數組 (1,2),(2,4),(3,6),(4,8)... 這也是無限數組,如果將 zip 函數執行完那么程序就會永遠執行下去。但 Haskell 卻不會陷入死循環,而是直接輸出第一位數字 1。這就是惰性計算的特性,無論數組有多長,只有真正用到某項時才對其進行計算,所以哪怕初始數據量或計算量很大,實際消耗的運算資源只取決于這次計算實際用到的部分。
由于深度學習數據量巨大,惰性求值可以忽略海量數據輸入,大大提升計算性能。
3 總結
本文介紹了為什么深度學習更適合使用函數式語言,以及介紹了 Clojure 與 Haskell 語言的共性:安全性、高性能,以及各自獨有的特性,證明了為何這兩種語言更適合用在深度學習中。
在前端領域說到函數式或函數之美,大部分時候想到的是 Class Component 與 Function Component 的關系,這個理解是較為片面的。通過本文我們可以了解到,函數式的思想與數學表達式思想如出一轍,以寫數學公式的思維方式寫代碼,就是一種較好的函數式編程思路。
函數式應該只有表達式,沒有語句,這是因為函數式是為了處理運算而誕生的,因此很適合用在深度學習領域。
討論地址是:精讀《深度學習 - 函數式之美》 · Issue #212 · dt-fe/weekly如果你想參與討論,請 點擊這里,每周都有新的主題,周末或周一發布。前端精讀 - 幫你篩選靠譜的內容。
關注 前端精讀微信公眾號版權聲明:自由轉載-非商用-非衍生-保持署名(創意共享 3.0 許可證)總結
以上是生活随笔為你收集整理的函数求值需要运行所有线程_精读《深度学习 - 函数式之美》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【二叉树】美团的【天天领现金】活动,不就
- 下一篇: 哈理工oj 1006 River Hop