這是新的系列教程,在本教程中,我們將介紹使用 FPGA 實現(xiàn)深度學(xué)習(xí)的技術(shù),深度學(xué)習(xí)是近年來人工智能領(lǐng)域的熱門話題。
在本教程中,旨在加深對深度學(xué)習(xí)和 FPGA 的理解。
用 C/C++ 編寫深度學(xué)習(xí)推理代碼
高級綜合 (HLS) 將 C/C++ 代碼轉(zhuǎn)換為硬件描述語言
FPGA 運(yùn)行驗證
在之前的文章中,我們已經(jīng)依次抽取了推理核的任務(wù)并行度和循環(huán)并行度。在本文中,我們將提取推理內(nèi)核的數(shù)據(jù)并行性。
數(shù)據(jù)并行
數(shù)據(jù)并行性表示待處理數(shù)據(jù)之間的并行度。
下面的代碼是一個簡單的向量加法運(yùn)算,但是由于c[0]和c[1]計算之間沒有依賴關(guān)系,所以可以同時計算。這種并行性就是數(shù)據(jù)并行性。
for(inti=0;i
另一方面,在以下處理的情況下,b[1]的值取決于b[0] ,因此在這種情況下無法提取數(shù)據(jù)并行性。
for(inti=0;i
補(bǔ)充一下和上次討論的循環(huán)并行的區(qū)別,在循環(huán)并行中,每個進(jìn)程都在一個流水線中執(zhí)行。因此,上述向量相加處理的處理波形如下。
循環(huán)并行
另一方面,提取數(shù)據(jù)并行性對應(yīng)于復(fù)制運(yùn)算單元。復(fù)制兩個計算器時的波形如下。
數(shù)據(jù)并行
處理數(shù)據(jù)并行性的難點(diǎn)在于,我們提取的數(shù)據(jù)并行性越多,消耗的硬件資源就越多。訪問內(nèi)存資源(加載/存儲)特別容易出現(xiàn)問題。請注意,F(xiàn)PGA 中的 BRAM 每個周期最多只能發(fā)出 2 次加載/存儲,因此如果每個周期需要超過 3 次加載/存儲,則 BRAM 將加倍(如果訪問目標(biāo)是 BRAM)。
如下圖所示,可以同時提取循環(huán)并行度和數(shù)據(jù)并行度。
數(shù)據(jù)+循環(huán)并行
本文所有代碼都是數(shù)據(jù)+循環(huán)并行的同時提取。
卷積處理中的數(shù)據(jù)并行
在卷積過程的6級循環(huán)(輸出通道,y坐標(biāo),x坐標(biāo),輸入通道,內(nèi)核y方向,內(nèi)核x方向)中可以提取出各種并行性。
在本文中,我們將提取其中兩者的并行性,提高內(nèi)核的性能。
首先,可以提取的并行度是下圖所示的處理像素之間的并行度。
處理像素之間的并行性
由于橙色和藍(lán)色像素的計算是相互獨(dú)立的,所以它們可以同時進(jìn)行。此外,用于卷積計算的內(nèi)核(黃色)可以為兩種計算共享。因此,一個過程(兩個卷積)所需的內(nèi)存訪問是兩次像素讀取和一次內(nèi)核讀取。
第二種并行度是下圖所示的輸出通道之間的并行度。
輸出通道之間的并行度
由于黃色和藍(lán)色核的卷積計算是相互獨(dú)立的,所以它們也可以同時進(jìn)行。在這個例子中,內(nèi)核需要兩次讀取,但像素只需要一次讀取。一個進(jìn)程(卷積)所需的內(nèi)存訪問是一次像素讀取和兩次內(nèi)核讀取。
兩種并行性的圖形表示如下所示。2個pixel reads和2個kernel reads可以計算出4個輸出值。
處理像素之間的并行度+輸出通道之間的并行度
基于此圖要實現(xiàn)的HW框圖如下。
卷積處理塊
此處重要的是計算單元 (PE) 可以以網(wǎng)格模式排列。
僅對一個通道(像素/輸出通道)進(jìn)行數(shù)據(jù)并行化時,并行度只排列一個n算子。n前面提到,并行度的增加會導(dǎo)致內(nèi)存資源使用量的增加,比如BRAM,在一個通道進(jìn)行并行化時無法充分使用DSP。
另一方面,如上圖所示,如果將兩個通道并行化,則像素側(cè)的并行度設(shè)為n,輸出通道側(cè)的并行度設(shè)為m,則總內(nèi)存訪問端口數(shù)相對于n+m的增加量,運(yùn)算器可以排列n*m個。這樣一來,F(xiàn)PGA中的大部分資源都可以分配給運(yùn)算。許多DNN體系結(jié)構(gòu)(如xDNN)都是這樣同時提取多通道數(shù)據(jù)并行性的。
代碼更改
由于兩個通道的數(shù)據(jù)并行化是完全一樣的,本文只描述兩個通道并行化的結(jié)果。另外,在上一篇文章中使用移位寄存器達(dá)到了II=1,但是這里我們使用以前的版本。后面會介紹使用方法。
修改后的卷積函數(shù)如下所示:完整的代碼后續(xù)會開源。
203template204staticvoidconv2d_unrolled_v2(constfloat*x,constfloat*weight,constfloat*bias,int32_twidth,int32_theight, 205int32_tin_channels,int32_tout_channels,int32_tksize,float*y){ 206 207for(int32_tblock_och=0;block_och
主要變化如下。
1、添加模板參數(shù)UNROLL_X, UNROLL_OCH(L203)
2、將輸出通道回路更改為 2 級(L207、L217、L247)
3、將 x 方向循環(huán)更改為 2 步(L209、L219、L249)
4、重復(fù)總和寄存器 (L210)
5、添加了新的編譯指示unroll(L218、L220、L248、L250)
6、設(shè)置內(nèi)循環(huán)管道II為4(L216)
1、添加了一個模板參數(shù),以便可以在外部指定函數(shù)的性能。Vivado HLS / Vitis 與 C++ 模板的兼容性非常好,不僅可以像這次這樣使用,還可以像這樣進(jìn)行設(shè)置#pragma HLS pipeline II=
。對于習(xí)慣寫Verilog HDL等的人來說,parameter幾乎可以像模塊語法一樣使用。 2中執(zhí)行的輸出通道更改為 2 級,內(nèi)部循環(huán)旋轉(zhuǎn)了數(shù)據(jù)并行化時的并行度,local_och外部block_och循環(huán)旋轉(zhuǎn)了UNROLL_OCH寬度。如果這樣做,當(dāng)輸出通道數(shù)UNROLL_OCH不是2的倍數(shù)時,會發(fā)生數(shù)組元素之外的訪問,因此需要像 L221 這樣的處理。
此外,local_och循環(huán)被移動到原始代碼中最內(nèi)層循環(huán)的循環(huán)內(nèi)kw。此更改不會影響此問題的輸出,但請注意,根據(jù)正在處理的問題,可能存在更改輸出的依賴項。
3和2的修改完全一樣,4的求和寄存器變成了存儲上述格運(yùn)算單元(PE)計算結(jié)果的寄存器。
5#pragma HLS unroll是用于將數(shù)據(jù)并行性應(yīng)用于循環(huán)并布置多個運(yùn)算單元的編譯指示。默認(rèn)情況下,運(yùn)算符根據(jù)循環(huán)的迭代次數(shù)重復(fù),但factor=N可以通過提供參數(shù)來控制運(yùn)算符的重復(fù)次數(shù)。
由于我們將這個 pragma 設(shè)置為local_och, local_w兩個循環(huán),所以 L223-239 中的求和操作和 L252-L256 中的偏置加法和輸出操作UNROLL_W * UNROLL_OCH是重復(fù)的??雌饋鞮239進(jìn)行x, weight的加載處理也UNROLL_W * UNROLL_OCH加了1,但是由于編譯器優(yōu)化,每個端口UNROLL_W, UNROLL_OCH只加1。
6中設(shè)置的II=4的值就是我們在上一篇文章中看到的處理延遲。在上一篇文章中,我介紹了一種使用移位寄存器實現(xiàn)如下波形的技術(shù)。
在這個例子中,我們使用了一個移位寄存器來處理每個周期切換輸出目標(biāo)寄存器,但是如果你像這次做數(shù)據(jù)并行化,你只需要每個周期切換數(shù)據(jù)作為計算目標(biāo)。我只是更改了左側(cè)的標(biāo)簽,但是如果對此進(jìn)行說明,它將如下圖所示。
如果這樣做,就不需要像上次那樣需要移位寄存器之間的求和,所以效率很高。
設(shè)置 II=4 的另一個原因是x, weight端口數(shù)。它們基本上是作為 BRAM 實現(xiàn)的,每個周期總共允許兩次讀/寫。由于x只是在卷積層處理的過程中被卷積層讀取,所以每個周期最多可以讀取2次,但是這樣一來,當(dāng)數(shù)據(jù)并行化的并行度設(shè)置為4時,端口數(shù)就不夠用了。
由于這次II=4,當(dāng)并行度為4時,每4個周期供給4個數(shù)據(jù)就足夠了。它只需要 1 個讀取端口,因此 BRAM 默認(rèn)帶寬就足夠了??焖僬f明如果想要超過 16 度的并行度該怎么做,這可以通過#pragma HLS array_partition pragma來完成,下面附上UG902中的示意圖。
C 中定義的數(shù)組默認(rèn)為 1 端口數(shù)組,如圖中左側(cè)所示。如果它像我在上一篇/這篇文章中使用的那樣擴(kuò)展為一個寄存器,或者如果它被并發(fā)訪問,就像在這個complete循環(huán) 其實適當(dāng)設(shè)置這個pragma而不降低II才是王道,但是這個pragma必須定義在數(shù)組資源(本次函數(shù))的定義范圍內(nèi),而且改變需要時間,所以這次沒有使用。
綜合結(jié)果/性能
使用x方向平行度為4、輸出通道方向平行度為4的卷積函數(shù)綜合推理函數(shù)。由于這個卷積函數(shù)如上所述有II=4,所以它每4個周期執(zhí)行16次操作。在這個配置中,需要的運(yùn)算單元個數(shù)為16/4 = 4,每個內(nèi)存的訪問端口為一個4/4 = 1端口。
查看綜合結(jié)果,卷積函數(shù)中浮點(diǎn)運(yùn)算單元資源使用量變化如下。
原始配置有兩個 fadds 和一個 fmul,具有兩個 fadds 的那個full_dsp_1用于添加偏差。另一方面,在數(shù)據(jù)并行化后的結(jié)果中,fadd為6,fmul為4。除了偏置加法的數(shù)量full_dsp_1增加到2之外,fadd/fmul各為4,資源量符合預(yù)期。
性能報告如下,conv2的執(zhí)行時間,上篇文章時為0.423ms,加速到82.875us。
審核編輯:湯梓紅
-
FPGA
+關(guān)注
關(guān)注
1629文章
21729瀏覽量
602977 -
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40275 -
人工智能
+關(guān)注
關(guān)注
1791文章
47183瀏覽量
238237 -
C++
+關(guān)注
關(guān)注
22文章
2108瀏覽量
73618 -
深度學(xué)習(xí)
+關(guān)注
關(guān)注
73文章
5500瀏覽量
121111
原文標(biāo)題:從FPGA說起的深度學(xué)習(xí)(八)-數(shù)據(jù)并行性
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論