梯度下降法的原理与实现

概念介绍

梯度下降法目的是为了“下降”,下降的方法是按照“梯度”。比如你在一座山上,当前你只能迈出一步,如何走才能使你的高度下降的最多呢,根据梯度的理论,我们沿着当前梯度的反方向走,会让我们的下降幅度最大。
上述例子中,山就是一个函数,在山上的你就是函数中待优化的变量,人的坐标表示变量初始值,我们要 求的是函数最小值即到达山底,人该如何走即如何迭代变量。所以我们只要沿着函数梯度的反方向,就能最快的到达我们要去的地方。
梯度下降是一种更新参数的方法,具体如何更新跟原函数的在某点的梯度有关。不会改变要求的最优解。
我们可以利用梯度下降法求最大值和最小值,求最大值沿着梯度方向走即可,求最小值则沿着梯度的反方向走。

公式

抽象的公式就一个
θnext=θnow−α▽f(θnow) \theta ^{next} = \theta ^{now} - \alpha\bigtriangledown f(\theta ^{now}) θnext=θnowαf(θnow)
θnext\theta ^{next}θnext:x在下个时刻的坐标
θnow\theta ^{now}θnow:x在当前时刻的坐标
α\alphaα:步长,每一步走多远,即学习率
▽f(θnow)\bigtriangledown f(\theta ^{now})f(θnow):目标函数f(x)在θnow\theta ^{now}θnow点的导数
举个例子:
目标函数 y=x2y = x^{2}y=x2,学习率α\alphaα=0.1,当前位置x =1,要求y最小值,则下一时刻x的值应该更新为多少呢。根据梯度下降理论,下一时刻:
x = 1 - 0.1*2*1 = 0.8,此时的xx=1的时候更接近0这个最小值。这就是一元变量下的梯度下降算法,多元变量是也是一样的,只是求梯度时有些许不同而已。

算法实现

结合线性回归模型来实现梯度下降算法。梯度下降是线性回归求最优解的一种常用方法。
设我们的线性回归模型为:
hθ(x(i))=θ0+θ1x1(i)+θ2x2(i)+...+θnxn(i) h_{\theta}( x^{(i)}) =\theta_{0} +\theta_{1}x_{1}^{(i)} + \theta_{2}x_{2}^{(i)} +...+\theta_{n}x_{n}^{(i)} hθ(x(i))=θ0+θ1x1(i)+θ2x2(i)+...+θnxn(i)

即我们变量是n维的,对每个维度上都加了个权重,求所有维度上的带权和然后加上一个偏置项就是我们的回归函数。而我们的目的就是求出Θ0...Θn\Theta_{0}...\Theta_{n}Θ0...Θn这n+1个参数。
定义代价函数
J(θ)=12m∑i=1m(hθ(x(i))−y(i))2 J(\theta) = \frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}( x^{(i)})-y^{(i)})^{2} J(θ)=2m1i=1m(hθ(x(i))y(i))2
这个代价函数就是预测值与实际值之间的距离的平方,m是样本的个数,求个平均更准确点嘛。分母的2是为跟后面平方项求导后的2约掉。
在程序中,多变量的运算都是通过矩阵来完成的,所以我们要将上述式子写成矩阵相乘的式子:
我们用Θ\ThetaΘ来表示[Θ0,...,Θn][\Theta_{0},...,\Theta_{n}][Θ0,...,Θn],用X来表示[0,x1,...,xn]X来表示[0,x_{1},...,x_{n}]X[0,x1,...,xn]
这样,原来的回归模型就可以写成:
hΘ(X)=XΘ h_{\Theta}( X) = X\Theta hΘ(X)=XΘ
原来的代价函数可以写成:
J(Θ)=12m(XΘ−Y)T(XΘ−Y) J(\Theta) = \frac{1}{2m}(X\Theta-Y)^{T}(X\Theta-Y) J(Θ)=2m1(XΘY)T(XΘY)
上述两式子中的变量全为矩阵表示。
将代价函数对我们要求的参数Θ\ThetaΘ求一阶导数得:
▽J(Θ)=1m(XΘ−Y)X \bigtriangledown J(\Theta) = \frac{1}{m}(X\Theta-Y) X J(Θ)=m1(XΘY)X
这个一阶导数就是我们每次更新参数时需要的梯度。这样我们就从一元变量扩展到了多元变量上。可以对含有多元参数的目标函数利用梯度下降法求出最优解。
代码实现:

import numpy as np


row = 20
x0 = np.zeros((row,1))
x1 = np.arange(0,row+0).reshape(row,1)
x2 = np.arange(10,row+10).reshape(row,1)
x3 = np.arange(21,row+21).reshape(row,1)
x = np.hstack((x1,x2,x3))
y = 2*x1 +3*x2 + 4*x3 + np.random.random(row).reshape(row,1)

#定义代价函数
def error_function(theta, x, y):
    diff = np.dot(x, theta) - y
    return (1./2*row) * np.dot(np.transpose(diff), diff)

#求梯度的函数
def gradient_function(theta, x, y):
    diff = np.dot(x, theta) - y
    return (1./row) * np.dot(np.transpose(x), diff)


#利用梯度下降法不断迭代参数
def gradient_descent(x, y, alpha):
    theta = np.array([1, 1, 1]).reshape(3, 1)
    gradient = gradient_function(theta, x, y)
    while not np.all(np.absolute(gradient) <= 1e-5):
        theta = theta - alpha * gradient
        gradient = gradient_function(theta, x, y)
        # print(gradient)
    return theta

alpa = 0.001
theta = gradient_descent(x,y,alpa)
print(theta)

输出结果:

[[1.        ]
 [1.97155845]
 [2.96137676]
 [4.05017691]]

可以看到这个跟我们设定函数时x0,x1,x2,x3是非常接近的。

注意

上述代码中 ,学习率为0.001,把学习率改成1会发生什么呢,自己试试看,会发现程序会一直运行不停止,打印出梯度会发现,梯度一直增加最终变成了无限大。
这是因为,如果学习率过大,会导致不收敛,因为跨的步数太大,会越过那个最低点,并一直震荡。至于为什么梯度会变得越来越大以至于无限大,可以举个例子,函数y=2x2y=2x^{2}y=2x2,其一阶导数为y=4xy =4xy=4x
在这里插入图片描述

在A(10,200)点,梯度为40,所以下一时刻到B点的横坐标为10-140= -30,
在B(-30,1800)点,梯度为-120,所以下一时刻到达的点的横坐标为-30+1
120=90,这样会导致离最优值越来越远。
所以:

  • 学习率太大会导致不收敛
  • 学习率太小会导致到达最低点的迭代次数太大,费时
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值