优化算法

优化算法,第1张

  SGD算法中的一个关键参数是学习率。之前,我们介绍的SGD使用固定的学习率。在实践中,有必要随着时间的推移逐渐降低学习率,因此我们将第 k 步迭代的学习率记作 ϵ k 。

  这是因为SGD中梯度估计引入的噪声源(m 个训练样本的随机采样)并不会在极小点处消失。相比之下,当我们使用批量梯度下降到达极小点时,整个代价函数的真实梯度会变得很小,之后为 0,因此批量梯度下降可以使用固定的学习率。保证SGD收敛的一个充分条件是

  若 ϵ 0 太大,学习曲线将会剧烈振荡,代价函数值通常会明显增加。温和的振荡是良好的,容易在训练随机代价函数(例如使用Dropout的代价函数)时出现。如果学习率太小,那么学习过程会很缓慢。如果初始学习率太低,那么学习可能会卡在一个相当高的代价值。通常,就总训练时间和最终代价值而言,最优初始学习率会高于大约迭代 100 次左右后达到最佳效果的学习率。因此,通常最好是检测最早的几轮迭代,选择一个比在效果上表现最佳的学习率更大的学习率,但又不能太大导致严重的震荡。

  虽然随机梯度下降仍然是非常受欢迎的优化方法,但其学习过程有时会很慢。动量方法 (Polyak, 1964) 旨在加速学习,特别是处理高曲率、小但一致的梯度,或是带噪声的梯度。动量算法积累了之前梯度指数级衰减的移动平均,并且继续沿该方向移动。动量的效果如图85所示

  受 Nesterov 加速梯度算法 (Nesterov, 1983, 2004) 启发,提出了动量算法的一个变种。这种情况的更新规则如下:

  其中参数 α 和 ϵ 发挥了和标准动量方法中类似的作用。Nesterov 动量和标准动量之间的区别体现在梯度计算上。Nesterov 动量中,梯度计算在施加当前速度之后。因此,Nesterov 动量可以解释为往标准动量方法中添加了一个校正因子。完整的Nesterov动量算法如算法32所示

  初始点能够决定算法是否收敛,有些初始点十分不稳定,使得该算法会遭遇数值困难,并完全失败。当学习收敛时,初始点可以决定学习收敛得多快,以及是否收敛到一个代价高或低的点。此外,差不多代价的点可以具有区别极大的泛化误差,初始点也可以影响泛化。

  也许完全确知的唯一特性是初始参数需要在不同单元间 ‘‘破坏对称性’’。如果具有相同激活函数的两个隐藏单元连接到相同的输入,那么这些单元必须具有不同的初始参数。如果它们具有相同的初始参数,然后应用到确定性损失和模型的确定性学习算法将一直以相同的方式更新这两个单元。即使模型或训练算法能够使用随机性为不同的单元计算不同的更新(例如使用Dropout的训练),通常来说,最好还是初始化每个单元使其和其他单元计算不同的函数。这或许有助于确保没有输入模式

丢失在前向传播的零空间中,没有梯度模式丢失在反向传播的零空间中。每个单元计算不同函数的目标促使了参数的随机初始化。我们可以明确地搜索一大组彼此互不相同的基函数,但这经常会导致明显的计算代价。例如,如果我们有和输出一样多的输入,我们可以使用 Gram-Schmidt 正交化于初始的权重矩阵,保证每个单元计算彼此非常不同的函数。在高维空间上使用高熵分布来随机初始化,计算代价小并且不太可能分配单元计算彼此相同的函数。

  通常情况下,我们可以为每个单元的偏置设置启发式挑选的常数,仅随机初始化权重。额外的参数(例如用于编码预测条件方差的参数)通常和偏置一样设置为启发式选择的常数。

  我们几乎总是初始化模型的权重为高斯或均匀分布中随机抽取的值。高斯或均匀分布的选择似乎不会有很大的差别,但也没有被详尽地研究。然而,初始分布的大小确实对优化过程的结果和网络泛化能力都有很大的影响。

  更大的初始权重具有更强的破坏对称性的作用,有助于避免冗余的单元。它们也有助于避免在每层线性成分的前向或反向传播中丢失信号——矩阵中更大的值在矩阵乘法中有更大的输出。如果初始权重太大,那么会在前向传播或反向传播中产生爆炸的值。在循环网络中,很大的权重也可能导致混沌(chaos)(对于输入中很小的扰动非常敏感,导致确定性前向传播过程表现随机)。在一定程度上,梯度爆炸问题可以通过梯度截断来缓解(执行梯度下降步骤之前设置梯度的阈值)。较大的权

重也会产生使得激活函数饱和的值,导致饱和单元的梯度完全丢失。这些竞争因素决定了权重的理想初始大小。

  也有助于避免在每层线性成分的前向或反向传播中丢失信号——矩阵中更大的值在矩阵乘法中有更大的输出。如果初始权重太大,那么会在前向传播或反向传播中产生爆炸的值。在循环网络中,很大的权重也可能导致混沌(chaos)(对于输入中很小的扰动非常敏感,导致确定性前向传播过程表现随机)。在一定程度上,梯度爆炸问题可以通过梯度截断来缓解(执行梯度下降步骤之前设置梯度的阈值)。较大的权重也会产生使得激活函数饱和的值,导致饱和单元的梯度完全丢失。这些竞争因素决定了权重的理想初始大小。

  有些启发式方法可用于选择权重的初始大小。一种初始化 m 个输入和 n 输出的全连接层的权重的启发式方法是从分布 U(−1/√ m ,

1/√ m ) 中采样权重,而 Glorot and Bengio 建议使用标准初始化

  后一种启发式方法初始化所有的层,折衷于使其具有相同激活方差和使其具有相同梯度方差之间。这假设网络是不含非线性的链式矩阵乘法,据此推导得出。现实的神经网络显然会违反这个假设,但很多设计于线性模型的策略在其非线性对应中的效果也不错。

  数值范围准则的一个缺点是,设置所有的初始权重具有相同的标准差,例如1/√ m ,会使得层很大时每个单一权重会变得极其小。Martens (2010) 提出了一种被称为稀疏初始化(sparse initialization)的替代方案,每个单元初始化为恰好有 k 个非零权重。这个想法保持该单元输入的总数量独立于输入数目 m,而不使单一权重元素的大小随 m 缩小。稀疏初始化有助于实现单元之间在初始化时更具多样性。但是,获得较大取值的权重也同时被加了很强的先验。因为梯度下降需要很长时间缩小 ‘‘不正确’’ 的大值,这个初始化方案可能会导致某些单元出问题,例如maxout单元有几个过滤器,互相之间必须仔细调整。

  Delta-bar-delta 算法 (Jacobs, 1988) 是一个早期的在训练时适应模型参数各自学习率的启发式方法。该方法基于一个很简单的想法,如果损失对于某个给定模型参数的偏导保持相同的符号,那么学习率应该增加。如果对于该参数的偏导变化了符号,那么学习率应减小。当然,这种方法只能应用于全批量优化中。

  AdaGrad 算法,如算法84所示,独立地适应所有模型参数的学习率,缩放每个参数反比于其所有梯度历史平方值总和的平方根 (Duchi et al, 2011)。具有损失最大偏导的参数相应地有一个快速下降的学习率,而具有小偏导的参数在学习率上有相对较小的下降。净效果是在参数空间中更为平缓的倾斜方向会取得更大的进步。

  在凸优化背景中,AdaGrad 算法具有一些令人满意的理论性质。然而,经验上已经发现,对于训练深度神经网络模型而言,从训练开始时积累梯度平方会导致有效学习率过早和过量的减小。AdaGrad在某些深度学习模型上效果不错,但不是全部。

  RMSProp 算法 (Hinton, 2012) 修改 AdaGrad 以在非凸设定下效果更好,改变梯度积累为指数加权的移动平均。AdaGrad旨在应用于凸问题时快速收敛。当应用于非凸函数训练神经网络时,学习轨迹可能穿过了很多不同的结构,最终到达一个局部是凸碗的区域。AdaGrad 根据平方梯度的整个历史收缩学习率,可能使得学习率在达到这样的凸结构前就变得太小了。RMSProp 使用指数衰减平均以丢弃遥远过去的历史,使其能够在找到凸碗状结构后快速收敛,它就像一个初始化于该碗状结构的 AdaGrad 算法实例。

  RMSProp 的标准形式如算法85所示,结合 Nesterov 动量的形式如算法86所示。相比于 AdaGrad,使用移动平均引入了一个新的超参数ρ,用来控制移动平均的长度范围。经验上,RMSProp 已被证明是一种有效且实用的深度神经网络优化算法。目前它是深度学习从业者经常采用的优化方法之一。

  Adam (Kingma and Ba, 2014) 是另一种学习率自适应的优化算法,最好被看作结合 RMSProp 和具有一些重要区别的动量的变种。首先,在 Adam 中,动量直接并入了梯度一阶矩(指数加权)的估计。将动量加入 RMSProp 最直观的方法是将动量应用于缩放后的梯度。结合缩放的动量使用没有明确的理论动机。其次,Adam 包括偏置修正,修正从原点初始化的一阶矩(动量项)和(非中心的)二阶矩的估计(算法87)。RMSProp 也采用了(非中心的)二阶矩估计,然而缺失了修正因子。因此,不像 Adam,RMSProp 二阶矩估计可能在训练初期有很高的偏置。Adam 通常被认为对超参数的选择相当鲁棒,尽管学习率有时需要从建议的默认修改。

  目前,最流行并且使用很高的优化算法包括 SGD、具动量的 SGD、RMSProp、具动量的 RMSProp、AdaDelta 和 Adam。

Inception系列的v1,v2读完了,该v3了,当年在渣浪做的视频推荐,提取视频特征的网络用的就是Inception v3。不过作者的标题没用v3,而是开始『Rethinking』了。

20181229 2777次。和v1,v2完全不在一个量级上。

2015年12月刊发于arXiv。比后来横空出世的ResNet也就早了几天。一作回归到了v1的作者Christian Szegedy,v2的作者Sergey Ioffe这次是三作,看来两人好基友,每次都是组团出现

1 提出一些设计网络架构的通用准则

2 各种分解卷积的骚套路

本文探索了各种扩大网络的方式,目标是通过合适的卷积分解和有效的正则化来尽可能有效地利用所增加的计算。

相比VGG和AlexNet,Inception的计算量和参数量都大大降低,因此可以用于大数据和移动设备。不过,如果架构只是简单的缩放,大部分计算带来的收益可能会立即丢失。

本文将介绍一些通用的准则和优化理念用来更有效的扩大卷积网络。

作者反复强调以上只是部分经验,实际使用时需根据具体情况抉择。。

GoogLeNet最初的收益大多数来自广泛使用的降维。这可以被视为以一种更有效的计算来分解卷积的特例。

因为Inception网络是全卷积的,每个权重对应于每次激活的一次乘法,因此任何计算代价的减少也会引起总参数量的减少。这意味着可以通过合适的分解,得到更可分的参数并因此加速训练。

较大的filter(例如,5×5或7×7)在计算方面往往不成比例地昂贵。

用两个3x3卷积代替一个5x5卷积

做了控制实验证明这种策略有效。

很自然的想到能不能把3x3继续缩小为2x2,作为比较,将3x3分解为2个2x2只能节约11%的计算,不过用3x1和1x3能节约33%。

理论上,还可以更进一步,将nxn都用1xn和nx1来代替。实践发现这种分解在早期的层效果并不好,但在中间的层效果非常好(对于m x m的特征图,m在12到20之间)

Inception-v1中使用的辅助分类器最初动机就是为了克服深层网络中的梯度消失问题,将有用的梯度使浅层立即可以使用。不过,经实验发现,在训练早期这样并不能改进收敛,只是在训练后期,比没有辅助分类器的网络稍微好一点。这说明在Inception-v1中的假设是错误的(自己打自己脸,佩服,有勇气承认,没有混过去)

传统上,卷积网络通过池化 *** 作来减小feature map的大小。为了避免表示性的bottleneck,在执行平均或最大池化前都会扩展filter的维度。

图9的左图虽然减小了网格尺寸,不过违反了通用原则1,过早的引入了bottleneck,右图倒是没违反,不过带来了3倍的计算量。

图10给出了解决办法,即引入两个并行的stride都为2的block:P和C,之后再联结起来。这样不仅代价更低而且避免了表示性的bottleneck。

(虽然这里是官方定义的v2,不过大家貌似都将BN-Inception认为是v2?)

把起初的7x7卷积分解为3个3x3卷积,用了不同结构的Inception块(图5,6,7),总共42层,计算代价只比v1高25倍。

提出了一种机制,通过估计训练期间标签丢失的边缘化效应来给分类层加正则。

分析了交叉熵损失过于自信导致过拟合的原因。提出一种机制鼓励模型减少这种自信。

对于标签为y的样本,将标签分布 替换为

在实验中,使用均匀分布 ,这样式子变为

称这种对真实标签分布的改变为标签平滑正则(label-smoothing regularization LSR)

另一种对LSR的解释是从交叉熵的角度出发

第二项损失惩罚了预测的标签分布p与先验u的偏差。这种偏差也可以通过KL散度等效地捕获,因为 ,而 是固定的。

在ILSVRC2012中,设置 , , 。带来了02%的提升。

用tensorflow训练了50个模型(这是Inception系列论文中第一次用tf),batch_size=32,epochs=100。起初的实验用带动量的SGD,decay=09。但是最佳的模型是RMSProp,decay=09, 。学习率用0045,每2个epoch衰减(指数衰减率为094)。

另外,发现用RNN中的梯度裁剪(设置阈值为20)可以使训练稳定。

常识 是,采用更高分辨率感受野的模型往往会显著提高识别性能。如果我们只是改变输入的分辨率而不进一步调整模型,那么最后就是使用低计算量的模型来解决更困难的任务。

问题转化为:如果计算量是恒定的,更高的分辨率能有多少帮助?

尽管低分辨率的网络需要训练更久,但是最终的效果差不太多。然而,只是根据输入分辨率简单的减小网络的尺寸结果往往会很糟糕。

(这部分的结论是更高分辨率的输入用更复杂的模型?)

本文将使用了所有提升的Inception-v2综合体称为Inception-v3。

我屮,表4有bug,Top-5和Top-1的标题反了

提供了几条设计准则,基于此来扩大卷积网络。

感觉Inception结构太复杂了,充满了魔数,看起来没有ResNet那种统一的简洁美。另外,感觉这篇讲的有点散,有种拼凑感。。要不是Label smoothing提升的不是特别多,应该都能专门拿出来写一篇。最受用的是几点设计准则,应该会有助于理解后来出现的网络的设计理念。

栈爆上一个用pandas实现label smoothing的示例

pytorch的官方实现只有v3,没有其他的

花书的751节(向输出目标注入噪声)解释了label smoothing背后的原理。

该篇文章探索的是用pytorch搭建的模型是否出现过拟合、欠拟合、数据问题。

怎么知道知道自己的模型是过拟合,欠拟合,数据问题?

1)学习曲线(learning curves)

2)交叉验证(corss-validation)

3)我们可以先通过训练集和测试集准确率的大小,直观的判断模型是否过拟合;当没有把握断定模型是否过拟合时,再借助学习曲线。

详细介绍请查看此 文章

一份可运行的学习曲线(learning curves) 1 2 3

过拟合是模型对训练集数据拟合能力太强,甚至将训练数据中的noise都学习进去了,造成了在测试集上预测能力差的情况。

出现过拟合的原因

·训练数据量级小于模型的复杂度;

·训练集和测试集特征分布不一致;

·样本里的噪声数据过大,大到模型过分记住了噪声特征,反而忽略了真实的输入输出的关系;

·权值学习迭代次数足够多(overtraining)

过拟合,克服思路

1·利用dropout

2·利用L2/L1 regularization

torchoptim集成了很多优化器,如SGD,Adadelta,Adam,Adagrad,RMSprop等,这些优化器中有一个参数weight_decay,用于指定权值衰减率,相当于L2正则化中的λ参数。L2正则化:

缺点:torchoptim的优化器只能实现L2正则化,不能实现L1正则化。

3·调小batch_size

4·搜集更多数据

5·对神经元归一化BatchNorm

pytorch中BatchNorm有BatchNorm1d、BatchNorm2d、BatchNorm3d三种,根据具体数据选择不同的BatchNorm,BatchNorm层的使用与普通的层使用方法类似。

参考文章:

1 sklearn模型调优(判断是否过拟合及选择参数)

2 过拟合(出现的原因4种、解决方案6种)

3 深度学习过拟合解决方案(pytorch相关方案实现)

4 欠拟合、过拟合及其解决方法

人工智能技术是当前炙手可热的话题,而基于神经网络的深度学习技术更是热点中的热点。去年谷歌的Alpha Go 以4:1大比分的优势战胜韩国的李世石九段,展现了深度学习的强大威力,后续强化版的Alpha Master和无师自通的Alpha Zero更是在表现上完全碾压前者。不论你怎么看,以深度学习为代表的人工智能技术正在塑造未来。

下图为英伟达(NVIDIA)公司近年来的股价情况, 该公司的主要产品是“图形处理器”(GPU),而GPU被证明能大大加快神经网络的训练速度,是深度学习必不可少的计算组件。英伟达公司近年来股价的飞涨足以证明当前深度学习的井喷之势。

好,话不多说,下面简要介绍神经网络的基本原理、发展脉络和优势。

神经网络是一种人类由于受到生物神经细胞结构启发而研究出的一种算法体系,是机器学习算法大类中的一种。首先让我们来看人脑神经元细胞:

一个神经元通常具有多个树突 ,主要用来接受传入信息,而轴突只有一条,轴突尾端有许多轴突末梢,可以给其他多个神经元传递信息。轴突末梢跟其他神经元的树突产生连接,从而传递信号。

下图是一个经典的神经网络(Artificial Neural Network,ANN):

乍一看跟传统互联网的拓扑图有点类似,这也是称其为网络的原因,不同的是节点之间通过有向线段连接,并且节点被分成三层。我们称图中的圆圈为神经元,左边三个神经元组成的一列为输入层,中间神经元列为隐藏层,右边神经元列为输出层,神经元之间的箭头为权重。

神经元是计算单元,相当于神经元细胞的细胞核,利用输入的数据进行计算,然后输出,一般由一个线性计算部分和一个非线性计算部分组成;输入层和输出层实现数据的输入输出,相当于细胞的树突和轴突末梢;隐藏层指既不是输入也不是输出的神经元层,一个神经网络可以有很多个隐藏层。

神经网络的关键不是圆圈代表的神经元,而是每条连接线对应的权重。每条连接线对应一个权重,也就是一个参数。权重具体的值需要通过神经网络的训练才能获得。我们实际生活中的学习体现在大脑中就是一系列神经网络回路的建立与强化,多次重复的学习能让回路变得更加粗壮,使得信号的传递速度加快,最后对外表现为“深刻”的记忆。人工神经网络的训练也借鉴于此,如果某种映射关系出现很多次,那么在训练过程中就相应调高其权重。

1943年,心理学家McCulloch和数学家Pitts参考了生物神经元的结构,发表了抽象的神经元模型MP:

符号化后的模型如下:

Sum函数计算各权重与输入乘积的线性组合,是神经元中的线性计算部分,而sgn是取符号函数,当输入大于0时,输出1,反之输出0,是神经元中的非线性部分。向量化后的公式为z=sgn(w^T a)(w^T=(w_1,w_2,w_3),a=〖(a_1,a_2,a_3)〗^T)。

但是,MP模型中,权重的值都是预先设置的,因此不能学习。该模型虽然简单,并且作用有限,但已经建立了神经网络大厦的地基

1958年,计算科学家Rosenblatt提出了由两层神经元组成(一个输入层,一个输出层)的神经网络。他给它起了一个名字–“感知器”(Perceptron)

感知器是当时首个可以学习的人工神经网络。Rosenblatt现场演示了其学习识别简单图像的过程,在当时引起了轰动,掀起了第一波神经网络的研究热潮。

但感知器只能做简单的线性分类任务。1969年,人工智能领域的巨擘Minsky指出这点,并同时指出感知器对XOR(异或,即两个输入相同时输出0,不同时输出1)这样的简单逻辑都无法解决。所以,明斯基认为神经网络是没有价值的。

随后,神经网络的研究进入低谷,又称 AI Winter 。

Minsky说过单层神经网络无法解决异或问题,但是当增加一个计算层以后,两层神经网络不仅可以解决异或问题,而且具有非常好的非线性分类效果。

下图为两层神经网络(输入层一般不算在内):

上图中,输出层的输入是上一层的输出。

向量化后的公式为:

注意:

每个神经元节点默认都有偏置变量b,加上偏置变量后的计算公式为:

同时,两层神经网络不再使用sgn函数作为激励函数,而采用平滑的sigmoid函数:

σ(z)=1/(1+e^(-z) )

其图像如下:

理论证明: 两层及以上的神经网络可以无限逼近真实的对应函数,从而模拟数据之间的真实关系 ,这是神经网络强大预测能力的根本。但两层神经网络的计算量太大,当时的计算机的计算能力完全跟不上,直到1986年,Rumelhar和Hinton等人提出了反向传播(Backpropagation,BP)算法,解决了两层神经网络所需要的复杂计算量问题,带动了业界使用两层神经网络研究的热潮。

但好景不长,算法的改进仅使得神经网络风光了几年,然而计算能力不够,局部最优解,调参等一系列问题一直困扰研究人员。90年代中期,由Vapnik等人发明的SVM(Support Vector Machines,支持向量机)算法诞生,很快就在若干个方面体现出了对比神经网络的优势:无需调参;高效;全局最优解。

由于以上原因,SVM迅速打败了神经网络算法成为主流。神经网络的研究再一次进入低谷, AI Winter again 。

多层神经网络一般指两层或两层以上的神经网络(不包括输入层),更多情况下指两层以上的神经网络。

2006年,Hinton提出使用 预训练 ”(pre-training)和“微调”(fine-tuning)技术能优化神经网络训练,大幅度减少训练多层神经网络的时间

并且,他给多层神经网络相关的学习方法赋予了一个新名词–“ 深度学习 ”,以此为起点,“深度学习”纪元开始了:)

“深度学习”一方面指神经网络的比较“深”,也就是层数较多;另一方面也可以指神经网络能学到很多深层次的东西。研究发现,在权重参数不变的情况下,增加神经网络的层数,能增强神经网络的表达能力。

但深度学习究竟有多强大呢?没人知道。2012年,Hinton与他的学生在ImageNet竞赛中,用多层的卷积神经网络成功地对包含一千类别的一百万张进行了训练,取得了分类错误率15%的好成绩,这个成绩比第二名高了近11个百分点,充分证明了多层神经网络识别效果的优越性。

同时,科研人员发现GPU的大规模并行矩阵运算模式完美地契合神经网络训练的需要,在同等情况下,GPU的速度要比CPU快50-200倍,这使得神经网络的训练时间大大减少,最终再一次掀起了神经网络研究的热潮,并且一直持续到现在。

2016年基于深度学习的Alpha Go在围棋比赛中以4:1的大比分优势战胜了李世石,深度学习的威力再一次震惊了世界。

神经网络的发展历史曲折荡漾,既有被捧上神坛的高潮,也有无人问津的低谷,中间经历了数次大起大落,我们姑且称之为“三起三落”吧,其背后则是算法的改进和计算能力的持续发展。

下图展示了神经网络自发明以来的发展情况及一些重大时间节点。

当然,对于神经网络我们也要保持清醒的头脑。由上图,每次神经网络研究的兴盛期持续10年左右,从最近2012年算起,或许10年后的2022年,神经网络的发展将再次遇到瓶颈。

神经网络作为机器学习的一种,其模型训练的目的,就是使得参数尽可能的与真实的模型逼近。理论证明,两层及以上的神经网络可以无限逼近真实的映射函数。因此,给定足够的训练数据和训练时间,总能通过神经网络找到无限逼近真实关系的模型。

具体做法:首先给所有权重参数赋上随机值,然后使用这些随机生成的参数值,来预测训练数据中的样本。假设样本的预测目标为yp ,真实目标为y,定义值loss,计算公式如下:

loss = (yp -y) ^2

这个值称之为 损失 (loss),我们的目标就是使对所有训练数据的损失和尽可能的小,这就转化为求loss函数极值的问题。

一个常用方法是高等数学中的求导,但由于参数不止一个,求导后计算导数等于0的运算量很大,所以常用梯度下降算法来解决这样的优化问题。梯度是一个向量,由函数的各自变量的偏导数组成。

比如对二元函数 f =(x,y),则梯度∇f=(∂f/∂x,∂f/∂y)。梯度的方向是函数值上升最快的方向。梯度下降算法每次计算参数在当前的梯度,然后让参数向着梯度的反方向前进一段距离,不断重复,直到梯度接近零时截止。一般这个时候,所有的参数恰好达到使损失函数达到一个最低值的状态。下图为梯度下降的大致运行过程:

在神经网络模型中,由于结构复杂,每次计算梯度的代价很大。因此还需要使用 反向传播 (Back Propagation)算法。反向传播算法利用了神经网络的结构进行计算,不一次计算所有参数的梯度,而是从后往前。首先计算输出层的梯度,然后是第二个参数矩阵的梯度,接着是中间层的梯度,再然后是第一个参数矩阵的梯度,最后是输入层的梯度。计算结束以后,所要的两个参数矩阵的梯度就都有了。当然,梯度下降只是其中一个优化算法,其他的还有牛顿法、RMSprop等。

确定loss函数的最小值后,我们就确定了整个神经网络的权重,完成神经网络的训练。

在神经网络中一样的参数数量,可以用更深的层次去表达。

由上图,不算上偏置参数的话,共有三层神经元,33个权重参数。

由下图,保持权重参数不变,但增加了两层神经元。

在多层神经网络中,每一层的输入是前一层的输出,相当于在前一层的基础上学习,更深层次的神经网络意味着更深入的表示特征,以及更强的函数模拟能力。更深入的表示特征可以这样理解,随着网络的层数增加,每一层对于前一层次的抽象表示更深入。

如上图,第一个隐藏层学习到“边缘”的特征,第二个隐藏层学习到“边缘”组成的“形状”的特征,第三个隐藏层学习到由“形状”组成的“图案”的特征,最后的隐藏层学习到由“图案”组成的“目标”的特征。通过抽取更抽象的特征来对事物进行区分,从而获得更好的区分与分类能力。

前面提到, 明斯基认为Rosenblatt提出的感知器模型不能处理最简单的“异或”(XOR)非线性问题,所以神经网络的研究没有前途,但当增加一层神经元后,异或问题得到了很好地解决,原因何在?原来从输入层到隐藏层,数据发生了空间变换,坐标系发生了改变,因为矩阵运算本质上就是一种空间变换。

如下图,红色和蓝色的分界线是最终的分类结果,可以看到,该分界线是一条非常平滑的曲线。

但是,改变坐标系后,分界线却表现为直线,如下图:

同时,非线性激励函数的引入使得神经网络对非线性问题的表达能力大大加强。

对于传统的朴素贝叶斯、决策树、支持向量机SVM等分类器,提取特征是一个非常重要的前置工作。在正式训练之前,需要花费大量的时间在数据的清洗上,这样分类器才能清楚地知道数据的维度,要不然基于概率和空间距离的线性分类器是没办法进行工作的。然而在神经网络中,由于巨量的线性分类器的堆叠(并行和串行)以及卷积神经网络的使用,它对噪声的忍耐能力、对多通道数据上投射出来的不同特征偏向的敏感程度会自动重视或忽略,这样我们在处理的时候,就不需要使用太多的技巧用于数据的清洗了。有趣的是,业内大佬常感叹,“你可能知道SVM等机器学习的所有细节,但是效果并不好,而神经网络更像是一个黑盒,很难知道它究竟在做什么,但工作效果却很好”。

人类对机器学习的环节干预越少,就意味着距离人工智能的方向越近。神经网络的这个特性非常有吸引力。

1) 谷歌的TensorFlow开发了一个非常有意思的神经网络 入门教程 ,用户可以非常方便地在网页上更改神经网络的参数,并且能看到实时的学习效率和结果,非常适合初学者掌握神经网络的基本概念及神经网络的原理。网页截图如下:

2) 深度学习领域大佬吴恩达不久前发布的《 神经网络和深度学习 》MOOC,现在可以在网易云课堂上免费观看了,并且还有中文字幕。

3) 《神经网络于深度学习》(Michael Nielsen著)、《白话深度学习与TensorFlow》也是不错的入门书籍。

双抗训练法是一种可以有效抑制训练中有害的冗余反向传播的方法,主要通过设置一定的反向传播权重来控制梯度的流动,使每次反向传播更新的梯度更加准确。

如何实现双抗训练:

1、首先要求损失函数对于所有参数具有对称性;

2、使用AdaGrad、Adam或RMSProp优化器来使用双抗;

3、在每一次反向传播中添加两个新的梯度,分别代表双抗的梯度和正常反向传播的梯度;

梯度下降是非常常用的优化算法。作为机器学习的基础知识,这是一个必须要掌握的算法。借助本文,让我们来一起详细了解一下这个算法。

前言

本文的代码可以到我的Github上获取:

>

本文的算法示例通过Python语言实现,在实现中使用到了numpy和matplotlib。如果你不熟悉这两个工具,请自行在网上搜索教程。

关于优化

大多数学习算法都涉及某种形式的优化。优化指的是改变x以最小化或者最大化某个函数的任务。

我们通常以最小化指代大多数最优化问题。最大化可经由最小化来实现。

我们把要最小化或最大化的函数成为目标函数(objective function)或准则(criterion)。

我们通常使用一个上标表示最小化或最大化函数的x值,记做这样:

[x^ = arg; min; f(x)]

优化本身是一个非常大的话题。如果有兴趣,可以通过《数值优化》和《运筹学》的书籍进行学习。

模型与假设函数

所有的模型都是错误的,但其中有些是有用的。– George Edward Pelham Box

模型是我们对要分析的数据的一种假设,它是为解决某个具体问题从数据中学习到的,因此它是机器学习最核心的概念。

针对一个问题,通常有大量的模型可以选择。

本文不会深入讨论这方面的内容,关于各种模型请参阅机器学习的相关书籍。本文仅以最简单的线性模型为基础来讨论梯度下降算法。

这里我们先介绍一下在监督学习(supervised learning)中常见的三个符号:

m,描述训练样本的数量

x,描述输入变量或特征

y,描述输出变量或者叫目标值

请注意,一个样本可能有很多的特征,因此x和y通常是一个向量。不过在刚开始学习的时候,为了便于理解,你可以暂时理解为这就是一个具体的数值。

训练集会包含很多的样本,我们用 表示其中第i个样本。

x是数据样本的特征,y是其目标值。例如,在预测房价的模型中,x是房子的各种信息,例如:面积,楼层,位置等等,y是房子的价格。在图像识别的任务中,x是图形的所有像素点数据,y是图像中包含的目标对象。

我们是希望寻找一个函数,将x映射到y,这个函数要足够的好,以至于能够预测对应的y。由于历史原因,这个函数叫做假设函数(hypothesis function)。

学习的过程如下图所示。即:首先根据已有的数据(称之为训练集)训练我们的算法模型,然后根据模型的假设函数来进行新数据的预测。

线性模型(linear model)正如其名称那样:是希望通过一个直线的形式来描述模式。线性模型的假设函数如下所示:

[h_{\theta}(x) = \theta_{0} + \theta_{1} x]

这个公式对于大家来说应该都是非常简单的。如果把它绘制出来,其实就是一条直线。

下图是一个具体的例子,即: 的图形:

在实际的机器学习工程中,你会拥有大量的数据。这些数据会来自于某个数据源。它们存储在csv文件中,或者以其他的形式打包。

但是本文作为演示使用,我们通过一些简单的代码自动生成了需要的数据。为了便于计算,演示的数据量也很小。

import numpy as np

max_x = 10

data_size = 10

theta_0 = 5

theta_1 = 2

def get_data:

x = nplinspace(1, max_x, data_size)

noise = nprandomnormal(0, 02, len(x))

y = theta_0 + theta_1 x + noise

return x, y

这段代码很简单,我们生成了x范围是 [1, 10] 整数的10条数据。对应的y是以线性模型的形式计算得到,其函数是:。现实中的数据常常受到各种因素的干扰,所以对于y我们故意加上了一些高斯噪声。因此最终的y值为比原先会有轻微的偏离。

最后我们的数据如下所示:

x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

y = [666, 911, 1108, 1267, 1512, 1676, 1875, 2135, 2277, 2456]

我们可以把这10条数据绘制出来这样就有一个直观的了解了,如下图所示:

虽然演示用的数据是我们通过公式计算得到的。但在实际的工程中,模型的参数是需要我们通过数据学习到的。所以下文我们假设我们不知道这里线性模式的两个参数是什么,而是通过算法的形式求得。

最后再跟已知的参数进行对比以验证我们的算法是否正确。

有了上面的数据,我们可以尝试画一条直线来描述我们的模型。

例如,像下面这样画一条水平的直线:

很显然,这条水平线离数据太远了,非常的不匹配。

那我们可以再画一条斜线。

我们初次画的斜线可能也不贴切,它可能像下面这样:

最后我们通过不断尝试,找到了最终最合适的那条,如下所示:

梯度下降算法的计算过程,就和这种本能式的试探是类似的,它就是不停的迭代,一步步的接近最终的结果。

代价函数

上面我们尝试了几次通过一条直线来拟合(fitting)已有的数据。

二维平面上的一条直线可以通过两个参数唯一的确定,两个参数的确定也即模型的确定。那如何描述模型与数据的拟合程度呢?答案就是代价函数。

代价函数(cost function)描述了学习到的模型与实际结果的偏差程度。以上面的三幅图为例,最后一幅图中的红线相比第一条水平的绿线,其偏离程度(代价)应该是更小的。

很显然,我们希望我们的假设函数与数据尽可能的贴近,也就是说:希望代价函数的结果尽可能的小。这就涉及到结果的优化,而梯度下降就是寻找最小值的方法之一。

代价函数也叫损失函数。

对于每一个样本,假设函数会依据计算出一个估算值,我们常常用来表示。即 。

很自然的,我们会想到,通过下面这个公式来描述我们的模型与实际值的偏差程度:

[(h_\theta(x^i) - y^i)^2 = (\widehat{y}^{i} - y^i)^2 = (\theta_{0} + \theta_{1} x^{i} - y^{i})^2]

请注意, 是实际数据的值, 是我们的模型的估算值。前者对应了上图中的离散点的y坐标,后者对应了离散点在直线上投影点的y坐标。

每一条数据都会存在一个偏差值,而代价函数就是对所有样本的偏差求平均值,其计算公式如下所示:

[L(\theta) = \frac {1}{m} \sum_{i=1}^{m}(h_\theta(x^i) - y^i)^2 = \frac {1}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} x^{i} - y^{i})^2]

当损失函数的结果越小,则意味着通过我们的假设函数估算出的结果与真实值越接近。这也就是为什么我们要最小化损失函数的原因。

不同的模型可能会用不同的损失函数。例如,logistic回归的假设函数是这样的:。其代价函数是这样的:

借助上面这个公式,我们可以写一个函数来实现代价函数:

def cost_function(x, y, t0, t1):

cost_sum = 0

for i in range(len(x)):

cost_item = nppower(t0 + t1 x[i] - y[i], 2)

cost_sum += cost_item

return cost_sum / len(x)

这个函数的代码应该不用多做解释,它就是根据上面的完成计算。

我们可以尝试选取不同的 和 组合来计算代价函数的值,然后将结果绘制出来:

import numpy as np

import matplotlibpyplot as plt

from matplotlib import cm

from mpl_toolkitsmplot3d import Axes3D

theta_0 = 5

theta_1 = 2

def draw_cost(x, y):

fig = pltfigure(figsize=(10, 8))

ax = figgca(projection='3d')

scatter_count = 100

radius = 1

t0_range = nplinspace(theta_0 - radius, theta_0 + radius, scatter_count)

t1_range = nplinspace(theta_1 - radius, theta_1 + radius, scatter_count)

cost = npzeros((len(t0_range), len(t1_range)))

for a in range(len(t0_range)):

for b in range(len(t1_range)):

cost[a][b] = cost_function(x, y, t0_range[a], t1_range[b])

t0, t1 = npmeshgrid(t0_range, t1_range)

axset_xlabel('theta_0')

axset_ylabel('theta_1')

axplot_surface(t0, t1, cost, cmap=cmhsv)

在这段代码中,我们对 和 各自指定了一个范围进行100次的采样,然后以不同的 组合对来计算代价函数的值。

如果我们将所有点的代价函数值绘制出来,其结果如下图所示:

从这个图形中我们可以看出,当 越接近 [5, 2]时其结果(偏差)越小。相反,离得越远,结果越大。

直观解释

从上面这幅图中我们可以看出,代价函数在不同的位置结果大小不同。

从三维的角度来看,这就和地面的高低起伏一样。最高的地方就好像是山顶。

而我们的目标就是:从任意一点作为起点,能够快速寻找到一条路径并以此到达图形最低点(代价值最小)的位置。

而梯度下降的算法过程就和我们从山顶想要快速下山的做法是一样的。

在生活中,我们很自然会想到沿着最陡峭的路往下行是下山速度最快的。如下面这幅图所示:

针对这幅图,细心的读者可能很快就会有很多的疑问,例如:

对于一个函数,怎么确定下行的方向?

每一步该往前走多远?

有没有可能停留在半山腰的平台上?

这些问题也就是本文接下来要讨论的内容。

算法描述

梯度下降算法最开始的一点就是需要确定下降的方向,即:梯度。

我们常常用 来表示梯度。

对于一个二维空间的曲线来说,梯度就是其切线的方向。如下图所示:

而对于更高维空间的函数来说,梯度由所有变量的偏导数决定。

其表达式如下所示:

[\nabla f({\theta}) = ( \frac{\partial f({\theta})}{\partial \theta_1} , \frac{\partial f({\theta})}{\partial \theta_2} , , \frac{\partial f({\theta})}{\partial \theta_n} )]

在机器学习中,我们主要是用梯度下降算法来最小化代价函数,记做:

[\theta ^ = arg min L(\theta)]

其中,L是代价函数,是参数。

梯度下降算法的主体逻辑很简单,就是沿着梯度的方向一直下降,直到参数收敛为止。

记做:

[\theta ^{k + 1}_i = \theta^{k}_i - \lambda \nabla f(\theta^{k})]

这里的下标i表示第i个参数。 上标k指的是第k步的计算结果,而非k次方。在能够理解的基础上,下文的公式中将省略上标k。

这里有几点需要说明:

收敛是指函数的变化率很小。具体选择多少合适需要根据具体的项目来确定。在演示项目中我们可以选择001或者0001这样的值。不同的值将影响算法的迭代次数,因为在梯度下降的最后,我们会越来越接近平坦的地方,这个时候函数的变化率也越来越小。如果选择一个很小的值,将可能导致算法迭代次数暴增。

公式中的 称作步长,也称作学习率(learning rate)。它决定了每一步往前走多远,关于这个值我们会在下文中详细讲解。你可以暂时人为它是一个类似001或0001的固定值。

在具体的项目,我们不会让算法无休止的运行下去,所以通常会设置一个迭代次数的最大上限。

线性回归的梯度下降

有了上面的知识,我们可以回到线性模型代价函数的梯度下降算法实现了。

首先,根据代价函数我们可以得到梯度向量如下:

[\nabla f({\theta}) = (\frac{\partial L(\theta)}{ \partial\theta_{0}}, \frac{ \partial L(\theta)}{ \partial\theta_{1}}) = (\frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} x^{i} - y^{i}) , \frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} x^{i} - y^{i}) x^{i})]

接着,将每个偏导数带入迭代的公式中,得到:

[\theta_{0} := \theta_{0} - \lambda \frac{\partial L(\theta_{0})}{ \partial\theta_{0}} = \theta_{0} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} x^{i} - y^{i}) \ \theta_{1} := \theta_{1} - \lambda \frac{\partial L(\theta_{1})}{ \partial\theta_{1}} = \theta_{1} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} x^{i} - y^{i}) x^{i}]

由此就可以通过代码实现我们的梯度下降算法了,算法逻辑并不复杂:

learning_rate = 001

def gradient_descent(x, y):

t0 = 10

t1 = 10

delta = 0001

for times in range(1000):

sum1 = 0

sum2 = 0

for i in range(len(x)):

sum1 += (t0 + t1 x[i] - y[i])

sum2 += (t0 + t1 x[i] - y[i]) x[i]

t0_ = t0 - 2 learning_rate sum1 / len(x)

t1_ = t1 - 2 learning_rate sum2 / len(x)

print('Times: {}, gradient: [{}, {}]'format(times, t0_, t1_))

if (abs(t0 - t0_) < delta and abs(t1 - t1_) < delta):

print('Gradient descent finish')

return t0_, t1_

t0 = t0_

t1 = t1_

print('Gradient descent too many times')

return t0, t1

这段代码说明如下:

我们随机选择了 都为10作为起点

设置最多迭代1000次

收敛的范围设为0001

学习步长设为001

如果我们将算法迭代过程中求得的线性模式绘制出来,可以得到下面这幅动态图:

最后算法得到的结果如下:

Times: 657, gradient: [5196562662718697, 1952931052920264]

Times: 658, gradient: [5195558390180733, 19530753071808193]

Times: 659, gradient: [5194558335124868, 19532189556399233]

Times: 660, gradient: [5193562479839619, 19533620008416623]

Gradient descent finish

从输出中可以看出,算法迭代了660次就收敛了。这时的结果[5193562479839619, 19533620008416623],这已经比较接近目标值 [5, 2]了。如果需要更高的精度,可以将delta的值调的更小,当然,此时会需要更多的迭代次数。

高维扩展

虽然我们举的例子是二维的,但是对于更高维的情况也是类似的。同样是根据迭代的公式进行运算即可:

[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \frac{2\lambda}{m} \sum_{i=1}^{m}(h_\theta(x^{k})-y^k)x_i^k]

这里的下标i表示第i个参数,上标k表示第k个数据。

梯度下降家族BGD

在上面的内容中我们看到,算法的每一次迭代都需要把所有样本进行遍历处理。这种做法称为之Batch Gradient Descent,简称BGD。作为演示示例只有10条数据,这是没有问题的。

但在实际的项目中,数据集的数量可能是几百万几千万条,这时候每一步迭代的计算量就会非常的大了。

于是就有了下面两个变种。

SGD

Stochastic Gradient Descent,简称SGD,这种算法是每次从样本集中仅仅选择一个样本来进行计算。很显然,这样做算法在每一步的计算量一下就少了很多。

其算法公式如下:

[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \lambda(h_\theta(x^k)-y^k)x_i^k]

当然,减少算法计算量也是有代价的,那就是:算法结果会强依赖于随机取到的数据情况,这可能会导致算法的最终结果不太令人满意。

MBGD

以上两种做法其实是两个极端,一个是每次用到了所有数据,另一个是每次只用一个数据。

我们自然就会想到两者取其中的方法:每次选择一小部分数据进行迭代。这样既避免了数据集过大导致每次迭代计算量过大的问题,也避免了单个数据对算法的影响。

这种算法称之为Mini-batch Gradient Descent,简称MBGD。

其算法公式如下:

[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \frac{2\lambda}{m} \sum_{i=a}^{a + b}(h_\theta(x^k)-y^k)x_i^k]

当然,我们可以认为SGD是Mini-batch为1的特例。

针对上面提到的算法变种,该如何选择呢?

下面是Andrew Ng给出的建议:

如果样本数量较小(例如小于等于2000),选择BGD即可。

如果样本数量很大,选择 来进行MBGD,例如:64,128,256,512。

下表是 Optimization for Deep Learning 中对三种算法的对比

方法准确性更新速度内存占用在线学习BGD好慢高否SGD好(with annealing)快低是MBGD好中等中等是

算法优化

式7是算法的基本形式,在这个基础上有很多人进行了更多的研究。接下来我们介绍几种梯度下降算法的优化方法。

Momentum

Momentum是动量的意思。这个算法的思想就是借助了动力学的模型:每次算法的迭代会使用到上一次的速度作为依据。

算法的公式如下:

[v^t = \gamma v^{t - 1} + \lambda \nabla f(\theta) \ \theta = \theta - v_t]

对比式7可以看出,这个算法的主要区别就是引入了,并且,每个时刻的受前一个时刻的影响。

从形式上看,动量算法引入了变量 v 充当速度角色——它代表参数在参数空间移动的方向和速率。速度被设为负梯度的指数衰减平均。名称动量来自物理类比,根据牛顿运动定律,负梯度是移动参数空间中粒子的力。动量在物理学上定义为质量乘以速度。在动量学习算法中,我们假设是单位质量,因此速度向量 v 也可以看作是粒子的动量。

对于可以取值0,而是一个常量,设为09是一个比较好的选择。

下图是momentum算法的效果对比:

对原来的算法稍加修改就可以增加动量效果:

def gradient_descent_with_momentum(x, y):

t0 = 10

t1 = 10

delta = 0001

v0 = 0

v1 = 0

gamma = 09

for times in range(1000):

sum1 = 0

sum2 = 0

for i in range(len(x)):

sum1 += (t0 + t1 x[i] - y[i])

sum2 += (t0 + t1 x[i] - y[i]) x[i]

v0 = gamma v0 + 2 learning_rate sum1 / len(x)

v1 = gamma v1 + 2 learning_rate sum2 / len(x)

t0_ = t0 - v0

t1_ = t1 - v1

print('Times: {}, gradient: [{}, {}]'format(times, t0_, t1_))

if (abs(t0 - t0_) < delta and abs(t1 - t1_) < delta):

print('Gradient descent finish')

return t0_, t1_

t0 = t0_

t1 = t1_

print('Gradient descent too many times')

return t0, t1

以下是该算法的输出:

Times: 125, gradient: [4955453758569991, 2000005017897775]

Times: 126, gradient: [4955309381126545, 19956928964532015]

Times: 127, gradient: [49542964317327005, 19855674828684156]

Times: 128, gradient: [49536358220657, 19781180992510465]

Times: 129, gradient: [495412496254411, 19788858350530971]

Gradient descent finish

从结果可以看出,改进的算法只用了129次迭代就收敛了。速度比原来660次快了很多。

同样的,我们可以把算法计算的过程做成动态图:

对比原始的算法过程可以看出,改进算法最大的区别是:在寻找目标值时会在最终结果上下跳动,但是越往后跳动的幅度越小,这也就是动量所产生的效果。

Learning Rate 优化

至此,你可能还是好奇该如何设定学习率的值。

事实上,这个值的选取需要一定的经验或者反复尝试才能确定。

《深度学习》一书中是这样描述的:“与其说是科学,这更像是一门艺术,我们应该谨慎地参考关于这个问题的大部分指导。”。

关键在于,这个值的选取不能过大也不能过小。

如果这个值过小,会导致每一次迭代的步长很小,其结果就是算法需要迭代非常多的次数。

那么,如果这个值过大会怎么样呢?其结果就是:算法可能在结果的周围来回震荡,却落不到目标的点上。下面这幅图描述了这个现象:

事实上,学习率的取值未必一定要是一个常数,关于这个值的设定有很多的研究。

下面是比较常见的一些改进算法。

AdaGrad

AdaGrad是Adaptive Gradient的简写,该算法会为每个参数设定不同的学习率。它使用历史梯度的平方和作为基础来进行计算。

其算法公式如下:

[\theta_i = \theta_i - \frac{\lambda}{\sqrt{G_t + \epsilon}} \nabla f(\theta)]

对比式7,这里的改动就在于分号下面的根号。

根号中有两个符号,第二个符号比较好理解,它就是为了避免除0而人为引入的一个很小的常数,例如可以设为:0001。

第一个符号的表达式展开如下:

[G_t = \sum_{i = 1}^{t} \nabla f(\theta){i}\nabla f(\theta){i}^{T}]

这个值其实是历史中每次梯度的平方的累加和。

AdaGrad算法能够在训练中自动的对learning rate进行调整,对于出现频率较低参数采用较大的学习率;相反,对于出现频率较高的参数采用较小的学习率。因此,Adagrad非常适合处理稀疏数据。

但该算法的缺点是它可能导致学习率非常小以至于算法收敛非常的慢。

关于这个算法的直观解释可以看李宏毅教授的视频课程:ML Lecture 3-1: Gradient Descent。

RMSProp

RMS是Root Mean Square的简写。RMSProp是AI教父Geoff Hinton提出的一种自适应学习率方法。AdaGrad会累加之前所有的梯度平方,而RMSProp仅仅是计算对应的平均值,因此可缓解Adagrad算法学习率下降较快的问题。

该算法的公式如下:

[E[\nabla f(\theta_{i})^2]^{t} = \gamma E[\nabla f(\theta_{i})^2]^{t - 1} + (1-\gamma)(\nabla f(\theta_{i})^{t})^{2} \ \theta_i = \theta_i - \frac{\lambda}{\sqrt{E[g^2]^{t+1} + \epsilon}} \nabla f(\theta_{i})]

类似的,是为了避免除0而引入。 是衰退参数,通常设为09。

这里的 是t时刻梯度平方的平均值。

Adam

Adam是Adaptive Moment Estimation的简写。它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。

Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。

该算法公式如下:

[m^{t} = \beta_{1} m^{t-1} + (1-\beta_{1}) \nabla f(\theta) \ v^{t} = \beta_{2} v^{t-1} + (1-\beta_{2}) \nabla f(\theta)^2 \ \widehat{m}^{t} = \frac{m^{t}}{1 - \beta^{t}_1} \ \widehat{v}^{t} = \frac{v^{t}}{1 - \beta^{t}_2} \ \theta = \theta - \frac{\lambda}{\sqrt{\widehat{v}^{t}} + \epsilon}\widehat{m}^{t}]

,分别是对梯度的一阶矩估计和二阶矩估计。, 是对,的校正,这样可以近似为对期望的无偏估计。

Adam算法的提出者建议 默认值为09,默认值为0999,默认值为 。

在实际应用中 ,Adam较为常用,它可以比较快地得到一个预估结果。

优化小结

这里我们列举了几种优化算法。它们很难说哪种最好,不同的算法适合于不同的场景。在实际的工程中,可能需要逐个尝试一下才能确定选择哪一个,这个过程也是目前现阶段AI项目要经历的工序之一。

实际上,该方面的研究远不止于此,如果有兴趣,可以继续阅读 《Sebastian Ruder: An overview of gradient descent optimization algorithms》 这篇论文或者 Optimization for Deep Learning 这个Slides进行更多的研究。

由于篇幅所限,这里不再继续展开了。

算法限制

梯度下降算法存在一定的限制。首先,它要求函数必须是可微分的,对于不可微的函数,无法使用这种方法。

除此之外,在某些情况下,使用梯度下降算法在接近极值点的时候可能收敛速度很慢,或者产生Z字形的震荡。这一点需要通过调整学习率来回避。

另外,梯度下降还会遇到下面两类问题。

局部最小值

局部最小值(Local Minima)指的是,我们找到的最小值仅仅是一个区域内的最小值,而并非全局的。由于算法的起点是随意取的,以下面这个图形为例,我们很容易落到局部最小值的点里面。

这就是好像你从上顶往下走,你第一次走到的平台未必是山脚,它有可能只是半山腰的一个平台的而已。

算法的起点决定了算法收敛的速度以及是否会落到局部最小值上。

坏消息是,目前似乎没有特别好的方法来确定选取那个点作为起点是比较好的,这就有一点看运气的成分了。多次尝试不同的随机点或许是一个比较好的方法,这也就是为什么做算法的优化这项工作是特别消耗时间的了。

但好消息是:

对于凸函数或者凹函数来说,不存在局部极值的问题。其局部极值一定是全局极值。

最近的一些研究表明,某些局部极值并没有想象中的那么糟糕,它们已经非常的接近全局极值所带来的结果了。

鞍点

除了Local Minima,在梯度下降的过程中,还有可能遇到另外一种情况,即:鞍点(Saddle Point)。鞍点指的是我们找到点某个点确实是梯度为0,但它却不是函数的极值,它的周围既有比它小的值,也有比它大的值。这就好像马鞍一样。

如下图所示:

多类随机函数表现出以下性质:在低维空间中,局部极值很普遍。但在高维空间中,局部极值比较少见,而鞍点则很常见。

不过对于鞍点,可以通过数学方法Hessian矩阵来确定。关于这点,这里就不再展开了,有兴趣的读者可以以这里提供的几个链接继续探索。

参考资料与推荐读物

Wikipeida: Gradient descent

Sebastian Ruder: An overview of gradient descent optimization algorithms

吴恩达:机器学习

吴恩达:深度学习

Peter Flach:机器学习

李宏毅 - ML Lecture 3-1: Gradient Descent

PDF: 李宏毅 - Gradient Descent

Intro to optimization in deep learning: Gradient Descent

Intro to optimization in deep learning: Momentum, RMSProp and Adam

Stochastic Gradient Descent – Mini-batch and more

刘建平Pinard - 梯度下降(Gradient Descent)小结

多元函数的偏导数、方向导数、梯度以及微分之间的关系思考

[Machine Learning] 梯度下降法的三种形式BGD、SGD以及MBGD

作者:阿Paul >

目标函数是衡量预测值和实际值的相似程度的指标。通常,我们希望得到使代价尽可能小的参数集,而这意味着你的算法性能不错。函数的最小可能代价被称为最小值。有时一个代价函数可以有多个局部极小值。幸运的是,在参数空间的维数非常高的情况下,阻碍目标函数充分优化的局部最小值并不经常出现,因为这意味着对象函数相对于每个参数在训练过程的早期都是凹的。但这并非常态,通常我们得到的是许多鞍点,而不是真正的最小值。

找到生成最小值的一组参数的算法被称为优化算法。我们发现随着算法复杂度的增加,则算法倾向于更高效地逼近最小值。我们将在这篇文章中讨论以下算法:

随机梯度下降法

动量算法

RMSProp

Adam 算法

随机梯度下降法

我的「Logistic 回归深入浅出」的文章里介绍了一个随机梯度下降如何运作的例子。如果你查阅随机梯度下降法的资料(SGD),通常会遇到如下的等式:

资料上会说,θ是你试图找到最小化 J 的参数,这里的 J 称为目标函数。最后,我们将学习率记为α。通常要反复应用上述等式,直到达到你所需的代价值。

这是什么意思?想一想,假如你坐在一座山顶上的雪橇上,望着另一座山丘。如果你滑下山丘,你会自然地往下移动,直到你最终停在山脚。如果第一座小山足够陡峭,你可能会开始滑上另一座山的一侧。从这个比喻中你可以想到:

学习率越高意味着摩擦力越小,因此雪橇会像在冰上一样沿着山坡下滑。低的学习率意味着摩擦力高,所以雪橇会像在地毯上一样,难以滑下。我们如何用上面的方程来模拟这种效果?

随机梯度下降法:

初始化参数(θ,学习率)

计算每个θ处的梯度

更新参数

重复步骤 2 和 3,直到代价值稳定

让我们用一个简单的例子来看看它是如何运作的!

在这里我们看到一个目标函数和它的导数(梯度):

我们可以用下面的代码生成函数和梯度值/30 的图:

importnumpy asnp

defminimaFunction(theta):

returnnpcos(3nppitheta)/theta

defminimaFunctionDerivative(theta):

const1 =3nppi

const2 =const1theta

return-(const1npsin(const2)/theta)-npcos(const2)/theta2

theta =nparange(1,21,01)

Jtheta=minimaFunction(theta)

dJtheta =minimaFunctionDerivative(theta)

pltplot(theta,Jtheta,label =r'$J(theta)$')

pltplot(theta,dJtheta/30,label =r'$dJ(theta)/30$')

pltlegend()

axes =pltgca()

#axesset_ylim([-10,10])

pltylabel(r'$J(theta),dJ(theta)/30$')

pltxlabel(r'$theta$')

plttitle(r'$J(theta),dJ(theta)/30 $ vs $theta$')

pltshow()

上图中有两个细节值得注意。首先,注意这个代价函数有几个极小值(大约在 025、10 和 17 附近取得)。其次,注意在最小值处的导数在零附近的曲线走向。这个点就是我们所需要的新参。

我们可以在下面的代码中看到上面四个步骤的实现。它还会生成一个视频,显示每个步骤的θ和梯度的值。

importnumpy asnp

importmatplotlibpyplot asplt

importmatplotlibanimation asanimation

defoptimize(iterations,oF,dOF,params,learningRate):

"""

computes the optimal value of params for a given objective function and its derivative

Arguments:

- iteratoins - the number of iterations required to optimize the objective function

- oF - the objective function

- dOF - the derivative function of the objective function

- params - the parameters of the function to optimize

- learningRate - the learning rate

Return:

- oParams - the list of optimized parameters at each step of iteration

"""

oParams =[params]

#The iteration loop

fori inrange(iterations):

# Compute the derivative of the parameters

dParams =dOF(params)

# Compute the update

params =params-learningRatedParams

# app end the new parameters

oParamsappend(params)

returnnparray(oParams)

defminimaFunction(theta):

returnnpcos(3nppitheta)/theta

defminimaFunctionDerivative(theta):

const1 =3nppi

const2 =const1theta

return-(const1npsin(const2)/theta)-npcos(const2)/theta2

theta =6

iterations=45

learningRate =0007

optimizedParameters =optimize(iterations,

minimaFunction,

minimaFunctionDerivative,

theta,

learningRate)

这似乎运作得很好!您应该注意到,如果θ的初始值较大,则优化算法将在某一个局部极小处结束。然而,如上所述,在极高维度空间中这种可能性并不大,因为它要求所有参数同时满足凹函数。

你可能会想,「如果我们的学习率太大,会发生什么?」。如果步长过大,则算法可能永远不会找到如下的动画所示的最佳值。监控代价函数并确保它单调递减,这一点很重要。如果没有单调递减,可能需要降低学习率。

SGD 也适用于多变量参数空间的情况。我们可以将二维函数绘制成等高线图。在这里你可以看到 SGD 对一个不对称的碗形函数同样有效。

importnumpy asnp

importmatplotlibmlab asmlab

importmatplotlibpyplot asplt

importscipystats

importmatplotlibanimation asanimation

defminimaFunction(params):

#Bivariate Normal function

X,Y =params

sigma11,sigma12,mu11,mu12 =(30,5,00,00)

Z1 =mlabbivariate_normal(X,Y,sigma11,sigma12,mu11,mu12)

Z =Z1

return-40Z

defminimaFunctionDerivative(params):

# Derivative of the bivariate normal function

X,Y =params

sigma11,sigma12,mu11,mu12 =(30,5,00,00)

dZ1X =-scipystatsnormpdf(X,mu11,sigma11)(mu11 -X)/sigma112

dZ1Y =-scipystatsnormpdf(Y,mu12,sigma12)(mu12 -Y)/sigma122

return(dZ1X,dZ1Y)

defoptimize(iterations,oF,dOF,params,learningRate,beta):

"""

computes the optimal value of params for a given objective function and its derivative

Arguments:

- iteratoins - the number of iterations required to optimize the objective function

- oF - the objective function

- dOF - the derivative function of the objective function

- params - the parameters of the function to optimize

- learningRate - the learning rate

- beta - The weighted moving average parameter

Return:

以上就是关于优化算法全部的内容,包括:优化算法、『Rethinking the Inception Architecture for Computer Vision』论文笔记、判断模型是否过拟合、欠拟合、数据问题等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9840253.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存