1. 卷積層
1.1 torch.nn.Conv2d() 類式接口
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
:零填充
import torch
import torch.nn as nn
# batch, channel, height, width
x = torch.rand(1,1,28,28)
# in_channel(和上面x中的channel數量一致), out_channel(kernel個數)
layer = nn.Conv2d(1,3,kernel_size=3,stride=2,padding=1)
# 或者直接out=layer(x)
out = layer.forward(x)
# torch.Size([1, 3, 14, 14]) 1指一張圖片,3指三個kernel,14*14指圖片大小
print(out.shape)
-
第五行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個數,不同的通道共用一個偏置。
-
1.2 F.conv2d() 函數式接口
-
PyTorch里一般小寫的都是 函數式的接口,相應的大寫的是類式接口。
-
函數式的更加 low-level 一些,如果不需要做特別復雜的配置 只要用 類式接口即可。
import torch
from torch.nn import functional as F
#手動定義卷積核(weight)和偏置
w = torch.rand(16, 3, 5, 5) # 16種3通道的5*5卷積核
b = torch.rand(16)
#定義輸入樣本
x = torch.randn(1, 3, 28, 28) # 1張3通道的28*28的圖像
#2D卷積得到輸出
# 1張圖片,16個輸出(16個filter),f = (28+2-5+1)
out = F.conv2d(x, w, b, stride=1, padding=1)
print(out.shape) # torch.Size([1, 16, 26, 26])
out = F.conv2d(x, w, b, stride=2, padding=2)
print(out.shape) # torch.Size([1, 16, 14, 14])
2. 池化層Pooling(下采樣)
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的圖像,在此基礎上做池化。
2.1 torch.MaxPool2d() 類式接口
# Maxpool
x = out # torch.Size([1, 16, 14, 14])
layer = nn.MaxPool2d(2,stride=2) # 池化層(池化核為2*2,步長為2),最大池化
out = layer(x)
print(out.shape) # torch.Size([1, 16, 7, 7])
2.2 F.avg_pool2d() 函數式接口
x = out # torch.Size([1, 16, 14, 14])
out = F.avg_pool2d(x, 2, stride=2)
print(out.shape) # torch.Size([1, 16, 7, 7])
Tip:池化后通道數不變。
3. upsample(上采樣)
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。
x = out # torch.Size([1, 16, 7, 7])
out = F.interpolate(x, scale_factor=2, mode='nearest') # 向上采樣,放大2倍,最近插值
print(out.shape) # torch.Size([1, 16, 14, 14])
4. RELU激活函數
4.1 torch.nn.RELU() 類式接口
x = out # torch.Size([1, 16, 14, 14])
layer = nn.ReLU(inplace=True) # ReLU激活,inplace=True表示直接覆蓋掉ReLU目標的內存空間
out = layer(x)
print(out.shape) # torch.Size([1, 16, 14, 14])
4.2 F.relu() 函數式接口
x = out # torch.Size([1, 16, 14, 14])
out = F.relu(x, inplace=True)
print(x.shape) # torch.Size([1, 16, 14, 14])
5. Batch Norm
-
歸一化:使代價函數平均起來看更對稱,使用梯度下降法更方便。
-
通常分為兩步:調整均值、方差歸一化
5.1 Batch 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.2 Layer Norm
-
樣本N的維度上滑動,對每個樣本的 所有通道 的 所有值 求均值和方差
-
一個Batch有幾個樣本實例,得到的就是幾個均值和方差。
-
eg. [6, 3, 784]會生成[6]
5.3 Instance Norm
- 在樣本N和通道C兩個維度上滑動,對Batch中的N個樣本里的每個樣本n,和C個通道里的每個樣本c,其組合[n, c]求對應的所有值的均值和方差,所以得到的是N*C個均值和方差。
5.4 Batch Norm詳解
-
輸入數據:6張3通道784個像素點的數據,將其分到三個通道上,在每個通道上也就是[6, 784]的數據
-
然后分別得到和通道數一樣多的統計數據 均值\(\mu\) 和 方差\(\sigma\)
-
將每個像素值減去 \(\mu\) 除以 \(\sigma\) 也就變換到了接近 \(N(0,1)\) 的分布
-
后面又使用參數 \(\beta\) 和 \(\gamma\) 將其變換到接近 \(N(β,γ)\) 的分布。
Tip:
-
\(\mu\) 和 \(\sigma\) 只是樣本中的統計數據,是沒有梯度信息的,不過會保存在運行時參數里。
-
而 \(\gamma\) 和 \(\beta\) 屬於要訓練的參數,他們是有梯度信息的。
正式計算:
nn.BatchNorm1d()
import torch
from torch import nn
x = torch.rand(100, 16, 784) # 100張16通道784像素點的數據,均勻分布
layer = nn.BatchNorm1d(16) # 傳入通道數,因為H和W已經flatten過了,所以用1d
out = layer(x)
print(layer.running_mean.shape ,layer.running_mean)
#torch.Size([16]) tensor([0.0499, 0.0501, 0.0501, 0.0501, 0.0501, 0.0502, 0.0500, 0.0499, 0.0499,
# 0.0501, 0.0500, 0.0500, 0.0500, 0.0501, 0.0500, 0.0500])
print(layer.running_var.shape,layer.running_var)
#tensor([0.9083, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083, 0.9084, 0.9083, 0.9083,
# 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()
import torch
from torch import nn
x = torch.rand(1, 16, 7, 7) # 1張16通道的7*7的圖像
layer = nn.BatchNorm2d(16) # 傳入通道數(必須和上面的通道數目一致)
out = layer(x)
print(out.shape) # torch.Size([1, 16, 7, 7])
print(layer.running_mean) # running-μ
print(layer.running_var) # running-σ^2
print(layer.weight.shape) # torch.Size([16]),對應上面的γ
print(layer.bias.shape) # torch.Size([16]),對應上面的β
print(vars(layer)) # 查看網絡中一個層上的所有參數
# {'training': True,
# '_parameters':
# OrderedDict([('weight', Parameter containing:
# tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], requires_grad=True)),
# ('bias', Parameter containing:
# tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], requires_grad=True))]),
# '_buffers':
# 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])),
# ('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])),
# ('num_batches_tracked', tensor(1))]),
# '_backward_hooks': OrderedDict(),
# '_forward_hooks': OrderedDict(),
# '_forward_pre_hooks': OrderedDict(),
# '_state_dict_hooks': OrderedDict(),
# '_load_state_dict_pre_hooks': OrderedDict(),
# '_modules': OrderedDict(),
# 'num_features': 16,
# 'eps': 1e-05,
# 'momentum': 0.1,
# 'affine': True,
# 'track_running_stats': True}
Tip:
-
layer.weight 和 layer.bias是當前batch上的;
-
如果在定義層時使用了參數
affine=False
,那么就是固定 \(\gamma=1\) 和 \(\beta=0\) 不自動學習,這時參數layer.weight
和layer.bias
將是None。
5.5 Train和Test
-
類似於Dropout,Batch Normalization在訓練和測試時的行為不同。
-
測試模式下,\(\mu\) 和 \(\sigma_2\) 使用訓練集得到的 全局\(\mu\) 和 \(\sigma_2\)
- 歸一化前調用
layer.eval()
設置Test模式。
- 歸一化前調用
5.6 使用Batch Norm好處
-
收斂更快(converge faster)
-
表現的更好(Better performance)
-
更穩定
-
Stable
-
larger learning rate(超參數沒有那么敏感)
-