有些地方還沒看懂, mark一下
文章來源: https://blog.csdn.net/g11d111/article/details/82855946
去年曾經使用過FCN(全卷積神經網絡)及其派生Unet,再加上在愛奇藝的時候做過一些超分辨率重建的內容,其中用到了畢業於帝國理工的華人博士Shi Wenzhe(在Twitter任職)發表的PixelShuffle《Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network
》的論文。PyTorch 0.4.1將這些上采樣的方式定義為Vision Layers,現在對這4種在PyTorch中的上采樣方法進行介紹。
0. 什么是上采樣?
上采樣,在深度學習框架中,可以簡單的理解為**任何可以讓你的圖像變成更高分辨率的技術。**最簡單的方式是重采樣和插值:將輸入圖片input image進行rescale到一個想要的尺寸,而且計算每個點的像素點,使用如雙線性插值bilinear等插值方法對其余點進行插值。
Unpooling是在CNN中常用的來表示max pooling的逆操作。這是從2013年紐約大學Matthew D. Zeiler和Rob Fergus發表的《Visualizing and Understanding Convolutional Networks》中引用的:因為max pooling不可逆,因此使用近似的方式來反轉得到max pooling操作之前的原始情況:
記住max pooling做的時候的size,比如下圖的一個4x4的矩陣,max pooling的size為2x2,stride為2,反卷積操作需要記住最大值的位置,將其余位置至為0就行。

Deconvolution(反卷積)在CNN中常用於表示一種反向卷積 ,但它並不是一個完全符合數學規定的反卷積操作。
與Unpooling不同,使用反卷積來對圖像進行上采樣是可以習得的。通常用來對卷積層的結果進行上采樣,使其回到原始圖片的分辨率。
反卷積也被稱為分數步長卷積(convolution with fractional strides)或者轉置卷積(transpose convolution)或者后向卷積backwards strided convolution。
真正的反卷積如wikipedia里面所說,但是不會有人在實際的CNN結構中使用它。
1. Vision Layer
在PyTorch中,上采樣的層被封裝在torch.nn中的Vision Layers里面,一共有4種:
- ① PixelShuffle
- ② Upsample
- ③ UpsamplingNearest2d
- ④ UpsamplingBilinear2d
下面,將對其分別進行說明
1.1 PixelShuffle
正常情況下,卷積操作會使feature map的高和寬變小。
但當我們的stride=
時,可以讓卷積后的feature map的高和寬變大——即分辨率增大,這個新的操作叫做sub-pixel convolution,具體原理可以看PixelShuffle《Real-Time Single Image and Video Super-Resolution Using an Efficient Sub-Pixel Convolutional Neural Network
》的論文。

pixelshuffle算法的實現流程如上圖,其實現的功能是:將一個H × W的低分辨率輸入圖像(Low Resolution),通過Sub-pixel操作將其變為rH x rW的高分辨率圖像(High Resolution)。
但是其實現過程不是直接通過插值等方式產生這個高分辨率圖像,而是通過卷積先得到 個通道的特征圖(特征圖大小和輸入低分辨率圖像一致),然后通過周期篩選(periodic shuffing)的方法得到這個高分辨率的圖像,其中 為上采樣因子(upscaling factor),也就是圖像的擴大倍率。
定義
該類定義如下:
class torch.nn.PixleShuffle(upscale_factor)
- 1
- 1
這里的upscale_factor就是放大的倍數,數據類型為int。
以四維輸入(N,C,H,W)為例,Pixelshuffle會將為(∗,
,H,W)的Tensor給reshape成(∗,C,rH,rW)的Tensor。形式化地說,它的輸入輸出的shape如下:
- 輸入: (N,C x upscale_factor ,H,W)
- 輸出: (N,C,H x upscale_factor,W x upscale_factor)
例子
>>> ps = nn.PixelShuffle(3) >>> input = torch.tensor(1, 9, 4, 4) >>> output = ps(input) >>> print(output.size()) torch.Size([1, 1, 12, 12])
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
怎么樣,是不是看起來挺簡單的?我將在最后完整的介紹一下1)轉置卷積 2)sub-pixel 卷積
3)反卷積以及pixelshuffle這幾個知識點。
1.2 Upsample(新版本中推薦使用torch.nn.functional.interpolate)
對給定多通道的1維(temporal)、2維(spatial)、3維(volumetric)數據進行上采樣。
對volumetric輸入(3維——點雲數據),輸入數據Tensor格式為5維:minibatch x channels x depth x height x width
對spatial輸入(2維——jpg、png等數據),輸入數據Tensor格式為4維:minibatch x channels x height x width
對temporal輸入(1維——向量數據),輸入數據Tensor格式為3維:minibatch x channels x width
此算法支持最近鄰,線性插值,雙線性插值,三次線性插值對3維、4維、5維的輸入Tensor分別進行上采樣(Upsample)。
定義
該類定義如下:
class torch.nn.Upsample(size=None, scale_factor=None, mode='nearest', align_corners=None)
- 1
- 1
其中:
- size 是要輸出的尺寸,數據類型為tuple: ([optional D_out], [optional H_out], W_out)
- scale_factor 在高度、寬度和深度上面的放大倍數。數據類型既可以是int——表明高度、寬度、深度都擴大同一倍數;亦或是tuple——指定高度、寬度、深度的擴大倍數。
- mode 上采樣的方法,包括最近鄰(nearest),線性插值(linear),雙線性插值(bilinear),三次線性插值(trilinear),默認是最近鄰(nearest)。
- align_corners 如果設為True,輸入圖像和輸出圖像角點的像素將會被對齊(aligned),這只在mode = linear, bilinear, or trilinear才有效,默認為False。
例子
>>> input = torch.arange(1, 5).view(1, 1, 2, 2).float() >>> input tensor([[[[ 1., 2.], [ 3., 4.]]]])>>> m = nn.Upsample(scale_factor=2, mode='nearest')
>>> m(input)
tensor([[[[ 1., 1., 2., 2.],
[ 1., 1., 2., 2.],
[ 3., 3., 4., 4.],
[ 3., 3., 4., 4.]]]])>>> m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
>>> m(input)
tensor([[[[ 1.0000, 1.2500, 1.7500, 2.0000],
[ 1.5000, 1.7500, 2.2500, 2.5000],
[ 2.5000, 2.7500, 3.2500, 3.5000],
[ 3.0000, 3.2500, 3.7500, 4.0000]]]])>>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
>>> m(input)
tensor([[[[ 1.0000, 1.3333, 1.6667, 2.0000],
[ 1.6667, 2.0000, 2.3333, 2.6667],
[ 2.3333, 2.6667, 3.0000, 3.3333],
[ 3.0000, 3.3333, 3.6667, 4.0000]]]])>>> # Try scaling the same data in a larger tensor
>>>
>>> input_3x3 = torch.zeros(3, 3).view(1, 1, 3, 3)
>>> input_3x3[:, :, :2, :2].copy_(input)
tensor([[[[ 1., 2.],
[ 3., 4.]]]])
>>> input_3x3
tensor([[[[ 1., 2., 0.],
[ 3., 4., 0.],
[ 0., 0., 0.]]]])>>> m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
>>> # Notice that values in top left corner are the same with the small input (except at boundary)
>>> m(input_3x3)
tensor([[[[ 1.0000, 1.2500, 1.7500, 1.5000, 0.5000, 0.0000],
[ 1.5000, 1.7500, 2.2500, 1.8750, 0.6250, 0.0000],
[ 2.5000, 2.7500, 3.2500, 2.6250, 0.8750, 0.0000],
[ 2.2500, 2.4375, 2.8125, 2.2500, 0.7500, 0.0000],
[ 0.7500, 0.8125, 0.9375, 0.7500, 0.2500, 0.0000],
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
>>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
>>> # Notice that values in top left corner are now changed
>>> m(input_3x3)
tensor([[[[ 1.0000, 1.4000, 1.8000, 1.6000, 0.8000, 0.0000],
[ 1.8000, 2.2000, 2.6000, 2.2400, 1.1200, 0.0000],
[ 2.6000, 3.0000, 3.4000, 2.8800, 1.4400, 0.0000],
[ 2.4000, 2.7200, 3.0400, 2.5600, 1.2800, 0.0000],
[ 1.2000, 1.3600, 1.5200, 1.2800, 0.6400, 0.0000],
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
1.3 UpsamplingNearest2d
本質上其實就是對jpg、png等格式圖像數據的Upsample(mode='nearest')。
定義
class torch.nn.UpsamplingNearest2d(size=None, scale_factor=None)
- 1
- 1
輸入輸出:

例子
>>> input = torch.arange(1, 5).view(1, 1, 2, 2) >>> input tensor([[[[ 1., 2.], [ 3., 4.]]]])
>>> m = nn.UpsamplingNearest2d(scale_factor=2)
>>> m(input)
tensor([[[[ 1., 1., 2., 2.],
[ 1., 1., 2., 2.],
[ 3., 3., 4., 4.],
[ 3., 3., 4., 4.]]]])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
1.4 UpsamplingBilinear2d
跟1.3類似,本質上其實就是對jpg、png等格式圖像數據的Upsample(mode='bilinear')。
定義
class torch.nn.UpsamplingBilinear2d(size=None, scale_factor=None)
- 1
- 1
輸入輸出:

例子
>>> input = torch.arange(1, 5).view(1, 1, 2, 2) >>> input tensor([[[[ 1., 2.], [ 3., 4.]]]])
>>> m = nn.UpsamplingBilinear2d(scale_factor=2)
>>> m(input)
tensor([[[[ 1.0000, 1.3333, 1.6667, 2.0000],
[ 1.6667, 2.0000, 2.3333, 2.6667],
[ 2.3333, 2.6667, 3.0000, 3.3333],
[ 3.0000, 3.3333, 3.6667, 4.0000]]]])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
2. 知識回顧
本段主要轉自《一邊Upsample一邊Convolve:Efficient Sub-pixel-convolutional-layers詳解
》
2.1 Transposed convolution(轉置卷積)
下面以一維向量進行卷積為例進行說明(stride=2),x為輸入y為輸出,通過1維卷積核/濾波器f來實現這個過程,x的size為8,f為[1, 2, 3, 4],y為5,x中灰色的方塊表示用0進行padding。在f權重中的灰色方塊代表f中某些值與x中的0進行了相乘。下圖就是1維卷積的過程,從x到y。

容易地,可以發現1維卷積的方式很直觀,那么什么是轉置卷積呢?故名思意,就是將卷積倒過來:

如上圖所示,1維卷積核/濾波器被轉過來了,這里進行一下額外的說明:
假設x = [
,
, …,
],y = [
,
, …,
],則最上面的白色塊體對應的是
。那么:
=
2.2 Sub-pixel convolution
還是以一維卷積為例,輸入為x = [
,
, …,
],輸出為y = [
,
, …,
]。sub-pixel convolution(stride=1/2)如圖:

在1.1 PixelShuffle中說過,sub-pixel convolution的步長是介於0到1之間的,但是這個操作是如何實現的呢?簡而言之,分為兩步:
- ① 將stride設為1
- ② 將輸入數據dilation(以stride=1/2為例,sub-pixel是將輸入x的元素之間插入一些元素0,並在前后補上一些元素0),或者說根據分數索引(fractional indices)重新創建數據的排列形式。
2.3 Deconvolution
這里以2維卷積來進行演示,輸入一個4 x 4的單通道圖像,卷積核取1個4 x 4的,假設這里取上采樣比例為2,那么我們的目標就是恢復成一個8 x 8的單通道圖像。

如上圖,我們首先通過fractional indices從原input中創建一個sub-pixel圖像,其中白色的像素點就是原input中的像素(在LR sapce中),灰色像素點則是通過zero padding而來的。

用一個4 x 4的卷積核來和剛才生成的sub-pixel圖像進行stride=1的卷積,首先發現卷積核和sub-pixel圖像中非零的像素進行了第一次有效卷積(圖中紫色像素代表被激活的權重),然后我們將sub-pixels整體向右移動一格,讓卷積核再進行一次卷積操作,會發現卷積核中藍色像素的權重被激活,同理綠色和紅色(注意這里是中間的那個8×8的sub-pixel圖像中的白色像素點進行移動,而每次卷積的方式都相同)。

最后我們輸出得到8 x 8的高分辨率圖像(HR圖像),HR圖像和sub-pixel圖像的大小是一致的,我們將其塗上顏色,顏色代表卷積核中權重和sub-pixel圖像中哪個像素點進行了卷積(也就是哪個權重對對應的像素進行了貢獻)。
Deconvlution的動態過程可見我之前翻譯過的一篇文章《CNN概念之上采樣,反卷積,Unpooling概念解釋》
顯然,我們可以看出,紫、藍、綠、紅四部分是相互獨立的,那么,可以將這個4 x 4的卷積核分成4個2 x 2的卷積核如下:

注意,這個操作是可逆的。因為每個卷積權重在操作過程中都是獨立的。
因此,我們可以直接對原始圖像(未經過sub-pixel處理)直接進行2 x 2的卷積,並對輸出進行周期篩選(periodic shuffling)來得到同樣的8 x 8的高分辨率圖像。

3. 說明
在新版本PyTorch中,這些插值Vision Layer都不推薦使用了,官方的說法是將其放在了torch.nn.functional.interpolate中,用此方法可以更個性化的定制用戶的上采樣或者下采樣的需求。
4. 參考資料
[1] 一邊Upsample一邊Convolve:Efficient Sub-pixel-convolutional-layers詳解
[2] 雙線性插值(Bilinear Interpolation)
[3] torch.nn.functional.interpolate說明
[4] PyTorch 0.4.1——Vision layers
</div>
