卷積神經網絡概念及使用 PyTorch 簡單實現

卷積神經網絡

  卷積神經網絡(CNN)是深度學習的表明算法之一  。具備表徵學習能力,可以按其階層結構對輸入信息進行平移不變分類,所以也被稱爲「平移不變人工神經網絡」隨着深度學習理論的提出和數值計算設備的改進,卷積神經網絡獲得了快速發展,並被應用於 計算機視覺天然語言處理等領域

  卷積是經過兩個函數 f,g 生成第三個函數的一種數學算子,表徵函數 f 與 g 通過翻轉和平移的重疊部分的面積。數學定義公式:html

  事實上,在卷積網絡上使用的離散卷積,也就是不連續的,它是一種運算方式,也就是按照卷積核,將輸入對應位置的數據進行加權和運算,接下來結合卷積核的概念,就會很好理解了。算法

  卷積神經網絡利用卷積結構減小須要學習的參數量,從而提升反向傳播算法的訓練效率。在卷積神經網絡中,第一個卷積層會直接接受圖像像素級的輸入,每個卷積操做只處理一小塊圖像,進行卷積操做後傳遞到後面的網絡,每一層卷積都會提取數據中最有效的特徵。這種方法能夠提取到圖像中最基礎的特徵,好比不一樣方向的拐角或者邊,然後進行組合和抽象成更高階的特徵,所以卷積神經網絡對圖像縮放、平移和旋轉具備不變性。數組

卷積神經網絡的要點就是卷積層中的局部鏈接、權值共享、和池化層中降採樣。局部鏈接、權值共享和降採樣下降了參數量,使得訓練複雜度大大下降,並減輕了過擬合的風險。同時還賦予了卷積神經網絡對平移、形變、尺度的某種程度的不變性,提升了模型的泛化能力網絡

 

 卷積神經網絡最重要的兩個知識點就是卷積核卷積神經網絡的結構框架

  • 卷積核
    • 卷積覈定義
    • 卷積操做
    • 深度
    • 步幅
    • 零填充
  • 卷積神經網絡的結構
    • 輸入層 INPUT
    • 卷積層 CONV
    • 激活函數層 RELU
    • 池化層 POOL
    • 全鏈接層 FC

 

卷積核

​  卷積核是整個網絡的核心,訓練 CNN 的過程就是不斷更新卷積核參數直到最優的過程。
less

  卷積核的定義:對於輸入圖像中的一部分區域,進行加權平均的處理,其中這個過程的權重,由一個函數定義,這個函數就是卷積核。ide

以下圖彩色圖像有RGB三個色值通道,分別表示紅、綠、藍,每一個通道內的像素能夠用一個像下圖右邊的二維數組表示,數值表明0-255之間的像素值。假設一張900*600的彩色的圖片,計算機裏面能夠用 (900*600*3)的數組表示。函數

卷積過程學習

  卷積過程是基於一個小矩陣,也就是卷積核,在上面所說的每層像素矩陣上不斷按步長掃過去的,掃到數與卷積覈對應位置的數相乘,而後求總和,每掃一次,獲得一個值,所有掃完則生成一個新的矩陣。以下圖測試

  卷積核如何設置能夠參考卷積神經網絡的卷積核大小、個數。卷積層數通常取(3,3)的小矩陣,卷積核裏面每一個值就是咱們須要尋找(訓練)的神經元參數(權重),開始會隨機有個初始值,當訓練網絡時,網絡會經過後向傳播不斷更新這些參數值,直到尋找到最佳的參數值。「最佳」、須要經過損失函數去評估。

  卷積操做至關於特徵提取,卷積核至關於一個過濾器,提取咱們須要的特徵。

以下圖卷積操做,從左上角掃到右下角,最終獲得右邊的特徵圖譜。

 

  圖解:一個過濾器(紅色邊框)在輸入圖像上移動(卷積操做)以生成特徵映射。在同一張圖像上,另外一個過濾器(綠色邊框)的卷積生成了不一樣的特徵圖。須要注意到,卷積操做捕獲原始圖像中的局部依賴關係很重要。還要注意這兩個不一樣的過濾器如何從同一張原始圖像獲得不一樣的特徵圖。請記住,以上圖像和兩個過濾器只是數值矩陣。
  實際上,卷積神經網絡在訓練過程當中會本身學習這些過濾器的值(儘管在訓練過程以前咱們仍須要指定諸如過濾器數目、大小,網絡框架等參數)。過濾器數目越多,提取的圖像特徵就越多,識別新圖像時效果就會越好。
 
特徵映射(卷積特徵)的大小由執行卷積步驟以前須要決定的三個參數控制:
  • 深度:深度對應於咱們用於卷積運算的過濾器數量。在圖7所示的網絡中,咱們使用三個不一樣的過濾器對初始的船圖像進行卷積,從而生成三個不一樣的特徵圖。能夠將這三個特徵地圖視爲堆疊的二維矩陣,所以,特徵映射的「深度」爲3。
  • 步幅:步幅是在輸入矩陣上移動一次過濾器矩陣的像素數量。當步幅爲1時,咱們一次將過濾器移動1個像素。當步幅爲2時,過濾器每次移動2個像素。步幅越大,生成的特徵映射越小。有橫行和縱向兩個方向
  • 零填充:有時,將輸入矩陣邊界用零來填充會很方便,這樣咱們能夠將過濾器應用於輸入圖像矩陣的邊界元素。零填充一個很好的特性是它容許咱們控制特徵映射的大小。添加零填充也稱爲寬卷積,而不使用零填充是爲窄卷積。

零填充(padding)

  卷積操做以後維度會變少,獲得的矩陣會比原來矩陣小,這樣很差計算,因此須要Padding,在每次卷積操做以前,在原矩陣外邊補包一層0,能夠只在橫向補,或只在縱向補,或者四周都補0,從而使得卷積後輸出的圖像跟輸入圖像在尺寸上一致。

好比:須要作一個5*5的原始矩陣的卷積,用一個3*3卷積核來掃,掃出來結果的矩陣應該是:3*3的矩陣,變小了。

卷積前加 Padding 操做補一圈0,即300*300矩陣外面周圍加一圈「0」,這樣的300*300就變成了302*302的矩陣,再進行卷積出來就是300*300 ,尺寸和原圖同樣。

 

卷積神經網絡結構

  CNN 通常來講分爲五個部分:輸入層卷積層激活函數層池化層全鏈接層

  不過注意須要注的點是,對於大部分卷積網絡,都會交替地用到中間地四層結構,也就是呈現出一種 卷積層——激活函數層——池化層——卷積層——激活函數層——池化層…地交替結構,固然,對於一些新出現地卷積網絡,連池化層都省去了,以上五層結構只是通常會出現的層次。

 

輸入層

  整個網絡的輸入,通常是一張圖象地像素矩陣,在上面的圖中,能夠看到輸入是一個立體的結構,這是由於通常的圖像都會有一個深度的概念,就像RGB的彩色圖像,就是 a*b*c 的形式,其中前兩維指定的是圖像的長和寬,第三維則是深度,彩色RGB的深度是3,而黑白圖像的深度是 1

 

卷積層

  這一層的主要部分就是進行卷積操做,前面已經介紹了卷積核的概念,卷積層實際上就是實現了這個卷積核的計算過程,在這一層中,可能會見到如下的幾種關鍵詞:

  • 濾波器 Filter:實現前面定義的卷積核的神經元。
  • 步長 Stride
  • 填充 Padding
  • 深度 Depth:這裏的深度不是指圖像,而是指某一層中神經元(濾波器)的個數,不一樣的 Filter 重點處理的特徵是不一樣的咱們想要獲得不一樣的這些特徵圖,因此設置了多個 Filter ,這樣每一個 Filter 處理獲得一張 Feature Map ,多個 Filter 就會獲得多個Feature Map, 將這些Feature Map 疊在一塊兒就是輸出的立體,能夠看到,Filter 與 Feature Map的數量是同樣的,這個數量,就是 深度。

 在PyTorch中, 類nn.Conv2d()是卷積核模塊。卷積核及其調用例子以下:

# 調用形式
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0,dilation=1,groups=1, bias=True)
'''
  nn.Conv2d中參數含義:
  in_channels表示輸入數據體的深度;
  out_channels表示輸出數據體的深度;
  kernel_size 表示卷積核的大小;
  stride表示滑動的步長;
  padding表示邊界0填充的個數;
  dilation表示輸入數據體的空間間隔;
  groups 表示輸入數據體和輸出數據體在深度上的關聯;
  bias 表示偏置。
'''

# With square kernels and equal stride
m = nn.Conv2d(16, 33, 3, stride=2)
# non-square kernels and unequal stride and with padding
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
# non-square kernels and unequal stride and with padding and dilation
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
nput = autograd.Variable(torch.randn(20, 16, 50, 100))
output = m(input)

 

激勵函數層

  實際中,使用激活函數處理每每是與以前的卷積層綁定在一塊兒,這個做用其實也就是是激活函數的做用,去線性化,在卷積網絡中,通常使用的激勵函數是 ReLu 函數,注意大多數狀況下都不會使用 Sigmoid 函數處理。

 

池化層

  做用在 Feature Map 上,至關於對輸入矩陣的尺寸進一步濃縮,也就是進一步提取特徵。卷積操做後咱們提取了不少特徵信息,相鄰區域有類似特徵信息,能夠相互替代的,若是所有保留這些特徵信息就會有信息冗餘,增長了計算難度,這時候池化就至關於降維操做。池化是在一個小矩陣區域內,取該區域的最大值或平均值來代替該區域,該小矩陣的大小能夠在搭建網絡的時候本身設置。小矩陣也是從左上角掃到右下角。以下圖

 

池化層有如下幾個功能:

  1. 對 Feature Map 又進行一次特徵提取,這也是減少數據量的操做
  2. 獲取更抽象的特徵,防止過擬合,提升泛化性
  3. 通過這個處理,對輸入的微小變化有更大的容忍,也就是說若是數據有一些噪音,那麼通過這個特徵提取的過程,就必定程度上減少了噪音的影響。

最後一點要注意的是,池化層並不是是卷積網絡所必需的。一些新的CNN網絡設計時候並無使用池化層。

在PyTorch中,池化層是包括在類nn.MaxPool2d和nn.AvgPoo2d。下面介紹一下nn.MaxPool2d及其調用例子。其調用以下

nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1,return_indices=False,ceil_mode=False)
'''
    nn.MaxPool2d中各個參數的含義:
    kernel_size, stride,padding, dilation在nn.Conv2d中已經解釋過。
    return_indices表示是否返回最大值所處的下標;
    ceil_model表示使用方格代替層結構
'''

# pool of square window of size=3, stride=2
m = nn.MaxPool2d(3, stride=2)
# pool of non-square window
m = nn.MaxPool2d((3, 2), stride=(2, 1))
input = autograd.Variable(torch.randn(20, 16, 50, 32))
output = m(input)

 

全鏈接層

  在一開始的結構圖中能夠看出, CNN網絡還有一個特色 : 可能有多輪 卷積層 和池化層的處理,這時候,模型已經將圖像輸入處理成了信息含量更高的特徵,爲了實現分類的任務(通常是分類,固然也會有其餘的任務),須要使用全鏈接層來完成分類任務。

  對n-1層和n層而言,n-1層的任意一個節點,都和第n層全部節點有鏈接。即第n層的每一個節點在進行計算的時候,激活函數的輸入是n-1層全部節點的加權。像下面的中間層就是全鏈接方式。

 

 

使用PyTorch實現卷積神經網絡

  使用PyTorch實現一個簡單的卷積神經網絡,使用的數據集是 MNIST,預期能夠達到 97.05%左右的準確率。該神經網絡由2個卷積層和3個全鏈接層構建,經過這個例子能夠掌握設計卷積神經網絡的特徵以及參數的配置。

# coding=utf-8
# 配置庫
import torch
from torch import nn, optim
import torch.nn.functional as F
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets

# 配置參數
torch.manual_seed(1)  # 設置隨機數種子,確保結果可重複  
batch_size = 128  # 批處理大小
learning_rate = 1e-2  # 學習率
num_epoches = 10  # 訓練次數 

# ---------------------- 加載MINSIT數據 ----------------------
# 下載訓練集 MNIST 手寫數字訓練集
train_dataset = datasets.MNIST(
    root='./data',  # 數據保持的位置
    train=True,  # 訓練集 
    transform=transforms.ToTensor(),  # 一個取值範圍是[0,255]的PIL.Image
    # 轉化爲取值範圍是[0,1.0]的torch.FloadTensor
    download=True)  # 下載數據

test_dataset = datasets.MNIST(
    root='./data',
    train=False,  # 測試集
    transform=transforms.ToTensor())


# ---------------------- 數據的批處理 ----------------------
# 數據的批處理,尺寸大小爲batch_size, 
# 在訓練集中,shuffle 必須設置爲True, 表示次序是隨機的
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)


# ---------------------- 建立CNN模型 ----------------------
# 一個類來創建CNN模型.這個CNN總體流程是:
# 卷積(Conv2d) -> 激勵函數(ReLU) -> 池化, 向下採樣(MaxPooling) -> 再來一遍 -> 展開多維的卷積成的特徵圖 -> 接入全鏈接層(Linear) -> 輸出。

# 定義卷積神經網絡模型
class Cnn(nn.Module):
    def __init__(self, in_dim, n_class):  # 1x28x28
        super(Cnn, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_dim, 6, 3, stride=1, padding=1),  # 28 x 28 
            nn.ReLU(True),
            nn.MaxPool2d(2, 2),  # 14 x 14
            nn.Conv2d(6, 16, 5, stride=1, padding=0),  # 16 x 10 x 10
            nn.ReLU(True), nn.MaxPool2d(2, 2))  # 16x5x5

        self.fc = nn.Sequential(
            nn.Linear(400, 120),  # 400 = 16 x 5 x 5 
            nn.Linear(120, 84),
            nn.Linear(84, n_class))

    def forward(self, x):
        out = self.conv(x)
        out = out.view(out.size(0), 400)  # 400 = 16 x 5 x 5  
        out = self.fc(out)
        return out


model = Cnn(1, 10)  # 圖片大小是28x28, 10是數據的種類
# 打印模型,呈現網絡結構
print(model)


# ---------------------- 訓練 ----------------------
# 將img, label都用Variable包起來, 而後放入model中計算out, 最後再計算less和正確率.

# 定義loss和optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

# 開始訓練
for epoch in range(num_epoches):
    running_loss = 0.0
    running_acc = 0.0
    for i, data in enumerate(train_loader, 1):  # 批處理
        img, label = data
        img = Variable(img)
        label = Variable(label)
        # 前向傳播 
        out = model(img)
        loss = criterion(out, label)  # loss 
        running_loss += loss.data[0] * label.size(0)  # total loss , 因爲loss 是batch 取均值的,須要把batch size 乘回去
        _, pred = torch.max(out, 1)  # 預測結果
        num_correct = (pred == label).sum()  # 正確結果的num
        # accuracy = (pred == label).float().mean() #正確率
        running_acc += num_correct.data[0]  # 正確結果的總數
        # 後向傳播
        optimizer.zero_grad()  # 梯度清零,以避免影響其餘batch
        loss.backward()  # 後向傳播,計算梯度
        optimizer.step()  # 利用梯度更新 W ,b參數

    # 打印一個循環後,訓練集合上的loss 和正確率
    print('Train {} epoch, Loss: {:.6f}, Acc: {:.6f}'.format(
        epoch + 1, running_loss / (len(train_dataset)), running_acc / (len(
            train_dataset))))


# ---------------------- 在測試集測試識別率 ----------------------
# 模型測試, 
model.eval()  # 因爲訓練和測試 BatchNorm, Dropout配置不一樣,須要說明是否模型測試
eval_loss = 0
eval_acc = 0
for data in test_loader:  # test set 批處理
    img, label = data

    img = Variable(img, volatile=True)  # volatile 肯定你是否不調用.backward(), 測試中不須要
    label = Variable(label, volatile=True)
    out = model(img)  # 前向算法 
    loss = criterion(out, label)  # 計算 loss
    eval_loss += loss.data[0] * label.size(0)  # total loss
    _, pred = torch.max(out, 1)  # 預測結果
    num_correct = (pred == label).sum()  # 正確結果
    eval_acc += num_correct.data[0]  # 正確結果總數

print('Test Loss: {:.6f}, Acc: {:.6f}'.format(eval_loss / (len(
    test_dataset)), eval_acc * 1.0 / (len(test_dataset))))

'''
最後的訓練和測試上loss和識別率分別是:
Train 1 epoch, Loss: 2.226007, Acc: 0.320517
Train 2 epoch, Loss: 0.736581, Acc: 0.803717
Train 3 epoch, Loss: 0.329458, Acc: 0.901117
Train 4 epoch, Loss: 0.252310, Acc: 0.923550
Train 5 epoch, Loss: 0.201800, Acc: 0.939000
Train 6 epoch, Loss: 0.167249, Acc: 0.949550
Train 7 epoch, Loss: 0.145517, Acc: 0.955617
Train 8 epoch, Loss: 0.128391, Acc: 0.960817
Train 9 epoch, Loss: 0.117047, Acc: 0.964567
Train 10 epoch, Loss: 0.108246, Acc: 0.966550
Test
Loss: 0.094209, Acc: 0.970500
'''

 

 

 

 

參考:http://www.sohu.com/a/241338315_787107

參考:http://www.javashuo.com/article/p-mfoladsn-mn.html

參考:https://www.cnblogs.com/wmr95/articles/7814892.html

相關文章
相關標籤/搜索