并行計算的量化模型及其在深度學(xué)習(xí)引擎里的應(yīng)用
天下武功,唯快不破。怎么更快地訓(xùn)練深度學(xué)習(xí)模型是業(yè)界一直關(guān)注的焦點,業(yè)界玩家或開發(fā)專用硬件,或開發(fā)軟件框架,各顯神通。
當(dāng)然,這些定律在計算機體系結(jié)構(gòu)的教材和文獻(xiàn)中都可看到,譬如這本《計算機體系結(jié)構(gòu):量化研究方法 ( Computer Architecture: a Quantative Approach )》,但本文的價值在于有針對性地挑選最根本的幾條定律,并結(jié)合深度學(xué)習(xí)引擎來理解。
1 關(guān)于計算量的假定
在研究并行計算的定量模型之前,我們先做一些設(shè)定。對于一個具體的深度學(xué)習(xí)模型訓(xùn)練任務(wù),假設(shè)總的計算量V固定不變,那可以粗略認(rèn)為只要完成V這個量級的計算,深度學(xué)習(xí)模型就完成訓(xùn)練。
GitHub這個頁面( https://github.com/albanie/convnet-burden )羅列了常見CNN模型處理一張圖片所需的計算量,需要注意的是,本頁面列出的是前向階段的計算量,在訓(xùn)練階段還需要后向階段的計算,通常后向階段的計算量是大于前向計算量的。這篇論文( https://openreview.net/pdf?id=Bygq-H9eg )對訓(xùn)練階段處理一張圖片的計算量給出了一個直觀的可視化結(jié)果:
以ResNet-50為例,訓(xùn)練階段處理一張224X224x3的圖片需要8G-Ops (約80億次計算),整個ImageNet數(shù)據(jù)集約有120萬張圖片,訓(xùn)練過程需要對整個數(shù)據(jù)集合處理90遍(Epochs),粗略估計,訓(xùn)練過程共需要(8*10^9) *(1.2*10^6)* 90 = 0.864*10^18次運算,那么ResNet-50訓(xùn)練過程的總計算量大約是10億乘以10億次運算,我們可以簡單地認(rèn)為,只要完成這些計算量就完成了模型運算。 深度學(xué)習(xí)計算引擎的目標(biāo)是以最短的時間完成這個給定的計算量。
2 關(guān)于計算裝置的假定
本文僅限于下圖所示的以處理器為中心的計算裝置(Processor-centric computing),以內(nèi)存為中心的計算(Processing in memory)裝置在業(yè)界有探索,但還不是主流。
上圖所示的計算裝置中Computing Unit可以是通用處理器如CPU, GPGPU, 也可以是專用芯片如TPU等。如果Computing Unit是通用芯片,通常程序和數(shù)據(jù)都存儲在Memory Unit,這也是現(xiàn)在最流行的馮諾依曼結(jié)構(gòu)計算機。
如果Computing Unit是專用芯片,通常只有數(shù)據(jù)存儲在Memory Unit。Communication Unit負(fù)責(zé)把數(shù)據(jù)從Memory Unit搬運給Computing Unit,完成數(shù)據(jù)加載(load),Computing Unit拿到數(shù)據(jù)后負(fù)責(zé)完成計算(數(shù)據(jù)的形式轉(zhuǎn)換),再由Communication Unit把計算結(jié)果搬運到Memory Unit完成數(shù)據(jù)存儲(Store)。
Communication Unit的傳輸能力通常用訪存(Memory access)帶寬beta表示,即每秒鐘可以搬運的字節(jié)數(shù),這通常和線纜數(shù)和信號的頻率相關(guān)。Computing Unit的計算能力通常用吞吐率pi表示,即每秒鐘可以完成的浮點計算次數(shù)(flops),這通常和計算單元上集成的邏輯運算器件個數(shù)及時鐘頻率有關(guān)。
深度學(xué)習(xí)引擎的目標(biāo)是通過軟硬件協(xié)同設(shè)計使得該計算裝置處理數(shù)據(jù)的能力最強,即用最短的時間完成給定的計算量。
3 Roofline Model: 刻畫實際計算性能的數(shù)學(xué)模型
一個計算裝置執(zhí)行一個任務(wù)時能達(dá)到的實際計算性能(每秒鐘完成的操作次數(shù))不僅與訪存帶寬beta以及計算單元的理論峰值pi有關(guān),還和當(dāng)前任務(wù)本身的 運算強度 (Arithemetic intensity,或Operational intensity)。
任務(wù)的運算強度定義為每字節(jié)數(shù)據(jù)需要的浮點計算次數(shù),即Flops per byte。通俗地理解,一個任務(wù)運算強度小,表示Computing Unit在Communication Unit搬運的一個字節(jié)上需要執(zhí)行的運算次數(shù)少,為了讓Computing Unit在這種情況下處于忙碌狀態(tài),Communication Unit就要頻繁搬運數(shù)據(jù);
一個任務(wù)運算強度大,表示Computing Unit在Communication Unit搬運的一個字節(jié)上需要執(zhí)行的運算次數(shù)多,Communication Unit不需要那么頻繁地搬運數(shù)據(jù)就能使Computing Unit處于忙碌狀態(tài)。
首先,實際計算性能不會超越計算單元的理論峰值pi。其次,假如訪存帶寬beta特別小,1秒鐘僅能把beta個字節(jié)從內(nèi)存搬運到Computing Unit,令I(lǐng)表示當(dāng)前計算任務(wù)中每個字節(jié)需要的操作次數(shù),那么beta * I 表示1秒鐘內(nèi)搬運過來的數(shù)據(jù)實際需要的操作次數(shù),如果beta * I < pi,則Computing Unit就不會飽和,也表示Computing Unit的利用率低于100%。
Roofline model 就是一種根據(jù)訪存帶寬,計算單元峰值吞吐率,任務(wù)的運算強度三者關(guān)系來推斷實際計算性能的數(shù)學(xué)模型。由David Patterson團隊在2008年發(fā)表在Communications of ACM上( https://en.wikipedia.org/wiki/Roofline_model ),是一種簡潔優(yōu)雅的可視化模型:
圖1:Roofline Model
圖1橫軸的自變量表示不同任務(wù)的運算強度,即每字節(jié)需要的浮點運算次數(shù)??v軸的因變量表示實際可達(dá)的計算性能,即每秒鐘執(zhí)行的浮點運算次數(shù)。上圖展示了兩個運算強度分別為I_1和I_2的任務(wù)能實際達(dá)到的計算性能,I_1的運算強度小于pi/beta,稱為訪存受限任務(wù),實際計算性能beta * I_1低于理論峰值pi。
I_2的運算強度高于pi/beta,稱為計算受限型任務(wù),實際計算性能達(dá)到理論峰值pi,訪存帶寬僅利用了pi/(I_2*beta)。圖中斜線的斜率為beta,斜線和理論峰值pi 水平線的交點稱為脊點(Ridge point),脊點的橫坐標(biāo)是pi/beta,當(dāng)任務(wù)的運算強度等于pi/beta時,Communication Unit和Computing Unit處于平衡狀態(tài),哪一個都不會浪費。
回顧深度學(xué)習(xí)引擎的目標(biāo)“ 以最短的時間完成給定的計算量 ”,就要最大化系統(tǒng)的實際可達(dá)的計算性能。為了實現(xiàn)這個目標(biāo),有幾種策略可用。
圖1中的I_2是計算受限型任務(wù),可以通過 增加Computing Unit的并行度 并進(jìn)而提高理論峰值來提高實際計算性能,譬如在Computing Unit上集成更多的運算邏輯單元(ALU)。具體到深度學(xué)習(xí)場景,就是增加GPU,從一個GPU增加到幾個GPU同時運算。
如圖2所示,當(dāng)在Computing Unit內(nèi)增加更多的并行度后,理論峰值高于beta * I_2,那么I_2的實際計算性能就更高,只需要更短的時間就可以。
圖2:提高Computing Unit的理論峰值來提高實際計算性能
圖1中的I_1是訪存受限型的任務(wù),則可以通過 改善Communication Unit的傳輸帶寬 來提高實際計算性能,提高數(shù)據(jù)供應(yīng)能力。如圖3所示,斜線的斜率表示Communication Unit的傳輸帶寬,當(dāng)斜線的斜率增大時,I_1由訪存受限型任務(wù)變成計算受限型任務(wù),實際計算性能得到提高。
圖3:提高Communication Unit的數(shù)據(jù)供應(yīng)能力來提高實際計算性能
除了通過改善硬件的傳輸帶寬或者理論峰值來提高實際計算性能外,還可以通過 改善任務(wù)本身的運算強度 來提高實際計算性能。同樣的任務(wù)可以有多種不同的實現(xiàn)方式,不同實現(xiàn)方式的運算強度也存在差別。運算強度由I_1改造成超過pi/beta后,就變成計算受限型任務(wù),實際計算性能達(dá)到pi,超過原來的beta*I_1。
在實際的深度學(xué)習(xí)引擎里,以上三種手段(提高并行度,改善傳輸帶寬,使用運算強度更好的算法實現(xiàn))都會用到。
4 Amdahl's Law: 如何計算加速比?
圖2 的示例通過增加Computing Unit的并行度來提高實際計算性能,到底能把任務(wù)的執(zhí)行時間縮短多少呢?這就是加速比問題,也就是效率提高了幾倍。
為了討論方便,(1)我們假設(shè)當(dāng)前的任務(wù)是計算受限型,令I(lǐng)表示運算強度,即I*beta>pi。在把Computing Unit的運算單元增加s倍后,理論計算峰值是s * pi,假設(shè)該任務(wù)的運算強度I足夠高,使得在理論峰值提高s倍之后仍是計算受限型,即I*beta > s*pi;(2)假設(shè)沒有使用流水線,Communication Unit和Computing Unit總是順序執(zhí)行(后文我們將專門討論流水線的影響)。讓我們來計算一下任務(wù)執(zhí)行效率提高了幾倍。
在理論峰值是pi的初始情況下,1秒鐘Communication Unit搬運了beta字節(jié)的數(shù)據(jù),Computing Unit需要(I*beta)/pi 秒來完成計算。即在1+(I*beta)/pi 秒時間內(nèi)完成了I*beta的計算,那么單位時間內(nèi)可以完成(I*beta) / (1 + (I*beta)/pi) 的計算,假設(shè)總計算量是V,則一共需要t1=V*(1+(I*beta)/pi)/(I*beta) 秒。
通過增加并行度把理論計算峰值提高s倍之后,Communication Unit搬運beta字節(jié)的數(shù)據(jù)仍需要1秒鐘,Computing Unit需要(I*beta)/(s*pi)秒來完成計算。假設(shè)總計算量是V,那么共需t2=V*(1+(I*beta)/(s*pi))/(I*beta)秒完成任務(wù)。
計算t1/t2即獲得加速比:1/(pi/(pi+I*beta)+(I*beta)/(s*(pi+I*beta))),很抱歉這個公式比較難看,讀者可以自己推導(dǎo)一下,比較簡單。
在理論峰值是pi時,搬運數(shù)據(jù)花了1秒,計算花了(I*beta)/pi 秒,那么計算時間占的比例是 (I*beta)/(pi + I*beta),我們令p表示這個比例,等于(I*beta)/(pi + I*beta)。
把p代入t1/t2的加速比,可以得到加速比為1/(1-p+p/s),這就是大名鼎鼎的Amdahl's law( https://en.wikipedia.org/wiki/Amdahl%27s_law )。其中p表示原始任務(wù)中可以被并行化部分的比例,s表示并行化的倍數(shù),則1/(1-p+p/s)表示獲得的加速比。
讓我們用一個簡單的數(shù)字演算一下,假設(shè)Communication Unit搬運數(shù)據(jù)花了1秒鐘,Computing Unit需要用9秒鐘來計算,則p=0.9。假設(shè)我們增強Computing Unit的并行度,令其理論峰值提高3倍,即s=3,則Computing Unit只需要3秒鐘就可以完成計算,那么加速比是多少呢?利用Amdahl's law可以得知加速比是2.5倍,加速比2.5小于Computing Unit的并行度倍數(shù)3。
我們嘗到了增加Computing Unit并行度的甜頭,能不能通過進(jìn)一步提高并行度s來獲得更好的加速比呢?可以。譬如令s=9,那么我們可以獲得5倍加速比,可以看到提高并行度的收益越來越小。
我們能通過無限提高s來提高加速比嗎?可以,不過越來越不劃算,試想令s趨于無窮大(即令Computing Unit理論峰值無限大),p/s就趨于0,那么加速比最大是1/(1-p)=10。
只要系統(tǒng)中存在不可并行的部分(Communication Unit),加速比不可能超過1/(1-p)。
實際情況可能比加速比上限1/(1-p)要更差一些,因為上述分析假設(shè)了運算強度I無窮大,而且在增加Computing Unit并行度時,通常會使得Communication Unit的傳輸帶寬下降,就使得p更小,從而1/(1-p)更大。
這個結(jié)論令人很悲觀,即使通信開銷(1-p)只占0.01,也意味著無論使用多少并行單元,成千上萬,我們最大只能獲得100倍的加速比。有沒有辦法讓p盡可能接近1,也就是1-p趨近于0,從而提高加速比呢?有一枚靈丹妙藥:流水線。
5 Pipelining: 靈丹妙藥
在推導(dǎo)Amdahl's law時,我們假設(shè)了Communication Unit和Computing Unit串行工作,總是先令Communication Unit搬運數(shù)據(jù),Computing Unit再做計算,計算完成再令Communication Unit搬運數(shù)據(jù),再計算,如此循環(huán)往復(fù)。
能不能讓Communication Unit和Computing Unit同時工作,一邊搬運數(shù)據(jù)一邊計算呢?如果Computing Unit每計算完一份數(shù)據(jù),就立刻可以開始計算下一批數(shù)據(jù),那么p就幾乎是1,無論并行度s提高多少倍,都能獲得線性加速比。讓我們研究一下什么條件下可以獲得線性加速比。
圖4:(同圖1)Roofline Model
圖4中的I_1是通信受限型任務(wù),1秒鐘Communication Unit可以搬運beta字節(jié)的數(shù)據(jù),處理這beta字節(jié)Computing Unit需要的計算量是beta*I_1次操作,理論計算峰值是pi,一共需要(beta*I_1)/pi秒完成計算。
對于通信受限型任務(wù),我們有beta*I_1<pi,所以Computing Unit的計算時間是小于1秒的。這也就意味著不到1秒的計算卻需要花1秒鐘的時間搬運數(shù)據(jù),那么計算時間就無法掩蓋住數(shù)據(jù)搬運時間,p最大可以做到(beta*I_1)/pi,加速比最大是1/(pi-beta*I_1)。
圖4中的I_2是計算受限任務(wù),1秒鐘Communication Unit可以搬運beta字節(jié)的數(shù)據(jù),處理這beta字節(jié)Computing Unit需要的計算量是beta*I_2次操作,理論計算峰值是pi,一共需要(beta*I_2)/pi秒完成計算。對于計算受限型任務(wù),我們有 beta*I_2>pi,所以Computing Unit的計算時間是大于1秒的。
這也就意味著,每花1秒鐘搬運的數(shù)據(jù)需要好幾秒才能計算完,在計算的時間內(nèi)有充足的時間去搬運下一批數(shù)據(jù),也就是計算時間能掩蓋住數(shù)據(jù)搬運時間,p最大是1,只要I是無窮大,加速比就可以無窮大。
使得Communication Unit和Computing Unit重疊工作的技術(shù)叫流水線( Pipelinging: https://en.wikipedia.org/wiki/Pipeline_(computing) )。是一種有效地提高Computing Unit利用率和提高加速比的技術(shù)。
6 并行計算的量化模型對深度學(xué)習(xí)引擎的啟發(fā)
上文討論的各種量化模型對深度學(xué)習(xí)引擎研發(fā)同樣適用,譬如對于計算受限型任務(wù),可以通過增加并行度(增加顯卡)來加速;即使是使用同樣的硬件設(shè)備,使用不同的并行方法(數(shù)據(jù)并行,模型并行或流水線并行)會影響到運算強度I,從而影響實際計算性能;分布式深度學(xué)習(xí)引擎包含大量的通信開銷和運行時開銷,如何減小或掩蓋這些開銷對于加速效果至關(guān)重要。
在Processor-centric計算裝置的視角下理解基于GPU訓(xùn)練深度學(xué)習(xí)模型,讀者可以思考一下怎么設(shè)計深度學(xué)習(xí)引擎來獲得更好的加速比。
在單機單卡情況下,只需要做好數(shù)據(jù)搬運和計算的流水線,就可以做到GPU 100%的利用率。實際計算性能最終取決于底層矩陣計算的效率,也就是cudnn的效率,理論上各種深度學(xué)習(xí)框架在單卡場景不應(yīng)該存在性能差距。
如果想在同一臺機器內(nèi)部通過增加GPU來獲得加速,與單卡場景相比,增加了GPU之間數(shù)據(jù)搬運的復(fù)雜性,不同的任務(wù)切分方式可能會產(chǎn)生不同的運算強度I(譬如對卷積層適合做數(shù)據(jù)并行,對全連接層適合模型并行)。除了通信開銷,運行時的調(diào)度開銷也會影響加速比。
多機多卡場景,GPU之間數(shù)據(jù)搬運的復(fù)雜性進(jìn)一步提高,機器之間通過網(wǎng)絡(luò)搬運數(shù)據(jù)的帶寬一般低于機器內(nèi)部通過PCIe搬運數(shù)據(jù)的帶寬,這意味著并行度提高了,可數(shù)據(jù)搬運帶寬降低了,代表著Roofline model中斜線的斜率變小了,CNN這種適合數(shù)據(jù)并行的場景通常意味著比較高的運算強度I,而還有一些模型譬如RNN/LSTM,運算強度I就小很多,這也意味著流水線中的通信開銷更難以掩蓋了。
7 總結(jié)
有用過分布式深度學(xué)習(xí)引擎的讀者應(yīng)該對軟件框架的加速比有切身的體會,基本上,卷積神經(jīng)網(wǎng)絡(luò)這種適合數(shù)據(jù)并行(運算強度I比較高)的模型通過增加GPU來加速的效果還是比較令人滿意的,然而,還有很大一類神經(jīng)網(wǎng)絡(luò)使用模型并行的運算強度才更高一點,而且即使使用模型并行,其運算強度也遠(yuǎn)低于卷積神經(jīng)網(wǎng)絡(luò),對于這些應(yīng)用如何通過增加GPU并行度來獲得加速是業(yè)界尚未解決的難題。
在之前的深度學(xué)習(xí)評測中,甚至發(fā)生了使用多GPU訓(xùn)練RNN速度比單個GPU還要慢的情況( https://rare-technologies.com/machine-learning-hardware-benchmarks/ )。無論使用什么技術(shù)解決深度學(xué)習(xí)引擎的效率問題,萬變不離其宗,為了提高加速比,都是為了減小運行時開銷,選擇合適的并行模式來提高運算強度,通過流水線掩蓋通信開銷,也都在本文描述的基本定律涵蓋的范圍之內(nèi)。