5、卷積神經(jīng)網(wǎng)絡(luò)
5.1、卷積
5.1.1、理論部分
全連接層后,卷積層出現(xiàn)的意義?
一個(gè)足夠充分的照片數(shù)據(jù)集,輸入,全連接層參數(shù),GPU成本,訓(xùn)練時(shí)間是巨大的。
(convolutional neural networks,CNN)是機(jī)器學(xué)習(xí)利用自然圖像中一些已知結(jié)構(gòu)的創(chuàng)造性方法,需要更少的參數(shù),在處理圖像和其他類型的結(jié)構(gòu)化數(shù)據(jù)上各類成本,效果,可行性普遍優(yōu)于全連接層。
卷積層做了什么?
將輸入和核矩陣進(jìn)行互相關(guān)運(yùn)算,加上偏移后得到輸出。
圖片中找模式的原則
- 平移不變性
- 局部性
對(duì)全連接層使用如上原則得到卷積層。
(詳細(xì)待補(bǔ)充)
二維卷積層
Y = X ★ W + b Y = X ★ W + b Y=X★W+b
輸入 X X X: n h × n w n_h × n_w nh?×nw?
圖中,h:高、w:寬、輸入大小 n = 3。
核 W W W: k h × k w k_h × k_w kh?×kw?
圖中,卷積核大小 k = 2,超參數(shù)。
偏差 b∈ R
輸出 Y Y Y: ( n h ? k h + 1 ) × ( n w ? k w + 1 ) ( n_h - k_h + 1)×(n_w - k_w + 1) (nh??kh?+1)×(nw??kw?+1)
圖中 (3-2 +1)*(3-2 +1) = 4 ,計(jì)算的是 Y 的形狀。
★:二維交叉操作子 | 外積
W 和 b是可學(xué)習(xí)的參數(shù)
卷積效果舉例
5.1.2、代碼實(shí)現(xiàn)
(1)實(shí)現(xiàn)互相關(guān)運(yùn)算
卷積運(yùn)算 ≠ 互相關(guān)運(yùn)算
卷積運(yùn)算:在卷積運(yùn)算中,核心(也稱為濾波器)在進(jìn)行滑動(dòng)時(shí),會(huì)被翻轉(zhuǎn)(180度旋轉(zhuǎn))后與輸入數(shù)據(jù)進(jìn)行逐元素相乘,并將乘積求和作為輸出。這意味著核心的權(quán)重是翻轉(zhuǎn)的。在卷積運(yùn)算中,處理核心的邊界像素會(huì)被覆蓋,所以輸出的大小通常會(huì)小于輸入的大小。
互相關(guān)運(yùn)算:在互相關(guān)運(yùn)算中,核心與輸入數(shù)據(jù)進(jìn)行逐元素相乘,并將乘積求和作為輸出,但核心不進(jìn)行翻轉(zhuǎn)?;ハ嚓P(guān)運(yùn)算不會(huì)覆蓋核心的邊界像素,所以輸出的大小與輸入的大小通常是一致的。
在深度學(xué)習(xí)中,人們通常使用卷積運(yùn)算的術(shù)語(yǔ)來描述這種操作,盡管實(shí)際實(shí)現(xiàn)可能使用了互相關(guān)運(yùn)算。卷積運(yùn)算和互相關(guān)運(yùn)算在數(shù)學(xué)上的操作相似,但在處理核心邊界像素時(shí)存在微小的差異。在實(shí)際深度學(xué)習(xí)應(yīng)用中,這兩個(gè)術(shù)語(yǔ)通??梢曰Q使用。
import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X, K): #@save
"""計(jì)算二維互相關(guān)運(yùn)算"""
h, w = K.shape
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
#點(diǎn)積求和
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
驗(yàn)證運(yùn)算結(jié)果
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
corr2d(X, K)
result:
tensor([[19., 25.], [37., 43.]])
實(shí)現(xiàn)二維卷積層
class Conv2D(nn.Module):
def __init__(self,kernel_size):
super().__init__()
self.weight =nn.Parameter(torch.rand(kernel_size))
self.bias = nn.Parameter(torch.zeros(1))
def forward(sekf, x):
return corr2d(x,self.weight) + self.bias
(2)學(xué)習(xí)由X生成Y卷積核
#一個(gè)輸入通道、一個(gè)輸出通道,不使用偏置
conv2d = nn.Conv2d(1,1,kernel_size=(1,2),bias =False)
X = X.reshape((1,1,6,8))
Y = Y.reshape((1,1,6,7))
for i in range(10):
Y_hat = conv2d(X)
l = (Y_hat - Y) **2
conv2d.zero_grad()
l.sum().backward()
conv2d.weight.data[:] -=3e-2 * conv2d.weight.grad
if(i + 1)% 2 == 0:
print(f'batch{i + 1}, loss {l.sum():.3f}')
所學(xué)卷積核權(quán)重
conv2d.weight.data.reshape((1,2))
tensor([[ 1.0084, -0.9816]])
5.1.3、邊緣檢測(cè)
利用卷積層檢測(cè) 圖像中的不同邊緣
輸入
X = torch.ones((6,8))
X[:, 2:6] =0
X
tensor([[1., 1., 0., 0., 0., 0., 1., 1.], [1., 1., 0., 0., 0., 0., 1., 1.], [1., 1., 0., 0., 0., 0., 1., 1.], [1., 1., 0., 0., 0., 0., 1., 1.], [1., 1., 0., 0., 0., 0., 1., 1.], [1., 1., 0., 0., 0., 0., 1., 1.]])
核矩陣
K = torch.tensor([[1,-1]])
輸出
Y = corr2d(X,K)
Y
tensor([[ 0., 1., 0., 0., 0., -1., 0.], [ 0., 1., 0., 0., 0., -1., 0.], [ 0., 1., 0., 0., 0., -1., 0.], [ 0., 1., 0., 0., 0., -1., 0.], [ 0., 1., 0., 0., 0., -1., 0.], [ 0., 1., 0., 0., 0., -1., 0.]])
只能檢測(cè)垂直邊緣
Y = corr2d(X.t(),K) Y
tensor([[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]])
將核矩陣一起轉(zhuǎn)置
Y = corr2d(X.t(),K.t()) Y
水平邊緣檢測(cè)可行。
tensor([[ 0., 0., 0., 0., 0., 0.], [ 1., 1., 1., 1., 1., 1.], [ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.], [-1., -1., -1., -1., -1., -1.], [ 0., 0., 0., 0., 0., 0.]])
5.2、填充和步幅
5.2.1、理論部分
填充操作
更大的卷積核可以更快地減小輸出大小。
如果不想結(jié)果太小,也可以通過填充實(shí)現(xiàn)輸出更大尺寸的X,實(shí)現(xiàn)控制輸出形狀的減少量。
填充 p h p_h ph?行 p w p_w pw?列,輸出形狀:
( n h ? k h + p h + 1 ) × ( n w ? k w + p w + 1 ) (n_h -k_h +p_h +1)×(n_w - k_w + p_w +1) (nh??kh?+ph?+1)×(nw??kw?+pw?+1)
填充的是外圍的一層:上下各填充 p h p_h ph?,寬度填充同理。
在卷積層代碼中有個(gè)參數(shù)叫做
padding=
,假設(shè)padding=p,代入上述公式:( n h ? k h + p ? 2 + 1 ) × ( n w ? k w + p ? 2 + 1 ) (n_h -k_h +p*2 +1)×(n_w - k_w + p*2 +1) (nh??kh?+p?2+1)×(nw??kw?+p?2+1)
計(jì)算錯(cuò)誤就是把 p h p_h ph?和 p p p混為一談了。
通常取 p h = k h ? 1 , ??? p w = k w ? 1 p_h = k_h -1, \ \ \ p_w =k_w -1 ph?=kh??1,???pw?=kw??1
- k h k_h kh?奇數(shù):上下兩側(cè)填充 p h / 2 p_h/2 ph?/2
- k h k_h kh?偶數(shù):上側(cè)填充 ? p h / 2 ? ?p_h/2? ?ph?/2?下側(cè)填充 ? p h / 2 ? ?p_h/2? ?ph?/2?
步幅
步幅指行/列滑動(dòng)步長(zhǎng)。
設(shè)置步幅的效果?
成倍減少輸出形狀。
下圖為高3寬2步幅示意圖:
(圖片來自 《DIVE INTO DEEP LEARNING》)
給定步幅,高度 s h s_h sh?寬度 s w s_w sw?,輸出形狀:
? ( n h ? k h + p h + s h ) / s h ? × ? ( n w ? k w + p w + s w ) / s w ? ?(n_h - k_h + p_h + s_h)/s_h? ×?(n_w - k_w + p_w + s_w)/s_w? ?(nh??kh?+ph?+sh?)/sh??×?(nw??kw?+pw?+sw?)/sw??
如果輸入高度寬度可被步幅整除,形狀為:
( n h / s h ) × ( n w / s w ) (n_h / s_h)×(n_w / s_w) (nh?/sh?)×(nw?/sw?)
5.2.2、代碼實(shí)現(xiàn)
填充、步幅是卷積層超參數(shù)。
所有側(cè)邊填充一個(gè)像素
import torch
from torch import nn
def comp_conv2d(conv2d, X):
X = X.reshape((1,1) + X.shape)
Y =conv2d(X)
return Y.reshape(Y.shape[2:])
conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1)
X= torch.rand(size=(8,8))
comp_conv2d(conv2d,X).shape
填充相同高度寬度
import torch
from torch import nn
def comp_conv2d(conv2d, X):
X = X.reshape((1,1) + X.shape)
#執(zhí)行一次卷積操作
Y =conv2d(X)
return Y.reshape(Y.shape[2:])
#padding=1 在輸入數(shù)據(jù)的邊界填充一行和一列的零值
conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1)
X= torch.rand(size=(8,8))
comp_conv2d(conv2d,X).shape
torch.Size([8, 8])
不同高度寬度
conv2d = nn.Conv2d(1,1,kernel_size=(5,3),padding=(2,1))
comp_conv2d(conv2d,X).shape
torch.Size([8, 8])
增設(shè)步幅,其寬高為2
conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1,stride =2)
comp_conv2d(conv2d,X).shape
torch.Size([4, 4])
成倍縮小。
5.3、多輸入多輸出通道
5.3.1、理論部分
彩色RGB圖片,是三通道輸入數(shù)據(jù)。
每個(gè)通道都有一個(gè)卷積核,結(jié)果為各通道卷積的和。
1×1卷積層
不識(shí)別空間,用途是融合通道。
二維卷積層(多通道)
Y = X ★ W + B Y = X ★ W + B Y=X★W+B
輸入 X X X: c i × n h × n w c_i × n_h × n_w ci?×nh?×nw?
c i c_i ci?輸入通道數(shù)、h高、w寬、輸入大小 n。
核 W W W: c o × c i × k h × k w c_o × c_i × k_h × k_w co?×ci?×kh?×kw?
c o c_o co?輸出通道數(shù)、卷積核大小 k。其中, c o c_o co?是卷積層的超參數(shù)。
偏差 B B B : c o × c i c_o × c_i co?×ci?
一共有 c o × c i c_o × c_i co?×ci?個(gè)卷積核 每個(gè)卷積核都有一個(gè)偏差
輸出 Y Y Y: c o × m h × m w c_o × m_h × m_w co?×mh?×mw?
m h ? m w m_h \ m_w mh??mw?大小與 填充p、核大小k有關(guān)。
★:二維交叉操作子 | 外積
怎么理解每個(gè)輸出通道有獨(dú)立的三維卷積核?
具有三個(gè)維度:高度、寬度和通道數(shù)。
通道變化的意義
通道變化是非線性變換。
- 通道變大的意義: 增加通道數(shù)可以增強(qiáng)網(wǎng)絡(luò)的表達(dá)能力和特征提取能力。通過引入更多的卷積核,網(wǎng)絡(luò)可以學(xué)習(xí)更多不同尺度和不同抽象級(jí)別的特征。它們有助于捕獲圖像的細(xì)節(jié)和紋理,以及高級(jí)別的抽象特征。在更高的層次上學(xué)習(xí)到更豐富的特征。
- 通道變小的意義: 減小通道數(shù)可以降低網(wǎng)絡(luò)的計(jì)算成本和參數(shù)數(shù)量,同時(shí)也可以起到特征壓縮和維度約減的作用。通過將較大通道數(shù)的特征圖進(jìn)行降維,從而實(shí)現(xiàn)網(wǎng)絡(luò)的輕量化。還可以起到正則化的作用,減少過擬合的風(fēng)險(xiǎn)。在某些情況下,通道數(shù)的減小也可以幫助網(wǎng)絡(luò)更好地學(xué)習(xí)到一些共享的、更為重要的特征。
5.3.2、代碼實(shí)現(xiàn)
(1)實(shí)現(xiàn)多通道互相關(guān)運(yùn)算
定義多通道輸入
import torch
from d2l import torch as d2l
#先遍歷“X”和“K”的第0個(gè)維度(通道維度),再把它們加在一起
def corr2d_multi_in(X,K):
return sum(d2l.corr2d(x,k) for x,k in zip(X,K))
多通道第零維度的幾何意義?
圖中X第零維度有兩組,幾何上就是通道數(shù)。
X :(tensor([[[0., 1., 2.], [3., 4., 5.], [6., 7., 8.]], [[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]]),
定義X,K
# X 2*3*3
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
#K 2*2*2
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
X,K,corr2d_multi_in(X, K)
(tensor([[[0., 1., 2.], [3., 4., 5.], [6., 7., 8.]], [[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]]), tensor([[[0., 1.], [2., 3.]], [[1., 2.], [3., 4.]]]), tensor([[ 56., 72.], [104., 120.]]))
定義多通道輸出
def corr2d_multi_in_out(X,K):
# 使用 PyTorch 的 torch.stack 函數(shù),它將一組張量沿著指定的維度(這里是維度0)進(jìn)行堆疊,生成一個(gè)新的張量。
return torch.stack([corr2d_multi_in(X,k) for k in K],0)
# K+1 K的每個(gè)值加一,K規(guī)模擴(kuò)成了原來3倍。
K = torch.stack((K,K+1,K+2),0)
K,K.shape
(tensor([[[[0., 1.], [2., 3.]], [[1., 2.], [3., 4.]]], [[[1., 2.], [3., 4.]], [[2., 3.], [4., 5.]]], [[[2., 3.], [4., 5.]], [[3., 4.], [5., 6.]]]]), torch.Size([3, 2, 2, 2]))
返回值那一行為什么用小k對(duì)應(yīng)X,多通道輸入那里不是用的大K對(duì)應(yīng)X,然后第零維度展開,抽出x,k對(duì)應(yīng)計(jì)算嗎?
K擴(kuò)了三倍,所以用小k規(guī)模和原來的K相當(dāng),因此X 對(duì)應(yīng)擴(kuò)充前的K,擴(kuò)充后的小k。
corr2d_multi_in_out(X,K)
tensor([[[ 56., 72.], [104., 120.]], [[ 76., 100.], [148., 172.]], [[ 96., 128.], [192., 224.]]])
(2)實(shí)現(xiàn)1*1卷積核
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape
c_o = K.shape[0]
X = X.reshape((c_i, h * w))
K = K.reshape((c_o, c_i))
# 全連接層中的矩陣乘法
Y = torch.matmul(K, X)
return Y.reshape((c_o, h, w))
X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
# 進(jìn)行斷言,驗(yàn)證使用 1x1 卷積操作得到的輸出 Y1 與多通道卷積操作得到的輸出 Y2 是否非常接近,以確保兩種方法的結(jié)果一致
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6
5.4、池化層 | 匯聚層
5.4.1、理論部分
最大池化,每個(gè)窗口最強(qiáng)的模式信號(hào),它針對(duì)卷積對(duì)空間位置敏感(邊緣檢測(cè)案例),允許輸入有一定的偏移。
也有平均池化層。
特點(diǎn)
- 具有填充,步幅;
- 沒有可學(xué)習(xí)的參數(shù);
- 輸出通道 = 輸入通道,一一對(duì)應(yīng)。
5.4.2、代碼實(shí)現(xiàn)
池化層向前傳播
import torch
from torch import nn
from d2l import torch as d2l
def pool2d(X, pool_size, mode='max'):
p_h, p_w = pool_size
Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
if mode == 'max':
Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
return Y
驗(yàn)證最大池化層
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]]) pool2d(X, (2, 2))
tensor([[4., 5.], [7., 8.]])
驗(yàn)證平均池化層
pool2d(X, (2,2), 'avg')
tensor([[2., 3.], [5., 6.]])
使用內(nèi)置的最大池化層
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
X
tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]]]])
pool2d = nn.MaxPool2d(3, padding=1, stride=2)#等價(jià)于nn.MaxPool2d((3,3), padding=(1,1), stride=(2,2))
pool2d(X)
tensor([[[[ 5., 7.], [13., 15.]]]])
pool2d = nn.MaxPool2d((2, 3), stride=(2, 3), padding=(0, 1))
pool2d(X)
tensor([[[[ 5., 7.], [13., 15.]]]])
驗(yàn)證多通道
匯聚層在每個(gè)輸入通道上單獨(dú)運(yùn)算,輸出通道數(shù)與輸入通道數(shù)相同。
拼接上
torch.cat
和torch.stack
的區(qū)別torch.cat
是在現(xiàn)有維度上進(jìn)行拼接。文章來源:http://www.zghlxwxcb.cn/news/detail-663820.html
torch.stack
用于創(chuàng)建一個(gè)新的維度,并將多個(gè)張量沿該新維度進(jìn)行堆疊。文章來源地址http://www.zghlxwxcb.cn/news/detail-663820.html
# 將兩個(gè)張量 X, X + 1 進(jìn)行拼接
X = torch.cat((X, X + 1), 1)
X
tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]], [[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 9., 10., 11., 12.], [13., 14., 15., 16.]]]])
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
pool2d(X)
tensor([[[[ 5., 7.], [13., 15.]], [[ 6., 8.], [14., 16.]]]])
到了這里,關(guān)于卷積神經(jīng)網(wǎng)絡(luò)——上篇【深度學(xué)習(xí)】【PyTorch】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!