# Hardware Accelerator for Multi-Head Attention and Position-Wise Feed-Forward in the Transformer <body> <div style="text-align: right;"> <p>CASLAB Group Meeting</p> <p>2024.02.20</p> <p>IEEE SOCC</p> <a href="https://arxiv.org/abs/2009.08605">Paper link</a> </div> --- [TOC] --- ## Background ### Transformer #### 1. Transformer Structure ##### &emsp; &emsp; 以中英文翻譯為例來說明 ##### &emsp; &emsp; 由 Encoder和Decoder組成,分別包含n, m個block。 ##### &emsp; &emsp; Step1 ##### &emsp; &emsp;&emsp; input向量X,X=單字的Embedding(從原始data萃取出的特徵)+位置的Embedding ##### &emsp; &emsp; Step2 ##### &emsp; &emsp;&emsp; 將X傳到Encoder,經過n個Encoder之後得到矩陣C,C = X_n * d(通常d=512)。 ##### &emsp; &emsp;&emsp; 其中每個Encoder block output dimension = input dimension ##### &emsp; &emsp; Step3 ##### &emsp; &emsp;&emsp; Encoder output C傳到Decoder,Decoder依序輸出透過Mask來遮住後面的單字。 ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/HkNYl4r9a.png) #### 2. Position Embedding(PE) ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/SyFRWSHcT.png) ##### &emsp; &emsp;&emsp;d = PE dimension = 單字dimension = input dimension #### 3. Self-Attention ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/SJV_LBrqp.png) ##### &emsp; &emsp;&emsp;Transformer中的Multi-Head Attention就是由多個Self-Attention組成 ##### &emsp; &emsp;&emsp;在計算的時候會用到矩陣Q, K, V,三者皆由X轉換而來 ##### &emsp; &emsp;&emsp;X * WQ = Q; X * WK = K; X * WV = V; ##### &emsp; &emsp;&emsp;得到Q, k, V之後即可求Self-Attention output ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/BkFd_Brca.png) ##### &emsp; &emsp;![GM24_0220-第 7 页 (1)](https://hackmd.io/_uploads/SkEgqCfhT.jpg) ##### &emsp; &emsp;&emsp;其中d_k是Q, K的列數 ##### &emsp; &emsp;&emsp;接著Q * K transpose之後,做softmax ##### &emsp; &emsp;&emsp;其中Softmax過後,每一行的和=1 ##### &emsp; &emsp; #### 4. Multi-head Attention(MHA) ##### &emsp; &emsp;&emsp;MHA包含多個Self-Attention ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/BkSMDht9a.png) ##### &emsp; &emsp;&emsp;各自生成h個輸出矩陣 Z_1 ~ Z_h 之後 ##### &emsp; &emsp;![GM24_0220-第 8 页 (1)](https://hackmd.io/_uploads/B19Bc0G2p.jpg) ##### &emsp; &emsp;&emsp;將 Z_1 ~ Z_h 連接後做線性變換,最終output Z與input X的dimansion相同 #### 5. Add and Norm ##### &emsp; &emsp;&emsp;X表示MHA或FFN的input ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/rkTSgTY5p.png) ##### &emsp; &emsp;&emsp;這邊的Add是指residual connection ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/HkU7b6F9T.png) ##### &emsp; &emsp;&emsp;這邊的Norm是指Layer Normalization ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/BJVvfXTsa.png) ##### &emsp; &emsp;&emsp;由上圖可以看出BatchNorm 與 LayerNorm最大的差別 ##### &emsp; &emsp;&emsp; ##### &emsp; &emsp;&emsp; ##### &emsp; &emsp;&emsp; #### 6. Feed Forward ##### &emsp; &emsp;&emsp;兩層的全連接層,第一層的Activation Function是Relu,第二層不用Activation Function ##### &emsp; &emsp;&emsp;其中input是X,輸出與X的dimension相同 ##### &emsp; &emsp;![image](https://hackmd.io/_uploads/SJE2EpKcT.png) #### 7. Decoder 與 Encoder差異 ##### &emsp; &emsp;&emsp;包含兩個MHA ##### &emsp; &emsp;&emsp;第一個MHA有Masked ##### &emsp; &emsp;&emsp;第二個MHA的K, V使用Encoder的輸出,Q是Masked的輸出 ##### &emsp; &emsp;&emsp;最後有一個Softmax #### 8. Decoder Masked(第一個MHA) ##### &emsp; &emsp;&emsp;![GM24_0220-第 9 页 (1)](https://hackmd.io/_uploads/BJ7h5CGhp.jpg) ##### &emsp; &emsp;&emsp;正常做完Q*K transpose之後 * Masked Matrix ##### &emsp; &emsp;&emsp;其中Masked Matrix的Masked部分即一極小負數,如此一來接著做softmax後,該部分趨近0 #### 9. Decoder Masked(第二個MHA) ##### &emsp; &emsp;&emsp;K, V使用Encoder的輸出,Q是Masked的輸出 ### Systolic array ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/ryEo6WTj6.png) ##### &emsp; &emsp;&emsp;上圖為SA的經典示意圖,因為往往access memory都比實際計算時間還要長,所以下面那個就是一個1D SA的範例,一次將同一個data隨時間傳遞給一整排的PE ##### &emsp; &emsp;&emsp;SA的優點是結構簡單且成本低,但缺點是靈活性很差,而且資料需要適當安排,下面以一個2D 3*3 SA為例 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/r1RDgf6i6.png) ##### &emsp; &emsp;&emsp;本篇文章就是用2D SA的概念,來加快GEMMs運算 ## Paper : Hardware Accelerator for Multi-Head Attention and Position-Wise Feed-Forward in the Transformer ### Introduction ##### &emsp; &emsp;&emsp;本篇是第一個為Transformer提出特定硬體加速器的文章。 ##### &emsp; &emsp;&emsp;MHA和FFN是Transformer的bottleneck,因此本篇文章也只著重在這2個部分,即下圖紅色框框,傳入硬體即為Q, K, V matrix,不管前面的Embedding處理。 ##### &emsp; &emsp;&emsp;![GM24_0220-第 6 页 (1)](https://hackmd.io/_uploads/SJIUPmTo6.jpg) ##### &emsp; &emsp;&emsp;貢獻 : ##### &emsp; &emsp;&emsp;1.提供一個有效的方法劃分Transformer的巨大陣列,使得MHA和FFN可以共享硬體資源。 ##### &emsp; &emsp;&emsp;2.提供了第一個硬體架構設計,可以完成這兩個Res-block的計算,其中為了確保SA的硬體Reuse rate足夠高,計算流程需要特別設計。 ##### &emsp; &emsp;&emsp;3.兩個最複雜的非線性函數scaled masked-softmax和LayerNormalization,都經過高度優化,變得對硬體更加友善。其中LayerNormalization的delay被盡可能地減少。 ##### &emsp; &emsp;&emsp;在Quantization使用int8的Transformer後,在Xilinx xcvu13pfhga2104-3-e FPGA上評估,當max sequence length=64且batch size=1的情況下,相比NVIDIA V100 GPU,MHA的部分有14.6X加速比,FFN則有3.4X ### Partitioning Matrices in the MHA and FFN ##### &emsp; &emsp;&emsp; Q, K and V通常形狀都是相同的,皆為s*d_model ##### &emsp; &emsp;&emsp; ![GM24_0220-第 5 页](https://hackmd.io/_uploads/HyGNCM6ja.jpg) ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/ryjQP5Pip.png) ##### &emsp; &emsp;&emsp;從論文"Attension is all you need"中的這張表,可以看出來基本上transformer 的d_model參數都是64的倍數。 ##### &emsp; &emsp;&emsp;所以本篇論文以s * 64為一塊,去拆分每一個大矩陣。 ### MHA ResBlock ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/BylEi9vsT.png) ##### &emsp; &emsp;&emsp;以上面這張圖來看的話,Q, K and V的形狀都是s * d_model,每一個head的weight都是d_model * 64的形狀,如此一來乘s * d_model形狀的矩陣之後,會得到s * 64的Q_h, K_h and V_h,接著K_h做transpose後與Q_h乘,然後做softmax後 * V_h,這樣就做完一個head的一次self-attention。接著將多個head的output接在一起之後, * W_G,即完成了一次MHA。 ##### &emsp; &emsp;&emsp;註: 上面提到的Q, K and V是加速器的input,即一般transformer的input X(推測是為了保有彈性,沒有硬性規定這邊的Q=K=V) ##### &emsp; &emsp;&emsp;MHA的output + Q,即residual connection(殘差連接),得到Matrix G。 ##### &emsp; &emsp;&emsp;Matrix G接著做LayerNorm之後就是一次MHA ##### &emsp; &emsp;&emsp;註 : 這張圖上的 general matrix-matrix multiplications (GEMMs)幾乎都能用s * 64的SA完成,且使用率很高。 ##### &emsp; &emsp;&emsp;除了Q * K_transpose的時候,如果s比64小的話就會需要再額外切成像下圖這樣,有可能會造成浪費,且需要分好幾次做。 ##### &emsp; &emsp;&emsp;![GM24_0220-第 3 页](https://hackmd.io/_uploads/HJICiUnjT.jpg =80%x) ##### &emsp; &emsp;&emsp;![GM24_0220-第 4 页](https://hackmd.io/_uploads/BJsRo8nsa.jpg =30%x) ##### &emsp; &emsp;&emsp;但如果是s比64大,的話就會需要再額外切成像下圖這樣,有可能會造成浪費,且需要分好幾次做。 ##### &emsp; &emsp;&emsp;![GM24_0220-第 1 页](https://hackmd.io/_uploads/B1LGFL2j6.jpg =50%x) ##### &emsp; &emsp;&emsp;![GM24_0220-第 2 页](https://hackmd.io/_uploads/SyAGtLhjp.jpg =30%x) ##### &emsp; &emsp;&emsp;只有當s = 64的時候,才不會有浪費。 ##### &emsp; &emsp;&emsp;但這部分只佔所有GEMMs運算的一小部分 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/r1wLhL2sT.png) ##### &emsp; &emsp;&emsp;因為s通常不大於128的關係,所以可以化減成這條式子。 ### FFN ResBlock ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/Skxs6kcsa.png) ##### &emsp; &emsp;&emsp;上圖是FFN的部分,這邊就很單純,X * W_1 + b_1,接著重點是ReLU,因為這邊要取Max(0, XW_1 + b_1),因此取ReLU的話>0的就會被保留,要是都沒有>0的部分就都會是0。接著 * W_2後 + b_2即FFN的輸出。接著 + input X 即residual connection(殘差連接),之後做LayerNorm,如此一來即Encoder的output。 ### Partition Weight Matrix ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/H1mmhg9iT.png) ##### &emsp; &emsp;&emsp;本篇論文是以s * 64為單位,所以以上圖為例的話,實際上會拆成許多s * 64的矩陣,且以常見的transformer model來看,s通常都是能整除d_model的。 ### Pseudo Code ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/rJMkWP2j6.png) ##### &emsp; &emsp;&emsp;由上到下 ##### &emsp; &emsp;&emsp;第一個for loop : ##### &emsp; &emsp;&emsp;跑h次self-attension,這邊的h是指head數量 ##### &emsp; &emsp;&emsp;第二個for loop : ##### &emsp; &emsp;&emsp;將self-attension的輸出 * W_G,且 + Q (即Add and Norm的Add),這邊的h是指weight被切成h塊 ##### &emsp; &emsp;&emsp;第三個for loop : ##### &emsp; &emsp;&emsp;用ReLU取max(0, XW_1 + b_1) ##### &emsp; &emsp;&emsp;第四個for loop : ##### &emsp; &emsp;&emsp;* W_2 + b_2之後 + X (即Add and Norm的Add) ### System Architecture ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/ryj8wD3sp.png) ##### &emsp; &emsp;&emsp;-------------------------------------------------------------------------------- MHA start -------------------------------------------------------------------------------- ##### &emsp; &emsp;&emsp;Step1. ##### &emsp; &emsp;&emsp;左邊的mux選Q,上面的mux選weight memory(W_Q傳進來),傳入SA module,做完QW得到Q之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step2. ##### &emsp; &emsp;&emsp;左邊的mux選K,上面的mux選weight memory(W_K傳進來),傳入SA module,做完KW得到K之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step3. ##### &emsp; &emsp;&emsp;左邊的mux選Q,上面的mux選Temp2(從上面adder的output拉過來),傳入SA module,做完Q * K_transpose之後傳入Softmax Module(這裡面會做/(d_k^0.5)) ##### &emsp; &emsp;&emsp;Step4. ##### &emsp; &emsp;&emsp;Softmax Module在做事的時候,SA是空閒的,所以可以平行運算。因此左邊的mux選V,上面的mux選weight memory(W_V傳進來),傳入SA module,做完VW得到V之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step5. ##### &emsp; &emsp;&emsp;左邊的mux選Temp1(softmax output傳進來),上面的mux選Temp2(從旁邊adder的output拉過來),傳入SA module,做完 * V之後,即完成一次self-attension ##### &emsp; &emsp;&emsp;Step6. ##### &emsp; &emsp;&emsp;左邊的mux選P,上面的mux選weight memory(W_G傳進來),傳入SA module,做完PW得到P之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step7. ##### &emsp; &emsp;&emsp;Step6的output傳到下面的adder,input Q 拉進來adder(綠色那條線),加完之後傳到LayerNorm module ##### &emsp; &emsp;&emsp;Step8. ##### &emsp; &emsp;&emsp;從LayerNorm Module出來之後即完成MHA ##### &emsp; &emsp;&emsp;註 : step1 ~ step5要做h次,才會做step6 ##### &emsp; &emsp;&emsp;-------------------------------------------------------------------------------- MHA end -------------------------------------------------------------------------------- ##### &emsp; &emsp;&emsp;-------------------------------------------------------------------------------- FFN start -------------------------------------------------------------------------------- ##### &emsp; &emsp;&emsp;Step1. ##### &emsp; &emsp;&emsp;左邊的mux選X,上面的mux選weight memory(W_1傳進來),傳入SA module,做完XW得到X之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step2. ##### &emsp; &emsp;&emsp;將adder的output傳入ReLU,即得到P ##### &emsp; &emsp;&emsp;Step3. ##### &emsp; &emsp;&emsp;左邊的mux選P,上面的mux選weight memory(W_2傳進來),傳入SA module,做完PW得到P之後,傳到旁邊的adder加bias ##### &emsp; &emsp;&emsp;Step4. ##### &emsp; &emsp;&emsp;Step3的output傳到下面的adder,input X 拉進來adder(綠色那條線),加完之後傳到LayerNorm module ##### &emsp; &emsp;&emsp;-------------------------------------------------------------------------------- FFN end -------------------------------------------------------------------------------- ### Softmax module ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/S1rdNn3jT.png) ##### &emsp; &emsp;&emsp;公式4中的D = Q * K_transpose, Y = softmax output, i = head number, j = D's row number ##### &emsp; &emsp;&emsp;由公式4可知,當M=0,即沒有Mask的時候,正常做softmax,當M=1時,輸出直接給0。 ##### &emsp; &emsp;&emsp;因為softmax和V matrix的SA運算是平行的,所以只要確保softmax的運算不要變成critical path即可。觀察式子可以發現又做除法又做exp對於硬體負擔還是蠻大的,因此將公式4化簡為公式5。 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/Hyp_N23ja.png) ##### &emsp; &emsp;&emsp;其中X_max是為了避免overflow的常見方法,即分子分母同除exp(X_max)。 ##### &emsp; &emsp;&emsp;接著使用log sum-exp trick,即可推出最終式,詳細推導如下。 ##### &emsp; &emsp;&emsp;![IMG_1151](https://hackmd.io/_uploads/SkSHya2s6.jpg) ##### &emsp; &emsp;&emsp;如此一來,就可以避免掉除法的計算。 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/r1sO6shop.png) ##### &emsp; &emsp;&emsp;上圖中,向右shift 3 bits即除8的意思 ##### &emsp; &emsp;&emsp;Stage 1 : 藍箭頭 ##### &emsp; &emsp;&emsp;當M = 0時,找D最大值,後傳入EXP Unit ##### &emsp; &emsp;&emsp;當M = 1時,mux選擇負無限大 ##### &emsp; &emsp;&emsp;Stage 2 : 紫箭頭 ##### &emsp; &emsp;&emsp;D和D_max傳入EXP Unit之後,相減做exp,接著傳出去做累加 ##### &emsp; &emsp;&emsp;Stage 3 : 綠箭頭 ##### &emsp; &emsp;&emsp;將累加完的exp(D - D_max)做ln ##### &emsp; &emsp;&emsp;Stage 4 : 橘箭頭 ##### &emsp; &emsp;&emsp;接著做完最後的相減之後做exp,輸出即為所求Y ##### &emsp; &emsp;&emsp;註 : 推測EXP Unit裡面有減法器 ### Layer Normalization module ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/S1Ej4phjT.png) ##### &emsp; &emsp;&emsp;註 : 這裡講的64h cycle指G matrix有64h rows ##### &emsp; &emsp;&emsp;原本在做Layer Normalization的流程是最上面的框框,計算完Matrix G之後依序算出E(G)和var(G)再求出output ##### &emsp; &emsp;&emsp;其中E(G)和var(G)是2個最花時間的步驟,因此此篇論文的方法就是想辦法讓這2個步驟能和Calculate Matrix G平行運算,如此一來就能大幅度降低總體運算時間。 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/BkFZLanip.png) ##### &emsp; &emsp;&emsp;式子6是常見的LayerNorm公式,其中ε = 10^-8,為了確保分母不要為0,E(G)為G每一列的平均,var(G)為G每一列的變異數,γ是縮放參數,β是平移參數 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/Hk51Ppnia.png) ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/r1elPa3ia.png) ##### &emsp; &emsp;&emsp;本論文中,處理E(G)的方法是在計算Matrix G的時候就先累加,不用等全部的G算完才累加。 ##### &emsp; &emsp;&emsp;處理var(G)的方法是將式子8改寫完下式,推導也是常見的變異數公式 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/SySTv63i6.png) ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/rJf-_p3ip.png) ##### &emsp; &emsp;&emsp;接著就是硬體實現的部分 ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/ryyI_6hsp.png) ##### &emsp; &emsp;&emsp;藍色框框是將計算完的G傳進來做累加,算出E(G)之後求出E(G)^2 ##### &emsp; &emsp;&emsp;橘紅色框框是做(G - E(G)) * γ ##### &emsp; &emsp;&emsp;綠色框框是計算變異數,將G^2 之後做累加,然後與E(G)^2從藍色框框傳進來,與ε一同加減之後,開根號 ##### &emsp; &emsp;&emsp;最終傳回橘紅色框框做相除,然後+ β即為LayerNorm的output ##### &emsp; &emsp;&emsp;註 : 開根號是使用LUT查表 ### Result : Quantization ##### &emsp; &emsp;&emsp;Training Data : ##### &emsp; &emsp;&emsp;IWSLT 2016 German-English parallel corpus,德英平行語料庫是指由國際語音和語言技術協會(International Workshop on Spoken Language Translation,簡稱IWSLT)在2016年發布的一個德語到英語的平行語料庫 ##### &emsp; &emsp;&emsp;“tst2014” ##### &emsp; &emsp;&emsp;註 : BLEU score為Decoder output與答案做比較得到的分數 | 配置 | BLEU score | | -------- | -------- | | All use FP32 | 23.88 | | GEMMs use INT8 | 23.48 | | GEMMs use INT8 and quantize softmax module | 23.57 | ### Result : Hardware Implementation ##### &emsp; &emsp;&emsp; | FPGA | | | -------- | -------- | | Parameter | batch size = 1, s = 64 | | Board | Xilinx xcvu13p-fhga2104-3-e FPGA | | Environment | Vivado 2018.2| | Operating Freq. | 200MHz| | Power | 16.7W (13.3W dynamic and 3.4W static)| ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/rkx7_WTsT.png) ##### &emsp; &emsp;&emsp;![image](https://hackmd.io/_uploads/HJ8ZdZpop.png) ##### &emsp; &emsp;&emsp;上表是在s=64最理想的情況下,跑transformer base model,只看MHA與NVIDIA V100相比,加速比為14.6X,只看FFN,加速比為3.4X ### Evaluation ##### &emsp; &emsp;&emsp;s * 64 MACs ##### &emsp; &emsp;&emsp;Input SRAM:2 * (s * dmodel) B (GEMMs INT8) ##### &emsp; &emsp;&emsp;Weight SRAM:(dk * dff) B ##### &emsp; &emsp;&emsp;Bias SRAM:(4 * dmodel) B ##### &emsp; &emsp;&emsp;Output SRAM:(s * dmodel) B ##### &emsp; &emsp;&emsp;Others SRAM:(s * dff) B and (s * dk) and (s * dmodel) ##### &emsp; &emsp;&emsp; | Suppose transformer base model | h=8, dk=64, s=64 | | -------- | -------- | | input | 64KB | | weight | 128KB | | bias | 2KB | | output | 32KB | | temp1 | 32KB | | temp2 | 4KB | | P | 128KB | | total | 390KB | --- ## Reference [【機器學習2021】Transformer (下)](https://youtu.be/N6aRv06iv2g?si=DEdk0J5Pbfqzzydg) 0:11:30 Transformer architecture 0:23:40 NAT 0:26:00 Cross attention 0:56:00 GLEU score [Transformer模型詳解](https://zhuanlan.zhihu.com/p/338817680) [Attention is all you need 導讀](https://www.youtube.com/watch?v=nzqlFIcCSWQ) [LayerNormalization詳解](https://blog.csdn.net/sinat_34072381/article/details/106173365) [Systolic Array](https://zhuanlan.zhihu.com/p/26522315) </body>