對CNN中局部感知、參數共享、多卷積核以及池化操作的理解與實例分析

1.CNN概念理解

1.1二維平面的卷積操作
下圖Image表示一幅圖片,有顏色填充的網格表示一個卷積核,卷積核的大小爲3*3。假設我們做步長爲1的卷積操作,表示卷積核每次向右移動一個像素(當移動到邊界時回到最左端並向下移動一個單位)。卷積核每個單元內有權重,下圖的卷積核內有9個權重。在卷積核移動的過程中將圖片上的像素和卷積核的對應權重相乘,最後將所有乘積相加得到一個名爲Convolved Feature的輸出。

1.2被卷積矩陣的深度
卷積操作在二維平面上很好理解,但在CNN中,被卷積的圖像(矩陣)是有深度的:
該深度可以類比彩色圖像的RGB三通道進行理解,因此被卷積矩陣的維度是:depth*height*width.對有深度的矩陣進行卷積操作可以理解爲二維卷積操作在所有深度上同時進行.

1.3局部感知
在傳統神經網絡中每個神經元都要與圖片上每個像素相連接,這樣的話就會造成權重的數量巨大造成網絡難以訓練。而在含有卷積層的的神經網絡中每個神經元的權重個數都是卷積核的大小,這樣就相當於每個神經元只與對應圖片部分的像素相連接,極大的減少了權重的數量。同時我們可以設置卷積操作的步長,假設將上圖卷積操作的步長設置爲3時每次卷積都不會有重疊區域(在超出邊界的部分補自定義的值)。局部感知的直觀感受如下圖:

使用局部感知的原因在於圖片中相鄰區域具有更大的相關性,距離較遠時相關性也較小.
1.4參數共享
卷積核的權重通過學習得到且在卷積過程中卷積核的權重不會改變,這是參數共享的思想。說明我們通過一個卷積核的操作提取了原圖的不同位置的同樣(類似)特徵。簡單來說就是在一幅圖片中的不同位置的相同目標,它們的特徵是基本相同的。其過程如下圖:
1.5多核卷積
權值共享部分給我們啓發:用一個卷積核操作只能得到一部分特徵可能獲取不到全部特徵,這時候就必須引入多核卷積來儘可能多得獲取圖像矩陣的全部特徵,即每個卷積核學習不同特徵(每個卷積核學習到不同的權重)來提取原圖特徵。
上圖的圖片經過三個卷積核的卷積操作得到三個特徵圖。需要注意的是,在多核卷積的過程中每個卷積核的大小應該是相同的。


上圖的圖片經三個卷積核的卷積操作得到三個特徵圖(每個特徵圖體現原圖不同特徵)。
注意:在多核卷積過程中每個卷積核大小應保持一致。

2.下采樣層(池化)

在卷積神經網絡中,我們經常會碰到池化操作,池化不同於卷積操作(作用於圖像的不同區域).池化層往往在卷積層後面,通過池化來降低卷積層輸出的特徵向量,同時改善結果(不易出現過擬合)。

爲什麼可以通過降低維度呢?

因爲圖像具有一種“靜態性”的屬性,這也就意味着在一個圖像區域有用的特徵極有可能在另一個區域同樣適用。因此,爲了描述大的圖像,一個很自然的想法就是對不同位置的特徵進行聚合統計,例如,人們可以計算圖像一個區域上的某個特定特徵的平均值 (或最大值)來代表這個區域的特徵。

3.源碼剖析

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 28 14:59:48 2018

@author: demons
"""

import sys
sys.path.append('..')

import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
from torchvision.datasets import CIFAR10

def vgg_block(num_convs, in_channels, out_channels):
    net = [nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), nn.ReLU(True)] # 定義第一層
    
    for i in range(num_convs-1): # 定義後面的很多層
        net.append(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))
        net.append(nn.ReLU(True))
        
    net.append(nn.MaxPool2d(2, 2)) # 定義池化層
    return nn.Sequential(*net)

block_demo = vgg_block(3, 64, 128)
print(block_demo)

# 首先定義輸入爲 (1, 64, 300, 300)
input_demo = Variable(torch.zeros(1, 64, 300, 300))
output_demo = block_demo(input_demo)
print(output_demo.shape)

 runfile('C:/Users/demons/Desktop/trainingtorch/VGG.py', wdir='C:/Users/demons/Desktop/trainingtorch')
Sequential(
  (0): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (1): ReLU(inplace)
  (2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (3): ReLU(inplace)
  (4): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (5): ReLU(inplace)
  (6): MaxPool2d(kernel_size=(2, 2), stride=(2, 2), dilation=(1, 1), ceil_mode=False)
)
torch.Size([1, 128, 150, 150])


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章