---
# System prepended metadata

title: FPGA Group10 Final Project Report

---

---
title: FPGA Group10 Final Project Report
---
FPGA Group10 Final Project Report
===
### Lenet5 on FPGA 之分析與實踐嘗試
Group Member:
* E24066226 魏晉成
* E24066470 余采蓴
[TOC]

## Motivation
### 主要參考論文
* A PYNQ-based Framework for Rapid CNN Prototyping
* E.Wang and J.J and P.Y.K Cheung
* IEEE Symposium on Field-programble Custom Computing Machines(FCCM)
* 2018

論文所做約可分成以下三個部分:
1. Inference Network : 利用quantization技巧將weight從floating point縮減成8-bit integer
2. Interface : 自行編寫的python，藉此由PS端控制PL端
3. Hardware : 使用HLS生成bitstream

我們以此論文內容為基礎，再做更進一步探討，有論文作為背景支持下，能更有效率進行實驗。比如論文有現成的interface，使我們能快速porting；有lenet5 pretrain model，使我們可專心於硬體設計；也有現成baseline，利於進行比較。

### 目標
1. 參考論文dataflow並進一步改良
2. 與單純PS處理做運算比較
3. 與原先HLS產生硬體的速度進行比較

## 論文介紹
### 論文所使用之網路
論文探討藉vivado vitis HLS對cifar10和lnet5進行硬體加速，但我們在進行重建時，無法產生cifar10的ip，因此主要針對Lenet5進行討論。
![](https://i.imgur.com/L7L3ufQ.png)

論文中所使用的lenet5網路與傳統(上圖)略有不同，是作者新提出的(如下圖所示)，主要差別在於作者提出的架構input size由$32\times32$變成$28\times28$，還有減少一層fully connected layer，fully connected layer輸入和輸出也有所不同。
![](https://i.imgur.com/OKAghP7.png =150x)


### 論文所使用之硬體架構
![](https://i.imgur.com/HQad23G.png)
從電腦端(PS)藉操控jupyter notebook輸入CNN input feature maps，並通過DDR memory與AXI DMA controller進行溝通，在PL端進行前面所述Lenet5架構，最後將做完辨識的分類結果傳回PS端。

![](https://i.imgur.com/kUPJMz0.png)
而在論文中，驅動 LeNet Engine 的方式，是先以軟體建構神經網路架構，並且將每個節點的權重輸入就完成軟體初始化的部分；接著再透過去軟體的神經網路中拿取權重的方式，將權重一一透過 AXI DMA 輸入到 Lenet Engine ，因為在作者的神經網路架構中，共有 2 層卷積層與 2 層全連接層，因此需要輸入四層權重，輸入完後便完成硬體的初始化的部分。最後就可以準備將圖片透過 AXI DMA 放入 LeNet Engine 中進行 inference 了。

### 論文所使用之卷積運算方式
![](https://i.imgur.com/CQljJX9.png)
在論文中，卷積並非以 Kernel 乘上 sliding window 內的 input 達成，而是將其分成以下兩個階段
1. 將 sliding window 內的資料展開成矩陣的 stream convolution input generator (SCIG)
![](https://i.imgur.com/RDEbR0c.png)
以第一層的 $28\times28\times1$ 的 input feature map 為例，首先 SCIG 會將其展開成寬度為 $25\times1$ 的矩陣，然後累計執行 $24\times24$ 次 (因為沒有做 zero-padding 的情況下導致 output feature map 在每個邊少掉 4 個 pixel )，便能得到一個已經展開的 $25\times576$ 的矩陣
2. 將展開的矩陣進行矩陣乘法的 stream matrix multiplier (SMM)
![](https://i.imgur.com/j9pM9dd.png)
以第一層已經被展開的 $25\times576$ 的矩陣為例，在 SMM 中，會與 $25\times20$ (20 個 output channel ) 的矩陣做相乘。在圖中可以看到與第一個 kernel 相乘的效果，就是把第一列展開後的 input feature map 與第一列 kernel 做內積，便可以得到 output feature map (0, 0) 位置的第一個 channel 的輸出；接著再重複進行其他 kernel 對第一列展開後的 input feature map 相乘的動作，便可以得到 output feature map (0, 0) 位置所有 output channel 的輸出，接著再換成下一列 input feature map 即可。

### 論文所使用之全連接層運算方式
![](https://i.imgur.com/cT6uKUh.png)
全連接的方式與矩陣乘法其實相同，如果以最後一層的 <1, 500> 大小的 input feature map 進行 500 -> 10 的全連接層計算，等價於 <1, 500> 的矩陣與 <10, 500> 的矩陣進行矩陣相乘。而在論文的 data flow 裡，使用的是先將第一列 weight 與 input feature map 內積，得到 output feature map 的第一個 element ，再以第二列 weight 與 input feature map 內積的方式進行。

### 詳細數據分析
![](https://i.imgur.com/sgnmhtB.png =300x)
|     | Accuracy | Execution Time |
| --- | -------- | -------------- |
| CPU | 0.985    | 21.1s          |

為了驗證原作者的 quantization 方法是否真的能夠達成辨識的效果，因此我們先嘗試使用 CPU 端來執行 quatized 後的 model 的 inference 精確度測試。
不過因為原作者讀取神經網路權重是以 Caffe 這個 framework ，然而 inference 是用 lasagne 這個 framework ，一來太過繁複，二來 Caffe 的安裝遇到一些困難，因此我在 Linux 的電腦上先將 Caffe 的權重讀進 lasagne 的神經網路內，再將 lasagne 網路中的權重轉成 int8 的型態並且讀出來，最後透過 numpy 將權重儲存成 npz 的格式後放到 pynq 上。
在 pynq 上我們直接以 numpy 將權重讀出來後便可以使用 lasagne 進行 inference；同時即便經過 quantized ，使用 mnist dataset 的準確度也在 98% 以上，代表 quantization 能在不丟失精度情況下減少運算量。
| Layer          | MACs                                         |
| -------------- | -------------------------------------------- |
| CONV<5, 5, 20> | $24\times24\times25\times20=288,000$         |
| CONV<5, 5, 50> | $8\times8\times25\times20\times50=1,600,000$ |
| FC<800, 500>   | $800\times500=400,000$                        |
| FC<500, 10>    | $500\times10=5,000$                          |

透過分析神經網路的每個 layer ，我們可以得出每個 layer 所需要的 multiplication-accumulation 數量，而其中可以看到在卷積第二層與全連接第一層中，佔有最高比例的乘累加運算，然而如下圖中的分析顯示，在合成 Vitis_HLS 產生的 RTL code 後，Vivado 並不會將更多的 DSP 資源交付給這兩層，而且在累計完全部的硬體模組後，在 FPGA 上其實還有額外的 DSP 資源可供這兩層使用，因此我們認為應該可以針對這兩層給予更多資源，以消除運算的 bottle neck。
除此之外，在檢查完模組使用的 BRAM 後，發現都是使用寬度為 8-bit 的 BRAM ，然而 BRAM 寬度能夠達到 32-bit ，因此這方面也是我們認為可以修改以增加記憶體存取平行度的方向。
![](https://i.imgur.com/xkFezyb.png)
![](https://i.imgur.com/tXf06Y9.png)


## 優化方向
### 改善資料傳遞平行運算
論文中data的傳遞是8-bit為一組，但卻使用32-bit的AXI進行傳遞，因此，我們想，若能使用40-bit data bus，就能增加平行運算並減少處理時間。
![](https://i.imgur.com/LURd0uc.png)
實行的方法可以藉由調整BRAM width至32或者用兩塊BRAM以達到width為40的效果
![](https://i.imgur.com/fo809iX.png)

### 重新調整每層的資源分配以及平行化的方法
在原先的設計中， high level synthesis 的模組使用 `#pragma pipeline II=1` 這種設計來進行 pipeline ，但以上面的資源使用率來看，使用這種方式只會達到單一列在進行乘加時的管線化，示意圖如下。
![](https://i.imgur.com/53wedhH.png)
但為了因應 2 倍與 7 倍的運算量，所以在卷積第二層與全連接第一層，我們認為應該給予額外的 DSP 與 BRAM ，讓兩組管線化的乘加能併行如下圖，如此就可以避免過大的運算量成為一層一層神經網路管線化執行下來的瓶頸。
![](https://i.imgur.com/NfwLx7c.png)


## 實作遭遇的困難
1. 實踐論文後發現，如前有提到的，cifar10因為timimg關係，無法產生bitstream。
`解法`改用Lenet5進行實驗，並成功產生bitsream，但是是有negtive slack的bitstream。
2. 作者有提供燒好的.image檔，但將.image檔讀入SD卡，並嘗試操控jupyter notebook時，卻無法取得連結。
`解法`推測此現象與板子版本有關，論文所用為pynq z1，而我們所用為pynq z2，因為讀取.image困難，所以決定採用第三點所述的方法進行model的寫入。
3. Caffe是類似於Tansorflow的深度學習框架，嘗試安裝卻因不明原因以失敗告終。
`解法`不直接使用caffe model而是將其轉成lasagne model此種較輕量的framework，並且用numpy格式將weight儲存，之後再透過 pickle 把 lasagne model 放到 PYNQ 上。
4. Python中ptnq的版本不一致，論文中為1.多版，而我們所用為2.6版，其中針對DMA操作的方式不同，所以無法成功將進行優化後的架構實行於FPGA上，雖說後來想將PS與PL分開處理改從RTL code下手，但因糾結於此問題過久而導致時間不足，來不及完成RTL的改進。

## 總結
可分成三個層面描述專題中所做。
1. 軟體層面
成功完成將既有model(graph和weight)放到板子上，以CPU運行完，並藉此測得準確率與速度
2. 架構層面
理解論文提出的卷積與全連接層運行方法，並且透過分析運算所需總數量，得出應使用資源多寡。
3. 硬體層面
成功模擬經vitis HLS產生的RTL模組，但尚未成功將我們提出的架構應用於FPGA版。