51.RNN训练难题--梯度弥散与梯度爆炸、详解机器学习中的梯度消失、爆炸原因及其解决方法;RNN网络的梯度推导公式(学习笔记,学习整理)

本文主要是介绍51.RNN训练难题--梯度弥散与梯度爆炸、详解机器学习中的梯度消失、爆炸原因及其解决方法;RNN网络的梯度推导公式(学习笔记,学习整理),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.51.RNN训练难题–梯度弥散与梯度爆炸
1.51.1.梯度弥散与梯度爆炸
1.51.2.详解机器学习中的梯度消失、爆炸原因及其解决方法
1.51.2.1.前言
1.51.2.2.第一部分:为什么要使用梯度更新规则
1.51.2.3.第二部分:梯度消失、爆炸
1.51.2.3.1.深层网络角度
1.51.2.3.2.激活函数角度
1.51.2.4.第三部分:梯度消失、爆炸的解决方案
1.51.2.4.1.方案1-预训练加微调
1.51.2.4.2.方案2-梯度剪切、正则
1.51.2.4.3.方案3-relu、leakrelu、elu等激活函数
1.51.2.4.4.解决方案4-batchnorm
1.51.2.4.5.解决方案5-残差结构
1.51.2.4.6.解决方案6-LSTM
1.51.2.4.7.参考资料:
1.51.3.RNN网络的梯度推导公式
1.51.3.1.梯度爆炸
1.51.3.1.1.解决办法
1.51.3.1.2.gradient clipping(梯度裁剪)
1.51.3.2.梯度弥散
1.51.4.参考博文

1.51.RNN训练难题–梯度弥散与梯度爆炸

1.51.1.梯度弥散与梯度爆炸

此部分摘自:https://blog.csdn.net/sinat_41144773/article/details/90712065

梯度弥散(梯度消失): 通常神经网络所用的激活函数是sigmoid函数,sigmod函数容易引起梯度弥散。这个函数能将负无穷到正无穷的数映射到0和1之间,并且对这个函数求导的结果是f′(x)=f(x)(1−f(x))f′(x)=f(x)(1−f(x))表示两个0到1之间的数相乘,得到的结果就会变得很小了。神经网络的反向传播是逐层对函数偏导相乘,因此当神经网络层数非常深的时候,最后一层产生的偏差就因为乘了很多的小于1的数而越来越小,最终就会变为0,从而导致层数比较浅的权重没有更新,这就是梯度消失。

梯度爆炸:就是由于初始化权值过大,前面层会比后面层变化的更快,就会导致权值越来越大,梯度爆炸的现象就发生了。

反向传播基于的是链式求导法则。如果导数小于1,那么随着层数的增多,梯度的更新量会以指数形式衰减,结果就是越靠近输出层的网络层参数更新比较正常,而靠近输入层的网络层参数可能基本就不更新。这就是梯度消失。而如果导数值大于1,那么由于链式法则的连乘,梯度更新量是会成指数级增长的。这就是梯度爆炸。

1.51.2.详解机器学习中的梯度消失、爆炸原因及其解决方法

转自:https://blog.csdn.net/qq_25737169/article/details/78847691

1.51.2.1.前言

本文主要深入介绍深度学习中的梯度消失和梯度爆炸的问题以及解决方案。本文分为三部分,第一部分主要直观的介绍深度学习中为什么使用梯度更新,第二部分主要介绍深度学习中梯度消失及爆炸的原因,第三部分对提出梯度消失及爆炸的解决方案。有基础的同鞋可以跳着阅读。
其中,梯度消失爆炸的解决方案主要包括以下几个部分。

  • 预训练加微调
  • 梯度剪切、权重正则(针对梯度爆炸)
  • 使用不同的激活函数
  • 使用batchnorm
  • 使用残差结构
  • 使用LSTM网络
1.51.2.2.第一部分:为什么要使用梯度更新规则

在介绍梯度消失以及爆炸之前,先简单说一说梯度消失的根源—–深度神经网络和反向传播。目前深度学习方法中,深度神经网络的发展造就了我们可以构建更深层的网络完成更复杂的任务,深层网络比如深度卷积网络,LSTM等等,而且最终结果表明,在处理复杂任务上,深度网络比浅层的网络具有更好的效果。但是,目前优化神经网络的方法都是基于反向传播的思想,即根据损失函数计算的误差通过梯度反向传播的方式,指导深度网络权值的更新优化。这样做是有一定原因的,首先,深层网络由许多非线性层堆叠而来,每一层非线性层都可以视为是一个非线性函数f(x)(非线性来自于非线性激活函数),因此整个深度网络可以视为是一个复合的非线性多元函数。
在这里插入图片描述
我们最终的目的是希望这个多元函数可以很好的完成输入到输出之间的映射,假设不同的输入,输出的最优解是g ( x ) ,那么,优化深度网络就是为了寻找到合适的权值,满足Loss=L(g(x),F(x))取得极小值点,比如最简单的损失函数
在这里插入图片描述
假设损失函数的数据空间是下图这样的,我们最优的权值就是为了寻找下图中的最小值点,对于这种数学寻找最小值问题,采用梯度下降的方法再适合不过了。
在这里插入图片描述

1.51.2.3.第二部分:梯度消失、爆炸

梯度消失与梯度爆炸其实是一种情况,看接下来的文章就知道了。两种情况下梯度消失经常出现,一是在深层网络中,二是采用了不合适的损失函数,比如sigmoid。梯度爆炸一般出现在深层网络和权值初始化值太大的情况下,下面分别从这两个角度分析梯度消失和爆炸的原因。

1.51.2.3.1.深层网络角度

比较简单的深层网络如下:
在这里插入图片描述
图中是一个四层的全连接网络,假设每一层网络激活后的输出为fi
在这里插入图片描述
注:下图中的隐层标号和第一张全连接图隐层标号刚好相反。
图中的曲线表示权值更新的速度,对于下图两个隐层的网络来说,已经可以发现隐藏层2的权值更新速度要比隐藏层1更新的速度慢
在这里插入图片描述
那么对于四个隐层的网络来说,就更明显了,第四隐藏层比第一隐藏层的更新速度慢了两个数量级:
在这里插入图片描述

总结:从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层学习的很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多。因此,梯度消失、爆炸,其根本原因在于反向传播训练法则,属于先天不足,另外多说一句,Hinton提出capsule的原因就是为了彻底抛弃反向传播,如果真能大范围普及,那真是一个革命。

1.51.2.3.2.激活函数角度

其实也注意到了,上文中提到计算权值更新信息的时候需要计算前层偏导信息,因此如果激活函数选择不合适,比如使用sigmoid,梯度消失就会很明显了,原因看下图,左图是sigmoid的损失函数图,右边是其导数的图像,如果使用sigmoid作为损失函数,其梯度是不可能超过0.25的,这样经过链式求导之后,很容易发生梯度消失,sigmoid函数数学表达式为:
在这里插入图片描述
在这里插入图片描述

同理,tanh作为激活函数,它的导数图如下,可以看出,tanh比sigmoid要好一些,但是它的导数仍然是小于1的。tanh数学表达为:
在这里插入图片描述
在这里插入图片描述

1.51.2.4.第三部分:梯度消失、爆炸的解决方案
1.51.2.4.1.方案1-预训练加微调

此方法来自Hinton在2006年发表的一篇论文,Hinton为了解决梯度的问题,提出采取无监督逐层训练方法,其基本思想是每次训练一层隐节点,训练时将上一层隐节点的输出作为输入,而本层隐节点的输出作为下一层隐节点的输入,此过程就是逐层“预训练”(pre-training);在预训练完成后,再对整个网络进行“微调”(fine-tunning)。Hinton在训练深度信念网络(Deep Belief Networks中,使用了这个方法,在各层预训练完成后,再利用BP算法对整个网络进行训练。此思想相当于是先寻找局部最优,然后整合起来寻找全局最优,此方法有一定的好处,但是目前应用的不是很多了。

1.51.2.4.2.方案2-梯度剪切、正则

梯度剪切这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。

注:在WGAN中也有梯度剪切限制操作,但是和这个是不一样的,WGAN限制梯度更新信息是为了保证lipchitz条件。

另外一种解决梯度爆炸的手段是采用权重正则化(weithts regularization)比较常见的是l1正则,和l2正则,在各个深度框架中都有相应的API可以使用正则化,比如在tensorflow中,若搭建网络的时候已经设置了正则化参数,则调用以下代码可以直接计算出正则损失:

regularization_loss = tf.add_n(tf.losses.get_regularization_losses(scope='my_resnet_50'))

如果没有设置初始化参数,也可以使用以下代码计算l2正则损失:

l2_loss = tf.add_n([tf.nn.l2_loss(var) for var in tf.trainable_variables() if 'weights' in var.name])

正则化是通过对网络权重做正则限制过拟合,仔细看正则项在损失函数的形式:
在这里插入图片描述
其中,α 是指正则项系数,因此,如果发生梯度爆炸,权值的范数就会变的非常大,通过正则化项,可以部分限制梯度爆炸的发生。

注:事实上,在深度神经网络中,往往是梯度消失出现的更多一些。

1.51.2.4.3.方案3-relu、leakrelu、elu等激活函数

Relu: 思想也很简单,如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了,每层的网络都可以得到相同的更新速度,relu就这样应运而生。先看一下relu的数学表达式:
在这里插入图片描述
其函数图像:
在这里插入图片描述
从上图中,我们可以很容易看出,relu函数的导数在正数部分是恒等于1的,因此在深层网络中使用relu激活函数就不会导致梯度消失和爆炸的问题。

relu的主要贡献在于:

  • 解决了梯度消失、爆炸的问题。
  • 计算方便,计算速度快
  • 加速了网络的训练

同时也存在一些缺点

  • 由于负数部分恒为0,会导致一些神经元无法激活(可通过设置小学习率部分解决)
  • 输出不是以0为中心的。

尽管relu也有缺点,但是仍然是目前使用最多的激活函数。

leakrelu
leakrelu就是为了解决relu的0区间带来的影响,其数学表达为:leakrelu=max(k∗x,x)其中k是leak系数,一般选择0.01或者0.02,或者通过学习而来
在这里插入图片描述leakrelu解决了0区间带来的影响,而且包含了relu的所有有点
elu
elu激活函数也是为了解决relu的0区间带来的影响,其数学表达式为:
在这里插入图片描述
其函数及其导数数学形式为:
在这里插入图片描述
但是elu相对leakrelu来说,计算要更耗时间一些。

1.51.2.4.4.解决方案4-batchnorm

在这里插入图片描述
有关batch norm详细的内容可以参考我的另一篇博客:
http://blog.csdn.net/qq_25737169/article/details/79048516

1.51.2.4.5.解决方案5-残差结构

残差结构说起残差的话,不得不提这篇论文了:Deep Residual Learning for Image Recognition,关于这篇论文的解读,可以参考知乎链接:https://zhuanlan.zhihu.com/p/31852747 这里只简单介绍残差如何解决梯度的问题。

事实上,就是残差网络的出现导致了image net比赛的终结,自动残差提出后,几乎所有的深度网络都离不开残差的身影,相比较之前的几层,几十层的深度网络,在残差网络面前都不值一提,残差可以很轻松的构建几百层,一千多层的网络而不用担心梯度消失过快的问题,原因就在于残差的捷径(shortcut)部分,其中残差单元如下图所示:
在这里插入图片描述
相比较与以前网络的直来直去结构,残差中有很多这样的跨层连接结构,这样的结构在反向传播中具有很大的好处,见下式:

1.51.2.4.6.解决方案6-LSTM

LSTM全称是长短期记忆网络(long-short term memory networks),是不那么容易发生梯度消失的,主要原因在于LSTM内部复杂的“门”(gates),如下图,LSTM通过它内部的“门”可以接下来更新的时候“记住”前几次训练的”残留记忆“,因此,经常用于生成文本中。目前也有基于CNN的LSTM,感兴趣的可以尝试一下。
在这里插入图片描述

1.51.2.4.7.参考资料:

1.《Neural networks and deep learning》
2.《机器学习》周志华
3. https://www.cnblogs.com/willnote/p/6912798.html
4. https://www.zhihu.com/question/38102762
5. http://www.jianshu.com/p/9dc9f41f0b29

1.51.3.RNN网络的梯度推导公式

在这里插入图片描述

1.51.3.1.梯度爆炸

在这里插入图片描述
参考论文:http://proceedings.mlr.press/v28/pascanu13.pdf

1.51.3.1.1.解决办法
1.51.3.1.2.gradient clipping(梯度裁剪)

理论详见:(https://blog.csdn.net/jetFlow/article/details/80161354?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160268066819195188324652%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160268066819195188324652&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v28-3-80161354.pc_first_rank_v2_rank_v28&utm_term=gradient%20clipping%20%E8%A7%A3%E5%86%B3%E4%BB%A3%E7%A0%81tf&spm=1018.2118.3001.4187)

针对梯度爆炸问题,解决方案是引入Gradient Clipping(梯度裁剪)。通过Gradient Clipping,将梯度约束在一个范围内,这样不会使得梯度过大。

在tensorflow 文档中,可以看到Gradient Clipping板块有五个函数。这里,我们仅仅介绍其中两个:

tf.clip_by_norm(t,clip_norm,name=None)
tf.clip_by_global_norm(t_list,clip_norm,use_norm=None,name=None)tf.clip_by_norm(t,clip_norm,name=None)
参数说明: t: a tensor ; clip_norm: 阈值,即maximum L2-norm。
如果 tensor的L2-norm大于clip_norm,则 t = t * clip_norm / l2norm(t)
否则t = ttf.clip_by_global_norm(t_list,clip_norm,use_norm=None,name=None)
参数说明: t_list: multiple tensors; clip_norm: a clipping ratio; use_norm: 如果你
已经计算出global_norm,你可以在use_norm进行指定。tf.clip_by_global_norm(t_list,clip_norm,use_norm=None,name=None)更慢一些,因为它需要
等所有的梯度都求出来才可以进行梯度截断。

通常会使用一种叫”clip gradients “的方法. 它能有效地权重控制在一定范围之内.
算法步骤如下:

  • 首先设置一个梯度阈值:clip_gradient
  • 在后向传播中求出个参数的梯度,这里我们不直接使用梯度进行参数更新,我们求这些梯度的2范数
  • 然后比较梯度的l2范数||g||与clip_gradient的大小
    如果前者大,求缩放因子clip_gradient/||g||, 由缩放因子可以看出梯度越大,则缩放因子越小,这样便很好地控制了梯度的范围
  • 最后将梯度乘上缩放因子便得到最后所需的梯度
lr = 0.01
max_grad_norm = 5tvars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(loss, tvars), max_grad_norm)
opt = tf.train.GradientDescentOptimizer(lr)
# opt = tf.train.AdamOptimizer(lr)
optimizer = opt.apply_gradients(zip(grads, tvars))

其中pytorch版本的Gradient Clipping代码如下:

loss = criterion(output, y)
model.zero_grad()
loss.backward()
for p in model.parameters():print(p.grad.norm())
torch.nn.utils.clip_grad_norm_(p, 10)
optimizer.step() 
1.51.3.2.梯度弥散

在这里插入图片描述LSTM解决梯度离散的问题

1.51.4.参考博文

https://blog.csdn.net/weixin_46649052/article/details/109083154
https://blog.csdn.net/qq_25737169/article/details/78847691
https://blog.csdn.net/sinat_41144773/article/details/90712065

这篇关于51.RNN训练难题--梯度弥散与梯度爆炸、详解机器学习中的梯度消失、爆炸原因及其解决方法;RNN网络的梯度推导公式(学习笔记,学习整理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918135

相关文章

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

MySQL中的分组和多表连接详解

《MySQL中的分组和多表连接详解》:本文主要介绍MySQL中的分组和多表连接的相关操作,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录mysql中的分组和多表连接一、MySQL的分组(group javascriptby )二、多表连接(表连接会产生大量的数据垃圾)MySQL中的

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

MyBatis模糊查询报错:ParserException: not supported.pos 问题解决

《MyBatis模糊查询报错:ParserException:notsupported.pos问题解决》本文主要介绍了MyBatis模糊查询报错:ParserException:notsuppo... 目录问题描述问题根源错误SQL解析逻辑深层原因分析三种解决方案方案一:使用CONCAT函数(推荐)方案二:

SpringBoot3.4配置校验新特性的用法详解

《SpringBoot3.4配置校验新特性的用法详解》SpringBoot3.4对配置校验支持进行了全面升级,这篇文章为大家详细介绍了一下它们的具体使用,文中的示例代码讲解详细,感兴趣的小伙伴可以参考... 目录基本用法示例定义配置类配置 application.yml注入使用嵌套对象与集合元素深度校验开发

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

判断PyTorch是GPU版还是CPU版的方法小结

《判断PyTorch是GPU版还是CPU版的方法小结》PyTorch作为当前最流行的深度学习框架之一,支持在CPU和GPU(NVIDIACUDA)上运行,所以对于深度学习开发者来说,正确识别PyTor... 目录前言为什么需要区分GPU和CPU版本?性能差异硬件要求如何检查PyTorch版本?方法1:使用命

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义