导数与梯度下降算法
什么是导数?
如果你不知道什么是导数,最好先去看3BLUE1BROWN大神的视频吧,这个视频的质量非常高 ,看过以后都说好。
微积分合集 传送门
个人总结:
在这里我希望你能建立起来一种直觉,微积分描述的就是X的一个微小变化量对结果Y的影响。
这句话请你一定要牢牢记住,整个微积分的精髓就是这样了。
怎么求函数的最小值?
利用刚才建立起来的直觉,我们去求函数的最小值。
BTW:学过微积分你应该知道通过二阶导数与一阶导数可以获得极值的解析解。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。
如果你没有学过微积分,就当什么都没有发生。
我们先用https://zh.numberempire.com/graphingcalculator.php 来画出函数图像增加你的直观感觉!
如果此时把一颗小球放在函数图像的曲线上,想想这颗小球如何滚到最低点呢?
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5 , 30)
y = (x) ** 2
plt.plot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
def fx():
"""把刚才画图的过程定义成一个函数,便于后面调用"""
x = np.linspace(-5, 5 , 30)
y = (x) ** 2
plt.plot(x, y)
plt.xlabel("X")
plt.ylabel("Y")
我们先想像一个物理画面,一颗小球沿着函数山谷向下滚动,它在什么时间停止不动?
这里我们思考几个问题:
小球的起点在哪里?
小球每次滚动的距离由什么决定?
小球在什么时候停止?
我们把这三个问题,物理化,想象一下
1 小球的起点我们可以设定为函数上的任意一点,只是这个点离最低距离远近,
决定了获得最小值的时间,在深度学习中,我们把它称为“收敛”时间。
2 小球每次滚动的距离由两个因素决定一个是小球的重量,一个是函数的“坡度”,
很显然在重量一致的情况下,坡度越陡峭,它滚动的距离就越远。
3 小球在最低点停止的时候,用数学表示就是上次移动的距离,和这次移动的距离非常小,
直到这个距离为0的时候,小球就静止了。
我们把上面的物理描述,换成数学语言,伪码一下就是:
|
设定初始点x = -4 #任意一点 。
设定小球的重量 = 0.1 #在深度学习里面通常称为 (读作"eta " ) 。
函数在某一点上的“坡度”也就是该点的导数"derivate" ,它和""相乘决定了小球本次移动的距离 。
小球上次与本次 在y轴上移动的距离如果小于 (读作"epsilon" )我们就认为它是静止的 。
有了上面的认知,接下来就是一个循环问题了:
whiel True:
求该点的导数
x = x - 移动的距离
为了画图,我们可以把x和gradient都记录到列表中
最后如果当小球在y方向上的移动距离和上次移动的距离小于 epsilon ,就可以结束循环了
实际的代码如下:
def derivate_x(x):
return 2*x
def fun_x(x):
return x**2
x = -4
eta = 0.1
epsilon = 1e-5
x_list = [x]
gradient_list=[]
while True:
gradient = derivate_x(x)
last_x = x
x = x - eta*gradient
x_list.append(x)
gradient_list.append(gradient)
if (abs(fun_x(last_x) - fun_x(x)) < epsilon):
break
x_list
[-4,
-3.2,
-2.56,
-2.048,
-1.6384,
-1.31072,
-1.0485760000000002,
-0.8388608000000002,
-0.6710886400000001,
-0.5368709120000001,
-0.4294967296000001,
-0.3435973836800001,
-0.27487790694400005,
-0.21990232555520003,
-0.17592186044416003,
-0.140737488355328,
-0.11258999068426241,
-0.09007199254740993,
-0.07205759403792794,
-0.057646075230342354,
-0.04611686018427388,
-0.03689348814741911,
-0.029514790517935284,
-0.02361183241434823,
-0.018889465931478583,
-0.015111572745182867,
-0.012089258196146294,
-0.009671406556917036,
-0.007737125245533628,
-0.006189700196426903,
-0.004951760157141522,
-0.003961408125713218]
gradient_list
[-8,
-6.4,
-5.12,
-4.096,
-3.2768,
-2.62144,
-2.0971520000000003,
-1.6777216000000004,
-1.3421772800000003,
-1.0737418240000003,
-0.8589934592000003,
-0.6871947673600002,
-0.5497558138880001,
-0.43980465111040007,
-0.35184372088832006,
-0.281474976710656,
-0.22517998136852482,
-0.18014398509481985,
-0.14411518807585588,
-0.11529215046068471,
-0.09223372036854777,
-0.07378697629483821,
-0.05902958103587057,
-0.04722366482869646,
-0.037778931862957166,
-0.030223145490365734,
-0.024178516392292588,
-0.01934281311383407,
-0.015474250491067256,
-0.012379400392853806,
-0.009903520314283045]
plt.plot(np.array(x_list),fun_x(np.array(x_list)),color='r',marker='*') #绘制x的轨迹
fx()
plt.show()
这里思考一下,
为什么小球在后来每次在x轴上移动的距离原来越短?
因为“坡度”越来越平缓了吗!
平缓的数学意思就是gradient越来越小了。
改变eta与epsilon
x = -4
eta = 0.8
epsilon = 1e-2
x_list = [x]
gradient_list=[]
while True:
gradient = derivate_x(x)
last_x = x
x = x - eta*gradient
x_list.append(x)
gradient_list.append(gradient)
if (abs(fun_x(last_x) - fun_x(x)) < epsilon):
break
plt.plot(np.array(x_list),fun_x(np.array(x_list)),color='r',marker='*')#绘制x的轨迹
fx()
plt.show()
gradient_list
[-8,
4.800000000000001,
-2.880000000000001,
1.7280000000000006,
-1.0368000000000004,
0.6220800000000004,
-0.37324800000000025,
0.22394880000000017]
我们修改了eta的值,还有epsilon的值,对应图像你能想明白为什么这个样子吗?
更改了eta的值,相当于小球在开始有一个很大的重力加速度,一下子滚到了对面的山坡。这个很符合物理直觉。
然后小球继续向下,注意此时的小球在对面的山坡,所以你的导数是一个正数。还记得导数代表的就是斜率吗?如果忘记了也没有关系。
物理意义就是如果斜率为正,代表x变大,y也变大,那x应该往小的方向走,也就是x-eta*gradient。那如果导数为负,就意味着
x变大,y就会变小,所以x应该往大的方向走,x - eta * gradient 此时gradient是一个负数,所以x会变大。
这里大家可以自己去测试一下,如果eta很大,epsilon很小会发生什么?
梯度下降算法
接下来我们放到一个更复杂的环境中,来讨论这个求函数最小值的问题,现在我们把物理场景换到一个小人下山。
图片来源https://www.analyticsvidhya.com/blog/2017/03/introduction-to-gradient-descent-algorithm-along-its-variants/
说复杂,是因为此时下山的路有多条,我们稍微简化一下,假设只有x,y两个方向下山的路,你该怎么选择呢?
这相当于你建立了一个x,y,z的坐标系,z代表是海拔高度,z的变化受,x,y影响,也就是当你往x方向走极小距离,会有对z有一个影响,
对照刚才的讲解,你知道如果只有x 你可以表示为x - eta * x_gradient 。同理z也收y的影响,y方向的移动可以表示为y - eta * y_gradient。
那z就是因变量,x,y就是自变量,用函数表示就是z=f(x, y)。
在下图中x被替换成,y被替换成, z被表示成J(, )
既然函数中有了两个方向,那此时的_gradient就要换个名字叫做针对J(, )的偏导数, 此时的偏导数的物理意义,就是必须指向山的最低点方向,也就是海拔最低的方向。
同理_gradient也要改名字为针对J(, )的偏导数。
你记住他的物理意义就好。
这种物理上沿着山坡下降最快的方向寻找函数最小值的办法,就叫做梯度下降算法。
梯度的定义就是在物理上指向下降速度最快的方向的向量。
它由 与 偏导组成。
所以就这样,你现在问自己一个问题,在代表多维空间的函数上如何找到最低点呢?
物理上可以通过放一个小球来实现:
1 小球该往那个方向滚,由什么来决定呢?
2 小球在什么时候停下来?停下来的条件是什么?
3 好吧,知道上面两个就够了!