伙伴云客服论坛»论坛 S区 S零代码 查看内容

0 评论

0 收藏

分享

推荐20个开源的前端低代码项目

无论是什么优化算法,最后都可以用一个简单的公式笼统:

推荐20个开源的前端低代码项目-1.jpg


推荐20个开源的前端低代码项目-2.jpg

是参数,而


推荐20个开源的前端低代码项目-3.jpg

是参数的增量,而各种优化算法的主要区别在于对


推荐20个开源的前端低代码项目-4.jpg

的计算不同,本文总结了下面十个优化算法的公式,以及简单的Python实现:


  • SGD
  • Momentum
  • Nesterov Momentum
  • AdaGrad
  • RMSProp
  • AdaDelta
  • Adam
  • AdaMax
  • Nadam
  • NadaMax


SGD



虽然有凑数的嫌疑,不过还是把SGD也顺带说一下,就算做一个符号说明了。常规的随机梯度下降公式如下:


推荐20个开源的前端低代码项目-5.jpg

其中


推荐20个开源的前端低代码项目-6.jpg

是学习率,


推荐20个开源的前端低代码项目-7.jpg

是损失关于参数的梯度(有的资料中会写成


推荐20个开源的前端低代码项目-8.jpg

等形式),不过相比SGD,用的更多的还是小批量梯度下降(mBGD)算法,不同之处在于一次训练使用多个样本,然后取所有参与训练样本梯度的平均来更新参数,公式如下:


推荐20个开源的前端低代码项目-9.jpg

其中


推荐20个开源的前端低代码项目-10.jpg

是第


推荐20个开源的前端低代码项目-11.jpg

次训练中


推荐20个开源的前端低代码项目-12.jpg

个样本损失关于参数梯度的均值,如无特别声明,下文所呈现


推荐20个开源的前端低代码项目-13.jpg

也遵循该定义。

另外


推荐20个开源的前端低代码项目-14.jpg

或者


推荐20个开源的前端低代码项目-15.jpg

在下面的优化算法中,只是作为一个传入的变量,其详细的计算是由其他模块负责,可以参考下面两个链接:

Numpy实现神经网络框架(3)——线性层反向传播推导及实现:

https://zhuanlan.zhihu.com/p/67854272

卷积核梯度计算的推导及实现:

https://zhuanlan.zhihu.com/p/64248652



Momentum

Momentum,也就是动量的意思。该算法将梯度下降的过程视为一个物理系统,下图是在百度图片中找的(侵删)。


推荐20个开源的前端低代码项目-16.jpg

图片来自网络

如上图所示,在该物理系统中有一个小球(质点),它所处的水平方向的位置对应为


推荐20个开源的前端低代码项目-17.jpg

的值,而垂直方向对应为损失。设其质量


推荐20个开源的前端低代码项目-18.jpg

,在第


推荐20个开源的前端低代码项目-19.jpg

时刻,在单位时间内,该质点受外力而形成的动量改变为:


推荐20个开源的前端低代码项目-20.jpg

(1.1)到(1.2)是因为


推荐20个开源的前端低代码项目-21.jpg

,所以约去了。另外受到的外力可以分为两个分量:重力沿斜面向下的力


推荐20个开源的前端低代码项目-22.jpg

和粘性阻尼力


推荐20个开源的前端低代码项目-23.jpg


推荐20个开源的前端低代码项目-24.jpg



推荐20个开源的前端低代码项目-25.jpg

代入(1.2)式中:


推荐20个开源的前端低代码项目-26.jpg

然后对“位置”停止更新:


推荐20个开源的前端低代码项目-27.jpg

所以这里


推荐20个开源的前端低代码项目-28.jpg

,另外


推荐20个开源的前端低代码项目-29.jpg

的方向与损失的梯度方向相反,并取系数为


推荐20个开源的前端低代码项目-30.jpg

,得到:


推荐20个开源的前端低代码项目-31.jpg

代入(1.4),得到速度的更新公式:


推荐20个开源的前端低代码项目-32.jpg

进一步的,将(1.6)式展开,可以得到:


推荐20个开源的前端低代码项目-33.jpg

可以看出来是一个变相的等比数列之和,且公比小于1,所以存在极限,当


推荐20个开源的前端低代码项目-34.jpg

足够大时,


推荐20个开源的前端低代码项目-35.jpg

趋近于


推荐20个开源的前端低代码项目-36.jpg

实现代码:

import numpy as npclass Momentum(object):    def __init__(self, alpha=0.9, lr=1e-3):        self.alpha = alpha  # 动量系数        self.lr = lr        # 学习率        self.v = 0          # 初始速度为0    def update(self, g: np.ndarray):    # g = J'(w) 为本轮训练参数的梯度        self.v = self.alpha * self.v - self.lr * g  # 公式        return self.v    # 返回的是参数的增量,下同
以上是基于指数衰减的实现方式,另外有的Momentum算法中会使用指数加权平均来实现,主要公式如下:


推荐20个开源的前端低代码项目-37.jpg

不过该方式因为


推荐20个开源的前端低代码项目-38.jpg

,刚开端时


推荐20个开源的前端低代码项目-39.jpg

会比期望值要小,需要停止修正,下面的Adam等算法会使用该方式

Nesterov Momentum

Nesterov Momentum是Momentum的改进版本,与Momentum唯一区别就是,Nesterov先用当前的速度


推荐20个开源的前端低代码项目-40.jpg

更新一遍参数,得到一个临时参数


推荐20个开源的前端低代码项目-41.jpg

,然后使用这个临时参数计算本轮训练的梯度。相当于是小球预判了自己下一时刻的位置,并提早使用该位置的梯度更新 :


推荐20个开源的前端低代码项目-42.jpg

为了更加直观,还是上几个图吧,以下是Momentum算法


推荐20个开源的前端低代码项目-43.jpg

的更新过程:


推荐20个开源的前端低代码项目-44.jpg

假设下一个位置的梯度如下:


推荐20个开源的前端低代码项目-45.jpg

那么Nesterov Momentum就提早使用这个梯度停止更新:


推荐20个开源的前端低代码项目-46.jpg

整体来看Nesterov的表现要好于Momentum,至于代码实现的话因为主要变化的是


推荐20个开源的前端低代码项目-47.jpg

,所以可以之前使用Momentum的代码

AdaGrad

AdaGrad全称为Adaptive Subgradient,其主要特点在于不时累加每次训练中梯度的平方,公式如下:


推荐20个开源的前端低代码项目-48.jpg

其中


推荐20个开源的前端低代码项目-49.jpg

是一个极小的正数,用来防止除0,而


推荐20个开源的前端低代码项目-50.jpg



推荐20个开源的前端低代码项目-51.jpg

是矩阵的哈达玛积运算符,另外,本文中矩阵的平方或者两矩阵相乘都是计算哈达玛积,而不是计算矩阵乘法

从公式中可以看出,随着算法不时迭代,


推荐20个开源的前端低代码项目-52.jpg

会越来越大,整体的学习率会越来越小。所以,一般来说AdaGrad算法一开端是鼓励收敛,到了后面就渐渐变成惩罚收敛,速度越来越慢

对于代码实现,首先将


推荐20个开源的前端低代码项目-53.jpg

展开得到:


推荐20个开源的前端低代码项目-54.jpg

通常


推荐20个开源的前端低代码项目-55.jpg

,所以在第一次训练时(2.2)式为:


推荐20个开源的前端低代码项目-56.jpg

因为每次训练


推荐20个开源的前端低代码项目-57.jpg

的值是不确定的,所以要防止处0,但是可以令


推荐20个开源的前端低代码项目-58.jpg

,这样就可以在(2.2)式中去掉


推荐20个开源的前端低代码项目-59.jpg




推荐20个开源的前端低代码项目-60.jpg



推荐20个开源的前端低代码项目-61.jpg

代入(2.3)式,可以得到:


推荐20个开源的前端低代码项目-62.jpg

可知


推荐20个开源的前端低代码项目-63.jpg

恒大于0,因而不用在计算


推荐20个开源的前端低代码项目-64.jpg

中额外参与


推荐20个开源的前端低代码项目-65.jpg

,代码如下:

class AdaGrad(object):    def __init__(self, eps=1e-8, lr=1e-3):        self.r = eps    # r_0 = epsilon        self.lr = lr    def update(self, g: np.ndarray):        r = r + np.square(g)        return -self.lr * g / np.sqrt(r)
RMSProp

RMSProp是AdaGrad的改进算法,其公式和AdaGrad的区别只要


推荐20个开源的前端低代码项目-66.jpg

的计算不同,先看公式


推荐20个开源的前端低代码项目-67.jpg

可以看出,与AdaGrad不同,RMSProp只会累积近期的梯度信息,对于“遥远的历史”会以指数衰减的形式放弃。

并且AdaGrad算法虽然在凸函数(Convex Functions)上表现较好,但是当目的函数非凸时,算法梯度下降的轨迹所经历的构造会复杂的多,早期梯度对当前训练没有太多意义,此时RMSProp往往表现更好

以下是将


推荐20个开源的前端低代码项目-68.jpg

展开后的公式:


推荐20个开源的前端低代码项目-69.jpg

与AdaGrad一样,令


推荐20个开源的前端低代码项目-70.jpg

,从而去掉计算


推荐20个开源的前端低代码项目-71.jpg

时的


推荐20个开源的前端低代码项目-72.jpg

,实现代码:

class RMSProp(object):    def __init__(self, lr=1e-3, beta=0.999, eps=1e-8):        self.r = eps        self.lr = lr        self.beta = beta    def update(self, g: np.ndarray):        r = r * self.beta + (1-self.beta) * np.square(g)        return -self.lr * g / np.sqrt(r)
AdaDelta

AdaDelta是与RMSProp相同时间对立开展出来的一个算法,在实现上可以看作是RMSProp的一个变种,先看公式:


推荐20个开源的前端低代码项目-73.jpg

可以看到该算法不需要设置学习率


推荐20个开源的前端低代码项目-74.jpg

,这是该算法的一大优势。除了同样以


推荐20个开源的前端低代码项目-75.jpg

来累积梯度的信息之外,该算法还多了一个


推荐20个开源的前端低代码项目-76.jpg

以指数衰减的形式来累积


推荐20个开源的前端低代码项目-77.jpg

的信息

与前面相同,令:


推荐20个开源的前端低代码项目-78.jpg

然后去掉(3.1)中的


推荐20个开源的前端低代码项目-79.jpg

,得到:


推荐20个开源的前端低代码项目-80.jpg

这样的话可以减少一些计算,代码如下:

class AdaDelta(object):    def __init__(self, beta=0.999, eps=1e-8):        self.r = eps        self.s = eps        self.beta = beta    def update(self, g: np.ndarray):        g_square = (1-self.beta) * np.square(g)     # (1-beta)*g^2        r = r * self.beta + g_square        frac = s / r        res = -np.sqrt(frac) * g        s = s * self.beta + frac * g_squaretmp      # 少一次乘法。。。        return res
关于以上几个算法的对比:


推荐20个开源的前端低代码项目-81.jpg

其中NAG是Nesterov Momentum


推荐20个开源的前端低代码项目-82.jpg

更多关于AdaDelta的信息,可以参考这篇文章:自适应学习率调整:AdaDelta(https://www.cnblogs.com/neopenx/p/4768388.html)

Adam

Adam的名称来自Adaptive Momentum,可以看作是Momentum与RMSProp的一个结合体,该算法通过计算梯度的一阶矩估计和二阶矩估计而为不同的参数设计独立的自适应性学习率,公式如下:


推荐20个开源的前端低代码项目-83.jpg

(4.1)和(4.2)在Momentum和RMSProp中已经介绍过了,而不直接使用


推荐20个开源的前端低代码项目-84.jpg

计算


推荐20个开源的前端低代码项目-85.jpg

却先经过(4.3)和(4.4)式是因为通常会设


推荐20个开源的前端低代码项目-86.jpg

,所以此时梯度的一阶矩估计和二阶矩估是有偏的,需要停止修正。

虽然没办法防止修正计算,但是还是可以省去一些计算过程,初始化时令:


推荐20个开源的前端低代码项目-87.jpg

然后(4.5)式变为:


推荐20个开源的前端低代码项目-88.jpg

因为


推荐20个开源的前端低代码项目-89.jpg

,可知当


推荐20个开源的前端低代码项目-90.jpg

足够大时修正将不起作用(也不需要修正了):


推荐20个开源的前端低代码项目-91.jpg

代码如下:

class Adam(object):    def __init__(self, lr=1e-3, alpha=0.9, beta=0.999, eps=1e-8):        self.s = 0        self.r = eps        self.lr = lr        self.alpha = alpha        self.beta = beta        self.alpha_i = 1        self.beta_i = 1    def update(self, g: np.ndarray):        self.s = self.s * self.alpha + (1-self.alpha) * g        self.r = self.r * self.beta + (1-self.beta) * np.square(g)        self.alpha_i *= self.alpha        self.beta_i *= self.beta_i        lr = -self.lr * (1-self.beta_i)**0.5 / (1-self.alpha_i)        return lr * self.s / np.sqrt(self.r)
AdaMax

首先回忆RSMSProp中


推荐20个开源的前端低代码项目-92.jpg

的展开式并且令


推荐20个开源的前端低代码项目-93.jpg

,得到:


推荐20个开源的前端低代码项目-94.jpg

可以看到这相当于是一个


推荐20个开源的前端低代码项目-95.jpg



推荐20个开源的前端低代码项目-96.jpg

范数,也就是说


推荐20个开源的前端低代码项目-97.jpg

的各维度的增量是根据该维度上梯度的


推荐20个开源的前端低代码项目-98.jpg

范数的累积量停止缩放的。假设用


推荐20个开源的前端低代码项目-99.jpg

范数替代就得到了Adam的不同变种,不过其中


推荐20个开源的前端低代码项目-100.jpg

范数对应的变种算法简单且稳定

对于


推荐20个开源的前端低代码项目-101.jpg

范数,第


推荐20个开源的前端低代码项目-102.jpg

轮训练时梯度的累积为:


推荐20个开源的前端低代码项目-103.jpg

然后求无穷范数:


推荐20个开源的前端低代码项目-104.jpg

由此再来递推


推荐20个开源的前端低代码项目-105.jpg



推荐20个开源的前端低代码项目-106.jpg

需要注意,这个max比较的是梯度各个维度上的当前值和历史最大值,详细可以结合代码来看,最后其公式总结如下:


推荐20个开源的前端低代码项目-107.jpg

另外,因为


推荐20个开源的前端低代码项目-108.jpg

是累积的梯度各个分量的绝对值最大值,所以直接用作分母且不需要修正,代码如下:

class AdaMax(object):    def __init__(self, lr=1e-3, alpha=0.9, beta=0.999):        self.s = 0        self.r = 0        self.lr = lr        self.alpha = alpha        self.alpha_i = 1        self.beta = beta    def update(self, g: np.ndarray):        self.s = self.s * self.alpha + (1-self.alpha) * g        self.r = np.maximum(self.r*self.beta, np.abs(g))        self.alpha_i *= self.alpha        lr = -self.lr / (1-self.alpha_i)        return lr * self.s / self.r
Nadam

Adam可以看作是Momentum与RMSProp的结合,既然Nesterov的表现较Momentum更优,那么自然也就可以把Nesterov Momentum与RMSProp组合到一起了,首先来看Nesterov的主要公式:


推荐20个开源的前端低代码项目-109.jpg

为了令其更加接近Momentum,将(5.1)和(5.2)修改为:


推荐20个开源的前端低代码项目-110.jpg

然后列出Adam中Momentum的部分:


推荐20个开源的前端低代码项目-111.jpg

将(5.5)和(5.6)式代入到(5.7)式中:


推荐20个开源的前端低代码项目-112.jpg

将上式中标红部分停止近似:


推荐20个开源的前端低代码项目-113.jpg

代入原式,得到:


推荐20个开源的前端低代码项目-114.jpg

接着,依照(5.4)式的套路,将


推荐20个开源的前端低代码项目-115.jpg

交换成


推荐20个开源的前端低代码项目-116.jpg

,得到:


推荐20个开源的前端低代码项目-117.jpg

整理一下公式:


推荐20个开源的前端低代码项目-118.jpg



同样令


推荐20个开源的前端低代码项目-119.jpg

,消去(5.8)式种的


推荐20个开源的前端低代码项目-120.jpg



推荐20个开源的前端低代码项目-121.jpg

代码如下:

class Nadam(object):    def __init__(self, lr=1e-3, alpha=0.9, beta=0.999, eps=1e-8):        self.s = 0        self.r = eps        self.lr = lr        self.alpha = alpha        self.beta = beta        self.alpha_i = 1        self.beta_i = 1    def update(self, g: np.ndarray):        self.s = self.s * self.alpha + (1-self.alpha) * g        self.r = self.r * self.beta + (1-self.beta) * np.square(g)        self.alpha_i *= self.alpha        self.beta_i *= self.beta_i        lr = -self.lr * (1-self.beta_i)**0.5 / (1-self.alpha_i)        return lr * (self.s * self.alpha + (1-self.alpha) * g) / np.sqrt(self.r)
NadaMax

依照同样的思路,可以将Nesterov与AdaMax结合变成NadaMax,回忆以下(5.8)式:


推荐20个开源的前端低代码项目-122.jpg

然后是AdaMax的二阶矩估计部分:


推荐20个开源的前端低代码项目-123.jpg

用(6.2)式交换掉(6.1)式中标红部分,得到:


推荐20个开源的前端低代码项目-124.jpg

最后,整理公式:


推荐20个开源的前端低代码项目-125.jpg

代码实现:

class NadaMax(object):    def __init__(self, lr=1e-3, alpha=0.9, beta=0.999):        self.s = 0        self.r = 0        self.lr = lr        self.alpha = alpha        self.alpha_i = 1        self.beta = beta    def update(self, g: np.ndarray):        self.s = self.s * self.alpha + (1-self.alpha) * g        self.r = np.maximum(self.r*self.beta, np.abs(g))        self.alpha_i *= self.alpha        lr = -self.lr / (1-self.alpha_i)        return lr * (self.s * self.alpha + (1-self.alpha) * g) / self.r参考资料:
[1]: 《机器学习算法背后的理论与优化》 ISBN 978-7-302-51718-4

[2]: Adam: A Method for Stochastic Optimization(https://arxiv.org/abs/1412.6980)

[3]: Incorporating Nesterov Momentum into Adam(https://openreview.net/forum?id=OM0jvwB8jIp57ZJjtNEZ¬eId=OM0jvwB8jIp57ZJjtNEZ)

[4]: An overview of gradient descent optimization algorithms(https://ruder.io/optimizing-gradient-descent/index.html)

回复

举报 使用道具

全部回复
暂无回帖,快来参与回复吧
本版积分规则 高级模式
B Color Image Link Quote Code Smilies

待在绿匣里的猫
注册会员
主题 18
回复 14
粉丝 0
|网站地图
快速回复 返回顶部 返回列表