梯度下降法的原理与实现

概念介绍

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

公式

抽象的公式就一个

θ

n

e

x

t

=

θ

n

o

w

α

f

(

θ

n

o

w

)

\theta ^{next} = \theta ^{now} – \alpha\bigtriangledown f(\theta ^{now})

θnext=θnowαf(θnow)

θ

n

e

x

t

\theta ^{next}

θnext:x在下个时刻的坐标

θ

n

o

w

\theta ^{now}

θnow:x在当前时刻的坐标

α

\alpha

α:步长,每一步走多远,即学习率

f

(

θ

n

o

w

)

\bigtriangledown f(\theta ^{now})

f(θnow):目标函数f(x)在

θ

n

o

w

\theta ^{now}

θnow点的导数
举个例子:
目标函数

y

=

x

2

y = 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

+

θ

1

x

1

(

i

)

+

θ

2

x

2

(

i

)

+

.

.

.

+

θ

n

x

n

(

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

(

θ

)

=

1

2

m

i

=

1

m

(

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

,

x

1

,

.

.

.

,

x

n

]

X来表示[0,x_{1},…,x_{n}]

X[0,x1,...,xn]
这样,原来的回归模型就可以写成:

h

Θ

(

X

)

=

X

Θ

h_{\Theta}( X) = X\Theta

hΘ(X)=XΘ
原来的代价函数可以写成:

J

(

Θ

)

=

1

2

m

(

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

(

Θ

)

=

1

m

(

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

=

2

x

2

y=2x^{2}

y=2x2,其一阶导数为

y

=

4

x

y =4x

y=4x
在这里插入图片描述

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

  • 学习率太大会导致不收敛
  • 学习率太小会导致到达最低点的迭代次数太大,费时
 

版权声明:本文为xiondun原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiondun/p/13725318.html