卷積是經過兩個函數 f,g 生成第三個函數的一種數學算子,表徵函數 f 與 g 通過翻轉和平移的重疊部分的面積。數學定義公式:html
事實上,在卷積網絡上使用的離散卷積,也就是不連續的,它是一種運算方式,也就是按照卷積核,將輸入對應位置的數據進行加權和運算,接下來結合卷積核的概念,就會很好理解了。算法
卷積神經網絡利用卷積結構減小須要學習的參數量,從而提升反向傳播算法的訓練效率。在卷積神經網絡中,第一個卷積層會直接接受圖像像素級的輸入,每個卷積操做只處理一小塊圖像,進行卷積操做後傳遞到後面的網絡,每一層卷積都會提取數據中最有效的特徵。這種方法能夠提取到圖像中最基礎的特徵,好比不一樣方向的拐角或者邊,然後進行組合和抽象成更高階的特徵,所以卷積神經網絡對圖像縮放、平移和旋轉具備不變性。數組
卷積神經網絡的要點就是卷積層中的局部鏈接、權值共享、和池化層中降採樣。局部鏈接、權值共享和降採樣下降了參數量,使得訓練複雜度大大下降,並減輕了過擬合的風險。同時還賦予了卷積神經網絡對平移、形變、尺度的某種程度的不變性,提升了模型的泛化能力網絡
卷積神經網絡最重要的兩個知識點就是卷積核和卷積神經網絡的結構框架
卷積核是整個網絡的核心,訓練 CNN 的過程就是不斷更新卷積核參數直到最優的過程。
less
卷積核的定義:對於輸入圖像中的一部分區域,進行加權平均的處理,其中這個過程的權重,由一個函數定義,這個函數就是卷積核。ide
以下圖彩色圖像有RGB三個色值通道,分別表示紅、綠、藍,每一個通道內的像素能夠用一個像下圖右邊的二維數組表示,數值表明0-255之間的像素值。假設一張900*600的彩色的圖片,計算機裏面能夠用 (900*600*3)的數組表示。函數
卷積過程學習
卷積過程是基於一個小矩陣,也就是卷積核,在上面所說的每層像素矩陣上不斷按步長掃過去的,掃到數與卷積覈對應位置的數相乘,而後求總和,每掃一次,獲得一個值,所有掃完則生成一個新的矩陣。以下圖測試
卷積核如何設置能夠參考卷積神經網絡的卷積核大小、個數。卷積層數通常取(3,3)的小矩陣,卷積核裏面每一個值就是咱們須要尋找(訓練)的神經元參數(權重),開始會隨機有個初始值,當訓練網絡時,網絡會經過後向傳播不斷更新這些參數值,直到尋找到最佳的參數值。「最佳」、須要經過損失函數去評估。
卷積操做至關於特徵提取,卷積核至關於一個過濾器,提取咱們須要的特徵。
以下圖卷積操做,從左上角掃到右下角,最終獲得右邊的特徵圖譜。
零填充(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
卷積層
這一層的主要部分就是進行卷積操做,前面已經介紹了卷積核的概念,卷積層實際上就是實現了這個卷積核的計算過程,在這一層中,可能會見到如下的幾種關鍵詞:
深度 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 上,至關於對輸入矩陣的尺寸進一步濃縮,也就是進一步提取特徵。卷積操做後咱們提取了不少特徵信息,相鄰區域有類似特徵信息,能夠相互替代的,若是所有保留這些特徵信息就會有信息冗餘,增長了計算難度,這時候池化就至關於降維操做。池化是在一個小矩陣區域內,取該區域的最大值或平均值來代替該區域,該小矩陣的大小能夠在搭建網絡的時候本身設置。小矩陣也是從左上角掃到右下角。以下圖
池化層有如下幾個功能:
最後一點要注意的是,池化層並不是是卷積網絡所必需的。一些新的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實現一個簡單的卷積神經網絡,使用的數據集是 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