杨 灿 王重熙 章隆兵
(处理器芯片国家重点实验室(中国科学院计算技术研究所) 北京 100190)
(中国科学院计算技术研究所 北京 100190)
(中国科学院大学 北京 100049)
近年来,深度神经网络在图像、语音、自然语言处理等领域有了广泛而深入的应用,针对不同的应用场景,都需要对神经网络模型进行训练以获得更优的参数,于是对训练速度的需求也不断提升,相应地,学术界和产业界也都将目光投向了神经网络模型的训练过程,提出了各类方法、各种加速器结构来实现对训练过程的加速。Google 推出了新一代加速器TPUv3[1],支持多类深度学习应用的训练。NVIDIA在最新的A100 图形处理器(graphics processing unit,GPU)中进一步升级了专门用来加速深度学习任务的Tensor 核心。华为的Ascend[2]采用了3D Cube单元实现对卷积和矩阵乘法的加速。Cambricon-Q[3]通过由专用的集成电路(application specific integrated circuit,ASIC)加速核心与近数据处理引擎的混合结构实现高效量化训练的方法。RaPiD[4]使用支持超低精度训练的加速器来提高训练的性能。Sigma[5]设计了一种新的约简树(reduction tree)结构来实现对稀疏、不规则的矩阵乘法的加速。
上述针对训练的加速器结构通常只关注了计算密集型层——例如卷积层、全连接层的加速,且通常将它们的操作化为矩阵乘法运算进行,并未涉及到神经网络模型中的其他访存密集型层——例如批归一化(batch normalization,BN)层、非线性激活层的加速。文献[6]提出了针对批归一化层、缩放平移层的推理阶段的加速方案,通过将其操作化为加法与乘法,映射到专门的运算单元中直接进行计算来提升性能。文献[7]提出了在推理过程中将批归一化层、缩放平移层、ReLU 层与卷积层合并处理的方案,采用对卷积层输出数据马上进行批归一化、缩放平移、ReLU 等运算来提高数据处理的效率。由于推理过程中批归一化操作所需的每个通道的均值与方差是已知的常数,于是批归一化操作退化为缩放平移操作,能够采用文献[6,7]的方案进行加速。然而,在训练过程中,批归一化层的均值和方差需要由这次迭代中所有样本的数据现场算出,不能直接退化为缩放平移操作,于是,文献[6,7]的方案完全不适用。文献[8]提出了一个通过将元素和与元素平方和化为矩阵运算的方法来加速批归一化层的方案,但在实际处理中,批归一化层作为访存密集型层,主要由访存带宽决定了执行效率,单独提升运算速度对提升整体的执行效率意义不大。
针对以上问题,本文首先确定了访存密集型层——批归一化层、加法层、非线性激活层是卷积神经网络模型中重要的组成部分,分析了它们的常见连接以及它们在训练的前向过程与反向过程中的计算特征后,提出了在训练的各阶段中,将访存密集型层与其前后的计算密集型层融合为一个新层执行的方式。访存密集型层的操作作为对融合新层中输入数据的前处理或者原始输出数据的后处理进行,大大减少了访存密集型层执行过程中与片外内存交互的数据量以及训练过程中需存储在片外内存中的数据量。在此基础上,本文设计实现了一个新的面向训练的加速器,能够高效地支持融合新层的前向过程与反向过程的执行,并采用了暂存前处理结果、后处理操作与计算密集层的运算操作并行执行的优化策略,进一步提升了融合新层的性能。实验结果显示,本文提出的融合方案与加速器结构在面积增加6.4%、功耗增加10.3%的开销下分别实现了对训练的前向阶段、反向阶段67.7%、77.6%的性能提升。
在卷积神经网络(convolutional neural network,CNN)模型中,常用的层包括了卷积层(convolutional layer,CONV)、池化层、全连接层和非线性层(nonlinear layer,NonL)。随着CNN 在算法上的发展,CNN 模型中还出现了另外两类常用的层。
第1 类是批归一化(BN)层。在训练的过程中,网络的参数不断变化导致了网络中激活值的分布不断发生变化,于是需要更低的学习速率和更精细的参数初始化来保证训练的精度以及收敛性,因而降低了训练的速度。为了解决这个问题,Google 提出了批归一化[9]的方法,通过对每层的输入进行归一化,固定住每层输入的均值和方差,减少内部协变量偏移(internal covariate shift),使得训练过程能够使用更高的学习速率,从而加速网络的训练。常用的CNN 模型中都包含了BN 层,主要的层间连接方式有CONV→BN→NonL 和CONV→BN。
第2 类是捷径连接(shortcut connections)与堆积层(stacked layers)的结果累加形成的累加层(Add)。文献[10]提出了一类残差网络模型,设计了“捷径连接”来实现需要的映射。捷径连接执行恒等映射,并将其结果与堆积层的结果累加起来,如图1 所示。该类捷径连接与累加结构获得了巨大的成功,在后续多种常用的CNN 模型中出现,如ResNext[11]、Wide ResNet[12]、MobileNet v2[13]等;并且成为神经架构搜索中的网络模型的基本组成部件之一,出现在了MobileNet v3[14]、MNASNet[15]、Reg-Net[16]等轻量级模型中。于是,捷径连接与堆积层的结果的累加层也成为了CNN 模型中的一个常用层。
图1 残差学习块[11]
深度神经网络的训练过程一般分为2 个部分,即前向传播(forward propagation,FP)和反向传播(backward propagation,BP)。在FP 过程中,将minibatch个样本作为网络模型的输入,按照网络的顺序从前至后依次进行计算,得到该网络模型的输出结果。接着,按照训练方法中规定的误差计算方式,计算出这批样本的误差值,然后开始BP 过程。在BP过程中,按照链式法则将误差在网络模型中由后向前传播,并利用每层的特征图和误差值计算出权值梯度。最后,根据权值梯度和训练算法中给定的更新权值的方法对权值进行更新后,开始下一批样本的迭代。
使用GPU(NVIDIA GeForce RTX 2080 Ti)执行了多个常用CNN 模型的训练过程,用PyTorch Profiler 测量了每个层在FP 和BP 阶段的运行时间。图2中给出了各模型的BN、NonL 和Add 层在FP 和BP过程中的执行时间,执行时间分别按照FP 和BP 过程中CONV 层的执行时间进行了归一化。从图2 中可以看出,FP 过程中BN、NonL 和Add 的平均执行时间分别是0.31、0.19 和0.1,总共达到了0.6;BP过程中BN、NonL 和Add 的平均执行时间分别是0.46、0.25 和0.09,总共达到了0.8。其中,Res-Net50 的FP、BP 过程,BN、NonL、Add 的执行时间总和分别是0.85 和1.11。以上观察说明,除了CONV层,BN 层、NonL 层和Add 层的执行时间在CNN 的训练过程中也占了很大的比重,对它们进行加速可以有效地缩短训练的时间。
图2 FP、BP 中BN、NonL、Add 层的执行时间
1.2.1 BN 层的计算过程
{xi,i=1,…,m} 是本次训练迭代中BN 层的mini-batch个样本的同一个通道上的m个输入值,{yi} 是BN 层的输出结果。BN 层的FP 过程[10],首先计算出{xi} 的均值μB和方差
最后,使用BN 层给定的参数γ和β,对进行线性变换操作得到BN 层的输出结果{yi}:
执行BN 层的FP 过程时,需要将BN 层的输入值从片外内存中读到片上,计算出均值和方差。由于片上的缓存区容量有限,在放不下mini-batch个样本的输入值时,执行式(3)、(4)中的计算需要从片外再读1 次BN 层的输入值到片上,才能计算出BN 层的结果并存回片外内存中,供下一层使用。因此,访存量是BN 层输入值的3 倍。
文献[10]给出了BN 层进行反向误差传播的计算公式:
1.2.2 NonL 和Add 层的计算过程
NonL 层最常用的激活函数是ReLU。NonL 层的FP 从片外内存中读入前一层的结果到片上,进行规定的激活操作后,将结果存回到片外,共2 倍输入值的访存量;NonL 层的BP 从片外读入输入误差值和FP 过程的输入特征图到片上,进行计算后存回片外,共3 倍输入值的访存量。
Add 层的FP 和BP 过程相同,都是从片外读入2 个需要累加的输入值,进行加法操作后,将结果存回片外,共3 倍输入值的访存量。
BN、NonL 和Add 层在FP 和BP 过程的操作都是访存密集型的,计算过程中对运算单元的利用率极低。
为了实现对BN、NonL、Add 层的加速,本节提出了一个将访存密集型层与计算密集型层融合执行的方式,能够减少运行过程中与片外内存交互的数据量,从而提升性能。
不管是FP 还是BP,NonL、Add 层的输出结果仅需同一个样本中与结果同一位置的输入作源操作数即可算得。于是,有2 种直观的融合方式:一是可以将其操作直接与其后执行的相邻层进行融合,读入NonL 或Add 层的输入值后,立即算出结果作为其后相邻层的输入值,接着进行其后相邻层的计算。对于融合后的新层,以其后相邻层的计算为主,NonL和Add 层的操作相当于对融合新层的输入值的一个“前处理”。二是可以将其操作与执行顺序的前一层进行融合,相当于对前一层的输出结果增加了一个“后处理”,成为融合新层的新输出结果。
两种方式下,都能够直接减少NonL 和Add 层与片外内存的交互数据量;并且,只需要将融合新层的输出值存在片外,内存中不需要存融合新层中内部层的结果,减少了训练过程中对内存容量的需求。
与NonL、Add 层不同,BN 层的一个结果与这次训练过程中使用的mini-batch个样本的该通道上的所有源操作数都相关,不能直接与其前后相邻层融合。将式(3)带入式(4)中,可以得到:
其中,F1和F2是关于BN 层每个通道的参数项,计算方式为
由式(9)可以看出,在算出关于每个通道的参数项F1和F2后,BN 层的FP 就化为了仅与同一样本的同一位置的源操作数相关的计算。
将式(5)~(7)带入式(8)中,可以得到:
其中,F1和B、C都是关于BN 层每个通道的参数项,B和C的计算方式为
由式(12)可以看出,在算出了每个通道的参数项B和C后,BN 的BP 就化为了仅与同一样本的同一位置的源操作数相关的计算。于是,式(9)和式(12)中的操作都能够直接与其后相邻层进行融合执行。
从式(12)~(15)中可以看出,FP 需要计算出均值μB、方差和参数项F1、F3供BP 阶段使用,并且需要计算出参数项F2供FP 阶段使用。
由式(10)、(11)、(15)可以看出,由于γ、β和ε都是训练中给定的数值,算出μB和后就能得到参数项F1、F2和F3。将式(2)展开,可得:
从式(13)、(14)中可以看出,由于F1、F3和μB都是FP 过程中已经算好的参数项,要算出参数项B和C仅需算出S1和S2。从式(16)和(17)可得,计算S1和S2即计算每个通道中的累积和。由于是BN 层的输入误差值,也是BN 在BP执行过程中的前一层的输出误差值,于是,可以将这2 对累积和的计算与前一层进行融合,在前一层的原操作完成以后,对输出值进行一个“后处理”,计算出需要的累积和。
于是,BN 层的FP 和BP 过程都可以分为2 个步骤:第1 步为计算累积和及参数项,与执行顺序的前一层进行融合,作为其输出值的“后处理”出现;第2 步为式(9)、(12)中的仅与同一样本的同一位置的源操作数相关的计算,与执行顺序的后一层进行融合,作为对融合新层的输入值的“前处理”出现。
下文中使用ifmap 和ofmap 分别代表FP 过程中一个层的输入数据与输出数据;使用ierr 和oerr 分别代表BP 过程中一个层的输入误差值与输出误差值;使用WG(weight gradient generation)来表示训练中反向传播时计算权值梯度的过程;使用C1、C2、C3 代表卷积层。
(1)C1→ReLU→C2
FP 时,ReLU 与C2 融合,仅将C1_ofmap 存回片外,不需要将ReLU_ofmap 存回片外。于是,Re-LU 层的片外访存量为0。
BP 时,由于C2 的WG 过程需要用到ReLU_ofmap,即会把C1_ofmap 从片外读到片上,于是选择ReLU 与C2 融合,对C2_oerr 进行后处理计算C2_oerr × ReLU′(C1_ofmap) 作为融合新层的结果,存回到片外,供C1 做BP 时使用。ReLU 化为后处理进行,需要用到C1_ofmap,但由于C2 的WG 过程本身就会将C1_ofmap 读到片上,于是,ReLU 带来的片外访存量为0。
(2)C1→BN→C2
FP 时,BN 的第1 步与C1 融合,计算出参数项;第2 步与C2 融合。仅将C1_ofmap 存回片外。因此,BN 层没有额外的片外访存,片外访存量为0。
BP 时,BN 的第1 步与C2 融合。由于WG 时需要用到BN_ofmap,于是从片外读C1_ofmap 到片上,参与BN 与C2 融合层的后处理和C2 的WG 的计算。BN 的第2 步与C1 融合,计算C1 的BP 时,读入C2_oerr 和C1_ofmap 执行式(12)中的计算,算出C1_ierr 后执行C1 层的操作。于是,C1 融合层的执行除了读C2_oerr 作为输入值外,还因为前处理多读了C1_ofmap 到片上,相当于BN 层给融合层的执行带来了1 倍输入值的片外访存量。
(3)C1→BN→ReLU→C2
FP 时,BN 的第1 步与C1 融合,计算出参数项;BN 的第2 步与ReLU 和C2 一起融合为一个新层,BN 第2 步和ReLU 都作为融合新层的前处理执行。仅将C1_ofmap 存回片外,于是,BN 和ReLU 没有额外的访存,片外访存量为0。
BP 时,BN 的第1 步与ReLU 和C2 融合,都作为对C2_oerr 的后处理执行。由于式(12)、(16)、(17)中的就是BN_ierr,有:
将式(19)带入式(16)、(17)中,就能计算出BN第1 步的参数项。BN 的第2 步与C1 融合,计算C1的BP 时,读入C2_oerr 和C1_ofmap,执行将式(19)带入式(12)后的计算,得到C1_ierr 后执行C1 层的操作。
C2 的后处理需要C1_ofmap,由于C2 的WG本身需要将C1_ofmap 读到片上,于是不增加新的片外访存。C1 融合新层的执行由于前处理多读了C1_ofmap 到片上,相当于BN 和ReLU 带来了1 倍输入值的片外访存量。
(4)C1→BN→Add→ReLU→C2
1)C1 的同个残差块中的捷径连接没有卷积计算
FP 时,BN 的第1 步与C1 融合;BN 的第2 步与Add、ReLU 层和C2 一起融合成一个新层,从片外读入C1_ofmap 算出BN_ofmap,从片外读入C1 同个残差块中捷径连接的输出结果与BN_ofmap 相加,做ReLU 操作,得到C2_ifmap 后进行C2 层的操作。这个过程中,除了要将C1_ofmap 存回片外,由于C2 的WG 需要用到C2_ifmap,选择将算好的C2_ifmap 也存到片外。因此,在计算C2 融合层时,需要从片外读入捷径连接的输出值且需要将算好C2_ifmap 存到片外,相当于BN、Add、ReLU 带来了2倍输入值的片外访存量。
BP 时,将BN 的第1 步、ReLU、误差Add 与C2融合为一个新层,都作为对C2_oerr 的后处理执行。用Scut_oerr 代表C2 同一残差块中捷径连接的误差,在C2 算出了C2_oerr 后,继续进行:
计算,将BN_ierr 作为融合新层的结果存回片外,并继续使用算出的BN_ierr 值进行BN 层的第1步。BN 的第2 步与C1 融合,计算时,读入BN_ierr和C1_ofmap 执行式(12)中的计算,算出C1_ierr后执行C1 层的操作。
由于C2 的WG 直接使用C2_ifmap,于是C2融合新层的后处理需要多读Scut_oerr 和C1_ofmap 到片上,带来了2 倍输入值的访存量;C1 融合新层的前处理时需要多读C1_ofmap 到片上。因此,BN、ADD、ReLU 总共带来了3 倍输入值的片外访存量。
2)C1 同个残差块中的捷径连接是C3→BN3
FP 时,BN3 的第1 步与C3 融合;BN3 的第2 步与BN 层的第2 步、Add、ReLU 和C2 一起融合为一个新层,作为对该新层的前处理出现。与1)中相同,除了将C2_ofmap 存回片外,还要将前处理结果C2_ifmap 存回片外。同样与1)中相同,BN、Add、ReLU 带来了2 倍输入值的片外访存量。
BP 时,将BN 的第1 步、BN3 的第1 步、ReLU、误差Add 与C2 融合为一个新层,都作为对C2_oerr的后处理执行。同样使用式(20)计算出BN_ierr作为融合新层的结果存回片外,并分别利用BN_ierr 执行BN 的第1 步和BN3 的第1 步。BN 的第2步与C1 融合。BN3 的第2 步与C3 融合。
由于C2 的WG 直接使用C2_ifmap,于是C2融合新层的后处理需要多读Scut_oerr、C1_ofmap、C3_ofmap 到片上,带来了3 倍输入值的访存量;C1融合新层的前处理时需要多读C1_ofmap 到片上;C3 融合新层的前处理时需要多读C3_ofmap 到片上。因此,BN、ADD、ReLU 总共带来了5 倍输入值的片外访存量。
表1 中总结了2.3 节中各类常见层间连接的访存密集型层在FP 和BP 过程中单独执行或融合执行时与片外内存交互的数据量,以一个访存密集型层的mini-batch个样本的输入数据量为单位。1.2.1 节和1.2.2 节说明了ReLU、BN、Add 的FP 和BP 过程在单独执行时分别有2、3、3 倍和3、5、3 倍输入值的片外访存量。情况3 包含了1 个BN 和1 个ReLU,因此FP 和BP 分别为5 倍和8 倍输入值访存量;情况(4)-1)包含了1 个BN、1 个Add 和1 个ReLU,因此FP 和BP 分别为8 倍和11 倍输入值访存量;情况(4)-2)包含了2 个BN、1 个ADD 和1 个ReLU,因此FP 和BP 分别为11 倍和16 倍的输入值访存量。
表1 访存密集型层片外访存量
从表1 中可以看出,融合后的访存密集型层执行时与片外内存交互的数据量在各类常见连接下都得到了大幅度的减少。
表2 中给出了2.3 节中各类常见层间连接的访存密集型层在训练过程中单独执行或融合执行时占用的片外内存的容量,以一个访存密集型层的minibatch个样本的输入数据量为单位。情况(1)~(3)中,完全消除了BN、ReLU 层在训练过程中对片外内存容量的占用;情况(4)-1)和(4)-2)由于需要在FP 时将前处理结果C2_ifmap 存回片外供WG 使用,于是占用1 倍输入值的内存容量。
表2 访存密集型层占用片外内存容量
融合后的执行方案大幅减少了训练过程中在片外内存中存储的数据量,能够容纳更大规模的样本在同一加速器芯片上进行训练,缓解因片外内存容量的限制而带来的训练的扩展性或性能上的问题。
基于上述融合方案,本文设计实现了一个面向训练的加速器,能够高效地支持融合新层的执行,采用了暂存前处理结果、后处理操作与计算密集层的运算操作并行执行优化的策略,进一步提升了融合新层的性能。
图3 展示了一个面向训练的加速器结构,其中,实线边框模块与实线连接线构成了加速器的基础结构,支持每个层单独执行。基础结构包含了M行N列的处理单元(processing unit,PU),主要用来执行计算密集型层的计算;有3 块分离的片上缓存区Fbuf_In、Wbuf 和Fbuf_Out,Fbuf_In 主要用来存放计算密集型层的输入值,Wbuf 用来存放权值以及权值梯度,Fbuf_Out 主要用来存放每层计算出的输出值以及WG 所需的ifmap 值;每块Fbuf_In 都连接着一个输入寄存器堆Ifreg,给对应行的所有PU提供输入值;每块Wbuf 都连接着一个权值寄存器堆Wreg,给对应列的所有PU 提供权值。每个PU中包含了多个乘加计算单元及一个输出寄存器堆Ofreg,Ofreg 与Fbuf_Out 直接相连,用来存计算过程中产生的中间值和最终的运算结果以及存WG 过程用到的ifmap 值。同一列中相邻2 个PU 形成一个组,共同拥有一个累加单元Adder,用来实现2 个PU 算出的结果的累加。Fbuf_Out 还连接着一个向量单元VecU,用来处理非访存密集型层中的计算。指令队列Inst_Queue 从Host 处接收指令并转换成控制信号控制每个PU 列的执行。
图3 加速器的结构
图3 中虚线边框模块(带阴影模块)与虚线连接线是为了支持非访存密集型层的融合执行而增加的硬件结构。在每行的Fbuf_In 和Ifreg 旁增加了一个PreU,用来执行对融合新层输入值的前处理。每列中相邻2 个PU 共同拥有一个PostU 模块,用来执行对融合新层的运算结果的后处理。每一列中增加了一个除法开方单元DivSqrt,用来处理BN 第1步中计算参数项涉及到的除法和开方运算。每列中还增加了一块缓存区Bbuf,用来存储BN 计算累积值或参数项过程中需要的参数值(γ和β)、计算的中间结果、算出的参数项;并为融合新层的前处理或后处理提供需要的参数项值。
对于每个从Fbuf_In 中读出的数据,都增加一份如图4 所示的PreU 单元,其中主要包含了1 个乘加单元、1 个16 比特的寄存器SrcReg 和一些多选逻辑。从Fbuf_In 中读出的数据可以写到Ifreg 或者SrcReg 中,和常数值以及参数项F一起,生成乘加单元的源操作数,乘加结果写回到Ifreg 或者SrcReg中。前处理过程的最终结果写到Ifreg 中,然后送到各行的PU 中进行后续的计算。
图4 PreU 的结构
PostU 的结构如图5 所示,主要包含了4 个乘加单元和2 项4 ×32 比特的寄存器Comp_Reg 和Acc_Reg。乘加单元使用来自Ofreg、Comp_Reg、Acc_Reg 的数据,常数值0、1 以及参数项F作为被乘数、乘数和加数,乘加结果写回到Ofreg 中或Comp_Reg、Acc_Reg 中。
3.2.1 前处理操作的加速方案
融合新层的前处理的执行时间可能会导致后续计算的阻塞,为了缓解这部分影响,执行过程中,将Fbuf_In 分成2 部分使用,一部分存融合新层的原输入值,当第1 次从Fbuf_In 中读入一行原输入值进行计算时,将该行输入值的前处理结果从Ifreg 中写回到Fbuf_In 的第2 部分中去;后续计算直接从Fbuf_In 的第2 部分中取算好的前处理结果到Ifreg中,直接参与计算。于是,后续关于该行的输入值的计算都不会再受到前处理的影响。
3.2.2 后处理操作的执行方案
为了消除后处理给融合新层带来的影响,采用了让PU 的计算与对结果的后处理并行执行的方案。如图5 所示,将一个PU 中的8 项Ofreg 平分成2 份ping-pong 使用,一份用来放PU 中正在计算的中间结果(32 比特浮点数);另一份存储着已经算出的原始输出结果(16 比特浮点数),将其作为源操作数送到PostU 中执行需要的后处理。由于原始输出结果只需要part0 部分的Ofreg 即可放下,于是空余的part1 部分可以用来放后处理过程中需要的其他源操作数。
后处理过程由2 类操作构成。第1 类是由原始输出结果算出融合新层的新输出结果,存回片外。
如图6 所示,使用Ofreg 0~3 存放PU0 和PU1中正在计算的中间结果;Ofreg 4~7 的part0 中存储着已经算完的原始结果,依次执行关于PU0 和PU1中的原始结果的后处理第1 类操作。
图6 后处理第1 类操作的执行过程
关于PU0 的后处理:①先从PU0 的Ofreg 4~7的part0 读出第1 列的4 个原始结果,分别从PU0和PU1 的Ofreg 4~7 的part1 读出第1 列的其他源操作数,送到PostU 的4 个乘加单元中进行计算,得到4 个新结果后,写回到PU0 的Ofreg 4~7 的part0中的第一列中;然后对②处的列进行相同的操作,直到PU0 的Ofreg 4~7 的part0 中的所有原始结果都处理完成,得到所有的输出结果为止;③将PU0 的Ofreg 4~7 的part0 中的新输出结果写回到Fbuf_Out 中。关于PU1 的后处理,与PU0 中类似执行即可。
后处理过程的第2 类操作是根据输出结果计算BN 的累积值和参数项。
如图7 所示,计算累积值时:①分别从Ofreg 4~7 的part0 和part1 的第1 列读出4 个结果值和4个其他源操作数,以及从Comp_Reg 中读出4 个已有的累积值,送到PostU 的4 个乘加单元中,计算出4 个新的属于同一个累加值的部分结果,存到Comp_Reg 中;然后对②处的列执行相同的操作,直到Ofreg 4~7 的part0 中的所有结果都处理完为止;③接着使用Adder 将Comp_Reg 中的4 个部分结果累加为1 个累积值,累积值写回到Acc_Reg 中;由于每列PU 执行同一个输出通道的计算,当PU0~3当前计算的这个输出通道的计算完成后,④使用PU0/1 旁的Adder 将PU0/1 和PU2/3 对应的Acc_Reg 中关于不同样本同一通道的累积值累加起来,得到新的累积值先写回到PU0/1 对应的Acc_Reg中;最后⑤存到Bbuf 中。
图7 后处理第2 类操作计算累积值的过程
等所有样本关于一个通道的累积值计算完成后,使用每列第1 个PostU(PU0/1 对应的PostU)进行参数项计算。从Bbuf 中读出源操作数,将源操作数和中间结果都存到Comp_Reg 和Acc_Reg 中;并使用该列的DivSqrt 进行参数项中的除法、开方计算。最终算出关于该通道的参数项,写回到Bbuf中。
本节介绍实验环境和实验数据,着重从性能的角度评估了本文提出的加速器结构及加速访存密集型层的融合方案。
利用Verilog 语言在寄存器传输级(register transfer level,RTL)实现了图3 所示的加速器。使用Synopsys Design Compiler 在ST 28 nm 工艺下进行综合。使用Synopsys VCS 工具对加速器设计进行模拟和验证,测量其性能。
实验中加速器采用16 行32 列的PU,每个PU中有32 个乘加运算单元,共16 384 个乘加单元。每个Adder 中采用4 个32 比特浮点格式的加法器。每行PU 对应1 块768 kB 的Fbuf_In,共12 MB。每列PU 对应1 块36 kB 的Wbuf,共1152 kB。2 个PU对应1 块64 kB 的Fbuf_Out,共16 MB。每列PU 对应1 块6 kB 的Bbuf,共192 kB。Fbuf_In 每次读写16 个数据,于是每行的PreU 中包含了16 个乘加运算单元。PU 中一项Ofreg 为16 ×32 比特。加速器的频率为800 MHz。关于Wbuf 和Bbuf 的访存带宽为25.6 GB/s,关于Fbuf_In 和Fbuf_Out 的访存带宽为205 GB/s。该配置下,主运算阵列的乘加单元数量、片上缓存区大小、带宽都与主流训练加速器相近。
为了评估本文提出的加速器的性能,选择了Pytorch 的torchvision.models 中提供的resnet18 和resnet50 作为测试模型。训练过程中mini-batch设为32。
图8 给出了FP 阶段ResNet50 的各个ResBlock在本文提出的加速器上融合执行、单独执行的时间。从图8 中可以看出,每个ResBlock 融合执行的时间都比单独执行的时间短,其中性能提升最大的是conv2_x blk0,提高了3 倍;conv2_x blk1/2、conv3_x blk1/2/3 的提升也较大,分别达到了1.3 倍和1倍;conv5_x blk1/2 的提升较小,约为10%。整个FP过程融合执行的性能比单独执行时提高了87.0%。
图9 给出了BP 阶段ResNet50 的各个ResBlock在本文提出的加速器上融合执行、单独执行的时间。除了conv5_x 的blk1 和blk2,其他ResBlock 的融合执行时间都比单独执行时间短,性能提升了0.41~2.8 倍,整体上融合执行时性能比单独执行时提升了1 倍。
图9 BP 阶段ResNet50 各ResBlock 融合、单独执行的时间
图8 和9 中还给出了各ResBlock 在GPU(NVIDIA GeForce RTX 2080 Ti)上单独执行时的时间。单独执行时,本文加速器与GPU 的执行时间相差在5%以内;采用融合方案后,本文加速器在FP 和BP阶段分别比GPU 的性能提高了78.9%和95.7%。
从图8 和9 中可以看出,ResNet50 网络模型中前面的ResBlock 采用融合方案执行带来的性能提升更大,后面的更小。这是因为前面的ResBlock 中采用了更大的ifmap、ofmap、ierr、oerr,因而在单独执行访存密集型层时,需要消耗更长的时间来与片外内存交互数据,于是在融合方案下能够消除更多的时间,带来更大的性能提升。
另外,由图8 和9 中还可以看出,融合后的执行时间比单独执行时卷积层的执行总时间要长,这主要由3 个方面的原因造成。第1,融合新层的前处理可能导致源操作数不能及时准备好送到PU 中执行运算;第2,融合新层的后处理可能导致在PU 计算完成后另一部分的后处理没完成而使PU 的运算阻塞;第3,融合新层相比于原始的卷积层,可能增加了执行过程中需要的源操作数,引起访存时间的增加,当访存带宽不够时,阻塞PU 的计算。图10中给出了ResNet50 的2 个ResBlock 块中的各融合新层在FP 和BP 时由上述3 类原因而增加的执行时间的占比。由于前处理增加的时间占比最大的是BP 时的conv3_x blk0 中的conv1,达到了15%,平均下来是5%,说明3.2.1 节中的前处理加速方案很好地消除了前处理带来的影响。图10 中,只有BP时conv2_x blk1 中的conv0 有后处理对执行时间的明显影响,其他融合新层中后处理的执行时间都隐藏在了卷积层运算执行的过程中,说明3.2.2 节中的后处理并行执行方案几乎消除了后处理对执行时间的影响。从图10 中可以看出,FP 时的conv0、BP时的conv0 和conv2 中由于访存而增加的时间占比很高,平均在50%。这是因为这些融合层执行时涉及到了Add 层的融合以及BP 时关于1 个或2 个BN层的后处理,相比于原始的卷积层需要更多的访存量。
图10 融合新层各类增加时间的占比
表3 分别给出了图3 中所示的加速器基础部分与支持融合的加速器的各组成部分的面积与功耗。加速器基础部分的面积和功耗分别为145.6 mm2和75.2 W,支持融合后的面积和功耗分别为154.9 mm2和82.9 W,分别增加了6.4%和10.3%。面积与功耗的增长主要由PU 中的PostU、Ifreg 中的PreU 以及Bbuf 带来。
表3 加速器的面积与功耗
表4 中给出了ResNet18 和ResNet50 的FP、BP阶段融合执行比单独执行性能提升的百分比,FP 平均提升了67.7%,BP 平均提升了77.6%。
本文针对卷积神经网络模型中的批归一化层、加法层、非线性激活层等访存密集型层以及其常见连接,提出了在训练的前向过程与反向过程中,将访存密集型层与其前后的计算密集型层融合为一个新层执行的方式,将访存密集型层的操作作为对融合新层中输入数据的前处理或者原始输出数据的后处理进行,大幅减少了访存密集型层执行过程中与片外内存交互的数据量以及训练过程中需存储在片外内存中的数据量;并针对该融合执行方案,设计实现了一个专门的面向训练的加速器,采用了暂存前处理结果、后处理操作与计算密集层的运算操作并行执行的优化策略,大幅提升了融合新层在训练各阶段执行的性能。实验结果显示,基于2 个常见的卷积神经网络模型,在面积仅增加6.4%、功耗增加10.3%的开销下,训练FP 和BP 阶段的性能分别实现了67.7%和77.6%的提升。