GoSPA: An Energy-efficient High-performance Globally Optimized SParse Convolutional Neural Network Accelerator
一种高能效高性能的全局优化稀疏卷积神经网络加速器
CNN模型同时存在激活稀疏性和模型稀疏性,这样以来,设计稀疏感知(sparsity-aware)CNN硬件就可以更好地加速CNN模型。
现有的稀疏CNN加速器利用intersection运算(这个intersection操作具体是啥后面介绍)
来搜索和识别两个稀疏向量之间匹配项的关键位置,从而避免不必要的计算。现今主流的设计有三个主要缺点:
本文就是解决上面这三个问题
见下图。
主要思想:
问题:
思想:
1.将权重和激活矩阵的非零值,按卷积的运算过程,以流的形式依次配对;(intersection)
2.将匹配位置的权重和激活值相乘再相加得到相应位置的结果。
问题:
1.intersection操作的硬件开销很大(前缀和,CAM扫描搜索)
2.intersection操作的延迟对执行的时间影响很大
3.intersection操作引入了不必要的数据传输(灰色框)
动态流:
执行计算前数据元素未知,取决于输入数据,比如激活值取决于输入特征或前一层的输出
静态流:
与动态流相反,数据元素是提前确定好的,且在执行计算过程中,不会改变,比如权重
利用intersection计算的通用方法:
将两个流的稀疏数据移动到intersection单元,执行intersection,然后根据intersection的结果计算,适用于两个动态流的情况,但DNN中的2维卷积有一个静态流,可以有更高效的方法。
首先基于静态流的稀疏信息,生成SSF(静态稀疏过滤器)。SSF使用bit mask(位掩码)标明静态流中非零值元素的索引。类似下面这样:
简言之,先根据静态流生成SSF,动态流传输前经过SSF筛一次,只传输非零索引对应的元素到计算阶段。
如图所示,一个激活值可能在不同的匹配中与不同的权重配对。之前的激活值是算一次就传一次,现在是把重复计算的激活值只传输一次。
前文提到的计算重排序需要通过CID和PID来实现,每个权重值对应一个PID,每个激活值有一个或多个CID和PID。CID的值代表一组卷积运算中的第几个,同时代表最终运算结果的矩阵中从左到右从上到下的次序。PID代表权重值在权重矩阵中从左到右从上到下的次序,在实际运算中,相同PID的权重和激活值会进行乘积运算,相同CID的一组激活值的运算结果加起来作为最终结果相应位置的值。CID和PID对应的数据从下图可以直观地了解。
首先解释一下其中的约束条件,注意权重和激活之间是多对多关系。
一些已知值:
F: 权重矩阵的维度
H: 激活矩阵的维度
S: 卷积运算的步长(stride)
第一个虚线框是权重坐标( W i j W_{ij} Wij)对应激活坐标( a x y a_{xy} axy)的公式,也就是给定权重坐标,求对应的激活坐标。想象卷积执行时权重矩阵在激活矩阵上移动
E = ( H − F ) / S + 1 E=(H-F)/S+1 E=(H−F)/S+1 是移动步数的上界(不可达)
自然得到权重对应激活的公式
x = i + m S x=i+mS x=i+mS
y = j + n S y=j+nS y=j+nS
第二个虚线框是给定激活坐标,求对应的权重坐标。
P x = x % S P_x=x\%S Px=x%S表示 a x y a_{xy} axy在最后一次被权重矩阵匹配时,在权重矩阵中的相对位置的 x x x坐标 P y = y Py=y%S Py=y同上,表示y坐标。 a x y a_{xy} axy不一定只会被匹配一次,mS和nS表示枚举所有移动的可能
C x = x / S C_x=x/S Cx=x/S: a x y a_{xy} axy在最后一次被权重矩阵匹配时,权重矩阵向下移动了多少步
C y = y / S C_y=y/S Cy=y/S: a x y a_{xy} axy在最后一次被权重矩阵匹配时,权重矩阵向右移动了多少步
G = F / S G=F/S G=F/S: 表示权重矩阵内元素,在一行/列扫描后最多被重复匹配几次
0 < = C x − m < E , 0 < = C y − n < E 0<=C_x-m
0<=Cx−m<E,0<=Cy−n<E : 移动不能超出上界
P x + m S < F , P y + n S < F P_x+mS
Px+mS<F,Py+nS<F : W_{ij}坐标不能超出权重矩阵边界
综上,激活坐标( a x y a_{xy} axy)对应的权重坐标( W i j W_{ij} Wij)可由如下公式求得:
i = p x + m S i=p_x+mS i=px+mS
j = p y + n S j=p_y+nS j=py+nS
Ps:如果二维坐标理解比较困难,可以先试着理解一维的关系。
有了权重坐标和激活坐标之间的关系,CID和PID是比较好算的,前面提到过,PID就是权重值在权重矩阵中的第几个(从左往右从上往下数),CID就是卷积运算匹配的顺序中的第几个。
权重 W i j W_{ij} Wij的PID是确定的,为 i F + j iF+j iF+j
对于激活a,可能有多个PID和CID,PID取决于配对时对应的权重 W i j W_{ij} Wij,在 a x y a_{xy} axy对应权重 W i j W_{ij} Wij时有:
i = P x + m S , j = P y + n S i=P_x+mS,j=P_y+nS i=Px+mS,j=Py+nS
此时对 a x y a_{xy} axy有:
P I D = i F + j = ( P x + m S ) F + P y + n S PID=iF+j=(P_x+mS)F+P_y+nS PID=iF+j=(Px+mS)F+Py+nS
a x y a_{xy} axy的CID代表此时在卷积运算过程中, a x y a_{xy} axy处于第几个匹配中。矩阵匹配的位置由 C x − m Cx-m Cx−m和 C y − n Cy-n Cy−n决定,E是一行最多移动几步,由于矩阵是从左到右,从上到下移动,于是
C I D = ( C x − m ) E + C y − n CID=(C_x-m)E+C_y-n CID=(Cx−m)E+Cy−n
如图,权重变成位掩码的形式(WSP)进入ID生成器,激活的非零值、坐标数据以及卷积参数也进入其中。最后出来的是有非零值匹配的激活值,以及对应的PID和CID值。下面的计算步骤就是PID相同的激活和权重相乘,接着CID相同的激活值的计算结果都加在一起作为输出的一个位置的值。最终按CID顺序排好就是卷积运算的结果。
第一步:取非零激活值,用CSR表示,传入Stage-1
第二步:在Stage-1中计算每个激活值的PID和CID,具有相同PID的激活值及其CID按顺序传入同一个FIFO-A队列中。
第三步: 所有FIFO-A中的数据经过各个FIFO-B队列的筛选(利用WSP),进入相应的PE中。因为这里一个FIFO-B队列对应一个PE,也就是一个权重。一个FIFO-B筛选出来的激活值与一个权重进行运算。也就是说,每个FIFO-A中的数据要进入所有的FIFO-B队列。上图中skip的位置就是WSP为0的位置。
PE中的操作见后续解析。
Weight SRAM模块:
存储权重数据,包括相应的WSP
Reg 寄存器:
按顺序存储非零的权重的值,以及相应的PID信息
Selector模块:
根据传入激活值的PID选择相应的权重
后面是乘法器,以及根据CID分开的不同加法器阵列
首先看数据格式
权重:位掩码+非零数据
激活:按PID的顺序依次进入
主要是寄存器的处理过程,以一个寄存器为例:
数据存储:当前权重PID,下一个权重PID,当前权重值,下一个权重值
计算:传入的激活值PID等于当前权重PID就将当前权重和传入的激活值传入selector后进入乘法器计算
数据更新:传入的激活值PID不等于当前权重PID,将当前权重PID更新为下一个权重PID,当前权重值更新为下一个权重值,下一个权重PID和权重值从SRAM中读取数据更新。
考虑多个寄存器的情况,让传入的激活值按顺序进入不同的寄存器,将上述数据作为全局变量即可。
实验部分略。