深度卷积网络:第三课
程序员文章站
2022-04-15 23:08:40
深度卷积网络:第三课概念:例子:找橘猫思路一思路二卷积(Convolution)⚠️ 尺寸问题池化(Pooling):一种缩小图像的方法转置卷积(Transposed Convolution):逐层生成一张不断增大的图像卷积和转置卷积的关系:卷积网络的运作例子:AlphaGo的基本原理网络的输入:one-hot编码研究一个已经训练好的简单下棋模型中的3*3卷积核。卷积网络与全联接网络的比较卷积神经网络的运作和训练(理论部分)用卷积网络解决MNIST问题批规范化层(Batch Normalization, B...
深度卷积网络:第三课
本文是《深度卷积网络 原理与实践》第4章的整理。
概念:
- 通道(channel) / 特征层(feature plane): n张图,可以看成n个通道或者n个特征层。
- 例子:对于彩色图片,电脑会按照{R,G,B}三个通道存储,所以输入的图就已经是3张图。再通过3*n个卷积核就可以得到n张图
例子:找橘猫
思路一
- 定义橘色的色彩范围
- 对图像中的每个点进行判断是否是橘色,如果橘色超过全体点的10%,则认为有橘猫。
- 实际上这个就是一个卷积核为1*1的卷积神经网络。
思路二
- 实质上橘色是橘猫的一个特征,我们也可以选取其他特征,例如橘猫的纹理。
- 我们可以在图像中试图寻找橘猫的纹理,然后计算区域的面积,达到1%面积就可认为图中有橘猫。
- 这个思路中找橘猫纹理,实际上是寻找图像中的局部特征,这个任务可以被卷积神经网络完成。
卷积(Convolution)
通过下面这个例子,我们可以发现卷积这个操作可以提取图片中的一些特征信息。
- 此外,通过卷积操作,原来n*n的图像的尺寸也会减少。
- 卷积是一个线性操作。
- 卷积也可以看成一个滤波器,可能类比于夫琅和费衍射里面的孔,信息光学里面对于频谱的操作❓❓
- 实际卷积中,我们还会对在卷积核输出的数据上再加一项偏置。
⚠️ 尺寸问题
- 假设:
- 原来图像n*n
- 卷积核k*k
- 卷积的步长(Stride) s:卷积核每做完一次卷积后移动几格。
- Padding m,即加m圈零
- 结论:
输入图像尺寸 | 卷积核尺寸 | 卷积步长 | Padding | 输出图像尺寸 |
---|---|---|---|---|
n | k | - | - | n-k+1 |
n | - | - | m | n+2m |
n | k | - | m | n+2m-k+1 |
n | - | s | - | +1 |
n | k | s | - | +1 |
输入图像尺寸 | Pooling Kernel尺寸 | Pooling步长 | 输出图像尺寸 |
---|---|---|---|
n | a | - | n-a+1 |
n | - | b | +1 |
表示向下取整数。
其实Pooling和卷积核的尺寸计算一致。
- 推导:
- 加完Padding后图像的尺寸为。我们其实可以固定卷积核左上角来计算,然后看卷积核左下角在卷积核在图像右下角这个情况下的坐标,所以为。
- 则无Stride情况下,输出的图像为:
一般情况下,我们经常取k=2m+1,即奇数大小的卷积核,这样在合适的padding(m个)之后,原来图像经过卷积之后还是n*n的。
- 如果有步长s,无卷积核,输入图像的尺寸为,输出图像尺寸为
[依旧采用固定左上角,实际上问题等价于1到n里面有多少个被s除余1的数字,即等价于0到n-1里面有几个被s除余1的数字,即为(n-1)/s向下取整数+1,因为从0开始算] - 如果有步长s,有卷积核k,输入图像的尺寸为,输出图像尺寸为,这里的数字不同框架会略有不同。在MXNet中的规范是向下取整数。
池化(Pooling):一种缩小图像的方法
-
对卷积后的图像进一步变换(局部):
- 最大池化:可取最大值,即标志着图中是否存在这个特征。
- 平均池化:平均值,标志着图中这个特征的密度。
- 可只在n*n的区域取最大值或平均值。
-
全局池化:
- 全局平均池化:取整张图像的平均值
- 全局最大池化:取整张图像的最大值
- 经常放在网络的末端。例如,在一个10分类任务中最后我们可以得到10个小图,然后对10个小图依次做全局平均池化可以得到10个数,再经过SoftMax可以得到最后的10个概率。
-
例子:
大小为2,步长为2,最大池化。
转置卷积(Transposed Convolution):逐层生成一张不断增大的图像
- 一种不严谨的称呼是反卷积。
卷积和转置卷积的关系:
卷积网络的运作
- 输出a张图片,通过a*b个卷积核可以得到b张图片。
- 从输出2张图片到输出3张图片的例子:
例子:AlphaGo的基本原理
网络的输入:one-hot编码
- 可以将19*19的棋盘上的棋子用-1,1,0表示。
-
气:它和它直接相连的同一方棋子周围的空点个数(只看上下左右方向,不包括斜线方向)。例如,下图中,横着的三个连着的棋子(灰色)的气都是4,而左上角的棋子的气是3。
之后可以采用one-hot编码来表示:有棋子的地方为1,否则为0。
- 因此,我们可以将当前19*19的棋盘拆解为若干个特征层进行输入:
- 本方棋子
- 对方棋子
- 空点
- 各个气数的棋子
- 最后一手的位置
研究一个已经训练好的简单下棋模型中的3*3卷积核。
这个简化的网络的模型可以如下表示:
- 我们可以研究对于不同特征层的卷积核的值,并对参数予以解释。然后将8个特征层经过卷积后的输出加和在一起得到最后的结果。
- RQ:
此模型的一个问题在于,如果去掉第8层(最后一手的位置),对于整体预测影响不大,而这个问题再更深的神经网络中将得到解决。
根据SoftMax的定义,如果两个点的分数差,选择的概率就差倍。
卷积网络与全联接网络的比较
实际上,通过简单的计算,我们可以发现,对于完全符合卷积核的形式,卷积之后值最大。相反,如果不完全符合卷积核的形式,它的值就相对比较小。此时如果再进行ReLU操作,就会留下正值,即留下与特征匹配的位置。因此ReLU很适合与卷积配合。
考虑以下问题:
- 输入:3张32*32的图像(因为每张图像有3个通道。实际上就是一张彩色图)
- 输出:64张30*30的图像
如果使用卷积神经网络:
- 需要3*64=192个的卷积核,因为。
- 1个卷积核需要9个参数
- 此外,每得到一个最终图像需要一个偏置项,即一共64个。
- 因此,此问题如果使用卷积神经网络一共需要64+192*9=1792个参数
如果使用全连接神经网络:
- 因为输出有个像素,所以需要57600个神经元。
- 因为每个神经元与个输入相连,再加上一个偏置,所以每个神经元有3073个参数。
- 因此,此问题如果使用全连接神经网络一共需要57600*3073=177004800个参数,远比卷积神经网络多。因而容易过拟合。
卷积神经网络的运作和训练(理论部分)
- 一个简单的例子(MSE损失):
前向传播:
反向传播:
-
-
- 如果最大:
- 否则:
-
再结合对应卷积核的定义就可以计算出。
-
结合上面三个可以得到最后的
- 更快的方法可以参考:im2col
用卷积网络解决MNIST问题
批规范化层(Batch Normalization, BN)
批规范化层,可以加速网络的收敛。常常防止在非线性激活层之前,甚者可以放在每个非线性激活层之前。
- 参数个数为:
代码实现
- 代码:1 / 4
import numpy as np
import os
import gzip #为了解压文件
import struct
import logging
import mxnet as mx
import matplotlib.pyplot as plt
logging.getLogger().setLevel(logging.DEBUG)
- 代码:2 / 4
# 辅助读入训练数据
def read_data(label_url,image_url):
with gzip.open(label_url) as flbl: #打开标签文件
magic,num = struct.unpack(">II", flbl.read(8)) #读入标签文件头
label = np.frombuffer(flbl.read(),dtype = np.int8) #读入标签内容
with gzip.open(image_url,'rb') as fimg: #打开图像文件
magic,num,rows,cols = struct.unpack(">IIII",fimg.read(16)) # 读入图像文件头,这里图像是28*28所以rows和cols都会是28
image = np.frombuffer(fimg.read(), dtype=np.uint8) #读入图像内容
image = image.reshape(len(label),1,rows,cols) # 设置为正确的数组格式
imgae = image.astype(np.float32)/255.0 # 归一化到0到1的区间
return (label,image)
# 读入数据
(train_lbl,train_img) = read_data('train-labels-idx1-ubyte.gz','train-images-idx3-ubyte.gz')
(val_lbl,val_img) = read_data('t10k-labels-idx1-ubyte.gz','t10k-images-idx3-ubyte.gz')
- 代码:3 / 4
# 网络架构的定义
# 输入是一张(1,28,28)的图片,即1通道(灰度),28*28的图片
data = mx.symbol.Variable('data')
# 第1个卷积层,有32个5*5的卷积核,采用BN层,relu激活以及最大池化
conv1 = mx.sym.Convolution(data = data, name = "conv1", kernel=(5,5),num_filter=32)
bn1 = mx.sym.BatchNorm(data = conv1, name = "bn1", fix_gamma=False) #对于BN层,我们往往会加上fix_gamma=False这个参数
act1 = mx.sym.Activation(data = bn1, name = "act1", act_type="relu")
pool1 = mx.sym.Pooling(data = act1, name="pool1", pool_type="max", kernel=(3,3), stride=(2,2)) #3*3大小,2*2步长的最大池化层
# 第2个卷积层
conv2 = mx.sym.Convolution(data = pool1, name = "conv2", kernel=(5,5),num_filter=64)
bn2 = mx.sym.BatchNorm(data = conv2, name = "bn2", fix_gamma=False)
act2 = mx.sym.Activation(data = bn2, name = "act2", act_type="relu")
pool2 = mx.sym.Pooling(data = act2, name = "pool2", pool_type="max", kernel=(3,3), stride=(2,2))
# 第3个卷积层,有10个3*3的卷积(为了将数字归成10类,所以通过10个卷积核生成10张图片)
conv3 = mx.sym.Convolution(data = pool2, name="conv3", kernel=(3,3), num_filter=10)
pool3 = mx.sym.Pooling(data = conv3, name = "pool3", global_pool=True, pool_type="avg", kernel=(1,1)) #全局平均池化,最后每张图片会得到一个数字
# 将图像摊平,我感觉Flatten能把多个数组合并成为一个数组[[1],[1]] -> [1,1]我猜测
flatten = mx.sym.Flatten(data=pool3,name="flatten")
# SoftMax层,将10个数变为10个分类的概率
net = mx.sym.SoftmaxOutput(data = flatten, name="softmax")
- 代码:4 / 4:这里可以换成GPU,CPU计算一代的时间大约在min的量级,所以要耐心点…不是代码问题