基于Python的深度学习理论与实现(P6——导数、偏导数与梯度)

导数

 一个函数对其某个参数的导数,就是这个函数的函数值在这个参数极微(即无穷小)变化下的发生的变化量。(虽然很绕口,但是本人认为比数学上传统的定义更好理解)导数的向前差分数学形式可以表示为:
d f ( x ) d x = lim ⁡ h → 0 f ( x + h ) − f ( x ) h \frac{df(x)}{dx} = \lim_{h\rightarrow 0}\frac{f(x+h)-f(x)}{h} dxdf(x)=h0limhf(x+h)f(x)
或使用中心差分表示为:
d f ( x ) d x = lim ⁡ h → 0 f ( x + h ) − f ( x − h ) 2 h \frac{df(x)}{dx} = \lim_{h\rightarrow 0}\frac{f(x+h)-f(x-h)}{2h} dxdf(x)=h0lim2hf(x+h)f(xh)
 在程序中,我们可以使用数值微分的方式实现函数求导。数值微分(numerical differentiation)指的是根据函数在其一些离散点的函数值,推算它在某点的导数或高阶导数的近似值的方法。(关于数值导数的方法是单独的一门课程,也是高等数学的内容,这里不再展开,好像因为自己的基础稀烂,也展不太开QAQ),以下是数值微分向前差分实现,其中numerical_diff函数是数值微分的函数,f(x)为目标函数。

def numerical_diff(f,x):
    h = 1e-10
    return(f(x+h)-f(x))/h

def f(x):
    return x**2 + 2*x + 5

if __name__ == "__main__":
    print(numerical_diff(f,1))
    #输出结果为:4.000000330961484

 输出结果是带有误差的,这个误差的来源是多方面的,除去计算机和语言表示和计算特性外,我们在实现数值微分的时候,使用了1e-10这个值作为极小值,即用这个值作为参数的极小变化,但是这样就反而产生了舍入误差,指的是,因省略了小数的精细部分的数值,而造成最终结果上的误差。所以我们采用中心差分来实现数值微分是更为合理的:

def numerical_diff(f,x):
    h=1e-10
    return (f(x+h)-f(x-h))/(2*h)

def f(x):
    return x**2 + 2*x + 5

if __name__ == "__main__":
    print(numerical_diff(f,1))

 我们使用以上数值微分的中心差分表示法来绘制函数及函数在x=5出的导数图像(即在x=5出的切线):

import numpy as np
import matplotlib.pyplot as plt
'''
def numerical_diff(f,x):
    h = 1e-10
    return(f(x+h)-f(x))/h
'''

def numerical_diff(f,x):
    h=1e-10
    return (f(x+h)-f(x-h))/(2*h)

def f(x):
    return x**2 + 2*x + 5

def tangent_line(f, x):
    d = numerical_diff(f, x)
    print(d)
    y = f(x) - d*x
    return lambda t: d*t + y

if __name__ == "__main__":
    x = np.arange(0.0,20.0,0.1)
    y = f(x)
    plt.xlabel("x")
    plt.ylabel("f(x)")
    plt.plot(x,y)
    tf = tangent_line(f, 5)
    y2 = tf(x)
    plt.plot(x, y2)
    y3 = tangent_line(f,10)
    plt.show()

绘制的图像为:
在这里插入图片描述

偏导数

 当一个函数是多元函数,导数的概念就会相应的扩大,即函数相对于每个参数都会有一个导数。比如:
f ( x 0 , x 1 ) = x 1 2 + x 2 2 f(x_0,x_1) = x_1^2 + x_2^2 f(x0,x1)=x12+x22
可以用Python实现为:

def f_2d(x):
    return x[0]**2 + x[1]**2
    #或者根据numpy库的广播性质,直接写成np.sum(x**2)

 这个函数有两个变量,对这个函数求导要区分是对哪一个变量进行的求导,我们将对其中某个变量求导的导数称为偏导数,记为:
∂ f ∂ x 0 、 ∂ f ∂ x 1 \frac{\partial f}{\partial x_0} 、\frac{\partial f}{\partial x_1} x0fx1f
 偏导数和单变量导数一样,都是在计算函数相对某个变量产生微小变化时的变化,只是偏导数需要将多个变量中的某一个变量设定为目标变量,同时将其他变量设定为固定值,比如,我们计算x0=3,x1=4时,关于x0的偏导数:

def func_tmp(x0):
	return x0*x0+4.0**2.0
numerical_diff(func_tmp,3.0)

 即,在求关于x0的偏导数时,我们假设变量x1为固定值,此时产生了一个临时的单变量函数,再对这个单变量函数求关于x0的导数,结果就是关于x0的偏导数。

梯度

 如果函数是一个从n维变量到1维变量的映射,那么函数的梯度定义为:
▽ f ( x ) = ( ∂ f ∂ x 1 ( x ) . . . ∂ f ∂ x n ( x ) ) \bigtriangledown f(\mathbf{x}) = \begin{pmatrix} \frac{\partial f}{\partial x_1}(\mathbf{x})\\ . \\. \\. \\ \frac{\partial f}{\partial x_n}(\mathbf{x}) \end{pmatrix} f(x)=x1f(x)...xnf(x)
 梯度在形式上是函数对每一个变量的偏导数汇总起来的向量,如果绘制一个函数的梯度向量,其起点为x,则梯度表示为一个向量场,梯度的方向上,也是x到f(x)的合理映射。
 在最优化理论中,梯度具有重要意义:
如果一个函数f在x0处的梯度不是零向量,那么这个梯度向量与水平集中的任意一条经过x0处的光滑曲线的切向量正交,因此,一个实值可微函数在某点处的函数值增加最快的方向正交于经过该点的函数水平集,也就是说,在梯度方向上自变量的细微变动,所导致的函数值的增加幅度要超过其他任意方向。
 由上述理论可知,梯度负方向就是函数在x处最快的方向。
 但是在继续梯度和深度学习的关系前一定要注意的是,梯度表示的是当前的x(x是一个多维向量,和梯度同维)处,函数值最快的变化方向,而不是函数最小值或者说我们真正需要的最终导向,它只能给我们当前迭代过程中作为向导,却并不能保证指引我们得到正确的答案。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值