AI算法-梯度下降

一.导数概念

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αθiJ(θ)
其中 α \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)=xmy(xm0),m=1n
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ηxmy(xm0),m=1n
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αθiJ(θ)

我们开始进行梯度下降的迭代计算过程:
θ 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)=10.421=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.20.420.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.040.420.04=0.008
⋯ \cdots
θ n = θ n − 1 − α ∗ J ′ ( θ n − 1 ) \theta_n=\theta_{n-1}-\alpha*J^\prime(\theta_{n-1}) θn=θn1αJ(θn1)

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(21,23)=(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(20.8,22.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(20.64,21.92)=(0.512,1.536)
⋯ \cdots
θ n = θ n − 1 − α ∇ J ( θ n − 1 ) \theta_n=\theta_{n-1}-\alpha\nabla J(\theta_{n-1}) θn=θn1αJ(θn1)

三.梯度下降三兄弟(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=(x02)=102=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=100.18=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.18=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αdx43dy2.090.10.092.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.10.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=21x22x+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=(x10)2+(y10)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 x0z=2(x010)=210=20,y0z=2(y010)=210=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αx0z=200.120=18,y1=y0αy0z=200.120=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=αx0z=0.120=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αx12z10.030.10.0610.02,y13=y12αy12z10.030.10.0610.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=αx12z=0.10.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即可。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值