梯度下降
一.导数概念
1.导数定义如下
导数反映的是函数
f
(
x
)
f(x)
f(x)在某一点处沿
x
x
x轴正方向的变化率。再强调一遍,是
f
(
x
)
f(x)
f(x)在
x
x
x轴上某一点处沿着x轴正方向的变化率/变化趋势,直观地看,也就是在
x
x
x轴上某一点处,如果
f
’
(
x
)
>
0
f’(x)>0
f’(x)>0,说明
f
(
x
)
f(x)
f(x)的函数值在
x
x
x点沿
x
x
x轴正方向是趋于增加的;如果
f
’
(
x
)
<
0
f’(x)<0
f’(x)<0,说明
f
(
x
)
f(x)
f(x)的函数值在
x
x
x点沿
x
x
x轴正方向是趋于减少的。
这里对一些量进行解释:
Δx:x的变化量;
dx:x的变化量Δx趋于0时,则记作微元dx;
Δy:Δy=f(x0+Δx)-f(x0),是函数的增量;
dy:dy=f’(x0)dx,是切线的增量;
当Δx→0时,dy与Δy都是无穷小,dy是Δy的主部,即Δy=dy+o(Δx).
2.偏导数的定义如下
可以看到,导数与偏导数本质是一致的,都是当自变量的变化量趋于0时,函数值的变化量与自变量变化量比值的极限。直观地说,偏导数也就是函数在某一点上沿坐标轴正方向的的变化率。
3.方向导数的定义如下
我们不仅要知道函数在坐标轴正方向上的变化率(即偏导数),而且还要设法求得函数在其他特定方向上的变化率。而方向导数就是函数在其他特定方向的变化率。
4.梯度的定义如下
梯度的提出只为回答一个问题:
函数在变量空间的某一点处,沿着哪一个方向有最大的变化率?
在某一点的梯度是这样一个向量,它的方向与取得最大方向导数的方向一致,而它的模为方向导数的最大值。
二.梯度下降
1.梯度下降的引入
举个例子,当你费劲全部力气登上高山时,欣赏完了落霞与孤鹜齐飞,秋水共长天一色的绝妙美色,体会了会当凌绝顶,一览众山小的感觉后,这时候你的肚子咕咕叫了起来,于是你准备吃饭,但是只有山下才有饭店。那么问题来了,怎么下山才是最快的呢?
梯度下降的基本过程就和下山的场景很类似。
首先,我们有一个可微分的函数,这个函数就代表着一座山。我们的目标就是找到这个函数的最小值,也就是山底。根据之前的场景假设,最快的下山的方式就是找到当前位置最陡峭的方向,然后沿着此方向向下走,对应到函数中,就是找到给定点的梯度 ,然后朝着梯度相反的方向,就能让函数值下降的最快!因为梯度的方向就是函数值变化最快的方向。
在这个过程我们进行了两个操作:
1.如何测量山峰的“陡峭”程度?
2.每一次走多长距离(步长)后重新进行陡峭程度测量,走太长,那么整体的测量次数就会比较少,可能会导致走的并不是最佳路线,错过了最低点;走太短,测量次数过于频繁,整体耗时太长。这里的步长如何设置?
2.梯度下降的核心
1.核心公式
θ
i
=
θ
i
−
α
∂
∂
θ
i
J
(
θ
)
\theta_i=\theta_i-\alpha\frac{\partial}{\partial\theta_i}J(\theta)
θi=θi−α∂θi∂J(θ)
其中
α
\alpha
α代表步长或学习率,即我们需要控制一次走的距离,其实就是不要走太快,错过了最低点。同时也要保证不要走的太慢,导致太阳下山了,还没有走到山下。所以
α
\alpha
α的选择在梯度下降法中往往是很重要的!
α
\alpha
α不能太大也不能太小,太小的话,可能导致迟迟走不到最低点,太大的话,会导致错过最低点!
下面举几个例子理解一下:
1.
α
\alpha
α太小,下降太慢
2.
α
\alpha
α太大,轨迹开始波动了,梯度方向不停的在变,最后还没收敛
2.梯度下降的一般步骤
假设函数
y
=
f
(
x
1
,
x
2
,
⋯
,
x
n
)
y=f(x_1,x_2,\cdots,x_n)
y=f(x1,x2,⋯,xn)只有一个极小点。
初始给定参数为
X
0
=
(
x
10
,
x
20
,
⋯
,
x
n
0
)
X_0=(x_{10},x_{20},\cdots,x_{n0})
X0=(x10,x20,⋯,xn0),从这个点如何搜索才能找到原函数的极小值点?
1.首先设定一个较小的正数
η
\eta
η,
ε
\varepsilon
ε。
2.求当前位置处的各个偏导数:
f
′
(
x
m
0
)
=
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
∼
n
f^\prime(x_{m0})=\frac{\partial y}{\partial x_m}(x_{m0}),m=1 \sim n
f′(xm0)=∂xm∂y(xm0),m=1∼n
3.修改当前函数的参数值,公式如下:
x
m
′
=
x
m
−
η
⋅
∂
y
∂
x
m
(
x
m
0
)
,
m
=
1
∼
n
x^\prime_m=x_m-\eta\cdot\frac{\partial y}{\partial x_m}(x_{m0}),m=1\sim n
xm′=xm−η⋅∂xm∂y(xm0),m=1∼n
4.如果参数变换量小于
ε
\varepsilon
ε,则退出程序,否则返回第二步。
注:第4步为终止条件,也可以选择采用迭代次数来终止程序,即设置迭代次数,当迭代停止时,程序即停止。
1.单变量函数的梯度下降:
我们假设有一个单变量的函数
J
(
θ
)
=
θ
2
J(\theta)=\theta^{2}
J(θ)=θ2
函数的微分,直接求导就可以得到
J
′
(
θ
)
=
2
θ
J^\prime(\theta)=2\theta
J′(θ)=2θ
初始化:也就是设置起点,起点可以随意的设置,这里设置为1
θ
0
=
1
\theta_0=1
θ0=1
学习率也可以随意的设置,这里设置为0.4
α
=
0.4
\alpha=0.4
α=0.4
根据梯度下降的计算公式
θ
i
=
θ
i
−
α
⋅
∂
∂
θ
i
J
(
θ
)
\theta_i=\theta_i-\alpha\cdot\frac{\partial}{\partial\theta_i}J(\theta)
θi=θi−α⋅∂θi∂J(θ)
我们开始进行梯度下降的迭代计算过程:
θ
0
=
1
\theta_0=1
θ0=1
θ
1
=
θ
0
−
α
∗
J
′
(
θ
0
)
=
1
−
0.4
∗
2
∗
1
=
0.2
\theta_1=\theta_0-\alpha*J^\prime(\theta_0)=1-0.4*2*1=0.2
θ1=θ0−α∗J′(θ0)=1−0.4∗2∗1=0.2
θ
2
=
θ
1
−
α
∗
J
′
(
θ
1
)
=
0.2
−
0.4
∗
2
∗
0.2
=
0.04
\theta_2=\theta_1-\alpha*J^\prime(\theta_1)=0.2-0.4*2*0.2=0.04
θ2=θ1−α∗J′(θ1)=0.2−0.4∗2∗0.2=0.04
θ
3
=
θ
2
−
α
∗
J
′
(
θ
2
)
=
0.04
−
0.4
∗
2
∗
0.04
=
0.008
\theta_3=\theta_2-\alpha*J^\prime(\theta_2)=0.04-0.4*2*0.04=0.008
θ3=θ2−α∗J′(θ2)=0.04−0.4∗2∗0.04=0.008
⋯
\cdots
⋯
θ
n
=
θ
n
−
1
−
α
∗
J
′
(
θ
n
−
1
)
\theta_n=\theta_{n-1}-\alpha*J^\prime(\theta_{n-1})
θn=θn−1−α∗J′(θn−1)
2.多元函数的梯度下降:
我们假设有一个目标函数
J
(
θ
)
=
θ
1
2
+
θ
2
2
J(\theta)=\theta^{2}_{1}+\theta^{2}_{2}
J(θ)=θ12+θ22
现在要通过梯度下降法计算这个函数的最小值。我们通过观察就能发现最小值其实就是 (0,0)点。但是接下来,我们会用梯度下降算法一步步计算出这个最小值。
初始化:
设置初始的起点:
θ
0
=
(
1
,
3
)
\theta_0=(1,3)
θ0=(1,3)
初始的学习率为:
α
=
0.1
\alpha=0.1
α=0.1
函数的梯度为:
∇
J
(
θ
)
=
<
2
θ
1
,
2
θ
2
>
\nabla J(\theta)=<2\theta_1,2\theta_2>
∇J(θ)=<2θ1,2θ2>
进行多次迭代:
θ
0
=
(
1
,
3
)
\theta_0=(1,3)
θ0=(1,3)
θ
1
=
θ
0
−
α
∇
J
(
θ
0
)
=
(
1
,
3
)
−
0.1
∗
(
2
∗
1
,
2
∗
3
)
=
(
0.8
,
2.4
)
\theta_1=\theta_0-\alpha\nabla J(\theta_0)=(1,3)-0.1*(2*1,2*3)=(0.8,2.4)
θ1=θ0−α∇J(θ0)=(1,3)−0.1∗(2∗1,2∗3)=(0.8,2.4)
θ
2
=
θ
1
−
α
∇
J
(
θ
1
)
=
(
0.8
,
2.4
)
−
0.1
∗
(
2
∗
0.8
,
2
∗
2.4
)
=
(
0.64
,
1.92
)
\theta_2=\theta_1-\alpha\nabla J(\theta_1)=(0.8,2.4)-0.1*(2*0.8,2*2.4)=(0.64,1.92)
θ2=θ1−α∇J(θ1)=(0.8,2.4)−0.1∗(2∗0.8,2∗2.4)=(0.64,1.92)
θ
3
=
θ
2
−
α
∇
J
(
θ
2
)
=
(
0.64
,
1.92
)
−
0.1
∗
(
2
∗
0.64
,
2
∗
1.92
)
=
(
0.512
,
1.536
)
\theta_3=\theta_2-\alpha\nabla J(\theta_2)=(0.64,1.92)-0.1*(2*0.64,2*1.92)=(0.512,1.536)
θ3=θ2−α∇J(θ2)=(0.64,1.92)−0.1∗(2∗0.64,2∗1.92)=(0.512,1.536)
⋯
\cdots
⋯
θ
n
=
θ
n
−
1
−
α
∇
J
(
θ
n
−
1
)
\theta_n=\theta_{n-1}-\alpha\nabla J(\theta_{n-1})
θn=θn−1−α∇J(θn−1)
三.梯度下降三兄弟(BGD,SGD, MBGD)
基于如何使用数据计算代价函数的导数,梯度下降法可以被定义为不同的形式。确切地说,根据使用数据量的大小,时间复杂度和算法的准确率,梯度下降法可分为:
1.批量梯度下降法(Batch Gradient Descent)
批量梯度下降法每次都使用训练集中的所有样本更新参数。它得到的是一个全局最优解,但是每迭代一步,都要用到训练集所有的数据,如果训练样本
m
m
m很大,那么迭代速度就会变得很慢。
优点:可以得出全局最优解。
缺点:样本数据集大时,训练速度慢。
2.随机梯度下降法(Stochastic Gradient Descent)
随机梯度下降法每次更新都从样本随机选择1组数据,因此随机梯度下降比批量梯度下降在计算量上会大大减少。 但是SGD有一个缺点是,其噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向。而且SGD因为每次都是使用一个样本进行迭代,因此最终求得的最优解往往不是全局最优解,而只是局部最优解。
优点:训练速度较快。
缺点:过程杂乱,准确度下降。
3.小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法是最广泛使用的一种算法,该算法每次使用 m m m个训练样本(称之为一批)进行训练,能够更快得出准确的答案。小批量梯度下降法不是使用完整数据集,在每次迭代中仅使用 m m m个训练样本去计算代价函数的梯度。一般小批量梯度下降法所选取的样本数量在50到256个之间,视具体应用而定。MBGD综合了上述两种方法,既保证了训练速度快,又保证了准确度。
四.实例代码
1.一元函数
1.手工推导
设置学习率
α
=
0.1
\alpha=0.1
α=0.1,
ε
=
0.01
\varepsilon=0.01
ε=0.01;
1.初始化:选择
x
0
=
10
x_0=10
x0=10;
2.求偏导:
d
y
d
x
0
=
(
x
0
−
2
)
=
10
−
2
=
8
\frac{dy}{dx_0}=(x_0-2)=10-2=8
dx0dy=(x0−2)=10−2=8
3.更新参数:
x
1
=
x
0
−
α
d
y
d
x
0
=
10
−
0.1
∗
8
=
9.2
x_1=x_0-\alpha\frac{dy}{dx_0}=10-0.1*8=9.2
x1=x0−αdx0dy=10−0.1∗8=9.2
4.判断参数变化量(确定是否停止):
△
x
=
α
d
y
d
x
0
=
0.1
∗
8
=
0.8
>
ε
=
0.01
\triangle x=\alpha\frac{dy}{dx_0}=0.1*8=0.8>\varepsilon=0.01
△x=αdx0dy=0.1∗8=0.8>ε=0.01
5.反复迭代:
x
44
=
x
43
−
α
d
y
d
x
43
≈
2.09
−
0.1
∗
0.09
≈
2.008
x_{44}=x_{43}-\alpha\frac{dy}{dx_{43}}\approx2.09-0.1*0.09\approx2.008
x44=x43−αdx43dy≈2.09−0.1∗0.09≈2.008
6.判断参数变化量:
△
x
=
α
d
z
d
x
43
=
0.1
∗
0.09
=
0.009
<
ε
=
0.01
\triangle x=\alpha\frac{dz}{dx_{43}}=0.1*0.09=0.009<\varepsilon=0.01
△x=αdx43dz=0.1∗0.09=0.009<ε=0.01
7.计算结束:最低点是
x
=
2.08
x=2.08
x=2.08
2.代码实现
计算函数 y = 1 2 x 2 − 2 x + 3 y=\frac{1}{2}x^2-2x+3 y=21x2−2x+3的最低点
import numpy as np
import matplotlib.pyplot as plt
# f的函数值
def f(x):
return x ** 2 * 0.5 -2 * x + 3
def d_f(x):
return (x - 2)
#定义梯度下降算法
def gradient_descent():
times = 100 # 迭代次数
alpha = 0.1 # 学习率
x =10# 设定x的初始值
x_axis = np.linspace(-10, 10) #设定x轴的坐标系
fig = plt.figure(1,figsize=(5,5)) #设定画布大小
ax = fig.add_subplot(1,1,1) #设定画布内只有一个图
ax.set_xlabel('X', fontsize=14)
ax.set_ylabel('Y', fontsize=14)
ax.plot(x_axis,f(x_axis)) #作图
#进行迭代
for i in range(times):
x1 = x
y1= f(x)
print("第%d次迭代:x=%f,y=%f" % (i + 1, x, y1))
x = x - alpha * d_f(x) #更新x
y = f(x) #更新y
ax.plot([x1,x], [y1,y], 'ko', lw=1, ls='-', color='coral')
plt.show()
if __name__ == "__main__":
gradient_descent()
运行结果:
绘制图形如下:
2.多元函数
计算函数 z = ( x − 10 ) 2 + ( y − 10 ) 2 z = (x-10)^2 + (y-10)^2 z=(x−10)2+(y−10)2的最低点
1.手工推导
设置学习率
α
=
0.1
\alpha=0.1
α=0.1,
ε
=
0.01
\varepsilon=0.01
ε=0.01;
1.初始化:选择
x
0
=
20
,
y
0
=
20
x_0=20,y_0=20
x0=20,y0=20;
2.求偏导:
∂
z
∂
x
0
=
2
∗
(
x
0
−
10
)
=
2
∗
10
=
20
,
∂
z
∂
y
0
=
2
∗
(
y
0
−
10
)
=
2
∗
10
=
20
\frac{\partial z}{\partial x_0}=2*(x_0-10)=2*10=20,\frac{\partial z}{\partial y_0}=2*(y_0-10)=2*10=20
∂x0∂z=2∗(x0−10)=2∗10=20,∂y0∂z=2∗(y0−10)=2∗10=20
3.更新参数:
x
1
=
x
0
−
α
∂
z
∂
x
0
=
20
−
0.1
∗
20
=
18
,
y
1
=
y
0
−
α
∂
z
∂
y
0
=
20
−
0.1
∗
20
=
18
x_1=x_0-\alpha\frac{\partial z}{\partial x_0}=20-0.1*20=18,y_1=y_0-\alpha\frac{\partial z}{\partial y_0}=20-0.1*20=18
x1=x0−α∂x0∂z=20−0.1∗20=18,y1=y0−α∂y0∂z=20−0.1∗20=18
4.判断参数变化量(确定是否停止):
△
x
=
α
∂
z
∂
x
0
=
0.1
∗
20
=
2
>
ε
=
0.01
\triangle x=\alpha\frac{\partial z}{\partial x_0}=0.1*20=2>\varepsilon=0.01
△x=α∂x0∂z=0.1∗20=2>ε=0.01
5.反复迭代:
x
13
=
x
12
−
α
∂
z
∂
x
12
≈
10.03
−
0.1
∗
0.06
≈
10.02
,
y
13
=
y
12
−
α
∂
z
∂
y
12
≈
10.03
−
0.1
∗
0.06
≈
10.02
x_{13}=x_{12}-\alpha\frac{\partial z}{\partial x_{12}}\approx10.03-0.1*0.06\approx10.02,y_{13}=y_{12}-\alpha\frac{\partial z}{\partial y_{12}}\approx10.03-0.1*0.06\approx10.02
x13=x12−α∂x12∂z≈10.03−0.1∗0.06≈10.02,y13=y12−α∂y12∂z≈10.03−0.1∗0.06≈10.02
6.判断参数变化量:
△
x
=
α
∂
z
∂
x
12
=
0.1
∗
0.06
=
0.006
<
ε
=
0.01
\triangle x=\alpha\frac{\partial z}{\partial x_{12}}=0.1*0.06=0.006<\varepsilon=0.01
△x=α∂x12∂z=0.1∗0.06=0.006<ε=0.01
7.计算结束:最低点是
(
10.02
,
10.02
)
(10.02,10.02)
(10.02,10.02)
2.代码实现
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
#求fx的函数值
def fx(x, y):#定义函数
return (x - 10) ** 2 + (y - 10) ** 2
def d_fx(x,y):#对x的偏导
return 2 * (x - 10)
def d_fy(x,y):#对y的偏导
return 2 * (y - 10)
def gradient_descent():
times = 100 # 迭代次数
alpha = 0.1 # 学习率
x = 20 # x的初始值
y = 20 # y的初始值
#绘图操作
fig = Axes3D(plt.figure()) # 将画布设置为3D
axis_x = np.linspace(0, 20, 100)#设置X轴取值范围
axis_y = np.linspace(0, 20, 100)#设置Y轴取值范围
axis_x, axis_y = np.meshgrid(axis_x, axis_y) #将数据转化为网格数据
z = fx(axis_x, axis_y)#计算Z轴数值
fig.set_xlabel('X', fontsize=14)
fig.set_ylabel('Y', fontsize=14)
fig.set_zlabel('Z', fontsize=14)
fig.view_init(elev=60,azim=300)#设置3D图的俯视角度,方便查看梯度下降曲线
fig.plot_surface(axis_x, axis_y, z, rstride=1, cstride=1, cmap=plt.get_cmap('rainbow')) #作出底图
#计算极值
for i in range(times):
x1 = x
y1 = y
f1 = fx(x, y)
print("第%d次迭代:x=%f,y=%f,f=%f" % (i + 1, x1, y1, f1))
#print(f"第{i+1}次迭代: x={x} y={y} fxy={f1}")
x = x - alpha * 2 * d_fx(x,y)#更新x
y = y - alpha * 2 * d_fy(x,y)#更新y
f = fx(x, y)#更新f
fig.plot([x1, x], [y1, y], [f1, f], 'ko', lw=2, ls='-')#绘制点
plt.show()#展示图像
if __name__ == "__main__":
gradient_descent()
运行结果:
绘制图像如下:
补充:
有些小伙伴不知道怎么把这个图绘制出来,其实很好解决,因为Spyder软件就自带这一功能。
所以我们只需要下载一个Spyder即可。