1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        矩陣乘法的優(yōu)化及其在卷積中的應用

        共 10318字,需瀏覽 21分鐘

         ·

        2021-04-12 10:22

        點擊上方小白學視覺”,選擇加"星標"或“置頂

        重磅干貨,第一時間送達

        本文轉(zhuǎn)自 | 視覺算法



        引言

        氣象預報、石油勘探、核子物理等現(xiàn)代科學技術大多依賴計算機的計算模擬,模擬計算的核心是表示狀態(tài)轉(zhuǎn)移的矩陣計算。另一方面,計算機圖形處理以及近年來興起的深度學習也和矩陣乘高度相關。而矩陣乘對計算資源消耗較大,除了計算機體系結(jié)構(gòu)的不斷更新外,軟件優(yōu)化方面也有大量的研究工作。

        本文簡要介紹通用矩陣乘(GEMM,General Matrix Multiplication)優(yōu)化的基本概念和方法、QNNPACK 對特定場景的矩陣乘的優(yōu)化方法、以及用 GEMM 優(yōu)化神經(jīng)網(wǎng)絡中卷積計算的一點方向。

        旨在幫助大家在概念中建立一些直覺,無甚高論。

        通用矩陣乘優(yōu)化


        基本概念


        通用矩陣乘(下文簡稱 GEMM)的一般形式是 ??=????C=AB, 其中 ??A 和 ??B 涵蓋了各自轉(zhuǎn)置的含義。圖一是矩陣乘計算中為計算一個輸出點所要使用的輸入數(shù)據(jù)。三個矩陣的形狀也如圖所示。

        圖一:矩陣乘一個輸出元素的計算

        該計算的偽代碼如下。該計算操作總數(shù)為  (其中 ??、??、?? 分別指代三層循環(huán)執(zhí)行的次數(shù),2 指代循環(huán)最內(nèi)層的一次乘法和加法) ,內(nèi)存訪問操作總數(shù)為 4??????(其中 4 指代對 ??、??、?? 三者的內(nèi)存訪問,?? 需要先讀取內(nèi)存、累加完畢在存儲,且忽略對 ?? 初始化時的操作)。GEMM 的優(yōu)化均以此為基點。

        for (int m = 0; m < M; m++) { for (int n = 0; n < N; n++) { C[m][n] = 0; for (int k = 0; k < K; k++) { C[m][n] += A[m][k] * B[k][n]; } }}
        How to optimize gemm (https://github.com/flame/how-to-optimize-gemm/wiki)介紹了如何采用各種優(yōu)化方法,將最基礎的計算改進了約七倍(如圖二)。其基本方法是將輸出劃分為若干個 4×4子塊,以提高對輸入數(shù)據(jù)的重用。同時大量使用寄存器,減少訪存;向量化訪存和計算;消除指針計算;重新組織內(nèi)存以地址連續(xù)等。詳細的可以參考原文。

        圖二:How to optimize gemm 的優(yōu)化效果


        計算拆分展示


        本節(jié)主要以圖形化的方式介紹計算拆分。

        圖三 將輸出的計算拆分為 1×4 的小塊,即將 ?? 維度拆分為兩部分。計算該塊輸出時,需要使用 ?? 矩陣的 1 行,和 ?? 矩陣的 4 列。

        圖三:矩陣乘計算 1×4輸出

        下面是該計算的偽代碼表示,這里已經(jīng)將 1×4 中 ??N 維度的內(nèi)部拆分進行了展開。這里的計算操作數(shù)仍然是 2?????? ,這一點在本文中不會有變化。這里的內(nèi)存訪問操作數(shù)尚未出現(xiàn)變化,仍然是 4??????,但接下來會逐步改進。

        for (int m = 0; m < M; m++) { for (int n = 0; n < N; n += 4) { C[m][n + 0] = 0; C[m][n + 1] = 0; C[m][n + 2] = 0; C[m][n + 3] = 0; for (int k = 0; k < K; k++) { C[m][n + 0] += A[m][k] * B[k][n + 0]; C[m][n + 1] += A[m][k] * B[k][n + 1]; C[m][n + 2] += A[m][k] * B[k][n + 2]; C[m][n + 3] += A[m][k] * B[k][n + 3]; } }}

        簡單的觀察即可發(fā)現(xiàn),上述偽代碼的最內(nèi)側(cè)計算使用的矩陣 ?? 的元素是一致的。因此可以將 ??[??][??] 讀取到寄存器中,從而實現(xiàn) 4 次數(shù)據(jù)復用(這里不再給出示例)。一般將最內(nèi)側(cè)循環(huán)稱作計算核(micro kernel)。進行這樣的優(yōu)化后,內(nèi)存訪問操作數(shù)量變?yōu)?(2+1/4)??????,其中 1/4是對 ?? 優(yōu)化的效果。

        類似地,我們可以繼續(xù)拆分輸出的 ?? 維度,從而在內(nèi)側(cè)循環(huán)中計算 4×4 輸出,如圖四。

        圖四:矩陣乘計算 4×44×4輸出

        同樣地,將計算核心展開,可以得到下面的偽代碼。這里我們將 1×4 中展示過的 ?? 維度的計算簡化表示。這種拆分可看成是 4×1×4,這樣 ?? 和 ?? 的訪存均可復用四次。由于乘數(shù)效應,4×4 的拆分可以將對輸入數(shù)據(jù)的訪存縮減到 2??????+1/4??????+1/4??????=(2+1/2)??????)。這相對于最開始的 4?????? 已經(jīng)得到了 1.6X 的改進,這些改進都是通過展開循環(huán)后利用寄存器存儲數(shù)據(jù)減少訪存得到的。

        for (int m = 0; m < M; m += 4) { for (int n = 0; n < N; n += 4) { C[m + 0][n + 0..3] = 0; C[m + 1][n + 0..3] = 0; C[m + 2][n + 0..3] = 0; C[m + 3][n + 0..3] = 0; for (int k = 0; k < K; k++) { C[m + 0][n + 0..3] += A[m + 0][k] * B[k][n + 0..3]; C[m + 1][n + 0..3] += A[m + 1][k] * B[k][n + 0..3]; C[m + 2][n + 0..3] += A[m + 2][k] * B[k][n + 0..3]; C[m + 3][n + 0..3] += A[m + 3][k] * B[k][n + 0..3]; } }}

        到目前為止,我們都是在輸出的兩個維度上展開,而整個計算還包含一個削減(Reduction)維度 ??。圖五展示了在計算 4×4 輸出時,將維度 ?? 拆分,從而每次最內(nèi)側(cè)循環(huán)計算出輸出矩陣 ?? 的 4x4 部分和。

        圖五:矩陣乘計算 4×4 輸出對 ?? 維度的拆分

        下面展示的是這部分計算的展開偽代碼,其中維度 ?? 和 ?? 已經(jīng)被簡寫。在這里,最內(nèi)側(cè)循環(huán)發(fā)生的計算次數(shù)已經(jīng)從最樸素版本的  發(fā)展到了  。

        for (int m = 0; m < M; m += 4) { for (int n = 0; n < N; n += 4) { C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; for (int k = 0; k < K; k += 4) { C[m + 0..3][n + 0..3] += A[m + 0..3][k + 0] * B[k + 0][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 1] * B[k + 1][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 2] * B[k + 2][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 3] * B[k + 3][n + 0..3]; } }}

        在對 ?? 和 ?? 展開時,我們可以分別復用 ?? 和 ?? 的數(shù)據(jù);在對 ?? 展開時,我們可以將部分和累加在寄存器中,最內(nèi)層循環(huán)一次迭代結(jié)束時一次寫到 ?? 的內(nèi)存中。那么內(nèi)存訪問次數(shù)為
          
        ——相對原始實現(xiàn)的 4X 改進!

        到目前為止,我們已經(jīng)對計算進行了三次維度拆分、展開并寄存器化(代碼未展示)和訪存復用。這對最基礎版本計算似乎已經(jīng)足夠了,因為數(shù)百條穩(wěn)定的順序計算指令對處理器流水線已經(jīng)很友好,且編譯器可以幫助我們做好軟件流水這樣的指令調(diào)度。

        然而一條計算指令只能完成一次乘加操作(MLA)效率還是比較低。實際上即使是最低端的移動手機處理器都會帶有 SIMD 支持,訪存和計算都可以向量化。因此我們可以再進一步,利用向量操作提高計算的性能。

        在介紹向量化計算的細節(jié)時,偽代碼是很難理解的,下面依據(jù)圖六介紹量化計算的具體過程。圖六左側(cè)部分的三幅小圖分別展示了兩個 4×4 矩陣相乘向量化的要素:首先是計算一個輸出元素使用到的輸入元素;然后是對各個矩陣內(nèi)存的編碼,均以行優(yōu)先的形式編號;最后是向量化的具體計算方法。
        圖六:兩個 4×4矩陣相乘的向量化

        這里的向量編號方式假定輸入輸出的內(nèi)存布局都是行優(yōu)先,那么兩個輸入各自的 16 個元素通過 4 次向量訪存即可加載到寄存器中。由矩陣乘的規(guī)則可知,輸入 ?? 中的行可一次性用作輸出的計算,而輸入 ??B 的行則要拆分使用。這也是向量計算最容易出錯的地方。

        圖六右側(cè)列出了三份偽代碼。第一份 C0 in detail 是計算 C0 中四個輸出元素的樸素方法的展開,連續(xù)四次計算得到一個 C0 元素的結(jié)果。將計算過程稍作重排,即可得到 C0 scheduled 展示的計算,這里連續(xù)的四次計算分別處理了 C0 中四個元素的 1/4 結(jié)果。在連續(xù)的四次計算中,重排前只有對 ??A 的訪存是連續(xù)的,重排后 ?? 和 ?? 的訪存都是連續(xù)的。那么向量化這些訪存和計算,即可得到第三列偽代碼中紅色 C0 部分。而第三列是通過對 C1、C2、C3 進行類似的處理得到的。

        施行向量化操作后,原本需要 64 條計算指令的計算過程所需指令減少到 16 條,訪存也有類似效果。而向量化對處理器資源的高效使用,又帶來了進一步優(yōu)化空間,例如可以一次計算 8×8 個局部輸出。

        處理內(nèi)存布局


        上一小節(jié)列出的是在輸入輸出原有內(nèi)存布局上所做的優(yōu)化。在最后向量化時,每次內(nèi)存訪問都是四個元素。當這些元素為單精度浮點數(shù)時,內(nèi)存大小為 16 字節(jié),這遠小于現(xiàn)代處理器高速緩存行大?。–ache line size)——后者一般為 64 字節(jié)。在這種情況下,內(nèi)存布局對計算性能的影響開始顯現(xiàn)。

        圖七展示的是不同內(nèi)存組織方式對的影響。圖中兩者都是行優(yōu)先的內(nèi)存排列,區(qū)別在于左側(cè)小方塊內(nèi)部是不連續(xù)的,右側(cè)小方塊內(nèi)部是連續(xù)的。圖中用幾個數(shù)字標記了各個元素在整個內(nèi)存中的編號。

        圖七:不同的局部內(nèi)存組織

        想象一下在這兩者不同內(nèi)存組織方式的輸入輸出中訪存,每次向量化內(nèi)存加載仍是 4 個元素。對于一個局部計算使用到的小方塊,左側(cè)四次訪存的內(nèi)存都是不連續(xù)的,而右側(cè)則是連續(xù)的。當數(shù)據(jù)規(guī)模稍大(一般情況肯定足夠大了),左側(cè)的連續(xù)四次向量化內(nèi)存加載都會發(fā)生高速緩存缺失(cache miss),而右側(cè)只會有一次缺失。

        在常規(guī)的數(shù)據(jù)規(guī)模中,由于左側(cè)會發(fā)生太多的高速緩存缺失,又由于矩陣乘這樣的計算對數(shù)據(jù)的訪問具有很高的重復性,將它重排成右側(cè)的內(nèi)存布局減少高速緩存缺失,可顯著地改進性能。另一方面,矩陣乘中兩個輸入矩陣往往有一個是固定的參數(shù),在多次計算中保持不變。那么可以在計算開始前將其組織成特定的形狀,這種優(yōu)化甚至可以將性能提高 2x。

        到這里為止,對 4×4 計算已經(jīng)有了足夠的優(yōu)化,可以開始考慮視野更廣一些的全局優(yōu)化。圖八是一個關于全局優(yōu)化的小示例。

        圖八:矩陣乘的全局優(yōu)化一瞥

        圖中字母標記的是全局性的工作順序,即輸出數(shù)據(jù)中外層循環(huán)迭代方式。左側(cè)小圖是常規(guī)的行優(yōu)先遍歷方式,中間小圖是列有限的遍歷方式。這兩者的區(qū)別是 ?? 和 ?? 兩個維度的循環(huán)哪個在最外層。

        上文已經(jīng)對 ?? 和 ?? 兩個維度分別進行了一次拆分,這里可以繼續(xù)這種拆分。右側(cè)的圖例中是將 ?? 和 ?? 兩個維度分別拆分為 ??,2,4 三部分,將外層拆分都交換到外層循環(huán)。下面是相應的偽代碼。

        for (int mo = 0; mo < M; mo += 8) { for (int no = 0; no < N; no += 8) { for (int mi = 0; mi < 2;mi ++) { for (int ni = 0; ni < 2; ni++) { int m = mo + mi * 4; int n = no + ni * 4; C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; C[m + 0..3][n + 0..3] = 0; for (int k = 0; k < K; k += 4) { C[m + 0..3][n + 0..3] += A[m + 0..3][k + 0] * B[k + 0][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 1] * B[k + 1][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 2] * B[k + 2][n + 0..3]; C[m + 0..3][n + 0..3] += A[m + 0..3][k + 3] * B[k + 3][n + 0..3]; } } } }}

        經(jīng)過這樣的調(diào)度,從整體計算來看,可看作是將 4×4 計算拓展成了 8×8 ,其實是同一種思路。

        QNNPACK 的矩陣乘優(yōu)化

        QNNPACK (Quantized Neural Network PACKage) 是 Facebook 開源的專門用于量化神經(jīng)網(wǎng)絡的計算加速庫。QNNPACK 和 NNPACK (Neural Network PACKage) 的作者都是 Marat Dukhan 。到目前為止,QNNPACK 仍然是已公開的,用于移動端(手機)的,性能最優(yōu)的量化神經(jīng)網(wǎng)絡加速庫。

        QNNPACK 開源時附帶了一份技術報告性質(zhì)的博客。本節(jié)將結(jié)合上節(jié)的內(nèi)容簡要地從博客原作中抽取一些關于 GEMM 的內(nèi)容。

        量化神經(jīng)網(wǎng)絡


        神經(jīng)網(wǎng)絡計算一般都是以單精度浮點(Floating-point 32, FP32)為基礎。而網(wǎng)絡算法的發(fā)展使得神經(jīng)網(wǎng)絡對計算和內(nèi)存的要求越來越大,以至于移動設備根本無法承受。為了提升計算速度,量化(Quantization)被引入到神經(jīng)網(wǎng)絡中,主流的方法是將神經(jīng)網(wǎng)絡算法中的權(quán)重參數(shù)和計算都從 FP32 轉(zhuǎn)換為 INT8 。


        兩種數(shù)值表示方法的方程如上。如果對量化技術的基本原理感興趣,可以參考 Neural Network Quantization Introduction 。

        應用量化技術后,計算方面顯現(xiàn)了若干個新的問題。首先是 NNPACK 這樣用于 FP32 的計算加速庫無法用于 INT8 ,這導致我們需要新的加速計算方法。再者是輸入輸出都轉(zhuǎn)化成 INT8 后,內(nèi)存帶寬需求直接下降為 1/4 。隨之而來的內(nèi)存容量需求變化出現(xiàn)了一些新的優(yōu)化機會。而 QNNPACK 充分利用了這些優(yōu)化方法,并結(jié)合神經(jīng)網(wǎng)絡領域的特點,大幅改進了計算性能。

        另一方面,Gemmlowp 是 Google 開源的低精度 GEMM 加速庫。在加速之外,Gemmlowp 的特別之處是提供了一套用定點計算模擬浮點計算的機制。例如  這樣浮點計算也可通過 Gemmlowp 在僅支持定點計算的處理器上運行。和 QNNPACK 不同,Gemmlowp 似乎目的在于支持 GEMM 而非單純神經(jīng)網(wǎng)絡,因此在神經(jīng)網(wǎng)絡方面的性能目前落后于 QNNPACK 。

        計算劃分與削減維度


        和上節(jié)所述類似,QNNPACK 的計算也是基于對輸出的劃分,拆分成如圖九的 ????×????MR×NR 小塊。這里需要注意的一點是,原文圖例中對 ??B 的標記有筆誤,??B 的列高度應該是 ??K 而非 ??N。(我們向 QNNPACK 報告了這一問題,目前尚未得到修正。)

        圖九:QNNPACK 矩陣乘劃分示例

        QNNPACK 實現(xiàn)了 4×8 的和 8×8 兩種計算核(micro kernel),分別用于支持 armv7 和 arm64 指令集的處理器。這兩種計算核在原理上區(qū)別不大,后者主要利用了更多的寄存器和雙發(fā)射(Dual Issue)以提高計算的并行度。

        拆分后 ????×???? 計算塊使用的內(nèi)存為 ?????????+???????+???????。由于常規(guī)的神經(jīng)網(wǎng)絡計算 ??<1024 , 那么這里的內(nèi)存消耗一般不超過 16KB,可以容納在一級高速緩存(L1 Cache)中。QNNPACK 的這一發(fā)現(xiàn)是其矩陣乘優(yōu)化的基礎。

        如圖十所示,在計算 ????×???? 小塊時,傳統(tǒng)的方法(即上一節(jié)的方法)是在 ?? 維度上拆分,在一次計算核的處理中,僅計算 ?? 維的局部。那么在每次計算核的處理中,都會發(fā)生對輸出的加載和存儲——要將本次計算產(chǎn)生的部分和累加到輸出中。

        圖十:QNNPACK 和傳統(tǒng)方法計算削減維度的對比

        而 QNNPACK 的做法是將整個 ??K 維全部在計算核中處理完,這樣就幾乎完全消除了輸出部分和的訪存。兩種的差異的細節(jié)可以參考圖十兩側(cè)的偽代碼。這里所說的「將整個 ?? 維全部」并不是指 ?? 維不拆分——在實際計算中 ??K 維還是會以 8 為基礎拆分——而是指拆分后不和其他維度交換(interchange)。

        內(nèi)存組織的特點


        上節(jié)中曾提到,對內(nèi)存的重新組織(Repacking)可以改進高速緩存命中率,從而提高性能。但是這種重新組織也是有開銷的。

        計算核中最小的計算單元處理的是兩個 4×4 矩陣相乘。傳統(tǒng)的方法由于 ?? 可能很大,需要對輸入內(nèi)存進行重新組織,防止相鄰的訪存引起高速緩存沖突,如圖十一。

        圖十一:QNNPACK 和傳統(tǒng)矩陣乘對局部計算的處理

        而在量化神經(jīng)網(wǎng)絡中,由于 ??K 比較小,計算核處理中使用到的內(nèi)存完全可以容納在一級高速緩存中,即使不重新組織內(nèi)存,高速緩存的重用率也足夠高。

        參考圖七左側(cè)部分,QNNPACK 計算核一次會使用 8 行輸入(假定圖中繪制以 8 為基礎分塊)。盡管對第一個 8×8 矩陣塊的向量化加載可能全部是高速緩存缺失(Cache miss),第二個 8×8 則全部命中——因為它們已經(jīng)作為同在一個高速緩存行的內(nèi)容隨第一個矩陣塊加載到了高速緩存中。其他矩陣塊也是類似情況。

        采用了這些基于神經(jīng)網(wǎng)絡領域先驗知識的優(yōu)化方法后,QNNPACK 擊敗了所有神經(jīng)網(wǎng)絡量化領域的用于移動端加速庫。不過,QNNPACK 的拆分著眼于削減維度,沒有在輸出維度上做全局調(diào)度。我們在 QNNPACK 基礎上實現(xiàn)了 ?? 和 ?? 維度的外層循環(huán)拆分調(diào)度,簡單的實驗獲得了相對于 QNNPACK 1.1x 的性能表現(xiàn)。

        卷積與矩陣乘

        卷積(Convolution)是神經(jīng)網(wǎng)絡的核心計算。而卷積的變種極為豐富,本身計算又比較復雜,因此其優(yōu)化算法也多種多樣,包括 im2col、Winograd 等等。本節(jié)重點關注卷積和矩陣乘的關系。

        im2col 計算方法


        作為早期的深度學習框架,Caffe 中卷積的實現(xiàn)采用的是基于 im2col 的方法,至今仍是卷積重要的優(yōu)化方法之一。

        im2col 是計算機視覺領域中將圖片的不同通道(channel)轉(zhuǎn)換成矩陣的列(column)的計算過程。Caffe 在計算卷積時,首先用 im2col 將輸入的三維數(shù)據(jù)轉(zhuǎn)換成二維矩陣,使得卷積計算可表示成兩個二維矩陣相乘,從而充分利用已經(jīng)優(yōu)化好的 GEMM 庫來為各個平臺加速卷積計算。

        圖十二是卷積的 im2col 過程的示例。隨著卷積過濾器在輸入上滑動,將被使用的那部分輸入展開成一行大小為 ????×????×???? 的向量。在滑動結(jié)束后,則得到特征矩陣 (??×??)×(????×????×????) 。將過濾器展開成 (????)×(????×????×????) 的矩陣,那么卷積即可表示成這兩個矩陣相乘的結(jié)果(特征矩陣要進行轉(zhuǎn)置操作)。

        圖十二:im2col 過程

        im2col 計算卷積使用 GEMM 的代價是額外的內(nèi)存開銷,輸入會使用額外的 ????×???? 倍內(nèi)存。當卷積核尺寸是 1×1 時,由于不需要重排輸入,GEMM 可以直接在原始輸入上運行,并且不需要使用額外的內(nèi)存。

        內(nèi)存布局與卷積性能


        神經(jīng)網(wǎng)絡中卷積的內(nèi)存布局主要有 NCHW 和 NHWC 兩種。最后重點分析一下 im2col 1×1 卷積性能和內(nèi)存布局的關系。

        對于不需要額外調(diào)整輸入的 1×1 卷積,將 NCHW 內(nèi)存布局的卷積對應到矩陣乘 ??=???? 時,?? 是卷積核(filter),?? 是輸入(input)。各個矩陣的維度如圖十二所示。
        圖十二:NCHW 內(nèi)存布局卷積轉(zhuǎn)換成的矩陣乘

        對該矩陣施行劃分后,將計算核的訪存局部性表現(xiàn)標記在圖十二中。其中 Inside 表示小塊矩陣乘內(nèi)部的局部性,Outside 表示在削減維度方向的局部性。

        對輸出而言,小塊內(nèi)向量化訪存局部性較差,外部表現(xiàn)取決于全局計算方向——行優(yōu)先則局部性較好,列優(yōu)先則較差。由于卷積核可以事先重排內(nèi)存,因此視其局部性都較好。輸入則小塊內(nèi)外都較差,因為削減維度是列優(yōu)先的,幾乎每次加載輸入都會發(fā)生高速緩存缺失。

        圖十三是與之相對的 NHWC 內(nèi)存布局的示例。值得注意的是,NHWC 和 NCHW 中 ??、?? 矩陣所代表的張量發(fā)生了調(diào)換——????????????=??????????×????????????。具體的拆分方式仍然一樣。

        圖十三:NHWC 內(nèi)存布局卷積轉(zhuǎn)換成的矩陣乘

        在 NHWC 中,輸出的局部性表現(xiàn)和 NCHW 一樣。同樣的,卷積核也視作局部性表現(xiàn)較好。對于輸入,小方塊的內(nèi)部局部性表現(xiàn)不是很好,因為幾次向量加載的地址不連續(xù);而外部局部性表現(xiàn)則較好,因為在削減維度滑動使用的內(nèi)存是連續(xù)的——這一點在「處理內(nèi)存布局」小節(jié)中已有闡述。

        可以看到,對于 1×1如果采用 im2col 方法計算,且不對輸入輸出進行額外的內(nèi)存重排,那么 NHWC 的訪存特征是顯著優(yōu)于 NCHW 的。

        總結(jié)
        至此,本文介紹了 GEMM 優(yōu)化的基本方法概念,在神經(jīng)網(wǎng)絡領域中 QNNPACK 基于量化對 GEMM 的優(yōu)化,和 im2col 方法對卷積計算及其內(nèi)存布局的意義。GEMM 優(yōu)化實質(zhì)上是個非常重要,且和特定領域綁定很強的話題,更進一步的內(nèi)容需要進入到特定領域深入研究。如果對 GEMM 各種優(yōu)化技巧所帶來的性能收益感興趣,可以參考 How to optimize gemm。如果對 GEMM 優(yōu)化和體系結(jié)構(gòu)結(jié)合的理論感興趣,可以參考 Anatomy of High-Performance Matrix Multiplication 。


        參考

        https://github.com/flame/how-to-optimize-gemm/wiki
        https://github.com/pytorch/QNNPACK
        https://dl.acm.org/citation.cfm?id=1356053
        https://github.com/Yangqing/caffe/wiki/Convolution-in-Caffe:-a-memo


        下載1:OpenCV-Contrib擴展模塊中文版教程
        在「小白學視覺」公眾號后臺回復:擴展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴展模塊教程中文版,涵蓋擴展模塊安裝、SFM算法、立體視覺、目標跟蹤、生物視覺、超分辨率處理等二十多章內(nèi)容。

        下載2:Python視覺實戰(zhàn)項目52講
        小白學視覺公眾號后臺回復:Python視覺實戰(zhàn)項目,即可下載包括圖像分割、口罩檢測、車道線檢測、車輛計數(shù)、添加眼線、車牌識別、字符識別、情緒檢測、文本內(nèi)容提取、面部識別等31個視覺實戰(zhàn)項目,助力快速學校計算機視覺。

        下載3:OpenCV實戰(zhàn)項目20講
        小白學視覺公眾號后臺回復:OpenCV實戰(zhàn)項目20講即可下載含有20個基于OpenCV實現(xiàn)20個實戰(zhàn)項目,實現(xiàn)OpenCV學習進階。

        交流群


        歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關微信群。請勿在群內(nèi)發(fā)送廣告,否則會請出群,謝謝理解~


        瀏覽 63
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日本A V网站 | 国产999999久久99999 | 性感美女啪啪 | 国产不卡影院 | 91精品一区二区三区在线 | 老牛AV国产性久久 | 91免费黄片 | 天天躁日日躁狠狠躁喷水 | 三级精品在线观看 | 123** |