深刻浅出--梯度降低法及其实现

  • 梯度降低的场景假设
  • 梯度
  • 梯度降低算法的数学解释
  • 梯度降低算法的实例
  • 梯度降低算法的实现
  • Further reading

本文将从一个下山的场景开始,先提出梯度降低算法的基本思想,进而从数学上解释梯度降低算法的原理,最后实现一个简单的梯度降低算法的实例!python

梯度降低的场景假设

梯度降低法的基本思想能够类比为一个下山的过程。假设这样一个场景:一我的被困在山上,须要从山上下来(i.e. 找到山的最低点,也就是山谷)。但此时山上的浓雾很大,致使可视度很低。所以,下山的路径就没法肯定,他必须利用本身周围的信息去找到下山的路径。这个时候,他就能够利用梯度降低算法来帮助本身下山。具体来讲就是,以他当前的所处的位置为基准,寻找这个位置最陡峭的地方,而后朝着山的高度降低的地方走,同理,若是咱们的目标是上山,也就是爬到山顶,那么此时应该是朝着最陡峭的方向往上走。而后每走一段距离,都反复采用同一个方法,最后就能成功的抵达山谷。算法

 

image.pngapi

 

咱们同时能够假设这座山最陡峭的地方是没法经过肉眼立马观察出来的,而是须要一个复杂的工具来测量,同时,这我的此时正好拥有测量出最陡峭方向的能力。因此,此人每走一段距离,都须要一段时间来测量所在位置最陡峭的方向,这是比较耗时的。那么为了在太阳下山以前到达山底,就要尽量的减小测量方向的次数。这是一个两难的选择,若是测量的频繁,能够保证下山的方向是绝对正确的,但又很是耗时,若是测量的过少,又有偏离轨道的风险。因此须要找到一个合适的测量方向的频率,来确保下山的方向不错误,同时又不至于耗时太多!函数

梯度降低

梯度降低的基本过程就和下山的场景很相似。工具


首先,咱们有一个可微分的函数。这个函数就表明着一座山。咱们的目标就是找到这个函数的最小值,也就是山底。根据以前的场景假设,最快的下山的方式就是找到当前位置最陡峭的方向,而后沿着此方向向下走,对应到函数中,就是找到给定点的梯度 ,而后朝着梯度相反的方向,就能让函数值降低的最快!由于梯度的方向就是函数之变化最快的方向(在后面会详细解释)
因此,咱们重复利用这个方法,反复求取梯度,最后就能到达局部的最小值,这就相似于咱们下山的过程。而求取梯度就肯定了最陡峭的方向,也就是场景中测量方向的手段。那么为何梯度的方向就是最陡峭的方向呢?接下来,咱们从微分开始讲起学习

微分

看待微分的意义,能够有不一样的角度,最经常使用的两种是:google

  • 函数图像中,某点的切线的斜率
  • 函数的变化率
    几个微分的例子:spa

     

    image.pngcode

上面的例子都是单变量的微分,当一个函数有多个变量的时候,就有了多变量的微分,即分别对每一个变量进行求微分orm

 

image.png

梯度

梯度实际上就是多变量微分的通常化。
下面这个例子:

 

image.png

咱们能够看到,梯度就是分别对每一个变量进行微分,而后用逗号分割开,梯度是用<>包括起来,说明梯度其实一个向量。

梯度是微积分中一个很重要的概念,以前提到过梯度的意义

  • 在单变量的函数中,梯度其实就是函数的微分,表明着函数在某个给定点的切线的斜率
  • 在多变量函数中,梯度是一个向量,向量有方向,梯度的方向就指出了函数在给定点的上升最快的方向

这也就说明了为何咱们须要想方设法的求取梯度!咱们须要到达山底,就须要在每一步观测到此时最陡峭的地方,梯度就恰巧告诉了咱们这个方向。梯度的方向是函数在给定点上升最快的方向,那么梯度的反方向就是函数在给定点降低最快的方向,这正是咱们所须要的。因此咱们只要沿着梯度的方向一直走,就能走到局部的最低点!

 

image.png

梯度降低算法的数学解释

上面咱们花了大量的篇幅介绍梯度降低算法的基本思想和场景假设,以及梯度的概念和思想。下面咱们就开始从数学上解释梯度降低算法的计算过程和思想!

 

image.png

 

此公式的意义是:J是关于Θ的一个函数,咱们当前所处的位置为Θ0点,要从这个点走到J的最小值点,也就是山底。首先咱们先肯定前进的方向,也就是梯度的反向,而后走一段距离的步长,也就是α,走完这个段步长,就到达了Θ1这个点!

 

image.png

下面就这个公式的几个常见的疑问:

  • α是什么含义?
    α在梯度降低算法中被称做为学习率或者步长,意味着咱们能够经过α来控制每一步走的距离,以保证不要步子跨的太大扯着蛋,哈哈,其实就是不要走太快,错过了最低点。同时也要保证不要走的太慢,致使太阳下山了,尚未走到山下。因此α的选择在梯度降低法中每每是很重要的!α不能太大也不能过小,过小的话,可能致使迟迟走不到最低点,太大的话,会致使错过最低点!

image.png

  • 为何要梯度要乘以一个负号?
    梯度前加一个负号,就意味着朝着梯度相反的方向前进!咱们在前文提到,梯度的方向实际就是函数在此点上升最快的方向!而咱们须要朝着降低最快的方向走,天然就是负的梯度的方向,因此此处须要加上负号

梯度降低算法的实例

咱们已经基本了解了梯度降低算法的计算过程,那么咱们就来看几个梯度降低算法的小实例,首先从单变量的函数开始

单变量函数的梯度降低

咱们假设有一个单变量的函数

 

image.png

 

函数的微分

 

image.png


初始化,起点为

image.png

 

学习率为

 

image.png


根据梯度降低的计算公式

image.png


咱们开始进行梯度降低的迭代计算过程:

image.png


如图,通过四次的运算,也就是走了四步,基本就抵达了函数的最低点,也就是山底

image.png

多变量函数的梯度降低

咱们假设有一个目标函数

 

image.png

 

如今要经过梯度降低法计算这个函数的最小值。咱们经过观察就能发现最小值其实就是 (0,0)点。可是接下来,咱们会从梯度降低算法开始一步步计算到这个最小值!
咱们假设初始的起点为:

 

image.png


初始的学习率为:

image.png

 

函数的梯度为:

 

image.png


进行屡次迭代:

image.png


咱们发现,已经基本靠近函数的最小值点

image.png

梯度降低算法的实现

下面咱们将用python实现一个简单的梯度降低算法。场景是一个简单的线性回归的例子:假设如今咱们有一系列的点,以下图所示

image.png

 

咱们将用梯度降低法来拟合出这条直线!

首先,咱们须要定义一个代价函数,在此咱们选用均方偏差代价函数

image.png

 

此公示中

  • m是数据集中点的个数
  • ½是一个常量,这样是为了在求梯度的时候,二次方乘下来就和这里的½抵消了,天然就没有多余的常数系数,方便后续的计算,同时对结果不会有影响
  • y 是数据集中每一个点的真实y坐标的值
  • h 是咱们的预测函数,根据每个输入x,根据Θ 计算获得预测的y值,即

     

    image.png

咱们能够根据代价函数看到,代价函数中的变量有两个,因此是一个多变量的梯度降低问题,求解出代价函数的梯度,也就是分别对两个变量进行微分

 

image.png

明确了代价函数和梯度,以及预测的函数形式。咱们就能够开始编写代码了。但在这以前,须要说明一点,就是为了方便代码的编写,咱们会将全部的公式都转换为矩阵的形式,python中计算矩阵是很是方便的,同时代码也会变得很是的简洁。

为了转换为矩阵的计算,咱们观察到预测函数的形式

 

image.png

 

咱们有两个变量,为了对这个公式进行矩阵化,咱们能够给每个点x增长一维,这一维的值固定为1,这一维将会乘到Θ0上。这样就方便咱们统一矩阵化的计算

 

image.png

而后咱们将代价函数和梯度转化为矩阵向量相乘的形式

 

image.png

coding time

首先,咱们须要定义数据集和学习率

import numpy as np

# Size of the points dataset.
m = 20

# Points x-coordinate and dummy value (x0, x1).
X0 = np.ones((m, 1))
X1 = np.arange(1, m+1).reshape(m, 1)
X = np.hstack((X0, X1))

# Points y-coordinate
y = np.array([
    3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,
    11, 13, 13, 16, 17, 18, 17, 19, 21
]).reshape(m, 1)

# The Learning Rate alpha.
alpha = 0.01

接下来咱们以矩阵向量的形式定义代价函数和代价函数的梯度

def error_function(theta, X, y):
    '''Error function J definition.'''
    diff = np.dot(X, theta) - y
    return (1./2*m) * np.dot(np.transpose(diff), diff)

def gradient_function(theta, X, y):
    '''Gradient of the function J definition.'''
    diff = np.dot(X, theta) - y
    return (1./m) * np.dot(np.transpose(X), diff)

最后就是算法的核心部分,梯度降低迭代计算

def gradient_descent(X, y, alpha):
    '''Perform gradient descent.'''
    theta = np.array([1, 1]).reshape(2, 1)
    gradient = gradient_function(theta, X, y)
    while not np.all(np.absolute(gradient) <= 1e-5):
        theta = theta - alpha * gradient
        gradient = gradient_function(theta, X, y)
    return theta

当梯度小于1e-5时,说明已经进入了比较平滑的状态,相似于山谷的状态,这时候再继续迭代效果也不大了,因此这个时候能够退出循环!

完整的代码以下

import numpy as np

# Size of the points dataset.
m = 20

# Points x-coordinate and dummy value (x0, x1).
X0 = np.ones((m, 1))
X1 = np.arange(1, m+1).reshape(m, 1)
X = np.hstack((X0, X1))

# Points y-coordinate
y = np.array([
    3, 4, 5, 5, 2, 4, 7, 8, 11, 8, 12,
    11, 13, 13, 16, 17, 18, 17, 19, 21
]).reshape(m, 1)

# The Learning Rate alpha.
alpha = 0.01

def error_function(theta, X, y):
    '''Error function J definition.'''
    diff = np.dot(X, theta) - y
    return (1./2*m) * np.dot(np.transpose(diff), diff)

def gradient_function(theta, X, y):
    '''Gradient of the function J definition.'''
    diff = np.dot(X, theta) - y
    return (1./m) * np.dot(np.transpose(X), diff)

def gradient_descent(X, y, alpha):
    '''Perform gradient descent.'''
    theta = np.array([1, 1]).reshape(2, 1)
    gradient = gradient_function(theta, X, y)
    while not np.all(np.absolute(gradient) <= 1e-5):
        theta = theta - alpha * gradient
        gradient = gradient_function(theta, X, y)
    return theta

optimal = gradient_descent(X, y, alpha)
print('optimal:', optimal)
print('error function:', error_function(optimal, X, y)[0,0])

运行代码,计算获得的结果以下

 

image.png

所拟合出的直线以下

 

image.png

小结

至此,咱们就基本介绍完了梯度降低法的基本思想和算法流程,而且用python实现了一个简单的梯度降低算法拟合直线的案例!
最后,咱们回到文章开头所提出的场景假设:
这个下山的人实际上就表明了反向传播算法,下山的路径其实就表明着算法中一直在寻找的参数Θ,山上当前点的最陡峭的方向实际上就是代价函数在这一点的梯度方向,场景中观测最陡峭方向所用的工具就是微分 。在下一次观测以前的时间就是有咱们算法中的学习率α所定义的。
能够看到场景假设和梯度降低算法很好的完成了对应!

Further reading

做者:六尺账篷 连接:https://www.jianshu.com/p/c7e642877b0e 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。

相关文章
相关标签/搜索