欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深度卷积网络:第三课

程序员文章站 2022-04-15 23:08:40
深度卷积网络:第三课概念:例子:找橘猫思路一思路二卷积(Convolution)⚠️ 尺寸问题池化(Pooling):一种缩小图像的方法转置卷积(Transposed Convolution):逐层生成一张不断增大的图像卷积和转置卷积的关系:卷积网络的运作例子:AlphaGo的基本原理网络的输入:one-hot编码研究一个已经训练好的简单下棋模型中的3*3卷积核。卷积网络与全联接网络的比较卷积神经网络的运作和训练(理论部分)用卷积网络解决MNIST问题批规范化层(Batch Normalization, B...

本文是《深度卷积网络 原理与实践》第4章的整理。

概念:

  1. 通道(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 - n1s\left \lfloor \frac{n-1}{s} \right \rfloor+1
n k s - nks\left \lfloor \frac{n-k}{s} \right \rfloor+1
输入图像尺寸 Pooling Kernel尺寸 Pooling步长 输出图像尺寸
n a - n-a+1
n - b n1b\left \lfloor \frac{n-1}{b} \right \rfloor+1

...\left \lfloor ... \right \rfloor表示向下取整数。
其实Pooling和卷积核的尺寸计算一致。

  • 推导:
  • 加完Padding后图像的尺寸为(n+2m)(n+2m)(n+2*m)*(n+2*m)。我们其实可以固定卷积核左上角来计算,然后看卷积核左下角在卷积核在图像右下角这个情况下的坐标,所以为n+2m(k1)n+2*m-(k-1)
  • 则无Stride情况下,输出的图像为:(n+2m(k1))(n+2m(k1))(n+2*m-(k-1)) \cdot (n+2*m-(k-1))

一般情况下,我们经常取k=2m+1,即奇数大小的卷积核,这样在合适的padding(m个)之后,原来图像经过卷积之后还是n*n的。

  • 如果有步长s,无卷积核,输入图像的尺寸为nnn*n,输出图像尺寸为n1s+1\left \lfloor \frac{n-1}{s} \right \rfloor+1
    [依旧采用固定左上角,实际上问题等价于1到n里面有多少个被s除余1的数字,即等价于0到n-1里面有几个被s除余1的数字,即为(n-1)/s向下取整数+1,因为从0开始算]
  • 如果有步长s,有卷积核k,输入图像的尺寸为nnn*n,输出图像尺寸为nks+1\left \lfloor \frac{n-k}{s} \right \rfloor+1,这里的数字不同框架会略有不同。在MXNet中的规范是向下取整数。

池化(Pooling):一种缩小图像的方法

  • 对卷积后的图像进一步变换(局部):

    • 最大池化:可取最大值,即标志着图中是否存在这个特征。
    • 平均池化:平均值,标志着图中这个特征的密度。
    • 可只在n*n的区域取最大值或平均值。
  • 全局池化

    • 全局平均池化:取整张图像的平均值
    • 全局最大池化:取整张图像的最大值
    • 经常放在网络的末端。例如,在一个10分类任务中最后我们可以得到10个小图,然后对10个小图依次做全局平均池化可以得到10个数,再经过SoftMax可以得到最后的10个概率。
  • 例子:
    大小为2,步长为2,最大池化。
    深度卷积网络:第三课

转置卷积(Transposed Convolution):逐层生成一张不断增大的图像

  • 一种不严谨的称呼是反卷积

卷积和转置卷积的关系:

深度卷积网络:第三课

卷积网络的运作

  • 输出a张图片,通过a*b个卷积核可以得到b张图片。
  • 从输出2张图片到输出3张图片的例子:
    深度卷积网络:第三课

例子:AlphaGo的基本原理

网络的输入:one-hot编码

  1. 可以将19*19的棋盘上的棋子用-1,1,0表示。
  2. :它和它直接相连的同一方棋子周围的空点个数(只看上下左右方向,不包括斜线方向)。例如,下图中,横着的三个连着的棋子(灰色)的气都是4,而左上角的棋子的气是3。
    深度卷积网络:第三课
    之后可以采用one-hot编码来表示:有棋子的地方为1,否则为0。
  • 因此,我们可以将当前19*19的棋盘拆解为若干个特征层进行输入:
  1. 本方棋子
  2. 对方棋子
  3. 空点
  4. 各个气数的棋子
  5. 最后一手的位置

研究一个已经训练好的简单下棋模型中的3*3卷积核。

这个简化的网络的模型可以如下表示:

  • 我们可以研究对于不同特征层的卷积核的值,并对参数予以解释。然后将8个特征层经过卷积后的输出加和在一起得到最后的结果。
  • RQ:
    此模型的一个问题在于,如果去掉第8层(最后一手的位置),对于整体预测影响不大,而这个问题再更深的神经网络中将得到解决。

根据SoftMax的定义,如果两个点的分数差XX,选择的概率就差eXe^X倍。

卷积网络与全联接网络的比较

实际上,通过简单的计算,我们可以发现,对于完全符合卷积核的形式,卷积之后值最大。相反,如果不完全符合卷积核的形式,它的值就相对比较小。此时如果再进行ReLU操作,就会留下正值,即留下与特征匹配的位置。因此ReLU很适合与卷积配合

考虑以下问题:

  • 输入:3张32*32的图像(因为每张图像有3个通道。实际上就是一张彩色图)
  • 输出:64张30*30的图像

如果使用卷积神经网络

  • 需要3*64=192个333*3的卷积核,因为[(3230+1)=3][(32-30+1)=3]
  • 1个卷积核需要9个参数
  • 此外,每得到一个最终图像需要一个偏置项,即一共64个。
  • 因此,此问题如果使用卷积神经网络一共需要64+192*9=1792个参数

如果使用全连接神经网络:

  • 因为输出有643030=5760064*30*30=57600个像素,所以需要57600个神经元。
  • 因为每个神经元与33232=30723*32*32=3072个输入相连,再加上一个偏置,所以每个神经元有3073个参数。
  • 因此,此问题如果使用全连接神经网络一共需要57600*3073=177004800个参数,远比卷积神经网络多。因而容易过拟合。

卷积神经网络的运作和训练(理论部分)

  • 一个简单的例子(MSE损失):
    深度卷积网络:第三课
    前向传播:
    O11=x11w11+x12w12+x21w21+x22w22+bO_{11}=x_{11}w_{11}+x_{12}w_{12}+x_{21}w_{21}+x_{22}w_{22}+b
    O21=...O_{21}=...
    O12=...O_{12}=...
    O22=x22w22+x23w23+x32w32+x33w33+bO_{22}=x_{22}w_{22}+x_{23}w_{23}+x_{32}w_{32}+x_{33}w_{33}+b
    OUT=MAX(O11,O12,O21,O22)OUT = MAX(O_{11},O_{12},O_{21},O_{22})
    LOSS=LOSSMSE=(OUT)2LOSS=LOSS_{MSE}=(OUT-期望输出)^2
    反向传播:
  1. LOSSOUT=2(OUT)\frac{\partial LOSS}{\partial OUT}=2(OUT-期望输出)

  2. OUTOi,j\frac{\partial OUT}{\partial O_{i,j}}

    • 如果Oi,jO_{i,j}最大:OUTOi,j=1\frac{\partial OUT}{\partial O_{i,j}}=1
    • 否则:OUTOi,j=0\frac{\partial OUT}{\partial O_{i,j}}=0
  3. 再结合对应卷积核的定义就可以计算出Oi,jwk,l\frac{\partial O_{i,j}}{\partial w_{k,l}}

  4. 结合上面三个可以得到最后的LOSSwk,l\frac{\partial LOSS}{\partial w_k,l}

  • 更快的方法可以参考:im2col

用卷积网络解决MNIST问题

批规范化层(Batch Normalization, BN)

批规范化层,可以加速网络的收敛。常常防止在非线性激活层之前,甚者可以放在每个非线性激活层之前。

  • 参数个数为: 22*通道数

代码实现

  • 代码: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的量级,所以要耐心点…不是代码问题