本文循序渐进描述梯度下降算法,从导数的几何意义开始聊起,如果熟悉微积分可以跳过,主要内容如下:
- 一. 导数的几何意义
- 二. 偏导数
- 三. 什么是梯度
- 四. 梯度下降算法
- αα是什么含义?
- 为什么是−−?
- 梯度下降举例一
- 梯度下降举例二
- 值得关注的一些问题
- 五. 梯度下降应用于线性回归
- 5.1 批量梯度下降
- 5.2 批量梯度下降算法python实现
一. 导数的几何意义
导数用来衡量函数对取值的微小变化有多敏感,如下图所示,假设有一辆汽车在行驶,s(t)s(t)是关于时间tt的行驶距离函数,dtdt表示一小段时间,dsds表示dtdt时间段内行驶的距离,导数表示,在tt点,当dtdt趋近于00时,dsds和dtdt的比值
那么当dtdt趋近于00时(注意,趋紧00,但不是00),导数的几何含义是什么呢?我们来看下面一张动图,解释了导数在几何意义上表示函数图上某点切线的斜率:
关于使用链式求导法计算导数的两个例子如下:
d(x2)dx=2xd(x2)dx=2x
d(5−θ)2dθ=−2(5−θ)(负号源于−θ)d(5−θ)2dθ=−2(5−θ)(负号源于−θ)
备注:
d(x2)dx=2xd(x2)dx=2x推导过程如下:
d(x2)dx=====limdx→0(x+dx)2−x2dxlimdx→0x2+2xdx+dx2−x2dxlimdx→02xdx+dx2dxlimdx→0(2x+dx)2x(1)(2)(3)(4)(5)(1)d(x2)dx=limdx→0(x+dx)2−x2dx(2)=limdx→0x2+2xdx+dx2−x2dx(3)=limdx→02xdx+dx2dx(4)=limdx→0(2x+dx)(5)=2x
二. 偏导数
一元函数是从实数到实数的映射f:R→Rf:R→R, 如果函数有多个变量呢?也就是说函数是从向量到实数的映射,那么这个函数就是多元函数(multivariate function),即f:Rn→Rf:Rn→R,其中nn是输入向量的维度。例如函数 f(θ)=θ21+θ22f(θ)=θ12+θ22 是从二维实数向量θ=[θ1,θ2]θ=[θ1,θ2] 到实数的映射 f:R2→Rf:R2→R。偏导数是函数关于其中一个变量的导数而保持其他变量恒定。举几个偏导数的例子:
∂∂xf(x,y)=∂∂x(x2y2)=2xy2∂∂xf(x,y)=∂∂x(x2y2)=2xy2
∂∂θ2f(θ1,θ2,θ3)=∂∂θ2(0.55−(5θ1+2θ2−12θ3))=−2∂∂θ2f(θ1,θ2,θ3)=∂∂θ2(0.55−(5θ1+2θ2−12θ3))=−2
∂∂θ3f(θ1,θ2,θ3)=∂∂θ3(0.55−(5θ1+2θ2−12θ3))=12∂∂θ3f(θ1,θ2,θ3)=∂∂θ3(0.55−(5θ1+2θ2−12θ3))=12
三. 什么是梯度
多元函数的梯度是一个向量,其中每个分量与函数关于该分量的偏导数成比例。例如我们有三维多元函数f(θ1,θ2,θ3)f(θ1,θ2,θ3),则梯度表示为:
∇f(θ)=[∂f(θ1,θ2,θ3)∂θ1,∂f(θ1,θ2,θ3)∂θ2,∂f(θ1,θ2,θ3)∂θ3]∇f(θ)=[∂f(θ1,θ2,θ3)∂θ1,∂f(θ1,θ2,θ3)∂θ2,∂f(θ1,θ2,θ3)∂θ3]
例如:f(θ)=0.55−(5θ1+2θ2−12θ3)f(θ)=0.55−(5θ1+2θ2−12θ3),则f(θ)f(θ)的梯度表示为:
∇f(θ)=[∂f∂θ1,∂f∂θ2,∂f∂θ3]=[−5,−2,12]∇f(θ)=[∂f∂θ1,∂f∂θ2,∂f∂θ3]=[−5,−2,12]
假设函数f(θ1,θ2,θ3)f(θ1,θ2,θ3)表示一个房间的在点[θ1,θ2,θ3][θ1,θ2,θ3]的温度,如果我们在θ1θ1方向上迈出一小步,记录温度的变化,我们得到ff相对于θ1θ1方向的导数,∂f(θ1,θ2,θ3)∂θ1∂f(θ1,θ2,θ3)∂θ1,让它的值为55,之后我们在其他两个方向重复这个过程,我们将得到温度在θ2θ2和θ3θ3方向上的变化,让它们的值分别为1515和00。此时
∇f(θ)=[∂f∂θ1,∂f∂θ2,∂f∂θ3]=[5,15,0]∇f(θ)=[∂f∂θ1,∂f∂θ2,∂f∂θ3]=[5,15,0]
由于在θ2θ2方向上的温度变化是θ1θ1方向上温度变化的33倍,因此如果在[5,15,0][5,15,0]方向上迈出一步,我们将得到函数f(θ1,θ2,θ3)f(θ1,θ2,θ3)的最大温度变化量。因此,梯度向量给出了多元函数的最大变化量和方向。
四. 梯度下降算法
梯度下降算法是通过逐步迭代达到函数最小值的算法(不是所有情况都能达到最小值,有一些注意事项后续说明),例如下图中的红色圆点:
想象一下我们在徒步下山,我们不能从全局的视野找到最快的下山路径,我们怎样该怎么走呢?是不是会看看周围,选择一条当前最陡峭的下坡路?怎么找这个方向呢?这个方向是上升最快的反方向。第三节中我们介绍了梯度向量给出了多元函数的最大变化量和方向,这意味着,如果我们在点θ0θ0,想要移动到附近的最低点(也就是“局部最小值”),我们的下一步θ1θ1应该这样计算:
αα是什么含义?
αα在梯度下降算法中被称作为学习率或者步长,意味着我们可以通过α来控制每一步走的距离,αα不能太大也不能太小,太小的话,可能导致迟迟走不到最低点,太大的话,会导致错过最低点。
(感兴趣的小伙伴可以尝试在谷歌机器学习课程上的调节学习率看效果https://developers.google.com/machine-learning/crash-course/fitter/graph)
为什么是−−?
梯度前加一个负号,表示朝着梯度相反的方向前进,我们在前文提到,梯度的方向就是函数在此点上升最快的方向,而我们需要朝着下降最快的方向走,自然就是负的梯度的方向,所以此处需要加上负号。
梯度下降举例一
我们来看下只有一个变量时梯度下降的计算,由于梯度下降算法常应用于求解损失函数,而损失函数通常用J(θ)J(θ)来表示,假设J(θ)=θ2J(θ)=θ2,则导数为J'(θ)=2θJ′(θ)=2θ,我们选择θ0=1θ0=1,α=0.4α=0.4,则:
θ0θ1θ2θ3θ4...========1θ0−α∗J'(θ0)1−0.4∗20.2θ1−α∗J'(θ1)0.040.0080.0016(6)(7)(8)(9)(10)(11)(12)(13)(14)(6)θ0=1(7)θ1=θ0−α∗J′(θ0)(8)=1−0.4∗2(9)=0.2(10)θ2=θ1−α∗J′(θ1)(11)=0.04(12)θ3=0.008(13)θ4=0.0016(14)...
而当我们将学习率设置为α=0.75α=0.75时:
θ0θ1θ2...======1θ0−α∗J'(θ0)1−0.75∗2−0.5θ1−α∗J'(θ1)−1.25(15)(16)(17)(18)(19)(20)(21)(15)θ0=1(16)θ1=θ0−α∗J′(θ0)(17)=1−0.75∗2(18)=−0.5(19)θ2=θ1−α∗J′(θ1)(20)=−1.25(21)...
如图所示,θ1θ1到了最低点左侧的位置,并且θ2θ2的值继续向左偏移,错过了最低点:
梯度下降举例二
我们来看下有两个变量时梯度下降的计算过程,假设J(Θ)=θ21+θ22J(Θ)=θ12+θ22. 容易看出在(0,0)(0,0)点,JJ达到最小值。我们来看下能否通过梯度下降得到这个结果。设置初始值Θ0=(1,3),α=0.1Θ0=(1,3),α=0.1,∇J(Θ)∇J(Θ)为:
∇J(Θ)=〈2θ1,2θ2〉∇J(Θ)=〈2θ1,2θ2〉
在(1,3)(1,3)点,梯度向量为 [2,6][2,6]。
Θ0Θ1Θ2Θ3Θ4...Θ10...Θ50...Θ100===========(1,3)Θ0−α∇J(Θ)(1,3)−0.1(2,6)(0.8,2.4)(0.8,2.4)−0.1(1.6,4.8)(0.64,1.92)(0.512,1.536)(0.4096,1.2288000000000001)(0.10737418240000003,0.32212254720000005)(1.1417981541647683e−05,3.425394462494306e−05)(1.6296287810675902e−10,4.888886343202771e−10)(22)(23)(24)(25)(26)(27)(28)(29)(30)(31)(32)(33)(34)(35)(22)Θ0=(1,3)(23)Θ1=Θ0−α∇J(Θ)(24)=(1,3)−0.1(2,6)(25)=(0.8,2.4)(26)Θ2=(0.8,2.4)−0.1(1.6,4.8)(27)=(0.64,1.92)(28)Θ3=(0.512,1.536)(29)Θ4=(0.4096,1.2288000000000001)(30)...(31)Θ10=(0.10737418240000003,0.32212254720000005)(32)...(33)Θ50=(1.1417981541647683e−05,3.425394462494306e−05)(34)...(35)Θ100=(1.6296287810675902e−10,4.888886343202771e−10)
可以看出我们的确在不断接近最小值,即(0,0)(0,0)。
最后,有一些非常重要的问题值得我们关注下~
- 函数必须是可微分的 (differentiable)(左侧示例);
- 学习率αα不应太小(中间示例)或太大(右侧示例)。
五. 梯度下降应用于线性回归
梯度下降算法是线性回归中用于找出误差或成本函数最小值的方法之一。 请注意,错误或成本函数必须是可微分的才能使用梯度下降。这是将误差进行平方的原因之一,使用绝对值计算误差可能会导致“拐角”,如图【梯度下降值得关注的问题】左侧所示。
5.1 批量梯度下降
在监督学习中,我们需要决定如何表示函数/假设h,而线性回归函数则是首选:
hθ(x)=θ0+θ1x1+θ2x2hθ(x)=θ0+θ1x1+θ2x2
其中,θθ是XX到YY映射的参数(也称为权重),为了书写简便,通常去掉下标θ,写作hθ(x)hθ(x),同时引入x0=1x0=1(截距),即:
h(x)=∑i=0nθixi=θTxh(x)=∑i=0nθixi=θTx
等式右侧是将θθ和xx看作是向量,nn是输入的参数个数(不算x0x0),那么问题来了,给定一个训练数据集,怎么确定参数θθ?比较合理的想法是让假设h(x)h(x)尽量接近yy,至少接近已有的训练数据集。定义成本函数(cost function)来表示h(x)h(x)和yy值相接近的程度:
J(θ)=12∑i=1m(hθ(x(i))−y(i))2J(θ)=12∑i=1m(hθ(x(i))−y(i))2
这里的系数1/2是为了后面求解偏导数时可以与系数相互抵消。我们的目标是要求解θθ使成本函数J(θ)J(θ)尽可能的小。使用梯度下降算法进行求解:
θj:=θj−α∂∂θjJ(θ)θj:=θj−α∂∂θjJ(θ)
(对所有j=0,...nj=0,...n求解θθ)
首先,我们先求解∂∂θjJ(θ)∂∂θjJ(θ):
∂∂θjJ(θ)====∂∂θj12(hθ(x)−y)22⋅12⋅(hθ(x)−y)⋅∂∂θj(hθ(x)−y)(hθ(x)−y)⋅∂∂θj(∑i=0nθixi−y)(hθ(x)−y)xj(36)(37)(38)(39)(36)∂∂θjJ(θ)=∂∂θj12(hθ(x)−y)2(37)=2⋅12⋅(hθ(x)−y)⋅∂∂θj(hθ(x)−y)(38)=(hθ(x)−y)⋅∂∂θj(∑i=0nθixi−y)(39)=(hθ(x)−y)xj
对于只含有一组数据的训练样本,我们可以得到更新θθ的规则为:
θj:=θj+α(y(i)−hθ(x(i))x(i)jθj:=θj+α(y(i)−hθ(x(i))xj(i)
扩展到多组数据样本,更新公式为:
RepeatRepeat untiluntil convergenceconvergence {{
θj:=θj+α∑i=1m((y(i)−hθ(x(i)))x(i)jθj:=θj+α∑i=1m((y(i)−hθ(x(i)))xj(i)
}}
这种方法称为批处理梯度下降算法,这种更新算法所需要的运算成本很高,尤其是数据量较大时。考虑下面的更新算法:
该算法又叫做随机梯度下降法,这种算法不停的更新θ,每次使用一个样本数据进行更新。当数据量较大时,一般使用后者算法进行更新。
LoopLoop {{
fromfrom i=1i=1 toto m,m, {{
θj:=θj+α∑i=1m((y(i)−hθ(x(i)))x(i)jθj:=θj+α∑i=1m((y(i)−hθ(x(i)))xj(i)
}}
}}
5.2 批量梯度下降算法python实现
假设有原始数据集:
1.1 1.5 2.5
1.3 1.9 3.2
1.5 2.3 3.9
1.7 2.7 4.6
1.9 3.1 5.3
2.1 3.5 6
2.3 3.9 6.7
2.5 4.3 7.4
2.7 4.7 8.1
2.9 5.1 8.8
使用批量梯度下降算法求解线性回归代码如下:
import numpy as np
"""
作者:_与谁同坐_
功能:使用批量梯度下降法求解线性回归
版本: 1.0
日期:2019/03/15
"""
def batch_gradient_descent(data_set):
"""
梯度下降法求解线性回归参数θ
:param data_set: 原始数据集
:return: 参数θ
"""
m, n = np.shape(data_set)
# 选取训练数据X,并补充参数x_0=1
train_data = np.ones((m, n))
train_data[:, :-1] = data_set[:, :-1]
x = train_data
# 最后一列作为y
y = data_set[:, -1]
# 初始化系数θ
theta = np.ones(n)
# 学习率α
alpha = 0.1
# 迭代次数
max_steps = 5000
x_trans = x.transpose()
for i in range(0, max_steps):
hypothesis = np.dot(x, theta)
# 计算损失
loss = hypothesis - y
# 计算梯度
gradient = np.dot(x_trans, loss) / m
# 迭代逐步求解θ
theta = theta - alpha * gradient
return theta
def predict(data_predict_set, theta):
"""
使用求解的线性函数参数θ来预测结果
:param x: 待预测的数据x
:param theta: 参数θ
:return: 预测出的结果
"""
m, n = np.shape(data_predict_set)
x_data = np.ones((m, n+1))
x_data[:, :-1] = data_predict_set
y_predict = np.dot(x_data, theta)
return y_predict
def main():
file_path = r"/Users/xxx/mldata/house.csv"
data_train_set = np.genfromtxt(file_path, delimiter=' ')
theta = batch_gradient_descent(data_train_set)
data_predict_set = np.array([[3.1, 5.5], [3.3, 5.9], [3.5, 6.3], [3.7, 6.7], [3.9, 7.1]])
print(predict(data_predict_set, theta))
if __name__ == '__main__':
main()
固定学习率为0.05,更改迭代次数为5000时,结果为:
[ 9.5 10.2 10.9 11.6 12.3]
参考资料
https://storage.googleapis.com/supplemental_media/udacityu/315142919/Gradient Descent.pdf
Gradient, Jacobian, Hessian, Laplacian and all that
梯度下降算法以及其Python实现 - 简书
【官方双语/合集】微积分的本质 - 系列合集_哔哩哔哩_bilibili
http://cs229.stanford.edu/notes/cs229-notes1.pdf