前言
为什么会有这篇文章?梯度下降,是每个入门DL/ML的童鞋避不开的一个坎~,说其是深度学习中的底层原理,也一点不为过,所谓底层原理,就是:你不把它搞懂,也不影响你学习深度学习,不影响你跑模型,开个玩笑:)不过,虽然是底层原理,也就是个披着狼皮的羊,很容易搞定的~ 本文的主要内容:
- 1.什么是梯度下降? ——用简单的叙述让你了解梯度下降,以及其和损失函数、深度学习的关系;
- 2.梯度下降的定义——硬核的梯度下降、方向导数的数学公式和定义;
- 3.梯度下降的代码实现——代码实现和matplotlib图表展示。
原文发表于语雀:
【深度学习理论】一文搞透梯度下降Gradient descent · 语雀www.yuque.com1.什么是梯度下降 ?
梯度下降Gradient descent 梯度下降,核心就在于两个字:梯度,下降就表示梯度减小/降低的意思。那么问题来了:【梯度】明明是个数学概念,怎么和深度学习挂上了钩?其实,关键在于——损失函数loss function。一句话:深度学习/机器学习中通常通过损失函数来评价模型的效果(量化模型预测值和真实值直接的差异),而损失函数通常可以采用梯度下降的方式进行优化迭代。 那么,问题又来了,为神马损失函数通常用梯度下降的方式进行优化迭代? 因为天下武功唯快不破,梯度方向代表了损失函数变化最快的方向(类似于函数导数变化率最大的方向)。如果我们需要增加loss损失函数,则只需要使损失函数朝着梯度上升的方向优化;but我们希望loss不断变小下降,so,我们需要的是梯度下降,我们需要将其向梯度下降的方向来优化。
有点懵?看下图
以二元函数为例,假设我的神经网络模型里只有两个参数:
BTW,如果你是在爬山,而且体力好,喜爱攀岩运动,你想最快速度爬上山顶,那么你应该往梯度下降的反方向——梯度上升的方向去爬:)
再多说一句,一元函数,如熟悉的抛物线:
2.梯度下降的定义
2.1 梯度
梯度这个概念不仅存在于数学,还常用于表示物理参数的中表示,如果参数为速度、浓度、温度或空间,则分别称为速度梯度、浓度梯度、温度梯度或空间梯度。深度学习中我们重点关注的是数学中的梯度,尤其是向量微积分中的梯度。在向量微积分中,标量场的梯度是一个向量场。标量场中某一点上的梯度指向标量场增长最快的方向,梯度的长度是这个最大的变化率。更严格的说,从欧几里得空间Rn到R的函数的梯度是在Rn某一点最佳的线性近似。在这个意义上,梯度是雅可比矩阵的特殊情况。
通俗理解
梯度也是个矢量,而且是建立在方向导数上的一个概念。矢量即有大小有方向的向量。梯度的大小,即梯度的模长;梯度的方向,等于该点处最大方向导数的方向,简单说就是该点处梯度的方向=方向导数的方向,而此方向使得该点处方向导数取得最大值。
梯度的意义:
其表示某一函数在该点沿着某方向的方向导数取得最大值,通俗理解梯度就是方向导数中最大的那个!意义:函数在该点处(此梯度的方向)变化最快,变化率最大(模长最大)。 这一点是不是很像导数?在一元函数y = f(x)中,我们求导数,导数反应了函数变化速度,其值即函数变化率;在多元函数中,我们同样需要求导,此时的导数和方向向量相结合,就相当于求梯度。
如上图所示,在二元函数z = f(x,y)中,过函数某一点的切平面上存在向上,向下的黑色箭头。按定义,向上的箭头,即可以表示梯度,因为沿着此方向,方向导数取得了最大值,函数变化率最大;向下的箭头则是相反,代表函数下降的最快方向。 我们在深度学习中,求梯度的意义在于梯度下降,即找到梯度的反方向(函数下降最快的方向)使得loss损失函数快速下降。
定义
百度百科
梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
二元函数中的定义
设二元函数
该函数就称为函数
,即有:
其中
设
由于当方向l与梯度方向一致时,有
三元函数中的定义
设三元函数
为函数
其中
同样,该梯度方向与取得最大方向导数的方向一致,而它的模为方向导数的最大值
wiki
标量函数 f(x, x, x, ..., x) 的梯度,表示为∇f ,其中∇(nabla)表示向量微分算子,梯度也可用grad f 来表示。f_的梯度定义为唯一向量场,其和任意向量v在每个点x处的点积都是f沿着v向量的方向导数。
三元函数中的定义
在具有欧几里得度量的三维笛卡尔坐标系中,如果存在梯度,则由下式给出:
例如,函数
的梯度是
在某些应用中,习惯上将梯度表示为直角坐标系中其组成部分的行向量或列向量。
某一点的梯度其大小等于各个方向导数的模长,其方向等于各个单位方向向量的叠加。在二元函数中,梯度大小即即沿x和y方向的
一元函数中,就一个变量x,函数在某点的梯度即函数在该点处的方向导数沿着该方向取得最大值,说白了,在一个特定的点上,可以引出无数条带方向的线(切线),构成矢量。这无数条线中,必然包含一条切线,这条切线方向的方向导数在众多切线中,数值最大。 众多方向的切线中,方向导数最大的,称为梯度。
2.2 方向导数
通俗理解
方向导数首先是个矢量,和方向相关,那么是不是从函数内任意一点出发,指向任意方向的一条射线都算方向导数?答案是NO,因为方向不能随便指,而是有限定范围的。 在多元微分函数中,如果一个函数在某点的邻域范围内可微(可导),则从该点出发指向领域范围内任意方向都存在方向导数,其意义反应了函数在可微区间内的瞬时变化率。 例如,在2元函数中,如果平面在某点处可微,则过该点必然存在一个切平面,则从该点出发的平面内任意方向都存在方向导数。
同样的道理,在一元函数y=f(x)中,方向导数其实就退化成了斜率,即函数的导数。
定义
wiki
在数学中,多元微分函数在给定点x上沿着给定向量v的方向导数直观地表示了函数的瞬时变化率,以v指定的速度在x中移动。因此,它概况了偏导数的概念(沿着曲线坐标轴的某个方向存在变化率而在其他坐标轴方向是恒定的) 标量函数
沿着向量
的方向导数定义为:
此定义在具有广泛的普适性,例如单位向量的范数没定义的情况下。如果函数在x处是可微的,则方向导数沿着任何向量v存在,并且:
等式左边表示方向导数,右边
欧几里得空间中,向量点积:
如果两向量正交,点积为0。
百度百科
在函数定义域的内点,对某一方向求导得到的导数。一般为二元函数和三元函数的方向导数,方向导数可分为沿直线方向和沿曲线方向的方向导数。
以二元函数为例 设函数z=f(x,y) 在点P(x,y)的某一邻域U(P)内有定义,自点P引射线 l,自x轴的正向到射线l的转角为
存在,则称此极限值为
2.3 概念补充
2.3.1 可微、可导
可微 == 可导,互为充要条件,在一元函数/多元函数里均适用。在一元函数里(如:y = kx + b),在某点处的导数即 = 其斜率k;多元函数中导数由各个方向的偏导组成。可微,全微分存在,对于二元函数,在某点处可微 == 存在切平面 ,而方向导数即存在于此切平面之上
2.3.2 偏导数
偏导数其实和导数一样,仅仅是进行了一个扩展,从原来的一元函数扩展到多元函数,这时就有了偏导数的概念。偏导数和导数一样都是表示切线的斜率。偏导数只是针对变元(这里的变元是指多元函数中的某一元)的导数。
3.梯度下降的代码实现
这里用一个一元函数模拟梯度下降的代码实现: 代码中x表示一个变量,y表示其损失函数,x和y满足的函数关系是:
图像表示如下:
迭代过程如下:
迭代次数:1; x:3.320000; y6.382400
迭代次数:2; x:2.856000; y4.444736
迭代次数:3; x:2.484800; y3.204631
迭代次数:4; x:2.187840; y2.410964
迭代次数:5; x:1.950272; y1.903017
迭代次数:6; x:1.760218; y1.577931
迭代次数:7; x:1.608174; y1.369876
迭代次数:8; x:1.486539; y1.236720
迭代次数:9; x:1.389231; y1.151501
迭代次数:10; x:1.311385; y1.096961
迭代次数:11; x:1.249108; y1.062055
迭代次数:12; x:1.199286; y1.039715
迭代次数:13; x:1.159429; y1.025418
迭代次数:14; x:1.127543; y1.016267
迭代次数:15; x:1.102035; y1.010411
迭代次数:16; x:1.081628; y1.006663
迭代次数:17; x:1.065302; y1.004264
迭代次数:18; x:1.052242; y1.002729
迭代次数:19; x:1.041793; y1.001747
迭代次数:20; x:1.033435; y1.001118
迭代次数:21; x:1.026748; y1.000715
迭代次数:22; x:1.021398; y1.000458
迭代次数:23; x:1.017119; y1.000293
迭代次数:24; x:1.013695; y1.000188
迭代次数:25; x:1.010956; y1.000120
迭代次数:26; x:1.008765; y1.000077
迭代次数:27; x:1.007012; y1.000049
迭代次数:28; x:1.005609; y1.000031
迭代次数:29; x:1.004488; y1.000020
迭代次数:30; x:1.003590; y1.000013
迭代次数:31; x:1.002872; y1.000008
迭代次数:32; x:1.002298; y1.000005
迭代次数:33; x:1.001838; y1.000003
迭代次数:34; x:1.001470; y1.000002
迭代次数:35; x:1.001176; y1.000001
完整代码如下:
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return (x-1)**2 + 1
def df(x):
return 2*x - 2
# 设置初始参数
x = 3.9
y = f(x)
lr = 0.1 # 学习率
# 设置数组,存放每次迭代过程中的x和y值,用于画图显示
x_temp = [x]
y_temp = [y]
# 设定当迭代次数>1000次或每次迭代y减少的值<0.000001时,停止迭代
iter=0
delta_y=y
while iter<1000 and delta_y > 0.000001:
dx = df(x)
x = x - lr*dx
delta_y = abs(y - f(x))
y = f(x)
x_temp.append(x)
y_temp.append(y)
iter+=1
print('迭代次数:%d; x:%f; y%f' % (iter, x, y))
# pyplot绘图显示迭代过程中的x,y坐标点
X = np.arange(-2,4,0.05)
Y = f(X)
Y = np.array(Y)
plt.plot(X,Y)
plt.scatter(x_temp,y_temp)
plt.title("$y = (x - 1)^2 + 1$")
plt.show()