首先說下pytorch中的tensor通道排列順序是:[batch, channel, height, width]
我們常用的卷積(conv2d)在pytorch中對應的函式是:
torch.nn.conv2d(in_channels,
out_channels,
kernel_size,
stride=1,
padding=0,
dilation=1,
groups=1,
bias=true,
padding_mode='zeros')
其中,in_channels引數代表輸入特徵矩陣的深度即channel,比如輸入一張rgb彩色影象,那in_channels=3
out_channels引數代表卷積核的個數,使用n個卷積核輸出的特徵矩陣深度即channel就是n
kernel_size引數代表卷積核的尺寸,輸入可以是int型別如3 代表卷積核的height=width=3,也可以是tuple型別如(3, 5)代表卷積核的height=3,width=5
stride引數代表卷積核的步距預設為1,和kernel_size一樣輸入可以是int型別,也可以是tuple型別
padding引數代表在輸入特徵矩陣四周補零的情況預設為0,同樣輸入可以為int型如1 代表上下方向各補一行0元素,左右方向各補一列0畫素(即補一圈0),如果輸入為tuple型如(2, 1) 代表在上方補兩行下方補兩行,左邊補一列,右邊補一列。可見下圖,padding[0]是在h高度方向兩側填充的,padding[1]是在w寬度方向兩側填充的:
注意:如果要實現更靈活的padding方式,可使用nn.zeropad2d方法。
bias引數表示是否使用偏置(預設使用)
dilation、groups是高階用法這裡不做講解,如有需要可以參看官方文件
在卷積操作過程中,我們知道矩陣經卷積操作後的尺寸由以下幾個因數決定:
輸入大小 w×w
filter大小 f×f
步長 s
padding的畫素數 p
經卷積後的矩陣尺寸大小計算公式為:
n = (w − f + 2p ) / s + 1
但在實際應用中,有時會出現n為非整數的情況(例如在alexnet,googlenet網路的第一層輸出),再例如輸入的矩陣h=w=5,卷積核的f=2,s=2,padding=1。經計算我們得到的n =(5 - 2 + 2*1)/ 2 +1 = 3.5此時在pytorch中是如何處理呢,先直接告訴你結論:在卷積過程中會直接將最後一行以及最後一列給忽略掉,以保證n為整數,此時n = (5 - 2 + 2*1 - 1)/ 2 + 1 = 3,接下來我們來看個簡單的例項:
(1)首先使用torch中的隨機函式生成乙個batch_size為1,channel為1,高和寬都等於5的矩陣
(2)接著我們定義乙個卷積核,input_size=1, output_size=1, kernel_size=2, stride=2, padding=1
(3)然後我們使用該卷積核對我們生成的隨機矩陣進行卷積操作
(4)列印各引數的數值
import torch.nn as nn
import torch
im = torch.randn(1, 1, 5, 5)
c = nn.conv2d(1, 1, kernel_size=2, stride=2, padding=1)
output = c(im)
print(im)
print(output)
print(list(c.parameters()))
通過計算我們知道輸出矩陣尺寸應該為n =(5 - 2 + 2*1)/ 2 +1 = 3.5,但實際的列印資訊如下:
# im
tensor([[[[-0.2146, 0.3375, 2.7877, 0.2052, -0.4651],
[-0.2261, 0.0116, -0.6255, 1.2523, -1.0565],
[-1.9227, -0.2575, -0.7725, 0.5658, 0.0717],
[ 0.8153, -1.3656, -0.1844, 0.1573, -0.2235],
[ 0.0184, -0.0475, 0.2359, 0.0127, 2.0665]]]])
# output
tensor([[[[-0.0467, -1.1766, -0.0450],
[ 0.5063, 0.1971, -1.0401],
[-0.0748, 0.4769, -0.8986]]]], grad_fn=)
# conv2d:parameters
[parameter containing:
tensor([[[[-0.4872, 0.0604],
[-0.3968, -0.3317]]]], requires_grad=true), parameter containing:
tensor([-0.1179], requires_grad=true)]
通過分析,我們可以知道真正的輸出矩陣尺寸是3x3,那內部具體是如何操作的呢,
(1)首先進行padding的填充,size:7 x 7
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.0000, -0.2146, 0.3375, 2.7877, 0.2052, -0.4651, 0.0000],
[0.0000, -0.2261, 0.0116, -0.6255, 1.2523, -1.0565, 0.0000],
[0.0000, -1.9227, -0.2575, -0.7725, 0.5658, 0.0717, 0.0000],
[0.0000, 0.8153, -1.3656, -0.1844, 0.1573, -0.2235, 0.0000],
[0.0000, 0.0184, -0.0475, 0.2359, 0.0127, 2.0665, 0.0000],
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]
(2)通過計算發現輸出為非整數,為了得到整數,將最後一行以及最後一列刪除掉,size:6 x 6
[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[0.0000, -0.2146, 0.3375, 2.7877, 0.2052, -0.4651],
[0.0000, -0.2261, 0.0116, -0.6255, 1.2523, -1.0565],
[0.0000, -1.9227, -0.2575, -0.7725, 0.5658, 0.0717],
[0.0000, 0.8153, -1.3656, -0.1844, 0.1573, -0.2235],
[0.0000, 0.0184, -0.0475, 0.2359, 0.0127, 2.0665]
(3)接著使用卷積核進行卷積操作,就能得到我們的輸出矩陣,需要注意的是pytorch中的卷積預設是帶有bias的,所以計算卷積後需要加上bias偏量。例如輸出的第乙個值的計算過程如下:
[0.0000, 0.0000], [-0.4872, 0.0604],
卷積 加上 [-0.1179]
[0.0000, -0.2146] [-0.3968, -0.3317]
# 即(0*(-0.4872)+ 0*(0.0604)+ 0*(-0.3968)+(-0.2146)*(-0.3317))+(-0.1179)= -0.0467
我們的計算結果與pytorch的輸出相同,我們只計算了其中乙個值,其他的值也一樣:
# output
tensor([[[[-0.0467, -1.1766, -0.0450],
[ 0.5063, 0.1971, -1.0401],
[-0.0748, 0.4769, -0.8986]]]], grad_fn=)
通過我們的實驗可以發現,在pytorch的卷積過程中,當通過n = (w − f + 2p ) / s + 1計算式得到的輸出尺寸非整數時,會通過刪除多餘的行和列來保證卷積的輸出尺寸為整數。
pytorch實現Sep conv卷積操作
這個卷積名字起得花裡胡哨的,其實總結起來就是輸入通道每個通道乙個卷積得到和輸入通道數相同的特徵圖,然後再使用若干個1 1的卷積聚合每個特徵圖的值得到輸出特徵圖。假設我們輸入通道是16,輸出特徵圖是32,並且使用3 3的卷積提取特徵,那麼第一步一共需要16 3 3個引數,第二步需要32 16 1 1個...
pytorch 自定義卷積核進行卷積操作
一 卷積操作 在pytorch搭建起網路時,大家通常都使用已有的框架進行訓練,在網路中使用最多就是卷積操作,最熟悉不過的就是 torch.nn.conv2d in channels,out channels,kernel size,stride 1,padding 0,dilation 1,grou...
PyTorch中的topk函式詳解
聽名字就知道這個函式是用來求tensor中某個dim的前k大或者前k小的值以及對應的index。用法torch.topk input,k,dim none,largest true,sorted true,out none tensor,longtensor topk最常用的場合就是求乙個樣本被網路...