Pytorch-卷積神經網絡&Batch Norm


1.卷積層

1.1torch.nn.Conv2d()類式接口

1 torch.nn.Conv2d(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True))

參數:

  • in_channel:輸入數據的通道數,例RGB圖片通道數為3;
  • out_channel:輸出數據的通道數,也就是kernel數量;
  • kernel_size: 卷積核大小,可以是int,或tuple;kernel_size=2,意味着卷積大小(2,2),kernel_size=(2,3),意味着卷積大小(2,3)即非正方形卷積
  •   stride:步長,默認為1,與kernel_size類似,stride=2,意味着步長上下左右掃描皆為2, stride=(2,3),左右掃描步長為2,上下為3;
  •   padding:零填充
1 import  torch
2 import  torch.nn as nn
3 
4 x=torch.rand(1,1,28,28)                                  #batch, channel, height, width
5 layer=nn.Conv2d(1,3,kernel_size=3,stride=2,padding=1)    #in_channel(和上面x中的channel數量一致), out_channel(kernel個數)
6 
7 out=layer.forward(x)               #或者直接out=layer(x)
8 print(out.shape)                   #torch.Size([1, 3, 14, 14]) 1指一張圖片,3指三個kernel,14*14指圖片大小
  • 第五行layer的相關屬性:
    • layer.weight.shape = torch.Size([3,1,3,3])  第一個3指out_channel,即輸出數據的通道數(kernel個數),第二個1指in_channel,即輸入數據的通道數,這個值必須和x的channel數目一致。(Tip:前兩個參數順序是和Conv2d函數的前兩個參數順序相反的
    • layer.bias.shape = torch.Size([3]) 這個3指kernel個數,不同的通道共用一個偏置。
  • out輸出維度的計算:$(n+2p)*(n+2p) $ 與 $f*f$ 做卷積操作= $\left \lfloor \frac{n+2p}{s}+1 \right \rfloor *\left \lfloor \frac{n+2p}{s}+1 \right \rfloor $
    • n表示圖片尺寸,p表示padding,f表示kernel尺寸,s表示slide步長,(28+2-3)/2+1=14.5,向下取整,即14

1.2F.conv2d()函數式接口

PyTorch里一般小寫的都是函數式的接口,相應的大寫的是類式接口。函數式的更加low-level一些,如果不需要做特別復雜的配置只要用類式接口即可。

 1 import torch
 2 from torch.nn import functional as F
 3 
 4 #手動定義卷積核(weight)和偏置
 5 w = torch.rand(16, 3, 5, 5)                       #16種3通道的5*5卷積核
 6 b = torch.rand(16)  
 7 
 8 #定義輸入樣本
 9 x = torch.randn(1, 3, 28, 28)                     #1張3通道的28*28的圖像
10 
11 #2D卷積得到輸出
12 out = F.conv2d(x, w, b, stride=1, padding=1) 
13 print(out.shape)                                  #torch.Size([1, 16, 26, 26])
14 
15 out = F.conv2d(x, w, b, stride=2, padding=2) 
16 print(out.shape)                                  #torch.Size([1, 16, 14, 14])

2.池化層Pooling(下采樣)

1 torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

參數:

  • kernel_size(int or tuple) - max pooling的窗口大小,
  • stride(int or tuple, optional) - max pooling的窗口移動的步長。默認值是kernel_size
  • padding(int or tuple, optional) - 輸入的每一條邊補充0的層數
  • dilation(int or tuple, optional) – 一個控制窗口中元素步幅的參數
  • return_indices - 如果等於True,會返回輸出最大值的序號,對於上采樣操作會有幫助
  • ceil_mode - 如果等於True,計算輸出信號大小的時候,會使用向上取整,代替默認的向下取整的操作

接着上面卷積運算得到的1張16通道的14*14的圖像,在此基礎上做池化。

torch.MaxPool2d類式接口

1 x=out                                    #torch.Size([1, 16, 14, 14])
2 layer=nn.MaxPool2d(2,stride=2)           #池化層(池化核為2*2,步長為2),最大池化
3 
4 out=layer(x)           
5 print(out.shape)                         #torch.Size([1, 16, 7, 7])

F.avg_pool2d()函數式接口

1 x = out                                  #torch.Size([1, 16, 14, 14])
2 out = F.avg_pool2d(x, 2, stride=2)
3 print(out.shape)                         #torch.Size([1, 16, 7, 7])

Tip:池化后通道數不變。

torch.nn.AdaptiveAvgPool2d((1, 1))可以將圖片尺寸變成任何想要的尺寸(通過元組的形式設定)

3.upsample(上采樣)

1 F.interpolate(input, size=None, scale_factor=None, mode='nearest', align_corners=None)

參數:

  • size(int):輸出的spatial大小;
  • scale_factor(float): spatial尺寸的縮放因子;
  • mode(string):上采樣算法:nearest,linear,bilinear,trilinear,area,默認nearest;
  • align_corners(bool, optional):如果align_corners=True,則對齊input和output的角點像素,只會對 mode=linear, bilinear 和 trilinear 有作用。默認是 False。
1 x = out                                                  #torch.Size([1, 16, 7, 7])
2 
3 out = F.interpolate(x, scale_factor=2, mode='nearest')   #向上采樣,放大2倍,最近插值
4 print(out.shape)                                         #torch.Size([1, 16, 14, 14])

4.RELU激活函數

4.1torch.nn.RELU()類式接口

1 x = out                                     #torch.Size([1, 16, 14, 14])
2 
3 layer = nn.ReLU(inplace=True)               #ReLU激活,inplace=True表示直接覆蓋掉ReLU目標的內存空間
4 out = layer(x)
5 print(out.shape)                            #torch.Size([1, 16, 14, 14])

4.2F.relu()函數式接口

1 x = out                   #torch.Size([1, 16, 14, 14])
2 
3 out = F.relu(x)
4 print(x.shape)            #torch.Size([1, 16, 14, 14])

5.Batch Norm

歸一化可以使代價函數平均起來看更對稱,使用梯度下降法更方便。

通常分為兩步:調整均值、方差歸一化

5.1Batch Norm

一個Batch的圖像數據shape為[樣本數N, 通道數C, 高度H, 寬度W],將其最后兩個維度flatten,得到的是[N, C, H*W],標准的Batch Normalization就是在通道channel這個維度上進行移動,對所有樣本的所有值求均值和方差,所以有幾個通道,得到的就是幾個均值和方差。

eg.[6, 3, 784]會生成[3],代表當前batch中每一個channel的特征均值,3個channel有3個均值和3個方差,只保留了channel維度,所以是[3]。

5.2Layer Norm

樣本N的維度上滑動,對每個樣本的所有通道的所有值求均值和方差,所以一個Batch有幾個樣本實例,得到的就是幾個均值和方差。

eg.[6, 3, 784]會生成[6]

5.3Instance Norm

在樣本N和通道C兩個維度上滑動,對Batch中的N個樣本里的每個樣本n,和C個通道里的每個樣本c,其組合[n, c]求對應的所有值的均值和方差,所以得到的是N*C個均值和方差。

5.4Batch Norm詳解

輸入數據是6張3通道784個像素點的數據,將其分到三個通道上,在每個通道上也就是[6, 784]的數據,然后分別得到和通道數一樣多的統計數據均值μ和方差σ,將每個像素值減去μ除以σ也就變換到了接近N(0,1)的分布,后面又使用參數β和γ將其變換到接近N(β,γ)的分布。

Tip:μ和σ只是樣本中的統計數據,是沒有梯度信息的,不過會保存在運行時參數里。而γ和β屬於要訓練的參數,他們是有梯度信息的。

正式計算:

nn.BatchNorm1d()

 1 import torch
 2 from torch import nn
 3 
 4 x = torch.rand(100, 16, 784)     #100張16通道784像素點的數據,均勻分布
 5 
 6 layer = nn.BatchNorm1d(16)       #傳入通道數,因為H和W已經flatten過了,所以用1d
 7 out = layer(x)
 8 
 9 print(layer.running_mean)
10 #tensor([0.0499, 0.0501, 0.0501, 0.0501, 0.0501, 0.0502, 0.0500, 0.0499, 0.0499,
11 #        0.0501, 0.0500, 0.0500, 0.0500, 0.0501, 0.0500, 0.0500])
12 print(layer.running_var)
13 #tensor([0.9083, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083,
14 #        0.9083, 0.9083, 0.9083, 0.9084, 0.9084, 0.9083, 0.9083])

Tip:layer.running_mean和layer.running_var得到的是全局的均值和方差,不是當前Batch上的,只不過這里只跑了一個Batch而已所以它就是這個Batch上的。

nn.BatchNorm2d()

 1 import torch
 2 from torch import nn
 3 
 4 x = torch.rand(1, 16, 7, 7)     #1張16通道的7*7的圖像
 5 
 6 layer = nn.BatchNorm2d(16)      #傳入通道數(必須和上面的通道數目一致)
 7 out = layer(x)
 8 
 9 print(out.shape)                #torch.Size([1, 16, 7, 7])
10 print(layer.running_mean)        
11 print(layer.running_var)      
12 print(layer.weight.shape)       #torch.Size([16])對應上面的γ
13 print(layer.bias.shape)         #torch.Size([16])對應上面的β
14 print(vars(layer))              #查看網絡中一個層上的所有參數
15 # {'training': True, 
16 #   '_parameters': 
17 #       OrderedDict([('weight', Parameter containing:
18 #                               tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], requires_grad=True)), 
19 #                   ('bias', Parameter containing:
20 #                               tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True))]), 
21 #   '_buffers': 
22 #       OrderedDict([('running_mean', tensor([0.0527, 0.0616, 0.0513, 0.0488, 0.0484, 0.0510, 0.0590, 0.0459, 0.0448, 0.0586, 0.0535, 0.0464, 0.0581, 0.0481, 0.0420, 0.0549])), 
23 #                   ('running_var', tensor([0.9089, 0.9075, 0.9082, 0.9079, 0.9096, 0.9098, 0.9079, 0.9086, 0.9081, 0.9075, 0.9052, 0.9081, 0.9093, 0.9075, 0.9086, 0.9073])), 
24 #                   ('num_batches_tracked', tensor(1))]), 
25 # '_backward_hooks': OrderedDict(), 
26 # '_forward_hooks': OrderedDict(), 
27 # '_forward_pre_hooks': OrderedDict(), 
28 # '_state_dict_hooks': OrderedDict(), 
29 # '_load_state_dict_pre_hooks': OrderedDict(), 
30 # '_modules': OrderedDict(), 
31 # 'num_features': 16, 
32 # 'eps': 1e-05, 
33 # 'momentum': 0.1, 
34 # 'affine': True, 
35 # 'track_running_stats': True}

Tip:

  • layer.weight和layer.bias是當前batch上的;
  • 如果在定義層時使用了參數affine=False,那么就是固定γ=1和β=0不自動學習,這時參數layer.weight和layer.bias將是None。

5.5Train和Test

類似於Dropout,Batch Normalization在訓練和測試時的行為不同。

測試模式下,$\mu$和$\sigma ^{2}$使用訓練集得到的全局$\mu$和$\sigma ^{2}$,歸一化前調用layer.eval()設置Test模式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM