简要介绍图中的优化算法,编程实现并2D可视化

 

目录

1. 被优化函数 ​编辑

2. 被优化函数 ​编辑 

3. 解释不同轨迹的形成原因

        算法介绍

分析各个算法的优缺点


1. 被优化函数 x^{2}

 能自适应调整学习率:AdaGrade、RMSprop

能进行梯度估计修正:动量法、Nesterov加速梯度法

综合学习率调整和梯度估计修正:Adam算法

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict


class SGD:
    """随机梯度下降法(Stochastic Gradient Descent)"""

    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]


class Momentum:
    """Momentum SGD"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
            params[key] += self.v[key]


class Nesterov:
    """Nesterov's Accelerated Gradient (http://arxiv.org/abs/1212.0901)"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key]
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]


class AdaGrad:
    """AdaGrad"""

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


class RMSprop:
    """RMSprop"""

    def __init__(self, lr=0.01, decay_rate=0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)


class Adam:
    """Adam (http://arxiv.org/abs/1412.6980v8)"""

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)

        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)


# def f(x, y):
#     return x ** 2 / 20.0 + y ** 2
def f(x,_):
    return x**2

# def df(x, y):
#     return x / 10.0, 2.0 * y
def df(x,_):
    return 2*x,0

# init_pos = (-7.0, 2.0)
init_pos=(-7.0,)
params = {}
# params['x'], params['y'] = init_pos[0], init_pos[1]
params['x']=init_pos[0]
grads = {}
# grads['x'], grads['y'] = 0, 0
grads['x']=0
learningrate = [0.9, 0.3, 0.3, 0.6, 0.3, 0.6, 0.6]
optimizers = OrderedDict()
optimizers["SGD"] = SGD(lr=learningrate[0])
optimizers["Momentum"] = Momentum(lr=learningrate[1])
optimizers["Nesterov"] = Nesterov(lr=learningrate[2])
optimizers["AdaGrad"] = AdaGrad(lr=learningrate[3])
optimizers["RMSprop"] = RMSprop(lr=learningrate[4])
optimizers["Adam"] = Adam(lr=learningrate[5])

idx = 1
id_lr = 0

for key in optimizers:
    optimizer = optimizers[key]
    lr = learningrate[id_lr]
    id_lr = id_lr + 1
    x_history = []
    y_history = []
    # params['x'], params['y'] = init_pos[0], init_pos[1]
    params['x']=init_pos[0]
    for i in range(30):
        x_history.append(params['x'])
        # y_history.append(params['y'])
        y_history.append(0)

        # grads['x'], grads['y'] = df(params['x'], params['y'])
        grads['x'],_=df(params['x'],0)
        optimizer.update(params, grads)

    x = np.arange(-10, 10, 0.01)
    y = np.arange(-5, 5, 0.01)

    X, Y = np.meshgrid(x, y)
    Z = f(X, Y)
    # for simple contour line
    mask = Z > 7
    Z[mask] = 0

    # plot
    plt.subplot(2, 3, idx)
    idx += 1
    plt.plot(x_history, y_history, 'o-', color="r")
    # plt.contour(X, Y, Z)  # 绘制等高线
    plt.contour(X, Y, Z, cmap='gray')  # 颜色填充
    plt.ylim(-10, 10)
    plt.xlim(-10, 10)
    plt.plot(0, 0, '+')
    # plt.axis('off')
    # plt.title(key+'\nlr='+str(lr), fontstyle='italic')
    plt.text(0, 10, key + '\nlr=' + str(lr), fontsize=20, color="b",
             verticalalignment='top', horizontalalignment='center', fontstyle='italic')
    plt.xlabel("x")
    plt.ylabel("y")

plt.subplots_adjust(wspace=0, hspace=0)  # 调整子图间距
plt.show()

 结果:

2. 被优化函数 x^{2}/20+y^{2} 

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict


class SGD:
    """随机梯度下降法(Stochastic Gradient Descent)"""

    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]


class Momentum:
    """Momentum SGD"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum   #控制之前的速度对当前梯度的影响程度。(动量)
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
            params[key] += self.v[key]


class Nesterov:
    """Nesterov's Accelerated Gradient (http://arxiv.org/abs/1212.0901)"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] *= self.momentum
            self.v[key] -= self.lr * grads[key] #计算动量修正
            params[key] += self.momentum * self.momentum * self.v[key]
            params[key] -= (1 + self.momentum) * self.lr * grads[key]       #更新


class AdaGrad:
    """AdaGrad"""

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] += grads[key] * grads[key]  #累积历史梯度平方和
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7) #更新参数,括号里的就是自适应调整学习率的过程


class RMSprop:
    """RMSprop"""

    def __init__(self, lr=0.01, decay_rate=0.9):
        self.lr = lr
        self.decay_rate = decay_rate    #平滑指数加权移动平均的超参数 常取0.9
        self.h = None

    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)

        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]  #计算梯度的平方的指数加权移动平均值
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)#更新。1e-7是为了稳定添加的非常小的常数


class Adam:
    """Adam (http://arxiv.org/abs/1412.6980v8)"""

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None

    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)

        self.iter += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter)

        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key])

            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)


def f(x, y):
    return x ** 2 / 20.0 + y ** 2
# def f(x,_):
#     return x**2

def df(x, y):
    return x / 10.0, 2.0 * y
# def df(x,_):
#     return 2*x,0

init_pos = (-7.0, 2.0)
# init_pos=(-7.0,)
params = {}
params['x'], params['y'] = init_pos[0], init_pos[1]
# params['x']=init_pos[0]
grads = {}
grads['x'], grads['y'] = 0, 0
# grads['x']=0
learningrate = [0.9, 0.1, 0.2, 0.6, 0.6, 0.3, 0.6]
optimizers = OrderedDict()
optimizers["SGD"] = SGD(lr=learningrate[0])
optimizers["Momentum"] = Momentum(lr=learningrate[1])
optimizers["Nesterov"] = Nesterov(lr=learningrate[2])
optimizers["AdaGrad"] = AdaGrad(lr=learningrate[3])
optimizers["RMSprop"] = RMSprop(lr=learningrate[4])
optimizers["Adam"] = Adam(lr=learningrate[5])

idx = 1
id_lr = 0

for key in optimizers:
    optimizer = optimizers[key]
    lr = learningrate[id_lr]
    id_lr = id_lr + 1
    x_history = []
    y_history = []
    params['x'], params['y'] = init_pos[0], init_pos[1]
    # params['x']=init_pos[0]
    for i in range(30):
        x_history.append(params['x'])
        y_history.append(params['y'])
        # y_history.append(0)

        grads['x'], grads['y'] = df(params['x'], params['y'])
        # grads['x'],_=df(params['x'],0)
        optimizer.update(params, grads)

    x = np.arange(-10, 10, 0.01)
    y = np.arange(-5, 5, 0.01)

    X, Y = np.meshgrid(x, y)
    Z = f(X, Y)
    # for simple contour line
    mask = Z > 7
    Z[mask] = 0

    # plot
    plt.subplot(2, 3, idx)
    idx += 1
    plt.plot(x_history, y_history, 'o-', color="r")
    # plt.contour(X, Y, Z)  # 绘制等高线
    plt.contour(X, Y, Z, cmap='gray')  # 颜色填充
    plt.ylim(-10, 10)
    plt.xlim(-10, 10)
    plt.plot(0, 0, '+')
    # plt.axis('off')
    # plt.title(key+'\nlr='+str(lr), fontstyle='italic')
    plt.text(0, 10, key + '\nlr=' + str(lr), fontsize=20, color="b",
             verticalalignment='top', horizontalalignment='center', fontstyle='italic')
    plt.xlabel("x")
    plt.ylabel("y")

plt.subplots_adjust(wspace=0, hspace=0)  # 调整子图间距
plt.show()

结果: 

3. 解释不同轨迹的形成原因
        算法介绍

SGD:每次迭代并不是沿着梯度最大的方向下降的,而是根据每个样本梯度进行更新,因为每个样本的梯度的差异,就会导致轨迹出现波动。另外,如果学习率设置过大,也会导致参数在范围内波动,形成这种轨迹。

Momentum:在更新参数时,不仅考虑当前梯度信息,还考虑了之前的更新方向和大小。给梯度一动量的概念,使得参数更新更加平滑,并且具有方向性。当函数沿着一个方向,连续下降,这种算法还能够加速收敛。(通过累积之前的速度信息,使得参数更新具有惯性,可以跳出局部最优解,并且在梯度方向上形成一种平滑的轨迹。这种轨迹有助于加速收敛。)

Nestrov:是上一种算法的改进。先根据当前位置加上动量项的方向进行预测,然后计算该位置的梯度并使用这个梯度来调整预测位置的方向。

Adagrad:其学习率会根据参数的历史梯度平方和进行调整。随着梯度更新,累积梯度的平方和将不断增加,导致学习率逐渐减小。这意味着在训练早期,学习率较大,可以更快地更新参数;而随着训练进行,由于梯度平方和的累积,学习率逐渐减小,参数更新的步长也会减小,导致算法在接近最优解时更为稳定。但是很显然,梯度累积,导致学习率过小,最后还没到最优值,参数更新就几乎停滞了,导致无法到最优点。

RMSprop:对AdaGrad算法的改进,解决了AdaGrad算法学习率过早衰减的问题。RMSprop使用梯度平方的指数加权移动平均来计算历史梯度的平方和,而不是简单地累加所有历史梯度的平方。使得学习率不会急剧衰减严重。

Adam:结合了动量法和自适应学习率的优化算法,常用于训练神经网络等深度学习模型。

分析各个算法的优缺点
  1. 随机梯度下降法(SGD)

    • 优点:
      • 实现简单,易于理解和实施。
      • 可用于大规模数据集和高维度模型。
    • 缺点:
      • 可能陷入局部最优点,尤其在目标函数非凸的情况下。
      • 学习率的设置,过大的学习率可能导致不稳定或跳出最优点。
      • 越到最后,算法的收敛速度就越慢。
  2. Momentum SGD

    • 优点:
      • 增加了动量项,有助于加速收敛。
      • 能够跳出局部极小值。
    • 缺点:
      • 需要调整额外的超参数(动量参数)。
      • 可能导致参数震荡。
  3. Nesterov加速梯度

    • 优点:
      • 对于动量法,有更好的收敛性和参数更新。
    • 缺点:
      • 对于高维度问题,需要更多的计算资源。
      • 需要调整额外的超参数。
  4. AdaGrad

    • 优点:
      • 自适应学习率,每个参数的学习率会随时间减小。
    • 缺点:
      • 学习率衰减过快,可能在训练后期导致学习速度过慢。
      • 累积梯度的平方可能导致学习率过小,难以收敛。
  5. RMSprop

    • 优点:
      • 解决了 AdaGrad 学习率下降过快的问题,引入了衰减系数,调整学习率。
    • 缺点:
      • 仍可能需要手动调整超参数(衰减系数,常取0.9)。
      • 有时可能收敛速度较慢。
  6. Adam

    • 优点:
      • 结合了动量法和 RMSprop 的优点,具有较好的性能。
      • 自适应调整学习率和动量。
    • 缺点:
      • 需要调整额外的超参数如学习率、动量系数。

链接:

【NNDL 作业】优化算法比较 增加 RMSprop、Nesterov

【23-24 秋学期】NNDL 作业12 优化算法2D可视化

深度学习中的Momentum算法原理

  • 13
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值