千次阅读
2022-01-14 11:16:14

class Nadam(Optimizer):

Much like Adam is essentially RMSprop with momentum,

Default parameters follow those provided in the paper.
It is recommended to leave the parameters of this optimizer
at their default values.

Arguments:
lr: float >= 0. Learning rate.
beta_1/beta_2: floats, 0 < beta < 1. Generally close to 1.
epsilon: float >= 0. Fuzz factor. If None, defaults to K.epsilon().
"""

def __init__(self,
lr=0.002,
beta_1=0.9,
beta_2=0.999,
epsilon=None,
schedule_decay=0.004,
**kwargs):
with K.name_scope(self.__class__.__name__):
self.iterations = K.variable(0, dtype='int64', name='iterations')
self.m_schedule = K.variable(1., name='m_schedule')
self.lr = K.variable(lr, name='lr')
self.beta_1 = K.variable(beta_1, name='beta_1')
self.beta_2 = K.variable(beta_2, name='beta_2')
if epsilon is None:
epsilon = K.epsilon()
self.epsilon = epsilon
self.schedule_decay = schedule_decay
...


β 1 , β 2 \beta_1, \beta_2 等参数与Adam的含义与数值都是一致的。

## 参考文献

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

更多相关内容

enum class Optimization {
SGD_Momentum, // SGD with Momentum
RMSProp, // Root Mean Square Propagation
};

void LogisticRegression2::calculate_gradient_descent(int start, int end)
{
switch (optim_) {
int len = end - start;
std::vector<float> m(feature_length_, 0.), v(feature_length_, 0.), mhat(feature_length_, 0.), vhat(feature_length_, 0.);
std::vector<float> z(len, 0.), dz(len, 0.);
float beta1t = 1., beta2t = 1.;
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

beta1t *= beta1_;
beta2t *= beta2_;

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
m[j] = beta1_ * m[j] + (1. - beta1_) * dw; // formula 19
v[j] = beta2_ * v[j] + (1. - beta2_) * (dw * dw); // formula 19

mhat[j] = m[j] / (1. - beta1t); // formula 20
vhat[j] = v[j] / (1. - beta2t); // formula 20

w_[j] = w_[j] - alpha_ * (beta1_ * mhat[j] + (1. - beta1_) * dw / (1. - beta1t)) / (std::sqrt(vhat[j]) + eps_); // formula 33
}

b_ -= (alpha_ * dz[x]);
}
}
break;
case Optimization::NAG: {
int len = end - start;
std::vector<float> v(feature_length_, 0.);
std::vector<float> z(len, 0), dz(len, 0);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z2(data_->samples[random_shuffle_[i]], v);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
v[j] = mu_ * v[j] + alpha_ * dw; // formula 5
w_[j] = w_[j] - v[j];
}

b_ -= (alpha_ * dz[x]);
}
}
break;
int len = end - start;
std::vector<float> m(feature_length_, 0.), u(feature_length_, 1e-8), mhat(feature_length_, 0.);
std::vector<float> z(len, 0.), dz(len, 0.);
float beta1t = 1.;
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

beta1t *= beta1_;

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
m[j] = beta1_ * m[j] + (1. - beta1_) * dw; // formula 19
u[j] = std::max(beta2_ * u[j], std::fabs(dw)); // formula 24

mhat[j] = m[j] / (1. - beta1t); // formula 20

// Note: need to ensure than u[j] cannot be 0.
// (1). u[j] is initialized to 1e-8, or
// (2). if u[j] is initialized to 0., then u[j] adjusts to (u[j] + 1e-8)
w_[j] = w_[j] - alpha_ * mhat[j] / u[j]; // formula 25
}

b_ -= (alpha_ * dz[x]);
}
}
break;
int len = end - start;
std::vector<float> m(feature_length_, 0.), v(feature_length_, 0.), mhat(feature_length_, 0.), vhat(feature_length_, 0.);
std::vector<float> z(len, 0.), dz(len, 0.);
float beta1t = 1., beta2t = 1.;
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

beta1t *= beta1_;
beta2t *= beta2_;

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
m[j] = beta1_ * m[j] + (1. - beta1_) * dw; // formula 19
v[j] = beta2_ * v[j] + (1. - beta2_) * (dw * dw); // formula 19

mhat[j] = m[j] / (1. - beta1t); // formula 20
vhat[j] = v[j] / (1. - beta2t); // formula 20

w_[j] = w_[j] - alpha_ * mhat[j] / (std::sqrt(vhat[j]) + eps_); // formula 21
}

b_ -= (alpha_ * dz[x]);
}
}
break;
int len = end - start;
std::vector<float> g(feature_length_, 0.), p(feature_length_, 0.);
std::vector<float> z(len, 0.), dz(len, 0.);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
g[j] = mu_ * g[j] + (1. - mu_) * (dw * dw); // formula 10

//float alpha = std::sqrt(p[j] + eps_) / std::sqrt(g[j] + eps_);
float change = -std::sqrt(p[j] + eps_) / std::sqrt(g[j] + eps_) * dw; // formula 17
w_[j] = w_[j] + change;

p[j] = mu_ * p[j] +  (1. - mu_) * (change * change); // formula 15
}

b_ -= (eps_ * dz[x]);
}
}
break;
case Optimization::RMSProp: {
int len = end - start;
std::vector<float> g(feature_length_, 0.);
std::vector<float> z(len, 0), dz(len, 0);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
g[j] = mu_ * g[j] + (1. - mu_) * (dw * dw); // formula 18
w_[j] = w_[j] - alpha_ * dw / std::sqrt(g[j] + eps_);
}

b_ -= (alpha_ * dz[x]);
}
}
break;
int len = end - start;
std::vector<float> g(feature_length_, 0.);
std::vector<float> z(len, 0), dz(len, 0);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
g[j] += dw * dw;
w_[j] = w_[j] - alpha_ * dw / std::sqrt(g[j] + eps_); // formula 8
}

b_ -= (alpha_ * dz[x]);
}
}
break;
case Optimization::SGD_Momentum: {
int len = end - start;
std::vector<float> v(feature_length_, 0.);
std::vector<float> z(len, 0), dz(len, 0);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
v[j] = mu_ * v[j] + alpha_ * dw; // formula 4
w_[j] = w_[j] - v[j];
}

b_ -= (alpha_ * dz[x]);
}
}
break;
case Optimization::SGD:
case Optimization::MBGD: {
int len = end - start;
std::vector<float> z(len, 0), dz(len, 0);
for (int i = start, x = 0; i < end; ++i, ++x) {
z[x] = calculate_z(data_->samples[random_shuffle_[i]]);
dz[x] = calculate_loss_function_derivative(calculate_activation_function(z[x]), data_->labels[random_shuffle_[i]]);

for (int j = 0; j < feature_length_; ++j) {
float dw = data_->samples[random_shuffle_[i]][j] * dz[x];
w_[j] = w_[j] - alpha_ * dw;
}

b_ -= (alpha_ * dz[x]);
}
}
break;
case Optimization::BGD:
default: // BGD
std::vector<float> z(m_, 0), dz(m_, 0);
float db = 0.;
std::vector<float> dw(feature_length_, 0.);
for (int i = 0; i < m_; ++i) {
z[i] = calculate_z(data_->samples[i]);
o_[i] = calculate_activation_function(z[i]);
dz[i] = calculate_loss_function_derivative(o_[i], data_->labels[i]);

for (int j = 0; j < feature_length_; ++j) {
dw[j] += data_->samples[i][j] * dz[i]; // dw(i)+=x(i)(j)*dz(i)
}
db += dz[i]; // db+=dz(i)
}

for (int j = 0; j < feature_length_; ++j) {
dw[j] /= m_;
w_[j] -= alpha_ * dw[j];
}

b_ -= alpha_*(db/m_);
}
}

展开全文

作者丨ycszen

来源｜https://zhuanlan.zhihu.com/p/22252270

编辑丨极市平台

导读

本文仅对一些常见的优化方法进行直观介绍和简单的比较。

# 前言

本文仅对一些常见的优化方法进行直观介绍和简单的比较，各种优化方法的详细内容及公式只好去认真啃论文了，在此我就不赘述了。

# SGD

SGD就是每一次迭代计算mini-batch的梯度，然后对参数进行更新，是最常见的优化方法了。即：

其中，是学习率，是梯度 SGD完全依赖于当前batch的梯度，所以 可理解为允许当前batch的梯度多大程度影响参数更新

缺点：（正因为有这些缺点才让这么多大神发展出了后续的各种算法）

选择合适的learning rate比较困难 - 对所有的参数更新使用同样的learning rate。对于稀疏数据或者特征，有时我们可能想更新快一些对于不经常出现的特征，对于常出现的特征更新慢一些，这时候SGD就不太能满足要求了

SGD容易收敛到局部最优，并且在某些情况下可能被困在鞍点【原来写的是“容易困于鞍点”，经查阅论文发现，其实在合适的初始化和step size的情况下，鞍点的影响并没这么大。感谢@冰橙的指正】

# Momentum

momentum是模拟物理里动量的概念，积累之前的动量来替代真正的梯度。公式如下：

其中，是动量因子

特点:

• 下降初期时，使用上一次参数更新，下降方向一致，乘上较大的能够进行很好的加速

• 下降中后期时，在局部最小值来回震荡的时候，， 使得更新幅度增大，跳出陷阱

• 在梯度改变方向的时候，能够减少更新 总而言之，momentum项能够在相关方向加速SGD，抑制振荡，从而加快收敛

# Nesterov

nesterov项在梯度更新时做一个校正，避免前进太快，同时提高灵敏度。将上一节中的公式展开可得：

可以看出， 并没有直接改变当前梯度，所以Nesterov的改进就是让之前的动量直接影响当前的动量。即：

所以，加上nesterov项后，梯度在大的跳跃后，进行计算对当前梯度进行校正。如下图：

momentum首先计算一个梯度(短的蓝色向量)，然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量)，nesterov项首先在之前加速的梯度方向进行一个大的跳跃(棕色向量)，计算梯度然后进行校正(绿色梯向量)

其实，momentum项和nesterov项都是为了使梯度更新更加灵活，对不同情况有针对性。但是，人工设置一些学习率总还是有些生硬，接下来介绍几种自适应学习率的方法

此处，对从1到进行一个递推形成一个约束项regularizer，  ， e用来保证分母非0

特点

• 前期较小的时候， regularizer较大，能够放大梯度

• 后期较大的时候，regularizer较小，能够约束梯度

• 适合处理稀疏梯度

缺点

由公式可以看出，仍依赖于人工设置一个全局学习率

设置过大的话，会使regularizer过于敏感，对梯度的调节太大

中后期，分母上梯度平方的累加将会越来越大，使，使得训练提前结束

其中，代表求期望。

特点

• 训练初中期，加速效果不错，很快

• 训练后期，反复在局部最小值附近抖动

# RMSprop

时，就变为了求梯度平方和的平均数。

如果再求根的话，就变成了RMS(均方根)：

此时，这个RMS就可以作为学习率的一个约束：

特点

• 其实RMSprop依然依赖于全局学习率

• 适合处理非平稳目标 - 对于RNN效果很好

其中，分别是对梯度的一阶矩估计和二阶矩估计，可以看作对期望的估计；是对  的校正，这样可以近似为对期望的无偏估计。可以看出，直接对梯度的矩估计对内存没有额外的要求，而且可以根据梯度进行动态调整，而 对学习率形成一个动态约束，而且有明确的范围。

特点

• 对内存需求较小

• 为不同的参数计算不同的自适应学习率

• 也适用于大多非凸优化 - 适用于大数据集和高维空间

# 经验之谈

最后展示两张可厉害的图，一切尽在图中啊，上面的都没啥用了... ...

损失平面等高线

在鞍点处的比较

# 引用：

[2]RMSprop[Lecture 6e]

（http://www.cs.toronto.edu/~tijmen/csc321/lecture_notes.shtml）

[6]On the importance of initialization and momentum in deep learning

（http://www.cs.toronto.edu/~fritz/absps/momentum.pdf）

[9]An overview of gradient descent optimization algorithms

[10]Gradient Descent Only Converges to Minimizers

（http://www.jmlr.org/proceedings/papers/v49/lee16.pdf）

[11]Deep Learning:Nature

（http://www.nature.com/nature/journal/v521/n7553/abs/nature14539.html）

本文仅做学术分享，如有侵权，请联系删文。

重磅！计算机视觉工坊-学习交流群已成立

扫码添加小助手微信，可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群，旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群，目前主要有ORB-SLAM系列源码学习、3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、深度估计、学术交流、求职交流等微信群，请扫描下面微信号加群，备注：”研究方向+学校/公司+昵称“，例如：”3D视觉 + 上海交大 + 静静“。请按照格式备注，否则不予通过。添加成功后会根据研究方向邀请进去相关微信群。原创投稿也请联系。

▲长按加微信群或投稿

▲长按关注公众号

3D视觉从入门到精通知识星球：针对3D视觉领域的视频课程（三维重建系列三维点云系列结构光系列手眼标定相机标定、激光/视觉SLAM、自动驾驶等）、知识点汇总、入门进阶学习路线、最新paper分享、疑问解答五个方面进行深耕，更有各类大厂的算法工程人员进行技术指导。与此同时，星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息，打造成集技术与就业为一体的铁杆粉丝聚集区，近4000星球成员为创造更好的AI世界共同进步，知识星球入口：

学习3D视觉核心技术，扫描查看介绍，3天内无条件退款

圈里有高质量教程资料、可答疑解惑、助你高效解决问题

觉得有用，麻烦给个赞和在看~

展开全文
• 【翻译自 ： Gradient Descent Optimization With Nadam From Scratch】 【说明：Jason BrownleePhD大神的文章个人很喜欢，所以闲暇时间里会做一点翻译和学习实践的工作，这里是相应工作的实践记录，希望能帮到有...

【说明：Jason Brownlee PhD大神的文章个人很喜欢，所以闲暇时间里会做一点翻译和学习实践的工作，这里是相应工作的实践记录，希望能帮到有需要的人！】

梯度下降是一种优化算法，遵循目标函数的负梯度以定位函数的最小值。

梯度下降的局限性在于，如果梯度变为平坦或大曲率，搜索的进度可能会减慢。可以将动量添加到梯度下降中，该下降合并了一些惯性以进行更新。可以通过合并预计的新位置而非当前位置的梯度（称为Nesterov的加速梯度（NAG）或Nesterov动量）来进一步改善此效果。

梯度下降是一种优化算法，它使用目标函数的梯度来导航搜索空间。
如何从头开始实现Nadam优化算法并将其应用于目标函数并评估结果。

# 教程概述

本教程分为三个部分： 他们是：

梯度下降
二维测试问题
可视化的Nadam优化

## 梯度下降

梯度下降是一种优化算法。它在技术上称为一阶优化算法，因为它明确利用了目标目标函数的一阶导数。

一阶导数，或简称为“导数”，是目标函数在特定点（例如，点）上的变化率或斜率。用于特定输入。

如果目标函数采用多个输入变量，则将其称为多元函数，并且可以将输入变量视为向量。反过来，多元目标函数的导数也可以视为向量，通常称为梯度。

梯度：多元目标函数的一阶导数。
对于特定输入，导数或梯度指向目标函数最陡峭的上升方向。梯度下降是指一种最小化优化算法，该算法遵循目标函数的下坡梯度负值来定位函数的最小值。

梯度下降算法需要一个正在优化的目标函数和该目标函数的导数函数。目标函数f（）返回给定输入集合的分数，导数函数f'（）给出给定输入集合的目标函数的导数。梯度下降算法需要问题中的起点（x），例如输入空间中的随机选择点。

假设我们正在最小化目标函数，然后计算导数并在输入空间中采取一步，这将导致目标函数下坡运动。首先通过计算输入空间中要移动多远的距离来进行下坡运动，计算方法是将步长（称为alpha或学习率）乘以梯度。然后从当前点减去该值，以确保我们逆梯度移动或向下移动目标函数。

x（t）= x（t-1）–step* f'（x（t））

在给定点的目标函数越陡峭，梯度的大小越大，反过来，在搜索空间中采取的步伐也越大。使用步长超参数来缩放步长的大小。

步长：超参数，用于控制算法每次迭代相对于梯度在搜索空间中移动多远。
如果步长太小，则搜索空间中的移动将很小，并且搜索将花费很长时间。如果步长太大，则搜索可能会在搜索空间附近反弹并跳过最优值。

m = 0
n = 0
该算法在从t = 1开始的时间t内迭代执行，并且每次迭代都涉及计算一组新的参数值x，例如。从x（t-1）到x（t）。如果我们专注于更新一个参数，这可能很容易理解该算法，该算法概括为通过矢量运算来更新所有参数。首先，计算当前时间步长的梯度（偏导数）。

g（t）= f'（x（t-1））

接下来，使用梯度和超参数“ mu”更新第一时刻。

m（t）=mu* m（t-1）+（1 –mu）* g（t）

然后使用“ nu”超参数更新第二时刻。

n（t）= nu * n（t-1）+（1 – nu）* ​​g（t）^ 2

接下来，使用Nesterov动量对第一时刻进行偏差校正。

mhat =（mu * m（t）/（1 – mu））+（（1 – mu）* g（t）/（1 – mu））

nhat = nu * n（t）/（1 – nu）

最后，我们可以为该迭代计算参数的值。

x（t）= x（t-1）– alpha /（sqrt（nhat）+ eps）* mhat

其中alpha是步长（学习率）超参数，sqrt（）是平方根函数，eps（epsilon）是一个较小的值，如1e-8，以避免除以零误差。

回顾一下，该算法有三个超参数。他们是：

alpha：初始步长（学习率），典型值为0.002。
nu：第二时刻的衰减因子（Adam中的beta2），典型值为0.999。

就是这样。接下来，让我们看看如何在Python中从头开始实现该算法。

### 二维测试问题

首先，让我们定义一个优化函数。我们将使用一个简单的二维函数，该函数将每个维的输入平方，并定义有效输入的范围（从-1.0到1.0）。下面的Objective（）函数实现了此功能

# objective function
def objective(x, y):
return x**2.0 + y**2.0

我们可以创建数据集的三维图，以了解响应面的曲率。下面列出了绘制目标函数的完整示例。

# 3d plot of the test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot

# objective function
def objective(x, y):
return x**2.0 + y**2.0

# define range for input
r_min, r_max = -1.0, 1.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行示例将创建目标函数的三维表面图。我们可以看到全局最小值为f（0，0）= 0的熟悉的碗形状。

我们还可以创建函数的二维图。 这在以后要绘制搜索进度时会很有帮助。下面的示例创建目标函数的轮廓图。

# contour plot of the test function
from numpy import asarray
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot

# objective function
def objective(x, y):
return x**2.0 + y**2.0

# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')
# show the plot
pyplot.show()

运行示例将创建目标函数的二维轮廓图。我们可以看到碗的形状被压缩为以颜色渐变显示的轮廓。 我们将使用该图来绘制在搜索过程中探索的特定点。

x ^ 2的导数在每个维度上均为x * 2。

f（x）= x ^ 2
f'（x）= x * 2

derived（）函数在下面实现了这一点。

# derivative of objective function
def derivative(x, y):
return asarray([x * 2.0, y * 2.0])

# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])

接下来，我们需要初始化力矩矢量。

# initialize decaying moving averages
m = [0.0 for _ in range(bounds.shape[0])]
n = [0.0 for _ in range(bounds.shape[0])]

然后，我们运行由“ n_iter”超参数定义的算法的固定迭代次数。

...
# run iterations of gradient descent
for t in range(n_iter):
...

第一步是计算当前参数集的导数。

...
g = derivative(x[0], x[1])

...
# build a solution one variable at a time
for i in range(x.shape[0]):
...

首先，我们需要计算力矩矢量。

# m(t) = mu * m(t-1) + (1 - mu) * g(t)
m[i] = mu * m[i] + (1.0 - mu) * g[i]

然后是第二个矩向量。

# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)
# n(t) = nu * n(t-1) + (1 - nu) * g(t)^2
n[i] = nu * n[i] + (1.0 - nu) * g[i]**2

然后是经过偏差校正的内斯特罗夫动量。

# mhat = (mu * m(t) / (1 - mu)) + ((1 - mu) * g(t) / (1 - mu))
mhat = (mu * m[i] / (1.0 - mu)) + ((1 - mu) * g[i] / (1.0 - mu))

偏差校正的第二时刻。

# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)

最后更新参数。

# x(t) = x(t-1) - alpha / (sqrt(nhat) + eps) * mhat
x[i] = x[i] - alpha / (sqrt(nhat) + eps) * mhat

然后，针对要优化的每个参数重复此操作。在迭代结束时，我们可以评估新的参数值并报告搜索的性能。

# evaluate candidate point
score = objective(x[0], x[1])
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))

# gradient descent algorithm with nadam
def nadam(objective, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8):
# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])
# initialize decaying moving averages
m = [0.0 for _ in range(bounds.shape[0])]
n = [0.0 for _ in range(bounds.shape[0])]
for t in range(n_iter):
g = derivative(x[0], x[1])
# build a solution one variable at a time
for i in range(bounds.shape[0]):
# m(t) = mu * m(t-1) + (1 - mu) * g(t)
m[i] = mu * m[i] + (1.0 - mu) * g[i]
# n(t) = nu * n(t-1) + (1 - nu) * g(t)^2
n[i] = nu * n[i] + (1.0 - nu) * g[i]**2
# mhat = (mu * m(t) / (1 - mu)) + ((1 - mu) * g(t) / (1 - mu))
mhat = (mu * m[i] / (1.0 - mu)) + ((1 - mu) * g[i] / (1.0 - mu))
# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)
# x(t) = x(t-1) - alpha / (sqrt(nhat) + eps) * mhat
x[i] = x[i] - alpha / (sqrt(nhat) + eps) * mhat
# evaluate candidate point
score = objective(x[0], x[1])
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))
return [x, score]

然后，我们可以定义函数和超参数的界限，并调用函数执行优化。在这种情况下，我们将运行该算法进行50次迭代，初始alpha为0.02，μ为0.8，nu为0.999，这是经过一点点反复试验后发现的。

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 50
# steps size
alpha = 0.02
mu = 0.8
# factor for average squared gradient
nu = 0.999
best, score = nadam(objective, derivative, bounds, n_iter, alpha, mu, nu)

运行结束时，我们将报告找到的最佳解决方案。

# summarize the result
print('Done!')
print('f(%s) = %f' % (best, score))

# gradient descent optimization with nadam for a two-dimensional test function
from math import sqrt
from numpy import asarray
from numpy.random import rand
from numpy.random import seed

# objective function
def objective(x, y):
return x**2.0 + y**2.0

# derivative of objective function
def derivative(x, y):
return asarray([x * 2.0, y * 2.0])

def nadam(objective, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8):
# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])
# initialize decaying moving averages
m = [0.0 for _ in range(bounds.shape[0])]
n = [0.0 for _ in range(bounds.shape[0])]
for t in range(n_iter):
g = derivative(x[0], x[1])
# build a solution one variable at a time
for i in range(bounds.shape[0]):
# m(t) = mu * m(t-1) + (1 - mu) * g(t)
m[i] = mu * m[i] + (1.0 - mu) * g[i]
# n(t) = nu * n(t-1) + (1 - nu) * g(t)^2
n[i] = nu * n[i] + (1.0 - nu) * g[i]**2
# mhat = (mu * m(t) / (1 - mu)) + ((1 - mu) * g(t) / (1 - mu))
mhat = (mu * m[i] / (1.0 - mu)) + ((1 - mu) * g[i] / (1.0 - mu))
# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)
# x(t) = x(t-1) - alpha / (sqrt(nhat) + eps) * mhat
x[i] = x[i] - alpha / (sqrt(nhat) + eps) * mhat
# evaluate candidate point
score = objective(x[0], x[1])
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))
return [x, score]

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 50
# steps size
alpha = 0.02
mu = 0.8
# factor for average squared gradient
nu = 0.999
best, score = nadam(objective, derivative, bounds, n_iter, alpha, mu, nu)
print('Done!')
print('f(%s) = %f' % (best, score))

注意：由于算法或评估程序的随机性，或者数值精度的差异，您的结果可能会有所不同。 考虑运行该示例几次并比较平均结果。

在这种情况下，我们可以看到在大约44次搜索迭代后找到了接近最佳的解决方案，输入值接近0.0和0.0，评估为0.0。

>40 f([ 5.07445337e-05 -3.32910019e-03]) = 0.00001
>41 f([-1.84325171e-05 -3.00939427e-03]) = 0.00001
>42 f([-6.78814472e-05 -2.69839367e-03]) = 0.00001
>43 f([-9.88339249e-05 -2.40042096e-03]) = 0.00001
>44 f([-0.00011368 -0.00211861]) = 0.00000
>45 f([-0.00011547 -0.00185511]) = 0.00000
>46 f([-0.0001075 -0.00161122]) = 0.00000
>47 f([-9.29922627e-05 -1.38760991e-03]) = 0.00000
>48 f([-7.48258406e-05 -1.18436586e-03]) = 0.00000
>49 f([-5.54299505e-05 -1.00116899e-03]) = 0.00000
Done!
f([-5.54299505e-05 -1.00116899e-03]) = 0.000001

# gradient descent algorithm with nadam
def nadam(objective, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8):
solutions = list()
# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])
# initialize decaying moving averages
m = [0.0 for _ in range(bounds.shape[0])]
n = [0.0 for _ in range(bounds.shape[0])]
for t in range(n_iter):
g = derivative(x[0], x[1])
# build a solution one variable at a time
for i in range(bounds.shape[0]):
# m(t) = mu * m(t-1) + (1 - mu) * g(t)
m[i] = mu * m[i] + (1.0 - mu) * g[i]
# n(t) = nu * n(t-1) + (1 - nu) * g(t)^2
n[i] = nu * n[i] + (1.0 - nu) * g[i]**2
# mhat = (mu * m(t) / (1 - mu)) + ((1 - mu) * g(t) / (1 - mu))
mhat = (mu * m[i] / (1.0 - mu)) + ((1 - mu) * g[i] / (1.0 - mu))
# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)
# x(t) = x(t-1) - alpha / (sqrt(nhat) + eps) * mhat
x[i] = x[i] - alpha / (sqrt(nhat) + eps) * mhat
# evaluate candidate point
score = objective(x[0], x[1])
# store solution
solutions.append(x.copy())
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))
return solutions

然后，我们可以像以前一样执行搜索，这一次将检索解决方案列表，而不是最佳的最终解决方案。

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 50
# steps size
alpha = 0.02
mu = 0.8
# factor for average squared gradient
nu = 0.999
solutions = nadam(objective, derivative, bounds, n_iter, alpha, mu, nu)

然后，我们可以像以前一样创建目标函数的轮廓图。

# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')

最后，我们可以将在搜索过程中找到的每个解决方案绘制成一条由一条线连接的白点。

# plot the sample as black circles
solutions = asarray(solutions)
pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')

# example of plotting the nadam search on a contour plot of the test function
from math import sqrt
from numpy import asarray
from numpy import arange
from numpy import product
from numpy.random import rand
from numpy.random import seed
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
return x**2.0 + y**2.0

# derivative of objective function
def derivative(x, y):
return asarray([x * 2.0, y * 2.0])

def nadam(objective, derivative, bounds, n_iter, alpha, mu, nu, eps=1e-8):
solutions = list()
# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])
# initialize decaying moving averages
m = [0.0 for _ in range(bounds.shape[0])]
n = [0.0 for _ in range(bounds.shape[0])]
for t in range(n_iter):
g = derivative(x[0], x[1])
# build a solution one variable at a time
for i in range(bounds.shape[0]):
# m(t) = mu * m(t-1) + (1 - mu) * g(t)
m[i] = mu * m[i] + (1.0 - mu) * g[i]
# n(t) = nu * n(t-1) + (1 - nu) * g(t)^2
n[i] = nu * n[i] + (1.0 - nu) * g[i]**2
# mhat = (mu * m(t) / (1 - mu)) + ((1 - mu) * g(t) / (1 - mu))
mhat = (mu * m[i] / (1.0 - mu)) + ((1 - mu) * g[i] / (1.0 - mu))
# nhat = nu * n(t) / (1 - nu)
nhat = nu * n[i] / (1.0 - nu)
# x(t) = x(t-1) - alpha / (sqrt(nhat) + eps) * mhat
x[i] = x[i] - alpha / (sqrt(nhat) + eps) * mhat
# evaluate candidate point
score = objective(x[0], x[1])
# store solution
solutions.append(x.copy())
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))
return solutions

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 50
# steps size
alpha = 0.02
mu = 0.8
# factor for average squared gradient
nu = 0.999
solutions = nadam(objective, derivative, bounds, n_iter, alpha, mu, nu)
# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')
# plot the sample as black circles
solutions = asarray(solutions)
pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')
# show the plot
pyplot.show()

运行示例将像以前一样执行搜索，但是在这种情况下，将创建目标函数的轮廓图。

在这种情况下，我们可以看到在搜索过程中找到的每个解决方案都显示一个白点，从最优点开始，逐渐靠近图中心的最优点。

展开全文
• 首先，Adam优化器的权重更新公式也可以写成： Nadam使用Nesterov通过将上式中的 m t − 1 ^ \hat{m_{t-1}} mt−1​^​ 替换为当前的 m t ^ \hat{m_t} mt​^​ 来提前更新梯度： 其中， m m m 和 v v v 初始化为 0 0...
• 一、准备知识 指数加权平均 指数加权平均值又称指数加权移动平均值，局部平均值，移动平均值。加权平均这个概念都很熟悉，即根据各个元素所占权重计算平均值。指数加权平均中的指数表示各个元素所占权重呈指数分布。...

千次阅读 2020-08-26 19:38:19
https://blog.csdn.net/qq_42109740/article/details/105401197
• 参考博客： ... ... ...标记成原创是因为加入了很多整理，内容并非原创，写在前面，希望后人不要误解，一切解释归参考博客中的博文所有 ...当然，实际情况下的loss损失函数可能更为复杂，比如鞍面，这样我们使用...10-NAdam

...