孫雁飛,王子牛,孫 瑩,亓 晉,董振江
(1.南京郵電大學物聯(lián)網(wǎng)學院,江蘇 南京 210003 2.南京郵電大學江蘇省高性能計算與智能處理工程研究中心,江蘇 南京 210023 3.南京郵電大學自動化學院、人工智能學院,江蘇 南京 210023 4.南京郵電大學計算機學院,江蘇 南京 210023)
卷積神經(jīng)網(wǎng)絡(luò)因其出色的特征提取能力,在圖像分類、目標檢測、語義分割等計算機視覺領(lǐng)域被廣泛應(yīng)用并發(fā)揮重要作用[1-2]。但卷積神經(jīng)網(wǎng)絡(luò)需要較高的計算復(fù)雜度和內(nèi)存消耗量,通常部署在云端進行計算,這在一定程度上限制了卷積神經(jīng)網(wǎng)絡(luò)的應(yīng)用場景。近年來,隨著物聯(lián)網(wǎng)設(shè)備的普及,這些設(shè)備產(chǎn)生的數(shù)據(jù)量呈現(xiàn)爆炸式增長,給云端計算帶來極大的負擔。此外,將數(shù)據(jù)傳輸?shù)皆贫诉M行計算也帶來了數(shù)據(jù)安全和響應(yīng)延遲等問題[3]。為解決上述問題,邊緣智能的概念被提出[4-5],并廣泛應(yīng)用于預(yù)測性維護、精準農(nóng)業(yè)、智慧城市等領(lǐng)域。
邊緣智能是邊緣計算和人工智能的結(jié)合,當前研究主要集中在輕量級推理框架[6-7]和輕量級卷積神經(jīng)網(wǎng)絡(luò)[8-9]。輕量級推理框架提供了在邊緣設(shè)備上部署卷積神經(jīng)網(wǎng)絡(luò)的方法;輕量級卷積神經(jīng)網(wǎng)絡(luò)研究高效的網(wǎng)絡(luò)結(jié)構(gòu),降低卷積神經(jīng)網(wǎng)絡(luò)對算力和內(nèi)存的需求。因此,如何將卷積神經(jīng)網(wǎng)絡(luò)部署在邊緣設(shè)備并進行低復(fù)雜度、小內(nèi)存計算,已成為當前的研究熱點。
盡管在邊緣設(shè)備上部署卷積神經(jīng)網(wǎng)絡(luò)成為可能,但對于內(nèi)存受限的邊緣設(shè)備如微控制器、數(shù)字信號處理器等,其內(nèi)存小于1 MB,難以滿足卷積神經(jīng)網(wǎng)絡(luò)的內(nèi)存需求。此外,內(nèi)存使用量的增加使得SRAM(Static Random Access Memory)的漏電流增大[10],從而縮減這些設(shè)備的續(xù)航時間。因此,有必要進一步研究降低卷積神經(jīng)網(wǎng)絡(luò)內(nèi)存使用量的方法,從而滿足邊緣設(shè)備的內(nèi)存約束條件。因此,本文旨在面向內(nèi)存受限設(shè)備,提出一種低內(nèi)存使用的新型卷積計算方法。
目前降低卷神經(jīng)網(wǎng)絡(luò)內(nèi)存使用量的研究主要集中在輕量化卷積神經(jīng)網(wǎng)絡(luò),通過設(shè)計特殊的卷積操作,降低網(wǎng)絡(luò)結(jié)構(gòu)的冗余,從而減少內(nèi)存使用量。然而,卷積神經(jīng)網(wǎng)絡(luò)的內(nèi)存使用量不僅取決于卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),還取決于組成卷積神經(jīng)網(wǎng)絡(luò)的卷積算子計算方法,不同計算方法的內(nèi)存使用量可以相差數(shù)十倍,有必要研究降低卷積計算內(nèi)存使用量的方法?,F(xiàn)有卷積計算方法主要包括直接卷積、im2col+GEMM卷積和快速卷積等。
(1)直接卷積:直接卷積根據(jù)卷積算子的定義,通過幾層嵌套循環(huán)進行計算,輸入矩陣中灰色窗口與卷積核的內(nèi)積即為輸出的一個元素,存放在輸出矩陣中對應(yīng)位置。灰色窗口按照步長參數(shù)從左到右,從上到下滑動,依次計算出對應(yīng)位置輸出結(jié)果,直至計算完全部數(shù)據(jù)。使用該方法卷積時只需分配存放輸出矩陣的內(nèi)存空間,如圖1所示。
圖1 直接卷積
文獻[11]通過動態(tài)編譯方法在x86架構(gòu)上優(yōu)化直接卷積實現(xiàn),其性能接近于理論水平;文獻[12]通過優(yōu)化直接卷積數(shù)據(jù)布局和計算中的循環(huán)順序,提高了直接卷積的計算速度。然而上述方法并未降低直接卷積的內(nèi)存使用,難以部署在內(nèi)存受限設(shè)備上。
(2) im2col+GEMM 卷積:該方法依靠 im2col轉(zhuǎn)換將卷積問題轉(zhuǎn)化為通用矩陣乘(General Matrix Multiplication,GEMM)問題,利用高度優(yōu)化后的線性代數(shù)庫,如 MKL、BLAS等加速卷積計算[13]。 該方法被主流深度學習框架如 TensorFlow、Pytorch、Caffe、MxNet等深度學習框架采用作為卷積計算方法[14]。im2col將輸入矩陣中滑動窗口內(nèi)元素展開成行向量,將卷積核展開成列向量,分別存儲在兩個中間矩陣中,這兩個矩陣的乘積即為輸出矩陣,如圖2所示。
圖2 im2col+GEMM 卷積
基于im2col+GEMM的方法需要構(gòu)造中間矩陣,空間復(fù)雜度為 O(K2CHW), 其中 K、C、H、W 分別表示卷積核高或?qū)挕⑤斎胪ǖ罃?shù)、輸出矩陣高、輸出矩陣寬。對于3×3的卷積核,輸入矩陣中每個元素被復(fù)制9次,即輸入矩陣轉(zhuǎn)換后的中間矩陣內(nèi)存使用量變?yōu)樵瓉淼?倍。基于im2col+GEMM改進的方法,如 MEC[15]、MFCA[16]等,有效降低了中間矩陣的大小,但相較于直接卷積方法仍需要大量額外內(nèi)存空間存放中間矩陣,因此并不適用于內(nèi)存受限設(shè)備。
(3)快速卷積:該類方法主要包括FFT(Fast Fourier Transform)[17]和 Winograd[18]方法,通過將輸入矩陣和卷積核映射到對應(yīng)的FFT空間和Winograd空間,將計算轉(zhuǎn)換成對應(yīng)元素相乘,再將結(jié)果逆線性變換映射到原空間即可得到卷積計算輸出,從而減少卷積計算的計算量。然而此類算法由于其內(nèi)在的數(shù)值不穩(wěn)定性,導(dǎo)致計算精度降低,僅適用于單位步長和卷積核小的情況。文獻[19]在Winograd的基礎(chǔ)上提出使用超線性多項式來構(gòu)造變換矩陣,提高了計算精度,緩解數(shù)值不穩(wěn)定性。但基于快速卷積的方法仍需額外的內(nèi)存存儲輸入矩陣和卷積核映射后的空間以及映射空間中的計算結(jié)果,同樣也不適用于內(nèi)存受限設(shè)備。
此外,內(nèi)存共享優(yōu)化[20]被廣泛用于TensorFlow、Caffe、MXNet等深度學習框架,通過記錄卷積神經(jīng)網(wǎng)絡(luò)中間各層輸出的大小和生命周期,回收不再使用中間結(jié)果的內(nèi)存并用于其他層,從而降低卷積神經(jīng)網(wǎng)絡(luò)的內(nèi)存消耗。內(nèi)存共享優(yōu)化屬于卷積神經(jīng)網(wǎng)絡(luò)中不同層之間的內(nèi)存優(yōu)化,并不涉及層內(nèi)卷積計算方法,本文方法屬于層內(nèi)的卷積計算優(yōu)化方法,兩者可以組合使用,進一步優(yōu)化內(nèi)存使用量,即混合內(nèi)存重用策略[21]。
綜上所述,現(xiàn)有卷積計算方法主要對卷積計算速度進行優(yōu)化,通過使用額外的內(nèi)存空間加速卷積計算過程,在此基礎(chǔ)上研究降低卷積內(nèi)存使用量方法,但對于內(nèi)存資源受限的設(shè)備來說,無法承擔這種優(yōu)化帶來的額外內(nèi)存開銷。針對上述問題,本文提出一種面向內(nèi)存資源受限設(shè)備的新型卷積計算方法,與直接卷積方法類似,根據(jù)卷積算子的定義直接進行卷積計算,但計算結(jié)果存放在輸入矩陣中,從而降低卷積計算的內(nèi)存使用量;與im2col+GEMM卷積和快速卷積不同,本文方法無需構(gòu)造中間矩陣,避免大量的內(nèi)存使用,大幅降低卷積計算的內(nèi)存使用量。
針對現(xiàn)有卷積計算方法內(nèi)存使用量大,難以滿足內(nèi)存受限設(shè)備的內(nèi)存約束的問題,本節(jié)將對卷積計算內(nèi)存使用量進行優(yōu)化,闡述通過復(fù)用輸入矩陣內(nèi)存從而降低卷積計算內(nèi)存使用量的一種新型卷積計算方法,并給出以下幾種卷積計算的具體實現(xiàn)方法。
該算法對輸入矩陣滑動窗口對應(yīng)的部分與卷積核直接進行卷積計算,并將計算結(jié)果存放在輸入矩陣中,避免或減少分配輸出矩陣的內(nèi)存。如圖3所示,對于輸入矩陣 I(ih×iw×ic=6×6×1),卷積核K(kh×kw×kc=3×3×1),其中下標h、w、c分別表示對應(yīng)矩陣高、寬和通道數(shù)?;瑒哟翱诿看位瑒佑嬎阃杲Y(jié)果后,窗口左上角元素在后續(xù)計算中不會被再次使用,因此可將計算結(jié)果存放在對應(yīng)窗口左上角位置,繼續(xù)滑動窗口直至計算完全部輸入矩陣,此時輸入矩陣中存放著輸出矩陣的內(nèi)容,輸入矩陣變?yōu)檩敵鼍仃?,不再需要額外分配內(nèi)存存放輸出矩陣,從而降低卷積過程內(nèi)存使用量。圖中淺灰色部分表示尚未計算的區(qū)域,深灰色部分表示正在計算的區(qū)域。
圖3 標準卷積計算方法示意圖
在更一般的情況中,若輸入矩陣為I(ih×iw×ic),卷積核為 K(kh×kw× kc),輸出矩陣為O(oh×ow×oc)時,可以分為兩種情況討論,用Mem表示矩陣占用內(nèi)存的大小。
情況一:MemI≥MemO,此時輸入矩陣可以存放全部計算結(jié)果,按照如下步驟計算卷積。
①分配內(nèi)存空間m,大小為h×ow×oc(h≥「kh/2?), 臨時緩存計算結(jié)果,如圖 4(a)所示。
②計算輸入矩陣中深灰色行卷積,計算結(jié)果存放在m中,如圖4(b)所示,其中第一行虛線框表示卷積計算時padding填充部分示意,并不占據(jù)實際內(nèi)存空間。
③此時輸入矩陣中白色行的輸入數(shù)據(jù)將不再被后續(xù)計算使用,將m中對應(yīng)行的計算結(jié)果復(fù)制到該位置,如圖4(c)所示,白色部分存放卷積計算結(jié)果。值得注意的是,當kh>1時,輸入矩陣中深灰色行除了參與以當前行為卷積中心的計算外,還會參與以相鄰行為卷積中心的計算,因此不能直接將深灰色行的輸入數(shù)據(jù)直接覆蓋。
④ 重復(fù)步驟②、③,如圖 4(d)、(e)所示,直至計算完全部輸入矩陣。
⑤計算結(jié)束后的結(jié)果如圖4(f)所示,此時卷積后的結(jié)果全部存放在輸入矩陣中,卷積計算完成。
圖4 標準卷積計算方法情況一
情況二:MemI<MemO,此時輸出矩陣通道數(shù)大于輸入矩陣通道數(shù),按照如下步驟計算卷積。
①分配內(nèi)存空間m,大小為h×ow×oc(h≥「kh/2?);分配內(nèi)存空間 M, 大小為 Memo- Memi,如圖 5(a)所示。
②計算輸出矩陣比輸入矩陣多出來的通道,采用直接計算方法,計算結(jié)果存放在M中,如圖5(b)所示。
③忽略多出來的通道數(shù)和內(nèi)存空間M,按照情況一的步驟②~④執(zhí)行,如圖5(c)所示。
圖5 標準卷積計算方法情況二
④最終計算結(jié)果結(jié)果如圖5(d)所示,此時計算結(jié)果分為兩部分存放,分別存放在輸入矩陣和內(nèi)存空間M中,卷積計算完成。
標準卷積計算方法的算法描述如算法1所示。
算法1 標準卷積計算方法
深度卷積的輸入通道個數(shù)與卷積核數(shù)量一致,輸入通道與對應(yīng)卷積核進行卷積計算,結(jié)算結(jié)果為對應(yīng)輸出通道,輸入輸出通道數(shù)保持一致。深度卷積計算可以看作多個通道數(shù)為1的標準卷積組合,因此可以使用上述提出標準卷積的計算方法。使用深度卷積時,輸入通道數(shù)和輸出通道數(shù)相等,輸出尺寸小于或等于輸入尺寸,因此并不需要分配內(nèi)存空間M,即深度可分離卷積只需要采用標準卷積中情況一的計算方法。
點卷積可以看作標準卷積中kh=kw=1的情況。點卷積計算不涉及輸入矩陣中相鄰行列的值,輸入矩陣中對應(yīng)位置數(shù)據(jù)計算完后將不再使用,因此,與標準卷積計算不同,點卷積計算時不再額外分配內(nèi)存空間m,計算結(jié)果可以直接存放在輸入矩陣中。點卷積計算方法如圖6所示,左上角元素與點卷積核計算后計算結(jié)果直接存放在原來位置,圖中輸入輸出矩陣為同一塊內(nèi)存空間。當MemI<MemO時,仍需分配內(nèi)存空間M存放額外的輸出結(jié)果,其大小為MemO-MemI。
圖6 點卷積計算方法
除了上述幾種卷積計算外,本節(jié)還提出一種降低卷積神經(jīng)網(wǎng)絡(luò)中池化計算內(nèi)存使用量的方法。池化計算方法如圖7所示,上層矩陣為輸入矩陣,下層為輸出矩陣,池化窗口每次移動計算后的結(jié)果依次存放在輸出矩陣對應(yīng)的位置上。由于計算后輸入空間中對應(yīng)區(qū)域的數(shù)據(jù)將不再被后續(xù)計算用到,即圖中白色區(qū)域,因此該區(qū)域可以依次存放其他位置的池化結(jié)果。值得注意的是,圖7中上下兩層矩陣實際上是同一塊內(nèi)存不同時間上的狀態(tài),在池化計算前代表輸入矩陣,池化計算結(jié)束后代表輸出矩陣,因此本節(jié)提出的池化計算方法不需要使用額外內(nèi)存。
圖7 池化計算方法
實驗測試硬件平臺選擇在內(nèi)存資源受限的微控制器上進行,選用微控制器的型號為STM32H750VBT6,主頻為480 MHz,測試內(nèi)存包括DTCM(Data Tightly?Coupled Memory)和 SRAM,其中DTCM大小為128 KB,與微控制器內(nèi)部CPU直接連接,訪存速度較快;SRAM大小為512 KB,通過AXI總線與CPU相連,訪存速度較慢。使用C++實現(xiàn)本文提出的卷積計算方法和其他對比方法。由于這類內(nèi)存資源受限的設(shè)備通常沒有GPU等硬件加速功能,因此基于im2col+GEMM方法的卷積實驗和其他方法均使用微控制器內(nèi)部CPU進行計算。
實驗測試數(shù)據(jù)包括單個卷積測試和LeNet[22]卷積神經(jīng)網(wǎng)絡(luò)測試,并分別對其進行內(nèi)存使用量分析和性能分析,驗證本文提出方法的有效性。其中單個卷積測試集參考文獻[15]給出的測試集,測試集信息如表1所示。
表1 測試集信息
單個卷積測試集內(nèi)存開銷大小如表2所示,其中內(nèi)存使用量包括卷積計算過程中的額外內(nèi)存使用量和輸出矩陣的內(nèi)存使用量,不包含輸入矩陣和卷積核的內(nèi)存使用量, Mim2col、MMEC、Mdirectconv和 Mours分別表示im2col+GEMM、MEC、直接卷積和本文方法內(nèi)存使用量大小,單位為字,即單個float32類型參數(shù)的內(nèi)存大小。
表2 單個卷積計算內(nèi)存使用量對比
從表2中可以看出,本文方法明顯優(yōu)于其他幾種卷積方法,大幅減少卷積的內(nèi)存使用量。對比其他幾種方法,本文方法的平均內(nèi)存使用量分別下降89.29%、82.60%和57.15%,減少的內(nèi)存使用量主要是由于對輸入矩陣內(nèi)存空間進行復(fù)用,減少分配輸出矩陣內(nèi)存大小,同時避免構(gòu)造中間矩陣使用量額外內(nèi)存。以CV1為例,im2col+GEMM方法需要構(gòu)造輸入矩陣的中間矩陣,內(nèi)存大小為14 400字(oh×ow×kh×kw×ic),分配輸出矩陣,內(nèi)存大小為3 200字(oh×ow×oc),共計需要17 600字內(nèi)存;MEC方法需要構(gòu)造輸入矩陣的中間矩陣,內(nèi)存大小為6 720字(oh×kh×ih×ic),分配輸出矩陣,內(nèi)存大小為3 200字,共計需要9 920字內(nèi)存;直接卷積方法無需構(gòu)造中間矩陣,僅需分配3 200字的輸出矩陣;本文方法需要分配內(nèi)存空間m,大小為1 280字 (「kh/2?× ow× oc), 此外還需要分配內(nèi)存空間M,大小為64字(Memo-Memi),共計需要1 344字內(nèi)存。為方便對比,對表2中幾種方法內(nèi)存的使用量用直方圖進行比較,以im2col+GEMM算法的內(nèi)存使用量為基準,結(jié)果如圖8所示。
圖8 幾種卷積方法的內(nèi)存開銷柱狀圖比較
除了對單個卷積進行實驗分析外,本文還對經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)LeNet進行實驗分析。LeNet是一種用于數(shù)字識別的卷積神經(jīng)網(wǎng)絡(luò),其結(jié)構(gòu)簡單,內(nèi)存消耗少,方便對不同的卷積計算方法進行分析。表3為LeNet卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)參數(shù)及不同卷積計算方法的內(nèi)存開銷對比。
由于卷積神經(jīng)網(wǎng)絡(luò)輸入為圖片,第一層卷積無法使用輸入空間存放卷積結(jié)果,因此本文方法在第一次卷積時和直接卷積采用相同的方式,分配輸出矩陣,大小為4 704字(oh×ow×oc)。 池化計算采用本文提出的池化計算方法,因此該網(wǎng)絡(luò)中池化計算并不使用額外的內(nèi)存。從表3中可以看出對于LeNet卷積神經(jīng)網(wǎng)絡(luò),本文方法相較于上述其他幾種方法內(nèi)存使用量分別下降89.90%、82.21%和28.07%。
表3 LeNet卷積神經(jīng)網(wǎng)絡(luò)的內(nèi)存使用量對比
為驗證本文算法對卷積計算性能的影響,本文還對不同卷積計算方法的計算時間實驗測試,分別對單個卷積和LeNet卷積神經(jīng)網(wǎng)絡(luò)進行實驗測試,測試結(jié)果分別如表4和表5所示。
表4 單個卷積計算時間對比 ms
表5 LeNet卷積神經(jīng)網(wǎng)絡(luò)的計算時間對比 ms
表4 中 Tim2col、TMEC、Tdirectconv和 Tours分別表示im2col+GEMM、MEC、直接卷積和本文方法計算單個卷積的時間,時間單位為ms。對于測試集中單個卷積計算內(nèi)存使用量小于128 KB的卷積,放在訪存速度較快的DTCM內(nèi)存中計算,即表4中標記“?”的位置;若卷積計算內(nèi)存使用量大于512 KB則無法在該設(shè)備上進行計算,在表4中以“—”表示;其他情況下均在SRAM內(nèi)存中進行卷積計算。雖然本文方法需要額外復(fù)制數(shù)據(jù)造成時間上的開銷,但得益于使用較少的內(nèi)存提高緩存命中率和更有效利用DTCM內(nèi)存,從表4中可以看出,實際平均計算時間相較于其他方法較快。此外其他卷積計算方法內(nèi)存使用量部分超出實驗設(shè)備限制,無法完成全部計算,而本文方法能夠完成測試集中全部卷積計算,為內(nèi)存資源受限設(shè)備運行卷積神經(jīng)網(wǎng)絡(luò)提供更多的選擇。
表5為LeNet卷積神經(jīng)網(wǎng)絡(luò)各層計算時間的對比,單位為ms,其中直接卷積和本文方法內(nèi)存使用量均小于128 KB,在DTCM中進行計算,用“?”表示;im2col+GEMM和MEC方法內(nèi)存使用量均大于128 KB,在SRAM中進行計算。
綜合表3和表5可以看出,雖然使用本文方法計算LeNet卷積神經(jīng)網(wǎng)絡(luò)相較于im2col+GEMM和MEC方法計算時間分別增加2.31%和19.93%,但內(nèi)存使用量分別下降89.90%和82.21%;而對于直接卷積方法,本文方法計算時間在下降6.98%的同時,內(nèi)存使用量下降28.07%,優(yōu)勢較為明顯。此外本文提出的池化層計算方法較標準池化計算方法計算時間增加14.29%,但總計僅增加0.04 ms的計算時間,對整個卷積神經(jīng)網(wǎng)絡(luò)而言可以忽略不計。綜合以上實驗分析,驗證了本文提出的卷積計算方法的有效性,大幅降低了卷積計算的內(nèi)存使用量。
本文提出一種面向內(nèi)存資源受限設(shè)備的卷積計算方法,卷積計算時對輸入矩陣計算后不再使用的內(nèi)存空間進行復(fù)用存放計算結(jié)果,從而減少分配新的內(nèi)存空間存放計算結(jié)果,達到降低內(nèi)存使用量的目的,使得在內(nèi)存資源受限的設(shè)備上可以運行更復(fù)雜的卷積神經(jīng)網(wǎng)絡(luò)。實驗結(jié)果驗證了本文方法的有效性,相較于其他方法均大幅降低內(nèi)存使用量,對于一些內(nèi)存使用量較大的卷積,其他方法均無法在有限的內(nèi)存中完成卷積計算,而本文方法可以完成卷積計算,對于卷積神經(jīng)網(wǎng)絡(luò)在內(nèi)存受限設(shè)備上的部署運行具有重要意義。