APP下载

基于RISC-V的神经网络卷积算法的研究与优化

2022-03-21苗瑞霞张雪兰谭星浩方华启

计算机工程与设计 2022年3期
关键词:指令集嵌入式指令

苗瑞霞,张雪兰,谭星浩,方华启

(1.西安邮电大学 电子工程学院,陕西 西安 710121;2.芯来智融科技有限公司,湖北 武汉 430074)

0 引 言

由于不断提高的嵌入式设备性能的需求和ARM指令集不能向后兼容的特性导致了在ARM平台上的人工智能算法不能很好地移植和维护。文献[1]和文献[2]分别阐述了从数据量化的方式和进行数据量化时,采用非线性量化的方式来分析此方法对嵌入式神经网络加速的可执行性。此结论为文章中提出采用8位定点数卷积模型来代替ARM CMSIS-NN模型中提出的16位定点数卷积模型这一操作奠定了理论基础。同时,从现有的神经网络加速优化的方向来看,本文主要采用了模型参数低精度量化的思想[5-8]进行设计,完善了当前ARM中不支持8位数据处理的神经网络平台的短板。并为RISC-V平台的神经网络模型发展做出补充。

嵌入式设备的芯片处理能力和内存资源都是有限且珍贵的,这一限制使得卷积这一复杂且运算量巨大的算法在嵌入式平台的部署成为难点。并且在嵌入式平台部署神经网络的情景时,需要解决性能和内存资源紧缺的问题[9-11]。同时,由于目前RISC-V没有自身专用的神经网络函数处理库和专用的嵌入式设备。针对以上阐述问题,本文主要采用了从软硬件协同的角度,提出了一种面向RISC-V处理器的卷积神经网络系统的实现方法,将模型参数量化与RISC-V硬件平台结合的方式来更大程度上优化神经网络在嵌入式平台上的部署。首先提出采用针对嵌入式平台的低精度定点量化的方法,在保持精度的同时减小了数据位宽,进而减小了神经网络模型在嵌入式设备上部署的内存限制,采用RISC-V指令架构[12-14]来完成卷积神经网络模型的底层实现以缩短运算的执行时间,提升整体的性能。本设计完善了神经网络中数据低精度的处理,减轻了内存资源对神经网络在嵌入式设备上部署的限制,同时又将神经网络和RISC-V相结合,不必承受遭受困扰ARM许久的兼容性问题的干扰,可以用在更广泛的高性能嵌入式设备中。

1 RISC-V ISA指令集

1.1 RISC-V指令集类型

(1)寄存器操作的R类型指令

(2)用于短立即数和访存load操作的I型指令

(3)用于访存store操作的S型指令

(4)用于条件跳转操作的B类型指令

(5)用于长立即数的U型指令和用于无条件跳转的J型

1.2 RISC-V指令集特点

(1)指令只有6种格式,所有指令都是32位位宽,b便于指令解码。例如ARM-32、x86-32中包含许多不同格式的指令,增加解码的成本。

(2)RISC-V指令架构含有3个寄存器操作数,与x86-32不同的是,后者让源操作数和目的操作数共享一个字段。这就需要多使用一条move(搬运)指令,来保存目的寄存器的值,增加了代码量。

(3)所有指令,总是在同一位置的读写寄存器的标识符可以大大简化解码操作,增加了指令的执行效率。

(4)这些格式的立即数字段总是符号扩展,符号位总是在指令中最高位。这一特征表明可能成为关键路径的立即数符号扩展,可以在指令解码之前进行,对于RISC-V的模块化指令特点具体见表1。

2 ARM CMSIS-NN模型

2.1 内核概述

CMSIS-NN其实是作为基于ARM的Cortex处理器微控制器的硬件抽象层,其本质是一个拥有众多神经网络功能函数接口的模型库。该库是高效神经网络的内核集合。使用者可以通过各种易于使用的标准化软件界面更快地编写软件而不用去了解这些功能函数的实现方法以及实现流程,只需要按照需求去调用相应的功能函数即可。

表1 RISC-V的模块化指令集

同时其一致的API(应用程序编程接口)提高了软件的可移植性和可重用性。通用软件库和接口提供一致的软件框架。同时,作为独立于编译器的一层,它允许使用者选择大部分主流的编译器。

内核代码主要由两部分组成:NN-Functions和NN-Support[15,16]函数,应用程序代码可以使用这些函数来实现机器学习的功能。NNSuppor库包括实用功能,如数据转换和激活函数表,服务于NN库的函数。应用程序代码也可以使用这些实用功能来构造更复杂的NN模块。ARM CMSIS-NN神经网络内核架构如图1所示。

图1 ARM CMSIS-NN神经网络内核架构

2.2 ARM-CMSIS卷积神经网络模型研究

该卷积神经网络利用了输入由图像组成的方式约束了体系结构。与常规神经网络不同的是,ARM CMSIS-NN的图层具有3个维度排列的神经元:宽度、高度、深度。例如CIFAR-10[17,18]中的输入图像是经过激活的输入量,并且大小为32*32*3(宽度、高度、深度)的尺寸。一个层中的神经元将只连接到它之前的一个小区域,而不是以完全连接的方式连接所有神经元。此外,CIFAR-10的最终输出层具有尺寸1×1×10,因为到ConvNet体系结构结束时,我们将整个图像缩减为类分数的单个矢量,并沿深度维度排列,如图2所示。

在CIFAR-10输入图像的大小仅为32*32*3(32宽、32高、3色通道),因此常规神经网络第一个隐藏层中的单个完全连接神经元将具有32*32*3=3072权重。此时权重数量还在可管理范围内,但很明显,此完全连接的结构不会扩展到更大的图像。例如输入更大的图像200*200*3将导致具有200*200*3=120 000个权重的神经元。显然,这样会造成浪费和大量的参数将迅速过度拟合,而这样的输入仅仅是一张尺寸较大图片的输入带来影响。如Alexnet[19]神经网络中的计算量达到上亿级,但是乘法运算就至少需要7亿次以上。卷积层的计算公式如式(1)

(1)

式中:w是权重矩阵,b为偏置,f是激活函数,x是每一层的输入,Y是每一层的输出。从公式可以看出输入与权重的乘积随着卷积层数的增加都是呈指数型增长的,数据量的增大对嵌入式设备的综合要求就越高,由此可见嵌入式神经网络平台受到自身资源因素限制。其最常见的3层神经网络如图2所示。

2.3 RISCV-P拓展指令简介

P标准扩展:封装的单指令多数据(Packed-SIMD)指令。P扩展细分了现有的寄存器架构,提供更小数据类型上的并行计算。P指令集扩展增加了RISC-V CPU IP产品的DSP算法处理能力,其支持8、16、32位数据操作。通过添加RISC-V P指令集扩展,RISC-V CPU现在可以以更低的功耗和更高的性能运行这些各种DSP应用程序。同时,P指令集封装的SIMD指令(单指令多数据指令)代表了一种合理复用现有宽数据通路的设计,这样更加有利于组合指令完成复杂的运算,有了更多实现方式,以便于移植和维护。

SIMD,即Single Instruction, Multiple Data,一条指令操作多个数据。是CPU基本指令集的扩展。主要用于提供fine grain parallelism,即琐碎数据的并行操作,同时更多用于图像处理,图像的数据常用的数据类型是RGB565、RGBA8888、YUV422等格式,这些格式的数据特点是一个像素点的一个分量总是用小于等于8 bit的数据表示。如果使用传统的处理器做计算,虽然处理器的寄存器是32位或是64位的,处理这些数据却只能用于它们的低8位,这样对于内存的使用效率略低。如果把64位寄存器拆成8个8位寄存器就能同时完成8个操作,计算效率提升了8倍。

2.4 RISC-V协处理系统简介

首先是协处理器的接口,课题中使用的蜂鸟E200处理器核的接口是借鉴的Rocket Core的协处理器接口,为了和其区分开来,名为EAI(extension accelerator interface)。其32位的EAI指令格式如图3所示。

图3 EAI 指令编码格式

课题采用支持协处理器访问存储器资源的RISC-V处理器核是因为其可以扩大处理器处理的指令范围。在LSU(load store unit)中有特定的协处理器接口资源,这就使得基于EAI接口的协处理器可以访问主处理器能访问的存储资源。

其实现的原理是在LUS中有预留的接口资源。另外在主处理器中有控制信号,即为了防止主处理器中的存储访问指令和协处理器中存储访问指令造成竞争,主处理器有专用的控制信号阻止后续的指令继续访问存取器,以此来规避竞争。另外协处理采用的是基于ICB总线的Valid和Ready握手机制。所以,只要初期的LSU单元能够接受多个存取器访问指令,那么协处理就可以发送多个存储器访问指令。这样也可以加快在数据处理中执行效率。同时,主处理器也能在协处理器没有访问操作的时候拉低其独占信号,使得主处理器中的后续指令得以执行,协处理器控制模块控制框架如图4所示。

图4 示例协处理器控制模块框架

能效分析如下:以3*3的平面卷积来看,启动指令需要一个周期,协处理器从数据缓存中读取到一个矩阵元素,那么采用流水线的方式去连续读取,第一行的所有矩阵元素相加之和就需要4个周期,从第二行开始后,在本行的基础之上,还要增加上一行的部分累加和,所以需要7个周期,每一个列累加直接从缓存中拿出计算结果即可在一个周期内计算完成,那么完成一个3*3的矩阵运算就需要22个时钟周期,这是纯C++实现的矩阵乘加性能的3~5倍。

3 具体实现

3.1 实现流程

在嵌入式平台方面,有限的计算和内存能力使得在它们上部署NN推理模型必须进行软件算法优化,如图5所示,本文采用纯软件模拟和协处理器辅助实现采用8位定点量化网络模型。为了保证对比实验结果的准确性,采用Convolve->Relu->MaxPool->Convolve->Relu->MaxPool->Relu->MaxPool->FullConnected->Softmax的流程,其中分开了卷积部分以区分优化前后的卷积。

图5 实现流程

在软件层次,对ARM CMSIS-NN算法模型中替换了RISC-V的P标准拓展指令,具有单指令多数据的特性的DSP专用指令集,在SIMD(单指令多数据)指令中可以最多同时处理8个8位的数据,并且不需在ARM的模型中将8位数据符号拓展成16位,经对比,在内存的使用效率上和数据的处理效率上分别提高了2倍和8倍,同时DSP不仅支持浮点也支持定点数据运算,所以为了保证数据的正确性,还在软件层次增加了饱和计算、舍入和移位的功能函数。

协处理器层次,因为在P标准的拓展DSP指令的大量使用下,会需要更多的乘加寄存器,并且为了保证定点计算的数据正确性,也增加了硬件方面的定点饱和,舍入和移位的实现和更多寄存器的实现,最后为P标准的DSP指令做了适配和兼容,以及后期的简易验证是否能够正确执行。

3.2 卷积数据处理优化

CMSIS-NN库函数大多数使用16位MAC(乘加)指令,文中采用8位数据类型指令操作来实现16位数据类型指令的算法模型。CMSIS提供了相应函数arm_q7_to_q15()执行数据转换。数据转换分为两个步骤:第一步将8位数据符号扩展到16位,它使用符号扩展指令(__SXTB16)进行数据转换;第二步重新排列数据,使输出遵循与输入相同的顺序,如图6所示。

图6 CMSIS-NN中q7_t转换至q15_t

在对数据进行优化之前采用的是16位数据运算,关键代码如下所示:

while (blkCnt > 0u)

in = arm_nn_read_q7x4_ia(&pIn);

in1 = __SXTB16(__ROR(in, 8));

in2 = __SXTB16(in);

#ifndef ARM_MATH_BIG_ENDIAN

out2 = __PKHTB(in1, in2, 16);

out1 = __PKHBT(in2, in1, 16);

#else

out1 = __PKHTB(in1, in2, 16);

out2 = __PKHBT(in2, in1, 16);

#endif

write_q15x2_ia(&pDst, out1);

write_q15x2_ia(&pDst, out2);

blkCnt- -;

上述代码中,首先是循环展开处理的第一部分。每次计算4个输出,旋转8位数据并将两个q7_t值符号扩展到q15_t值,再将剩余的两个q7_t值符号扩展到q15_t值。在获得拓展成16位数据之后再通过__PKHTB和__PKHBT指令将in1参数的位低16位与in2参数的高16位左移16之后的结果组合在一起成一个32位操作数。以此来实现在一个操作数中具有2个真实有效的由8位拓展而来的16位数据,之后在使用相应16位的mul指令进行运算。设计中的优化省略了数据拓展这一操作。关键代码如下所示:

while (blkCnt > 0u) {

in = read_q7x4_ia((q7_t **)&pIn);

write_q7x4_ia(&pDst, in);

blkCnt- -;

}

优化后的卷积数据处理过程是直接将8位输入分开读取至8位位宽的内存指针中,在取出相应的存储单元地址写入数据即可。省略数据拓展操作后即不需要__PKHTB()和__SXTB16()指令操作,提升了存储单位的使用效率且提高了单位时间内的数据操作数量,降低了运算时间。

卷积的矩阵乘法优化,优化前矩阵乘法函数关键代码:

While (colCnt)

q31_t inA11, inA12, inA21, inA22;

q31_t inB1 = arm_nn_read_q15x2_ia (& pB);

q31_t inB2 = arm_nn_read_q15x2_ia (& pB2);

pA = read_and_pad (pA, &inA11, &inA12);

pA2 = read_and_pad (pA2, & inA21, & inA22);

sum = __SMLAD (inA11, inB1, sum);

sum2 = __SMLAD (inA11, inB2, sum2);

sum3 = __SMLAD (inA21, inB1, sum3);

sum4 = __SMLAD (inA21, inB2, sum4);

inB1 = arm_nn_read_q15x2_ia (& pB);

inB2 = arm_nn_read_q15x2_ia (& pB2);

sum = __SMLAD (inA12, inB1, sum);

sum2 = __SMLAD (inA12, inB2, sum2);

sum3 = __SMLAD (inA22, inB1, sum3);

sum4 = __SMLAD (inA22, inB2, sum4);

可以看出在arm-cmsis中对于矩阵的运算每次都是需要先将存储中的数据地址通过指针传递给运算指令,然后重复上面图6的步骤,对低精度的数据进行零拓展,然后在进行数据重新排序,对于处理器来讲这样的操作就会每次都造成额外的存储访问,在大量数据的存储访问操作环境中,这对卷积运算的处理效率有着很大的影响,甚至对于处理器的冒险协调模块是个极大的挑战。因此为了解决以上潜在问题,以及加速在卷积模块中对于卷积运算所需要的数据处理,化简后关键代码如下所示,矩阵乘法核如图7所示:

while (colCnt){

q31_t inB1 = *__SIMD32 (pB)++;

q31_t inB2 = *__SIMD32 (pB2)++;

q31_t inA1 = *__SIMD32 (pA)++;

q31_t inA2 = *__SIMD32 (pA2)++;

sum = __SMAQA (inA1, inB1, sum);

sum2 = __SMAQA (inA1, inB2, sum2);

sum3 = __SMAQA (inA2, inB1, sum3);

sum4 = __SMAQA (inA2, inB2, sum4);

colCnt- -;}

图7 q7_t操作数输出2×2矩阵乘法核

从上述代码中可以看出,相较于之前的操作我们简化了处理操作,通过将低精度的8 bit数据的地址直接通过指针的形式传递给RISCV-P拓展的SIMD运算指令,然后进行乘加运算。将其和图6所示的计算过程相比,显而易见的是单位操作数据量翻了一倍,同时节省了大量的存储访问指令,减轻了对片上存储的压力,降低了运算的复杂度,同时增加了算法的效率并增加了计算的吞吐量。图7用图形化的方式对矩阵乘法内核内循环的执行进行展示,在填充计算两个空间相邻输出像素所需的两个im2col缓冲区后,矩阵乘法内循环如图7所示。在循环的每一次迭代中,从两个im2col[20]缓冲器中的每一个(图中的指针pBuffer1和pBuffer2)和从两个权重库(指针pWeight1和pWeight2)加载到寄存器中,所需的负载操作总数为4个。这样,就有足够的元素来在4个不同的累加器上设置4个_SMAQA指令操作数。因此,在矩阵乘法内核内循环的一次运行中,我们可以计算4个__SMAQA指令,它们对应于8位的MAC操作,而代价是4个负载指令,极大简化了矩阵乘法函数。

同时,为了最大程度上的优化卷积运算因大量的数据而对存储造成的压力,课题中实现了im2col模块,事实上,在卷积层中是通过计算滤波器权重与输入要素图中的一个小的接受区域之间的点积来提取新的特征图。但是基于CPU实现的卷积分解为输入重新排序和扩展(即im2col,图像转列)和矩阵乘法运算。im2col其实是将类似图像的输入转换为代表每个卷积过滤器所需数据的列。但是要实现im2col的最大困难之一是内存占用量的增加,这就与意图减少内存占用的初心违背,因为输入图像在im2col输出矩阵中部分数据是重复的。为了缓解内存占用问题,并且保留im2col的性能优势,为卷积实现了部分im2col内核。内核只会扩展有限数量的列,对于点积后矩阵的数据加以特征值的提取,因此足以获取矩阵乘法内核,最大程度地减低内存占用,以及获取输入图像的完整特征值。在此基础上最大提升了存储访问的性能,同时保持了内存的最小开销。im2col过程如图8所示。

图8 3×3内核的2D图像上的im2col示例

另外发现随着输入的图像格式的变化,对于卷积运算的效率也是有一定的影响。当输入的数据批量是1,那么im2col其实就是平面卷积,在神经网络中最常见的数据输入格式有两种,Channel-Width-Height(CHW),即首先是通道。Height-Width Channel(HWC),即最后一个通道。因此,在蜂鸟E200开发板上分别执行了HWC和CHW格式的数据来比较两者im2col执行时间,结果显示HWC格式具有更好的im2col性能。因此在后面的性能测试中,采用的也是基于HWC格式的数据输入例如图9所示的数据布局实验结果。

图9 CHW和HWC数据布局的实验结果

4 性能测试分析

4.1 SPIKE仿真器简介及验证

SPIKE,RISC-V ISA模拟器,内部实现了多个RISC-V harts的功能模型。为了便于对项目维护并且进行后续的版本控制以及查看API何时扩展或呈现不兼容,SPIKE遵守版本控制方案,当进行向后不兼容的API更改时,将主要版本号递增;添加新API时,次要版本号递增;当以向后兼容的方式修复Bug时,修补程序版本号将递增。同时SPIKE的主要公共API是RISC-VISA。目前C++与SPIKE内部接口的一个接口不被视为公共API,并且将在不增加主要版本号的情况下对此接口进行向后不兼容的更改。

本设计中所采用的开源ARM CMSIS-NN模型,使用软件语言C++将模块功能以代码来实现。功能仿真测试通过开源的RISC-V拓展指令集自测试用例,测试处理器是否符合指令集架构的功能级仿真器SPIKE。该测试程序是由RISC-V架构开发者为了检测处理器是否符合指令集架构中的定义而编写的测试程序,但是由于本设计中对硬件方面的优化做出的改动为使用了RISC-V架构协处理器,并且自行设计拓展了RISC-V的DSP专用指令。原生SPIKE仿真器不支持DSP的拓展指令,因此在做仿真测试前需要在SPIKE功能仿真器上适配DSP功能。

4.2 性能测试

为了量化卷积运算协处理器的加速效果,在此对软件实现的卷积运算和协处理器提供一定量相同的随机输入数据,并且在SPIKE的仿真器上模拟设置和在蜂鸟E203 FPGA开发板上相同的16 MHZ的运行频率来保证相同的输入环境和相同的运行频率,然后对比两者分别所需要的时间。

实验选取一个卷积神经网络(CNN)示例的演示,卷积、ReLU激活、池化和完全连接的功能。实验中使用的CNN基于BVLC Caffe的CIFAR-10示例。神经网络由3个卷积层组成,散布有ReLU激活层和最大池化层,最后是一个完全连接地层。网络的输入是32×32像素的颜色图片,将被分为10个输出类别之一。设置32.3 KB的存储权重,以及40 KB的激活权重,同时在实验开始前需要调试好软件环境。

实验准备需要安装工具Ubuntu Linux 18.04 LTS,以及使用sudo apt-get install make zip命令解压缩python2.7 pythonpip,还有相应环境设置,如设置项目路径等。同时还需要使用两条硬件线将GPIO与BTN连接,硬件连线如图10所示。

图10 硬件连线

实验测试参数设定结果见表2。

表2 卷积网络输入各参量

实验条件控制:输入数据为随机,并且对比确认纯软件实现算法和协处理器实现算法的输入相同,输出结果对比结果完全相同,在此实验环境下得出的两种前景下的消耗时间见表3。

表3 测试结果数据对比

经软件模拟RISC-V处理器,利用RISC-V处理器中可用的数字信号处理(DSP)扩展和集群的并行性,分别在开启DSP拓展模式和未开启DSP拓展模式下通过测试所得数据,如表4所示在q7_t数据类型上优化后的卷积神经网络算法加速比为11.968 101 93,此数据在蜂鸟E203上采用8 M大小RAM测得。

表4 采用hbird-e-sdk使用riscv-cifar10测试所得数据

5 结束语

本文针对当前嵌入式神经网络平台逐渐提高的性能要求和部署神经网络模型时受到寄存器资源限制这一现状,设计了一款面向RISC-V的神经网络嵌入式平台模型,且采用8位点量化数据对ARM CMSIS-NN定开源的卷积神经网络模型进行优化。分析了采用8位定点量化数据的必要性、将神经网络部署在嵌入式设备上的必要性、ARM CMSIS-NN模型中的有待优化之处。实验结果表明,RISC-V的拓展指令集在测试平台可以验证通过。优化后的神经网络卷积模型在蜂鸟E203开发板上实现逻辑功能,并完成了SPIKE仿真分析和示例测试。由测试结果可知,性能加Cortex-M3提升了11.968倍。目前该模型已经用于蜂鸟E203嵌入式设备,下一步将面向特定的模型进行优化,对嵌入式神经网络的全连接层、池化层进一步研究。

猜你喜欢

指令集嵌入式指令
基于Kubernetes的RISC-V异构集群云任务调度系统①
Focal&Naim同框发布1000系列嵌入式扬声器及全新Uniti Atmos流媒体一体机
3DNow指令集被Linux淘汰
《单一形状固定循环指令G90车外圆仿真》教案设计
TS系列红外传感器在嵌入式控制系统中的应用
嵌入式PLC的设计与研究
基于Dais—CMX模型机的斐波那契数列指令集设计
嵌入式单片机在电机控制系统中的应用探讨
中断与跳转操作对指令串的影响
什么是AMD64