FPGA实现LSTM算法(三):一文学会FPGA实现LSTM算法

使用Xilinx FPGA搭建LSTM算法框架

这篇博客从pytorch框架搭建的神经网络的训练讲起,到FPGA搭建LSTM框架,详细讲解了过程中的每一步和每一模块的实现。

文章目录

  • 使用Xilinx FPGA搭建LSTM算法框架
  • 1. 整体思路
  • 2. FPGA结构图
  • 3. RAM存储单元
  • 4. 运算处理单元
  • 5. 加法器
  • 6. 激活模块
  • 7. EWU
  • 8. CRAM和HRAM
  • 总结

1. 整体思路

LSTM

使用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结构图

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内发

  • 很多点感觉还没有说清楚,以后慢慢添加编辑修改吧。