本文轉摘於如下鏈接:
逆卷積的詳細解釋ConvTranspose2d(fractionally-strided convolutions) https://www.cnblogs.com/wanghui-garcia/p/10791328.html
pytorch官方手冊:https://pytorch.org/docs/stable/nn.html?highlight=convtranspose#torch.nn.ConvTranspose2d
ConvTranspose2d
torch.nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros')
output_padding參數作用:https://www.cnblogs.com/wanghui-garcia/p/10791778.html
1.首先先定義進行卷積的參數:
輸入特征圖為高寬一樣的Hin*Hin大小的x
卷積核大小kernel_size
步長stride
padding填充數(填充0)
輸出特征圖為Hout*Hout大小的y
計算式子為:
Hout = floor( Hin + 2*padding - kernel_size / stride) + 1
2.然后實現上面的卷積的轉置卷積
定義其參數為:
輸入特征圖為高寬一樣的Hout*Hout大小的y
卷積核大小kernel_size
步長stride
paddingnew 填充數(填充0)
輸出特征圖為Hin*Hin大小的x
逆卷積的過程主要分兩步:
對輸入的特征圖y進行變換,得到新的特征圖ynew
內部變換,與卷積時設置的stride相關
外部變換,與卷積時設置的padding相關
根據得到的特征圖進行卷積即可
1)對輸入的特征圖y進行變換,得到新的特征圖ynew
1》內部變換
當卷積時設置的stride>1時,將對輸入的特征圖y進行插值操作(interpolation)。
即需要在輸入的特征圖y的每個相鄰值之間插入(stride-1)行和列0,因為特征圖中能夠插入的相鄰位置有(height-1)個位置,所以此時得到的特征圖的大小由HoutHout(Hout即height) 變為新的 Hout_newHout_new,即[Hout + (stride-1) * (Hout-1)] * [Hout + (stride-1) * (Hout-1)]
2》外部變換
為了實現由HoutHout大小的y逆卷積得到HinHin大小的x,還需要設置paddingnew的值為(kernel_size - padding - 1),這里的padding是卷積操作時設置的padding值
所以計算式子變為:
Hin = floor( [Hout_new + 2*paddingnew - kernel_size] / stride') + 1
⚠️該式子變換后,定義向下取整的分母stride'值為定值1
Hout_new和paddingnew的值代入上面的式子,即變為:
Hin = floor( Hout + (stride-1) * (Hout-1) + 2*(kernel_size - padding - 1) - kernel_size) + 1
化簡為:
Hin = floor( (Hout - 1) * stride - 2*padding + kernel_size - 1) + 1
= (Hout - 1) * stride - 2*padding + kernel_size
這樣式子使的卷積Conv2d和逆卷積ConvTranspose2d在初始化時具有相同的參數,而在輸入和輸出形狀方面互為倒數。
所以這個式子其實就是官網給出的式子:
可見這里沒考慮output_padding
output_padding的作用:可見nn.ConvTranspose2d的參數output_padding的作用
3.下面舉例說明
https://github.com/vdumoulin/conv_arithmetic#convolution-arithmetic
1)當stride=1時,就不會進行插值操作,只會進行padding,舉例說明:
卷積操作為:
藍色為輸入特征圖HinHin=44,綠色為輸出特征圖HoutHout=22,卷積核kernel_size=3, stride=1
根據式子Hout = floor( Hin + 2*padding - kernel_size / stride) + 1
可得padding=0
其對應的逆卷積操作為:
藍色為輸入特征圖HoutHout=22,綠色為輸出特征圖HinHin=44,卷積核kernel_size=3, stride=1
卷積時的padding=0
將這些值代入上面的式子Hin = (Hout - 1) * stride - 2*padding + kernel_size
果然輸入HoutHout=22能得到輸出HinHin=44
變形過程為:
paddingnew = kernel_size - padding -1 = 3 -0 -1 = 2
所以可見下方的藍色最后的大小為77 = Hout + 2paddingnew = 2 + 2*2 = 6
⚠️這里可見是有padding的,為什么定義是為no padding呢?
這是因為它對應的卷積操作的padding=0
1)當stride=2時,進行插值和padding操作,舉例說明:
卷積操作為:
藍色為輸入特征圖HinHin=55,綠色為輸出特征圖HoutHout=33,卷積核kernel_size=3, stride=2
根據式子Hout = floor( Hin + 2*padding - kernel_size / stride) + 1
可得padding=1
其對應的逆卷積操作為:
藍色為輸入特征圖HoutHout=33,綠色為輸出特征圖HinHin=55,卷積核kernel_size=3,stride=2
卷積時的padding=1
將這些值代入上面的式子Hin = (Hout - 1) * stride - 2*padding + kernel_size
果然輸入HoutHout=33能得到輸出HinHin=55
變形操作為:
Hout_new = Hout + (stride-1) * (Hout-1) = 3 + (2-1)*(3-1) = 5
paddingnew = kernel_size - padding -1 = 3 -1 -1 = 1
所以可見下方的藍色最后的大小為77 = Hout_new + 2paddingnew = 5 + 2*1 = 7
⚠️因為這里的逆卷積對應的卷積操作的padding= 1,所以這里不是no padding,而是padding
output_padding參數######################################################################################################################
output_padding參數######################################################################################################################
output_padding參數######################################################################################################################
使用前提:stride > 1
補充:same卷積操作
是通過padding使得卷積之后輸出的特征圖大小保持不變(相對於輸入特征圖),不代表得到的輸出特征圖的大小與輸入特征圖的大小完全相同,而是他們之間的比例保持為 輸入特征圖大小/輸出特征圖大小 = stride
舉例:
比如輸入特征圖為66,stride=2, kernel_size = 3, 所以進行same卷機操作得輸出特征圖為33 (6/2 = 3)
如果輸入特征圖為55,stride=2,kernel_size = 3,這時候設置padding = 1,那么也會得到輸出特征圖為33
那么這樣的情況就會導致在逆卷積時出現一個問題。
問題:
問題就是,不同大小的圖片經過卷積運算能得到相同尺寸的輸出,那么作為逆運算,同樣的一張輸入圖像經過反卷積是否會有不同尺寸的合法輸出?這樣的話就存在爭議了
上面還只是進行same卷積的情況,如果考慮valid卷積,stride=2, kernel_size = 3,padding=0時,輸入特征圖為77和88的結果也是3*3
解決爭議的辦法就是使用output_padding參數
output_padding的作用是:
當stride > 1時,Conv2d將多個輸入形狀映射到相同的輸出形狀。output_padding通過在一邊有效地增加計算出的輸出形狀來解決這種模糊性。
首先我們要認同一個前提:
大多數情況下我們都希望經過卷積/反卷積處理后的圖像尺寸比例與步長相等,即輸入特征圖大小/輸出特征圖大小 = stride,也就是same模式。
所以我們只要通過添加output_padding這一參數來使得結果滿足這一前提,那么輸出的圖片的大小就能夠保證為輸入圖片*stride的大小,而不是任意可能的大小
實現辦法:
因為pytorch將參數padding(注意與output_padding區別)建議設置為(kernel_size - 1)/2,由式子padding= kernel - 1 - padding轉換而來
那么根據式子:
當我們希望得到輸入特征圖大小/輸出特征圖大小 = stride的話,代入上面的式子能夠得到結果:
padding = (kernel_size - stride + output_padding )/2
所以為了讓padding = (kernel_size - 1)/2,則output_padding應該取值為stride - 1,這樣就能夠滿足輸入特征圖大小/輸出特征圖大小 = stride
當然,你可以取別的值,這並不會影響到逆卷積的計算,但是在后面進行有關大小的操作時就很可能出現問題,因為輸出的圖片的大小並不能保證是 輸入圖片stride的大小,可能是任意正確的大小,如上面舉的例子,可能是77或8*8等