[数值计算-19]:万能的任意函数的数值求导数方法

作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:https://blog.csdn.net/HiWangWenBing/article/details/120378620


目录

第1章 前言

第2章 导数的数值定义

第3章 导数的数值计算

3.1 定义支持任意一元函数的求导的函数

3.2 案例1:一元函数1

3.3 案例2:一元函数2

3.3 多元函数求偏导




第1章 前言

张量运算的前向运算与参数的自动反向求导都是深度学习最核心的基础。

前向运算其实直观,因为有前向运算的函数,一次带入函数的参数,就可以获得前向运算的数值。

如 loss =(f(x, w1, b1, w2, b2) - y) = (w2 * (w1 * x0 + b1) - b2 - y0) *2

在上述函数 中,假定(x0,y0)是一个已知的样本值,而w1,b1, w2, b2是自变量。

如果给定w1,b1, w2, b2的一个数值点(w1_0,b1_0, w2_0, b2_0)),很容易获得前向运算的值。

现在有一个问题:

(1)如何获得参数w1,b1, w2, b2在点点(w1_0,b1_0, w2_0, b2_0))的导数值呢?

(2)如果f()是任意的函数:自变量的个数任意,函数的表达式任意,如何获得每个参数在任意点处到偏导数呢?

(3)深度学习框架在反向求导的时候,为什么能够对任意loss函数进行反向求导呢?

(4)我们知道,如果获得某一函数的导函数的表达式,是很容易通过前向运算的方式,根据导函数获取计算出导数在任意一点的值,只就是某一函数在任意一点处的导数值,但如果无法获取导函数,有如何求导呢?

实际上,上述问题的核心是问题是:是否有一种简便的、万能的方法、进行数值求导?

答案是:有!

第2章 导数的数值定义

先回到导数的定义上,从中来发现解决问题的方法。

 上述表达是的核心思想是:要计算某一个点处的导数(斜率),可以用该点(x0,y0) 与一个与之极其相邻处的点(x0-p, f(x0-p) 这两个点斜率作为该点(x0,y0)的近似值。当p足够小时,近似度越高。

 这就是数值求导的核心思想。

第3章 导数的数值计算

3.1 定义支持任意一元函数的求导的函数

import numpy as np    
import math


#定义对任何函数
# 未优化函数:x+p, x
def approximate_derivative(func, x, p=1e-3):
    return ((func(x + p) - func(x)) / p)

# 优化后的函数:x+p,x-p
def approximate_derivative_opt(func, x, p=1e-3):
    return ((func(x + p) - func(x - p)) / (2*p))

其中, 影响求导精度最重要的参数就是p,即delta_x。

3.2 案例1:一元函数1

(0)定义一元函数1

# 原函数
def f1(x):
    return (x**2 + x**1 + 1)

# 导函数
def f1_derivative(x):
    return (2*x + 1)

(1)求样本点x=1.0处的导数

x = 1.0
p = 1e-6
f = f1

y_x = f(x)
deriv_x_p0 = f1_derivative(x)
deriv_x_p1 = approximate_derivative(f, x)
deriv_x_p2 = approximate_derivative(f, x, p)
deriv_x_p3 = approximate_derivative_opt(f, x, p)
print("前向运算值   =", y_x)
print("导函数求导p0 =", deriv_x_p0, "Error = ", deriv_x_p0 - deriv_x_p0)
print("反向导数值p1 =", deriv_x_p1, "Error = ", deriv_x_p1 - deriv_x_p0)
print("反向导数值p2 =", deriv_x_p2, "Error = ", deriv_x_p2 - deriv_x_p0)
print("反向导数值p3 =", deriv_x_p3, "Error = ", deriv_x_p3 - deriv_x_p0)
前向运算值   = 3.0
导函数求导p0 = 3.0 Error =  0.0
反向导数值p1 = 3.0009999999993653 Error =  0.0009999999993652864
反向导数值p2 = 3.000000999620056 Error =  9.99620056063577e-07
反向导数值p3 = 2.9999999999752447 Error =  -2.475530891388189e-11

备注:

  • 优化后的数值求导approximate_derivative_opt()具有更高的精度
  • 在数值求导公式不变的情况下,导数的精度与p的大小密切相关,p越小,精度越高

(2)求样本点x=2.0处的导数

x = 2.0
p = 1e-6
f = f1

y_x = f(x)
deriv_x_p0 = f1_derivative(x)
deriv_x_p1 = approximate_derivative(f, x)
deriv_x_p2 = approximate_derivative(f, x, p)
deriv_x_p3 = approximate_derivative_opt(f, x, p)
print("前向运算值   =", y_x)
print("导函数求导p0 =", deriv_x_p0, "Error = ", deriv_x_p0 - deriv_x_p0)
print("反向导数值p1 =", deriv_x_p1, "Error = ", deriv_x_p1 - deriv_x_p0)
print("反向导数值p2 =", deriv_x_p2, "Error = ", deriv_x_p2 - deriv_x_p0)
print("反向导数值p3 =", deriv_x_p3, "Error = ", deriv_x_p3 - deriv_x_p0)
前向运算值   = 7.0
导函数求导p0 = 5.0 Error =  0.0
反向导数值p1 = 5.001000000000033 Error =  0.0010000000000331966
反向导数值p2 = 5.00000100078779 Error =  1.0007877904172346e-06
反向导数值p3 = 5.000000000254801 Error =  2.5480062504357193e-10

3.3 案例2:一元函数2

(0)定义一元函数2

def f2(x):
    return (x**2 + x**1 + 1 + math.exp(x))

def f2_derivative(x):
    return (2*x + 1 + math.exp(x))

(1)求样本点x=1.0处的导数

x = 1.0
p = 1e-6
f = f2

y_x = f(x)
deriv_x_p0 = f2_derivative(x)
deriv_x_p1 = approximate_derivative(f, x)
deriv_x_p2 = approximate_derivative(f, x, p)
deriv_x_p3 = approximate_derivative_opt(f, x, p)
print("前向运算值   =", y_x)
print("导函数求导p0 =", deriv_x_p0, "Error = ", deriv_x_p0 - deriv_x_p0)
print("反向导数值p1 =", deriv_x_p1, "Error = ", deriv_x_p1 - deriv_x_p0)
print("反向导数值p2 =", deriv_x_p2, "Error = ", deriv_x_p2 - deriv_x_p0)
print("反向导数值p3 =", deriv_x_p3, "Error = ", deriv_x_p3 - deriv_x_p0)
前向运算值   = 5.718281828459045
导函数求导p0 = 5.718281828459045 Error =  0.0
反向导数值p1 = 5.720641422533035 Error =  0.0023595940739902233
反向导数值p2 = 5.71828418749476 Error =  2.359035715215896e-06
反向导数值p3 = 5.718281828492877 Error =  3.383249236321717e-11

(2)求样本点x=2.0处的导数

x = 2.0
p = 1e-6
f = f2

y_x = f(x)
deriv_x_p0 = f2_derivative(x)
deriv_x_p1 = approximate_derivative(f, x)
deriv_x_p2 = approximate_derivative(f, x, p)
deriv_x_p3 = approximate_derivative_opt(f, x, p)
print("前向运算值   =", y_x)
print("导函数求导p0 =", deriv_x_p0, "Error = ", deriv_x_p0 - deriv_x_p0)
print("反向导数值p1 =", deriv_x_p1, "Error = ", deriv_x_p1 - deriv_x_p0)
print("反向导数值p2 =", deriv_x_p2, "Error = ", deriv_x_p2 - deriv_x_p0)
print("反向导数值p3 =", deriv_x_p3, "Error = ", deriv_x_p3 - deriv_x_p0)
前向运算值   = 14.38905609893065
导函数求导p0 = 12.38905609893065 Error =  0.0
反向导数值p1 = 12.393751858796875 Error =  0.004695759866224947
反向导数值p2 = 12.389060794149032 Error =  4.695218381201016e-06
反向导数值p3 = 12.389056099237905 Error =  3.072546661542219e-10

3.3 多元函数求偏导

多元函数与一元函数求导的原理和本质是一样的,在具体实现上,就是定义任意参数的函数。


作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:https://blog.csdn.net/HiWangWenBing/article/details/120378620

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 可以使用matlab中的diff函数来求函数在指定点的数值。具体步骤如下: 1. 定义函数f(x),并将x的取值范围设定为一个向量x。 2. 使用diff函数求出f(x)在x中每个点的一阶导,即df/dx。 3. 根据需要,可以使用diff函数再次求出df/dx在x中每个点的一阶导,即二阶导d2f/dx2。 4. 最后,根据需要,可以使用interp1函数对求得的导进行插值,以得到指定点处的数值。 示例代码如下: % 定义函数f(x) f = @(x) sin(x); % 设定x的取值范围 x = linspace(, pi, 100); % 使用diff函数求出f(x)在x中每个点的一阶导 dfdx = diff(f(x))./diff(x); % 使用interp1函数对dfdx进行插值,以得到指定点处的数值 x = pi/4; % 指定点 dfdx = interp1(x(1:end-1), dfdx, x); % 输出结果 fprintf('f''(%f) = %f\n', x, dfdx); ### 回答2: 在matlab中,我们可以通过一些内置函数来求函数在指定点的数值。具体的步骤如下: 1. 定义函数 首先,我们需要定义要求导函数。可以使用matlab的函数句柄来定义函数,也可以直接在命令窗口中定义匿名函数。例如,我们定义一个函数f(x) = x^2: >> f = @(x) x^2; 2. 指定求导点 然后,我们需要指定要求导的点。可以直接将点的值赋值给一个变量,也可以使用matlab的输入函数来输入值。例如,我们指定求导点为x=2: >> x = 2; 3. 求一阶导 接下来,我们可以使用matlab内置的diff函数来求一阶导,也可以使用gradient函数来求近似的一阶导。例如,使用diff函数求f(x)在x=2处的一阶导: >> diff(f(x)) 或者使用gradient函数,将指定点和函数句柄作为输入: >> gradient(f(x), x) 4. 求高阶导 如果要求高阶导,则可以使用matlab内置的diff函数多次求导。例如,求f(x)在x=2处的二阶导: >> diff(f(x), 2) 5. 数值求导 除了使用解析法求导外,我们还可以使用数值方法来近似求导。matlab内置的numderivative函数可以计算函数在指定点的数值,其输入参函数句柄、求导点、求导和差分步长。例如,使用numderivative函数求f(x)在x=2处的一阶导: >> numderivative(f, x, 1, 0.0001) 在使用数值方法求导时,需要注意选择合适的步长和阶,以避免误差过大。 ### 回答3: 在Matlab中,我们可以使用diff函数来求解函数在指定点的数值。diff函数的语法为: dy = diff(y)/dx 其中,y表示函数的自变量值,dx表示自变量的步长,即求导的精度,dy表示函数在每个y值处的导数值。例如,对于函数y = sin(x),我们要在x = 0.5处求其导,步长为0.01,可以使用如下代码: x = 0.5; dx = 0.01; y = sin(x); dy = diff(y) / dx; disp(['在x = ', num2str(x), ' 处,sin(x)的导为:', num2str(dy)]) 代码的输出结果为: 在x = 0.5 处,sin(x)的导为:0.86986 我们可以看到,通过diff函数求解函数在指定点的导非常简便。当然,在实际应用中,我们还需要考虑求导的精度和步长等问题,需要根据具体情况进行调整,以保证求解结果的准确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文火冰糖的硅基工坊

你的鼓励是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值