背景:FPGA2017的最佳论文为深鉴科技的ESE,把稀疏网络的FPGA实现给出了丰富的参考意义。
目的:详细解析ESE Efficient speech recognition engine with sparse LSTM on FPGA论文。
论文地址:https://arxiv.org/abs/1612.00694
目录
一、摘要
1.1 motivation
1.2 贡献点
1.3 实现
1.4 实现流程
二、背景 LSTM
2.1 模型概览
2.2 模型实现
2.3 LSTM
三、模型压缩
3.1 剪枝
3.2 基于负载平衡的剪枝
3.3 wegiht与activation 的quantization
四、编码与编译
稀疏矩阵的存储
五、硬件实现
5.1 硬件实现的难点
FPGA实现的难点
5.2 系统实现概览
软件端
外部存储
FPGA芯片
5.3 ESE控制器(sheduler)
5.4 ESE channel architecture
ActQueue
SpmatRead
SpMV
ElemMul
Adder Tree
Sigmoid/Tanh
5.5 存储系统
六、实验
6.1 平台及设置
6.2 资源利用
FIFO深度
6.3 准确率,速度与能效
七、个人总结
LSTM(long short term memory)被广泛应用于语音识别领域
但为了获得更好的识别效果,LSTM模型通常过大,这会导致:
传统的方法是将相应的神经网络直接部署于嵌入式设备上,导致运行速度慢,并且能耗高。
training ——inference
本文采用的方法,先用sheduler进行软件端的压缩,然后进行硬件端的部署从而获得了更好的速度与能效。
training——comprssion——accelerated inference
算法端压缩,用shdeuling的方法将其压缩,并且用稀疏矩阵用CSC的方法进行存储。然后硬件端用FPGA加速。
如上图 figure 3,包含了两个单元 front-End, Back-End单元。
Back-end包含了:
这里LSTM主要用于acoustic model
通过front-end前端提取出的特征被用于AM:acoustic model(声学模型)
然后通过解码器运用AM和LM来运用MAP(maximum a posteriori probability最大化后验概率),从而预测相应的语音:
其中:
因为特征X已经固定,所以上面的方程可以写为:
上面这两个P(X|W)和P(W)分别是由AM声学模块和LM语言模块得出。
实现过程中,LSTM架构被广泛应用于大规模的语言识别模型。LSTM也是语音识别模块之中最耗费存储和耗费运算的部分。
LSTM的数据流如下图所示:
例如上图之中:
LSTM的一系列输入为x=(x1:x2:x3:...:xT), 获得一系列输出 y=(y1:y2:y3:...:yT):
其中,
模型压缩部分与deep compression高度相关:
Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman coding 论文详解:
https://blog.csdn.net/weixin_36474809/article/details/80643784
权重绝对值小于某个值,就剪掉这个权重:此部分详细参考Deep compression
在Kaldi speech recognition数据集上进行相应的剪枝操作,关于剪掉的比例与精度损失parameters pruned away and phone error rate (PER)如图6所示:
剪枝掉93%的数据依然有很好的准确率。在TIMIT数据集上,作者的权值运用了90%的稀疏率。
为了更好的执行稀疏矩阵的并行化,作者实施相应的基于负载平衡的剪枝方法。
关于负载平衡的问题,在EIE之中有讨论:
则根据木桶短板效应,并行化之后最慢的PE是所有PE的时间的时长。
基于负载平衡的剪枝就是解决解决这个问题的,在剪枝的过程中,作者将权重的稀疏性定义为10%,然后避免其中的子矩阵低于5%或者大于15%,这样,有利于不同PE之间的负载平衡。
作者进一步的压缩32bit的浮点数到12bit定点,然后运用线性的量化来实现于weight和activation
在权值量化的部分,权值分布的dynamic range在每层LSTM之中会被预先分析,以免数据溢出。
权值在不同比特数下的量化。作者用查表和Linear interpolation来实现相应的激活函数,例如sigmoid或者tanh,然后分析相应的dynamic range。然后作者探索用于维持精度的最小的量化的比特数。12bit数之下可以达到没有精度损失。
对于激活函数中的sigmoid或者tanh,采样的分布分别为从[-64 64]和[128 128], 输出为16bit with 15bit的十进制数
TIMIT展示在table 4之中
LSTM的运算包括稀疏矩阵的相乘,element wise的multiplication,与memory reference。作者设计一个数据流来实现确定相应的硬件工作。
数据根据所在的行被分成n个block,n是PE的硬件加速模块的一个channel,刚开始的n个行被放入n个不同的PE之中,n+1行被放入第一个PE之中,入错循环。这样可以保证矩阵的第一部分会被第一时间读入,也会在后面的运算之中迅速实现。
(这里可以参考EIE之中,讲的更详尽一些,摘录过来)
这是一个稀疏矩阵相乘的过程,输入向量a,乘以矩阵W,输出矩阵为b,然后经过了ReLU。
用于实现相乘累加的单元称为PE,相同颜色的相乘累加在同一个PE中实现。例如上面绿色的都是PE0的责任。则PE0只需要存下来权值的位置和权值的值。所以上面绿色的权值在PE0中的存储为下面这样:
通过CSC存储,作者可以很快看出virtual weight的值。(CSC不懂见上一节推导)。行标与元素的行一致,列标可以恢复出元素在列中的位置。
向量a可以并行的传入每个PE之中,0元素则不并行入PE,非零元素则同时进入每一个PE。若PE之中对应的权重为0,则不更新b的值,若PE之中对应的权重非零,则更新b的值。
因为DDR上的对齐问题,所以作者采用16 bit的数据。量化后的权重
下图为csc来存储稀疏矩阵的实现。关于CSC具体可以参考deep compression中的关于CSC的部分。
稀疏权值矩阵的存储:比如作者这个稀疏的矩阵里面,n×n的矩阵,里面大多数的值是零值,然后作者通过相应的存储稀疏矩阵的方式对这个矩阵进行存储。首先把所有的非零值存为AA,假设所有的非零值的元素的个数为a,然后把每一行第一个非零元素对应在AA的位置存为JA,最后一个数是所有非零元素的个数+1,所以JA中的元素就是行数n+1,然后把AA中每一个元素在原始矩阵中的列存为IC。所以作者把一个原始的n×n的稀疏矩阵存为2a+n+1个数字。
剪枝之前的矩阵是非稀疏的矩阵,例如一个n*n的矩阵,经过剪枝的过程之后,这个n*n的矩阵就变为一个n*n的稀疏矩阵,其中很多零值。可以采用CSR或者CSC的方法对这个矩阵进行存储从而减少相应的存储量。
例如CSC的存储稀疏矩阵的方法
第一行AA存储所有的非零元素,
第二行JA存储所有系数矩阵中每行第一个非零元素在AA的位置,例如第一个元素是4.0,在AA中位置是第一个,第二行第一个元素是4.0,在AA中位置是第四个。通过JA可以将AA中所有元素对应的行恢复出来。
第三行JC是所有元素对应的列标。
这样,一个稀疏的矩阵通过三行就能存下来,达到了很好的存储压缩。由N*N变为了2a+N+1个元素
相对位置的参数:在压缩完参数之后,作者存了权值和权值对应的参数。之前的参数存的是绝对的参数,作者现在存相对的参数,就是两个参数的差值,比如作者用三个特存相对的参数,只要两个元素的距离小于8,都能把参数存为3个比特的,如果两个参数的距离大于这个值,作者就在第8个位置设置一个0值。
通过CSC得到了压缩的矩阵,可以通过差分存储进一步压缩存储数量。例如作者想用三比特的值来存储相应的Index。
3bit可以容忍的间距为8
数据序列与负载平衡
上图之中的连接PE与CCU之间的序列。如果CCU直接将数据广播入PE,则根据木桶短板效应,最慢的PE是所有PE的时间的时长。
此图展现了一个ESE系统的概览,实现由三部分组成,1 FPGA的加速器,2 CPU上的软件程序,3 FPGA板子上的外部存储
由CPU和相应的内存组成,通过PCI-Express总线与FPGA通信。初始化阶段向FPGA传输LSTM模型的参数。会向FPGA传输语音信号和从FPGA获得已经得到的结果。
外部存储存储着所有参数和voice vector。因为BRAM的数量限制,所以LSTM不能够全部的放入BRAM之上。所以加速器需要通过内存控制器(MEM controller)来通过MIG(Memroy interface generator)实现内存的接入。
FPGA芯片上实现了相应的ESE加速器,ESE 控制器, PCIE 控制器,内存控制器,与片上buffer。
ESE加速器包含PE单元,用于实现LSTM网络中的大部分功能。
最耗费的是稀疏矩阵相乘的单元。作者将LSTM之中的稀疏矩阵相乘的公式写为Table 5的实现模式。
上面为相应的矩阵相乘的公式。
LSTM要实现复杂的数据流。图11就是显示一个sheduler需要实现的部分。
此部分涉及较多硬件内容,我后续补充解析。
ESE的结构与EIE非常类似,可以参考下EIE做个类比:
https://blog.csdn.net/weixin_36474809/article/details/85326634
上面这张是架构图,图中具体的结构实现不同的功能,下面解释每个模块的作用:
activation vector queue激活队列
关于负载平衡的问题,韩松在EIE之中详细的讲过。
所以作者在每个PE之前设置一个队列,用于存储,这样PE之间不同同步,只用处理各自队列上的值。
这样,PE之间就能最大限度的处理数据。一定程度上解决了负载平衡的问题。
稀疏矩阵读取单元,sparse matrix read,分为指针读取单元和稀疏矩阵读取单元,用于编码权重矩阵的存储和输出。
SparseMatrix-vectorMultiplication,稀疏矩阵相乘单元,
元素级别的乘法单元。每个通道上设置16个远东是级别的乘法器。
用于累加结果和偏置bias
非线性的激活函数。
运用4GB的DDR3作为DRAM来实现为片外的存储。在上图之中分别交DDR_1和DDR_2
ESE实现于XCKU060的FPGA上,时钟设置为200MHz。外部存储采用 4GB的DDR3.
运用TIMIT数据集来测评相应的效果,这个数据集包含了630个人的美国英语的8个主要的方言。用1000小时的音频训练,100小时验证,10小时测试。
实验的baseline设置为用 i7-5930k CPU和Pascal Titan X GPU和MKL BLAS/cuBLAS的CPU/GPU的系数矩阵相乘。
运用MKL SPARSE/ cuSPARSE 与CPU/GPU作为稀疏矩阵相乘的实现。
table 6展示了ESE运用32通道设计的资源占用。其实现为32个通道,并行32块PE,在XCKU060的FPGA上。
根据实验得出的最好的并行的PE个数为32
FIFO就是激活队列的问题,韩松已经在很多文章中探讨过。
本篇文章贡献点与EIE类似。都是在稀疏的基础上压缩模型,运用稀疏相乘实现于硬件之上。
但是有两个创新点值得我们参考: