梯度下降算法过程及为什么负梯度方向是下降最快方向(附代码)

对于梯度下降算法我们熟知的一个例子就是下山问题,当我们位于山的某一点处,沿着当前位置寻找山坡最陡方向以一定步长进行移动,直到走到山脚。那么这个过程具体是怎么做到的?为什么说负梯度方向是下降最快方向呢?

首先我们设定一个初始值x_{k}【相当于我们在山上的一个初始位置】,优化的目标函数为f(x)【可以理解为将山体轮廓作为目标函数f(x)】,接下来我们可以在x_{k}处进行一阶泰勒展开【对函数局部的线性近似】:

f(x)=f(x_{k})+f'(x_{k})(x-x_{k})

我们期望的是在下一个位置x_{k+1}处有f(x_{k+1})<f(x_{k})【山体的下一个位置的值低于前面的值才说明我们正在下山,这个过程才是有效的】,我们令x=x_{k+1}则有:

f(x_{k+1})=f(x_{k})+f'(x_{k})(x_{k+1}-x_{k})

进一步得到:f(x_{k+1})-f(x_{k})=f'(x_{k})(x_{k+1}-x_{k})<0

为了保证f(x_{k+1})-f(x_{k})<0可以设x_{k+1}-x_{k}=-f'(x_{k}),又为了需要使x_{k+1}-x_{k}距离尽可能的小以减小误差,引入步长系数\gamma \epsilon (0,1),则有:

x_{k+1}-x_{k}=-\gamma f'(x_{k})

x_{k+1}=x_{k}-\gamma f'(x_{k})

这里只是举例的一元函数,如果是多元函数,上式为偏导数。根据上述过程不停的迭代寻找最优解。【上述公式也就是我们常看到的权重更新   w:w-\eta \triangledown J(w)

那么又为什么是负梯度方向才是下降的最快方向呢?

首先x_{k+1}-x_{k}也是一个向量,这个向量的大小(模值)也其实就是步长系数\gamma【注:步长是一个标量】,如果我们再引入一个单位向量\overrightarrow{e},那么向量x_{k+1}-x_{k}可表示为:

x_{k+1}-x_{k}=\gamma \overrightarrow{e}

那么泰勒展开则为:                     f(x_{k+1})=f(x_{k})+\gamma \overrightarrow{e}f'(x_{k})

我们又希望                                 f(x_{k+1})-f(x_{k})=\gamma \overrightarrow{e}f'(x_{k})<0

我们已知\gamma是一个为正值的标量,\overrightarrow{e}f'(x_{k})是矢量,两个向量相乘为\left | \overrightarrow{e} \right |\cdot \left | f'(x_{k}) \right |\cdot cos\alpha\alpha是两个向量之间的夹角。为了让相乘后的结果为“负最大值”那么向量之间的夹角应为180°,当夹角为0的时候有正的最大值(梯度方向上值增长最快的方向)。所以说当x_{k+1}-x_{k}和梯度方向相反(负梯度方向)的时候,能让值尽可能减小。

我们又已知\overrightarrow{e}是单位向量,模为1,则可以表示为:\overrightarrow{e}=-\frac{f'(x)}{\left | f'(x) \right |}。于是可得权重更新公式为:

x_{k+1}=x_{k}-\gamma\frac{f'(x)}{\left | f'(x) \right |}

又因为\left | f'(x) \right |是标量,所以和\gamma写在一起x_{k+1}=x_{k}-\eta f'(x),如果对于多元函数,则为:

x:x-\eta \triangledown f(x)

代码实现: 

# 假如目标函数为f(x) = 2(x - 5)² # 可以知道当函数值为0的时候,x=5
# 定义函数
def f(x):
    return 2 * (x - 5) ** 2


# 函数求导
def df(x):
    return 4 * (x - 5)


# 定义学习率(步长)
learning_rate = 0.1

res_x = []  # 保存每次更新后x的值
res_y = []  # 保存每次更新x后y的值

x0 = 1  # 初始位置
y = f(x0)  # 初始值
y_current = 0  # y当前值
# 循环20次
for epoch in range(20):
    x0 = x0 - learning_rate * df(x0)  # 权重更新  梯度下降
    tmp = f(x0)  # 将初始位置先放在一个临时变量中
    y_current = tmp  # 将临时变量中的值赋值为当前值
    res_x.append(x0)  # 记录x的变化
    res_y.append(y_current)  # 记录y的变化

打印一下x的输出结果:可以看出x的值越来越接近于5了

2.6
3.56
4.136
4.4816
4.68896
4.813376
4.8880256
4.93281536
4.959689216
4.9758135296
4.98548811776
4.991292870656
4.9947757223936
4.9968654334361595
4.998119260061696
4.998871556037018
4.999322933622211
4.999593760173327
4.999756256103996
4.999853753662398

print("{:.10f}".format(res_y[-1])) #无限接近0

输出为:0.0000000428

可视化:可以看到x的值越来越趋近于5,y的值越来越接近于0

代码参考:梯度下降算法(附代码实现) - 知乎

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃肉的鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值