深度学习-tensorflow 使用keras进行深度神经网络训练

news2024/12/26 5:57:40

概要

深度学习网络的训练可能会很慢、也可能无法收敛,本文介绍使用keras进行深度神经网络训练的加速技巧,包括解决梯度消失和爆炸问题的策略(参数初始化策略、激活函数策略、批量归一化、梯度裁剪)、重用预训练层方法、更快的优化器算法,以及使用正则化避免过拟合的算法。

目录

一、梯度消失或者梯度爆炸

解法方案一:权重参数初始化

解决方案二:非饱和激活函数

 解决方案三:批量归一化(Batch Normalization)

 解决方案四:梯度裁剪

二、重用预训练层

例子:使用Keras进行迁移学习

三、更快的优化器

1. 动量优化

原始Momentum算法

Nesterov加速梯度

2. 自适应学习率算法

3. 学习率调度

四、通过正则化避免过拟合

L1和L2正则化

Dropout 

最大范数正则化 

五、实用指南


一、梯度消失或者梯度爆炸

梯度向下传播到较低层是,梯度通常会越来越小,结果是提督下降的更新导致较低层连接权重不变,训练不能收敛到一个良好的解,称为梯度消失;相反的情况是提督越来越大,各层需要更新很大的权重,直到算法发散,称为梯度爆炸。

梯度消失或者梯度爆炸是2000年代初期深度神经网络被抛弃的原因之一。

解法方案一:权重参数初始化

理论:每层大输出方差等于输入方差;反方向流经某层的之前和之后的梯度具有相同的方差,实际上很难满足,只能采取折中方案

主要有3种初始化策略:Xavier初始化或Glorot初始化、LeCun初始化、He初始化

 默认情况下,Keras使用均匀分布的Xavier初始化,通过kernel_initializer进行设置

keras.layers.Dense(10, activation="relu", kernel_initializer="he_normal")

解决方案二:非饱和激活函数

sigmoid函数存在饱和区,容易导致梯度消失问题

ReLU函数通常比sigmoid函数表现要好,但在输入小于0时,梯度是0,输出一直是0,导致这些神经元“死亡”了,严重的情况可能出现一般的神经元“死亡”。

ReLU变体LeakyReLU,在输入小于0时,为一个小斜率直线,LeakyReLU=max(az,z)。

ReLU函数变体 ELU(Exponential Linear Unit,指数线型单位)

SELU(Scaled ELU)为ELU的变体,即加入一个参数lamda,使得使用该激活函数,网络时自动归一化的 :

相比ReLU函数,ELU的主要问题是计算速度较慢 

 解决方案三:批量归一化(Batch Normalization)

尽管权重参数初始化策略和激活函数策略可以缓解梯度消失和爆炸问题,但仍然会出现,这个时候,通过BN可以减少这个问题,且使得可以使用饱和激活函数。

BN层对于浅层网络可能效果不明显,但对于深层网络非常有用

 解决方案四:梯度裁剪

对于梯度在反向传播期间设定一个阈值,使得梯度不超过该阈值,称为梯度裁剪,可缓解梯度爆炸问题,通常用在RNN中,原因是RNN中难以使用BN。

实现方法为在创建优化器是设置clipvalue或者clipnorm

optimizer = keras.optimizers.SGD(clipvalue=1.0) model.compile(loss="mse", optimizer=optimizer)

上述代码将限制所有的梯度再-1到1之间,这种设置可能会改变梯度的方向,比如原始梯度向量为[0.9,100.0],梯度主要指向第二个轴方向,按梯度裁剪后,得到[0.9,1.0],基本指向两个轴对角线。要想不改变梯度方向,则应该使用clipnorm进行裁剪,比如clipnorm=1.0,则得到梯度向量为[0.009,0.999]

二、重用预训练层

从头开始训练非常大的DNN通常不是一个好的主意,试图找到一个与现有问题类似的神经网络,然后用该网络的较低层,此技术称为迁移学习。它能够大大的加快训练速度,减少对训练数据的要求。

假设你可以访问一个经过训练的DNN,将图片分为100个不同类别,其中包括动物、植物和车辆。现在需要训练DNN来对特质类型的车辆进行分类,这些任务非常相似,因此应该尝试利用第一个网络中的一部分。

通常应该替换掉原始模型的输出层而保留较底层,因为对于新的任务有用的高级特征可能与原始任务存在很大的区别,甚至对于新的任务,没有正确数量的输出,任务约相似,保留的隐藏层就越多。

例子:使用Keras进行迁移学习

假设Fashion MNIST数据集仅仅包括8个类别(除了凉鞋和衬衫之外的所有类别),有人在该数据集上训练了Keras模型,并且获得了相当不错的性能(精度>90%),我们称此模型为A。

现在需要处理另外一项任务,要训练一个二分类器(正例=衬衫,负例=凉鞋),但数据集非常小,只有200张图片。你意识到新任务和任务A非常相似,也许可以通过迁移学习会有所帮助,让我们看看该怎么做。

首先,我们需要加载模型A并基于该模型层创建一个新模型,我们重用除输出层之外的所有层。

# 对模型A进行克隆,这样在训练模型B_on_A的时候,不回对模型的参数进行修改
model_A_clone = keras.models.clone_model(model_A)
model_A_clone.set_weights(model_A.get_weights())

model_B_on_A = keras.models.Sequential(model_A_clone.layers[:-1]) # 去除模型A的输出层
model_B_on_A.add(keras.layers.Dense(1, activation="sigmoid")) # 增加新模型的输出层

# 由于新的输出层是随机初始化的,在最初的几个轮次中会产生较大的错误,存在较大的错误梯度,这会破坏重用的权重。为避免这种情况,在前几个轮次的训练时,冻结重用层,给新层一些时间来学习合理权重,为此将重用层的训练属性设置为False。

for layer in model_B_on_A.layers[:-1]: 
    layer.trainable = False

model_B_on_A.compile(loss="binary_crossentropy",
                     optimizer=keras.optimizers.SGD(learning_rate=1e-3),
                     metrics=["accuracy"])

history = model_B_on_A.fit(X_train_B, y_train_B, epochs=4,
                           validation_data=(X_valid_B, y_valid_B))

# 几轮训练后,可以对重用层进行解冻,参数可以更新

for layer in model_B_on_A.layers[:-1]:
    layer.trainable = True

model_B_on_A.compile(loss="binary_crossentropy",
                     optimizer=keras.optimizers.SGD(learning_rate=1e-3),
                     metrics=["accuracy"])
history = model_B_on_A.fit(X_train_B, y_train_B, epochs=16,
                           validation_data=(X_valid_B, y_valid_B))

三、更快的优化器

训练一个非常大的神经网络可能会非常缓慢,前面已经知道了五种加速训练的方法:

  • 权重参数初始化
  • 使用良好的激活函数
  • 批量归一化
  • 梯度裁剪
  • 重用预训练网络中的一部分

与常规的梯度下降相比,使用更快的有花期也可以带来巨大的速度提升,包括动量优化算法:原始动量优化和Nesterov加速梯度,以及自适应学习率优化:AdaGrad、RMSProp、Adam、Nadam。

1. 动量优化

参考这两篇文章:机器学习 | 优化——动量优化法(更新方向优化) - 简书

优化算法Optimizer比较和总结 - 知乎

momentum算法思想:权重参数更新时在一定程度上保留之前更新的方向,同时又利用当前batch的梯度微调最终的更新方向,简言之就是通过积累之前的动量来加速当前的梯度。

原始Momentum算法

下面一张图可以很直观地表达Momentum算法的思想。举个简单例子,假设上次更新时梯度是往前走的,这次更新的梯度算出来是往左走,这变化太剧烈了,所以我们来做个折中,往左前方走。感觉上,像是上次更新还带有一定的惯性。

具体算法如下: 

Momentum算法的优点:

当某个参数在最近一段时间内的梯度方向不一致时,其真实的参数更新幅度变小;相反,当在最近一段时间内的梯度方向都一致时,其真实的参数更新幅度变大,起到加速作用。

一般而言,在迭代初期,梯度方向都比较一致,动量法会起到加速作用,可以更快地到达最优点。在迭代后期,梯度方向会取决不一致,在收敛值附近震荡,动量法会起到减速作用,增加稳定性。从某种角度来说,当前梯度叠加上部分的上次梯度,一定程度上可以近似看作二阶梯度

——参考:邱锡鹏:《神经网络与深度学习》

Nesterov加速梯度

Nesterov 加速梯度(Nesterov Accelerated Gradient,NAG),也叫 Nesterov 动量法(Nesterov Momentum),是对Momentum算法的一种改进,可以看成是Momentum算法的一种 变体

动量法每下降一步都是由前面下降方向的一个累积和当前点的梯度方向组合而成。于是一位大神(Nesterov)就开始思考,既然每一步都要将两个梯度方向(历史梯度、当前梯度)做一个合并再下降,那为什么不先按照历史梯度往前走那么一小步,按照前面一小步位置的“超前梯度”来做梯度合并呢?

如此一来,小球就可以先不管三七二十一先往前走一步,在靠前一点的位置看到梯度,然后按照那个位置再来修正这一步的梯度方向。

2. 自适应学习率算法

学习率是一个非常重要的超参数,但是学习率是非常难确定的,虽然可以通过多次训练来确定合适的学习率,但是一般也不太确定多少次训练能够得到最优的学习率,玄学事件,对人为的经验要求比较高,所以是否存在一些策略自适应地调节学习率的大小,从而提高训练速度。 目前的自适应学习率优化算法主要有:AdaGrad算法,RMSProp算法,Adam算法以及AdaDelta算法。

AdaGrad(Adaptive Gradient)算法:梯度更新时,要除以“梯度的累积平方和根”,因此梯度前期更新较快,中后期,分母上梯度累加的平方和会越来越大,使得参数更新量趋近于0,使得训练提前结束。

RMSProp(Root Mean Square Propagation)算法:梯度更新时,“梯度的累积平方和根”会进行衰减,最近一步“梯度平方和根”有一定权重。

Adam(Adaptive Moment estimation) 算法:结合了动量优化和RMSProp思想,像动量优化一样,跟踪梯度指数衰减,像RMSProp一样,关注梯度平方和根的衰减。

3. 学习率调度

一般可以先设置较大的学习率,然后逐步降低学习率,以使得模型快速收敛。主要有幂调度、指数调度、分段恒定调度、性能调度、周期调度几种方法。

幂调度:将学习率设置为lr = lr0 / (1 + steps / s)**c(\eta (t) = \eta_{0} /(1+t/s)^{c},Keras uses c=1,经过s个步骤之后,学习率下降为原来的1/2,在keras实现幂调度非常简单

initial_learning_rate = 0.1
decay_steps = 10.0
decay_rate = 0.5
learning_rate_fn = keras.optimizers.schedules.InverseTimeDecay(
  initial_learning_rate, decay_steps, decay_rate)
optimizer = tf.keras.optimizers.SGD(learning_rate = learning_rate_fn)

指数调度:\eta (t) = \eta_{0} *0.1^{t/s}

分段恒定调度:对于一定的轮次使用一个固定的学习率,比如前5个轮次使用0.1,6-20轮次使用0.05,21轮次以后使用0.01,以此类推。可以使用keras.optimizers.schedules.PiecewiseConstantDecay

step = tf.Variable(0, trainable=False)
boundaries = [5, 20]
values = [0.1, 0.05, 0.1]
learning_rate_fn = keras.optimizers.schedules.PiecewiseConstantDecay(
    boundaries, values)

# Later, whenever we perform an optimization step, we pass in the step.
learning_rate = learning_rate_fn(step)

性能调度:每N步测量一次验证误差,并且当误差停止下降时,讲学习率降低lamda倍。

四、通过正则化避免过拟合

L1和L2正则化

keras可以使用keras.regularizers.l1()、keras.regularizers.l2()、keras.regularizers.l1-l2()来分别实现l1、l2、以及l1+l2正则化。

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(300, activation="elu",
                       kernel_initializer="he_normal",
                       kernel_regularizer=keras.regularizers.l2(0.01)),
    keras.layers.Dense(100, activation="elu",
                       kernel_initializer="he_normal",
                       kernel_regularizer=keras.regularizers.l2(0.01)),
    keras.layers.Dense(10, activation="softmax",
                       kernel_regularizer=keras.regularizers.l2(0.01))
])

由于通常需要将相同的正则化应用于网络中的所有层,上述方法重复了相同的参数切容易出错,可以考虑使用python 的functools.partial()函数 

from functools import partial

RegularizedDense = partial(keras.layers.Dense,
                           activation="elu",
                           kernel_initializer="he_normal",
                           kernel_regularizer=keras.regularizers.l2(0.01))

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    RegularizedDense(300),
    RegularizedDense(100),
    RegularizedDense(10, activation="softmax")
])

Dropout 

对深度学习,dropout是最受欢迎的正则化技术之一,由Geoffrey Hinton在2012年提出。在每个训练步骤中,每个神经元(不包括输出神经元)都有暂时被“删除”的概率p,但在下一步骤中可能处于激活状态,超参数p称为dropout率,一般设置为10%到50%。dropout只能用于训练,不会用于预测,因此在悬链完后,我们需要将每个输入权重乘以保留概率1-p。

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),
    keras.layers.Dropout(rate=0.2),
    keras.layers.Dense(10, activation="softmax")
])
model.compile(loss="sparse_categorical_crossentropy", optimizer="nadam", metrics=["accuracy"])
n_epochs = 2
history = model.fit(X_train_scaled, y_train, epochs=n_epochs,
                    validation_data=(X_valid_scaled, y_valid))

最大范数正则化 

对每个神经元,限制传入链接的权重W,使得\left \| \mathbf{w} \right \|_{2}\leq r, r称为最大范数超参数。最大范数正则化不会正则化损失添加到总体损失中,而是在每个训练步骤后,计算\left \| \mathbf{w} \right \|_{2},如果大于r,则进行缩放。

五、实用指南

默认DNN超参数配置

如果网络时密集层的简单堆叠,可以使用自归一化,可以使用下表的参数配置

自归一化网络的DNN配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/484905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux进程描述指令:ps与top

这里写自定义目录标题 一 ps指令1 ps -aux2. ps -a3. ps -u4.ps -x 二 top指令1 top2 top -d 时间3. top -i4 top -p ID 一 ps指令 1 ps -aux 显示系统中的所有进程 PID就是进程的唯一编号,操作系统书里有一个PCD的概念,就是一个标识性的控制单元 [ro…

学系统集成项目管理工程师(中项)系列16b_风险管理(下)

1. 规划风险应对 1.1. 针对项目目标,制订提高机会、降低威胁的方案和措施的过程 1.2. 制订风险应对措施 1.3. 制订风险应对计划 1.4. 次生风险是实施风险应对措施的直接结果 1.5. 应对措施必须与风险的重要性相匹配,能经济有效地应对挑战 1.5.1. 【…

项目管理-计算专题(三点估算、PERT估算)

基本概念 通过考虑估算中的不确定性和风险,可以提高活动持续时间估算的准确性。这个概念源自计划评审技术(PERT)。PERT使用三种估算值来界定活动持续时间的近似区间: 最可能时间(tM):基于最可能获得的资源、最可能取得的资源生产率、对资源可用时间的现…

C语言CRC-16 MODBUS格式校验函数

C语言CRC-16 MODBUS格式校验函数 CRC-16校验产生2个字节长度的数据校验码,通过计算得到的校验码和获得的校验码比较,用于验证获得的数据的正确性。基本的CRC-16校验算法实现,参考: C语言标准CRC-16校验函数。 不同应用规范通过对…

Simulink 自动代码生成电机控制:弱磁控制从仿真到硬件开发板验证实验

目录 前言 仿真 生成代码在硬件上运行 总结 前言 测试了一个新的小电机,24V只能跑2000RPM左右,在此基础上测试一下弱磁控制,由于MTPA只适用于表贴电机,测试效果不能直观看出来,一般是通过测试效率。而前馈解耦的响…

【Paper】2019_DoS/数据注入攻击下基于一致性的信息物理系统安全性研究_曹雄

2019_DoS/数据注入攻击下基于一致性的信息物理系统安全性研究_曹雄 文章目录 第2章 拒绝服务攻击下多智能体系统安全性研究2.1 问题描述2.1.1 系统模型2.1.2 拒绝服务攻击模型 2.2 安全估计与一致性分析2.4 数值仿真2.4.1 观测器网络受到攻击下的结果及分析2.4.1.1 线性多智能体…

视频分享平台PeerTube的搭建

老苏下载了吴恩达的 Prompt for developer 课程,不管是否有兴趣,都可以看看,早晚我们都得学会使用 ChatGPT 虽然课程对话是英文,但有中文字幕,课程地址:https://www.aliyundrive.com/s/H3CpaapD87Z 这个软…

使用ChatGPT+Xmind一键生成思维导图,简直泰裤辣

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜📝 个人主页:馆主阿牛🔥🎉 支持我:点赞👍收藏⭐️留言&#x1f4d…

python 编写K210控制步进电机的程序示例

今天正好看到K210的脉冲章节,就顺便拿出步进电机做个小实验,也好巩固一下所学的知识。下面是K210关于脉冲的相关介绍: 构造函数 machine.PWM(tim, freq, duty, pin, enableTrue) PWM 对象在 machine 模块下 【tim】K210 的 PWM 依赖…

789. 数的范围(C++和Python3)——2023.5.3打卡

文章目录 QuestionIdeasCode Question 给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。 对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。 如果数组中不存在该元素,则返回 -1 -…

【C++从0到王者】第二站:类和对象(上)

文章目录 一、面向过程与面向对象二、类的引入三、类的访问限定符四、类的定义五、封装六、类的作用域七、类的实例化八、类对象模型1.如何计算类对象的大小2.类对象存储方式猜测 九、this指针1.this指针的引出2.this指针的特性 一、面向过程与面向对象 C语言是面向过程的&…

Web2与Web3开发的不同之处

Web2是引入交互功能的第二代互联网,也是我们今天所熟悉的。随着Web的不断发展,第三代互联网,也被称为Web3,正处于积极开发中。Web3引入了在区块链上运行的去中心化和无需许可的系统。但是Web2和Web3开发之间有什么区别呢&#xff…

23.5.1总结

这几天都在写项目: 在实现页面,调用数据库的时候,总是把数据库的表改了又改,然后完善了下数据库的表。 存储的思路大概是: 一个课程下可以有多个班级,所以以课程id作为主键建一个表,内容包括…

在win11搭建ubuntu目标机器的QT开发环境的实践

环境: 笔记本电脑 16G内存 win11 尝试wsl的方案: wsl2 ubuntu gnome xrdp wsl安装ubuntu并设置gnome图形界面详细步骤(win11ubuntu18)_heusjh的博客-CSDN博客 wsl2 ubuntu gnome VcXsrv Windows中WSL2 配置运行GNOM…

Centos7快速安装Elasticsearch 7.17.7

从 Elasticsearch 7.x 版本开始,Elasticsearch 发行版包括了自己的 JDK。因此,您不需要单独安装 Java。以下是在 CentOS 7 上安装 Elasticsearch 7.17.7 的完整步骤:(数据默认保存在/var/lib/elasticsearch下,自行更改…

vmware安装arch linux

vmware安装arch linux 1、下载镜像2、安装2.1、VMware 系统版本选择 其他Linux 5.x 内核 64位2.2、进行磁盘分区 3、重启系统后登录进来发现没有地址 由于安装系统时没有安装任何软件 只安装了1个vim 无法动态获取地址4、安装必需的软件 最小化安装5、编辑/etc/ssh/sshd_config…

【Python】flask框架学习 flask框架的基本使用

flask框架是什么? Flask 是一个轻量级的 Web 框架,用于构建 Web 应用程序。它基于 Python 编程语言和 Werkzeug 工具包,提供了简单易用的 API,可以轻松地创建 RESTful API 和 Web 应用程序。 flask的特点 轻量级:Fl…

学会这些常用调试技巧,让你的C/C++代码调试起来如虎添翼

本篇博客主要讲解程序员最应该掌握的技能之一——调试。我个人认为,学习编程,有2件事情非常重要,一是画图,一是调试。下面我会以Visual Studio 2022为例(VS的其他版本大同小异),演示如何调试一个…

怎么体验gpt4-国内怎么使用chatGPT

gpt4api要等多久 目前,OpenAI尚未公布GPT-4 API的发布计划和时间表。GPT-4 将是前置还增加强大的自然语言处理能力和推理能力,OpenAI正在为其开发和研究,以使其更加流畅、准确和智能。因此,GPT-4 API的发布时间尚未确定。但是&am…

云原生Istio安装和使用

目录 1 Kubernetes集群环境2 安装Istio2.1 快速部署Istio2.2 回顾K8S组件以及使用2.2.1 Deployment2.2.2 Labels and Selectors2.2.3 Namespace2.2.4 Service2.2.5 Ingress 2.3 初步感受istio2.4 手动注入2.5 自动注入sidecar 1 Kubernetes集群环境 Istio支持在不同的平台下安装…