摘要
本文将以通俗易懂的方式介绍梯度下降算法的原理,以及文章的最后会去简要实现梯度下降算法(python代码实现)
基本思想
首先,我们需要了解梯度下降算法的原理,这里我举一个简单的例子:
有一个农夫在山上砍柴,天色将晚,农夫准备从山顶走到山底的家,由于天气原因,可见度很低,他每走一步只能看到很短的一段距离,这里我们想一下,这位农夫怎样才能以最快的时间走到家,这里这位农夫就可以使用梯度下降算法了,答案是他每一步都去走自己附近最陡峭的地方。好,问题解决了,我们继续,那么假设这位农夫具有判断最陡峭方向的能力,那么问题又来了,这位农夫每次沿着这个最陡峭的方向走多大的步子呢?如果步子很大就会错过家门,然而如果步子很小,就很有可能在很短的时间里到不了家,所以每一步的大小也是一个至关重要的问题。
好了,故事讲完了,这里我想说的是,每一次走最陡峭的地方就是梯度下降算法的原理,而每一步的大小又是梯度学习算法中很重要的学习率参数问题,相信读到这里,你的脑子中会记住一些概念了,接下来我们首先利用数学的角度去解释梯度下降算法,可能有些枯燥,但一定坚持读下去。
梯度下降算法的数学原理解释
x
1
=
x
0
−
α
∗
δ
y
(
x
)
x1= x0 - \alpha * \delta y(x)
x1=x0−α∗δy(x)
其中上述公式中的参数分别表示为:
x1:next position 下一步的位置
x0:current position 当前所处的位置
α
\alpha
α:every step 每一步的步长(也就是上文中的学习率 不能太大,也不能太小)
δ
\delta
δy(x): direction of the fastest increase 梯度也就是最陡峭位置的方向
这里我们简单举一个例子:
首先我们举一个单变量的函数:
y
(
x
)
=
x
2
y(x) = x^2
y(x)=x2 则
δ
(
y
(
x
)
)
=
2
∗
x
\delta(y(x)) = 2 *x
δ(y(x))=2∗x
我们定初始位置x0 = 1 步长(学习率)
α
=
0.4
\alpha=0.4
α=0.4
则根据公式
x
1
=
x
0
−
α
∗
δ
y
(
x
)
x1= x0 - \alpha * \delta y(x)
x1=x0−α∗δy(x)有:
x1 = 1
x2 = 0.04
x3= 0.008
x4 = 0,.0016
如图,经过四次的运算,也就是走了四步,基本就抵达了函数的最低点,也就是山底
接下来,我们尝试一个多变量的例子:
z
(
x
,
y
)
=
x
2
+
y
2
z(x,y) = x^2 + y^2
z(x,y)=x2+y2
现在要通过梯度下降法计算这个函数的最小值。我们通过观察就能发现最小值其实就是 (0,0)点。但是接下来,我们会从梯度下降算法开始一步步计算到这个最小值!
我们假设初始的起点为(x,y)=(1,3) 初始的学习率为
α
=
0.1
\alpha = 0.1
α=0.1 则
δ
(
z
)
=
<
2
x
,
2
y
>
\delta(z) = <2x,2y>
δ(z)=<2x,2y>
然后进行多次迭代:
最后我们尝试用python进行梯度下降算法的实现
场景是一个简单的线性回归的例子,现假设我们有一系列的点,如下图所示:
首先,我们定义一个代价函数,在此我们采用均方误差函数:
此公式中
m是数据集中点的个数
½是一个常量,这样是为了在求梯度的时候,二次方乘下来就和这里的½抵消了,自然就没有多余的常数系数,方便后续的计算,同时对结果不会有影响
y 是数据集中每个点的真实y坐标的值
h 是我们的预测函数,根据每一个输入x,根据Θ 计算得到预测的y值,即我们可以根据代价函数看到,代价函数中的变量有两个,所以是一个多变量的梯度下降问题,求解出代价函数的梯度,也就是分别对两个变量进行微分
明确了代价函数和梯度,以及预测的函数形式。我们就可以开始编写代码了。
import numpy as np
# Size of the points dataset.
m = 20
# Points x-coordinate and dummy value (x0, x1).
X0 = np.ones((m, 1))
X1 = np.arange(1, m+1).reshape(m, 1)
X = np.hstack((X0, X1))
# Points y-coordinate
y = np.array([
3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,
11, 13, 13, 16, 17, 18, 17, 19, 21
]).reshape(m, 1)
# The Learning Rate alpha.
alpha = 0.01
def error_function(theta, X, y):
'''Error function J definition.'''
diff = np.dot(X, theta) - y
return (1./2*m) * np.dot(np.transpose(diff), diff)
def gradient_function(theta, X, y):
'''Gradient of the function J definition.'''
diff = np.dot(X, theta) - y
return (1./m) * np.dot(np.transpose(X), diff)
def gradient_descent(X, y, alpha):
'''Perform gradient descent.'''
theta = np.array([1, 1]).reshape(2, 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)
return theta
optimal = gradient_descent(X, y, alpha)
print('optimal:', optimal)
print('error function:', error_function(optimal, X, y)[0,0])
结果如下:
所拟合的直线如下:
相信,读完这篇文章,你对梯度下降就能理解了。