反卷积的计算与加速
反卷積的計算與加速
- 介紹
- 插值補零法
- 交錯相加法
- 小卷積核法
介紹
反卷積(標準叫法為轉置卷積),是卷積的一種逆運算(注意:是卷積的逆運算,不是卷積的逆過程),屬于上采樣的一種,在計算機視覺的深度學習領域中被廣泛用作超分辨率重建等。反卷積的詳細推導過程可以看這篇。反卷積(Transposed Convolution)詳細推導
本文將介紹三種反卷積計算方法(三種計算方法在數學上完全等價),并且簡述每種方法的優劣。可以根據不同的加速平臺選擇不同的計算部署方法。
首先我們列舉幾個參數如下,
i:inputsizeo:outputsizekd:kernelsizeofdeconvsd:strideofdeconvpd:paddingofdeconv\begin{aligned} i:&input\ size \\ o:&output\ size \\ k_d:&kernel\ size\ of \ deconv \\ s_d:&stride\ of\ deconv \\ p_d:&padding\ of\ deconv \end{aligned} i:o:kd?:sd?:pd?:?input?sizeoutput?sizekernel?size?of?deconvstride?of?deconvpadding?of?deconv?
其中kdk_dkd?,sds_dsd?分別是反卷積的kernel size和stride,sds_dsd?可以理解為feature map的放大倍數(在實際的超分網絡中sds_dsd?常見為2,3,4,分別代表將feature map放大2倍,3倍,4倍)。
在本文介紹的每個方法下面用一個相同的例子來說明這些算法的等價性(golden數據采用TF的計算方式):
input(i=3×3i=3\times 3i=3×3): [123456789]\left[ \begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{matrix} \right]???147?258?369????,
kernel(kd×kd=3×3k_d\times k_d= 3\times 3kd?×kd?=3×3): [101?1100?10]\left[ \begin{matrix} 1 & 0 & 1 \\ -1 & 1 & 0 \\ 0 & -1 & 0 \\ \end{matrix} \right]???1?10?01?1?100????,
stride(sd=2s_d=2sd?=2),
output(o=6×6o=6\times 6o=6×6): [103050?11?22?334?19?211?3?44?55?667?415?517?6?77?88?99]\left[ \begin{matrix} 1 & 0 & 3 & 0 & 5 & 0 \\ -1 & 1 & -2 & 2 & -3 & 3 \\ 4 & -1 & 9 & -2 & 11 & -3 \\ -4 & 4 & -5 & 5 & -6 & 6 \\ 7 & -4 & 15 & -5 & 17 & -6 \\ -7 & 7 & -8 & 8 & -9 & 9 \\ \end{matrix} \right]?????????1?14?47?7?01?14?47?3?29?515?8?02?25?58?5?311?617?9?03?36?69?????????? (TF1.15計算結果)
插值補零法
此計算方法是目前最常見的計算反卷積的方法,核心思想就是在input feature map里的每個pixel之間插000,將kernel在二維平面旋轉180°,然后進行kernel=kd,stride=1kernel=k_d,stride=1kernel=kd?,stride=1的常規卷積計算。其中000的個數 nnn 與sds_dsd?有關,具體數學關系為:
n=stride?1n=stride-1n=stride?1
此時反卷積的輸入輸出尺寸關系為:
o=sd(i?1)?2pd+kdo=s_d(i-1)-2p_d+k_do=sd?(i?1)?2pd?+kd?
實際用tensorflow或者其他框架進行反卷積計算時,必須指定輸出圖像的大小 ooo,參考崔權:關于tf中的conv2d_transpose的用法,tensorflow反卷積層中也沒有 pdp_dpd?這個參數( 注:TF中的padding只有’SAME’和’VALID’兩種模式),我個人理解實際計算中的pdp_dpd?應該是由i,o,sd,kdi,o,s_d,k_di,o,sd?,kd?反推出來的。
讓我們來看下面兩張采用插值補零法的反卷積計算動圖:
上圖中 i=3,o=5,kd=3,sd=3i=3,o=5,k_d=3,s_d=3i=3,o=5,kd?=3,sd?=3,因此可以推算出該反卷積在top、bottom、left、right都補了1個000。
上圖中 i=3,o=6,kd=3,sd=3i=3,o=6,k_d=3,s_d=3i=3,o=6,kd?=3,sd?=3,因此可以推算出該反卷積在top、left補了1個000,而在bottom、right補了2個000注:TF的計算規則是優先在top、left補2個000,在bottom、right補1個000)。
回到我們的例子,我們采用插值補零法計算反卷積的過程如下,
輸入input插值補零(根據TF的計算規則): [0000000000000000001020300000000000405060000000000070809000000000]\left[ \begin{matrix} 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 2 & 0 & 3 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 4 & 0 & 5 & 0 & 6 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 7 & 0 & 8 & 0 & 9 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{matrix} \right]?????????????00000000?00000000?00104070?00000000?00205080?00000000?00306090?00000000?????????????? ,
deconv kernel旋轉180°: [0?1001?1101]\left[ \begin{matrix} 0 & -1 & 0 \\ 0 & 1 & -1 \\ 1 & 0 & 1 \\ \end{matrix} \right]???001??110?0?11????,
最后正向卷積得: [103050?11?22?334?19?211?3?44?55?667?415?517?6?77?88?99]\left[ \begin{matrix} 1 & 0 & 3 & 0 & 5 & 0 \\ -1 & 1 & -2 & 2 & -3 & 3 \\ 4 & -1 & 9 & -2 & 11 & -3 \\ -4 & 4 & -5 & 5 & -6 & 6 \\ 7 & -4 & 15 & -5 & 17 & -6 \\ -7 & 7 & -8 & 8 & -9 & 9 \\ \end{matrix} \right]?????????1?14?47?7?01?14?47?3?29?515?8?02?25?58?5?311?617?9?03?36?69?????????? ,
可以看出和TF golden結果一致。
優點:將反卷積轉化成常規卷積,可以用專用的卷積加速器(NPU、DPU等各種xPU)進行加速計算。
缺點:需要在每個pixel間插0,那么從外部存儲器(DDR)讀取數據就會不連續,容易形成帶寬bound;計算中也有很多冗余計算(乘0累加),實際計算效率(computation efficiency)也很低。
交錯相加法
此方法是名符其實的“反”卷積,讓我們回想下二維卷積的計算方法,一個 k×kk\times kk×k 的卷積核在以 stride 的間隔距離滑過input feature map,每一次滑動做一次卷積核和滑窗pixel的乘累加計算,并且得到一個output feature map中的一個pixel,如下圖所示:
反卷積的計算就是正向卷積計算的逆向操作,先放圖:
具體計算過程就是input feature map上的每一個pixel和 k×kk\times kk×k 的卷積核點乘得到一個k×kk\times kk×k的patch,然后相鄰的patch以間隔 stride 錯位相加,如上圖所示。一般來說,這種計算方法還需要對邊界進行裁剪以符合output feature map的大小 ooo 。這種計算過程正好和正向的卷積計算是“反”過來的,故名“反卷積”(大霧)。
我們還是以計算為例,input每個pixel和反卷積核點乘得到9個 3×33\times 33×3 patch:
[101?1100?10],[202?2200?20],[303?3300?30]\left[ \begin{matrix} 1 & 0 & 1 \\ -1 & 1 & 0 \\ 0 & -1 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 2 & 0 & 2 \\ -2 & 2 & 0 \\ 0 & -2 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 3 & 0 & 3 \\ -3 & 3 & 0 \\ 0 & -3 & 0 \\ \end{matrix} \right]???1?10?01?1?100????,???2?20?02?2?200????,???3?30?03?3?300???? ,
[404?4400?40],[505?5500?50],[606?6600?60]\left[ \begin{matrix} 4 & 0 & 4 \\ -4 & 4 & 0 \\ 0 & -4 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 5 & 0 & 5 \\ -5 & 5 & 0 \\ 0 & -5 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 6 & 0 & 6 \\ -6 & 6 & 0 \\ 0 & -6 & 0 \\ \end{matrix} \right]???4?40?04?4?400????,???5?50?05?5?500????,???6?60?06?6?600???? ,
[707?7700?70],[808?8800?80],[909?9900?90]\left[ \begin{matrix} 7 & 0 & 7 \\ -7 & 7 & 0 \\ 0 & -7 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 8 & 0 & 8 \\ -8 & 8 & 0 \\ 0 & -8 & 0 \\ \end{matrix} \right] , \left[ \begin{matrix} 9 & 0 & 9 \\ -9 & 9 & 0 \\ 0 & -9 & 0 \\ \end{matrix} \right]???7?70?07?7?700????,???8?80?08?8?800????,???9?90?09?9?900???? ,
相鄰patch以 stride=2 錯位相加得: [1030503?11?22?3304?19?211?36?44?55?6607?415?517?69?77?88?9900?70?80?90]\left[ \begin{matrix} 1 & 0 & 3 & 0 & 5 & 0 & 3\\ -1 & 1 & -2 & 2 & -3 & 3 & 0 \\ 4 & -1 & 9 & -2 & 11 & -3 & 6 \\ -4 & 4 & -5 & 5 & -6 & 6 & 0\\ 7 & -4 & 15 & -5 & 17 & -6 & 9\\ -7 & 7 & -8 & 8 & -9 & 9 & 0\\ 0 & -7 & 0 & -8 & 0 & -9 & 0\\ \end{matrix} \right]???????????1?14?47?70?01?14?47?7?3?29?515?80?02?25?58?8?5?311?617?90?03?36?69?9?3060900???????????? ,
對最右列和最底行進行裁剪得到最終結果: [103050?11?22?334?19?211?3?44?55?667?415?517?6?77?88?99]\left[ \begin{matrix} 1 & 0 & 3 & 0 & 5 & 0 \\ -1 & 1 & -2 & 2 & -3 & 3 \\ 4 & -1 & 9 & -2 & 11 & -3 \\ -4 & 4 & -5 & 5 & -6 & 6 \\ 7 & -4 & 15 & -5 & 17 & -6 \\ -7 & 7 & -8 & 8 & -9 & 9 \\ \end{matrix} \right]?????????1?14?47?7?01?14?47?3?29?515?8?02?25?58?5?311?617?9?03?36?69?????????? 。
可以看出和TF golden結果一致。
優點:沒有任何的冗余計算,不需要對輸入補0。
缺點:無法利用專用的卷積加速器進行加速,但是適合用CPU或GPU這種比較通用的處理器進行計算(注:caffe的反卷積采用的就是此計算方法)。
小卷積核法
此方法是本人最為欣賞的一個方法,該方法將每一個大小為 kd×kdk_d\times k_dkd?×kd? 的反卷積核先在二維方向旋轉180°,然后再將每一個反卷積核拆成 sd×sds_d\times s_dsd?×sd? 個小卷積核(注:此時我們可以認為 coc_oco? 擴大了 sd×sds_d\times s_dsd?×sd? 倍),然后用這些小卷積核對input feature map做 stride=1stride=1stride=1 的正向卷積,最后通過depth2space將coc_oco?方向的pixel重排到二維平面上。具體過程如下圖示:
上圖kernel=3,stride=2的反卷積,我們先將其分拆并重排成4個kernel=2,stride=1的正向卷積核,重排方案如下:
圖中的0-8代表二維 3×33\times 33×3 deconv kernel每個點的index(注:-1代表這個位置沒有值且補零)。
接著用這些正向卷積核對input feature map做正向卷積,得到4個output feature map,再用depth2space將 c_o 方向的數據重排到二維平面上。如下圖所示:
反卷積核的重排可以離線完成,不占用計算時間也不占用帶寬。
回到我們的例子,deconv kernel拆成4個kernel=2的小卷積核:
[0011]\left[ \begin{matrix} 0 & 0 \\ 1 & 1 \\ \end{matrix} \right][01?01?] , [0?100]\left[ \begin{matrix} 0 & -1 \\ 0 & 0 \\ \end{matrix} \right][00??10?] , [000?1]\left[ \begin{matrix} 0 & 0 \\ 0 & -1 \\ \end{matrix} \right][00?0?1?] , [0001]\left[ \begin{matrix} 0 & 0 \\ 0 & 1 \\ \end{matrix} \right][00?01?] ,
對input的左列和上行進行1次padding: [0000012304560789]\left[ \begin{matrix} 0 & 0 & 0 & 0 \\ 0 & 1 & 2 & 3 \\ 0 & 4 & 5 & 6 \\ 0 & 7 & 8 & 9 \\ \end{matrix} \right]?????0000?0147?0258?0369?????? ,
用每個小卷積核對上面padding過的input進行stride=1的正向卷積得:
[135491171517]\left[ \begin{matrix} 1 & 3 & 5 \\ 4 & 9 & 11 \\ 7 & 15 & 17 \\ \end{matrix} \right]???147?3915?51117???? , [000?1?2?3?4?5?6]\left[ \begin{matrix} 0 & 0 & 0 \\ -1 & -2 & -3 \\ -4 & -5 & -6 \\ \end{matrix} \right]???0?1?4?0?2?5?0?3?6???? , [?1?2?3?4?5?6?7?8?9]\left[ \begin{matrix} -1 & -2 & -3 \\ -4 & -5 & -6 \\ -7 & -8 & -9 \\ \end{matrix} \right]????1?4?7??2?5?8??3?6?9???? , [123456789]\left[ \begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{matrix} \right]???147?258?369???? ,
以上結果可以看成是4個通道的feature map,最后進行depth2space得:
[103050?11?22?334?19?211?3?44?55?667?415?517?6?77?88?99]\left[ \begin{matrix} 1 & 0 & 3 & 0 & 5 & 0 \\ -1 & 1 & -2 & 2 & -3 & 3 \\ 4 & -1 & 9 & -2 & 11 & -3 \\ -4 & 4 & -5 & 5 & -6 & 6 \\ 7 & -4 & 15 & -5 & 17 & -6 \\ -7 & 7 & -8 & 8 & -9 & 9 \\ \end{matrix} \right]?????????1?14?47?7?01?14?47?3?29?515?8?02?25?58?5?311?617?9?03?36?69??????????。
可以看出和TF golden結果一致。
優點:可以用專用的卷積加速器加速反卷積計算,沒有帶寬bound。
缺點:卷積核重排比較復雜,沒有統一的方案,部分卷積加速器可能不支持depth2space。
總結
- 上一篇: 一个自用Typora魔改主题
- 下一篇: 实验3:二层直连式WLAN组网设计