APP下载

基于FPGA的指令集架构神经网络协处理器的设计与验证

2021-05-24陈章进屠程力

小型微型计算机系统 2021年6期
关键词:池化端口指令

邓 良,陈章进,2,乔 栋,屠程力

1(上海大学 微电子研究与开发中心,上海 200444)2(上海大学 计算中心,上海 200444)

E-mail:1593481663@qq.com

1 前 言

随着近年来大数据的爆发与计算机性能的飞跃式的提升,人工智能的研究重新变得火热起来,神经网络算法已经被广泛应用于图像处理[1]、人脸识别[2]、语音识别[3]等场景.随着神经网络结构的不断加深,对运算平台的资源与计算能力要求越来越高,使得通用处理器对于网络模型的运算越来越吃力.目前针对神经网络模型进行加速的方案主要有3种:1)使用图像处理器GPU进行模型计算的加速,但存在着功耗高,难于集成等缺点,只适合于大型运算平台或云端服务器;2)使用专用集成电路进行模型计算的加速,具有更高性能、更低功耗的特点,但存在设计周期长、开发成本高等缺点;3)使用FPGA进行模型计算的加速,具有低功耗、开发周期短、灵活性高等特点[4],逐渐成为目前研究神经网络加速的热门平台.

2011年Farabet等[5]人提出了一种基于FPGA的卷积神经网络处理器,该处理器在计算时可重构数据流,包含了多个计算单元.2013年Peenmen M等[6]人在Virtex6 FPGA上实现了卷积神经网络的加速,使用MicroBlaze软核处理器作为控制器.2014年Gokhale V等[7]人使用Xilinx ZYNQ FPGA设计了一种卷积神经网络加速器,该方法使用8路并行的计算单元,每个计算单元能够进行最大10×10的卷积计算.2016年Motamedi M等[8]人提出了输出间并行、卷积核间并行和卷积核内并行3种可行的并行计算方法,详尽的总结了卷积层并行计算的设计方案.2017年Yu 等[9]人使用Winograd算法[10]实现卷积运算,减少了模型计算过程中乘法器的使用数量,提高了计算的并行度,但Winograd算法通常只适用于较小的卷积神经网络模型.2019年曾成龙等[11]人提出了一种单计算引擎的神经网络加速器的设计方案,只使用单个计算引擎进行实时配置实现不同卷积神经网络层的计算,大大减小对FPGA资源的消耗.

这些研究工作主要关注于对并行设计空间的探索,尝试通过使用多计算单元计算,数据分块与并行,使用流水计算等方法提高设计的并行度,往往需要消耗大量的资源,而没有全面考虑到对FPGA资源的合理利用,基于FPGA的神经网络加速架构主要有数据流处理架构和单计算单元架构2种[12].数据流处理架构通常是在FPGA上实现神经网络模型的全部层,数据从实现这些层的电路中流过就完成了一次计算,因为每一层的电路结构都经过精心的设计,能够为每一层灵活的配置资源,提高计算的性能.但是当计算不同的神经网络模型时,需要重新配置FPGA,并且这种方法对FPGA设备的资源要求很高,对于大型的神经网络模型,很难进行部署.单计算单元架构的加速器通过对计算单元的配置来实现神经网络中不同层的计算,按照一定的配置顺序就能实现整个神经网络的计算,当计算不同的神经网络模型时,只需要改变配置顺序即可,与数据流处理架构相比,尽管达不到最佳的计算性能,但占用资源少,灵活性与通用性强.

本文基于FPGA平台提出一种自定义指令集的神经网络处理器的设计方案,指令集包含了16条的运算指令,该处理器使用128位宽的指令来控制实现不同计算操作,对卷积与池化系列的计算电路进行复用,减少对FPGA资源的消耗,使用流水线CORDIC算法[13]实现激活函数的计算,同时本文设计了一种多端口读写控制模块,处理器通过该模块实现对总线数据的读写.

2 卷积神经网络的结构分析与运算模块设计

2.1 卷积神经网络结构分析

卷积神经网络[14]是神经网络中最常用到的网络之一,卷积神经网络模型通常包括:卷积层、池化层、激活函数计算、全连接层等.卷积神经网络每一层的输入通常都是一个三维矩阵,分别定义为:长、宽、深度,深度有时也被表述为通道数.图1是一种常见的卷积神经网络结构.

图1 卷积神经网络结构图Fig.1 Convolutional neural network structure

2.1.1 卷积层运算模型

卷积层的作用是为了提取输入图像的某些特征,并输出一个新的特征图作为下一层的输入图像,计算的过程通常为先卷积再经过一个激活函数得到结果.卷积层存在4种卷积的实现方式:直接卷积、展开式卷积[15]、Winograd卷积和快速傅里叶卷积[16].本文中使用直接卷积的方法进行计算.直接卷积使用卷积核与在图像上截取到的相同尺寸的图像进行累乘和累加计算,计算的数学表达式为:

(1)

其中f(·)是非线性激活函数,βfi表示偏移值,O(x,y)表示输入特征图坐标(x,y)处的值,w(kx,ky)表示卷积核坐标(kx,ky)上的权重值,In(x+kx,y+ky)表示输出特征图坐标(x+kx,y+ky)上的值;kx,ky表示卷积核的大小,fi表示第i幅输入特征图,Nfi表示输入特征图的数目.

2.1.2 池化层运算模型

池化层也被称为下采样层,通常被放置在卷积层后,根据不同的采样规格分为最大池化与平均池化.池化层的作用是提取特征图的局部特征,并对特征图进行降维,减少模型的计算时间.最大池化的表达式为式(2),平均池化的表达式为式(3):

(2)

(3)

式(3)中的fout表示输出图像(i,j)位置的值,fin表示输入图像中(i,j)位置的值,p表示池化核的尺寸.

2.1.3 激活函数介绍

激活函数能够对输入的激励进行非线性转换,通常跟在卷积层之后,使用激活函数能够使网络的表达能力变得更强大,而不是只能简单的进行线性表达.常用的激活函数有Sigmoid函数、Tanh函数和ReLu函数,它们的表达式分别为式(4)、式(5)、式(6).

(4)

(5)

Relu=max(0,x)

(6)

2.2 卷积与池化运算模块设计

本文中卷积层使用直接卷积的方法进行计算,直接卷积需要在输入图像上进行划窗操作,再将窗口截取到的矩阵与卷积核进行逐点累乘与累加,最后的累加值作为一次卷积的输出值,随着窗口在图像上的滑动,最终完成对图像的卷积计算.池化计算与卷积计算类似,也需要在输入图像上进行划窗操作,但截取到的窗口矩阵不再进行累乘和累加计算,而是根据相应的采样规则计算窗口矩阵的值.由于卷积与池化都包含了划窗这个操作,因此本文对电路进行复用,通过信号*arith_type来控制实现卷积与池化功能.

为实现划窗操作,首先需要对输入图像进行行缓存,当缓存所需窗口尺寸的行数后,再按列进行输出,即可实现开窗的操作,随着图像数据的串行输入,最终达到划窗的效果.由于卷积与池化计算的输入图像尺寸是不确定的,不能采用常规的条状移位寄存器来进行行缓存,本文中使用双端口RAM来实现窗口的构造,图2是卷积与池化运算模块设计框图.将双口RAM进行首尾相连,当一个RAM缓存输入图像宽度个数据后,开始从当前RAM读取数据并写入到下一个RAM中,当最后一个RAM也缓存输入图像宽度个数据后,将所有RAM中的数据按列一拍一拍的输出,同时,图像继续串行输入,从而实现划窗的操作.由于图像的数据是串行输入的,导致开窗后的数据矩阵与真正需要计算的矩阵是倒置的,因此需要将开窗截取到的数据矩阵进行一次重映射,重映射后的数据矩阵就是正确开窗后的矩阵.卷积核配置通过写入模块根据相关信号的控制,配置寄存器的个数,形成卷积核矩阵,将从外部存储器中读取到的权重值按照顺序写入到寄存卷积核矩阵中.当卷积核与卷积域的数据都存放到相应的寄存器后,将存放权重的寄存器矩阵与存放图像数据的寄存器矩阵进行逐点乘法计算,将计算后的值存放到一个加法树寄存器矩阵中.当逐点乘法运算完成后,对加法树矩阵进行基2加法树运算,即每相邻的两个寄存器的值相加放到下一层的加法树寄存器中,通过层层的加法运算,最终求得加法树矩阵中所有值的和,即为一次窗口卷积的结果,配合前边的划窗操作,最终实现对图像的卷积计算.

图2 卷积与池化模块设计框图Fig.2 Convolution and pooling module design block diagram

对于池化运算,其划窗操作与卷积运算的划窗操作相同,只需要对卷积核中的数值进行配置即可实现池化运算.当执行最大池化运算时,通过卷积核配置模块将卷积核寄存器中的值都配置为1,同时基2加法树运算阵列不再是(a+b)运算,而是执行max{a,b}运算;当执行平均池化运算时,通过卷积核配置模块将卷积核寄存器中的值都配置为1,基2加法树阵列执行(a+b)运算,将最终的累加和使用定点除法器求商,作为最后的池化结果.当执行卷积计算与最大池化运算时,除法器的除数设置为1.

2.3 激活函数计算模块设计

为了使卷积神经网络的表征能力更强,通常会在卷积计算之后使用激活函数进行非线性映射,常见的激活函数包括了:Sigmoid函数、Tanh函数和ReLU函数.本文基于CORDIC算法设计了一种流水线结构的激活函数计算模块.激活函数计算过程中最复杂的就是指数计算,以exp指数计算为例,阐述基于CORDIC算法的流水线结构的激活函数计算模块的设计原理.CORDIC算法的迭代表达式为式(7).其中θk表示第k次旋转的角度,dk表示第k次旋转的方向,x,y表示坐标,z表示角度值.

(7)

图3 流水线CORDIC算法实现框图Fig.3 Pipeline CORDIC algorithm implementation block diagram

卷积神经网络中的Sigmoid函数和Tanh函数可以通过式(4)和式(5)的指数运算得到.因此为了实现Sigmoid函数与Tanh函数的流水线型计算,需要额外增加流水线型除法器.针对不同级的流水,通过CORDIC算法计算激活函数所消耗的资源,实现的精度有所不同,需要在精度与消耗的资源之间进行衡量.根据实验测试发现,选择10级流水的CORDIC算法实现Sigmoid函数与Tanh函数的效果最好.

3 指令集架构的协处理器设计

为了能够对不同类型的卷积神经网络进行加速计算,本文设计了一种基于指令集架构的神经网络协处理器,图4为处理器的整体框图.处理器采用指令的形式进行控制与计算,每一条指令的运算结果都以二维矩阵为粒度,每一行的运算结果都缓存在片上RAM中并最终写入到片外存储器中.处理器的运算指令需要固化在片内RAM中,可以通过总线进行修改与配置,卷积神经网络中的权重值、输入图像的数据、中间的运算结果都通过多端口读写控制模块存储到片外存储器中.

图4 协处理器总体设计框架Fig.4 Coprocessor overall design framework

3.1 指令集与指令执行器设计

在卷积神经网络中,大多数运算都可以分为向量运算、标量运算和矩阵运算[17],如加法、乘法、激活函数的运算可以看作是向量运算,矩阵乘法、点乘可以看作是矩阵运算,立即数的加减运算可以看作是标量运算.因此本文将卷积神经网络的计算具体细分到了矩阵的加减法、乘法、非线性函数映射、二维图像的卷积与池化运算、矩阵与立即数的加减运算等指令操作.为方便计算,本文中所有的运算数据都是32位有符号的定点运算,其中高16位表示整数位,低16位表示小数位.指令被设计为128位,指令结构如表1所示.

表1 协处理器指令结构说明Table 1 Coprocessor instruction structure description

其中[127∶124]表示操作码;[123∶92]表示该指令输入数据参数1的起始地址;[91∶60]表示该指令输入数据参数2的起始地址;[59∶28]表示输出数据参数3的起始地址;[27∶0]根据指令的不同分别用于表示图像的尺寸,卷积核的尺寸等运算参数.

本文设计指令执行器,用于控制指令的顺序执行,不需要每执行完一条指令就访问外部处理器.在系统复位后,只需向神经网络协处理器发送一条启动运算的指令,处理器就能够按照顺序将指令RAM中的指令全部执行一遍.指令顺序执行器的实现分为两个步骤:1)将外部发送过来的指令存储到指令RAM中;2)接收到启动运算指令后,按照顺序获取指令RAM中的指令,当指令全部执行结束后,发出运算结束信号.整个指令顺序执行器设计框图见图5,主要有指令分析模块和顺序执行状态机组成.指令分析模块需要对接收到的指令进行分析,如果是启动指令,则通知顺序执行状态机启动神经网络计算模块的指令调度.指令执行状态机在接收到指令分析模块的启动命令后,会循环给出指令的地址,并获取指令,调度计算单元开始计算,直到所有指令执行完成.

图5 指令执行器设计框图Fig.5 Instruction executor design block diagram

顺序执行状态机由IDLE、READ、SEND、DELAY、WAIT、ADDR这6个状态组成,各个状态与跳转条件说明如下:

1)系统复位进入空闲状态IDLE;

2)在IDLE状态,如果收到启动运算信号,则会跳转到READ状态;

3)在READ状态,等待3个时钟完成指令读取任务,如果指令是无效指令,则跳转到IDLE,否则进入SEND状态;

4)在SEND状态,将指令发送给计算单元,并给出指令有效信号,之后进入DELAY状态;

5)在DELAY状态,等待5个时钟周期,使计算单元接收指令和指令有效信号,之后进入WAIT状态;

6)在WAIT状态,等待计算单元完成计算,在收到计算单元给出的完成信号后,之后进入ADDR状态;

7)在ADDR状态,对指令RAM的寻址地址加1,之后进入READ状态.

3.2 多端口读写控制器设计

由于外部的DDR存储器通常只有一个总线接口,而在系统中存在多个单元需要对存储器进行读写,并且不同的单元对存储器的访问速度也可能不同.本文设计一种基于异步FIFO的多端口读写控制器,将DDR控制的单端口分时复用,从而实现多个端口对一个端口的读写控制功能,不同的端口之间存在着优先级.图6是2个端口写和2个读写的控制器框架,想要实现更多的端口只需增加相应的FIFO即可.

写端口缓存与读端口地址缓存有3个输入信号,分别为:地址信号、请求信号和缓存余量信号;读端口数据缓存有两个输出信号,分别为数据有效信号和数据读完信号.对于写端口,使用64bit宽度,深度为8的异步FIFO进行数据缓存,在FIFO中每个存储空间存放32bit地址与32bit数据;对于读端口,使用32bit宽度,深度为8的异步FIFO进行缓存,在FIFO中每个存储空间存放了要读取的DDR地址.使用32bit宽度,深度为64 的异步FIFO缓存从DDR中读取到的数据.

图6 多端口读写控制器设计框图Fig.6 Multiport read-write controller design block diagram

读写端口选择控制模块主要由状态机构成,状态机的跳转逻辑如下:

1)系统复位后,状态机进入0状态;

2)在0状态,首先使能所有的读写端口,并按照顺序扫描不同的读写端口,如果某个端口的异步FIFO非空,根据端口序号跳转到1~4不同状态;

3)在1状态,处理来自写入端口#0的写入请求,生成DDR相应的写地址、写请求信号,在完成写入操作后,撤销DDR写入请求,随后屏蔽端口#0,使能其他端口,扫描是否有读写端口存在请求;

4)在2状态,处理来自读取端口#1的读取请求,生成DDR相应的读地址、读请求信号,在完成数据的读取后,撤销DDR读取请求,随后屏蔽端口#1,使能其他端口,扫描是否有读写端口存在请求;

5)在3状态,处理来自写入端口#2的写入请求,生成DDR相应的写地址、写请求信号,在完成写入操作后,撤销DDR写入请求,随后屏蔽端口#2,使能其他端口,扫描是否有读写端口存在请求;

6)在4状态,处理来自读取端口#3的读取请求,生成DDR相应的读地址、读请求信号,在完成数据的读取后,撤销DDR读取请求,随后屏蔽端口#3,使能其他端口,扫描是否有读写端口存在请求;

为了区分读数据信号与读数据有效信号对应读取端口#1还是读取端口#3,使用一个4bit宽度,深度为64的异步FIFO进行读取端口选择的缓存,运行时钟与端口选择控制模块时钟相同.使用多端口控制器进行读写端口的扩展,要求各个读写端口尽可能的发起连续地址的读写请求,以达到最大的DDR读写带宽利用率.每次端口选择控制模块切换端口的周期计算为:

(8)

其中,Ts为切换端口的周期;Nf为当前FIFO在被选中时刻的深度,Wf为FIFO的位宽,Fd为控制器的工作频率,Wd为内存的位宽,Ed为控制器的读写效率.

3.3 神经网络计算单元设计

协处理器中的运算逻辑在神经网络计算单元中实现,计算单元由运算控制逻辑、指令解析逻辑、指令运算逻辑和DDR读写接口等部分组成.如果计算单元准备就绪,就会发出Ready信号,表示可以接收运算指令,在接收到指令和指令有效信号后,解析指令的执行类型并由控制逻辑控制计算单元开始数据的读取与指令的运算,神经网络计算单元设计框图见图7.读写地址生成模块用于产生读写操作时DDR中的数据的地址,回写缓存模块用于缓存每条指令计算的结果.

图7 神经网络计算单元设计框图Fig.7 Design block diagram of neural network computing unit

根据指令类型的不同,计算任务可以划分为双矩阵逐点运算(矩阵的加法、减法和点积运算)、单矩阵逐点运算(矩阵与立即数的加法、减法、乘法运算以及激活函数的计算)、矩阵转置运算、矩阵乘法运算和图像的卷积与池化运算等任务.不同任务对数据的读写与运算方式不同,任务间的转换通过运算控制逻辑实现.运算控制逻辑主要由有限状态机构成,包含了执行不同任务的子状态机.当系统复位时,控制逻辑处于空闲状态,接收到指令后,根据Opcode的编码跳转到相应任务的子状态机中,通过子状态机控制对数据的读写与运算.

为提高运算的效率,执行每个任务时进行边读边写的操作.执行双矩阵逐点运算任务时,先读取参数1的一行数据,然后读取参数2的一行数据,同时每读取参数2的一个数据便与对应的参数1的数据进行逐点运算,并将结果进行缓存,当一行数据全部计算完成后将结果写回到DDR,之后开始读取下一行的数据,直到整个矩阵全部读取完成.执行单矩阵逐点运算任务时,只要先读取参数1的数据,进行相应的流水线运算,对一行的数据逐点进行相应运算的同时写回到DDR,当一行数据计算完成后继续读取下一行的数据,直到参数1的数据全部计算完成.执行矩阵转置任务时,为保证写回的数据是按行连续的,因此在读取数据时优先按列读取参数1指示的数据,并直接按行写回到DDR,直到整个矩阵完整转置.执行矩阵乘法任务时,需要先读取参数1指示数据的某一行,之后依次读取参数2指示数据的每一列,在读取参数2指示数据的同时进行乘累加运算,将结果进行缓存,当参数2 指示数据的每一列都读取完成后,将运算结果写回到DDR,并开始读取参数1指示数据的下一行,直到参数1指示的数据完全读取.

4 实验结果与分析

4.1 实验方案

本文中的实验使用Tensorflow[18]深度学习框架搭建卷积神经网络,网络结构如表2,在Windows10环境下,使用Intel I5 9400 CPU+GTX1070 GPU在MNIST数据集[19]上进行训练,在PYNQ-Z2开发板进行前向推理.PYNQ-Z2开发板的芯片型号为Xilinx 的XC7Z020-1CLG400C,主要的内部资源分为PS、PL和外设等部分,PS端包括双核ARM Cortex-A9处理器,工作频率为650MHz,PL端包括1.3M逻辑片,220个DSPs,同时开发板包括512MB16位总线宽度的DDR3存储器,最高工作频率为1050Mbps.

表2 卷积神经网络结构说明Table 2 Convolutional neural network structure description

为验证神经网络计算单元指令集运算的准确性,本文使用Python与UVM验证方法学搭建指令运算验证平台,使用Modelsim软件进行仿真,验证平台结构如图8所示.首先使用Python读取卷积神经网络模型结构文件,通过解析各个层的种类,生成相应的处理器运算指令;之后使用Python读取卷积神经网络模型参数文件,将这些参数量化为32位有符号定点数,分配到不同的内存地址,并生成DDR初始化文件.使用Modelsim加载生成的指令文件对指令RAM存储模型进行初始化,之后加载DDR初始化文件将模型的参数初始化到DDR仿真模型中.通过将Monitor接收到的运算结果与在Tensorflow中模型的运算结果进行对比,验证指令运算的准确性,并对计算误差进行统计.

4.2 实验结果

通过搭建的指令运算验证平台,可以精准地知道神经网络协处理器在运算过程中每一步和最终的计算结果,以及与单精度浮点数运算相比,由精度损失导致的相对误差.通过进行大量指令运算,将运算结果的相对误差进行统计,图9是部分指令的计算相对误差.通过统计发现,加法、点乘、乘法和卷积等指令的运算结果相对误差控制在10-4级别,激活函数的计算相对误差控制在0.05以下,满足神经网络协处理器的运算需求.

图8 协处理器验证平台Fig.8 Coprocessor verification platform

图9 部分指令运算误差统计Fig.9 Partial instruction operation error statistics

使用vivado 2018.3软件对设计进行综合与时序分析,在100MHz的工作频率下,通过时序分析,确定设计中各条关键路径均满足时序要求.表3是设计中各个模块的资源消耗情况,BRAMs表示消耗的块RAM;LUTs表示消耗的逻辑资源;DSPs表示消耗的DSP的数量.计算单元中存在卷积、矩阵乘法等运算,消耗较多的DSP,而BRAM主要用于行缓存与指令的存储,多端口读写模块使用纯逻辑单元实现.

表3 设计中各模块资源消耗Table 3 Resource consumption of each module in the design

将设计下载到PYNQ-Z2开发板上对MINST数据集进行分类识别,准确率达98%以上,达到主流的识别准确率.表4是本设计与其他卷积神经网络处理器的FPGA实现的结果对比,为更加全面公平的比对加速效果,增加能效比参数.

表4 与文献中的方案对比Table 4 Compared with the scheme in the literature

从表4中可以看出,本设计的能效为19.87GOPS.W-1,消耗24663LUTs,与文献[4]的设计相比,本文的设计能效降低30%左右,消耗的资源降低60%;与文献[11]的设计相比,本文的设计在达到相当能效的条件下,消耗的资源降低80%左右;与文献[20]的设计相比,本文的设计能效提高40倍,消耗的资源降低70%左右.本文的设计在达到同类设计的主流水平时,消耗的逻辑资源仅为同类设计的20%,消耗的DSP资源为同类设计的20%.

5 结 语

本文提出一种在FPGA平台上实现指令集架构的神经网络协处理器的方案,可通过对不同神经网络结构的解析,生成计算指令,能灵活实现复杂神经网络在FPGA平台上的加速计算,缩短神经网络部署的时间;并通过硬件电路结构的分时复用,减少对资源的消耗.方案中使用Python与UVM验证方法学搭建验证平台,验证了指令计算的误差在10-4级别,满足运算的需求,通过在PYNQ-Z2开发板上测试,运算能效为19.87GOPS.W-1,达到同级别设计主流水平,消耗的资源比同级别产品平均降低80%.本方案中的运算使用的数据位宽为32位,为更好的降低功耗,提高计算的并行度,在后续的研究与设计中,可考虑使用16位或8位的数据进行运算.

猜你喜欢

池化端口指令
基于高斯函数的池化算法
温度对圆柱壳结构应力及端口变形影响研究
华为交换机端口Hybrid 模式的应用
卷积神经网络中的自适应加权池化
基于抽象汇编指令的恶意软件家族分类方法
一种端口故障的解决方案
《单一形状固定循环指令G90车外圆仿真》教案设计
新机研制中总装装配指令策划研究
用于手写汉字识别的文本分割方法
为程序或设备在路由器上打开端口