dropout神经网络_零基础入门深度学习(五):卷积神经网络基础之BN和Dropout

2022-10-22 09:27:28
fe6d028d61ff3d5796fc94158ac9a1ab.png
课程名称 | 零基础入门深度学习授课讲师 | 孙高峰 百度深度学习技术平台部资深研发工程师授课时间 | 每周二、周四晚20:00-21:00编辑整理 | 孙高峰内容来源 | 百度飞桨深度学习集训营出品平台 | 百度飞桨 01 导读 本课程是百度官方开设的零基础入门深度学习课程,主要面向没有深度学习技术基础或者基础薄弱的同学,帮助大家在深度学习领域实现从0到1+的跨越。 从本课程中,你将学习到:
  1. 深度学习基础知识
  2. numpy实现神经网络构建和梯度下降算法
  3. 计算机视觉领域主要方向的原理、实践
  4. 自然语言处理领域主要方向的原理、实践
  5. 个性化推荐算法的原理、实践
本文由百度深度学习技术平台部资深研发工程师孙高峰 为大家带来卷积神经网络基础之BN和Dropout。 02 批归一化(Batch Normalization) 批归一化方法方法 (Batch Normalization,BatchNorm)是由Ioffe和Szegedy于2015年提出的,已被广泛应用在深度学习中,其目的是对神经网络中间层的输出进行标准化处理,使得中间层的输出更加稳定。 通常我们会对神经网络的数据进行标准化处理,处理后的样本数据集满足均值为0,方差为1的统计分布,这是因为当输入数据的分布比较固定时,有利于算法的稳定和收敛。 对于深度神经网络来说,由于参数是不断更新的,即使输入数据已经做过标准化处理,但是对于比较靠后的那些层,其接收到的输入仍然是剧烈变化的,通常会导致数值不稳定,模型很难收敛。 BatchNorm能够使神经网络中间层的输出变得更加稳定,并有如下三个优点:
  • 使学习快速进行(能够使用较大的学习率)
  • 降低模型对初始值的敏感性
  • 从一定程度上抑制过拟合
BatchNorm主要思路是在训练时按mini-batch为单位,对神经元的数值进行归一化,使数据的分布满足均值为0,方差为1。 具体计算过程如下:1. 计算mini-batch内样本的均值 其中表示mini-batch中的第个样本。 例如输入mini-batch包含3个样本,每个样本有2个特征,分别是: 对每个特征分别计算mini-batch内样本的均值: 则样本均值是:2. 计算mini-batch内样本的方差 上面的计算公式先计算一个批次内样本的均值和方差,然后再对输入数据做归一化,将其调整成均值为0,方差为1的分布。 对于上述给定的输入数据,可以计算出每个特征对应的方差: 则样本方差是:3. 计算标准化之后的输出 其中是一个微小值(例如),其主要作用是为了防止分母为0。 对于上述给定的输入数据,可以计算出标准化之后的输出:
  • 读者可以自行验证由构成的mini-batch,是否满足均值为0,方差为1的分布。
如果强行限制输出层的分布是标准化的,可能会导致某些特征模式的丢失,所以在标准化之后,BatchNorm会紧接着对数据做缩放和平移。 其中和是可学习的参数,可以赋初始值,在训练过程中不断学习调整。 上面列出的是BatchNorm方法的计算逻辑,下面针对两种类型的输入数据格式分别进行举例。 飞桨支持输入数据的维度大小为2、3、4、5四种情况,这里给出的是维度大小为2和4的示例。
  • 示例一: 当输入数据形状是时,一般对应全连接层的输出,示例代码如下所示。
这种情况下会分别对K的每一个分量计算N个样本的均值和方差,数据和参数对应如下:
  • 输入 x, [N, K]
  • 输出 y, [N, K]
  • 均值 ,[K, ]
  • 方差 , [K, ]
  • 缩放参数, [K, ]
  • 平移参数, [K, ]
# 输入数据形状是 [N, K]时的示例import numpy as npimport paddleimport paddle.fluid as fluidfrom 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('bn', 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_stdprint('std {}, mean {}, \n output {}'.format(a_mean, a_std, b))# 建议读者对第1和第2个特征进行验证,观察numpy计算结果与paddle计算结果是否一致
  • 示例二: 当输入数据形状是时, 一般对应卷积层的输出,示例代码如下所示。
这种情况下会沿着C这一维度进行展开,分别对每一个通道计算N个样本中总共个像素点的均值和方差,数据和参数对应如下:
  • 输入 x, [N, C, H, W]
  • 输出 y, [N, C, H, W]
  • 均值 ,[C, ]
  • 方差 , [C, ]
  • 缩放参数, [C, ]
  • 平移参数, [C, ]

小窍门: 可能有读者会问: “BatchNorm里面不是还要对标准化之后的结果做仿射变换吗,怎么使用Numpy计算的结果与BatchNorm算子一致? ” 这是因为BatchNorm算子里面自动设置初始值,这时候仿射变换相当于是恒等变换。 在训练过程中这两个参数会不断的学习,这时仿射变换就会起作用。
# 输入数据形状是[N, C, H, W]时的batchnorm示例import numpy as npimport paddleimport paddle.fluid as fluidfrom 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('bn', 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_stdprint('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
- 预测时使用BatchNorm 上面介绍了在训练过程中使用BatchNorm对一批样本进行归一化的方法,但如果使用同样的方法对需要预测的一批样本进行归一化,则预测结果会出现不确定性。 例如样本A、样本B作为一批样本计算均值和方差,与样本A、样本C和样本D作为一批样本计算均值和方差,得到的结果一般来说是不同的。 那么样本A的预测结果就会变得不确定,这对预测过程来说是不合理的。 解决方法是在训练过程中将大量样本的均值和方差保存下来,预测时直接使用保存好的值而不再重新计算。 实际上,在BatchNorm的具体实现中,训练时会计算均值和方差的移动平均值。 在飞桨中,默认是采用如下方式计算: 在训练过程的最开始将和设置为0,每次输入一批新的样本,计算出和,然后通过上面的公式更新和,在训练的过程中不断的更新它们的值,并作为BatchNorm层的参数保存下来。 预测的时候将会加载参数和,用他们来代替和。 03 丢弃法(Dropout) 丢弃法(Dropout)是深度学习中一种常用的抑制过拟合的方法,其做法是在神经网络学习过程中,随机删除一部分神经元。 训练时,随机选出一部分神经元,将其输出设置为0,这些神经元将不对外传递信号。图11 是Dropout示意图,左边是完整的神经网络,右边是应用了Dropout之后的网络结构。 应用Dropout之后,会将标了的神经元从网络中删除,让它们不向后面的层传递信号。 在学习过程中,丢弃哪些神经元是随机决定,因此模型不会过度依赖某些神经元,能一定程度上抑制过拟合。
7248eb7bd690e15561115e2298d3d474.png
图11 Dropout示意图 在预测场景时,会向前传递所有神经元的信号,可能会引出一个新的问题: 训练时由于部分神经元被随机丢弃了,输出数据的总大小会变小了。 比如: 计算其范数会比不使用Dropout时变小,但是预测时却没有丢弃神经元,这将导致训练和预测时数据的分布不一样。 为了解决这个问题,飞桨支持如下两种方法:
  • 1downgrade_in_infer
训练时以比例随机丢弃一部分神经元,不向后传递它们的信号; 预测时向后传递所有神经元的信号,但是将每个神经元上的数值乘以 。
  • 2upscale_in_train
训练时以比例随机丢弃一部分神经元,不向后传递它们的信号,但是将那些被保留的神经元上的数值除以 ; 预测时向后传递所有神经元的信号,不做任何处理。 在飞桨 dropout API 中,paddle.fluid.layers.dropout通过dropout_implementation参数来指定用哪种方式对神经元进行操作,dropout_implementation参数的可选值是'downgrade_in_infer'或'upscale_in_train',缺省值是'downgrade_in_infer'。
说明: 不同框架中dropout的默认处理方式可能不一样,读者可以查看其API以确认用的是哪种方式。
飞桨 dropout API 包含的主要参数如下:
  • x,数据类型是Tensor,需要采用丢弃法进行操作的对象。
  • dropout_prob,对x中元素进行丢弃的概率。
  • is_test,是否运行在测试阶段,由于dropout在训练和测试阶段表现不一样,通过此参数控制其表现,默认值为False。
  • dropout_implementation,丢弃法的实现方式,有'downgrade_in_infer'和'upscale_in_train'两种,具体情况请见上面的说明,默认是'downgrade_in_infer'。
下面这段程序展示了经过dropout之后输出数据的形式。
# dropout操作import numpy as npimport paddleimport 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()))

03总结 本文重点展开讲解了卷积神经网络里面的常用模块,如BN和Dropout。在后期课程中,将继续为大家带来内容更丰富的课程,帮助学员快速掌握深度学习方法。【如何学习】
  1. 如何观看配套视频?如何代码实践?
视频+代码已经发布在AI Studio实践平台上,视频支持PC端/手机端同步观看,也鼓励大家亲手体验运行代码哦。 扫码或者打开以下链接: https://aistudio.baidu.com/aistudio/course/introduce/888
  1. 学习过程中,有疑问怎么办?
加入深度学习集训营QQ群: 726887660,班主任与飞桨研发会在群里进行答疑与学习资料发放。
  1. 如何学习更多内容?
百度飞桨将通过飞桨深度学习集训营的形式,继续更新《零基础入门深度学习》课程,由百度深度学习高级研发工程师亲自授课,每周二、每周四8:00-9:00不见不散,采用直播+录播+实践+答疑的形式,欢迎关注~ 请搜索AI Studio,点击课程-百度架构师手把手教深度学习,或者点击文末「阅读原文 」收看。
  • 作者:环球科学
  • 原文链接:https://blog.csdn.net/weixin_34537864/article/details/112109881
    更新时间:2022-10-22 09:27:28