第三章 计算机视觉(中)
课程连接:话不多说,这么良心的课程赶快扫码上车!
2.4 激活函数ReLU和Sigmoid
激活函数:通常在卷积或者全连接这样的线性操作之后,会加上一个非线性的函数,作用在每一个神经元的输出上,从而实现非线性变换的效果
多个对x的线性变换 等价于 一个对x的线性变换
Sigmoid函数
1.在x的数值非常大或者非常小的地方,导数都接近于0Sigmoif函数只有在x接近于0的地方,导数才比较大,但最大值只有1/4
即使x的数值接近于0,但其梯度最大不超过y的梯度的1/4
如果有多层网络使用Sigmoid激活函数,将导致较靠前的哪些层,梯度变化非常小
在神经网络里,将这种反向传播后,梯度值衰减到接近于0的现象称作梯度消失现象
卷积神经网络基础 ——ReLU激活函数
在x>0的地方,ReLU函数的导数为1,能够将y的梯度完整传递给x,不会引起梯度消失
目前用的较多的激活函数是ReLU
下面的程序画出了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()
2.5 批归一化(Batch Normalization)
数据分布和模型的数值稳定性
模型收敛-需要稳定的数据分布
浅层神经网络=》对输入数据做标准化(归一化) 满足均值为0,方差为1的分布
深层神经网络=》仅仅对输入数据标准化不够
参数在学习调整中,输入数据经过多层后,相对于子模型来说,变化巨大,它观察到的输入数据的分布是不稳定的
Batch Mormalization
对中间层的输出做标准化,可以保证在网络学习的过程中,网络层的输出具有稳定的分布
- 可以使学习快速进行(能够使用较大的学习率)
- 可以降低模型对初始值的敏感性
- 可以从一定程度上抑制过拟合
计算过程
ε-为了数值稳定性,在方差为0的极端情况下,不会出现分母为0的情况
使用paddle的API BatchNorm计算归一化
或者用numpy计算均值,方差,归一化的输出
示例1代码
# 输入数据形状是 [N, K]时的示例
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')
# 使用BatchNorm计算归一化的输出
with fluid.dygraph.guard():
# 输入数据维度[N, K],num_channels等于K
bn = BatchNorm(num_channels=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))
# 建议读者对第1和第2个特征进行验证,观察numpy计算结果与paddle计算结果是否一致
输入数据形状是[N,C,H,W] 一般对应着卷积层的输出
沿着C这一维度展开,分别对每个通道计算计算 N个样本 总共NxHxW个像素点均值和方差
示例2代码
# 输入数据形状是[N, C, H, W]时的batchnorm示例
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import BatchNorm
# 设置随机数种子,这样可以保证每次运行结果一致
np.random.seed(100)
# 创建数据
data = np.random.rand(2,3,3,3).astype('float32')
# 使用BatchNorm计算归一化的输出
with fluid.dygraph.guard():
# 输入数据维度[N, C, H, W],num_channels等于C
bn = BatchNorm(num_channels=3)
x = fluid.dygraph.to_variable(data)
y = bn(x)
print('input of BatchNorm Layer: \n {}'.format(x.numpy()))
print('output of BatchNorm 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计算出来的输出
# 与BatchNorm算子的结果略有差别,
# 因为在BatchNorm算子为了保证数值的稳定性,
# 在分母里面加上了一个比较小的浮点数epsilon=1e-05
测试时不测试样本内均值和方差,用训练时候保存好的整个数据集的均值和方差
训练通过滚动平均的方式,计算在整个数据集上的均值和方差并存
把上轮均值和方差存下来,和本轮的均值和方差做加权平均(如上图右侧所示)
0.9是滚动平均的momentum
2.6 丢弃法(Dropout)
用于抑制过拟合
训练5次会比较耗时
训练阶段:每次随机的删除一部分神经元,不向前传播携带的信息,相当于每次都是让不懂的模型在学习。抑制掉了某些可能会引起过拟合的神经元
测试阶段:向前传播所有神经元的信息,相当于让这些不同的模型一起工作
实例
# 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)
out1_1 = fluid.layers.dropout(x1, dropout_prob=0.5, is_test=False)
out1_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()))
downgrade_in_infer
训练时以比例r随机丢弃一部分神经元,不向后传递它们的信号;预测时向后传递所有神经元的信号,但是将每个神经元上的数值乘以 (1−r)(1 - r)(1−r)。
upscale_in_train
训练时以比例r随机丢弃一部分神经元,不向后传递它们的信号,但是将那些被保留的神经元上的数值除以 (1−r)(1 - r)(1−r);预测时向后传递所有神经元的信号,不做任何处理。
在飞桨dropout API中,paddle.fluid.layers.dropout通过dropout_implementation参数来指定用哪种方式对神经元进行操作,dropout_implementation参数的可选值是’downgrade_in_infer’或’upscale_in_train’,缺省值是’downgrade_in_infer’。
飞桨dropout API包含的主要参数如下:
x (Variable)- 数据类型是Tensor,需要采用丢弃法进行操作的对象。
dropout_prob (float32) - 对xxx中元素进行丢弃的概率,即输入单元设置为0的概率,该参数对元素的丢弃概率是针对于每一个元素而言而不是对所有的元素而言。举例说,假设矩阵内有12个数字,经过概率为0.5的dropout未必一定有6个零。
is_test (bool) - 是否运行在测试阶段,由于dropout在训练和测试阶段表现不一样,通过此参数控制其表现,默认值为’False’。
dropout_implementation (str) - 丢弃法的实现方式,有’downgrade_in_infer’和’upscale_in_train’两种,具体情况请见上面的说明,默认是’downgrade_in_infer’。