Sklearn 与 TensorFlow 机器学习实用指南——第十一章总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yanying1113/article/details/86504664


第10介绍了人工神经网络,并训练了包含两个隐含层的浅的DNN。
如果你需要解决非常复杂的问题,例如检测高分辨率图像中的数百种类型的对象,可能需要训练更深的 DNN,也许有 10 层,每层包含数百个神经元,通过数十万个连接来连接。
这时就会遇到一些问题:

  1. 首先,将面临棘手的梯度消失问题(或相关的梯度爆炸问题),这会影响深度神经网络,并使较低层难以训练。
  2. 其次,对于如此庞大的网络,训练将非常缓慢。
  3. 第三,具有数百万参数的模型将会有严重的过拟合训练集的风险。
    在本章中,我们将依次讨论这些问题,并提出解决问题的技巧。
    这一章内容相当多,也都是干货,非常值得细细咀嚼。本文参考地址

梯度消失/爆炸问题

像第 10 章中所讨论的那样,反向传播算法的工作原理是从输出层到输入层,传播误差的梯度。 一旦该算法已经计算了网络中每个参数的损失函数的梯度,它就使用这些梯度来用梯度下降步骤来更新每个参数。

不幸的是,梯度往往变得越来越小,随着算法进展到较低层。 结果,梯度下降更新使得低层连接权重实际上保持不变,并且训练永远不会收敛到良好的解决方案。 这被称为梯度消失问题。 在某些情况下,可能会发生相反的情况:梯度可能变得越来越大,许多层得到了非常大的权重更新,算法发散。这是梯度爆炸的问题,在循环神经网络中最为常见。

这种不幸经过了很长一段时间,直到 2010 年左右,Xavier Glorot 和 Yoshua Bengio 发表论文发现了一些疑问和改进方法,简言之就是这个问题是由激活函数和初始化方法导致的。
因此在初始化方法方面有一些比较好的策略提出,可以大大加快训练速度。
在这里插入图片描述

非饱和激活函数

Glorot 和 Bengio 在 2010 年的论文中的一个见解是,消失/爆炸的梯度问题部分是由于激活函数的选择不好造成的。事实证明,其他激活函数在深度神经网络中表现得更好,特别是 ReLU 激活函数,主要是因为它对正值不会饱和(也因为它的计算速度很快)。

为了解决这个问题,你可能需要使用 ReLU 函数的一个变体,比如 leaky ReLU。这个函数定义为LeakyReLUα(z)= max(αz,z)(见图 11-2)。超参数α定义了函数“leaks”的程度:它是z < 0时函数的斜率,通常设置为 0.01。

Glorot 和 Bengio还评估了随机化 leaky ReLU(RReLU),其中α在训练期间在给定范围内随机挑选,并在测试期间固定为平均值。它表现相当好,似乎是一个正则项(减少训练集的过拟合风险)。最后,他们还评估了参数 leaky ReLU(PReLU),其中α被授权在训练期间被学习(而不是超参数,它变成可以像任何其他参数一样被反向传播修改的参数)。据说这在大型图像数据集上的表现强于 ReLU,但是对于较小的数据集,其具有过度拟合训练集的风险。
在这里插入图片描述最后,Djork-Arné Clevert 等人在 2015 年的一篇论文中提出了一种称为指数线性单元(exponential linear unit,ELU)的新的激活函数,在他们的实验中表现优于所有的 ReLU 变体:训练时间减少,神经网络在测试集上表现的更好。
在这里插入图片描述
那么具体应该使用哪个激活函数来处理深层神经网络的隐藏层?虽然目标会有所不同,一般情况下ELU > leaky ReLU(及其变体)> ReLU > tanh > sigmoid。 如果你关心运行时性能,那么你可能喜欢 leaky ReLU超过ELU。 如果你不想调整另一个超参数,你可以使用前面提到的默认的α值(leaky ReLU 为 0.01,ELU 为 1)。如果你有充足的时间和计算能力,你可以使用交叉验证来评估其他激活函数,特别是如果你的神经网络过拟合,则为RReLU; 如果你拥有庞大的训练数据集,则为 PReLU。

批量标准化

尽管使用 He初始化和 ELU(或任何 ReLU 变体)可以显著减少训练开始阶段的梯度消失/爆炸问题,但不保证在训练期间问题不会回来。

在 2015 年的一篇论文中,Sergey Ioffe 和 Christian Szegedy 提出了一种称为批量标准化(Batch Normalization,BN)的技术来解决梯度消失/爆炸问题,每层输入的分布在训练期间改变的问题,更普遍的问题是当前一层的参数改变,每层输入的分布会在训练过程中发生变化(他们称之为内部协变量偏移问题)。

该技术包括在每层的激活函数之前在模型中添加操作,简单地对输入进行zero-centering和规范化,然后每层使用两个新参数(一个用于尺度变换,另一个用于偏移)对结果进行尺度变换和偏移。 换句话说,这个操作可以让模型学习到每层输入值的最佳尺度,均值。为了对输入进行归零和归一化,算法需要估计输入的均值和标准差。 它通过评估当前小批量输入的均值和标准差(因此命名为“批量标准化”)来实现。
在这里插入图片描述
作者证明,这项技术大大改善了他们试验的所有深度神经网络。梯度消失问题大大减少了,他们可以使用饱和激活函数,如 tanh 甚至 sigmoid 激活函数,毕竟使用标准化之后特征数据会大概率处于线性区域(激活函数中间那段)。网络对权重初始化也不那么敏感。他们能够使用更大的学习率,显著加快了学习过程。

梯度裁剪

减少梯度爆炸问题的一种常用技术是在反向传播过程中简单地剪切梯度,使它们不超过某个阈值(这对于递归神经网络是非常有用的;参见第 14 章)。 这就是所谓的梯度裁剪。一般来说,人们更喜欢批量标准化,但了解梯度裁剪以及如何实现它仍然是有用的。

复用预训练层

从零开始训练一个非常大的 DNN 通常不是一个好主意,相反,您应该总是尝试找到一个现有的神经网络来完成与您正在尝试解决的任务类似的任务,然后复用这个网络的较低层:这就是所谓的迁移学习。这不仅会大大加快训练速度,还将需要更少的训练数据。

复用 TensorFlow 模型

如果原始模型使用 TensorFlow 进行训练,则可以简单地将其恢复并在新任务上进行训练:

[...] # construct the original model 
with tf.Session() as sess:
    saver.restore(sess, "./my_model_final.ckpt")
    # continue training the model...

冻结较低层

第一个 DNN 的较低层可能已经学会了检测图片中的低级特征,这将在两个图像分类任务中有用,因此您可以按照原样重新使用这些层。 在训练新的 DNN 时,“冻结”权重通常是一个好主意:如果较低层权重是固定的,那么较高层权重将更容易训练(因为他们不需要学习一个移动的目标)。
要在训练期间冻结较低层,最简单的解决方案是给优化器列出要训练的变量,不包括来自较低层的变量:

train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope="hidden[34]|outputs")
training_op = optimizer.minimize(loss,var_list=train_vars)

第一行获得隐藏层 3 和 4 以及输出层中所有可训练变量的列表。 这留下了隐藏层 1 和 2 中的变量。接下来,我们将这个受限制的可列表变量列表提供给optimizer的minimize()函数。现在,层 1 和层 2 被冻结:在训练过程中不会发生变化(通常称为冻结层)。

缓存冻结层

由于冻结层不会改变,因此可以为每个训练实例缓存最上面的冻结层的输出。 由于训练贯穿整个数据集很多次,这将给你一个巨大的速度提升,因为每个训练实例只需要经过一次冻结层(而不是每个迭代一次)。

调整,删除或替换较高层

原始模型的输出层通常应该被替换,因为对于新的任务来说,它最有可能没有用处,甚至可能没有适合新任务的输出数量。

类似地,原始模型的较高隐藏层不太可能像较低层一样有用,因为对于新任务来说最有用的高层特征可能与对原始任务最有用的高层特征明显不同。

尝试先冻结所有复制的层,然后训练模型并查看它是如何执行的。 然后尝试解冻一个或两个较高隐藏层,让反向传播调整它们,看看性能是否提高。 您拥有的训练数据越多,您可以解冻的层数就越多。

如果仍然无法获得良好的性能,并且您的训练数据很少,请尝试删除顶部的隐藏层,并再次冻结所有剩余的隐藏层。 您可以迭代,直到找到正确的层数重复使用。 如果您有足够的训练数据,您可以尝试替换顶部的隐藏层,而不是丢掉它们,甚至可以添加更多的隐藏层。

Model Zoos

想要找到类似任务训练的神经网络,除了查看自己的模型之外,还可以在Model Zoos中搜索, 许多人为了各种不同的任务而训练机器学习模型,并且善意地向公众发布预训练模型。

TensorFlow 在 https://github.com/tensorflow/models 中有自己的模型动物园。 特别是,它包含了大多数最先进的图像分类网络,如 VGG,Inception 和 ResNet(参见第 13 章,检查model/slim目录),包括代码,预训练模型和 工具来下载流行的图像数据集。

另一个流行的模型动物园是 Caffe 模型动物园。 它还包含许多在各种数据集(例如,ImageNet,Places 数据库,CIFAR10 等)上训练的计算机视觉模型(例如,LeNet,AlexNet,ZFNet,GoogLeNet,VGGNet,开始)。 Saumitro Dasgupta 写了一个转换器,可以在 https://github.com/ethereon/caffe-tensorflow。

无监督的预训练

假设你想要解决一个复杂的任务,你没有太多的标记的训练数据,更不幸的是,你不能找到一个类似的任务训练模型。 首先,你当然应该尝试收集更多的有标签的训练数据,但是如果这太难或太昂贵,你仍然可以进行无监督的训练(如下图)。也就是说,如果你有很多未标记的训练数据,你可以尝试逐层训练层,从最低层开始,然后上升,使用无监督的特征检测算法(如限制波尔兹曼机或自动编码器)。 一旦所有层都以这种方式进行了训练,就可以使用监督式学习(即反向传播)对网络进行微调。
在这里插入图片描述

更快的优化器

训练一个非常大的深度神经网络可能会非常缓慢。
到目前为止,我们已经看到了四种加速训练的方法(并且达到更好的解决方案):

  1. 对连接权重应用良好的初始化策略,
  2. 使用良好的激活函数,
  3. 使用批量规范化,
  4. 重用预训练网络的部分。
    另一个巨大的速度提升来自使用比普通渐变下降优化器更快的优化器。 在本节中,我们将介绍最流行的:动量优化,Nesterov 加速梯度,AdaGrad,RMSProp,最后是 Adam 优化。

动量优化

想象一下,一个保龄球在一个光滑的表面上滚动:它会缓慢地开始,但是它会很快地达到最终的速度(如果有一些摩擦或空气阻力的话)。这是 Boris Polyak 在 1964 年提出的动量优化背后的一个非常简单的想法。相比之下,普通的梯度下降只需要沿着斜坡进行小的有规律的下降步骤,所以需要更多的时间才能到达底部。

梯度下降算法: θ : = θ η θ J ( θ ) \theta:=\theta-\eta\nabla_\theta J(\theta) ,它不关心早期的梯度是什么。 如果局部梯度很小,则会非常缓慢。

动量优化则关心以前的梯度:在每次迭代时,它将动量矢量m与局部梯度相加,并且通过简单地减去该动量矢量来更新权重(参见公式 11-4)。 换句话说,梯度用作加速度,不用作速度。 为了模拟某种摩擦机制,避免动量过大,该算法引入了一个新的超参数β,简称为动量,它必须设置在 0(高摩擦)和 1(无摩擦)之间。 典型的动量值是 0.9。
在这里插入图片描述
很容易地验证,如果梯度保持不变,则最终速度(即,权重更新的最大大小)等于该梯度乘以学习率η乘以1/(1-β)。 例如,如果β = 0.9,则最终速度等于学习率的梯度乘以 10 倍,因此动量优化比梯度下降快 10 倍! 这使动量优化比梯度下降快得多。

由于动量的原因,优化器可能会超调一些,然后再回来,再次超调,并在稳定在最小值之前多次振荡(有点像步子迈大了,跨过去了然后再往回走)。这就是为什么在系统中有一点摩擦的原因之一:它消除了这些振荡,从而加速了收敛。

在 TensorFlow 中实现动量优化是一件简单的事情:只需用MomentumOptimizer替换GradientDescentOptimizer,增加了一个超参数β ,然而,在实践中动量值通常取0.9 ,几乎总是比梯度下降快。

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,momentum=0.9)

Nesterov 加速梯度

Yurii Nesterov 在 1983 年提出的动量优化的一个小变体几乎总是比普通的动量优化更快。
Nesterov 动量优化或 Nesterov 加速梯度(Nesterov Accelerated Gradient,NAG)的思想是测量损失函数的梯度不是在局部位置,而是在动量方向稍微靠前(见公式 11-5)。
与普通的动量优化的唯一区别在于梯度是在θ+βm而不是在θ处测量的。
在这里插入图片描述
针对动量优化做了一个小小的调整,一般来说,动量矢量将指向正确的方向(即朝向最优方向),所以使用在该方向上测得的梯度稍微更精确,而不是使用原始位置的梯度。
如下图所示(其中∇1代表在起点θ处测量的损失函数的梯度,∇2代表位于θ+βm的点处的梯度)。可以看出绿线(Nesterov)比蓝线(动量优化)更接近最佳值(optimum)。此外,请注意,当动量推动权重横跨山谷时,▽1继续推进越过山谷,而▽2推回山谷的底部。 这有助于减少振荡,从而更快地收敛。
在这里插入图片描述
与常规的动量优化相比,NAG 几乎总能加速训练。 要使用它,只需在创建MomentumOptimizer时设置use_nesterov = True:

optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,momentum=0.9, use_nesterov=True)

AdaGrad

再次考虑细长碗的问题:梯度下降从最陡峭的斜坡快速下降,然后缓慢地下到谷底。 如果算法能够早期检测到这个问题并且纠正它的方向来指向全局最优点,那将是非常好的。
在这里插入图片描述
AdaGrad 算法通过沿着最陡的维度缩小梯度向量来实现这一点(见公式 11-6):
在这里插入图片描述

第一步将梯度的平方累加到矢量s中(⊗符号表示单元乘法)。 这个向量化形式相当于向量s的每个元素si计算 s i : = s i + ( J ( θ ) / θ i ) 2 s_i := s_i + (\partial J(\theta) / \partial \theta_i)^2 。换一种说法,每个 s i s_i 累加损失函数对参数 θ i \theta_i 的偏导数的平方。 如果损失函数沿着第i维陡峭,则在每次迭代时, s i s_i 将变得越来越大。

第二步几乎与梯度下降相同,但有一个很大的不同:梯度矢量按比例缩小 s + ε \sqrt{s+ε} (⊘符号表示元素分割,ε是避免被零除的平滑项,通常设置为 1 0 10 10^{-10} 。 这个矢量化的形式相当于计算 θ i θ i η / θ i J ( θ ) / s i + ε \theta_i\leftarrow\theta_i-\eta \partial/\partial\theta_iJ(\theta)/\sqrt{s_i+ε} 对于所有参数 θ i \theta_i

简而言之,这种算法会降低学习速度,但对于陡峭的尺寸,其速度要快于具有温和的斜率的尺寸。 这被称为自适应学习率。 它有助于将更新的结果更直接地指向全局最优.
在这里插入图片描述
对于简单的二次问题,AdaGrad 经常表现良好,但不幸的是,在训练神经网络时,它经常停止得太早。 学习率被缩减得太多,以至于在达到全局最优之前,算法完全停止。所以,即使 TensorFlow 有一个AdagradOptimizer,你也不应该用它来训练深度神经网络(虽然对线性回归这样简单的任务可能是有效的)。

optimizer = tf.train.AdagradOptimizer(learning_rate=learning_rate)

RMSProp

尽管 AdaGrad 的速度变慢了一点,但是 RMSProp 算法通过仅累积最近迭代(而不是从训练开始以来的所有梯度)的梯度来修正这个问题。 它通过在第一步中使用指数衰减来实现(见公式 11-7)。
在这里插入图片描述

它的衰变率β通常设定为 0.9。这是一个新的超参数,但是这个默认值通常运行良好,所以你可能根本不需要调整它。

optimizer = tf.train.RMSPropOptimizer(learning_rate=learning_rate,momentum=0.9, decay=0.9, epsilon=1e-10)

除了非常简单的问题,这个优化器几乎总是比 AdaGrad 执行得更好。 它通常也比动量优化和 Nesterov 加速梯度表现更好。 事实上,这是许多研究人员首选的优化算法,直到 Adam 优化出现。

Adam 优化

Adam,代表自适应矩估计,结合了动量优化和 RMSProp 的思想:就像动量优化一样,它追踪过去梯度的指数衰减平均值,就像 RMSProp 一样,它跟踪过去平方梯度的指数衰减平均值 (见方程式 11-8)。
在这里插入图片描述
T 代表迭代次数(从 1 开始)
如果你只看步骤 1, 2 和 5,你会注意到 Adam 与动量优化和 RMSProp 的相似性。 唯一的区别是第 1 步计算指数衰减的平均值,而不是指数衰减的和,但除了一个常数因子(衰减平均值只是衰减和的1 - β1倍)之外,它们实际上是等效的。 步骤 3 和步骤 4 是一个技术细节:由于m和s初始化为 0,所以在训练开始时它们会偏向0,所以这两步将在训练开始时帮助提高m和s。

动量衰减超参数β1通常初始化为 0.9,而缩放衰减超参数β2通常初始化为 0.999。 如前所述,平滑项ε通常被初始化为一个很小的数,例如 10-8。这些是 TensorFlow 的AdamOptimizer类的默认值,所以你可以简单地使用:

optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)

实际上,由于 Adam 是一种自适应学习率算法(如 AdaGrad 和 RMSProp),所以对学习率超参数η的调整较少。 您经常可以使用默认值η= 0.001,使 Adam 更容易使用相对于梯度下降。

训练稀疏模型

所有刚刚提出的优化算法都会产生密集的模型,这意味着大多数参数都是非零的。 如果你在运行时需要一个非常快速的模型,或者如果你需要它占用较少的内存,你可能更喜欢用一个稀疏模型来代替。

实现这一点的一个微不足道的方法是像平常一样训练模型,然后摆脱微小的权重(将值很小的权重设置为 0)。

另一个选择是在训练过程中应用强 l1 正则化,因为它会推动优化器尽可能多地消除权重。

学习率调整

找到一个好的学习速度可能会非常棘手。 如果设置太高,训练实际上可能偏离(如我们在第 4 章)。 如果设置得太低,训练最终会收敛到最佳状态,但这需要很长时间。
在这里插入图片描述
由上图可以看出,如果我们可以对学习率作出调整,那么将会快速学习并收敛到良好的解决方案。

因此我们可以提出一些学习率调整策略,其中最常见的是:

预定的分段恒定学习率:
例如,首先将学习率设置为 η 0 = 0.1 η_0 = 0.1 ,然后在 50 个迭代之后将学习率设置为 η 1 = 0.001 η_1= 0.001 。虽然这个解决方案可以很好地工作,但是通常需要弄清楚正确的学习速度以及何时使用它们。

性能调度:
每 N 步测量验证误差(就像提前停止一样),当误差下降时,将学习率降低一个因子λ。

指数调度:
将学习率设置为迭代次数t的函数: η ( t ) = η 0 1 0 t / r \eta(t)=\eta_010^{-t/r} 。 这很好,但它需要调整 η 0 η_0 和r。 学习率将由每r步下降10倍。

幂调度:
设学习率为 η ( t ) = η 0 ( 1 + t / r ) c \eta(t)=\eta_0(1+t/r)^{-c} 。超参数c通常被设置为 1。这与指数调度类似,但是学习率下降要慢得多。

Andrew Senior 等2013年的论文。 比较了使用动量优化训练深度神经网络进行语音识别时一些最流行的学习率调整的性能。 作者得出结论:在这种情况下,性能调度和指数调度都表现良好,但他们更喜欢指数调度,因为它实现起来比较简单,容易调整,收敛速度略快于最佳解决方案。

通过正则化避免过拟合

深度神经网络通常具有数以万计的参数,有时甚至是数百万。 有了这么多的参数,网络拥有难以置信的自由度,可以适应各种复杂的数据集。 但是这个很大的灵活性也意味着它很容易过拟合训练集。

在本节中,我们将介绍一些最流行的神经网络正则化技术,以及如何用 TensorFlow 实现它们:早期停止,l1 和 l2 正则化,drop out,最大范数正则化和数据增强。

早期停止

为避免过度拟合训练集,一个很好的解决方案就是尽早停止训练(在第 4 章中介绍):只要在训练集的性能开始下降时中断训练。

在TensorFlow 中实现方法之一是评估其对设置周期(例如,每 50 步)验证模型,如果它优于以前“winner”的快照,则保存之。 计算自上次“winner”快照保存以来的步数,并在达到某个限制时(例如 2000 步)中断训练。 然后恢复最后的“winner”快照。

虽然早期停止在实践中运行良好,但是通过将其与其他正则化技术相结合,您通常可以在网络中获得更高的性能。

L1 和 L2 正则化

就像你在第 4 章中对简单线性模型所做的那样,你可以使用 l1 和 l2 正则化约束一个神经网络的连接权重(但通常不包含它的偏置)。
使用 TensorFlow 做到这一点的一种方法是简单地将适当的正则化项添加到您的损失函数中。

Dropout

Dropout由 GE Hinton 于 2012 年提出,并在 Nitish Srivastava 等人的论文中进一步详细描述,并且已被证明是非常成功的:即使是最先进的神经网络,仅仅通过增加丢失就可以提高1-2%的准确度。

这是一个相当简单的算法:在每个训练步骤中,每个神经元(包括输入神经元,但不包括输出神经元)都有一个暂时“丢弃”的概率p,这意味着在这个训练步骤中它将被完全忽略, 在下一步可能会激活(见图 11-9)。 超参数p称为丢失率,通常设为 50%。 训练后,神经元不会再下降。这是一个粗鲁而有效的方法。
在这里插入图片描述
有一个小而重要的技术细节。 假设p = 50%,在这种情况下,在测试期间,在训练期间神经元将被连接到两倍于(平均)的输入神经元。 为了弥补这个事实,我们需要在训练之后将每个神经元的输入连接权重乘以 0.5。 如果我们不这样做,每个神经元的总输入信号大概是网络训练的两倍,这不太可能表现良好。 更一般地说,我们需要将每个输入连接权重乘以训练后的保持概率(1-p)。 或者,我们可以在训练过程中将每个神经元的输出除以保持概率(这些替代方案并不完全等价,但它们工作得同样好)。

如果观察到模型过拟合,则可以增加 dropout 率。 相反,如果模型欠拟合训练集,则应尝试降低 dropout 率。 它也可以帮助增加大层的 dropout 率,并减少小层的 dropout 率。

最大范数正则化

另一种在神经网络中非常流行的正则化技术被称为最大范数正则化:对于每个神经元,它约束输入连接的权重w,使得 w 2 r ||w||_2\leq r ,其中r是最大范数超参数, ||\cdot|| 是 l2 范数。

我们通常通过在每个训练步骤之后计算 w 2 ||w||_2 来实现这个约束,并且如果需要的话可以剪切W, w w r w 2 w\leftarrow w \frac{r}{||w||_2}

减少r增加了正则化的数量,并有助于减少过拟合。 最大范数正则化还可以帮助减轻梯度消失/爆炸问题。
在tensorflow中有两种方式实现最大范数正则化:
一种是获取权重进行处理之后再将其赋值:

threshold = 1.0# 设置阈值
weights = tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0")#提取权重
clipped_weights = tf.clip_by_norm(weights, clip_norm=threshold, axes=1)#处理
clip_weights = tf.assign(weights, clipped_weights)#赋值

另一种是定义一个正则化函数 max_norm_regularizer()函数:

def max_norm_regularizer(threshold, axes=1, name="max_norm",collection="max_norm"):
    def max_norm(weights):
        clipped = tf.clip_by_norm(weights, clip_norm=threshold, axes=axes)
        clip_weights = tf.assign(weights, clipped, name=name)
        tf.add_to_collection(collection, clip_weights)
        return None # there is no regularization loss term
    return max_norm

然后你可以调用这个函数来得到一个最大范数调节器(与你想要的阈值)。 当你创建一个隐藏层时,你可以将这个正则化器传递给kernel_regularizer参数:

max_norm_reg = max_norm_regularizer(threshold=1.0)
with tf.name_scope("dnn"):
    hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,kernel_regularizer=max_norm_reg, name="hidden1")
    hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,kernel_regularizer=max_norm_reg, name="hidden2")
    logits = tf.layers.dense(hidden2, n_outputs, name="outputs")

数据增强

最后一个正则化技术,数据增强,包括从现有的训练实例中产生新的训练实例,人为地增加了训练集的大小。 这将减少过拟合,使之成为正则化技术。
例如,如果您的模型是为了分类蘑菇图片,您可以稍微移动,旋转和调整训练集中的每个图片的大小,并将结果图片添加到训练集。这迫使模型更能容忍图片中蘑菇的位置,方向和大小。 如果您希望模型对光照条件更加宽容,则可以类似地生成具有各种对比度的许多图像。 假设蘑菇是对称的,你也可以水平翻转图片。 通过结合这些转换,可以大大增加训练集的大小。
在这里插入图片描述

实践指南

在本章中,我们已经涵盖了很多技术,你可能想知道应该使用哪些技术。 下表中的配置在大多数情况下都能正常工作。
在这里插入图片描述
另外,如果可以找到解决类似问题的方式,我们可以尝试重用预训练的网络的一部分。
另外根据实际应用问题,这个默认配置可以做一些调整:

  • 如果你找不到一个好的学习率(收敛速度太慢,所以你增加了训练速度,现在收敛速度很快,但是网络的准确性不是最理想的),那么你可以尝试添加一个学习率调整,如指数衰减。
  • 如果你的训练集太小,你可以实现数据增强。
  • 如果你需要一个稀疏的模型,你可以添加 l1 正则化混合(并可以选择在训练后将微小的权重归零)。 如果您需要更稀疏的模型,您可以尝试使用 FTRL 而不是 Adam 优化以及 l1 正则化。
  • 如果在运行时需要快速模型,则可能需要删除批量标准化,并可能用 leakyReLU 替换 ELU 激活函数。 有一个稀疏的模型也将有所帮助。

这一章内容学习过后,我们就可以训练比较深的网络,(如果有耐心或者有服务器的话),如果使用单台机器,则可能需要等待几天甚至几个月才能完成训练。下一章中,我们将讨论如何使用分布式 TensorFlow 在许多服务器和 GPU 上训练和运行模型。另外由于本章内容较多,习题将在下一篇博客中叙述。

猜你喜欢

转载自blog.csdn.net/yanying1113/article/details/86504664