卷积神经网络
1、卷积(Convolution)
2、池化 (Pooling)
3、ReLU 激活函数
4、批归一化 (Batch Normalization)
5、丢弃法 (Dropout)
卷积(Convolution)
1、卷积计算
2、填充(padding)
3、步幅 (stride)
4、感受野 (Receptive Field)
5、多输入通道、多输出通道和批量操作
卷积计算
填充(padding)
为了避免卷积后的图片尺寸变小,通常会在图片外围进行填充(padding)
数组经过卷积后,图片尺寸会变小,卷据输出特征图的尺寸计算方法如下
H(out) = H - K(h) + 1
W(out) = W - W(h) + 1
步幅(stride)
卷积核每次滑动一个像素点,步幅为 1
当宽度和高度方向的步幅度分别为 S(h) 、S(w) 时,特征图的尺寸计算公式为:
H(out) = (H + 2P(h) - K(h)) / S(h) + 1
W(out) = (H + 2P(w) - K(w)) / S(w) + 1
感受野(Receptive Field)
输出图上的每个点,对应的输入图上的像素范围
多输入通道、多输出通道和批量操作
**1、多输入通道场景**
输入图片的通道数为 C(in)
输入图片数据的形状为 C(in) * H(in) * W(in)
卷积核数组形状为 C(in) * k(h) * k(w)
将 C(in) 个通道的计算结果相加,得到一个形状为 H(out) * W(out) 的二位数组
**2、多输出通道场景**
输出特征图也具有多个通道 C(out),我们需要设计 C(out) 个维度的 C(in)*K(h)*K(w) 的卷积核
卷积核的数组维度为 C(out) * C(in) * K(h) * K(w)
输出图的数组维度为 C(out) * H(out) * W(out) 的三维数组
**3、批量操作**
操作多条数据
输入数据维度为 N * C(in) * H(in) * W(in)
卷积核维度为 C(out) * C(in) * K(h) * K(w)
输出特征图维度为 N * C(out) * H(out) * W(out)
飞桨卷积API
paddle.fluid.dygraph.nn.Conv2D
# 常用参数
1、name_scope # 卷积层的名字,数据类型为字符串
2、num_filters # 输出通道数,相当于 C(out)
3、filter_size # 卷积核大小,可以是整数,比如 3,或列表 [3,3]
4、stride # 步幅,整数 比如 2,或列表[2,2]
5、padding # 填充大小,可以是整数,比如 1,或列表 [1,1]
6、act # 激活函数,卷积操作完成后,使用此激活函数作用在神经元上
[N,C(in),H(in),W(in)] # 输入数据维度
[N,num_filters,H(out),W(out)] # 输出数据维度
[num_filters,C(in),filter_size_h,filter_size_w] # 卷积核的维度, 权重参数w维度
[num_filters] # 偏置参数 b 的维度
黑白边界检测
import matplotlib.pyplot as plt
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D
from paddle.fluid.initializer import NumpyArryInitializer
%matplotlib inline
with fluid.dygraph.guard():
# 创建一个数组,后续通过此数组创建 卷积核
w = np.array([1,0,-1],dtype = "float32")
# 将数组的维度调整为 [C(out),C(in),K(h),k(w)] 的四维数据
w = w.reshape([1,1,1,3])
# Conv2D 创建二维卷积核,输出通道为1,数据维度为[1,3],
# fluid.ParamAttr() 创建一个参数属性对象,可设置参数的名称初始化方式、学习率、正则化规则、是否需要训练、梯度裁剪方式、是否做模型平均等属性.
# initializer 数据的初始化方式
conv = Conv2D('conv',num_filters = 1,filter_size =[1,3],param_attr = fluid.ParamAttr(initializer = NumpyArrayIntializer(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 = fluid.dygraph.to_variable(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 ='qray')
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()
# 查看卷积层的参数
with fluid.dygraph.guard():
# 通过 conv.parameters()查看卷积层的参数,返回值是list,包含两个元素
print(conv.parameters())
# 查看卷积层的权重参数名字和数值
print(conv.parameters()[0].name, conv.parameters()[0].numpy())
# 参看卷积层的偏置参数名字和数值
print(conv.parameters()[1].name, conv.parameters()[1].numpy())
图片中物体边缘检测
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D
from paddle.fluid.initializer import NumpyArrayInitializer
img = Image.open('./work/images/section1/000000098520.jpg')
with fluid.dygraph.guard():
# 创建一个数组, 调整为 [C(out),C(in),K(h),k(w)] 的四维数据 ,后面用于创建卷积核
w = np.array([[-1,-1,-1], [-1,8,-1], [-1,-1,-1]], dtype='float32')/8
w = w.reshape([1,1,3,3])
# 由于输入的通道是3(RPG),所以调整卷积核的维度为 [1,3,3,3]
w = np.repeat(w,3,axis = 1)
# 根据 w 创建 2 维卷积核
conv = Conv2D('conv', num_filters=1, filter_size=[3, 3],
param_attr=fluid.ParamAttr(
initializer=NumpyArrayInitializer(value=w)))
# 将读取的图片转化成 float32 类型的 numpy 数组
x = np.array(img).astype('float32')
# numpy 读取出的图片形状为 [H,w,3],所以将数据进行调整
x = np.transpose(x,(2,0,1))
# 将数据形状调整为 [N, C, H, W]格式
x = x.reshape(1,3.image.height,img.width)
# 将 numpy.ndarray 转化成 paddle 中的 tensor
x = fluid.dygraph.to_variable(x)
y = conv(x)
out = y.numpy()
plt.figure(figsize=(20, 10))
f = plt.subplot(121)
f.set_title('input image', fontsize=15)
plt.imshow(img)
f = plt.subplot(122)
f.set_title('output feature map', fontsize=15)
plt.imshow(out.squeeze(), cmap='gray')
plt.show()
图像均值模糊
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D
from paddle.fluid.initializer import NumpyArrayInitializer
# 读入图片并转成numpy.ndarray
#img = Image.open('./images/section1/000000001584.jpg')
img = Image.open('./work/images/section1/000000355610.jpg').convert('L')
img = np.array(img)
# 换成灰度图
with fluid.dygraph.guard():
# 创建初始化参数
w = np.ones([1, 1, 5, 5], dtype = 'float32')/25
conv = Conv2D('conv', num_filters=1, filter_size=[5, 5],
param_attr=fluid.ParamAttr(
initializer=NumpyArrayInitializer(value=w)))
x = img.astype('float32')
x = x.reshape(1,1,img.shape[0], img.shape[1])
x = fluid.dygraph.to_variable(x)
y = conv(x)
out = y.numpy()
plt.figure(figsize=(20, 12))
f = plt.subplot(121)
f.set_title('input image')
plt.imshow(img, cmap='gray')
f = plt.subplot(122)
f.set_title('output feature map')
out = out.squeeze()
plt.imshow(out, cmap='gray')
plt.show()
池化 pooling
"""
池化是使用某以位置相邻输出的总体统计特征代替网络在该位置的输出,池化后的图像会变小,后面接全连接层,可以减少神经元的个数,节省存储空间并提高计算效率。
与卷积核类似池化窗口也在图片上滑动,每次滑动步长为步幅。
"""
常用池化方式
1、平均池化
2、最大值池化
假设第一行填充 P(h1) , 最后一行填充 P(h2), 步幅 为 S(h),S(w).
H(out) = (H + P(h1) + P(h2) - K(h)) / S(h) + 1
W(out) = (W + P(w1) + P(w2) - K(w)) / S(w) + 1
ReLU激活函数
数据进行批归一化处理
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import BatchNorm
data = np.array([[1,2,3], [4,5,6], [7,8,9]]).astype('float32')
with fluid.dygraph.guard():
# 输入数据维度为 [N,K], num_channels 等于K
# BatchNorm 用于对一批样本进行归一化处理
bn = BatchNorm('bn',num_channel = 3)
x = fluid.dygraph.to_variable(data)
y = bn(x)
print('output of BatchNorm 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))
丢弃法
"""
丢弃法 是深度学习中一种常用的控制过拟合的方法,其做法是 在神经网络学习中,随机删除一部分神经元。训练时,随机选出一部分神经元将其输出设置为 0,这些神经元将不对外传递信号
1、downgrade_in_infer
训练时以比例 r 随机丢弃一分部神经元,不向后传递它们的信号,预测时向后传递多所有神经元的信号,但将每个神经元上的数值 乘以 (1 - r)
2、upscale_in_train
训练时以比例 r 随机丢弃一部分神经元,不向后传递信号,但是将保留的神经元上的数值乘以 ( 1-r ) ,预测时向后传递的神经元信号,不做任何处理。
"""
paddle.fluid.layers.dropout
1、dropout_implementation —— 指定用哪种方式对神经元进行操作
参数 1 : downgrade_in_infer (默认)
参数 2 : upscale_in_train
dropout API 参数
1、x —— 数据类型的 Tensor, 需采用丢弃法进行操作
2、dropout_prob —— 对 x 中元素进行丢弃的概率
3、is_test —— 是否运行在测试阶段,由于dropout在训练核测试阶段的表现不一样,通过此参数控制其表现,默认False
4、 dropout_implementation,丢弃法的实现方式,有'downgrade_in_infer'和'upscale_in_train'两种,具体情况请见上面的说明,默认是'downgrade_in_infer'。
dropout 操作
import numpy as np
import paddle
import paddle.fluid as fluid
# 设置随机种子,以保证每次运行结果一致
np.random.seed(100)
# 创建数据 [N, C, H, W], 对应卷积层的输出
data1 = np.random.rand(2,3,3,3).astype('float32')
# 创建数据[N, K],一般对应全连接层的输出
data2 = np.arange(1,13).reshape([-1,3]).astype('float32')
# 使用dropout 作用在输入数据上
with fluid.dygraph.guard():
x1 = fluid.dygraph.to_variable(data1)
out_1 = fluid.layers.dropout(x1,dropout_prob=0.5,is_test=False)
out_2 = fluid.layers.dropout(x1,dropout_prob = 0.5,is_test = True)
x2 = fluid.dygraph.to_variable(data2)
out2_1 = fluid.layers.dropout(x2, dropout_prob=0.5, dropout_implementation='upscale_in_train')
out2_2 = fluid.layers.dropout(x2, dropout_prob=0.5, dropout_implementation='upscale_in_train', is_test=True
print('x1 {}, \n out1_1 \n {}, \n out1_2 \n {}'.format(data1, out1_1.numpy(), out1_2.numpy()))
print('x2 {}, \n out2_1 \n {}, \n out2_2 \n {}'.format(data2, out2_1.numpy(), out2_2.numpy()))