百度深度學(xué)習(xí)平臺PaddlePaddle框架解析
PaddlePaddle 是 2016 年 8 月底百度開源的深度學(xué)習(xí)平臺,并且在短時間內(nèi)迅速成為引發(fā)全球開發(fā)熱度,并且成為Github Pull Request 數(shù)量增速***的開源深度學(xué)習(xí)平臺。
PaddlePaddle 的迭代速度非???,同時也廣受社區(qū)的關(guān)注。剛開源的時候,PaddlePaddle 的設(shè)計思想是基于Layer的設(shè)計。后來推出了“v2”和“Fluid”兩次迭代:其中 v2增加了 operators 的概念,把 layers “打碎”成更細(xì)粒度的 operators,同時支持更復(fù)雜的網(wǎng)絡(luò)拓?fù)?ldquo;圖”;Fluid 類似 PyTorch,但是不依賴 Python 的控制流(if-else、for等),而是提供自己的解釋器甚至編譯器,因此不受限于 Python 的執(zhí)行速度。
我們今天就從PaddlePaddleFluid講起,隨后講述為了能更方便利用集群而提供的在瀏覽器端訓(xùn)練的PaddlePaddleCloud,也著重講解集群訓(xùn)練的原理、配置、和實(shí)驗(yàn)結(jié)果,也就是PaddlePaddleEDL部分。***,講解PaddlePaddleVisualDL,這個非常強(qiáng)大的訓(xùn)練日志解析和可視化工具。
PaddlePaddleFluid
PaddlePaddleFluid提供類似于高級語言中的控制流結(jié)構(gòu)(例如while 、 if 、if-else、 for等),不僅能利用編譯優(yōu)化技術(shù)保證計算性能,提升使用者的開發(fā)效率。
PaddlePaddleFluid 的設(shè)計思路非常領(lǐng)先,不再沿用層(layer)結(jié)構(gòu)和操作(operator)結(jié)構(gòu)的模式。也就是說不再有“模型”的概念,也就不再有“圖”(graph of operators)或者“串”(sequence of layers)。而只有“程序”的概念。同時,程序是可以導(dǎo)出一個圖的,從程序中可以導(dǎo)出成 ONNX 文件格式的模型。
深度學(xué)習(xí)基礎(chǔ)架構(gòu)是最快速發(fā)展的技術(shù)之一,在四年之內(nèi),已經(jīng)發(fā)明了三代技術(shù)。從下表可以看出,深度學(xué)習(xí)技術(shù)架構(gòu)設(shè)計的方向正是逐漸擺脫模型的。
年份 |
層結(jié)構(gòu)模型 |
操作結(jié)構(gòu)模型 |
沒有模型 |
2013 |
Caffe, Theano, Torch, PaddlePaddle |
||
2015 |
TensorFlow, MxNet, Caffe2, ONNX, n-graph |
||
2016 |
PyTorch, TensorFlow Eager Execution, PaddlePaddle Fluid |
基于Python語言強(qiáng)大的生態(tài),PyTorch 和 Eager Execution 中的控制流都是用的 Python,但面臨的一個瓶頸是Python 執(zhí)行速度慢且難以提速。解決 PyTorch 和 Eager Execution 程序的執(zhí)行速度受限于 Python 的執(zhí)行速度的問題,F(xiàn)luid有一個比 PyTorch 和 Eager Execution 更激進(jìn)的技術(shù)思路。在Fluid的設(shè)計上,執(zhí)行會把編寫的Python 程序輸出成一個 protobuf message,隨后調(diào)用 Fluid 解釋器(而不是 Python 解釋器)來解釋執(zhí)行這個 protobuf message。Fluid 解釋器極大地加快了執(zhí)行圖的速度。同時,在編譯執(zhí)行的方式上 ,通過寫一個 transpiler 把 protobuf message翻譯成 C++ 程序,然后用 nvcc、icc、gcc 編譯成二進(jìn)制代碼,可以直接運(yùn)行在服務(wù)器和手機(jī)上。
PaddlePaddleCloud
PaddlePaddle 有一個 Web-based IDE,支持使用者在瀏覽器用 JupyterNotebook 編程來開發(fā) AI 應(yīng)用,隨后可以把程序發(fā)送到云端(Kubernetes 集群上)調(diào)試或者運(yùn)行,程序運(yùn)行時的輸出會實(shí)時地顯示在瀏覽器里。這樣使用者就不需要在個人電腦和集群等多個編程環(huán)境之間切換并且維護(hù)多個環(huán)境的版本和配置的一致性,極大地提升了工作效率。
PaddlePaddleEDL
PaddlePaddle EDL 對標(biāo)的是 Google KubeFlow。PaddlePaddle EDL通過與 Kubernetes 合作來實(shí)現(xiàn)彈性作業(yè)調(diào)度,是全球***支持彈性作業(yè)調(diào)度的開源 AI 云解決方案。
盡管現(xiàn)在很多深度學(xué)習(xí)應(yīng)用用一個幾臺機(jī)器的小集群就可以解決,但是 隨著數(shù)據(jù)量的增加和AI應(yīng)用場景的不斷擴(kuò)大,例如Web scaled 應(yīng)用(廣告、搜索、推薦等),以及通過傳感器采集海量數(shù)據(jù)的無人車,都需要大規(guī)模深度學(xué)習(xí)計算能力的。
這里主要為了解決深度學(xué)習(xí)面臨的兩大挑戰(zhàn)。其一是需要大量的計算能力。研究室和公司經(jīng)常構(gòu)建由SLURM,MPI或SGE管理的GPU集群。這些集群要么運(yùn)行一個提交的作業(yè)(假定它需要的比閑置的資源要少)或者將作業(yè)掛起一段難以預(yù)估的時間。但是這種方法有個缺點(diǎn):在有99個可用節(jié)點(diǎn)和一個需要100個提交作業(yè)的任務(wù)時,作業(yè)必須等待而不能運(yùn)行。
PaddlePaddle EDL彈性調(diào)度體現(xiàn)在可以空閑的時候一個訓(xùn)練作業(yè)多用一些資源,忙碌的時候少用一些,但是資源的變化并不會導(dǎo)致作業(yè)失??;這是優(yōu)于KubeFlow的特點(diǎn)之一。同時,EDL也彈性調(diào)度其他作業(yè)(比如 Nginx、MySQL 等),從而極大地提升集群總體利用率。[2]這樣在公有云和私有云上的推廣和部署時,就很容易節(jié)省幾倍的機(jī)器,為公司一年節(jié)省的計算成本可以高達(dá)百萬、甚至數(shù)百萬美元。
另一個挑戰(zhàn)是,工業(yè)用戶傾向于將深度學(xué)習(xí)作業(yè)作為完整數(shù)據(jù)管道的子集階段,例如日志采集器等。這種通用集群需要基于優(yōu)先級的彈性調(diào)度。比如網(wǎng)絡(luò)開銷較高的時間段內(nèi)深度學(xué)習(xí)任務(wù)少運(yùn)行,在網(wǎng)絡(luò)流量較低時優(yōu)先進(jìn)行深度學(xué)習(xí)任務(wù)。這就需要了解全局的情況,并協(xié)調(diào)與各種工作有關(guān)的進(jìn)程的數(shù)量。
PaddlePaddleEDL的測試實(shí)驗(yàn)
面對上述這兩種挑戰(zhàn),PaddlePaddle作業(yè)都可以輕松應(yīng)對進(jìn)程數(shù)量忽高忽低的變化。這里有Fluid EDL的兩種測試用例:
- Kubernetes集群只運(yùn)行PaddlePaddle作業(yè);
- 集群運(yùn)行PaddlePaddle和Nginx作業(yè)。
在***個測試中,我們開始了20個PaddlePaddle作業(yè),間隔10秒。每個作業(yè)有60個trainers和10個參數(shù)服務(wù)進(jìn)程,并將持續(xù)數(shù)小時。我們重復(fù)實(shí)驗(yàn)20次:關(guān)閉Fluid EDL 10次,打開Fluid EDL 10次。在下圖中,實(shí)線對應(yīng)于前10個實(shí)驗(yàn),其余的是虛線。在圖的上半部分,我們看到未處理作業(yè)的數(shù)量在沒有EDL的情況下單調(diào)遞增。但是,當(dāng)EDL打開時,資源將平均分配給所有作業(yè)。Fluid EDL殺死了一些現(xiàn)有的進(jìn)程,為新的其他任務(wù)騰出空間,并在晚些時候任務(wù)開始運(yùn)行。在這兩種情況下,集群都被平等利用(見圖的下半部分)。
在第二個測試中,每個實(shí)驗(yàn)都運(yùn)行了400個Nginx Pods,其優(yōu)先級高于6個PaddlePaddle作業(yè)。最初,每個PaddlePaddle工作有15個trainers和10個參數(shù)服務(wù)。我們每90秒殺死100個Nginx Pods,直到剩下100個,然后我們開始將Nginx工作的數(shù)量每90秒增加100個。下圖的上半部分顯示了這個過程。圖中的中間顯示,F(xiàn)luid EDL通過減少Nginx Pods來自動啟動一些PaddlePaddle進(jìn)程,并在稍后增加Nginx Pods來殺死PaddlePaddle進(jìn)程。結(jié)果,該集群維持在90%左右的利用率,如圖所示。當(dāng)Fluid EDL被關(guān)閉時,沒有PaddlePaddle進(jìn)程自動增加,并且利用率隨著Nginx Pods數(shù)量的變化而波動。
PaddlePaddleEDL的設(shè)計和實(shí)現(xiàn)
那上面這種深度學(xué)習(xí)服務(wù)和其它云端服務(wù)共享計算資源的過程,以及各個任務(wù)的優(yōu)先級動態(tài)地調(diào)整和伸縮的過程,從而充分地利用集群CPU/GPU是如何實(shí)現(xiàn)的呢?
EDL和HPA
Horizontal Pod Autoscaling (HPA)是Kubernetes提供的一種彈性調(diào)度機(jī)制。它的設(shè)計出發(fā)點(diǎn)是通過公平分配計算資源給某一個單一的計算任務(wù)中的各個Pod來實(shí)現(xiàn)分布式系統(tǒng)資源針對單一任務(wù)的***化利用。但我們的訓(xùn)練任務(wù)可能多種多樣(語音、圖像等)、部署時間有先有后,對資源的需求也不通,因此我們希望這種彈性調(diào)度機(jī)制能對每一種訓(xùn)練任務(wù)所需的系統(tǒng)資源有個全局的了解,然后按需分配。但目前HPA controller還沒有實(shí)現(xiàn)。
同時,HPA的彈性調(diào)度是針對同種類型的計算任務(wù)(homogenous computing task)下的 Pods。但深度學(xué)習(xí)系統(tǒng)里的計算節(jié)點(diǎn)和參數(shù)服務(wù)器往往是在不同類型的Pods里 的。
上述特有的需求導(dǎo)致使用Kubernetes的時候需要有特定的彈性調(diào)度解決方案, 而不能直接采用HPA。因此更好的解決方案是PaddlePaddle EDL。
PaddlePaddleEDL 的具體設(shè)計和實(shí)現(xiàn)
1.讓Kubernetes支持定制的彈性調(diào)度機(jī)制
Kubernetes本身就支持定制的資源管理機(jī)制。用戶可以通過提交定制的resource declaration file 和controller file來實(shí)現(xiàn)對某種Pods的彈性調(diào)度。以下圖為例,這個training_job.yaml保證了 controller會自動監(jiān)管pservers,并且保證它們的數(shù)量在min-instance和max-instance之間。
在Kubernetes集群上,這個定制的資源可以通過 kubectl create -f training_job.yaml 命令獲得。接下來,我們需要有個定制的training job controller來調(diào)度這個資源。
定制的training job controller跑在一個Pod里,對集群資源有一個統(tǒng)一的了解,它通過Kubernetes API對集群資源起到監(jiān)控和調(diào)度的作用。下圖是training job controller配置文件的一個例子。
在Kubernetes集群上,這個定制的資源管理Pod可以通過 kubectl create -f training_job_controller.yaml 命令啟動。
2.控制程序的實(shí)現(xiàn)
上面提到的定制化資源在Kubernetes里面目前有兩種實(shí)現(xiàn)方式。一種是 Custom Resource Definition (CRD) ,由Kubernetes 1.7版本引入;另一種是 Third Party Resource (TRP)。 PaddlePaddle項(xiàng)目現(xiàn)在用的是Kubernetes 1.6版本,所以實(shí)現(xiàn)的是TRP模式,今后將整合CRD模式。
當(dāng)前PaddlePaddle假設(shè)只有一個單獨(dú)的training job controller在運(yùn)行。當(dāng)前的training job controller依照下面的邏輯管理資源:
3.彈性調(diào)度算法
PaddlePaddle根據(jù)定制資源的配置文件(training_job.yaml)來判斷某個job需不需要彈性調(diào)度, 而判斷的標(biāo)準(zhǔn)是trainer和pserver的min-instance =/ max-instance。
集群中GPU的調(diào)度
controller知道集群中全部GPU個數(shù),以及當(dāng)前閑置的GPU的個數(shù),并試圖把閑置的GPU全部分配給當(dāng)前訓(xùn)練任務(wù)。PaddlePaddle給需求GPU的訓(xùn)練任務(wù)定義一個“滿足程度”的評分(fulfillment score),此評分的范圍是[0,1]。PaddlePaddle會優(yōu)先分配GPU資源給滿足程度評分***的訓(xùn)練任務(wù)。如果有分?jǐn)?shù)相同的情況,則分別優(yōu)先考慮GPU需求數(shù),CPU需求數(shù),內(nèi)存需求數(shù)。如果有某個訓(xùn)練任務(wù)的GPU min-instance沒有滿足(除非cur-instance=min-instance),那么PaddlePaddle會把一個滿足程度***分的訓(xùn)練任務(wù)里的GPU資源拿出來分給它。如果滿足程度分?jǐn)?shù)***的訓(xùn)練任務(wù)cur-instance=min-instance,則整個集群不再執(zhí)行新的訓(xùn)練任務(wù),新來的任務(wù)需等待。
集群中CPU的調(diào)度
CPU資源的分配和GPU思路相同。controller知道集群中一共有多少個CPU,內(nèi)存,它們的負(fù)載情況;同時也知道訓(xùn)練任務(wù)對CPU的需求。同樣的,CPU資源根據(jù)滿足程度評分被按需分配。
PaddlePaddle容錯機(jī)制
這里討論P(yáng)addlePaddle的容錯機(jī)制。在一個分布式訓(xùn)練任務(wù)里,如果master進(jìn)程或者所有的參數(shù)服務(wù)進(jìn)程都死掉了,那么整個訓(xùn)練任務(wù)會被停掉,過一段時間被Kubernetes整個重啟。如果具體訓(xùn)練進(jìn)程沒有都死掉,則整個訓(xùn)練任務(wù)繼續(xù)。
PaddlePaddle用etcd來記錄訓(xùn)練進(jìn)程的狀態(tài)。etcd是高可靠性的分布式key-value存儲,訓(xùn)練進(jìn)程會定時把自身狀態(tài)寫進(jìn)etcd,而這些信息將會在必要的時候用來恢復(fù)訓(xùn)練進(jìn)程。具體過程如下圖:
當(dāng)master進(jìn)程被Kubernetes啟動時,它進(jìn)行如下操作:
- 從etcd中取一個唯一的master lock,以此避免多個master實(shí)例存在
- 查看etcd中是否存在任務(wù)隊列。如果不存在,則新建一個任務(wù)隊列;否則得到這個任務(wù)隊列中的信息
- 把自身的ip地址寫進(jìn)etcd中/master/addr 這個key中,便于后來的訓(xùn)練進(jìn)程和自己通信
- 開端口監(jiān)聽訓(xùn)練進(jìn)程的任務(wù)需求,如果收到來自訓(xùn)練進(jìn)程的任務(wù)請求,從任務(wù)隊列中取任務(wù)分配之,并且更新任務(wù)隊列。
如果master進(jìn)程因?yàn)槿魏卧蛩赖袅?,Kubernetes會將它重啟,從被重啟到獲取etcd的信息,獲取訓(xùn)練進(jìn)程的任務(wù),這個過程一般是幾分鐘。
訓(xùn)練進(jìn)程
當(dāng)訓(xùn)練進(jìn)程被Kubernetes啟動時,它進(jìn)行如下操作:
- 查看etcd中包含參數(shù)服務(wù)前綴 /ps/ 獲取當(dāng)前參數(shù)服務(wù)進(jìn)程的數(shù)量并等待,直到該數(shù)量達(dá)到配置文件中的要求
- 從etcd的/master/addr key中獲取master進(jìn)程地址
- 向master發(fā)起任務(wù)請求,根據(jù)任務(wù)開始訓(xùn)練程序
當(dāng)訓(xùn)練進(jìn)程死掉之后,Kubernetes會將它重啟,新起來的進(jìn)程會重復(fù)上述工作直到開始新的訓(xùn)練工作。
參數(shù)服務(wù)進(jìn)程
當(dāng)參數(shù)服務(wù)進(jìn)程被Kubernetes啟動時,它進(jìn)行如下操作:
- 從etcd /ps_desired中讀取訓(xùn)練任務(wù)所需求的參數(shù)服務(wù)進(jìn)程個數(shù)
- 在etcd /ps/<index> (/ps/0, /ps/1, ...)里找一個小于所需進(jìn)程數(shù)里***的還不存在的id,并在etcd里創(chuàng)建這個entry,以此作為自身的id。(如下圖)
當(dāng)?shù)谌齻€參數(shù)服務(wù)器加入時:
3. 參數(shù)服務(wù)進(jìn)程會從自身對應(yīng)的etcd path中找到已有的訓(xùn)練結(jié)果參數(shù)并且將它讀入
4. 參數(shù)服務(wù)進(jìn)程開始接收來自訓(xùn)練進(jìn)程的請求。
PaddlePaddle的可視化—PaddlePaddleVisualDL
PaddlePaddleVisualDL是PaddlePaddle自帶的一個強(qiáng)大的可視化工具,也是一個Web應(yīng)用程序套件。PaddlePaddleVisualDL目前支持4種可視化,即SCALARS、IMAGES、GRAPHS、HISTOGRAMS。這4種可視化的主要功能如下。
● SCALARS:展示訓(xùn)練過程中的準(zhǔn)確率、損失值、權(quán)重/偏置的變化情況。
● IMAGES:展示訓(xùn)練過程中記錄的圖像。
● GRAPHS:展示模型的數(shù)據(jù)流圖,以及訓(xùn)練在各個設(shè)備上消耗的內(nèi)存和時間。
● HISTOGRAMS:展示訓(xùn)練過程中記錄的數(shù)據(jù)的柱狀圖。
PaddlePaddleVisualDL通過運(yùn)行一個本地服務(wù)器,來監(jiān)聽8080端口。在瀏覽器發(fā)出請求時,分析訓(xùn)練時記錄的數(shù)據(jù),繪制訓(xùn)練過程中的圖像。而且VisualDL 兼容 ONNX, 通過與 python SDK的結(jié)合,VisualDL可以兼容包括 PaddlePaddle, pytorch, mxnet, Caffe2 在內(nèi)的大部分主流DNN平臺。而Tensorboard目前僅適用于Tensorflow、Pytorch、MXNet等。
PaddlePaddleVisualDL的可視化界面如下圖所示。
VisualDL的使用
VisualDL的使用方式非常簡單,只需要下面三個過程:
VisualDL的特性
VisualDL的特性主要有下面4點(diǎn):
支持Scalar打點(diǎn)折線圖展示,方便觀察訓(xùn)練整體趨勢
支持Image查看數(shù)據(jù)樣本的質(zhì)量和訓(xùn)練中間結(jié)果
支持Histogram查看參數(shù)分布展示和變化趨勢
支持Graph查看深度神經(jīng)網(wǎng)絡(luò)的模型結(jié)構(gòu)
總結(jié)
隨著PaddlePaddle新特性的不斷增加,相信在科研和使用中能給廣大使用者帶來很多幫助。這里從PaddlePaddleFluid講起,隨后講述為了能更方便利用集群而提供的在瀏覽器端訓(xùn)練的PaddlePaddleCloud,也著重講解集群訓(xùn)練的原理、配置、和實(shí)驗(yàn)結(jié)果,也就是PaddlePaddleEDL部分。***,講解PaddlePaddleVisualDL,這個非常強(qiáng)大的訓(xùn)練日志解析和可視化工具。