pytorch中的nn.Unfold()函數(shù)和fold()函數(shù)解讀
pytorch的nn.Unfold()函數(shù)和fold()函數(shù)
1.nn.Unfold()函數(shù)
描述:pytorch中的nn.Unfold()函數(shù),在圖像處理領(lǐng)域,經(jīng)常需要用到卷積操作,但是有時(shí)我們只需要在圖片上進(jìn)行滑動(dòng)的窗口操作,將圖片切割成patch,而不需要進(jìn)行卷積核和圖片值的卷積乘法操作。
這是就需要用到nn.Unfold()函數(shù),該函數(shù)是從一個(gè)batch圖片中,提取出滑動(dòng)的局部區(qū)域塊,也就是卷積操作中的提取kernel filter對應(yīng)的滑動(dòng)窗口。
torch.nn.Unfold(kernel_size,dilation=1,paddding=0,stride=1)
該函數(shù)的輸入是(bs,c,h,w),其中bs為batch-size,C是channel的個(gè)數(shù)。
而該函數(shù)的輸出是(bs,Cxkernel_size[0]xkernel_size[1],L)其中L是特征圖或者圖片的尺寸根據(jù)kernel_size的長寬滑動(dòng)裁剪后得到的多個(gè)patch的數(shù)量。
import torch.nn as nn import torch batches_img=torch.rand(1,2,4,4)#模擬圖片數(shù)據(jù)(bs,2,4,4),通道數(shù)C為2 print("batches_img:\n",batches_img) nn_Unfold=nn.Unfold(kernel_size=(2,2),dilation=1,padding=0,stride=2) patche_img=nn_Unfold(batches_img) print("patche_img.shape:",patche_img.shape) print("patch_img:\n",patche_img)
該方法的主要應(yīng)用場景是將圖片切割成不同的patch,配合一下代碼實(shí)現(xiàn)
#上面的代碼能夠獲取到patch_img,(bs,C*K*K,L),L代表的是將每張圖片分割成多少塊 reshape_patche_img=patche_img.view(batches_img.shape[0],batches_img.shape[1],2,2,-1) print(reshape_patche_img.shape)#[bs, C, k, k, L] reshape_patche_img=reshape_patche_img.permute(0,4,1,2,3)#[N, L, C, k, k] print(reshape_patche_img.shape)
結(jié)果:
2.nn.Fold()函數(shù)
該函數(shù)是nn.Unfold()函數(shù)的逆操作。
fold = torch.nn.Fold(output_size=(4, 4), kernel_size=(2, 2), stride=2) inputs_restore = fold(patches) print(inputs_restore) print(inputs_restore.size())
nn.functional.fold/unfold
作用:fold和unfold的作用恰好相反,unfold是用一個(gè)滑窗來提取圖像中的像素值,類似于卷積操作,但是只提取不計(jì)算,fold恰好相反將滑窗提取的值返回為一個(gè)圖像
nn.functional.unfold(input, kernel_size, dilation=1, padding=0, stride=1)
- input: 輸入tensor
- kernel_size: 提取時(shí)的滑窗大小
- dilation: 滑窗是否有空洞
- padding: 是否對原圖進(jìn)行填充
- stride: 滑窗移動(dòng)的步長
下面舉一個(gè)例子直觀解釋
x = torch.Tensor([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [ 13, 14, 15, 16]]]]) x = F.unfold(x, kernel_size=(2, 2), padding=0, stride=2) print(x) print(x.size()) ''' tensor([[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]) torch.Size([1, 4, 4]) '''
執(zhí)行過程很簡單,用一個(gè) 2 × 2 的窗在圖上滑動(dòng),步長為2,第一次覆蓋的內(nèi)容為1256,第二次為3478,以此類推,每次滑窗的結(jié)果用一個(gè)列向量表示,列數(shù)就是滑窗提取的次數(shù)。
如果我們要得到每次滑窗的結(jié)果,例如第一次提取的結(jié)果,用表達(dá)式x[:,:,0]
即可
nn.functional.fold(input, output_size, kernel_size, dilation=1, padding=0, stride=1)
- input: 輸入tensor
- output_size: 輸出圖像的大?。ū仨氈付ǎ?/li>
- kernel_size: 在圖像中填充的形狀
- dilation: 滑窗是否有空洞
- padding: 是否對原圖進(jìn)行填充
- stride: 存放窗的tensor時(shí)移動(dòng)的步長
網(wǎng)上很少有講這個(gè)函數(shù)的,都說是unfold的逆過程,我們依然用幾個(gè)例子來對其進(jìn)行詳細(xì)的解釋
1. 第一個(gè)例子
x = torch.Tensor([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [ 13, 14, 15, 16]]]]) x = F.unfold(x, kernel_size=(2, 2), padding=0, stride=2) x = F.fold(x, output_size=(4,4), kernel_size=(2,2), padding=0, stride=2) ''' tensor([[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]) torch.Size([1, 4, 4]) tensor([[[[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 9., 10., 11., 12.], [13., 14., 15., 16.]]]]) torch.Size([1, 1, 4, 4]) '''
fold函數(shù)是如何執(zhí)行的呢,他會(huì)提取unfold函數(shù)的每一列,首先提取1256這一列,然后根據(jù)kernel_size的大小將1256重新resize并填到output的第一個(gè)位置,如下
[[ 1., 2., 0., 0.], [ 5., 6., 0., 0.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]]
隨后提取第二列2.6.10.14,resize為 2 × 2 2\times 2 2×2的形狀,根據(jù)步長為2添加到output的下一個(gè)位置,并以此類推
[[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 0., 0., 0., 0.], [ 0., 0., 0., 0.]]
注意:output,kernel以及stride必須滿足一定的關(guān)系(參考文檔)
知道原理以后我們可以自由操作上述tensor,但是注意,如果步長等設(shè)置不合適的話,最后的結(jié)果是有overlap的,下面我們展示兩個(gè)例子
2. 第二個(gè)例子
自由操作tensor
x = torch.Tensor([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [ 13, 14, 15, 16]]]]) x = F.unfold(x, kernel_size=(2, 2), padding=0, stride=2) x = F.fold(x, output_size=(4,4), kernel_size=(4,1), padding=0, stride=1) ''' tensor([[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]) torch.Size([1, 4, 4]) # tensor又變回了原來的樣子 tensor([[[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]]) torch.Size([1, 1, 4, 4]) '''
overlap的情況
根據(jù)上述講的可以自己推一下
x = torch.Tensor([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [ 13, 14, 15, 16]]]]) x = F.unfold(x, kernel_size=(2, 2), padding=0, stride=2) x = F.fold(x, output_size=(3,3), kernel_size=(2,2), padding=0, stride=1) ''' tensor([[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]) torch.Size([1, 4, 4]) tensor([[[[ 1., 5., 4.], [14., 34., 20.], [13., 29., 16.]]]]) torch.Size([1, 1, 3, 3]) '''
3. kernel size小于列向量的情況
上面講了,fold每次都會(huì)對列向量進(jìn)行提取,之前的例子都是kernel size等于列向量,如果我們的kernel size小于列向量就會(huì)出現(xiàn)以下情況
x = torch.Tensor([[[[ 1, 2, 3, 4], [ 5, 6, 7, 8], [ 9, 10, 11, 12], [ 13, 14, 15, 16]]]]) x = F.unfold(x, kernel_size=(2, 2), padding=0, stride=2) x = F.fold(x, output_size=(2,2), kernel_size=(1,1), padding=0, stride=1) ''' tensor([[[ 1., 3., 9., 11.], [ 2., 4., 10., 12.], [ 5., 7., 13., 15.], [ 6., 8., 14., 16.]]]) torch.Size([1, 4, 4]) tensor([[[[ 1., 3.], [ 9., 11.]], [[ 2., 4.], [10., 12.]], [[ 5., 7.], [13., 15.]], [[ 6., 8.], [14., 16.]]]]) torch.Size([1, 4, 2, 2]) '''
解釋一下,我們第一次提取的應(yīng)該是1256,但是由于我們的kernel太小了, 1 × 1 = 1 ,只能提取一個(gè)元素,因此就是1,我們的output size是 2 × 2 ,步長為1,所以第一次提取的結(jié)果如下
[[ 1, 0], [ 0, 0]]
第二次提取時(shí),就需要移動(dòng)了,提取的不是列向量中的2,而是橫向移動(dòng)的3,接著放到剛才那個(gè)元素后面
[[ 1, 3], [ 0, 0]]
之后的過程以此類推,直到我們提取到11,這時(shí)我們的行向量提取完了,但是列向量沒有,所以我們從第二列開始重復(fù)剛才的過程即可,可以看到最終我們輸出向量大小為[1,4,2,2]
,4就是我們提取了4次行向量,兩個(gè)2就是每次提取的大小(即output size)
最后加一個(gè)復(fù)雜的具有padding的例子
padding就是在對tensor進(jìn)行操作之前在tensor四周補(bǔ)0或其他的值。例子中僅對unfold進(jìn)行padding,如果對fold進(jìn)行padding也同理
x = torch.Tensor([[[[ 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], [ 57, 58, 59, 60, 61, 62, 63, 64]]]]) x = F.unfold(x, kernel_size=(6,6), padding=1, stride=4) x = F.fold(x, output_size=(12,12), kernel_size=(6,6), padding=0, stride=6) ''' tensor([[[ 0., 0., 0., 28.], [ 0., 0., 25., 29.], [ 0., 0., 26., 30.], [ 0., 0., 27., 31.], [ 0., 0., 28., 32.], [ 0., 0., 29., 0.], [ 0., 4., 0., 36.], [ 1., 5., 33., 37.], [ 2., 6., 34., 38.], [ 3., 7., 35., 39.], [ 4., 8., 36., 40.], [ 5., 0., 37., 0.], [ 0., 12., 0., 44.], [ 9., 13., 41., 45.], [10., 14., 42., 46.], [11., 15., 43., 47.], [12., 16., 44., 48.], [13., 0., 45., 0.], [ 0., 20., 0., 52.], [17., 21., 49., 53.], [18., 22., 50., 54.], [19., 23., 51., 55.], [20., 24., 52., 56.], [21., 0., 53., 0.], [ 0., 28., 0., 60.], [25., 29., 57., 61.], [26., 30., 58., 62.], [27., 31., 59., 63.], [28., 32., 60., 64.], [29., 0., 61., 0.], [ 0., 36., 0., 0.], [33., 37., 0., 0.], [34., 38., 0., 0.], [35., 39., 0., 0.], [36., 40., 0., 0.], [37., 0., 0., 0.]]]) torch.Size([1, 36, 4]) tensor([[[[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 2., 3., 4., 5., 4., 5., 6., 7., 8., 0.], [ 0., 9., 10., 11., 12., 13., 12., 13., 14., 15., 16., 0.], [ 0., 17., 18., 19., 20., 21., 20., 21., 22., 23., 24., 0.], [ 0., 25., 26., 27., 28., 29., 28., 29., 30., 31., 32., 0.], [ 0., 33., 34., 35., 36., 37., 36., 37., 38., 39., 40., 0.], [ 0., 25., 26., 27., 28., 29., 28., 29., 30., 31., 32., 0.], [ 0., 33., 34., 35., 36., 37., 36., 37., 38., 39., 40., 0.], [ 0., 41., 42., 43., 44., 45., 44., 45., 46., 47., 48., 0.], [ 0., 49., 50., 51., 52., 53., 52., 53., 54., 55., 56., 0.], [ 0., 57., 58., 59., 60., 61., 60., 61., 62., 63., 64., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]]]) torch.Size([1, 1, 12, 12]) '''
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python3轉(zhuǎn)換html到pdf的不同解決方案
今天小編就為大家分享一篇關(guān)于Python3轉(zhuǎn)換html到pdf的不同解決方案,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03Python實(shí)現(xiàn)arctan換算角度的示例
本文主要介紹了Python實(shí)現(xiàn)arctan換算角度的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03詳解sklearn?Preprocessing?數(shù)據(jù)預(yù)處理功能
這篇文章主要介紹了sklearn?Preprocessing?數(shù)據(jù)預(yù)處理功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08Python中使用第三方庫xlutils來追加寫入Excel文件示例
這篇文章主要介紹了Python中使用第三方庫xlutils來追加寫入Excel文件示例,本文直接給出追加寫入示例和追加效果,需要的朋友可以參考下2015-04-04Pycharm+Flask零基礎(chǔ)項(xiàng)目搭建入門的實(shí)現(xiàn)
本文主要介紹了Pycharm+Flask零基礎(chǔ)項(xiàng)目搭建入門的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Python如何通過百度翻譯API實(shí)現(xiàn)翻譯功能
這篇文章主要介紹了Python如何通過百度翻譯API實(shí)現(xiàn)翻譯功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Python基于機(jī)器學(xué)習(xí)方法實(shí)現(xiàn)的電影推薦系統(tǒng)實(shí)例詳解
這篇文章主要介紹了Python基于機(jī)器學(xué)習(xí)方法實(shí)現(xiàn)的電影推薦系統(tǒng),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06python word轉(zhuǎn)pdf代碼實(shí)例
這篇文章主要介紹了python word轉(zhuǎn)pdf代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08