深度之眼【Pytorch】--卷積層、池化層

本文主要爲深度之眼pytorch訓練營二期學習筆記,詳細課程內容移步:深度之眼 https://ai.deepshare.net/index

目錄

背景知識

一:卷積及其運算

二:池化及其運算

三:激活層

Pytorch實現

卷積層--nn.Conv2d

(選)轉置卷積層--nn.ConvTranspose2d

池化層--nn.MaxPool2d

Keras實現

One more thing

卷積運算的三個重要思想:稀疏交互、參數共享、等變表示

池化運算--平移不變性

 

背景知識

對於處理圖像數據,我們爲什麼不用全連接層而選擇了卷積層呢?

(一):參數太多如果圖像大小爲100×100×3(即圖像高度爲100,寬度爲100,3個顏色通道:RGB)使用全連接層,後一層單獨每一個神經元到輸入層上就有100×100×3 = 30,000個相互獨立的連接,每個連接都對應一個權重參數)使用全連接層參數會很多。而卷積層(由於局部連接全局共享的特性, 如使用一個3x3x3(通道數)的卷積核掃描圖像,一層特徵圖就只有3x3x3個參數)和全連接神經網絡相比,要學習的參數更少。

(二):局部不變性特徵。自然圖像中的物體都具有局部不變性特徵,比如在尺度縮放、平移、旋轉等操作不影響其語義信息。而全連接網絡很難提取這些局部不變特徵。

 

一:卷積及其運算

卷積層(Convolution Layer):通常用作對輸入層輸入數據進行特徵提取,通過卷積核矩陣對原始數據中隱含關聯性的一種抽象。(卷積核的作用就相當於是一個濾波器)

 

二:池化及其運算

池化層(Pooling Layer)又稱爲降採樣層(Downsampling Layer),作用是對感受域內的特徵進行篩選,能夠有效地降低輸出特徵尺度,進而減少模型所需要的參數量。(輸入數據發生微小偏差時,池化仍會返回相同的結果。因此,池化對輸入數據的微小偏差具有魯棒性)。常見的池化有最大池化(返回感受域內的最大的元素),平均池化(返回感受域內元素的平均值)。

尺寸計算:

Out = \frac{input-k}{s}

 

 

三:激活層

激活層(Activation Layer)負責對卷積層抽取的特徵進行激活,由於卷積操作是由輸入矩陣與卷積核矩陣進行相差的線性變化關係,需要激活層對其進行非線性的映射。激活層主要由激活函數組成,即在卷積層輸出結果的基礎上嵌套一個非線性函數,讓輸出的特徵圖具有非線性關係。

常用激勵函數:

 

Pytorch實現

卷積層--nn.Conv2d

torch.nn.Conv2d(
 in_channels,  #輸入通道
 out_channels, #輸出通道 
 kernel_size,  #卷積核大小
 stride=1,     #步長
 padding=0,    #填充個數
 dilation=1,   #空洞卷積核大小
 groups=1,     #分組卷積設置
 bias=True,    #偏置
 padding_mode='zeros')

 

import os
import torch.nn as nn
from PIL import Image
from torchvision import transforms
from matplotlib import pyplot as plt

#下面庫完成反變換,這裏沒有附代碼
from tools.common_tools import transform_invert, set_seed

set_seed(3)  # 設置隨機種子

# =============== load img =======
path_img = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lena.png")
img = Image.open(path_img).convert('RGB')  # 0~255

# convert to tensor
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)

img_tensor.unsqueeze_(dim=0)    # C*H*W to B*C*H*W

# ================ create convolution layer =====
flag = 1
#flag = 0
if flag:
    conv_layer = nn.Conv2d(3, 1, 3)   # input:(i, o, size) weights:(o, i , h, w)
    nn.init.xavier_normal_(conv_layer.weight.data)

    # calculation
    img_conv = conv_layer(img_tensor)

# ============ visualization ================
print("卷積前尺寸:{}\n卷積後尺寸:{}".format(img_tensor.shape, img_conv.shape))
img_conv = transform_invert(img_conv[0, 0:1, ...], img_transform)
img_raw = transform_invert(img_tensor.squeeze(), img_transform)
plt.subplot(122).imshow(img_conv, cmap='gray')
plt.subplot(121).imshow(img_raw)
plt.show()

 

(選)轉置卷積層--nn.ConvTranspose2d

torch.nn.ConvTranspose2d
(in_channels,   #輸入通道
out_channels,   #輸出通道
kernel_size,    #卷積核尺寸
stride=1,       #步長
padding=0,      #填充
output_padding=0,  
groups=1,  
bias=True, 
dilation=1, 
padding_mode='zeros')
#深度之眼教學代碼
import os
import torch.nn as nn
from PIL import Image
from torchvision import transforms
from matplotlib import pyplot as plt
from tools.common_tools import transform_invert, set_seed

set_seed(3)  # 設置隨機種子

# ================================= load img ==================================
path_img = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lena.png")
img = Image.open(path_img).convert('RGB')  # 0~255

# convert to tensor
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)
img_tensor.unsqueeze_(dim=0)    # C*H*W to B*C*H*W


# ================ transposed
flag = 1
#flag = 0
if flag:
    conv_layer = nn.ConvTranspose2d(3, 1, 3, stride=2)   # input:(i, o, size)
    nn.init.xavier_normal_(conv_layer.weight.data)

    # calculation
    img_conv = conv_layer(img_tensor)


# ================================= visualization ==================================
print("卷積前尺寸:{}\n卷積後尺寸:{}".format(img_tensor.shape, img_conv.shape))
img_conv = transform_invert(img_conv[0, 0:1, ...], img_transform)
img_raw = transform_invert(img_tensor.squeeze(), img_transform)
plt.subplot(122).imshow(img_conv, cmap='gray')
plt.subplot(121).imshow(img_raw)
plt.show()

一格一格的:棋盤效應

 

池化層--nn.MaxPool2d

torch.nn.MaxPool2d(
kernel_size,    #池化核大小
stride=None,    #步長,默認爲池化核的大小。
padding=0,      
dilation=1, 
return_indices=False,   #記錄池化像素的索引,在上採樣中有重要作用。
ceil_mode=False)
torch.nn.AvgPool2d(
kernel_size,   #池化尺寸
stride=None,   #步長
padding=0,     #填充
ceil_mode=False, 
count_include_pad=True,  
divisor_override=None    #除法因子
)
torch.nn.MaxUnpool2d(
kernel_size,   #核大小
stride=None,   #步長
padding=0)     

 


import torch
import random
import numpy as np
import torchvision
import torch.nn as nn
from torchvision import transforms
from matplotlib import pyplot as plt
from PIL import Image
from tools.common_tools import transform_invert, set_seed

set_seed(1)  # 設置隨機種子

# ================================= load img ======
path_img = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lena.png")
img = Image.open(path_img).convert('RGB')  # 0~255

# convert to tensor
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)
img_tensor.unsqueeze_(dim=0)    # C*H*W to B*C*H*W

# ================ 最大池化
# flag = 1
flag = 0
if flag:
    maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2))   # input:(i, o, size) weights:(o, i , h, w)
    img_pool = maxpool_layer(img_tensor)

# ================ 平均池化
# flag = 1
flag = 0
if flag:
    avgpoollayer = nn.AvgPool2d((2, 2), stride=(2, 2))   # input:(i, o, size) weights:(o, i , h, w)
    img_pool = avgpoollayer(img_tensor)


# ================ 最大上採樣()
# flag = 1
flag = 0
if flag:
    # pooling
    img_tensor = torch.randint(high=5, size=(1, 1, 4, 4), dtype=torch.float)
    maxpool_layer = nn.MaxPool2d((2, 2), stride=(2, 2), return_indices=True)
    img_pool, indices = maxpool_layer(img_tensor)

    # unpooling
    img_reconstruct = torch.randn_like(img_pool, dtype=torch.float)
    maxunpool_layer = nn.MaxUnpool2d((2, 2), stride=(2, 2))
    img_unpool = maxunpool_layer(img_reconstruct, indices)

    print("raw_img:\n{}\nimg_pool:\n{}".format(img_tensor, img_pool))
    print("img_reconstruct:\n{}\nimg_unpool:\n{}".format(img_reconstruct, img_unpool))


# ================================= visualization ==================================
# print("池化前尺寸:{}\n池化後尺寸:{}".format(img_tensor.shape, img_pool.shape))
# img_pool = transform_invert(img_pool[0, 0:3, ...], img_transform)
# img_raw = transform_invert(img_tensor.squeeze(), img_transform)
# plt.subplot(122).imshow(img_pool)
# plt.subplot(121).imshow(img_raw)
# plt.show()

 

Keras實現

from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

 

One more thing

卷積神經網絡學到的模式具有平移不變性(translation invariant)。這使得卷積神經網絡在處理圖像時可以高效利用數據(因爲視覺世界從根本上具有平移不變性),卷積神經網絡可以學到模式的空間層次結構。

卷積運算的三個重要思想:稀疏交互、參數共享、等變表示

稀疏連接  ----傳統的網絡層是全連接的,卷積層通過使用核矩陣來實現稀疏交互(也稱作稀疏連接,或者稀疏權重),這降低了網絡的參數和計算量,不僅減少了模型的存儲需求,也降低了計算複雜度。

參數共享  ---- 傳統的全連接層中,權重矩陣不同位置處的參數相互獨立,卷積層中,同一個核會在輸入的不同區域做同樣的卷積運算。傳統的全連接層權重只使用一次,而卷積層權重會共享。

等變表示(平移等變性)--如果一個函數滿足:輸入發生改變,輸出也以同樣的方式改變,則稱它是等變的equivariant 。如果函數 f(x),g(x)滿足 f(g(x)) =g(f(x))  ,則稱 f(x)對於變換 g(x)具有等變性。(平移等變性的意義在於,假如圖像裏有一隻貓,那麼無論它在圖像的左邊還是右邊,模型都應該把它識別成貓,也就是說模型輸出對於平移變換來說應該是等變的)

對於卷積層,它具有平移等變的性質:如果 g 是輸入的任何一個平移函數(如:將圖片向右移動一個像素),則下面的兩個操作的結果是相同的:

先卷積,再應用平移g  =  先應用平移g,再卷積 

但是:卷積對於某些變換並不是等變的:如縮放變換(改變圖像的尺寸)、角度變換(旋轉)

 

池化運算--平移近似不變性

池化除了能夠降低參數量。還有平移近似不變性。

1.當輸入做出了少量的平移時,最大池化能夠獲得輸出的近似不變性。即:當輸入平移一個微小的量時,經過最大池化的輸出近似不變

2.局部平移不變性是個很重要的性質。該性質表明:網絡只關心某個特徵是否出現,而不關心它出現的具體位置。如:判斷一張圖片是否是人臉時,它並不關心眼睛的具體位置,只需要知道有眼睛,有嘴巴,有鼻子。 但是人臉識別就要知道位置信息了。

3.對於平移,伸縮,旋轉操作的不變性,對於輸入的少量平移基本不變。這就意味着模型會更加的穩定,魯班。

資源:

http://www.huaxiaozhuan.com/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/chapters/5_CNN.html

《神經網絡與深度學習》-邱錫鵬

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