使用Xilinx FPGA搭建LSTM算法框架
这篇博客从pytorch框架搭建的神经网络的训练讲起,到FPGA搭建LSTM框架,详细讲解了过程中的每一步和每一模块的实现。
文章目录
- 使用Xilinx FPGA搭建LSTM算法框架
- 1. 整体思路
- 2. FPGA结构图
- 3. RAM存储单元
- 4. 运算处理单元
- 5. 加法器
- 6. 激活模块
- 7. EWU
- 8. CRAM和HRAM
- 总结
1. 整体思路
使用FPGA实现LSTM,python部分需要解决以下问题:
- LSTM存在tanh和sigm这两个非线性激活函数,而FPGA计算非线性函数消耗资源极大,因此通常使用分段线性函数去替代非线性激活函数tanh和sigm函数,这个功能的实现,需要通过自定义RNN结构实现,自定义RNN结构的模板,可以点赞关注评论区留邮箱,48h内发。
- 训练结束后,得到的模型为32位浮点数模型,FPGA中的运算为定点数补码运算,为了减少误差,需要将pytorch中的参数进行量化训练,并进行剪枝,这篇博客只将其量化位16位定点数补码形式(1个符号位,2个整数位和13个小数位),量化更低位数的训练和剪枝以后再写;
- 将得到32位浮点数量化为16位定点数补码形式后,输出coe文件,之后便可以加载到Xilinx FPGA中了。
- 如何将pytorch中的权值矩阵量化为定点数补码,并加载到FPGA中,可参考这两篇博客:
- FPGA实现LSTM算法(一):将参数量化为二进制补码定点数的形式
- FPGA实现LSTM算法(二):将pytorch的权值矩阵量化为定点数补码并导入FPGA中
2. FPGA结构图
- IRAM用于存储Wi和Ui,FRAM存储Wf和Uf,ORAM用于存储Wo和Uo,CWRAM存储Wc和Uc,BiasRAM用于存储bi,bf,bo,bc等偏置信息,CRAM用于存储c,HRAM存储h
- 每次从存储权值矩阵的RAM中,读出一行权值,送入运算处理单元和输入x,h进行乘法累加,得到的结果加偏置b,再送入激活模块,得到门控单元的一个值+
- 再送入EWU单元,计算出c和h的其中一个值
- 将c,h存入CRAM和HRAM中
- 继续读取权值矩阵的值,直到权值矩阵中的值全部被读出。此时CRAM和HRAM将得到完整的h,c的值
- CRAM将h,c的值输出,作为下一次循环时的输入,从而实现LSTM算法
3. RAM存储单元
- 若用RAM存储一个16位数(首位符号位,2位整数位,13位小数位),大小为32 * 32的矩阵,则RAM共有32个地址,每个地址对应32 * 16个位的输出,读取时使用一个5位数 [4:0] addr做地址,一个32 * 16位数 [32 * 16 - 1:0] data为输出,则data[15:0]代表一个权值,data[31:16]代表一个权值,依次类推;
4. 运算处理单元
-
运算处理单元实现矩阵-向量乘法(Mvs)的运算,举个例子,若LSTM的x,h维度均为32,则矩阵W大小为32*32,W * x 的值为32维度的向量;每次从存储权值矩阵的RAM中读出地址为addr的data值,送入运算处理单元,data[15:0] * x[0] + data[31:16] * x[1] +…+data[32 * 16-1: 31 * 16] * x[31],从而得到第addr行的结果。分32次将所有地址的权值矩阵输出到运算处理单元中运算,得到的32次输出,分别对应W * x的32个不同行的值,从而实现矩阵乘法的运算,因此,运算处理单元包含32个乘法器和多个加法器。
-
需要注意的是,本文选择数据格式为16位数(首位符号位,2位整数位,13位小数位),因此运算处理单元做乘法运算时,乘法操作得到31位数,只保留第31位(符号位),第28-27位(2个整数位)和第26-14位(13个小数位),保持数据格式的统一。
-
PE乘法器单元如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | module PE_shift#( parameter MV_DAT_WIDTH = 16, parameter INOUT_DAT_WIDTH = 16 )( input signed [MV_DAT_WIDTH-1:0] w, input signed [INOUT_DAT_WIDTH-1:0] in, input ena_pe, input clk, output signed [INOUT_DAT_WIDTH-1:0] out ); reg signed [31:0] out_; always@(posedge clk) begin if(ena_pe) out_ <= $signed($signed(w) * $signed(in)); else out_ <= 0; end assign out = {<!-- -->out_[31],out_[27:13]}; endmodule |
5. 加法器
- 获得矩阵向量乘法运算后的第addr行的结果后,需要加上第addr行的偏置b,通过一个加法器就可以实现
6. 激活模块
- 激活模块实现sigm和tanh功能,计算出第addr行门控单元的值,我们使用分段线性函数替代这两个函数,因此该模块可以通过简单的移位寄存器(乘法)和加法器实现
7. EWU
- EWU模块实现最后的c,h的运算,计算c时,使用简单的乘法器和加法器即可;
- 计算h时,由于我们使用的y=x (-1
8. CRAM和HRAM
- CRAM和HRAM存储来自EWU的第addr行的h,c的值
- 当获得32次h,c的值(全)输入时,将新的c,h输出,从而实现LSTM算法
总结
这篇博客介绍,分析了如何使用Xilinx FPGA来搭建一个完整LSTM框架的思路,各个模块的代码将在整理后上传,需要pytorch自定义RNN模版的可以评论区留邮箱,48h内发
- 很多点感觉还没有说清楚,以后慢慢添加编辑修改吧。