深度學習軟體平臺的演化
本文整理自袁進輝(老師木)代表OneFlow團隊在2017全球網際網路架構大會上海站做了《深度學習平臺技術演進》的報告以及機器之心線下技術分享 INTERFACE 第五期,袁老師為我們分析已有框架的優缺點,分享深度學習框架應該怎麼做的觀點。
觀點
- 計算力是神經網路/深度學習復興的最大推動力之一
- 面對深度學習的計算力挑戰,軟體和硬體一樣關鍵,單靠硬體無法提供易用性和擴充套件性
- 鑑於深度學習上層業務和底層硬體的獨特性,傳統大資料平臺裡的某些技術未必對深度學習平臺適用
- 深度學習軟體平臺技術在快速演進中,一部分早期被採用的技術正在被新方法替代
- 仍有很多重要問題未被現有開源深度學習平臺解決
- 深度學習軟體尚處在發展早期,百花齊放,百家爭鳴,但必將收斂到一種業界公認的最佳實踐(best practice)
概念理解
機器學習能幹什麼?
從訓練資料中挖掘統計規律,自動推匯出比程式設計專家的作品還要好的程式。
- 1
機器學習在幹什麼?
簡單理解:高維空間中尋找分類超平面 注:機器學習可以視為一種從訓練資料中自動推匯出程式的方法。以最常見的有監督學習(supervised learning)為例,可簡單理解為,通過優化方法自動在高維空間找到分類超平面。
深度學習(神經網路)在幹什麼?
簡單理解:尋找從輸入空間到特徵空間的非線性對映,使得特徵空間線性可分 注:現實中遇到的絕大部分機器學習問題,基於原始特徵(Input Space)無法找到分類超平面把訓練資料裡的正例和負例恰好分開。在機器學習領域,有一些通用的手段來處理線性不可分的問題,譬如可以在Input Space 尋求非線性分介面,而不再尋求線性分介面;也可以通過對特徵做預處理,通過非線性對映的手段把訓練資料從Input Space 對映到一個所謂的Feature Space,在原始Input Space無法線性可分的樣例在Feature Space有可能線性可分。深度學習就是這種思想的一個典型應用。
神經網路的矩陣表達
矩陣表達意味著高度並行的稠密計算 注:深度學習從計算上體現為一連串的變換(transformation),常見的變換都可以表示成矩陣計算。以上圖為例,輸入層有6個神經元,輸出層共有4個神經元,每個輸出神經元都和輸入層的每個神經元有一條邊相連,每條邊有一個對應的權重(紅色神經元的輸入邊權重都用紅色表示)。輸入資料是4個樣例,每個樣例是一個6維的列向量,列向量的每一維對應輸入層的一個神經元。輸入資料經過這層神經元的作用,仍是4個樣例,每個樣例變成了4維的列向量。這樣一層神經網路可以用右圖的矩陣乘法來表示。像這種稠密矩陣運算意味著平行計算的潛力。
硬體基礎
CNN處理一張圖片的計算量
注:以大家耳熟能詳的卷積神經網路CNN 為例,可以感覺一下目前訓練深度學習模型需要多少計算力。這張表列出了常見CNN模型處理一張圖片需要的記憶體容量和浮點計算次數,譬如VGG-16網路處理一張圖片就需要16Gflops。值得注意的是,基於ImageNet資料集訓練CNN,資料集一共大約120萬張圖片,訓練演算法需要對這個資料集掃描100遍(epoch),這意味著10^18次浮點計算,即1exaFlops。簡單演算一下可發現,基於一個主頻為2.0GHz的CPU core來訓練這樣的模型需要好幾年的時間。下面我們看一下使用幾種典型硬體來訓練CNN模型需要多少時間。
多核架構:CPU運算能力
注:Intel 的伺服器級多核(multi core)處理器一般有20個主頻為2~3GHz的core,每顆CPU 做稠密矩陣計算的吞吐率約為400Gflops, 以此推算,理想情況下,在ImageNet 訓練一個CNN模型需要數天時間(考慮到訪存瓶頸,cache miss等實際情況,可能需要數月時間)。
眾核架構:GPU運算能力
注:這是Nvidia出品的最新款GPU, 它的吞吐率比CPU高一到兩個數量級,可以把CNN訓練時間從幾個月縮短到幾天。GPU 和 CPU一樣都是通用處理器,只是採用了眾核架構(many core),一顆晶片上集成了數千個計算核心(core),儘管每個core的主頻一般要比CPU core的主頻低,但GPU的核心數和訪存頻寬都遠遠高於CPU,這也是GPU成為深度學習訓練硬體不二之選的原因。
專用硬體:TPU運算能力
注:這是Google 研發的TPU, 按TPU 的吞吐率,可以在幾個小時完成一個CNN模型的訓練。TPU 與 GPU 的區別在於它是專用硬體,免去了通用處理器裡取指,指令譯碼等控制電路的複雜性,可以在同樣的面積內整合更多深度學習訓練需要的計算器件。除了Google 這種大公司,目前國內外也有不少初創公司在研發深度學習專用的晶片。
單個硬體裝置運算能力不能無限擴大
注:當前,無論是通用處理器GPU還是專用晶片TPU 相對於CPU 都強大了許多倍,但現實應用對計算力的渴求是無止境的,從業者需要以更快的速度,以更大規模的模型處理更大規模的資料,這單靠一個硬體裝置無法滿足。硬體的發展要受限於製造工藝(芯片面積,功耗,時鐘訊號傳播範圍)的限制,不可能無限制的提高一顆晶片的處理能力。因此,人們常常通過高速互聯技術把多個高通量的裝置連線在一起,協同完成大規模任務。左圖顯示了一種常見的GPU 叢集架構,同一個節點(伺服器)內部的GPU 通過NVLink或者PCIe 通訊,多個節點通過高速乙太網或者Infiniband 互聯。右圖顯示了Google 內部 TPU Cloud的硬體部署,每個伺服器管理若干個TPU,多個伺服器通過高速互聯技術連成大規模叢集。如何使得多個互聯的裝置在一起高效工作,這給深度學習軟體開發帶來了嚴峻挑戰。
軟體挑戰
深度學習軟體平臺的定位
注:這張圖揭示了深度學習軟體平臺在整個人工智慧技術棧中的角色:通過易用性降低演算法研發門檻,讓資料科學家,演算法研究人員不需要了解底層細節就可以描述和實現深度學習業務需求;通過擴充套件性更快的解決更大規模問題,釋放硬體潛能。如果說人工智慧技術是各行各業提高效率的賦能者 (enabler),深度學習軟體平臺就是人工智慧行業的賦能者(enabler)。深度學習軟體平臺的關鍵作用就像網際網路時代的瀏覽器,移動網際網路時代的Android OS,起著承上啟下的作用,向上看是作為各種應用的入口,向下看起著定義硬體功能和角色的作用。正是因為這種戰略地位,成為行業巨頭的兵家必爭之地,Google, Microsoft, Amazon, Facebook, Baidu 都開發了自己的深度學習平臺,並且都開源了,衝著建造行業生態的目標在努力。值得注意的是,深度學習軟體平臺具有極高的技術門檻,鮮有初創公司進入這個領域。
注:深度學習軟體平臺的目標是:一方面是要通過抽象以最簡優雅的方式解決上層開發者靈活多變的需求,另一方面則是釋放硬體潛能,提高裝置的利用率。這兩個目標都因為深度學習獨有的特點而變得非常挑戰,主要要現在:單個硬體裝置速度越快,軟體要高效協同多個裝置就更加挑戰。首先,自上而下看,深度學習模型通常採用所謂的隨機梯度下降(SGD)演算法,這與傳統基於批量(Batch)訓練機器學習演算法顯著不同。在隨機梯度下降演算法中,每處理一小片資料(即所謂mini-batch)就需要更新一次模型,在分散式環境下就意味著要在多個機器或多個裝置間通過通訊同步模型,因此不僅是計算密集型,而且是通訊密集型任務。其次,自底向上看,深度學習訓練主要使用異構環境,不僅有CPU 參與,還有所謂的協處理器 GPU 參與,密集計算都是解除安裝(offload)到協處理器上完成的,協處理器的吞吐率是CPU 的十倍以上,意味著同樣大小的任務在協處理器上只需要1/10的時間就完成了。不過,雖然資料處理速度提升了,但資料在裝置之間搬運的速度並沒有相對於傳統的CPU叢集有質的提升,譬如機器內部通訊通過PCIe完成,機器之間即使採用最快的Infiniband互聯,其頻寬也要比GPU cores訪問片上記憶體(device memory)慢上一到兩個數量級。一個小批次的資料在GPU 裝置上處理的時間可能是數十毫秒,但這批資料從裝置外部拷貝到裝置上也要花上差不多量級的時間,這就意味著資料的搬運(data movement)在分散式深度學習平臺中必須被當作一等公民(first class citizen)來對待。最後,GPU 高吞吐率的計算需要把計算所依賴的資料(輸入資料或模型)首先搬運到視訊記憶體(device memory),而視訊記憶體容量一般要遠小於主存(host memory),因此使用GPU 叢集訓練深度學習模型在處理大規模模型(譬如一個模型的容量或計算產生的中間資料超過視訊記憶體容量)時,如何最好利用有限資源也帶來了新的問題。
為什麼傳統大資料技術架構在深度學習平臺中不再適用?
注:諸多經典的大資料處理系統都採用了資料流(Data flow)引擎,譬如 Hadoop, Spark。在資料流系統裡,一個作業(Job)被分解成一系列互相依賴的任務(Task),這種依賴關係通常用有向無環圖(Directed acyclic graph, DAG)來描述,圖中每個節點表示一個任務,節點之間的邊表示一個數據依賴關係(生產者和消費者關係)。在一般的大資料系統中,有一箇中心排程器(Centralized scheduler)負責監控整個作業的進度以及整個系統的資源使用狀況,首先選取DAG中沒有輸入資料依賴或者輸入資料已經就緒的節點並分配給一個資源足夠的工作機(Worker),當一個Worker 執行完一個任務時,會通知Scheduler ,Scheduler 會從DAG 中刪除已成功執行的節點,然後再選取一個全部輸入資料已經就緒的節點分配給Worker去執行。當前主流的深度學習系統也借鑑了這種資料流執行引擎的思想。頁面左下角紅色五角星的個數表示目前主流框架對這項特性的支援程度,全紅五星角表示所有框架已支援,全綠五角星表示目前主流框架都還不支援。
注:非深度學習場景的資料流執行引擎都是基於中心排程器的,中心排程器實現難度小,也比較容易支援容錯等高階特性。但在深度學習系統中,中心排程器引發的開銷已經不可忽略,不可接受。分散式深度學習平臺,或者顯式或者隱式的,都採用了去中心化排程機制。中心排程器面臨的困難包括:
- Scheduler 和 Worker 之間存在通訊開銷;
- 深度學習Job分解後的Task 粒度非常小,無論是資料傳輸還是GPU上的計算,通常是數十毫秒級別,設想數十臺機器或數百塊GPU裝置同時工作,意味著每1ms時間,整個系統就發生一個事件(Task 開始或結束),就需要排程器做一個決策,而在排程器做決策的過程時,它看到的局面可能已經發生了變化。
在去中心化排程中,每臺機器,每個裝置上Task 都不需要了解全域性資訊,而只需要和自己有關的區域性上下文Task通訊,沒有多餘的通訊開銷,狀態資訊能第一時間更新,每個Task能及時響應狀態的變化。
靜態放置(Static Placement)
注:所謂放置(Placement)是指某個Task在哪個節點或裝置(Device)上執行。在傳統的大資料系統中,一般使用動態放置(Dynamic Placement)的策略,也就是某一個任務並不繫結在一個特定的機器或裝置上執行,到底在哪個機器或裝置上執行,要看排程器在派遣這個Task時綜合考慮負載均衡(Load balance) 和區域性性(Locality)等方面的因素選擇一個最合適的機器或裝置去執行這個Task。但在深度學習系統中,某一個Task 到底在哪個機器或裝置上執行都是在執行之前手工或自動指定好的,即所謂的靜態放置(Static placement),這一般出於兩方面的考慮:
- 深度學習是迭代式計算,一個Task要反覆執行很多次,如果執行該Task的裝置不固定,那麼每換一次裝置就需要在啟動Task前做一些初始化(如資源分配)工作,Task結束時要做一些清理工作,上文提到深度學習中Task本身粒度已經小到數十毫秒級,同時在異構裝置上管理資源的開銷(譬如申請和釋放裝置記憶體)都比在CPU上高的多,靜態放置能顯著減少這些資源管理的開銷;
- 深度學習同時具備計算密集和通訊密集型的特點,使用流水線技術重疊通訊和計算對系統擴充套件性非常關鍵,而要使用流水線進行資料的預傳(Prefetch 或 Presend)就需要事先知道每個生產者和消費者Task 所處的位置,只有靜態放置才能支援流水線機制。
資源管理
注:與放置(Placement)密切相關的一個問題是資源(特別是記憶體)管理策略也可以是動態或靜態,顯然動態放置需要動態記憶體管理,按需分配和釋放,但靜態放置可以使用動態記憶體管理也可以是靜態記憶體管理。現有開源深度學習平臺既有選擇動態記憶體管理的(Tensorflow),也有選擇靜態記憶體管理的(Caffe等)。靜態記憶體管理有一系列的優點:
-
降低資源管理開銷,上文提到過在GPU上申請和釋放記憶體的開銷比較大,另外如果要支援RDMA或者主存和視訊記憶體的非同步記憶體拷貝,需要使用鎖頁記憶體(Pinned Memory),如果每次需要資料傳輸時都臨時呼叫鎖頁操作開銷也比較顯著;
-
使用靜態記憶體管理,系統穩定性比較高,很多風險(譬如死鎖,Out of Memory)都可以在執行前避免,而採用動態記憶體管理的系統為了避免這些風險在系統設計上增加很多複雜性。當然,靜態記憶體管理有可能(不一定)降低記憶體資源的利用率。
專用叢集,獨佔資源,容錯簡單
注:剛才我們看到深度學習系統與傳統大資料系統的一些顯著區別:傾向於使用靜態策略。這在熟悉傳統大資料系統技術的人看來無異於“開歷史倒車”:靜態策略無異於犧牲了一系列的靈活性。深度學習平臺還有一項“倒行逆施”的特點是:通常執行在專用叢集上,而且每個作業(Job)會獨佔分配給它的硬體資源。這與傳統大資料架構提倡“虛擬化”,“共享資源”來提高資源利用率很不一樣。其實深度學習這些特點也是為了提高資源利用率,基於上文分析的種種原因,在訓練深度學習模型時共享資源反而會使得系統穩定性變差,提高資源利用率也非常難。深度學習系統也傾向於使用最簡單的容錯機制:系統快照(Snapshot or Checkpoint),fail fast, warm start,主要原因是:1,深度學習平臺使用的伺服器通常成本比較高,也比較穩定,叢集規模也較小,數十臺伺服器的場景就算比較大了;2,複雜的容錯機制會增加系統複雜性,降低系統執行效率,系統設計者寧願讓系統在正常狀態下跑的更快,即使出錯了,能從最近一次快照熱啟動即可。
至此,我們瞭解到,深度學習系統和傳統大資料系統是很不一樣的,下面我們將看到深度學習平臺技術尚處在非常早期的階段,即使在最近兩三年,技術選項也在快速演變。
資料並行
注:資料並行是指把資料分成多份,每個裝置處理一份,這樣每個裝置只需要處理整體資料的一小部分即可,系統執行時間從一個裝置處理所有資料需要的時間降低到一個裝置處理一小部分資料需要的時間,從而得到加速,這是大資料場景最常見的並行方式。以上圖為例,首先把4個訓練樣例等分成2份,分別分配給兩個裝置去處理,兩個裝置上持有同一份模型的兩個拷貝。訓練深度學習模型時,一個裝置上的前向和後向計算可以獨立開展,但每個模型上獲得模型的更新梯度後需要在裝置間同步,聚合成完整資料集上的梯度之後再做模型更新。資料並行特別適合卷積神經網路這種場景,卷積神經網路的模型由很多小的卷積核構成,模型體積小,從而裝置間同步模型梯度時通訊量較小。模型很大的場景就不適合用資料並行了。當前,所有框架都能很好的支援這種並行模式。
注:資料並行等並行模式經常依賴於一些集合通訊原語,譬如broadcast, scatter, reduce, gather 等。如何高效支援這些基本原語,在傳統超算領域(MPI)已經研究的很透徹了。以資料並行模式下模型梯度同步為例,就可以通過reduce和broadcast兩個原語來支援。
模型並行
注:除了資料並行,還有些場景模型太大以至於使用資料並行通訊開銷太大,或者模型超過GPU視訊記憶體容量,這種情況必須對模型進行切分,每個裝置上只完成一部分模型對應的計算,這稱為模型並行。Alex Krizhevsky 曾提出根據並行時傳輸資料還是傳輸模型的通訊量大小來自動選擇資料並行或模型並行,感興趣的讀者可以去讀一讀 (https://arxiv.org/abs/1404.5997)。如上圖所示,模型並行的一種做法是,讓一個GPU 負責上面紅色的兩個輸出神經元的計算,讓另一個GPU 負責下面綠色的兩個輸出神經元的計算,相當於對矩陣進行分片,每個裝置只完成一部分計算。可以看出,模型並行時,不需要在裝置間同步模型,但需要在裝置間同步資料。當前絕大部分開源框架不支援模型並行,或者支援比較弱,需要非常微妙的調整才能高效執行。模型並行是業界公認的難題。
從資料並行到模型並行:前向計算資料路由
注:除了模型並行本身比較複雜之外,模型並行模組與其它並行模式的協同也非常複雜,需要小心的管理上下游之間的資料傳輸(路由)。本頁以相鄰的兩層神經網路為例,第一個層次使用資料並行,第二個層次使用模型並行,那麼在前向計算時,就需要把資料並行部分的結果經過 Copy, Concat 兩層路由彙總到模型並行的兩個裝置上去,如果前後兩層在不同的機器上執行,那麼還需要通過跨機通訊。如果這些複雜的資料路由需要使用者手工參與管理,那麼一方面過於複雜(想象一下資料並行和模型並行的各種組合模式),另一方面極易出錯。理想的情況,這些複雜性應該由深度學習平臺來處理,但非常可惜,現有已開源的深度學習平臺都不支援這一功能。
從資料並行到模型並行:後向計算資料路由
注:本頁描述了前後兩層分別是資料並行和模型並行的場景,後向計算時的資料路由方法。限於篇幅,這裡就不再列舉從資料並行到資料並行,從模型並行到資料並行,從模型並行到模型並行的資料路由方法了。
分散式計算模式
注:一般來說,集合通訊原語扮演了一種屏障(barrier)的角色,以reduce操作為例,要實現4個節點上數值的累加,就需要等待4個節點全部完成,reduce操作才能開始,這種等待所有生產者就緒,消費者才能開始計算的模式被稱為BSP (Bulk synchronizaton protocol),現在這種模式可能受所謂 straggler 的影響,即最慢的那一個生產者的影響。為了解決straggler的問題,學者們發現在傳統分散式機器學習中可以對同步條件放鬆,不一定要嚴格同步,只要分散式計算節點在一定程度上保持同步就可以在更短的時間內得到差不多的收斂結果,因此發明了SSP 或者ASP的模式,在深度學習之前的場景多有應用。但是近些年,分散式深度學習平臺預設都是用BSP模式,這主要是因為:
- 深度學習叢集規模通常較小,機器質量比較均衡,straggler 現像沒有那麼嚴重;
- 實踐上發現非同步SGD收斂效果比同步SGD 差不少,人們寧願用更長的時間取得更好的效果。
注:多年以來,引數伺服器(Parameter server)被多個分散式機器學習系統採用,譬如Google 上一代分散式機器學習系統DistBelief, 甚至在若干知名深度學習平臺中被採用,譬如PaddlePaddle, MxNet等。本質上,在資料並行場景,引數伺服器是用來實現Allreduce語義的一種途徑。我們能看到一種趨勢,引數伺服器在某些場景有可能不是最合適的選擇:
- 引數伺服器比MPI Allreduce好在支援非同步梯度更新,但如上文所述,深度學習場景同步SGD更受青睞;
- 引數伺服器本質是client server 架構,深度學習的網路結構可能遠比client server的二分圖結構複雜;
- 引數伺服器只是實現Allreduce的一種技術,即星狀拓撲,MPI 多年以來的研究結果表明,該用什麼拓撲(星狀,樹狀,環狀)來實現集合通訊,取決於叢集內的節點數,資料傳輸量,通訊頻寬等因素。
注:在最近一年,已經可以看到一些分散式深度學習平臺摒棄了引數伺服器,反而去使用MPI 的Allreduce實現,譬如Uber Horovod 通過把Tensorflow裡的引數伺服器機制更換成 Allreduce在資料並行場景得到一倍的效率提升,Caffe2 也通過自研的gloo通訊庫實現了定製的Allreduce支援,百度的DeepSpeech系統採用了環狀Allreduce支援,Nvidia 提供的集合通訊庫NCCL也支援多種集合通訊原語。
流水線
注:這是來自賈揚清的一個例子,探討了通過流水線來提高訓練效率的必要性。不使用流水線的做法較容易理解:L1, L2, L3 表示依次三層神經網路的前向計算,前向計算完成後,自後向前開始後向計算,L3b, L2b, L1b,後向計算完成後,依次通過Allreduce來實現三個層次上權重梯度的同步,R3, R2, R1,在Reduce完成後,再依次完成三個層次的權重更新 (Update), U3, U2, U1。不難發現,L3b完成後就可以啟動R3, U3了,如果通過流水線把可以同時做的事情並行執行就會減小總體的執行時間。基於這個洞見,目前Caffe2, MxNet, Petuum Poseidon 都實現了這種優化,帶來了顯著的效率提升。但需要指出的是,除此之外,深度學習模型訓練過程中還有大量的可以通過流水線來提高並行度的機會,但現有系統都需要人工去發現和手工控制流水線,如果沒有顯式使用這種優化,系統就不會自動支援流水線。 注:更多的利用流水線的機會出現在前後多個階段之間的重疊執行上,以上圖的神經網路為例,通過3塊GPU的協作完成訓練,如果沒有使用流水線,硬體利用率會非常低:當GPU1在執行運算時,GPU2和GPU3因為沒有輸入資料只能停頓等待資料到達,當GPU1完成計算把資料從GPU1向GPU2搬運時,GPU1和GPU2,GPU3都處於停頓狀態,當資料搬運完之後,GPU2才能開始計算。一般來說,一個良好的流水線機制應該能把磁碟IO, PCIe傳輸,網路傳輸,GPU計算都能同時執行,但目前已有開源框架都沒有這種功能(或者非常微弱的支援)。
總結:一個理想的深度學習平臺在易用性和擴充套件性方面都達到極致。我們即將釋出的OneFlow系統衝著這個目標做了一系列獨特的創新,敬請關注。