在上一篇文章中,我們用C語言實(shí)現(xiàn)了一個(gè)卷積層,并查看了結(jié)果。在本文中,我們將實(shí)現(xiàn)其余未實(shí)現(xiàn)的層:全連接層、池化層和激活函數(shù) ReLU。
每一層的實(shí)現(xiàn)
全連接層
全連接層是將輸入向量X乘以權(quán)重矩陣W,然后加上偏置B的過程。下面轉(zhuǎn)載第二篇的圖,能按照這個(gè)圖計(jì)算就可以了。
全連接層的實(shí)現(xiàn)如下。
voidlinear(constfloat*x,constfloat*weight,constfloat*bias, int64_tin_features,int64_tout_features,float*y){ for(int64_ti=0;i
該函數(shù)的接口和各個(gè)數(shù)據(jù)的內(nèi)存布局如下。
考慮稍后設(shè)置 PyTorch 參數(shù),內(nèi)存布局與 PyTorch 對(duì)齊。
輸入
x: 輸入圖像。shape=(in_features)
weight: 權(quán)重因子。shape=(out_features, in_features)
bias: 偏置值。shape=(out_features)
輸出
y: 輸出圖像。shape=(out_features)
參數(shù)
in_features: 輸入順序
out_features: 輸出順序
在全連接層中,內(nèi)部操作數(shù)最多為out_channels * in_channels一個(gè),對(duì)于典型參數(shù),操作數(shù)遠(yuǎn)低于卷積層。
另一方面,關(guān)注權(quán)重因子,卷積層為shape=(out_channels, in_channels, ksize, ksize),而全連接層為shape=(out_features, in_features)。
例如,如果層從卷積層變?yōu)槿B接層,in_features = channels * width * height則以下關(guān)系成立。width, height >> ksize考慮到這一點(diǎn),在很多情況下,全連接層參數(shù)的內(nèi)存需求大大超過了卷積層。由于FPGA內(nèi)部有豐富的SRAM緩沖區(qū),因此擅長處理內(nèi)存訪問量大和內(nèi)存數(shù)據(jù)相對(duì)于計(jì)算總量的大量復(fù)用。
單個(gè)全連接層不會(huì)復(fù)用權(quán)重?cái)?shù)據(jù),但是在視頻處理等連續(xù)處理中,這是一個(gè)優(yōu)勢,因?yàn)橐M(jìn)行多次全連接。另一方面,本文標(biāo)題中也提到的邊緣環(huán)境使用小型FPGA,因此可能會(huì)出現(xiàn)SRAM容量不足而需要訪問外部DRAM的情況。
如果你有足夠的內(nèi)存帶寬,你可以按原樣訪問它,但如果你沒有足夠的內(nèi)存帶寬,你可以在參數(shù)調(diào)整和訓(xùn)練后對(duì)模型應(yīng)用稱為剪枝和量化的操作。池化層
池化層是對(duì)輸入圖像進(jìn)行縮小的過程,這次使用的方法叫做2×2 MaxPooling。在這個(gè)過程中,取輸入圖像2x2區(qū)域的最大值作為輸出圖像一個(gè)像素的值。這個(gè)看第二張圖也很容易理解,所以我再貼一遍。
即使在池化層,輸入圖像有多個(gè)通道,但池化過程本身是針對(duì)每個(gè)通道獨(dú)立執(zhí)行的。因此,輸入圖像中的通道數(shù)和輸出圖像中的通道數(shù)在池化層中始終相等。
池化層的實(shí)現(xiàn)如下所示:
voidmaxpool2d(constfloat*x,int32_twidth,int32_theight,int32_tchannels,int32_tstride,float*y){ for(intch=0;ch
這個(gè)函數(shù)的接口是:
此實(shí)現(xiàn)省略了邊緣處理,因此圖像的寬度和高度都必須能被stride整除。
輸入
x: 輸入圖像。shape=(channels, height, width)
輸出
y: 輸出圖像。shape=(channels, height/stride, width/stride)
參數(shù)
width: 圖像寬度
height: 圖像高度
stride:減速比
ReLU
ReLU 非常簡單,因?yàn)樗皇菍⒇?fù)值設(shè)置為 0。
voidrelu(constfloat*x,int64_tsize,float*y){ for(int64_ti=0;i
由于每個(gè)元素的處理是完全獨(dú)立的,x, y因此未指定內(nèi)存布局。
硬件生成
到這里為止的內(nèi)容,各層的功能都已經(jīng)完成了。按照上一篇文章中的步驟,可以確認(rèn)這次創(chuàng)建的函數(shù)也產(chǎn)生了與 libtorch 相同的輸出。
此外,Vivado HLS 生成了一個(gè)通過 RTL 仿真的電路。從這里開始,我將簡要說明實(shí)際生成了什么樣的電路。如果將上述linear函數(shù)原樣輸入到 Vivado HLS,則會(huì)發(fā)生錯(cuò)誤。這里,將輸入輸出設(shè)為指針->數(shù)組是為了決定在電路制作時(shí)用于訪問數(shù)組的地址的位寬。
另外,in_features的值為778=392,out_將features的值固定為32。這是為了避免Vivado HLS 在循環(huán)次數(shù)可變時(shí)輸出性能不佳。
staticconststd::size_tkMaxSize=65536; voidlinear_hls(constfloatx[kMaxSize],constfloatweight[kMaxSize], constfloatbias[kMaxSize],floaty[kMaxSize]){ dnnk::linear(x,weight,bias,7*7*8,32,y); }
linear_hls函數(shù)的綜合報(bào)告中的“性能估計(jì)”如下所示:
在Timing -> Summary中寫入了綜合時(shí)指定的工作頻率,此時(shí)的工作頻率為5.00 ns = 200MHz。
重要的是 Latency -> Summary 部分,它描述了執(zhí)行此函數(shù)時(shí)的周期延遲(Latency(cycles))和實(shí)時(shí)延遲(Latency(absolute))??纯催@個(gè),我們可以看到這個(gè)全連接層在 0.566 ms內(nèi)完成。在 Latency -> Detail -> Loop 列中,描述了每個(gè)循環(huán)的一次迭代所需的循環(huán)次數(shù)(Iteration Latency)和該循環(huán)的迭代次數(shù)(Trip Count)。
延遲(周期)包含Iteration Latency * Trip Count +循環(huán)初始化成本的值。Loop 1 是out_features循環(huán)到loop 1.1 in_features。
在Loop1.1中進(jìn)行sum += x[j] * weight[i * in_features + j]; 簡單計(jì)算會(huì)發(fā)現(xiàn)需要 9 個(gè)周期才能完成 Loop 1.1 所做的工作。使用HLS中的“Schedule Viewer”功能,可以更詳細(xì)地了解哪些操作需要花費(fèi)更多長時(shí)間。
下圖橫軸的2~10表示Loop1.1的處理內(nèi)容,大致分為x,weights等的加載2個(gè)循環(huán),乘法(fmul)3個(gè)循環(huán),加法(fadd)4個(gè)循環(huán)共計(jì)9個(gè)循環(huán)。
在使用 HLS 進(jìn)行開發(fā)期間通過添加#pragma HLS pipeline指令,向此代碼添加優(yōu)化指令以指示它創(chuàng)建高效的硬件。
與普通的 FPGA 開發(fā)類似,運(yùn)算單元的流水線化和并行化經(jīng)常用于優(yōu)化。通過這些優(yōu)化,HLS 報(bào)告證實(shí)了加速:流水線:減少迭代延遲(min=1)
并行化:減少行程次數(shù),刪除循環(huán)
正如之前也說過幾次的那樣,這次的課程首先是以FPGA推理為目的,所以不會(huì)進(jìn)行上述的優(yōu)化。
最后,該函數(shù)的接口如下所示。
由于本次沒有指定接口,所以數(shù)組接口如x_ 等ap_memory對(duì)應(yīng)FPGA上可以1個(gè)周期讀寫的存儲(chǔ)器(BRAM/Distributed RAM)。
在下一篇文章中,我們將連接每一層的輸入和輸出,但在這種情況下,我們計(jì)劃連接 FPGA 內(nèi)部的存儲(chǔ)器作為每一層之間的接口,如本例所示。總結(jié)
在本文中,我們實(shí)現(xiàn)了全連接層、池化層和 ReLU。現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了所有層,我們將在下一篇文章中組合它們。之后我們會(huì)實(shí)際給MNIST數(shù)據(jù),確認(rèn)我們可以做出正確的推論。
審核編輯:劉清
-
FPGA
+關(guān)注
關(guān)注
1631文章
21807瀏覽量
606777 -
DRAM
+關(guān)注
關(guān)注
40文章
2334瀏覽量
184000 -
sram
+關(guān)注
關(guān)注
6文章
769瀏覽量
114961 -
C語言
+關(guān)注
關(guān)注
180文章
7616瀏覽量
138054
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
C語言實(shí)現(xiàn):見縫插針游戲!代碼思路+源碼分享
介紹一個(gè)C語言實(shí)現(xiàn)的http下載器
如何用C語言實(shí)現(xiàn)面向?qū)ο缶幊?/a>
C語言實(shí)現(xiàn)的泛型函數(shù)swap()
正余弦函數(shù)曲線的C語言繪制方法
正余弦函數(shù)曲線的C語言繪制方法
用C語言實(shí)現(xiàn)DES算法
用C語言實(shí)現(xiàn)FFT算法
用C語言實(shí)現(xiàn)數(shù)字濾波
激活函數(shù)中sigmoid、ReLU等函數(shù)的一些性質(zhì)
![<b class='flag-5'>激活</b><b class='flag-5'>函數(shù)</b>中sigmoid、<b class='flag-5'>ReLU</b>等<b class='flag-5'>函數(shù)</b>的<b class='flag-5'>一</b>些性質(zhì)](https://file.elecfans.com/web1/M00/59/02/o4YBAFtiqnmAT_whAAAcdMk1YHw695.png)
使用C語言實(shí)現(xiàn)圖書館管理系統(tǒng)的源代碼和函數(shù)及軟件等資料合集
![使用<b class='flag-5'>C</b><b class='flag-5'>語言實(shí)現(xiàn)</b>圖書館管理系統(tǒng)的源代碼和<b class='flag-5'>函數(shù)</b>及軟件等資料合集](https://file.elecfans.com/web1/M00/B5/2F/pIYBAF5eDIuAWWesAAMoWf9m0KU735.png)
評(píng)論