楊 燦 王重熙 章隆兵
(處理器芯片國(guó)家重點(diǎn)實(shí)驗(yàn)室(中國(guó)科學(xué)院計(jì)算技術(shù)研究所) 北京 100190)
(中國(guó)科學(xué)院計(jì)算技術(shù)研究所 北京 100190)
(中國(guó)科學(xué)院大學(xué) 北京 100049)
近年來(lái),深度神經(jīng)網(wǎng)絡(luò)在圖像、語(yǔ)音、自然語(yǔ)言處理等領(lǐng)域有了廣泛而深入的應(yīng)用,針對(duì)不同的應(yīng)用場(chǎng)景,都需要對(duì)神經(jīng)網(wǎng)絡(luò)模型進(jìn)行訓(xùn)練以獲得更優(yōu)的參數(shù),于是對(duì)訓(xùn)練速度的需求也不斷提升,相應(yīng)地,學(xué)術(shù)界和產(chǎn)業(yè)界也都將目光投向了神經(jīng)網(wǎng)絡(luò)模型的訓(xùn)練過(guò)程,提出了各類方法、各種加速器結(jié)構(gòu)來(lái)實(shí)現(xiàn)對(duì)訓(xùn)練過(guò)程的加速。Google 推出了新一代加速器TPUv3[1],支持多類深度學(xué)習(xí)應(yīng)用的訓(xùn)練。NVIDIA在最新的A100 圖形處理器(graphics processing unit,GPU)中進(jìn)一步升級(jí)了專門用來(lái)加速深度學(xué)習(xí)任務(wù)的Tensor 核心。華為的Ascend[2]采用了3D Cube單元實(shí)現(xiàn)對(duì)卷積和矩陣乘法的加速。Cambricon-Q[3]通過(guò)由專用的集成電路(application specific integrated circuit,ASIC)加速核心與近數(shù)據(jù)處理引擎的混合結(jié)構(gòu)實(shí)現(xiàn)高效量化訓(xùn)練的方法。RaPiD[4]使用支持超低精度訓(xùn)練的加速器來(lái)提高訓(xùn)練的性能。Sigma[5]設(shè)計(jì)了一種新的約簡(jiǎn)樹(shù)(reduction tree)結(jié)構(gòu)來(lái)實(shí)現(xiàn)對(duì)稀疏、不規(guī)則的矩陣乘法的加速。
上述針對(duì)訓(xùn)練的加速器結(jié)構(gòu)通常只關(guān)注了計(jì)算密集型層——例如卷積層、全連接層的加速,且通常將它們的操作化為矩陣乘法運(yùn)算進(jìn)行,并未涉及到神經(jīng)網(wǎng)絡(luò)模型中的其他訪存密集型層——例如批歸一化(batch normalization,BN)層、非線性激活層的加速。文獻(xiàn)[6]提出了針對(duì)批歸一化層、縮放平移層的推理階段的加速方案,通過(guò)將其操作化為加法與乘法,映射到專門的運(yùn)算單元中直接進(jìn)行計(jì)算來(lái)提升性能。文獻(xiàn)[7]提出了在推理過(guò)程中將批歸一化層、縮放平移層、ReLU 層與卷積層合并處理的方案,采用對(duì)卷積層輸出數(shù)據(jù)馬上進(jìn)行批歸一化、縮放平移、ReLU 等運(yùn)算來(lái)提高數(shù)據(jù)處理的效率。由于推理過(guò)程中批歸一化操作所需的每個(gè)通道的均值與方差是已知的常數(shù),于是批歸一化操作退化為縮放平移操作,能夠采用文獻(xiàn)[6,7]的方案進(jìn)行加速。然而,在訓(xùn)練過(guò)程中,批歸一化層的均值和方差需要由這次迭代中所有樣本的數(shù)據(jù)現(xiàn)場(chǎng)算出,不能直接退化為縮放平移操作,于是,文獻(xiàn)[6,7]的方案完全不適用。文獻(xiàn)[8]提出了一個(gè)通過(guò)將元素和與元素平方和化為矩陣運(yùn)算的方法來(lái)加速批歸一化層的方案,但在實(shí)際處理中,批歸一化層作為訪存密集型層,主要由訪存帶寬決定了執(zhí)行效率,單獨(dú)提升運(yùn)算速度對(duì)提升整體的執(zhí)行效率意義不大。
針對(duì)以上問(wèn)題,本文首先確定了訪存密集型層——批歸一化層、加法層、非線性激活層是卷積神經(jīng)網(wǎng)絡(luò)模型中重要的組成部分,分析了它們的常見(jiàn)連接以及它們?cè)谟?xùn)練的前向過(guò)程與反向過(guò)程中的計(jì)算特征后,提出了在訓(xùn)練的各階段中,將訪存密集型層與其前后的計(jì)算密集型層融合為一個(gè)新層執(zhí)行的方式。訪存密集型層的操作作為對(duì)融合新層中輸入數(shù)據(jù)的前處理或者原始輸出數(shù)據(jù)的后處理進(jìn)行,大大減少了訪存密集型層執(zhí)行過(guò)程中與片外內(nèi)存交互的數(shù)據(jù)量以及訓(xùn)練過(guò)程中需存儲(chǔ)在片外內(nèi)存中的數(shù)據(jù)量。在此基礎(chǔ)上,本文設(shè)計(jì)實(shí)現(xiàn)了一個(gè)新的面向訓(xùn)練的加速器,能夠高效地支持融合新層的前向過(guò)程與反向過(guò)程的執(zhí)行,并采用了暫存前處理結(jié)果、后處理操作與計(jì)算密集層的運(yùn)算操作并行執(zhí)行的優(yōu)化策略,進(jìn)一步提升了融合新層的性能。實(shí)驗(yàn)結(jié)果顯示,本文提出的融合方案與加速器結(jié)構(gòu)在面積增加6.4%、功耗增加10.3%的開(kāi)銷下分別實(shí)現(xiàn)了對(duì)訓(xùn)練的前向階段、反向階段67.7%、77.6%的性能提升。
在卷積神經(jīng)網(wǎng)絡(luò)(convolutional neural network,CNN)模型中,常用的層包括了卷積層(convolutional layer,CONV)、池化層、全連接層和非線性層(nonlinear layer,NonL)。隨著CNN 在算法上的發(fā)展,CNN 模型中還出現(xiàn)了另外兩類常用的層。
第1 類是批歸一化(BN)層。在訓(xùn)練的過(guò)程中,網(wǎng)絡(luò)的參數(shù)不斷變化導(dǎo)致了網(wǎng)絡(luò)中激活值的分布不斷發(fā)生變化,于是需要更低的學(xué)習(xí)速率和更精細(xì)的參數(shù)初始化來(lái)保證訓(xùn)練的精度以及收斂性,因而降低了訓(xùn)練的速度。為了解決這個(gè)問(wèn)題,Google 提出了批歸一化[9]的方法,通過(guò)對(duì)每層的輸入進(jìn)行歸一化,固定住每層輸入的均值和方差,減少內(nèi)部協(xié)變量偏移(internal covariate shift),使得訓(xùn)練過(guò)程能夠使用更高的學(xué)習(xí)速率,從而加速網(wǎng)絡(luò)的訓(xùn)練。常用的CNN 模型中都包含了BN 層,主要的層間連接方式有CONV→BN→NonL 和CONV→BN。
第2 類是捷徑連接(shortcut connections)與堆積層(stacked layers)的結(jié)果累加形成的累加層(Add)。文獻(xiàn)[10]提出了一類殘差網(wǎng)絡(luò)模型,設(shè)計(jì)了“捷徑連接”來(lái)實(shí)現(xiàn)需要的映射。捷徑連接執(zhí)行恒等映射,并將其結(jié)果與堆積層的結(jié)果累加起來(lái),如圖1 所示。該類捷徑連接與累加結(jié)構(gòu)獲得了巨大的成功,在后續(xù)多種常用的CNN 模型中出現(xiàn),如ResNext[11]、Wide ResNet[12]、MobileNet v2[13]等;并且成為神經(jīng)架構(gòu)搜索中的網(wǎng)絡(luò)模型的基本組成部件之一,出現(xiàn)在了MobileNet v3[14]、MNASNet[15]、Reg-Net[16]等輕量級(jí)模型中。于是,捷徑連接與堆積層的結(jié)果的累加層也成為了CNN 模型中的一個(gè)常用層。
圖1 殘差學(xué)習(xí)塊[11]
深度神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過(guò)程一般分為2 個(gè)部分,即前向傳播(forward propagation,FP)和反向傳播(backward propagation,BP)。在FP 過(guò)程中,將minibatch個(gè)樣本作為網(wǎng)絡(luò)模型的輸入,按照網(wǎng)絡(luò)的順序從前至后依次進(jìn)行計(jì)算,得到該網(wǎng)絡(luò)模型的輸出結(jié)果。接著,按照訓(xùn)練方法中規(guī)定的誤差計(jì)算方式,計(jì)算出這批樣本的誤差值,然后開(kāi)始BP 過(guò)程。在BP過(guò)程中,按照鏈?zhǔn)椒▌t將誤差在網(wǎng)絡(luò)模型中由后向前傳播,并利用每層的特征圖和誤差值計(jì)算出權(quán)值梯度。最后,根據(jù)權(quán)值梯度和訓(xùn)練算法中給定的更新權(quán)值的方法對(duì)權(quán)值進(jìn)行更新后,開(kāi)始下一批樣本的迭代。
使用GPU(NVIDIA GeForce RTX 2080 Ti)執(zhí)行了多個(gè)常用CNN 模型的訓(xùn)練過(guò)程,用PyTorch Profiler 測(cè)量了每個(gè)層在FP 和BP 階段的運(yùn)行時(shí)間。圖2中給出了各模型的BN、NonL 和Add 層在FP 和BP過(guò)程中的執(zhí)行時(shí)間,執(zhí)行時(shí)間分別按照FP 和BP 過(guò)程中CONV 層的執(zhí)行時(shí)間進(jìn)行了歸一化。從圖2 中可以看出,FP 過(guò)程中BN、NonL 和Add 的平均執(zhí)行時(shí)間分別是0.31、0.19 和0.1,總共達(dá)到了0.6;BP過(guò)程中BN、NonL 和Add 的平均執(zhí)行時(shí)間分別是0.46、0.25 和0.09,總共達(dá)到了0.8。其中,Res-Net50 的FP、BP 過(guò)程,BN、NonL、Add 的執(zhí)行時(shí)間總和分別是0.85 和1.11。以上觀察說(shuō)明,除了CONV層,BN 層、NonL 層和Add 層的執(zhí)行時(shí)間在CNN 的訓(xùn)練過(guò)程中也占了很大的比重,對(duì)它們進(jìn)行加速可以有效地縮短訓(xùn)練的時(shí)間。
圖2 FP、BP 中BN、NonL、Add 層的執(zhí)行時(shí)間
1.2.1 BN 層的計(jì)算過(guò)程
{xi,i=1,…,m} 是本次訓(xùn)練迭代中BN 層的mini-batch個(gè)樣本的同一個(gè)通道上的m個(gè)輸入值,{yi} 是BN 層的輸出結(jié)果。BN 層的FP 過(guò)程[10],首先計(jì)算出{xi} 的均值μB和方差
最后,使用BN 層給定的參數(shù)γ和β,對(duì)進(jìn)行線性變換操作得到BN 層的輸出結(jié)果{yi}:
執(zhí)行BN 層的FP 過(guò)程時(shí),需要將BN 層的輸入值從片外內(nèi)存中讀到片上,計(jì)算出均值和方差。由于片上的緩存區(qū)容量有限,在放不下mini-batch個(gè)樣本的輸入值時(shí),執(zhí)行式(3)、(4)中的計(jì)算需要從片外再讀1 次BN 層的輸入值到片上,才能計(jì)算出BN 層的結(jié)果并存回片外內(nèi)存中,供下一層使用。因此,訪存量是BN 層輸入值的3 倍。
文獻(xiàn)[10]給出了BN 層進(jìn)行反向誤差傳播的計(jì)算公式:
1.2.2 NonL 和Add 層的計(jì)算過(guò)程
NonL 層最常用的激活函數(shù)是ReLU。NonL 層的FP 從片外內(nèi)存中讀入前一層的結(jié)果到片上,進(jìn)行規(guī)定的激活操作后,將結(jié)果存回到片外,共2 倍輸入值的訪存量;NonL 層的BP 從片外讀入輸入誤差值和FP 過(guò)程的輸入特征圖到片上,進(jìn)行計(jì)算后存回片外,共3 倍輸入值的訪存量。
Add 層的FP 和BP 過(guò)程相同,都是從片外讀入2 個(gè)需要累加的輸入值,進(jìn)行加法操作后,將結(jié)果存回片外,共3 倍輸入值的訪存量。
BN、NonL 和Add 層在FP 和BP 過(guò)程的操作都是訪存密集型的,計(jì)算過(guò)程中對(duì)運(yùn)算單元的利用率極低。
為了實(shí)現(xiàn)對(duì)BN、NonL、Add 層的加速,本節(jié)提出了一個(gè)將訪存密集型層與計(jì)算密集型層融合執(zhí)行的方式,能夠減少運(yùn)行過(guò)程中與片外內(nèi)存交互的數(shù)據(jù)量,從而提升性能。
不管是FP 還是BP,NonL、Add 層的輸出結(jié)果僅需同一個(gè)樣本中與結(jié)果同一位置的輸入作源操作數(shù)即可算得。于是,有2 種直觀的融合方式:一是可以將其操作直接與其后執(zhí)行的相鄰層進(jìn)行融合,讀入NonL 或Add 層的輸入值后,立即算出結(jié)果作為其后相鄰層的輸入值,接著進(jìn)行其后相鄰層的計(jì)算。對(duì)于融合后的新層,以其后相鄰層的計(jì)算為主,NonL和Add 層的操作相當(dāng)于對(duì)融合新層的輸入值的一個(gè)“前處理”。二是可以將其操作與執(zhí)行順序的前一層進(jìn)行融合,相當(dāng)于對(duì)前一層的輸出結(jié)果增加了一個(gè)“后處理”,成為融合新層的新輸出結(jié)果。
兩種方式下,都能夠直接減少NonL 和Add 層與片外內(nèi)存的交互數(shù)據(jù)量;并且,只需要將融合新層的輸出值存在片外,內(nèi)存中不需要存融合新層中內(nèi)部層的結(jié)果,減少了訓(xùn)練過(guò)程中對(duì)內(nèi)存容量的需求。
與NonL、Add 層不同,BN 層的一個(gè)結(jié)果與這次訓(xùn)練過(guò)程中使用的mini-batch個(gè)樣本的該通道上的所有源操作數(shù)都相關(guān),不能直接與其前后相鄰層融合。將式(3)帶入式(4)中,可以得到:
其中,F1和F2是關(guān)于BN 層每個(gè)通道的參數(shù)項(xiàng),計(jì)算方式為
由式(9)可以看出,在算出關(guān)于每個(gè)通道的參數(shù)項(xiàng)F1和F2后,BN 層的FP 就化為了僅與同一樣本的同一位置的源操作數(shù)相關(guān)的計(jì)算。
將式(5)~(7)帶入式(8)中,可以得到:
其中,F1和B、C都是關(guān)于BN 層每個(gè)通道的參數(shù)項(xiàng),B和C的計(jì)算方式為
由式(12)可以看出,在算出了每個(gè)通道的參數(shù)項(xiàng)B和C后,BN 的BP 就化為了僅與同一樣本的同一位置的源操作數(shù)相關(guān)的計(jì)算。于是,式(9)和式(12)中的操作都能夠直接與其后相鄰層進(jìn)行融合執(zhí)行。
從式(12)~(15)中可以看出,FP 需要計(jì)算出均值μB、方差和參數(shù)項(xiàng)F1、F3供BP 階段使用,并且需要計(jì)算出參數(shù)項(xiàng)F2供FP 階段使用。
由式(10)、(11)、(15)可以看出,由于γ、β和ε都是訓(xùn)練中給定的數(shù)值,算出μB和后就能得到參數(shù)項(xiàng)F1、F2和F3。將式(2)展開(kāi),可得:
從式(13)、(14)中可以看出,由于F1、F3和μB都是FP 過(guò)程中已經(jīng)算好的參數(shù)項(xiàng),要算出參數(shù)項(xiàng)B和C僅需算出S1和S2。從式(16)和(17)可得,計(jì)算S1和S2即計(jì)算每個(gè)通道中的累積和。由于是BN 層的輸入誤差值,也是BN 在BP執(zhí)行過(guò)程中的前一層的輸出誤差值,于是,可以將這2 對(duì)累積和的計(jì)算與前一層進(jìn)行融合,在前一層的原操作完成以后,對(duì)輸出值進(jìn)行一個(gè)“后處理”,計(jì)算出需要的累積和。
于是,BN 層的FP 和BP 過(guò)程都可以分為2 個(gè)步驟:第1 步為計(jì)算累積和及參數(shù)項(xiàng),與執(zhí)行順序的前一層進(jìn)行融合,作為其輸出值的“后處理”出現(xiàn);第2 步為式(9)、(12)中的僅與同一樣本的同一位置的源操作數(shù)相關(guān)的計(jì)算,與執(zhí)行順序的后一層進(jìn)行融合,作為對(duì)融合新層的輸入值的“前處理”出現(xiàn)。
下文中使用ifmap 和ofmap 分別代表FP 過(guò)程中一個(gè)層的輸入數(shù)據(jù)與輸出數(shù)據(jù);使用ierr 和oerr 分別代表BP 過(guò)程中一個(gè)層的輸入誤差值與輸出誤差值;使用WG(weight gradient generation)來(lái)表示訓(xùn)練中反向傳播時(shí)計(jì)算權(quán)值梯度的過(guò)程;使用C1、C2、C3 代表卷積層。
(1)C1→ReLU→C2
FP 時(shí),ReLU 與C2 融合,僅將C1_ofmap 存回片外,不需要將ReLU_ofmap 存回片外。于是,Re-LU 層的片外訪存量為0。
BP 時(shí),由于C2 的WG 過(guò)程需要用到ReLU_ofmap,即會(huì)把C1_ofmap 從片外讀到片上,于是選擇ReLU 與C2 融合,對(duì)C2_oerr 進(jìn)行后處理計(jì)算C2_oerr × ReLU′(C1_ofmap) 作為融合新層的結(jié)果,存回到片外,供C1 做BP 時(shí)使用。ReLU 化為后處理進(jìn)行,需要用到C1_ofmap,但由于C2 的WG 過(guò)程本身就會(huì)將C1_ofmap 讀到片上,于是,ReLU 帶來(lái)的片外訪存量為0。
(2)C1→BN→C2
FP 時(shí),BN 的第1 步與C1 融合,計(jì)算出參數(shù)項(xiàng);第2 步與C2 融合。僅將C1_ofmap 存回片外。因此,BN 層沒(méi)有額外的片外訪存,片外訪存量為0。
BP 時(shí),BN 的第1 步與C2 融合。由于WG 時(shí)需要用到BN_ofmap,于是從片外讀C1_ofmap 到片上,參與BN 與C2 融合層的后處理和C2 的WG 的計(jì)算。BN 的第2 步與C1 融合,計(jì)算C1 的BP 時(shí),讀入C2_oerr 和C1_ofmap 執(zhí)行式(12)中的計(jì)算,算出C1_ierr 后執(zhí)行C1 層的操作。于是,C1 融合層的執(zhí)行除了讀C2_oerr 作為輸入值外,還因?yàn)榍疤幚矶嘧x了C1_ofmap 到片上,相當(dāng)于BN 層給融合層的執(zhí)行帶來(lái)了1 倍輸入值的片外訪存量。
(3)C1→BN→ReLU→C2
FP 時(shí),BN 的第1 步與C1 融合,計(jì)算出參數(shù)項(xiàng);BN 的第2 步與ReLU 和C2 一起融合為一個(gè)新層,BN 第2 步和ReLU 都作為融合新層的前處理執(zhí)行。僅將C1_ofmap 存回片外,于是,BN 和ReLU 沒(méi)有額外的訪存,片外訪存量為0。
BP 時(shí),BN 的第1 步與ReLU 和C2 融合,都作為對(duì)C2_oerr 的后處理執(zhí)行。由于式(12)、(16)、(17)中的就是BN_ierr,有:
將式(19)帶入式(16)、(17)中,就能計(jì)算出BN第1 步的參數(shù)項(xiàng)。BN 的第2 步與C1 融合,計(jì)算C1的BP 時(shí),讀入C2_oerr 和C1_ofmap,執(zhí)行將式(19)帶入式(12)后的計(jì)算,得到C1_ierr 后執(zhí)行C1 層的操作。
C2 的后處理需要C1_ofmap,由于C2 的WG本身需要將C1_ofmap 讀到片上,于是不增加新的片外訪存。C1 融合新層的執(zhí)行由于前處理多讀了C1_ofmap 到片上,相當(dāng)于BN 和ReLU 帶來(lái)了1 倍輸入值的片外訪存量。
(4)C1→BN→Add→ReLU→C2
1)C1 的同個(gè)殘差塊中的捷徑連接沒(méi)有卷積計(jì)算
FP 時(shí),BN 的第1 步與C1 融合;BN 的第2 步與Add、ReLU 層和C2 一起融合成一個(gè)新層,從片外讀入C1_ofmap 算出BN_ofmap,從片外讀入C1 同個(gè)殘差塊中捷徑連接的輸出結(jié)果與BN_ofmap 相加,做ReLU 操作,得到C2_ifmap 后進(jìn)行C2 層的操作。這個(gè)過(guò)程中,除了要將C1_ofmap 存回片外,由于C2 的WG 需要用到C2_ifmap,選擇將算好的C2_ifmap 也存到片外。因此,在計(jì)算C2 融合層時(shí),需要從片外讀入捷徑連接的輸出值且需要將算好C2_ifmap 存到片外,相當(dāng)于BN、Add、ReLU 帶來(lái)了2倍輸入值的片外訪存量。
BP 時(shí),將BN 的第1 步、ReLU、誤差A(yù)dd 與C2融合為一個(gè)新層,都作為對(duì)C2_oerr 的后處理執(zhí)行。用Scut_oerr 代表C2 同一殘差塊中捷徑連接的誤差,在C2 算出了C2_oerr 后,繼續(xù)進(jìn)行:
計(jì)算,將BN_ierr 作為融合新層的結(jié)果存回片外,并繼續(xù)使用算出的BN_ierr 值進(jìn)行BN 層的第1步。BN 的第2 步與C1 融合,計(jì)算時(shí),讀入BN_ierr和C1_ofmap 執(zhí)行式(12)中的計(jì)算,算出C1_ierr后執(zhí)行C1 層的操作。
由于C2 的WG 直接使用C2_ifmap,于是C2融合新層的后處理需要多讀Scut_oerr 和C1_ofmap 到片上,帶來(lái)了2 倍輸入值的訪存量;C1 融合新層的前處理時(shí)需要多讀C1_ofmap 到片上。因此,BN、ADD、ReLU 總共帶來(lái)了3 倍輸入值的片外訪存量。
2)C1 同個(gè)殘差塊中的捷徑連接是C3→BN3
FP 時(shí),BN3 的第1 步與C3 融合;BN3 的第2 步與BN 層的第2 步、Add、ReLU 和C2 一起融合為一個(gè)新層,作為對(duì)該新層的前處理出現(xiàn)。與1)中相同,除了將C2_ofmap 存回片外,還要將前處理結(jié)果C2_ifmap 存回片外。同樣與1)中相同,BN、Add、ReLU 帶來(lái)了2 倍輸入值的片外訪存量。
BP 時(shí),將BN 的第1 步、BN3 的第1 步、ReLU、誤差A(yù)dd 與C2 融合為一個(gè)新層,都作為對(duì)C2_oerr的后處理執(zhí)行。同樣使用式(20)計(jì)算出BN_ierr作為融合新層的結(jié)果存回片外,并分別利用BN_ierr 執(zhí)行BN 的第1 步和BN3 的第1 步。BN 的第2步與C1 融合。BN3 的第2 步與C3 融合。
由于C2 的WG 直接使用C2_ifmap,于是C2融合新層的后處理需要多讀Scut_oerr、C1_ofmap、C3_ofmap 到片上,帶來(lái)了3 倍輸入值的訪存量;C1融合新層的前處理時(shí)需要多讀C1_ofmap 到片上;C3 融合新層的前處理時(shí)需要多讀C3_ofmap 到片上。因此,BN、ADD、ReLU 總共帶來(lái)了5 倍輸入值的片外訪存量。
表1 中總結(jié)了2.3 節(jié)中各類常見(jiàn)層間連接的訪存密集型層在FP 和BP 過(guò)程中單獨(dú)執(zhí)行或融合執(zhí)行時(shí)與片外內(nèi)存交互的數(shù)據(jù)量,以一個(gè)訪存密集型層的mini-batch個(gè)樣本的輸入數(shù)據(jù)量為單位。1.2.1 節(jié)和1.2.2 節(jié)說(shuō)明了ReLU、BN、Add 的FP 和BP 過(guò)程在單獨(dú)執(zhí)行時(shí)分別有2、3、3 倍和3、5、3 倍輸入值的片外訪存量。情況3 包含了1 個(gè)BN 和1 個(gè)ReLU,因此FP 和BP 分別為5 倍和8 倍輸入值訪存量;情況(4)-1)包含了1 個(gè)BN、1 個(gè)Add 和1 個(gè)ReLU,因此FP 和BP 分別為8 倍和11 倍輸入值訪存量;情況(4)-2)包含了2 個(gè)BN、1 個(gè)ADD 和1 個(gè)ReLU,因此FP 和BP 分別為11 倍和16 倍的輸入值訪存量。
表1 訪存密集型層片外訪存量
從表1 中可以看出,融合后的訪存密集型層執(zhí)行時(shí)與片外內(nèi)存交互的數(shù)據(jù)量在各類常見(jiàn)連接下都得到了大幅度的減少。
表2 中給出了2.3 節(jié)中各類常見(jiàn)層間連接的訪存密集型層在訓(xùn)練過(guò)程中單獨(dú)執(zhí)行或融合執(zhí)行時(shí)占用的片外內(nèi)存的容量,以一個(gè)訪存密集型層的minibatch個(gè)樣本的輸入數(shù)據(jù)量為單位。情況(1)~(3)中,完全消除了BN、ReLU 層在訓(xùn)練過(guò)程中對(duì)片外內(nèi)存容量的占用;情況(4)-1)和(4)-2)由于需要在FP 時(shí)將前處理結(jié)果C2_ifmap 存回片外供WG 使用,于是占用1 倍輸入值的內(nèi)存容量。
表2 訪存密集型層占用片外內(nèi)存容量
融合后的執(zhí)行方案大幅減少了訓(xùn)練過(guò)程中在片外內(nèi)存中存儲(chǔ)的數(shù)據(jù)量,能夠容納更大規(guī)模的樣本在同一加速器芯片上進(jìn)行訓(xùn)練,緩解因片外內(nèi)存容量的限制而帶來(lái)的訓(xùn)練的擴(kuò)展性或性能上的問(wèn)題。
基于上述融合方案,本文設(shè)計(jì)實(shí)現(xiàn)了一個(gè)面向訓(xùn)練的加速器,能夠高效地支持融合新層的執(zhí)行,采用了暫存前處理結(jié)果、后處理操作與計(jì)算密集層的運(yùn)算操作并行執(zhí)行優(yōu)化的策略,進(jìn)一步提升了融合新層的性能。
圖3 展示了一個(gè)面向訓(xùn)練的加速器結(jié)構(gòu),其中,實(shí)線邊框模塊與實(shí)線連接線構(gòu)成了加速器的基礎(chǔ)結(jié)構(gòu),支持每個(gè)層單獨(dú)執(zhí)行?;A(chǔ)結(jié)構(gòu)包含了M行N列的處理單元(processing unit,PU),主要用來(lái)執(zhí)行計(jì)算密集型層的計(jì)算;有3 塊分離的片上緩存區(qū)Fbuf_In、Wbuf 和Fbuf_Out,Fbuf_In 主要用來(lái)存放計(jì)算密集型層的輸入值,Wbuf 用來(lái)存放權(quán)值以及權(quán)值梯度,Fbuf_Out 主要用來(lái)存放每層計(jì)算出的輸出值以及WG 所需的ifmap 值;每塊Fbuf_In 都連接著一個(gè)輸入寄存器堆Ifreg,給對(duì)應(yīng)行的所有PU提供輸入值;每塊Wbuf 都連接著一個(gè)權(quán)值寄存器堆Wreg,給對(duì)應(yīng)列的所有PU 提供權(quán)值。每個(gè)PU中包含了多個(gè)乘加計(jì)算單元及一個(gè)輸出寄存器堆Ofreg,Ofreg 與Fbuf_Out 直接相連,用來(lái)存計(jì)算過(guò)程中產(chǎn)生的中間值和最終的運(yùn)算結(jié)果以及存WG 過(guò)程用到的ifmap 值。同一列中相鄰2 個(gè)PU 形成一個(gè)組,共同擁有一個(gè)累加單元Adder,用來(lái)實(shí)現(xiàn)2 個(gè)PU 算出的結(jié)果的累加。Fbuf_Out 還連接著一個(gè)向量單元VecU,用來(lái)處理非訪存密集型層中的計(jì)算。指令隊(duì)列Inst_Queue 從Host 處接收指令并轉(zhuǎn)換成控制信號(hào)控制每個(gè)PU 列的執(zhí)行。
圖3 加速器的結(jié)構(gòu)
圖3 中虛線邊框模塊(帶陰影模塊)與虛線連接線是為了支持非訪存密集型層的融合執(zhí)行而增加的硬件結(jié)構(gòu)。在每行的Fbuf_In 和Ifreg 旁增加了一個(gè)PreU,用來(lái)執(zhí)行對(duì)融合新層輸入值的前處理。每列中相鄰2 個(gè)PU 共同擁有一個(gè)PostU 模塊,用來(lái)執(zhí)行對(duì)融合新層的運(yùn)算結(jié)果的后處理。每一列中增加了一個(gè)除法開(kāi)方單元DivSqrt,用來(lái)處理BN 第1步中計(jì)算參數(shù)項(xiàng)涉及到的除法和開(kāi)方運(yùn)算。每列中還增加了一塊緩存區(qū)Bbuf,用來(lái)存儲(chǔ)BN 計(jì)算累積值或參數(shù)項(xiàng)過(guò)程中需要的參數(shù)值(γ和β)、計(jì)算的中間結(jié)果、算出的參數(shù)項(xiàng);并為融合新層的前處理或后處理提供需要的參數(shù)項(xiàng)值。
對(duì)于每個(gè)從Fbuf_In 中讀出的數(shù)據(jù),都增加一份如圖4 所示的PreU 單元,其中主要包含了1 個(gè)乘加單元、1 個(gè)16 比特的寄存器SrcReg 和一些多選邏輯。從Fbuf_In 中讀出的數(shù)據(jù)可以寫到Ifreg 或者SrcReg 中,和常數(shù)值以及參數(shù)項(xiàng)F一起,生成乘加單元的源操作數(shù),乘加結(jié)果寫回到Ifreg 或者SrcReg中。前處理過(guò)程的最終結(jié)果寫到Ifreg 中,然后送到各行的PU 中進(jìn)行后續(xù)的計(jì)算。
圖4 PreU 的結(jié)構(gòu)
PostU 的結(jié)構(gòu)如圖5 所示,主要包含了4 個(gè)乘加單元和2 項(xiàng)4 ×32 比特的寄存器Comp_Reg 和Acc_Reg。乘加單元使用來(lái)自O(shè)freg、Comp_Reg、Acc_Reg 的數(shù)據(jù),常數(shù)值0、1 以及參數(shù)項(xiàng)F作為被乘數(shù)、乘數(shù)和加數(shù),乘加結(jié)果寫回到Ofreg 中或Comp_Reg、Acc_Reg 中。
3.2.1 前處理操作的加速方案
融合新層的前處理的執(zhí)行時(shí)間可能會(huì)導(dǎo)致后續(xù)計(jì)算的阻塞,為了緩解這部分影響,執(zhí)行過(guò)程中,將Fbuf_In 分成2 部分使用,一部分存融合新層的原輸入值,當(dāng)?shù)? 次從Fbuf_In 中讀入一行原輸入值進(jìn)行計(jì)算時(shí),將該行輸入值的前處理結(jié)果從Ifreg 中寫回到Fbuf_In 的第2 部分中去;后續(xù)計(jì)算直接從Fbuf_In 的第2 部分中取算好的前處理結(jié)果到Ifreg中,直接參與計(jì)算。于是,后續(xù)關(guān)于該行的輸入值的計(jì)算都不會(huì)再受到前處理的影響。
3.2.2 后處理操作的執(zhí)行方案
為了消除后處理給融合新層帶來(lái)的影響,采用了讓PU 的計(jì)算與對(duì)結(jié)果的后處理并行執(zhí)行的方案。如圖5 所示,將一個(gè)PU 中的8 項(xiàng)Ofreg 平分成2 份ping-pong 使用,一份用來(lái)放PU 中正在計(jì)算的中間結(jié)果(32 比特浮點(diǎn)數(shù));另一份存儲(chǔ)著已經(jīng)算出的原始輸出結(jié)果(16 比特浮點(diǎn)數(shù)),將其作為源操作數(shù)送到PostU 中執(zhí)行需要的后處理。由于原始輸出結(jié)果只需要part0 部分的Ofreg 即可放下,于是空余的part1 部分可以用來(lái)放后處理過(guò)程中需要的其他源操作數(shù)。
后處理過(guò)程由2 類操作構(gòu)成。第1 類是由原始輸出結(jié)果算出融合新層的新輸出結(jié)果,存回片外。
如圖6 所示,使用Ofreg 0~3 存放PU0 和PU1中正在計(jì)算的中間結(jié)果;Ofreg 4~7 的part0 中存儲(chǔ)著已經(jīng)算完的原始結(jié)果,依次執(zhí)行關(guān)于PU0 和PU1中的原始結(jié)果的后處理第1 類操作。
圖6 后處理第1 類操作的執(zhí)行過(guò)程
關(guān)于PU0 的后處理:①先從PU0 的Ofreg 4~7的part0 讀出第1 列的4 個(gè)原始結(jié)果,分別從PU0和PU1 的Ofreg 4~7 的part1 讀出第1 列的其他源操作數(shù),送到PostU 的4 個(gè)乘加單元中進(jìn)行計(jì)算,得到4 個(gè)新結(jié)果后,寫回到PU0 的Ofreg 4~7 的part0中的第一列中;然后對(duì)②處的列進(jìn)行相同的操作,直到PU0 的Ofreg 4~7 的part0 中的所有原始結(jié)果都處理完成,得到所有的輸出結(jié)果為止;③將PU0 的Ofreg 4~7 的part0 中的新輸出結(jié)果寫回到Fbuf_Out 中。關(guān)于PU1 的后處理,與PU0 中類似執(zhí)行即可。
后處理過(guò)程的第2 類操作是根據(jù)輸出結(jié)果計(jì)算BN 的累積值和參數(shù)項(xiàng)。
如圖7 所示,計(jì)算累積值時(shí):①分別從Ofreg 4~7 的part0 和part1 的第1 列讀出4 個(gè)結(jié)果值和4個(gè)其他源操作數(shù),以及從Comp_Reg 中讀出4 個(gè)已有的累積值,送到PostU 的4 個(gè)乘加單元中,計(jì)算出4 個(gè)新的屬于同一個(gè)累加值的部分結(jié)果,存到Comp_Reg 中;然后對(duì)②處的列執(zhí)行相同的操作,直到Ofreg 4~7 的part0 中的所有結(jié)果都處理完為止;③接著使用Adder 將Comp_Reg 中的4 個(gè)部分結(jié)果累加為1 個(gè)累積值,累積值寫回到Acc_Reg 中;由于每列PU 執(zhí)行同一個(gè)輸出通道的計(jì)算,當(dāng)PU0~3當(dāng)前計(jì)算的這個(gè)輸出通道的計(jì)算完成后,④使用PU0/1 旁的Adder 將PU0/1 和PU2/3 對(duì)應(yīng)的Acc_Reg 中關(guān)于不同樣本同一通道的累積值累加起來(lái),得到新的累積值先寫回到PU0/1 對(duì)應(yīng)的Acc_Reg中;最后⑤存到Bbuf 中。
圖7 后處理第2 類操作計(jì)算累積值的過(guò)程
等所有樣本關(guān)于一個(gè)通道的累積值計(jì)算完成后,使用每列第1 個(gè)PostU(PU0/1 對(duì)應(yīng)的PostU)進(jìn)行參數(shù)項(xiàng)計(jì)算。從Bbuf 中讀出源操作數(shù),將源操作數(shù)和中間結(jié)果都存到Comp_Reg 和Acc_Reg 中;并使用該列的DivSqrt 進(jìn)行參數(shù)項(xiàng)中的除法、開(kāi)方計(jì)算。最終算出關(guān)于該通道的參數(shù)項(xiàng),寫回到Bbuf中。
本節(jié)介紹實(shí)驗(yàn)環(huán)境和實(shí)驗(yàn)數(shù)據(jù),著重從性能的角度評(píng)估了本文提出的加速器結(jié)構(gòu)及加速訪存密集型層的融合方案。
利用Verilog 語(yǔ)言在寄存器傳輸級(jí)(register transfer level,RTL)實(shí)現(xiàn)了圖3 所示的加速器。使用Synopsys Design Compiler 在ST 28 nm 工藝下進(jìn)行綜合。使用Synopsys VCS 工具對(duì)加速器設(shè)計(jì)進(jìn)行模擬和驗(yàn)證,測(cè)量其性能。
實(shí)驗(yàn)中加速器采用16 行32 列的PU,每個(gè)PU中有32 個(gè)乘加運(yùn)算單元,共16 384 個(gè)乘加單元。每個(gè)Adder 中采用4 個(gè)32 比特浮點(diǎn)格式的加法器。每行PU 對(duì)應(yīng)1 塊768 kB 的Fbuf_In,共12 MB。每列PU 對(duì)應(yīng)1 塊36 kB 的Wbuf,共1152 kB。2 個(gè)PU對(duì)應(yīng)1 塊64 kB 的Fbuf_Out,共16 MB。每列PU 對(duì)應(yīng)1 塊6 kB 的Bbuf,共192 kB。Fbuf_In 每次讀寫16 個(gè)數(shù)據(jù),于是每行的PreU 中包含了16 個(gè)乘加運(yùn)算單元。PU 中一項(xiàng)Ofreg 為16 ×32 比特。加速器的頻率為800 MHz。關(guān)于Wbuf 和Bbuf 的訪存帶寬為25.6 GB/s,關(guān)于Fbuf_In 和Fbuf_Out 的訪存帶寬為205 GB/s。該配置下,主運(yùn)算陣列的乘加單元數(shù)量、片上緩存區(qū)大小、帶寬都與主流訓(xùn)練加速器相近。
為了評(píng)估本文提出的加速器的性能,選擇了Pytorch 的torchvision.models 中提供的resnet18 和resnet50 作為測(cè)試模型。訓(xùn)練過(guò)程中mini-batch設(shè)為32。
圖8 給出了FP 階段ResNet50 的各個(gè)ResBlock在本文提出的加速器上融合執(zhí)行、單獨(dú)執(zhí)行的時(shí)間。從圖8 中可以看出,每個(gè)ResBlock 融合執(zhí)行的時(shí)間都比單獨(dú)執(zhí)行的時(shí)間短,其中性能提升最大的是conv2_x blk0,提高了3 倍;conv2_x blk1/2、conv3_x blk1/2/3 的提升也較大,分別達(dá)到了1.3 倍和1倍;conv5_x blk1/2 的提升較小,約為10%。整個(gè)FP過(guò)程融合執(zhí)行的性能比單獨(dú)執(zhí)行時(shí)提高了87.0%。
圖9 給出了BP 階段ResNet50 的各個(gè)ResBlock在本文提出的加速器上融合執(zhí)行、單獨(dú)執(zhí)行的時(shí)間。除了conv5_x 的blk1 和blk2,其他ResBlock 的融合執(zhí)行時(shí)間都比單獨(dú)執(zhí)行時(shí)間短,性能提升了0.41~2.8 倍,整體上融合執(zhí)行時(shí)性能比單獨(dú)執(zhí)行時(shí)提升了1 倍。
圖9 BP 階段ResNet50 各ResBlock 融合、單獨(dú)執(zhí)行的時(shí)間
圖8 和9 中還給出了各ResBlock 在GPU(NVIDIA GeForce RTX 2080 Ti)上單獨(dú)執(zhí)行時(shí)的時(shí)間。單獨(dú)執(zhí)行時(shí),本文加速器與GPU 的執(zhí)行時(shí)間相差在5%以內(nèi);采用融合方案后,本文加速器在FP 和BP階段分別比GPU 的性能提高了78.9%和95.7%。
從圖8 和9 中可以看出,ResNet50 網(wǎng)絡(luò)模型中前面的ResBlock 采用融合方案執(zhí)行帶來(lái)的性能提升更大,后面的更小。這是因?yàn)榍懊娴腞esBlock 中采用了更大的ifmap、ofmap、ierr、oerr,因而在單獨(dú)執(zhí)行訪存密集型層時(shí),需要消耗更長(zhǎng)的時(shí)間來(lái)與片外內(nèi)存交互數(shù)據(jù),于是在融合方案下能夠消除更多的時(shí)間,帶來(lái)更大的性能提升。
另外,由圖8 和9 中還可以看出,融合后的執(zhí)行時(shí)間比單獨(dú)執(zhí)行時(shí)卷積層的執(zhí)行總時(shí)間要長(zhǎng),這主要由3 個(gè)方面的原因造成。第1,融合新層的前處理可能導(dǎo)致源操作數(shù)不能及時(shí)準(zhǔn)備好送到PU 中執(zhí)行運(yùn)算;第2,融合新層的后處理可能導(dǎo)致在PU 計(jì)算完成后另一部分的后處理沒(méi)完成而使PU 的運(yùn)算阻塞;第3,融合新層相比于原始的卷積層,可能增加了執(zhí)行過(guò)程中需要的源操作數(shù),引起訪存時(shí)間的增加,當(dāng)訪存帶寬不夠時(shí),阻塞PU 的計(jì)算。圖10中給出了ResNet50 的2 個(gè)ResBlock 塊中的各融合新層在FP 和BP 時(shí)由上述3 類原因而增加的執(zhí)行時(shí)間的占比。由于前處理增加的時(shí)間占比最大的是BP 時(shí)的conv3_x blk0 中的conv1,達(dá)到了15%,平均下來(lái)是5%,說(shuō)明3.2.1 節(jié)中的前處理加速方案很好地消除了前處理帶來(lái)的影響。圖10 中,只有BP時(shí)conv2_x blk1 中的conv0 有后處理對(duì)執(zhí)行時(shí)間的明顯影響,其他融合新層中后處理的執(zhí)行時(shí)間都隱藏在了卷積層運(yùn)算執(zhí)行的過(guò)程中,說(shuō)明3.2.2 節(jié)中的后處理并行執(zhí)行方案幾乎消除了后處理對(duì)執(zhí)行時(shí)間的影響。從圖10 中可以看出,FP 時(shí)的conv0、BP時(shí)的conv0 和conv2 中由于訪存而增加的時(shí)間占比很高,平均在50%。這是因?yàn)檫@些融合層執(zhí)行時(shí)涉及到了Add 層的融合以及BP 時(shí)關(guān)于1 個(gè)或2 個(gè)BN層的后處理,相比于原始的卷積層需要更多的訪存量。
圖10 融合新層各類增加時(shí)間的占比
表3 分別給出了圖3 中所示的加速器基礎(chǔ)部分與支持融合的加速器的各組成部分的面積與功耗。加速器基礎(chǔ)部分的面積和功耗分別為145.6 mm2和75.2 W,支持融合后的面積和功耗分別為154.9 mm2和82.9 W,分別增加了6.4%和10.3%。面積與功耗的增長(zhǎng)主要由PU 中的PostU、Ifreg 中的PreU 以及Bbuf 帶來(lái)。
表3 加速器的面積與功耗
表4 中給出了ResNet18 和ResNet50 的FP、BP階段融合執(zhí)行比單獨(dú)執(zhí)行性能提升的百分比,FP 平均提升了67.7%,BP 平均提升了77.6%。
本文針對(duì)卷積神經(jīng)網(wǎng)絡(luò)模型中的批歸一化層、加法層、非線性激活層等訪存密集型層以及其常見(jiàn)連接,提出了在訓(xùn)練的前向過(guò)程與反向過(guò)程中,將訪存密集型層與其前后的計(jì)算密集型層融合為一個(gè)新層執(zhí)行的方式,將訪存密集型層的操作作為對(duì)融合新層中輸入數(shù)據(jù)的前處理或者原始輸出數(shù)據(jù)的后處理進(jìn)行,大幅減少了訪存密集型層執(zhí)行過(guò)程中與片外內(nèi)存交互的數(shù)據(jù)量以及訓(xùn)練過(guò)程中需存儲(chǔ)在片外內(nèi)存中的數(shù)據(jù)量;并針對(duì)該融合執(zhí)行方案,設(shè)計(jì)實(shí)現(xiàn)了一個(gè)專門的面向訓(xùn)練的加速器,采用了暫存前處理結(jié)果、后處理操作與計(jì)算密集層的運(yùn)算操作并行執(zhí)行的優(yōu)化策略,大幅提升了融合新層在訓(xùn)練各階段執(zhí)行的性能。實(shí)驗(yàn)結(jié)果顯示,基于2 個(gè)常見(jiàn)的卷積神經(jīng)網(wǎng)絡(luò)模型,在面積僅增加6.4%、功耗增加10.3%的開(kāi)銷下,訓(xùn)練FP 和BP 階段的性能分別實(shí)現(xiàn)了67.7%和77.6%的提升。