一、卷積神經(jīng)網(wǎng)絡(luò)簡介
卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Network,CNN)是一種深度學習神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),主要用于圖像識別、計算機視覺等領(lǐng)域。該結(jié)構(gòu)在處理圖像等高維數(shù)據(jù)時表現(xiàn)出色,因為它具有共享權(quán)重和局部感知的特點,一方面減少了權(quán)值的數(shù)量使得網(wǎng)絡(luò)易于優(yōu)化,另一方面降低了模型的復雜度,也就是減小了過擬合的風險。
卷積神經(jīng)網(wǎng)絡(luò)主要由卷積層(Convolutional layer)、池化層(Pooling layer)、全連接層(Fully connected layer)和激活函數(shù)(Activation function)等組成。其中,卷積層是CNN的核心部分,它通過卷積操作提取輸入圖像的特征,并將這些特征作為下一層的輸入。池化層則用于降采樣,可以減少卷積層輸出的特征圖的大小,從而減少網(wǎng)絡(luò)參數(shù)和計算量。全連接層則用于將卷積層和池化層的輸出連接起來,以便最終進行分類和預測。
卷積神經(jīng)網(wǎng)絡(luò)的訓練主要是通過反向傳播算法(Back-propagation)來更新網(wǎng)絡(luò)中的權(quán)重(類似于BP神經(jīng)網(wǎng)絡(luò)),從而使得網(wǎng)絡(luò)能夠逐步學習到輸入數(shù)據(jù)的特征,并在最終的分類或預測任務(wù)中得到較好的性能。
目前,卷積神經(jīng)網(wǎng)絡(luò)已經(jīng)在圖像分類、物體識別、人臉識別、自然語言處理等領(lǐng)域取得了非常出色的成果,是現(xiàn)代深度學習領(lǐng)域的重要組成部分。
二、卷積層
在數(shù)學中,兩個函數(shù)(比如?f?和 g)之間的“卷積”被定義為:
也就是說,卷積是把一個函數(shù)“翻轉(zhuǎn)”并移位x,測量f?和 g?之間的重疊。當為離散對象時,積分就變成求和。例如,對于索引為Z的、平方可和的、無限維向量集合中抽取的向量,我們可得到如下定義:
對于二維張量,則為?f?的索引(a,b)和?g?的索引(i-a, j-b)上的對應(yīng)加和:
這里需要說明的是,在卷積神經(jīng)網(wǎng)絡(luò)中,卷積層的實現(xiàn)方式實際上是數(shù)學中定義的互相關(guān) (cross-correlation)運算,與數(shù)學分析中的卷積定義有所不同,使用互相關(guān)運算作為卷積的定義。互相關(guān)(Cross-Correlation)是一個衡量兩個序列相關(guān)性的函數(shù),通常是用滑動窗口的點積計算來實現(xiàn),而卷積則需要將濾波器經(jīng)過反轉(zhuǎn)。
2.1 一維卷積
一維卷積經(jīng)常用在信號處理中,用于計算信號的延遲累積。假設(shè)一個信號發(fā)生器每個時刻t產(chǎn)生一個信號,其信息的衰減率為,即在k-1個時間步長后,信息為原來的倍。而在時刻t收到的信號為當前時刻產(chǎn)生的信息和以前時刻延遲信息的疊加,即:
其中,被稱為濾波器(filter)或卷積核(convolution kernnel)。
2.2 二維卷積?
在圖像處理中,圖像是以二維矩陣的形式輸入到神經(jīng)網(wǎng)絡(luò)中,給定一個圖像()和一個濾波器(),且有,則其卷積為:
具體計算過程如下圖所示:
通常我們將一個輸入信息X和濾波器W的二維卷積定義為。假設(shè)卷積核的高和寬分別為和,則將稱其為卷積,如卷積,就是指卷積核的高為3,寬為5。在卷積神經(jīng)網(wǎng)絡(luò)中,一個卷積算子除了上面描述的卷積過程外,還包括加上偏置項的操作。
當卷積核尺寸大于1時,經(jīng)過一次卷積之后,輸出特征圖的尺寸會小于輸入圖片尺寸,輸出特征圖尺寸計算方法有:
2.2.1 填充
如果經(jīng)過多次卷積,輸出圖片尺寸會不斷減小,為避免卷積之后圖片尺寸變小,通常會在圖片的外圍進行填充(padding),如下圖所示:
若沿著圖片高度方向,在第一行之前填充行,在最后一行之后填充行;沿著圖片寬度方向,在第一列之前填充列,在最后一列之后填充列,則填充之后的圖片經(jīng)過大小為的卷積核操作之后,輸出圖片的尺寸為:
在卷積計算過程中,通常會在高度或?qū)挾鹊膬蓚?cè)采取等量填充,即要求:
則變換后的尺寸變?yōu)椋?/p>
卷積核通常用1,3,5,7這樣的奇數(shù)。如果使用的填充大小為:
則卷積之后圖像尺寸不變。例如,當卷積核大小為5,padding大小為2時,卷積之后圖像尺寸就不會改變。
為加深理解,假設(shè)創(chuàng)建一個高度和寬度為3的二維卷積層,并在所有側(cè)邊填充一個像素,給定高度和寬度為8的輸入,則輸出的高度和寬度也是8:
import torch
from torch import nn
# 為了方便起?,我們定義了一個計算卷積層的函數(shù)。
# 此函數(shù)初始化卷積層權(quán)重,并對輸入和輸出提高和縮減相應(yīng)的維數(shù)
def comp_conv2d(conv2d, X):
# 這里的(1,1)表示批量大小和通道數(shù)都是1
X = X.reshape((1, 1) + X.shape)
Y = conv2d(X)
# 省略前兩個維度:批量大小和通道
return Y.reshape(Y.shape[2:])
# 請注意,這里每邊都填充了1行或1列,因此總共添加了2行或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape
## 結(jié)果
torch.Size([8, 8])
當卷積核的高度和寬度不同時,我們可以填充不同的高度和寬度,使輸出和輸入具有相同的高度和寬度。若使用高度為5,寬度為3的卷積核,高度和寬度兩邊的填充則分別為2和1:
conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape
## 結(jié)果
torch.Size([8, 8])
2.2.2 步幅
步幅(stride)也會影響輸出圖片的尺寸,下圖是步幅為2的卷積過程,卷積核在圖片上移動時,每次移動大小為2 個像素點:
?當寬和高方向的步幅分別為和時,輸出特征圖尺寸為:
2.2.3 代碼實現(xiàn)
我們可以使用torch庫實現(xiàn)以上過程。設(shè)定如下輸入數(shù)據(jù)與核函數(shù),輸出數(shù)據(jù)可表示為:
import torch
from torch import nn
def corr2d(X, K): #@save
"""計算二維互相關(guā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]):
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
輸入上圖中的張量x和卷積核張量k,可以得到圖上的輸出張量:
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)
## 輸出結(jié)果
tensor([[19., 25.],
[37., 43.]])
2.2.4 感受野
在二維卷積中,隨著卷積層數(shù)的提高,輸出特征圖上每個點的數(shù)值代表的信息會更多。在一層卷積中,輸出特征圖上每個點的數(shù)值是由輸入圖片上大小為的區(qū)域中元素與卷積核每個元素相乘再相加得到的,所以輸入圖像上該區(qū)域內(nèi)每個元素數(shù)值的改變都會影響輸出點的像素值。我們可將這個區(qū)域叫做輸出特征圖上對應(yīng)點的感受野。以卷積為例,對應(yīng)的感受野為大小的區(qū)域:
?而卷積層數(shù)為2時,感受野的大小會增加到,如下圖所示:
2.2.5 連接
在卷積層中每一個神經(jīng)元都只和下一層中局部的神經(jīng)元相連,構(gòu)成一個局部連接網(wǎng)絡(luò)。使用卷積層代替全連接層后,可以使得層和層之間的連接數(shù)大大減少,如圖所示(一維與二維):
?2.2.6 通道?
我們熟知的圖像一般包含了三個通道(三個顏色),稱為RGB。實際上,圖像并不是二維張量,而是一個由高度、寬度和顏色組成的三位張量,如個像素,前兩個軸與像素的空間位置有關(guān),而第三個軸可以看作每個像素的多維表示。
(1)多輸入通道場景
若要計算卷積的輸出結(jié)果,卷積核的形式也會發(fā)生變化,假設(shè)輸入圖片的通道數(shù)為,輸入數(shù)據(jù)的形狀為,計算過程如下:
- 對每個通道分別涉及一個2維數(shù)組作為卷積核,卷積核數(shù)組的形狀是;
- 對任一通道,分別用大小為的卷積核在大小為二維數(shù)組上做卷積;
- 將這個通道的計算結(jié)果相加,得到的是一個形狀為的二維數(shù)組。
通過torch庫實現(xiàn)以下兩個輸入通道之間的運算:
import torch
from d2l import torch as d2l
def corr2d_multi_in(X, K):
# 先遍歷“X”和“K”的第0個維度(通道維度),再把它們加在一起
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
將上圖中的值對應(yīng)的張量x和核張量k輸入該函數(shù):
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 = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
## 輸出結(jié)果
tensor([[ 56., 72.],
[104., 120.]])
?(2)多輸出通道場景
卷積操作的輸出特征圖也會具有多個通道,這時要設(shè)計個維度為的卷積核,卷積核數(shù)組的維度是,如下圖所示:
卷積核的輸出通道數(shù)也稱為卷積核的個數(shù),圖中包含了兩個卷積核,紅綠藍代表第一個卷積核的三個輸入通道,顏色稍淺的代表第二個卷積核的三個輸入通道。
在一個卷積層中,一個卷積核可以學習并提取圖像中的一種特征,但往往圖片中包含多種不同的特征信息,因此我們需要多個不同的卷積核提取不同的特征。?
同樣我們實現(xiàn)一個計算多個通道的輸出函數(shù):
def corr2d_multi_in_out(X, K):
# 迭代“K”的第0個維度,每次都對輸入“X”執(zhí)行互相關(guān)運算。 # 最后將所有結(jié)果都疊加在一起
return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
將核張量k與k+1(k中每個元素加1)和k+2連接起來,構(gòu)造一個具有3個輸出通道的卷積核:
K = torch.stack((K, K + 1, K + 2), 0)
K.shape
## 結(jié)果
torch.Size([3, 2, 2, 2])
我們對輸入張量與卷積核張量k執(zhí)行運算,現(xiàn)在輸出包含3個通道,第一個通道結(jié)果與先前輸入張量x和多輸入單輸出通道的結(jié)果一致:
corr2d_multi_in_out(X, K)
## 輸出結(jié)果
tensor([[[ 56., 72.],
[104., 120.]],
[[ 76., 100.],
[148., 172.]],
[[ 96., 128.],
[192., 224.]]])
(3)批量操作
在卷積神經(jīng)網(wǎng)絡(luò)計算過程中,通常將多個樣本放在一起形成一個mini-batch進行批量操作,即輸入數(shù)據(jù)的維度是。由于會對每張圖片使用同樣的卷積核進行卷積操作,卷積核的維度與上面多輸出通道的情況一樣,仍然是,輸出特征圖的維度是,如下圖所示:
三、池化層與全連接層
3.1?池化
3.1.1 池化操作
池化(pooling layer)也稱匯聚層或子采樣層,自主要作用是進行特征選擇、降低特征數(shù)量從而減少參數(shù)數(shù)量。池化相當于在空間范圍內(nèi)做了維度刪減,分別作用于每個輸入的特征并減小其大小。
池化層包含預設(shè)定的池化函數(shù),其功能是將特征圖中單個點的結(jié)果替換為其相鄰區(qū)域的特征圖統(tǒng)計量。使用某一位置的相鄰輸出的總體統(tǒng)計特征代替網(wǎng)絡(luò)在該位置的輸出,其好處是當輸入數(shù)據(jù)做出少量平移時,經(jīng)過池化函數(shù)后的大多數(shù)輸出還能保持不變。
池化通常有兩種,分別為平均池化和最大池化,如下圖所示:
與卷積核類似,池化窗口(用表示池化窗口)在圖片上滑動時,每次移動的步長稱為步幅,當寬和高方向的移動大小不一樣時,分別用和表示。當然也可對需要進行池化的圖片進行填充,填充方式與卷積類似,假設(shè)在第一行之前填充行,在最后一行后面填充行,在第一列之前填充列,在最后一列之后填充列,則池化層的輸出特征圖大小為:
通常使用大小的池化窗口,步幅也使用2,填充為0;通過這種方式的池化,輸出特征圖的高和寬都減半,但通道數(shù)不會改變。
為實現(xiàn)池化層的前向傳播,我們?nèi)钥梢允褂胏orr2d函數(shù),只是此處沒有卷積核,輸出為輸入中每個區(qū)域的最大值或平均值:
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
構(gòu)建輸入張量x,驗證二維最大池化層的輸出:
## 最大池化
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))
## 結(jié)果
tensor([[4., 5.],
[7., 8.]])
# 平均池化
pool2d(X, (2, 2), 'avg')
# 結(jié)果
tensor([[2., 3.],
[5., 6.]])
3.1.2 池化作用
池化層不但可以有效地減少神經(jīng)元的數(shù)量,還可以使得網(wǎng)絡(luò)對一些小的局部形態(tài)改變保持不變性,并擁有更大的感受野,如下圖所示:
?3.2?全連接層
全連接層(fully-connected layer)中每一個節(jié)點都與上一層所有節(jié)點相連,把前向?qū)犹崛〉降奶卣骶C合起來。由于其全相連的特征,一般全連接層的參數(shù)也是最多的。
在 CNN 結(jié)構(gòu)中,經(jīng)多個卷積層和池化層后,連接著1個或1個以上的全連接層,與 MLP 類似,全連接層中的每個神經(jīng)元與其前一層的所有神經(jīng)元進行全連接。為提升 CNN 網(wǎng)絡(luò)性能,全連接層每個神經(jīng)元的激勵函數(shù)一般采用 ReLU 函數(shù),最后一層全連接層的輸出值被傳遞給一個輸出,可以采用softmax邏輯回歸進行分類。
四、LeNet網(wǎng)絡(luò)
LeNet是最早發(fā)布的卷積神經(jīng)網(wǎng)絡(luò)之一,因其在計算機視覺任務(wù)中的高效性能而受到廣泛關(guān)注。這個模型是由AT&T?爾實驗室的研究員Yann LeCun在1989年提出。當時,LeNet取得了與支持向量機(support vector machines)性能相媲美的成果,成為監(jiān)督學習的主流方法。LeNet被廣泛用于自動取款機(ATM)機中,幫助識別處理支票的數(shù)字,到如今還有相當范圍的使用。
4.1 LeNet結(jié)構(gòu)
LeNet網(wǎng)絡(luò)由兩個部分組成,分別為:
- 卷積編碼器:由兩個卷積層組成
- 全連接層密集塊:由三個全連接層組成。
其網(wǎng)絡(luò)架構(gòu)如下圖所示:
?每個卷積塊中的基本單元是一個卷積層、一個sigmoid激活函數(shù)和平均池化層。每個卷積層使用5卷積核和一個sigmoid激活函數(shù)。這 些層將輸入映射到多個二維特征輸出,通常同時增加通道的數(shù)量。第一卷積層有6個輸出通道,而第二個卷積層有16個輸出通道,卷積的輸出形狀由批量大 小、通道數(shù)、高度、寬度決定。
深度學習框架實現(xiàn)此類模型并不難,只需實例化一個Sequential模塊并將需要的層連接在一起:
import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Flatten(),
nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
nn.Linear(120, 84), nn.Sigmoid(),
nn.Linear(84, 10))
對原始模型做一點小改動,去掉最后一層的高斯激活,其余與最初的網(wǎng)絡(luò)一致。將一個大小為的單通道(黑白)圖像通過LeNet,通過在每一層打印輸出的形狀,我們可以檢查模型,以確保其操作與我們期望的一致,如下圖:
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape: \t',X.shape)
## 輸出結(jié)果
Conv2d output shape: torch.Size([1, 6, 28, 28])
Sigmoid output shape: torch.Size([1, 6, 28, 28])
AvgPool2d output shape: torch.Size([1, 6, 14, 14])
Conv2d output shape: torch.Size([1, 16, 10, 10])
Sigmoid output shape: torch.Size([1, 16, 10, 10])
AvgPool2d output shape: torch.Size([1, 16, 5, 5])
Flatten output shape: torch.Size([1, 400])
Linear output shape: torch.Size([1, 120])
Sigmoid output shape: torch.Size([1, 120])
Linear output shape: torch.Size([1, 84])
Sigmoid output shape: torch.Size([1, 84])
Linear output shape: torch.Size([1, 10])
在整個卷積塊中,與上一層相比,每一層的特征高度和寬度都減小了。第一個卷積層使用2個像素的 填充,來補償卷積核導致的特征減少。相反,第二個卷積層沒有填充,因此高度和寬度都減少了4個像素。隨著層疊的上升,通道的數(shù)量從輸入時的1個,增加到第一個卷積層之后的6個,再到第二個卷積層之后的16個。同時,每個匯聚層的高度和寬度都減半。最后,每個全連接層減少維數(shù),最終輸出一個維數(shù)與結(jié)果分類數(shù)相匹配的輸出。
4.2?LeNet模型訓練
在實現(xiàn)LeNet的基礎(chǔ)上,用其實現(xiàn)Fashion-MNIST數(shù)據(jù)集上的表現(xiàn)。
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
雖然卷積神經(jīng)網(wǎng)絡(luò)的參數(shù)較少,但與深度的多層感知機相比,它們的計算成本仍然很高,因為每個參數(shù)都參與更多的乘法。通過使用GPU,可以用它加快訓練。
def evaluate_accuracy_gpu(net, data_iter, device=None): #@save
"""使用GPU計算模型在數(shù)據(jù)集上的精度"""
if isinstance(net, nn.Module):
net.eval() # 設(shè)置為評估模式
if not device:
device = next(iter(net.parameters())).device
# 正確預測的數(shù)量,總預測的數(shù)量
metric = d2l.Accumulator(2)
with torch.no_grad():
for X, y in data_iter:
if isinstance(X, list):
X = [x.to(device) for x in X]
else:
X = X.to(device)
y = y.to(device)
metric.add(d2l.accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
與全連接層一樣,我們使用交叉熵損失函數(shù)和小批量隨機梯度下降:
#@save
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
net.to(device)
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test cc'])
timer, num_batches = d2l.Timer(), len(train_iter)
for epoch in range(num_epochs):
# 訓練損失之和,訓練準確率之和,樣本數(shù)
metric = d2l.Accumulator(3)
net.train()
for i, (X, y) in enumerate(train_iter):
timer.start()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
optimizer.step()
with torch.no_grad():
metric.add(l * X.shape[0],
d2l.accuracy(y_hat,y), X.shape[0])
timer.stop()
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
test_acc = evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
f'on {str(device)}')
之后,我們可以訓練和評估LeNet模型:文章來源:http://www.zghlxwxcb.cn/news/detail-673471.html
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
## 輸出結(jié)果
loss 0.467, train acc 0.825, test acc 0.821
88556.9 examples/sec on cuda:0
文章來源地址http://www.zghlxwxcb.cn/news/detail-673471.html
到了這里,關(guān)于深度學習|卷積神經(jīng)網(wǎng)絡(luò)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!