深度之眼【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

《神经网络与深度学习》-邱锡鹏

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