深度學習加速:算法、編譯器、體系結構與硬件設計

概述

NeurlPS2019 大會的「Efficient Processing of Deep Neural Network: from Algorithms to Hardware Architectures」的演講概括性地介紹了目前深度學習加速領域的進展,看後覺得這個演講的邏輯清晰,於是想結合

演講ppt內容 和近期調研的一些加速器相關內容,總括性地理一下深度學習加速領域的內容。首先關於深度學習加速,一般會想到的就是關於深度學習加速器的硬件設計,但其實更寬泛地講,從算法頂層,到編譯器,到體系結構,硬件最底層都有涉及。下面的介紹也大致圍繞這幾個方面展開。(有新的東西會繼續往上加,長期更新)

目錄

1.算法頂層

  • 大規模分佈式機器學習(並行模式、調度模式、更新策略)
  • 優化算法
  • 輕量級網絡設計
  • 神經網絡架構搜索
  • 量化與剪枝
  • 卷積運算的優化

2.深度學習編譯器

  • 需求與痛點
  • TVM
  • Pytorch Glow
  • Tensorflow XLA

3.體系結構與硬件設計

  • 關注指標
  • CPU和GPU平臺與其設計考量
  • Domain-Specific 硬件設計
  • 設計關注點
  • 深度學習應用數據重用機會
  • 兩類設計範式:Temporal Arch. 與 Spatial Arch.
  • 加速器設計可以利用的特性(稀疏、低精度、壓縮)
  • 一些經典的加速器設計案例分析(DianNao、PuDiannao、TPU、Eyeriss)

1. 算法頂層

算法頂層在深度學習加速的工作也囊括了很多方面,例如:更好的分佈式訓練調度(大規模分佈式機器學習系統),更好的優化算法,更簡單高效的神經網絡結構,更自動化的網絡搜索機制(神經網絡架構搜索NAS), 更有效的網絡參數量化剪枝算法、卷積運算的優化等等。

1.1. 大規模分佈式機器學習

這方面主要是分佈式系統的一些設計。現在的網絡模型越來越大,為了達到好的擬合效果,許多公司需要用極大的數據量餵給模型訓練,這些數據往往單機都塞不下,而要一個batch一個batch慢慢給可能要訓練到猴年馬月,因此才需要大規模分佈式機器學習系統。

深度學習加速:算法、編譯器、體系結構與硬件設計

下面內容先概括:

  • 並行方式有數據並行和模型並行
  • 調度方式有集中式(參數服務器)和去中心化調度(RingAllReduce)
  • 參數更新方式有同步和異步兩種,從另外角度分的話有基於參數的,有基於梯度方式的

分佈式機器學習系統的並行加速本質上是就是DLP的多機延伸,具體來說其並行的方式分為兩種:

  • 數據並行
深度學習加速:算法、編譯器、體系結構與硬件設計

顧名思義就是每臺機器都有模型的副本,但是將數據的不同部分分別餵給各個模型(各個機器), 最後的結果以某種方式進行合併(可能是直接參數融合,也可能是傳遞梯度數據進行融合)。數據並行是最常見也是最直觀可以理解的。

  • 模型並行
深度學習加速:算法、編譯器、體系結構與硬件設計

模型並行的話,就是模型本身太大了,因此需要將模型各個部分分散在各個機器間,這種情況在更新參數時需要層間跨機器通信。

  • 兩者結合
深度學習加速:算法、編譯器、體系結構與硬件設計

當然實際上還會有數據並行和模型並行的結合,可以從這個角度理解: 模型各個部分分散在單機的多個GPU中(模型並行),同時模型在多個機器都有副本,數據各個部分分別餵給各個機器並行訓練(數據並行)。

上面介紹的是並行的方式,那麼在實際分佈式訓練中是怎麼進行訓練的調度呢?調度方式主要分為集中式調度 和 去中心化調度,前者代表有Parameter Server方式,後者代表有Ring Allreduce方式。

  • Parameter Server

參數服務器是一種集中式的架構,有專門的機器用於存放全局參數,其他的worker用於訓練!那麼怎麼更新呢?

一種方法是參數平均,這是一種同步方式!

深度學習加速:算法、編譯器、體系結構與硬件設計

這種方式下,參數服務器每次將全局參數分配到各個worker,worker們每次迭代更新了參數後再彙集到參數服務器求平均得到這一輪的結果,之後再重複上述步驟直到收斂,是不是很簡單!然而實際在工程上要考慮更多!例如,由於參數服務器要等待所有worker的參數到來後才可以操作(同步方式),那麼如果有worker發生故障呢? 如果worker每一次迭代就發參數給參數服務器,那麼通信可能會成為瓶頸,而我們知道,計算比重 要大於傳輸比重,才有收益,所以可能需要考慮在worker那邊多做幾輪迭代後再給參數服務器更新,減少通信開銷。諸如此類的衡量還有很多。

另一種就是基於梯度的方式,worker不是發送本地更新後的參數給參數服務器,而是發送梯度數據,參數服務器彙總的是梯度,然後在那邊統一進行梯度更新。這也是一種同步更新的方式!兩種方式似乎大同小異,但是由於梯度的稀疏特性,在通信前可以利用壓縮算法來減少傳輸開銷。

深度學習加速:算法、編譯器、體系結構與硬件設計

參數平均和基於梯度的方式本質上都是同步更新方式,有同步就有異步方式(基於梯度的方法本身可以做成異步更新的方式)。異步是說參數服務器不再等待最後一個worker才開始操作,而是有worker的參數到了,我這邊就開算,不進行等待。那麼兩種更新方式的區別是啥?

  • 同步方式: 有較大通信開銷和同步等待開銷,但是優點是收斂過程比較穩定。
  • 異步方式: 速度快,高吞吐,等待時間少。但是噪聲更大,有參數過期問題,所以收斂過程沒有前者穩定。
深度學習加速:算法、編譯器、體系結構與硬件設計

  • Ring AllReduce

這種調度方式是與Parameter Server相反的方式,體現了設備去中心化思想。集中式調度容易管理,但是隨著worker數目增加,master的負擔越來越大,加速比也急劇惡化。

深度學習加速:算法、編譯器、體系結構與硬件設計

而 Ring AllReduce可以將參數通信分散到各個GPU,經過一循環的數據傳遞和計算後,得到正確結果。通過Ring AllReduce這種比較巧妙的方式,每個設備的負載更加均衡,基本上可以實現當GPU並行卡數的增加,實現計算性能的線性增長。具體的scatter和reduce流程可以參考

這裡 ,一些相關的分佈式訓練庫: horovod baidu-allreduce

深度學習加速:算法、編譯器、體系結構與硬件設計

關於大規模分佈式機器學習系統先介紹到這裡,以後再細講。之前看過一本相關書籍《 分佈式機器學習:算法、理論與實踐 (豆瓣) 》,感覺不錯,感興趣可以參考一下。

1.2. 優化算法

深度學習的優化算法我覺得也可以算入深度學習加速的範疇,因為各式各樣的優化算法 (SGD,Adagrad,Adadelta,RMSprop,Momentum,Adam,Adamax,Nadam) 的目標都是使得梯度下降搜索的時候可以更加趨近全局最優,使得收斂的速度更快,從而加速訓練進度。當然,優化算法在分佈式機器學習系統上需要實現其相應的分佈式的版本,各類優化算法在分佈式系統的參數更新方式上可以玩出很多花來。

1.3. (輕量級)高效的神經網絡結構

由於神經網絡對於噪聲不敏感,所以許多早期經典模型的參數其實是存在很大冗餘度的,這樣的模型很難部署在存儲有限的邊緣設備,因此學術界開始追求一些輕量級的網絡設計(這代表著更少的參數,更少的乘法操作,更快的速度):

  • 減小卷積核大小
深度學習加速:算法、編譯器、體系結構與硬件設計

主要的做法包括使用更小的3x3卷積(加深網絡來彌補感受野變小);將大卷積核分解成一系列小的卷積核的操作組合。

  • 減少通道數
深度學習加速:算法、編譯器、體系結構與硬件設計

第一種方式:1x1卷積的應用。在大卷積核前應用1X1卷積,可以靈活地縮減feature map通道數(同時1X1卷積也是一種融合通道信息的方式),最終達到減少參數量和乘法操作次數的效果。

深度學習加速:算法、編譯器、體系結構與硬件設計

第二種方式:group convolution,將feature map的通道進行分組,每個filter對各個分組進行操作即可,像上圖這樣分成兩組,每個filter的參數減少為傳統方式的二分之一(乘法操作也減少)。

深度學習加速:算法、編譯器、體系結構與硬件設計

第三種方式:depthwise convolution,是組卷積的極端情況,每一個組只有一個通道,這樣filters參數量進一步下降。

深度學習加速:算法、編譯器、體系結構與硬件設計

如果只採用分組卷積操作,相比傳統卷積方式,不同組的feature map信息無法交互,因此可以在組卷積之後在feature map間進行Shuffle Operation來加強通道間信息融合。或者更巧妙的是像Pointwise convolution那樣將原本卷積操作分解成兩步,先進行 depthwise convolution,之後得到的多個通道的feature map再用一個1X1卷積進行通道信息融合,拆解成這樣兩步,卷積參數也減少了。

  • 減少filter數目
深度學習加速:算法、編譯器、體系結構與硬件設計

直接減少的filter數目雖然可以減少參數,但是導致每層產生的feature map數目減少,網絡的表達能力也會下降不少。一個方法是像DenseNet那樣,一方面減少每層filter數目,同時在每層輸入前充分重用之前每一層輸出的feature map。

  • 池化操作
深度學習加速:算法、編譯器、體系結構與硬件設計

池化操作是操作卷積神經網絡的標準操作,池化層沒有參數,同時又可以靈活縮減上一級的feature map大小,從而減少下一級卷積的乘法操作。

1.4. 神經網絡架構搜索

1.3節所介紹的那些高效的神經網絡結構本質上是人工精心(費力)設計的結構,而神經網絡架構搜索NAS則是將神經網絡設計探索這個過程自動化。

深度學習加速:算法、編譯器、體系結構與硬件設計

NAS主要包括三個主要的components:

  • 搜索空間:搜索空間的每個樣本就是一種神經網絡設計架構
  • 優化算法:如何進行採樣得到一種網絡設計
  • 性能評價:用什麼指標評價採樣出來的網絡設計,評價的結果反過來指導優化算法的採樣。通常的指標是DNN的accuracy和所需的搜索時間。

優化總搜索時間可以從縮小搜索空間、提升優化算法和簡化性能評價指標三部分入手:

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 縮小搜索空間
深度學習加速:算法、編譯器、體系結構與硬件設計

可以通過增加一些搜索約束來減少探索空間,例如 權衡架構的寬度;放低所要達到的性能目標;結合domain knowledge,通過現有的人工設計經驗來縮小探索範圍。下面是一種搜索的例子,將設計空間設定為 layer operations和 layer connections的不同組合,然後NAS系統在人工設計的幾種選擇組合選項中進行搜索:

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 提升優化算法
深度學習加速:算法、編譯器、體系結構與硬件設計

關於如何用一種比較好的啟發算法從搜索空間採樣出網絡設計,NAS + 以前優化領域那一系列智能算法又可以玩出各式花樣來,遺傳算法、模擬退火、粒子群、螞蟻算法...等等都有相關的結合工作。

  • 簡化性能評價
深度學習加速:算法、編譯器、體系結構與硬件設計

先在小型輕量級的數據集訓練和快速驗證,觀察驗證集精度曲線決定是否早期停止等等,這些經常使用的訓練技巧在NAS上也同樣適用。

深度學習加速:算法、編譯器、體系結構與硬件設計

基於歷史搜索經驗來減少一些無意義的探索

深度學習加速:算法、編譯器、體系結構與硬件設計

估算搜索出來的網絡架構的參數,由此推算該網絡的乘加操作次數,如果超出了設定的約束範圍,那麼可以間接預估出該網絡架構過於複雜,不符合要求,那麼直接丟棄這個設計,連訓都不用訓。另外可以建立查找表記錄各個搜索出來的網絡架構的時間,根據表中信息,可以推算出新網絡架構的時間。

1.5. 網絡量化剪枝

  • 什麼是量化
深度學習加速:算法、編譯器、體系結構與硬件設計

根據上圖,最簡單的理解方式就是:統計網絡權重和激活值的取值範圍,找到最大值最小值後進行min-max映射把所有的權重和激活映射到到INT8整型範圍(-127~128).

  • 量化為什麼有效
深度學習加速:算法、編譯器、體系結構與硬件設計

首先量化會損失精度,這相當於給網絡引入了噪聲,但是神經網絡一般對噪聲是不太敏感的,只要控制好量化的程度,對高級任務精度影響可以做到很小。

其次,傳統的卷積操作都是使用FP32浮點,浮點運算時需要很多時間週期來完成,但是如果我們將權重參數和激活在輸入各個層之前量化到INT8,位數少了乘法操作少了,而且此時做的卷積操作都是整型的乘加運算,比浮點快很多,運算結束後再將結果乘上scale_factor變回FP32,這整個過程就比傳統卷積方式快很多。

提前從體系結構的考量角度思考量化帶來的另一個好處是節能和芯片面積,怎麼理解呢?每個數使用了更少的位數,做運算時需要搬運的數據量少了,減少了訪存開銷(節能),同時所需的乘法器數目也減少(減少芯片面積)。

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 量化的一些其它考量

上面舉的例子都是INT8量化,實際上要根據設備的支持情況以及應用的性能要求選擇INT32、INT16、 INT8量化。

單純使用min-max映射可以將分佈均勻的浮點數映射到INT8空間,但是如果權重和激活比較多地集中在0.0以上,只有小部分落在負數,那麼此時min-max映射後許多數也會集中在INT8某個小範圍,那麼INT8寶貴的動態範圍就被浪費了,因此我們需要選擇一個合適的閾值對激活和權重的範圍進行裁剪,NIVDIA在實際操作中是根據KL散度來選擇出這個裁剪閾值,具體細節參考 章小龍:Int8量化-介紹(一)

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 權重剪枝

神經網絡的權重剪枝就是把網絡趨近於0的權重直接裁剪成0,以達到稀疏性。權重剪枝,可以作為量化模型後進一步的優化,有了稀疏性,通過壓縮後可以進一步減少參數存儲量;有了稀疏性,運算時如果跳過權重為0的計算,又可以減少功耗。TensorFlow Lite框架對模型的移動端部署的核心優化就是量化和權重剪枝。

稀疏模型的優勢在於易於可壓縮,在推理過程中跳過權重為0的計算。剪枝通過壓縮模型來實現。在犧牲微小精度的前提下,我們的模型有6倍的性能提升。剪枝這項技術,也在實際應用到語音應用中,如語音識別、文本轉語音以及橫跨各種視覺與翻譯的模型中。

權重剪枝主要有兩種方式

後剪枝:拿到一個模型直接對權重進行剪枝,不需要其他條件。

訓練時剪枝:訓練迭代時邊剪枝,使網絡在訓練過程中權重逐漸趨於0,但是由於訓練時權重動態調整,使得剪枝操作對網絡精度的影響可以減少,所以訓練時剪枝比後剪枝更加穩定。

TF官方提供了詳盡的 Keras剪枝教程Python API文檔 ,以及 訓練稀疏模型等高級用法的指導 。此外,還有一個 MNIST手寫體圖像識別的CNN模型剪枝代碼 ,和 IMDN情感分類的LSTM模型的剪枝代碼 也可以參考。

1.6. 卷積運算的優化

  • 卷積轉化為矩陣乘法

大部分深度學習框架會把卷積操作轉化成矩陣乘法操作,這樣可以利用現有成熟的矩陣乘法優化方案,比較常見的轉化方式是img2col:

深度學習加速:算法、編譯器、體系結構與硬件設計

全連接層可以直接轉化為矩陣乘法,因為全連接的操作本質上就是Y=WX的矩陣操作。從另一個角度,全連接的操作其實還可以看作 每個神經元為1x1filter,所進行的卷積操作。而對於卷積層,轉化為矩陣乘法後,會引入一定冗餘度:

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 矩陣乘法的一些優化操作
深度學習加速:算法、編譯器、體系結構與硬件設計

矩陣分塊可以更好地匹配各級cache的大小,這樣cache局部性更好,訪存時的miss率更低。除此之外,對矩陣的內存數據佈局進行重排布來增加待計算數據放置的連續性,這樣取數的時候cache miss率也會更低,像常見的 NCWH 和 NWHC 排布模式都是這方面考慮。

  • 矩陣乘法的近似計算
深度學習加速:算法、編譯器、體系結構與硬件設計

使用其它的卷積近似替代算法,可以減少複雜度。不過有些算法有使用條件,像Winograd只是對小卷積核操作有比較大的優化效果。傅里葉變換似乎不太常用,因為要達到理論上的加速,要求卷積核要很大才可以,而實際上深度學習用的都是小卷積核。

深度學習加速:算法、編譯器、體系結構與硬件設計

2. 深度學習編譯器

2.1 需求

這個算是連接頂層和體系結構的部分,之所以有深度學習編譯器的需求,是因為我們的模型從各式各樣的頂層框架訓練出來(Pytorch, Tensorflow, MXNet, Caffe等 ),但很多時候並不是還在訓練時用的平臺部署來進行推理,最終落地階段可能需要將模型部署到各式各樣的設備(Intel CPU, Nvidia GPU, 移動端Arm CPU, 移動端GPU, FPGA, Risc-V CPU ), 而這些設備是用不同的指令集架構的。

舉個例子,你使用Tensorflow或者Pytorch為服務器端的Nvidia GPU所實現的算子操作(一些列cuda 函數) 完成了模型在GPU的訓練,你得到了模型參數,接下來你被要求將模型部署到手機端cpu,那麼可以肯定的是,你訓練時候所使用的那些cuda函數肯定在手機端用不了了,怎麼辦呢?你需要針對你手機端的指令集架構Arm,重新手寫算子(實現一些前向推理需要的卷積啊池化操作的函數,並且實現方式還必須高效,不然到時模型跑起來速度太慢也沒有意義) ,這樣你的模型才跑得起來,隻手寫一套你覺得還OK,頂得住,那如果還要部署要其他類型的設備呢?問題變得繁瑣起來...

人工的話不太可能有精力對那麼多目標平臺重新手寫切合各個平臺指令集架構的算子(各類卷積,池化,點乘...),因此,如果有深度學習編譯器來作為中間部分,將頂層框架的模型流圖轉化成中間層表示IR,在這個中間層表示做系列標準的流圖優化操作(loop 調度,算子融合...),最後在按需求生成各個目標平臺的機器碼,那就是頂好的!

深度學習加速:算法、編譯器、體系結構與硬件設計

2.2 TVM

深度學習加速:算法、編譯器、體系結構與硬件設計

從上圖可以看出來,TVM其實是一個橋樑作用,TVM可以實現算子代碼的自動生成,也就是說現在我在頂層訓練好了模型需要部署到不同的目標平臺,通過TVM可以生成針對這些平臺的優化的算子和推理代碼。當然自動生成的算子還可以結合手工進一步優化(自動與手工結合)。除了自動生成算子代碼功能,TVM很有吸引力的一點就是其自動生成的代碼可以媲美那些手工優化的加速庫。

TVM的使用場景:

一種是離線支持敏捷算子優化,通過TVM自動代碼生成的功能,部分解放優化人員手動添加優化的負擔。具體地,給定一個計算,優化人員進行workload characterization分析完計算訪存pattern之後,可以擬定幾種可能的優化方案,如果TVM的schedule中包含,那麼可以直接應用TVM來生成包含該優化的代碼。然後藉助profiler來分析代碼的瓶頸,進一步修正或者更新優化方案,再試圖藉助TVM的自動生成能力,如此循環往復。到最後,如果TVM代碼的可讀性足夠的話,優化人員可以在其上做手動優化;如果可讀性不好的話,TVM也可以幫助優化人員快速確定優化方案。 另外一種場景是,運行時代碼生成。當研究員在試驗新的算子時,由於沒有可供調用的計算庫,同時缺乏CUDA等高性能代碼開發經驗,因此很多情況下是通過裸跑python代碼來調用新算子的。PyTorch已經在今年5月份開始將TVM接入到PyTorch中實現運行時代碼優化編譯,目前已經可以運行了。

詳細的討論參考: HONG:也談TVM和深度學習編譯器

關於深度學習編譯器我還了解不多,下一步打算深入探究一下TVM再來補充。TVM入門可以參考 藍色:手把手帶你遨遊TVM

2.3 Tensorflow XLA

XLA(Accelerated Linear Algebra,加速線性代數)是一種優化TensorFlow計算的編譯器。 XLA(加速線性代數)是為優化TensorFlow計算的特定領域編譯器。它可以提高服務器和移動平臺的速度、改進內存使用、提升便攜性(或者說是可移植性)。 XLA框架是目前處於開發中的實驗性項目。更多信息請閱讀 XLA指南

深度學習加速:算法、編譯器、體系結構與硬件設計

XLA有幾個與目標硬件無關的優化,例如 公共子表達式消除(CSE,common subexpression elimination) ,過程與硬件後端無關的(target-independent)算子融合,以及用於為計算而在運行時做內存分配的緩衝區分析。 硬件目標無關的步驟之後,XLA將HLO計算發送到後端去做進一步的HLO級優化,HLO級優化會針對硬件目標特定信息和需求,如XLA GPU後端的算子融合、確定如何將計算劃分為流。此階段,後端還可以將某些算子或其組合與優化的庫調用進行模式匹配。 下一步是特定於目標硬件的代碼生成。CPU包括XLA使用的GPU後端會使用 LLVM 生成低級IR、優化和代碼生成。硬件後端以有效方式發出了表示XLA HLO計算所必需的LLVM IR,然後調用LLVM從LLVM IR發出原生代碼。 GPU後端目前通過LLVM NVPTX後端支持NVIDIA GPU,CPU後端支持多個CPU指令級架構( ISA)

詳細可以參考: 按時計劃:TensorFlow Lite概述:轉換器、解釋器、XLA和2019年路線圖

2.4 Pytorch Glow

Glow是一個機器學習的編譯器和執行引擎,它可以面向多種硬件, 是基於LLVM項目進行了開發的。(後續繼續補充....)

深度學習加速:算法、編譯器、體系結構與硬件設計

3. 體系結構與硬件設計

傳統標量處理器適合分支情況複雜的程序,但是對於深度學習這種數據驅動的、有明顯的DLP特徵的應用就顯得不適用了,因此現在通常是通用處理器和專用處理器的結合,像我們手機有多個CPU負責一般程序執行,又有NPU去加速器一些神經網絡應用。

3.0. 關注指標

深度學習加速:算法、編譯器、體系結構與硬件設計

DNN加速器的關注的點不僅僅是 OPS/W,更多的是準確度、吞吐量、時延、功耗、硬件成本、硬件的靈活度或者說對新出現模型的可複用性等等各個方面的權衡。

3.1. CPU與GPU平臺

深度學習加速:算法、編譯器、體系結構與硬件設計

現有的CPU和GPU都有官方的加速庫來加速矩陣運算。CPU和GPU為了支持DLP行為,有專門的向量指令集,例如CPU的SIMD指令,不過SIMD畢竟是通用處理器對DLP的補充,並行度其實不算太高;還有就是GPU的SIMT,其實本質上是SIMD的一種不同的抽象方式,並且藉助GPU大量的運算核心,在並行度方面遠超支持向量處理的CPU。

SIMD和SIMT本質上是一種時間上的並行展開,屬於Temporal架構,怎麼理解呢?就好比一個循環並行,我每個並行計算單元做的是某次迭代的事,相當於把通用CPU在一段時間內的循環操作在我們的加速器的並行單元上一次性展開了。

深度學習加速:算法、編譯器、體系結構與硬件設計

SIMD/SIMT本質上是一種Temporal Arch

進一步,有些CPU/GPU支持INT8 INT16低精度運算,在取相同的數據量情況下,還可以進行更多的乘法運算(4個INT8 相當於原先一個 FP32所佔的空間)。

深度學習加速:算法、編譯器、體系結構與硬件設計

CPU和GPU的設計考慮

  1. 軟件(編譯器)
  • 通過對卷積操作的變換來減少不必要的MACs,例如前面提到的卷積近似計算
  • 通過編譯時的優化PASS對循環執行進行調度(循環展開、循環tile等等)
  • 編譯器優化減少load,store指令,儘量用寄存器操作
  1. 硬件
  • 提高PEs處理每個MAC的時間.
  • 提高並行度(使用更多並行的PEs,例如PE陣列).
  • 提高PE的利用率. 意思是儘量使得PE忙碌,一個方法是增加片上緩存大小,提高訪存總線寬度等等入手,這些都旨在減少訪存的瓶頸,PEs可以更快拿到數,減少等待時間;另一種方式是讓數據儘可能在數據通路中多停留,這樣在訪存能力不變的情況下,就可以用流水線的形式讓PE儘可能有事做,後面講解的TPU所用的脈動陣列,和流行的 DtaFlow數據流架構都是類似方式,當然在GPU的設計上也可以融入上述通用思想.

3.2. Domain-Specific 硬件設計

設計關注點

深度學習加速:算法、編譯器、體系結構與硬件設計

上圖展示了DNN加速器在體系結構設計所關注的幾個點:數據的讀寫也就是訪存,以及計算。尤其是訪存問題,因為通常PE數目很容易堆上去,並行度上去後,數據讀寫往往會成為瓶頸,也就是常說的撞到“內存牆”,內存訪問在我們的機器上是有層級的,寄存器,cache很快,但是到了內存取數就很慢,數據在cache miss後代價會很大,因此如果沒有很好的訪存優化,那麼對於大數據的深度學習,加速比是很難做上去的。

深度學習加速:算法、編譯器、體系結構與硬件設計

從DNN加速器的經典實現 DianNao系列、TPU、Eyeriss來看(後面會講到),這些加速器都十分關注訪存方面的設計,後續有時間就其一些設計細節展開。不過大體上優化訪存的方式有:

研究新的內存製作工藝:這個沒那麼沒那麼容易其實

從更下級cache取數:一般CPU從L1 cache取數,但是L1 cache能裝的數比較少,我看到有些加速器從L2 cache取數,因為L2 更大一些,取數 miss幾率小,不過這麼做要解決一下cache一致性問題(

Hwacha )。

增大加速器的片上buffer:好理解,如果我加速器的buffer大的可以裝進更多權重,那麼計算部件PE要消費數據的時候取數從片上buffer取,那肯定比從內存取數要快。

數據重用:如果這個PE產生的結果下一次計算會被接著用上,那麼可以讓這個數據留在PE中,等待下次複用,這樣就減少了數據被store回去、下一次又load回來的開銷。從另外一個角度看,第二種方式本質上也是在利用數據的複用,只不過把數放在片上buffer來複用,複用的粒度不同。

兩類設計範式

深度學習加速:算法、編譯器、體系結構與硬件設計

  • Temporal Arch. 這個前面有提及主要以SIMD,SIMT為代表.
  • Spatial Arch. 也就是數據流模式. 相比Temporal Arch的不同之處就是,數據流模式中PE與PE之間是可以相互通信的,也就是說數據可以在PE間流動,這一週期計算出來的結果可以在下一週期流到其他PE參與運算,而不用發生訪存動作。所以,這樣帶來的好處是有更大的數據吞吐量,適合具有很好數據重用特性的應用。
深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習應用的data reuse機會

深度學習加速:算法、編譯器、體系結構與硬件設計

前面說道,處理訪存問題是DNN加速器設計的一個關鍵,一個方法就是發掘卷積操作中的數據複用性。

卷積神經網絡三個角度複用,一是複用權重;二是複用激活(也就是feature map);三是複用兩者。不同的複用策略,可以引申出不同設計架構和取數策略:

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

加速器Eyeriss則是採用了 行復用的方式:

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

加速器設計一些可以利用的點

  • 稀疏性質
深度學習加速:算法、編譯器、體系結構與硬件設計

為什麼稀疏是一個對硬件設計友好的特性呢?首先,減少不必要的乘法操作。0乘上任何數都是0,那麼如果有0出現的話可以不使能乘法單元去計算,直接輸出結果0,這樣可以節省運算產生的功耗;乘法的操作數有0的話,不必費力去內存取0,直接有個存0寄存器輸出0給乘法單元即可,這樣就減少了數據搬移的開銷。體系結構設計上可以利用剪枝的思想,把很小的只裁剪成0,這樣就增大數據稀疏性,這裡說的剪枝是硬件實現的剪枝。

  • 低精度

前面算法層面有講過量化的加速作用,但是量化本身其實是一個軟硬件結合的設計,怎麼理解呢?你在程序上實現的量化算法,落到指令架構級別都需要硬件的支持才行。這裡再加強一下,INT8低精度支持可以使得數據的搬移量減少,同時整形操作速度更加快從而起到加速效果,目前許多設備都開始支持低精度推理

深度學習加速:算法、編譯器、體系結構與硬件設計

  • 壓縮

同樣,如果硬件支持壓縮功能,那麼也可以減少數據搬移,例如,Eyeriss在寫回數據時候會進行壓縮編碼再寫回,這樣大大減少訪存的時間。

深度學習加速:算法、編譯器、體系結構與硬件設計

一些經典加速器設計案例分析

DianNao

深度學習加速:算法、編譯器、體系結構與硬件設計

可以說是深度學習加速器的開山之作,比TPU還要早。裡面的一些設計哲學就是在分析了DNN的行為模式後設計的。論文的貢獻在於:

1、對大規模的CNNs和DNNs最先進機器學習算法的綜合硬件設計

  • 之前的加速器將神經網絡每一層一次性全部實現到了硬件上,這種做法只適合規模小的網絡結構。而且可拓展性很差(改動新的網絡參數或者結構發生變化那麼這個加速器就用不了了)。DianNao的利用計算單元的複用來適應大規模的網絡。

2、專注於優化訪存的性能,著重減緩”存儲牆”的問題。

  • 不用cache,用sratchpad作為buffer。因為cache的數據衝突問題會帶來高功耗。
  • 將輸入數據split成兩個獨立的輸入buffer。因為對於神經網絡來說,輸入數據和權重是兩類很明顯獨立的數據,分開的話就不用混在一起,這樣更加容易處理。考慮reuse的話,例如一個Nbin中一列輸入數據Tn需要和實際中權重矩陣中多列進行相同的運算(也就是輸入數據的重用),那麼SB中的一列的位寬可以做的大一些(NBin一列是Tn=16寬度,也就是一列放16個輸入特徵; 那麼SB一列可以設置成TnXTn,也就是SB的一列代表權重矩陣Tn列,每列Tn個權重值,一共TnXTn個數)。 從這個角度來說,將NBin和SB分開可以方便的根據reuse模式裁剪不同的位寬,如果是隻用一個統一的buffer的話就做不到這點(一個buffer只能用一種位寬)。
  • NBout在輸出最後結果之前,大部分時間可以用來緩存中間結果,這樣中間結果先不回去memory。NBout緩存的中間結果可以回去NFU2進一步計算得到最終結果。
  • NBin和SB在DMA控制下進行preload,也就是每次計算單元取數時候數已經preload在buffer中了。
  • 三個buffer用三個DMA控制,這樣方便三個buffer中各自的取數寫數操作。例如在計算過程中,SB和NBin的DMA要負責 preload 和 放數據給計算單元這兩類任務。 NBout的DMA負責將中間結果wirte進NBout緩存 以及 將最終結果load回內存 這兩類任務。可見DMA分開可以各自幹各自事,比較靈活。

PuDianNao

普電腦和DianNao的區別就在於,DianNao主要針對神經網絡,而普電腦而是支持一類機器學習算法(KNN、樸素貝葉斯、SVM、K-Means、線性迴歸、決策樹等等)的加速,屬於特定領域的通用加速(狗頭。 論文中花了大量篇幅,仔細分析了各類機器學習算法在訪存行為、計算模式上的異同,(搞體系結構的訪存真的時常掛嘴邊啊...,強烈建議搞體系結構、加速器設計的同學仔細讀一下論文裡面對各個算法案例的分析),最終得出一個結論:

各類機器學習的行為模式很大不同(相比各類DNN而言),但是從更細粒度,也就是在數據重用、計算操作(乘加、計數比較..)角度上是可以分幾類的,針對這一點設計如下的結構:

深度學習加速:算法、編譯器、體系結構與硬件設計

深度學習加速:算法、編譯器、體系結構與硬件設計

  • HotBuf和ColdBuf 就是在分析幾類算法的數據重用行性後設計的,有的數據重用距離小,有的數據重用距離大,所以將buffer根據重用距離進行分離。
  • 數據通路根據總結了幾類算法出現的高頻運算操作(有比較操作、計數、乘加(乘法+加法樹組合)、中間結果緩存、非線性運算)做成流水線,對於某種特定的類型,如果在某級不需要該運算,可以直接bypass.

PuDianNao論文的詳細分析可以參考: 中科院說的深度學習指令集diannaoyu到底是什麼? 大佬講的蠻好的。另外,如果想細究一下加速器一些具體的設計細節,可以看看 ShiDianNao ,這篇論文沒有太多架構創新,但是在加速器設計細節上講的應該是所有DianNao系列論文中最細緻的了.

TPU

深度學習加速:算法、編譯器、體系結構與硬件設計

設計哲學就是:

  • Simple and regular design:可以說,簡單和規則是脈動陣列的一個重要原則。而這樣的設計主要是從“成本”的角度來考慮問題的。
  • Balancing computation with I/O:平衡運算和I/O,應該說是脈動陣列最重要的設計目標。 儘量讓數據在運算部件中多停留。 儘量讓運算部件保持繁忙,不會因為要取數而停滯計算。

脈動矩陣

TPU使用了脈動陣列,這是一個很有趣的點,脈動陣列在很早之前就已經出現,後來被認為實用性不大,但是在深度學習時代,計算密集的大矩陣運算讓脈動矩陣煥發活力,可以算是加速器設計的重新考古發現。關於脈動陣列的計算方式可以參考 https://github.com/meton-robean/PaperNotes/issues/3

深度學習加速:算法、編譯器、體系結構與硬件設計

簡述一下脈動陣列計算方式就是:權重先送進各個PE,之後input 矩陣數據從左往右流進陣列計算,之後計算結果從下面依次流出(不同列輸出相差若干個週期),沒錯,這就是一種數據流架構的實現嘛.

脈動矩陣的一些看法與分析:

1. 脈動架構是一種很特殊的設計,結構簡單,實現成本低。但它靈活性較差,只適合特定運算。而作者認為,卷積運算是展示脈動架構特點的理想應用。 2. 脈動陣列難以擴展,因為它需要帶寬的成比例的增加來維持所需的加速倍數。這違反摩爾定律和儲存速度落後於邏輯速度的技術趨勢。另外它又使延遲變得更糟,這違反了論文中提到的用戶需求的趨勢。即使TPU中的矩陣乘法單元只是脈動矩陣乘法的多種選項之一。是否可以找到可擴展並且節能的替代方案,來實現所需的傳播和累加而不以犧牲延遲時間為代價,這是一個值得思考的有趣的話題。 3. 數據重排。脈動矩陣主要實現向量/矩陣乘法。以CNN計算為例,CNN數據進入脈動陣列需要調整好形式,並且嚴格遵循時鐘節拍和空間順序輸入。數據重排的額外操作增加了複雜性,據推測由軟件驅動實現。 4. 規模適配.在數據流經整個陣列後,才能輸出結果。當計算的向量中元素過少,脈動陣列規模過大時,不僅難以將陣列中的每個單元都利用起來,數據的導入和導出延時也隨著尺寸擴大而增加,降低了計算效率。因此在確定脈動陣列的規模時,在考慮面積、能耗、峰值計算能力的同時,還要考慮典型應用下的效率。


分享到:


相關文章: