深度學習框架(一):AI 模型是如何訓練出來的。

本系列專題以百度 PaddlePaddle《飛槳》開源深度學習框架為例,深入淺出,介紹下深度學習框架的底層計算原理及實現《Python 前端和 C++ 後端》。

深度學習框架到底能幹什麼

首先,我們用 tensorflow、pytorch 或者 paddlepaddle 寫一段 python 代碼來組建一個神經網路模型,然後訓練,達到一定精度後再保存模型,最後基於訓練好的模型做圖像識別、語音識別等任務。這個流程想必大家都很清楚了,那這一切都是誰來計算的呢?答案是後臺框架,你所寫的 python 代碼隻不過是前端 API,真正調用的是後端 C 或 C++ 計算邏輯,而前端 python API 和 後端計算邏輯正是通過 pybind 綁定的。

深度學習框架最基本的功能就是要能提供一系列算子《俗稱『OP』,operator 的簡稱》,支持前向計算和反向梯度更新。如此說來,框架應該很簡單呀。實則不然,且聽我細細道來。首先,這些 OP 數量很大,比如卷積、全連接、各種激活函數《如 Relu、Sigmoid》、各種梯度更新算法《如 Adam、RMS》等等。其次,組建神經網路模型時,需要提供靜態圖模式《聲明式編程》和動態圖模式《函數式編程》。動態圖好理解,就是我們平時寫代碼的邏輯,do A -> do B -> do C,按流程順序執行任務,每寫完一行代碼就能得到相應的結果。而靜態圖就不大好理解了,用戶所寫的代碼僅僅是為了構建一張圖,圖構建完之後再去執行,執行完才能得到結果,而不像動態圖那樣能實時獲取結果。靜態圖這種方式有什麼好處呢?答案是便於做性能優化,通過優化這張圖的結構,使得程序執行效率更高。靜態圖中的『圖』,又叫做 SSA Graph,Directed Acyclic Single Static Assignment Graph《有向無環靜態單賦值圖》,那這張圖是如何構建出來的呢?如何去描述?如何把它序列化成二進制字節流在不同進程間傳遞呢?如何執行的?又是如何優化的呢?

還有,樣本數據該如何存儲呢?內存?緩存?SSD?還是哈希表,這些存儲資源該怎麼管理呢?原始樣本文件又如何轉化成模型『認識』的 feed data 呢?

另外,模型如何保存呢?保存完整模型還是保存部分模型《比如隻保留模型最後兩層》?模型又是如何加載呢?從頭開始訓練《冷啟動》和增量訓練《熱啟動》又有什麼區別?

更重要的是,隨著模型越來越大,參數規模達到百億、千億,甚至萬億,對模型的訓練性能提出了非常高的要求,高性能的訓練框架不僅能大大縮短訓練時間,同時大大節約硬件資源,這可是實打實的真金白銀呀。另外,在推薦領域,All is Embedding,大規模稀疏參數需要大量的存儲空間,單機無法容納,需要借助分佈式文件系統。於是,參數服務器《Parameter Server》這一解決方案應運而生。

考慮到不同廠家的各類 AI 芯片,如英偉達的 GPU、華為的昇騰、百度的昆侖等芯片,想要充分利用這些高性能 AI 硬件的能力,軟件必須得兼容這些硬件,而它們的編程語法,編譯方法和英特爾的 x86 CPU 又不一樣,比如 cuda 編程等,硬件之間又涉及到通信問題,如 nccl。自然地,CPU 參數服務器進化成了異構參數服務器。參數服務器系統中又牽扯出了各式各樣的並行優化策略,如數據並行、模型並行、流水線並行、混合並行、自動並行等,數據和模型中的各個層《layer》要如何切分,才能充分利用硬件資源呢?不僅如此,分佈式系統中的通信算法,如 gloo、mpi 等,不同進程間又涉及到同步問題,如 barrier 算法。

從應用層面看,還包括基於 k8s、docker、yarn等訓練資源調度系統的搭建、實現基於 C++、Java、Go 等主流語言的線上低時延,高吞吐推理預測服務、終端低功耗的 PaddleLite 解決方案等。一款合格的深度學習訓練框架應該是工業級的產品,具備高性能、支持各類 AI 硬件、能大規模部署、能支持所有 AI 模型,API 還必須得是用戶友好的。