计算机视觉-卷积神经网络基础(paddle学习笔记)

计算机视觉概述

计算机视觉:让机器学会如何去“看”

  1. 识别图像总的物体
  2. 检测出物体所在位置
  3. 对目标物体进行追踪
  4. 理解并能描述图像里面的场景和故事

最基本的子任务:

  • a.图像分类
  • b.物体检测
  • c.图像语义分割
  • d.实例分割

计算机视觉-卷积神经网络基础(paddle学习笔记)_第1张图片

卷积神经网络基础

主要内容

  • 卷积(Convolution)
  • 池化(Pooling)
  • 批归一化(BatchNorm)
  • 丢弃法(Dropout)

计算机视觉-卷积神经网络基础(paddle学习笔记)_第2张图片
全连接网络的问题

计算机视觉-卷积神经网络基础(paddle学习笔记)_第3张图片

卷积(Convolution)

卷积操作的数学定义

b [ i , j ] = ∑ u , v a [ i + u , j + v ] ⋅ w [ u , v ] b[i ,j] = \sum_{u,v} a[i+u,j+v]\cdot w[u,v] b[i,j]=u,va[i+u,j+v]w[u,v]

计算机视觉-卷积神经网络基础(paddle学习笔记)_第4张图片
在卷积神经网络示意图中 用 ⨂ \bigotimes 符号表示卷积操作

卷积示例

计算机视觉-卷积神经网络基础(paddle学习笔记)_第5张图片
假设输入图片大小为 H × \times ×W,卷积核大小为 k h × k w k_h\times k_w kh×kw,输出特征图尺寸是

H o u t = H − k h + 1 H_{out} =H- k_h + 1 Hout=Hkh+1
W o u t = H − k w + 1 W_{out}=H - k_w + 1 Wout=Hkw+1

卷积:填充

计算机视觉-卷积神经网络基础(paddle学习笔记)_第6张图片

卷积:步幅

计算机视觉-卷积神经网络基础(paddle学习笔记)_第7张图片
新输出图片尺寸公式
假设输入图片大小为 H × \times ×W,卷积核大小为 k h × k w k_h\times k_w kh×kw,补零的行数和列数(padding)为p,步幅(stride)为s,输出特征图尺寸是

H o u t = H + 2 p h − k h s h + 1 H_{out} =\frac{H + 2p_h- k_h}{s_h} + 1 Hout=shH+2phkh+1 W o u t = W + 2 p w − k w s w + 1 W_{out} =\frac{W + 2p_w- k_w}{s_w} + 1 Wout=swW+2pwkw+1

卷积:多通道输入

计算机视觉-卷积神经网络基础(paddle学习笔记)_第8张图片

卷积:批量操作多张图像

计算机视觉-卷积神经网络基础(paddle学习笔记)_第9张图片

卷积:感受野

计算机视觉-卷积神经网络基础(paddle学习笔记)_第10张图片

卷积:黑白检测

计算机视觉-卷积神经网络基础(paddle学习笔记)_第11张图片
思考:当卷积核处在图片上不同位置输出什么样?
黑白边界处输出1
其他位置输出0

计算机视觉-卷积神经网络基础(paddle学习笔记)_第12张图片

import matplotlib.pyplot as plt
import numpy as np
import paddle
from paddle.nn import Conv2D
from paddle.nn.initializer import Assign
%matplotlib inline

# 创建初始化权重参数w
w = np.array([1, 0, -1], dtype='float32')
# 将权重参数调整成维度为[cout, cin, kh, kw]的四维张量
w = w.reshape([1, 1, 1, 3])
# 创建卷积算子,设置输出通道数,卷积核大小,和初始化权重参数
# kernel_size = [1, 3]表示kh = 1, kw=3
# 创建卷积算子的时候,通过参数属性weight_attr指定参数初始化方式
# 这里的初始化方式时,从numpy.ndarray初始化卷积参数
conv = Conv2D(in_channels=1, out_channels=1, kernel_size=[1, 3],
       weight_attr=paddle.ParamAttr(
          initializer=Assign(value=w)))

# 创建输入图片,图片左边的像素点取值为1,右边的像素点取值为0
img = np.ones([50,50], dtype='float32')
img[:, 30:] = 0.
# 将图片形状调整为[N, C, H, W]的形式
x = img.reshape([1,1,50,50])
# 将numpy.ndarray转化成paddle中的tensor
x = paddle.to_tensor(x)
# 使用卷积算子作用在输入图片上
y = conv(x)
# 将输出tensor转化为numpy.ndarray
out = y.numpy()
f = plt.subplot(121)
f.set_title('input image', fontsize=15)
plt.imshow(img, cmap='gray')
f = plt.subplot(122)
f.set_title('output featuremap', fontsize=15)
# 卷积算子Conv2D输出数据形状为[N, C, H, W]形式
# 此处N, C=1,输出数据形状为[1, 1, H, W],是4维数组
# 但是画图函数plt.imshow画灰度图时,只接受2维数组
# 通过numpy.squeeze函数将大小为1的维度消除
plt.imshow(out.squeeze(), cmap='gray')
plt.show()

池化(Pooling)

池化是使用某一位置的相邻输出的总体统计特征代替网络在该位置的输出
其好处是当输入数据做出少量平移时,经过池化函数后的大多数输出还能保持不变。比如:当识别一张图像是否是人脸时,我们需要知道人脸左边有一只眼睛,右边也有一只眼睛,而不需要知道眼睛的精确位置,这时候通过池化某一片区域的像素点来得到总体统计特征会显得很有用。由于池化之后特征图会变得更小,如果后面连接的是全连接层,能有效的减小神经元的个数,节省存储空间并提高计算效率。 如 图 所示,将一个2×2的区域池化成一个像素点。通常有两种方法,平均池化和最大池化。

与卷积核类似,池化窗口在图片上滑动时,每次移动的步长称为步幅,当宽和高方向的移动大小不一样时,分别用 s w s_w sw s h s_h sh表示。也可以对需要进行池化的图片进行填充,填充方式与卷积类似,假设在第一行之前填充 p h 1 p_{h1} ph1行,在最后一行后面填充 p h 2 p_{h2} ph2行。在第一列之前填充 p w 1 p_{w1} pw1列,在最后一列之后填充 p w 2 p_{w2} pw2列,则池化层的输出特征图大小为:

计算机视觉-卷积神经网络基础(paddle学习笔记)_第13张图片

H o u t = H + p h 1 + p h 2 − k h s h + 1 H_{out} =\frac{H + p_{h1}+p_{h2}- k_h}{s_h} + 1 Hout=shH+ph1+ph2kh+1 H o u t = H + p w 1 + p w 2 − k w s w + 1 H_{out} =\frac{H + p_{w1}+p_{w2}- k_w}{s_w} + 1 Hout=swH+pw1+pw2kw+1

池化的性质

计算机视觉-卷积神经网络基础(paddle学习笔记)_第14张图片

ReLU激活函数

在神经网络发展的早期,Sigmoid函数用的比较多,而目前用的较多的激活函数是ReLU。这是因为Sigmoid函数在反向传播过程中,容易造成梯度的衰减。让我们仔细观察Sigmoid函数的形式,就能发现这一问题。

Sigmoid激活函数定义如下:

y = 1 1 + e − x y= \frac{1}{1+e^{-x}} y=1+ex1

ReLU激活函数的定义如下:

y = { 0 , ( x < 0 ) x , ( x ≥ 0 ) y=\left\{ \begin{aligned} 0 ,\quad (x<0)\\ x , \quad (x \ge 0)\\ \end{aligned} \right. y={0,(x<0)x,(x0)

下面的程序画出了Sigmoid和ReLU函数的曲线图:

# ReLU和Sigmoid激活函数示意图
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
plt.figure(figsize=(10, 5))

# 创建数据x
x = np.arange(-10, 10, 0.1)

# 计算Sigmoid函数
s = 1.0 / (1 + np.exp(0. - x))

# 计算ReLU函数
y = np.clip(x, a_min=0., a_max=None)

#####################################
# 以下部分为画图代码
f = plt.subplot(121)
plt.plot(x, s, color='r')
currentAxis=plt.gca()
plt.text(-9.0, 0.9, r'$y=Sigmoid(x)$', fontsize=13)
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

f = plt.subplot(122)
plt.plot(x, y, color='g')
plt.text(-3.0, 9, r'$y=ReLU(x)$', fontsize=13)
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

plt.show()

计算机视觉-卷积神经网络基础(paddle学习笔记)_第15张图片

批归一化(Batch Normalization)

批归一化方法(Batch Normalization,BatchNorm)是由Ioffe和Szegedy于2015年提出的,已被广泛应用在深度学习中,其目的是对神经网络中间层的输出进行标准化处理,使得中间层的输出更加稳定。

通常我们会对神经网络的数据进行标准化处理,处理后的样本数据集满足均值为0,方差为1的统计分布,这是因为当输入数据的分布比较固定时,有利于算法的稳定和收敛。对于深度神经网络来说,由于参数是不断更新的,即使输入数据已经做过标准化处理,但是对于比较靠后的那些层,其接收到的输入仍然是剧烈变化的,通常会导致数值不稳定,模型很难收敛。BatchNorm能够使神经网络中间层的输出变得更加稳定,并有如下三个优点:

  • 使学习快速进行(能够使用较大的学习率)

  • 降低模型对初始值的敏感性

  • 从一定程度上抑制过拟合

计算机视觉-卷积神经网络基础(paddle学习笔记)_第16张图片
BatchNorm主要思路是在训练时以mini-batch为单位,对神经元的数值进行归一化,使数据的分布满足均值为0,方差为1。
具体计算过程如下:
计算机视觉-卷积神经网络基础(paddle学习笔记)_第17张图片
计算机视觉-卷积神经网络基础(paddle学习笔记)_第18张图片
计算机视觉-卷积神经网络基础(paddle学习笔记)_第19张图片

计算机视觉-卷积神经网络基础(paddle学习笔记)_第20张图片

计算机视觉-卷积神经网络基础(paddle学习笔记)_第21张图片

上面列出的是BatchNorm方法的计算逻辑,下面针对两种类型的输入数据格式分别进行举例。飞桨支持输入数据的维度大小为2、3、4、5四种情况,这里给出的是维度大小为2和4的示例。
示例一: 当输入数据形状是[N,K]时,一般对应全连接层的输出,示例代码如下所示。
这种情况下会分别对K的每一个分量计算N个样本的均值和方差,数据和参数对应如下:

  • 输入 x, [N, K]
  • 输出 y,[N, K]
  • 均值 μ B \mu_B μB [K, ]
  • 方差 σ B 2 \sigma_B^2 σB2, [K, ]
  • 缩放参数 γ \gamma γ, [K, ]
  • 平移参数 β \beta β, [K, ]
# 输入数据形状是 [N, K]时的示例
import numpy as np
import paddle
from paddle.nn import BatchNorm1D
# 创建数据
data = np.array([[1,2,3], [4,5,6], [7,8,9]]).astype('float32')
# 使用BatchNorm1D计算归一化的输出
# 输入数据维度[N, K],num_features等于K
bn = BatchNorm1D(num_features=3)    
x = paddle.to_tensor(data)
y = bn(x)
print('output of BatchNorm1D Layer: \n {}'.format(y.numpy()))

# 使用Numpy计算均值、方差和归一化的输出
# 这里对第0个特征进行验证
a = np.array([1,4,7])
a_mean = a.mean()
a_std = a.std()
b = (a - a_mean) / a_std
print('std {}, mean {}, \n output {}'.format(a_mean, a_std, b))

示例二: 当输入数据形状是[N,C,H,W]时, 一般对应卷积层的输出,示例代码如下所示。

  • 输入 x, [N,C,H,W]
  • 输出 y,[N,C,H,W]
  • 均值 μ B \mu_B μB [C, ]
  • 方差 σ B 2 \sigma_B^2 σB2, [C, ]
  • 缩放参数 γ \gamma γ, [C, ]
  • 平移参数 β \beta β, [C, ]
# 输入数据形状是[N, C, H, W]时的batchnorm示例
import numpy as np
import paddle
from paddle.nn import BatchNorm2D

# 设置随机数种子,这样可以保证每次运行结果一致
np.random.seed(100)
# 创建数据
data = np.random.rand(2,3,3,3).astype('float32')
# 使用BatchNorm2D计算归一化的输出
# 输入数据维度[N, C, H, W],num_features等于C
bn = BatchNorm2D(num_features=3)
x = paddle.to_tensor(data)
y = bn(x)
print('input of BatchNorm2D Layer: \n {}'.format(x.numpy()))
print('output of BatchNorm2D Layer: \n {}'.format(y.numpy()))

# 取出data中第0通道的数据,
# 使用numpy计算均值、方差及归一化的输出
a = data[:, 0, :, :]
a_mean = a.mean()
a_std = a.std()
b = (a - a_mean) / a_std
print('channel 0 of input data: \n {}'.format(a))
print('std {}, mean {}, \n output: \n {}'.format(a_mean, a_std, b))

# 提示:这里通过numpy计算出来的输出
# 与BatchNorm2D算子的结果略有差别,
# 因为在BatchNorm2D算子为了保证数值的稳定性,
# 在分母里面加上了一个比较小的浮点数epsilon=1e-05

丢弃法(Dropout)

计算机视觉-卷积神经网络基础(paddle学习笔记)_第22张图片
在预测场景时,会向前传递所有神经元的信号,可能会引出一个新的问题:训练时由于部分神经元被随机丢弃了,输出数据的总大小会变小。比如:计算其L1范数会比不使用Dropout时变小,但是预测时却没有丢弃神经元,这将导致训练和预测时数据的分布不一样。为了解决这个问题,飞桨支持如下两种方法:

  • downscale_in_infer
    训练时以比例rrr随机丢弃一部分神经元,不向后传递它们的信号;预测时向后传递所有神经元的信号,但是将每个神经元上的数值乘以 (1−r)。

  • upscale_in_train
    训练时以比例rrr随机丢弃一部分神经元,不向后传递它们的信号,但是将那些被保留的神经元上的数值除以 (1−r);预测时向后传递所有神经元的信号,不做任何处理。

你可能感兴趣的:(DL,计算机视觉,人工智能,卷积神经网络)