本文主要为深度之眼pytorch训练营二期学习笔记,详细课程内容移步:深度之眼 https://ai.deepshare.net/index
目录
背景知识
对于处理图像数据,我们为什么不用全连接层而选择了卷积层呢?
(一):参数太多。如果图像大小为100×100×3(即图像高度为100,宽度为100,3个颜色通道:RGB)使用全连接层,后一层单独每一个神经元到输入层上就有100×100×3 = 30,000个相互独立的连接,每个连接都对应一个权重参数)使用全连接层参数会很多。而卷积层(由于局部连接和全局共享的特性, 如使用一个3x3x3(通道数)的卷积核扫描图像,一层特征图就只有3x3x3个参数)和全连接神经网络相比,要学习的参数更少。
(二):局部不变性特征。自然图像中的物体都具有局部不变性特征,比如在尺度缩放、平移、旋转等操作不影响其语义信息。而全连接网络很难提取这些局部不变特征。
一:卷积及其运算
卷积层(Convolution Layer):通常用作对输入层输入数据进行特征提取,通过卷积核矩阵对原始数据中隐含关联性的一种抽象。(卷积核的作用就相当于是一个滤波器)
二:池化及其运算
池化层(Pooling Layer)又称为降采样层(Downsampling Layer),作用是对感受域内的特征进行筛选,能够有效地降低输出特征尺度,进而减少模型所需要的参数量。(输入数据发生微小偏差时,池化仍会返回相同的结果。因此,池化对输入数据的微小偏差具有鲁棒性)。常见的池化有最大池化(返回感受域内的最大的元素),平均池化(返回感受域内元素的平均值)。
尺寸计算:
三:激活层
激活层(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
《神经网络与深度学习》-邱锡鹏