SGD和带momentum的SGD算法

参考:
https://crazyang.blog.csdn.net/article/details/84618536

1、关于SGD算法:

随机梯度下降算法的出现是因为,BGD的迭代速度在大数据量下会变得很慢,因为它每次迭代都是用的是所有的数据样本。而SGD一次迭代只需要使用一个样本。可以根据这一个样本来计算梯度。

# 随机梯度下降SGD
# 拟合函数为:y = theta * x
# 代价函数为:J = 1 / (2 * m) * ((theta * x) - y) * ((theta * x) - y).T;
# 梯度迭代为: theta = theta - alpha / m * (x * (theta * x - y).T);
# 以 y=2*x1+9*x2为例

import numpy as np
import math

def sgd():
    # 训练集,每个样本有2个分量
    x = np.array([(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2), (-1, -2), (-2, -1)])
    y = np.array([11, 20, 13, 22, 15, 24, -20, -13])

    # 初始化
    m, dim = x.shape
    theta = np.zeros(dim)  # 参数
    alpha = 0.01  # 学习率
    threshold = 0.0001  # 停止迭代的错误阈值
    iterations = 1500  # 迭代次数
    error = 0  # 初始错误为0

    # 迭代开始
    for i in range(iterations):

        error = 1 / (2 * m) * np.dot((np.dot(x, theta) - y).T, (np.dot(x, theta) - y))
        # 迭代停止
        if abs(error) <= threshold:
            break
        
        j = np.random.randint(0, m)

        theta -= alpha * (x[j] * (np.dot(x[j], theta) - y[j]))

    print('迭代次数:%d' % (i + 1), 'theta:', theta, 'error:%f' % error)


if __name__ == '__main__':
    sgd()

迭代次数:1278 theta: [2.01294926 8.98333428] error:0.000100

2、带动量的随机梯度下降Momentum-SGD

SGD在随机挑选某一分量的梯度方向进行收敛,加一个“动量”的话,相当于有了一个惯性在里面。当我们人在运动时,向前跑由于有惯性,故后期跑起来会比刚开始起步时轻松,另外当你想要往反方向时,会降低你的返回速度。故,在SGD的基础上加一个动量,如果当前收敛效果好,就可以加速收敛,如果不好,则减慢它的步伐。


用更科学的话来讲是:如果本次和上次的梯度符号是相同的,那么就能够加速下降(幅度变大),就能够解决原先下降太慢的问题;如果本次和上次的梯度符号是相反的,那么这次就和上次相互抑制,减缓震荡。由于有动量的作用,在局部最优点时,它可以借助动量跳出来,不易陷入局部最优点。

 带动量的随机梯度下降SGD
def momentum_sgd():
    # 训练集,每个样本有三个分量
    x = np.array([(1, 1), (1, 2), (2, 1), (2, 2), (3, 1), (3, 2), (-1, -2), (-2, -1)])
    y = np.array([11, 20, 13, 22, 15, 24, -20, -13])

    # 初始化
    m, dim = x.shape
    theta = np.zeros(dim)  # 参数
    alpha = 0.01  # 学习率
    momentum = 0.1  # 动量
    threshold = 0.0001  # 停止迭代的错误阈值
    iterations = 1500  # 迭代次数
    error = 0  # 初始错误为0
    gradient = 0  # 初始梯度为0

    # 迭代开始
    for i in range(iterations):
        j = i % m
        error = 1 / (2 * m) * np.dot((np.dot(x, theta) - y).T,
                                     (np.dot(x, theta) - y))
        # 迭代停止
        if abs(error) <= threshold:
            break

        gradient = momentum * gradient + alpha * (x[j] *
                                                  (np.dot(x[j], theta) - y[j]))
        theta -= gradient

    print('迭代次数:%d' % (i + 1), 'theta:', theta, 'error:%f' % error)


if __name__ == '__main__':
    momentum_sgd()

迭代次数:1104 theta: [2.01263177 8.98305501] error:0.000099
相比于SGD的1278的迭代次数,增加momentum=0.1后的SGD的迭代速度加快了。预测的准确度二者是差不多的。
将momentum=0.1改为0.3后,我们发现,函数的迭代速度又增加了,且准确率依然能够保持。继续调整参数:

  • 我将momentum设为0.7后得到的结果是:迭代次数:352 theta: [2.01228743 8.98298099] error:0.000098
  • momentum=0.8:迭代次数:220 theta: [2.01265289 8.98319982] error:0.000098
  • momentum=0.9:迭代次数:117 theta: [2.00238961 9.0050505 ] error:0.000077

故,我得到:在momentum=0.9时得到的结果是最好的。此时迭代速度提高了快10倍,并且theta的准确率更高。

3、数学推导:


对于没有momentum的SGD,计算梯度并更新参数的公式如下: g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i ; θ ) , y i ) g = \frac 1m\nabla\theta\sum_{i=1}^m L\bigl(f(x_i;\theta),y_i\bigr) g=m1θi=1mL(f(xi;θ),yi)
θ = θ − δ g \theta=\theta-\delta g θ=θδg
每次梯度更新的时候都会更新 − δ g -\delta g δg,这个值是恒定的。对于SGD,更新值的方差较大,收敛过程会产生波动,可能落入极小值(卡在鞍点)。


加入动量后:


计算梯度并更新参数的公式如下:
g = 1 m ∇ θ ∑ i = 1 m L ( f ( x i ; θ ) , y i ) g = \frac 1m\nabla\theta\sum_{i=1}^m L\bigl(f(x_i;\theta),y_i\bigr) g=m1θi=1mL(f(xi;θ),yi)
v = α v − δ g v=\alpha v-\delta g v=αvδg
θ = θ + v \theta = \theta + v θ=θ+v
故第一次迭代公式如下, v 1 = − δ g 1 v_1=- \delta g_1 v1=δg1
θ = θ + v 1 = θ − δ g 1 \theta = \theta + v_1 = \theta - \delta g_1 θ=θ+v1=θδg1
第二次迭代公式如下:
θ = θ + v 2 = θ + α v 1 − δ g 2 = θ − δ ( α g 1 + g 2 ) \theta = \theta + v_2 = \theta + \alpha v_1- \delta g_2 = \theta - \delta(\alpha g_1 + g_2) θ=θ+v2=θ+αv1δg2=θδ(αg1+g2)
由上述推断可知:如果和上次的梯度符号是相同的,那么就能够加速下降(解决BGD的迭代速度慢);如果本次和上次的梯度符号是相反的,那么这次就和上次相互抑制,减缓震荡(解决SGD波动大,可能落入极小值)。

  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值