端到端GPU性能優化在深度學習場景下的應用實踐

摘要:在2017杭州雲棲大會機器學習平台PAI專場上,阿里巴巴高級演算法專家楊軍結合具體案例分享了端到端GPU性能優化在深度學習場景下的應用實踐。

本文內容根據嘉賓演講視頻以及PPT整理而成。

目前深度學習和GPU已經成為了人工智慧的基礎,一軟一硬的結合能夠幫助我們實現圖像識別、語音識別以及視頻的處理,那麼如何優化深度學習框架與GPU資源也是機器學習平台的一個研究方向。

本次分享主要分為以下5個部分:

1. 背景介紹

2. 優化思考

3. 案例1

4. 案例2

5. 總結

一、背景介紹

下圖中左側是深度神經網路的發展歷史,這張圖來自鄧力的Slide。可以看到深度學習不是非常新鮮的事物,深度學習大概在1980年被提出,當時也被寄予了很高的期望,那個時候大家希望使用兩層的神經網路擬合任何一個複雜的函數,但是由於當時的數據和計算能力的限制,逐漸發現雖然想法非常美好,但是並不能實現實際的目的,所以隨著SVM這樣的方法的興起,神經網路也進入了低潮期。大概在2009年,Hinton的學生在谷歌把深度學習帶入改進了第一版的Voice Search系統,然後發現效果非常不錯,從此深度學習又一次進入人們的視野。在2012年左右,在一次Image比賽中,Alex Krizhevsky設計了一個非常強大的模型,一舉打敗了近十年的模型的改善結果,於是神經網路也開始一馬平川獲得人們的關注。

上圖中右側是一個對比圖,這張圖來自Bengio的一本教科書。其實,深度學習本質上也是一個機器學習的方法,和其他的機器學習沒有本質上的區別,都是需要學習X和Y的映射關係,只不過中間怎樣獲得映射的講究不一樣。對於普通的機器學習方法而言,會有一個人工特徵設計的過程,人們會在數據的基礎上做一些特徵抽取以及變換處理,這可能是我們耳熟能詳的方法。而在深度學習框架中,則會將人工的負擔從特徵設計上推到模型設計的層面,也就是將一些更加精細化的工作從由人工完成變成由機器實現,希望能夠解放人工的負擔,並獲得更好的普適性。

當然,這也提出了相應的要求,因為存在一個基本的常識:當一個工作需要機器工作5天的話,通常會存在一定的冗餘和過度開銷。這也是為什麼深度學習需要更高的計算能力以及更多數據的原因,這也是為什麼到了這幾年隨著IoT的興起,隨著GPU的興起,人們發現深度學習可以更好地解決實際的問題的原因。

下圖中左側是深度學習在Image大賽裡面的趨勢圖,右側比較好玩,它是MICRO 2016 的Tutorial圖。MICRO屬於計算機體系結構領域的頂會了,其實近兩年出現了一個趨勢,就是像深度學習作為演算法的應用,它會和計算機體系結構等出現更多的交互。這張圖左邊顯示的是Error Rate的情況,從圖中可以看到從2010年到2014年, Error Rate有一個明顯的下降趨勢。右邊這一列表示的是GPU的採用率,在圖中可以發現2011年的時候還是0個任務採用了GPU,而當到了2014年就有110個任務使用了GPU,這是非常明顯的變化。當然這裡面也存在一個問題,就是為什麼會選擇使用GPU解決問題,這一定是存在某些原因導致了深度學習和GPU更匹配一些,所以才會導致這樣的情況。

下圖也是深度學習經典教科書中的一張圖,圖中的X軸是OP類型,也就是說神經網路本質上是由一個個小的操作函數構成的。Y軸表示一些比較流行的模型,可以看到不同的模型在不同OP裡面計算耗時的分布是不同的,比如對於AlexNet而言,主要耗時在卷積這個地方,但是其他的模型可能在其他的操作裡面耗時比較多。這就給了我們很多信息,包括卷積、矩陣乘法以及加法等的特性都是與GPU高度契合的。

下圖是CPU和GPU的對比圖。CPU的設計圖在左邊,其設計比較有意思,下面是一些訪存的體系,上面和中間這一大部分是Cache的邏輯,左上角是一個控制邏輯。因為如果大家對於計算機體系結構比較了解就會知道CPU中往往會有一些非常複雜的分支預測和流水線這樣的邏輯,希望能夠以此來提升單核的計算吞吐率,而因為這樣使得計算相關的資源在整個晶元裡面所佔的面積是比較低的。而圖中右邊是典型的GPU的示意圖,可以看到其中有大量的計算核,只有很少的一些控制邏輯以及Cache邏輯,這也就是為什麼GPU更加適合於做一些並行計算的本質原因,也就是因為GPU通過削減自己的target domain來獲得預期的收益。

接下來為大家簡單介紹一些深度學習在阿里巴巴中的實際應用。因為阿里巴巴PAI的深度學習團隊本身更加關注於底層的優化,所以也更加關心尋找一些場景來驅動優化的邊界。

上圖中第一個是機器翻譯的場景,這個是應用在釘釘中的應用,其背後支持的是神經網路翻譯的模型,在圖中上面是中文,下面是對應翻譯的英文。中間這幅圖是阿里PAI團隊和螞蟻金服合作的信用建模,目前大家所使用的芝麻信用分背後的模型就是由它賦能的。最右邊就是OCR場景下的做的模型訓練以及模型量化預測等優化工作。

二、??優化思考

為什麼要進行優化呢?首先,優化需要解決一些實際的問題,當遇到某些挑戰時需要進行優化。以下就羅列出了一些應用深度學習時遇到的挑戰:

計算量。隨著我們把更多的工作從人工轉向機器的時候,會發現機器能夠做很多事情,但是同時也會存在一些冗餘,所以希望這些人工的事情被更加具有效率的硬體所代替。但是硬體卻會存在一定的極限,這個硬體的極限就需要靠人來彌補。

顯存。運行過GPU任務的同學會有印象,GPU的顯存屬於受限的資源,當任務超過顯存限制就會出現out of memory的情況,這就會讓建模的同學非常頭痛。

多設備支持。因為我們往往希望當模型訓練好之後,能夠在各種適配機器上運行。這兩年也有一個趨勢,就是深度學習除了在離線訓練以外,在線場景中也有很多的應用了。所以模型的多個設備支持也是一個非常有意思的課題。最近兩年也有很多方案來解決這個問題,包括Google的XLA等。

模型可操控性。這是一個比較小眾,但是在最近半年內廣受大家關注的問題。其基本的原則就是現在已經有很多工具來做深度學習的建模了,包括TensorFlow、Caffe等工具,但是還是會存在一個問題,用戶所描述的模型和底層實現之間還是存在Gap的,而這個Gap導致模型的性能、效果不是特別易用。所以這個層面的問題其實是語言設計的問題,需要設計一個更好的領域語言驅動用戶建模更加高效。

可解釋性。這是一個對於工業界比較有用的問題,因為機器學習相比於原來的確定性演算法會引入一定的隨機因素,而深度學習由於本身的特點會引入更多的黑盒因素,所以我們希望在一些更加嚴格的場景下能夠對於模型的工作原理進行解釋,這樣才能夠更加有信心地將模型部署在更加嚴格的場景中去。在這一點上,最近兩年也有一些學術界和工業界的研究在推進。

三、案例1

模型結構

第一個分享的案例是一個比較經典的案例,這裡面糅合了很多的內容,包括分散式優化、顯存優化、圖優化以及計算優化等。下圖是阿里巴巴機器翻譯團隊的Slide中的一個場景,如圖所示的是一個典型的翻譯結構圖。這個結構圖並不是非常複雜,大概輸入層是Embedding層,中間加了Encoder層、Attention層和Decoder層,最後還有一個Beam Search層進行結果的預測輸出。

這個模型給翻譯團隊帶來了一些困擾,導致需要和深度學習團隊合作做一些事情。首先的問題是訓練時間很長,單卡訓練耗時長達20天以上,基本上用戶不可迭代。第二個問題就是模型結構非常複雜導致會出現顯存用盡的情況,比如現在的model是兩個Encoder層,如果換成四個Encoder層,顯存就爆掉了,或者多加上一層Attention層,顯存也會爆掉,正是因為這樣的困擾,導致機器翻譯團隊不得不做一些妥協。此外,在線Inference延時很大,上線前相較Shallow模型延時達到了10倍,而這個延時是不能忍受的。

分散式優化

所以阿里巴巴深度學習團隊圍繞著上述三個方向做了一系列優化的工作。第一個就是分散式優化的工作,其思路比較直接是Model Average的思想,樸素而言,就是會將計算工作分配到不同的Worker裡面去,每個Worker都會做完自己的工作負載,之後會進行Average匯總並push到中心存儲裡面去再拿回模型的參數。而這裡面就存在一個小問題,雖然這樣來做可以把硬體的資源打的更滿一些,因為本質上而言減少了通訊的開銷,但是相對而言也會對於模型的精度存在一定影響。所以為了配合這個策略,我們增加了一個Learning rate auto-tuning機制,通過這個機制可以動態地調整Learning Rate來更好地使用Model Average的情況。此外,為了更好地適應這一優化,我們增加了BMUF機制,BMUF這個演算法本質上是為了當Worker數量增加的時候能夠彌補對於模型精度所帶來的影響。

當進行分散式優化之後可以看到如下圖所示的統計數據。當單個GPU時,收斂加速比是1,當4個GPU時的收斂加速比是3,16個GPU時收斂加速比則達到了9。

顯存優化

顯存優化其實是一個比較好玩的工作。因為業務方在訓練比較複雜的模型的時候總是會發現自己的模型在顯存中放不下,這就導致業務方不得不將自己的模型變得更小。所以就思考能否利用一些技術的方法來彌補用戶的建模需求和硬體顯存之間的矛盾。我們將這個工作稱之為Swap2Host,之所以叫這個名字是因為其本質上利用了CPU裡面更大的內存,將一部分計算的內容放過去。下圖是一個小示意圖,白色的這個圖是用戶使用的原始計算圖,帶顏色的則是優化後的計算圖,本質上是做切換的一些tensor和變數,把他們換到CPU裡面去,等到需要的時候再換回來,通過這樣一個換入換出的工作減少顯存峰值的開銷。這裡就會涉及到幾個問題,第一個就是什麼時候換出去,其次就是什麼時候換進來,都是有很精細的設計需要考量的。

下圖是一些統計數據,上面兩個表格是顯存優化的效果,下面的表格則是性能影響的結果。顯存優化中存在兩個尺度,一個是優化之後在模型不變的情況下,一個是在模型會變化但是基本結構不變的情況。下面的表則是優化之後對於性能的影響,各個計算模型可能會有少量的計算性能的損失,這一部分還在持續優化。

之前所提到的都是對於所有模型都有效的優化,但是效果並不是完全理想,並沒有達到最好的預期。為了實現更好的優化,我們還針對於Seq Model本身進行了相應的優化。提到Seq Model,首先需要分享一下其基本的計算邏輯,Seq Mode將計算邏輯進行了拆分,分為前向和後向,前向用於Inference,後向則是訓練會用到的邏輯。本質上來說,Seq Model會有很多個Step構成,每個Step會有一個輸入和一個輸出,後向類似。但是仔細思考中間的這些結果是否一定需要,是不是可以在做完前向之後丟棄掉,當後向需要用到的時候再重新進行計算,這就是針對於Seq Model優化的樸素原理。

下圖是優化後的結果,可以看到在模型不變的情況下,顯存性能有一個顯著的提升。

當完成以上優化之後,我們認為還存在優化的空間,所以繼續深挖細節問題。後來發現Attention機制本身會有一個比較好的特性,可以更好地捕捉一些用戶的上下文,這對於建模很友好,但是這個特性對於顯存開銷是比較龐大的,因為其本質上每一個Attention的計算都會需要Encoder Step的所有輸出和Decoder Step的一個輸出來為每個Decoder的輸入做準備,這樣就是N方的複雜度,N為Step的數量。所以就考慮將Attention的Operator進行優化,下圖所示的就是優化的大致思路,左邊是優化前的圖,計算出中間結果輸出一個Attention Score,後向將中間結果送過去,這時候就會想能夠將中間砍掉,不要中間結果,當後向需要就重新進行計算。而後向計算本質上只需要做一個加法,這是非常輕量的,所以想要獲得好的收益只需要非常少的開銷。而且經過實際測試發現,因為減少了顯存的訪問,實際上計算反而變得更快了,這也是一個意料之外的收益。

下圖是做完Attention優化之後的最終數據,從表格中可以看到隨著模型的Time Step增加,出現了顯著的優化效果。

圖優化

下圖是與Inference相關的優化,這是個比較通用的圖優化工作。圖中下側是一個用戶自己描述的TensorFlow的計算圖,因為翻譯邏輯比較複雜,所以會需要遍歷很多的子圖,每一個Step都需要遍歷子圖,搜索出結果之後再進行下一個Step,一直到最後N個Step。這樣就會出現問題了,每個子圖都會有一些訪存、中間結果的保存等的開銷問題,所以我們做了圖轉化器,這樣就減少了中間的開銷,獲得了性能收益。

計算優化

與此同時,我們也做了一些Op Fusion的優化,通過把一些Softmax OP進行融合替換成一個大的OP,並實現了一些定製的擴大優化。下圖展示了測試數據,可以看到在優化前用戶自己寫的模型運行在GPU上面Batch Size大概為1,延遲大概700毫秒,QPS只有1,在優化之後,延遲縮短到了119毫秒,QPS達到100,所以在性能上是極大的提升,目前阿里巴巴也在部署相應的工作。

四、案例 2

與第一個案例的全面優化不同,第二個案例更偏向於模型的優化,其主要是在演算法層面做了模型的壓縮以及模型結構的優化。而這個案例主要是與OCR項目合作的實施的。

檢測識別參數共享

第一個是模型結構的嘗試,做OCR的識別需要兩個工作,一個工作是檢測,另外一個工作是識別。這兩個工作都會涉及到特徵提取的過程,這時候就在思考,是否能夠通過一次的特徵提取滿足兩次不同的需求,於是就做了檢測識別參數共享的工作。這個工作主要需要滿足三個要求,第一個就是保證精度不要下降,第二就是計算量足夠顯著,第三個就是調整足夠泛化的,因為我們不希望更換模型之後就變得無法適用。

下圖是深度學習團隊所做的一些實驗,左邊是RFCN檢測模型+CNN識別模型,右邊是一個RFCN檢測模型+FCN的識別模型,其實除此之外,還有很多組合方式,我們希望通過不同組合的驗證優化方法是是普適的。

下圖是一個統計數據,從數據中可以看出,檢測識別參數共享方案基本上達到了所期望的結果。

除了精度之外,我們也關注性能節約情況,所以也做了一組對比實驗,實驗結果如下圖所示。可以看到隨著設置的layer不同,性能節約的量也不一樣,大概從38%到46%。

Kernel 分解

在OCR案例中,所做的第二個工作是矩陣分解的工作,會對於卷積和進行拆分。因為本質上神經網路會有一些冗餘在裡面,所以可以對其做一些相應的降維處理,在精度有限的情況下能夠獲得顯著的計算量的提升。下圖是一個原始的模型示意圖,這是一個5*5*1000的卷積和,我們對於其進行了拆分,拆成兩個低維的卷積核外加一個重建的矩陣再還原回去,這樣拆分之後的計算量遠遠小於之前的情況,所以就能夠獲得相應計算量的提升。

下圖是基於單個OCR進行試驗的模型,這個模型不是特別複雜,大概是四個卷積核、一個全連接層加一個輸出層。

同樣,我們也做了不同的實驗來驗證效果,分別是針對卷基層進行分解和針對全連接層進行分解,只不過分解的策略則會有一些小的區別。第一層只prune了filter number,而後三層同時做了filter pruning和低秩矩陣分解,相比而言更加激進一些。

下圖是測試實驗所得到的實驗結果,我們能夠看到如果只去分解卷積核,能夠看到卷積時間能夠省去一倍的樣子,而精度下降很有限。如果FC層也進行分解,精度下降則會明顯一些,因為FC層距離輸出更近一些,冗餘更少一些,這就是當時的情況。最近,我們也在推進卷積核自動化拆解等任務的開展。

五、總結

簡單總結一下,可以將GPU性能優化分成以下四個面向:

面向Performance,這包括了計算性能、顯存性能以及離線性能和在線性能等,這個維度的工作會包括不同的角度,比如可以從System角度來做,也可以從演算法角度來做,可以進行矩陣優化、顯存優化以及OP優化等等,而演算法優化則可以做檢測識別共享,Kernel分解、優化模型等等。也可以從另外的角度來拆分、比如OP的Library優化,可以寫更有效的ops以及把這個問題變成編程語言設計的問題,甚至可以做RunTime優化。

面向Productivity,因為我們認為深度學習是一種建模語言,它為我們提供了一種描述問題的手段,所以我們希望這個手段越高效越好,希望用戶可以很容易地寫出自己的模型,而不用關心底層的細節,就可以成功運行。

面向Portability,也就是怎樣做到寫一份模型在不同設備中能夠高效運行,這個裡面主要涉及到對於Runtime和compiler等的一些可移植要求。

面向Precision,這一點與業務的關係更加密切,也就是如何更好地優化模型的邊界,從而更好地優化模型的精度。

推薦閱讀:

到底什麼樣的車算是性能車?
JIT 為什麼能大幅度提升性能?
國內用traceview的人多嗎?這個工具這麼強大,實際用到他一般是幹什麼?

TAG:算法 | 性能 | 神经网络 |