如何使用Numpy计算导数?


91

例如,如何计算函数的导数

y = x 2 +1

使用numpy

假设我想要x = 5时的导数值...


5
您需要使用Sympy:sympy.org/en/index.html Numpy是Python的数值计算库
prrao 2012年

另外,您是否需要一种估算导数数值的方法?为此,您可以使用有限差分法,但请记住,它们往往会带来可怕的噪音。
亨利·戈默索尔

Answers:


142

您有四个选择

  1. 有限差异
  2. 自动衍生工具
  3. 象征差异
  4. 手动计算导数。

有限的差异不需要外部工具,但容易出现数值误差,如果您处于多变量情况,则可能需要一段时间。

如果您的问题足够简单,则使用符号区分是理想的选择。如今,符号方法变得越来越强大。SymPy是一个出色的项目,可以很好地与NumPy集成。查看autowrap或lambdify函数,或查看Jensen的博客文章,探讨类似问题

自动导数非常酷,不易出现数字错误,但确实需要一些其他库(为此,google,有一些不错的选择)。这是设置选项中最可靠但也最复杂/最困难的。如果您可以限制自己使用numpy语法,那么Theano可能是一个不错的选择。

这是使用SymPy的示例

In [1]: from sympy import *
In [2]: import numpy as np
In [3]: x = Symbol('x')
In [4]: y = x**2 + 1
In [5]: yprime = y.diff(x)
In [6]: yprime
Out[6]: 2x

In [7]: f = lambdify(x, yprime, 'numpy')
In [8]: f(np.ones(5))
Out[8]: [ 2.  2.  2.  2.  2.]

抱歉,如果这看起来很愚蠢,那么3.Symmbolic区分和4.hand区分有什么区别?
DrStrangeLove

11
当我说“符号区分”时,我的意思是暗示该过程是由计算机处理的。原则3和4的区别仅在于工作的人,计算机或程序员。由于一致性,可伸缩性和惰性,3比4更可取。如果3找不到解决方案,则4是必需的。
MRocklin

4
在第7行中,我们制作了f,该函数可计算y wrt x的导数。在8中,我们将此导数函数应用于所有矢量,并获得所有矢量。这是因为,如第6行所述,yprime = 2 * x。
MRocklin

仅出于完整性考虑,您还可以通过积分(请参阅柯西的积分公式)进行微分,例如通过实现mpmath(不确定它们的作用)。
DerWeh

有没有一种简单的方法可以在numpy中进行有限的差异而无需自己实现?例如,我想在预定义点找到函数的梯度。
Alex

41

我能想到的最简单的方法是使用numpy的渐变函数

x = numpy.linspace(0,10,1000)
dx = x[1]-x[0]
y = x**2 + 1
dydx = numpy.gradient(y, dx)

这样,dydx将使用中心差异进行计算,并且与y具有相同的长度,这与numpy.diff不同,后者使用前向差异并返回(n-1)个大小矢量。


2
如果dx不是常数怎么办?
weberc2

3
@ weberc2,在这种情况下,您应该将一个向量除以另一个向量,但是要分别用正向和反向导数分别处理边。
Sparkler

2
或者,您可以将y与常数dx插值,然后计算梯度。
IceArdor '16

@Sparkler感谢您的建议。如果我要问两个小问题,(i)我们为什么dxnumpy.gradient代替x?(ii)我们也可以按如下方式处理您的最后一行dydx = numpy.gradient(y, numpy.gradient(x))吗?
user929304

2
从v1.13开始,可以使用数组作为第二个参数来指定非均匀间距。请参阅本页的“示例”部分。
纳撒尼尔·琼斯

26

NumPy不提供用于计算导数的常规功能。但是,它可以处理多项式的简单特殊情况:

>>> p = numpy.poly1d([1, 0, 1])
>>> print p
   2
1 x + 1
>>> q = p.deriv()
>>> print q
2 x
>>> q(5)
10

如果要通过数值计算导数,则可以在大多数应用程序中避免使用中心商。对于单点的导数,公式将类似于

x = 5.0
eps = numpy.sqrt(numpy.finfo(float).eps) * (1.0 + x)
print (p(x + eps) - p(x - eps)) / (2.0 * eps * x)

如果您有一个x横坐标数组和一个对应y的函数值数组,则可以使用以下公式计算导数的近似值:

numpy.diff(y) / numpy.diff(x)

2
“为更一般的情况计算数值导数很容易”-我要不同,为一般情况计算数值导数相当困难。您只是选择了表现良好的函数。
高性能Mark

>>> print p后2是什么意思?(在第二行)
DrStrangeLove 2012年

@DrStrangeLove:那是指数。它旨在模拟数学符号。
Sven Marnach 2012年

@SvenMarnach是最大指数吗?或者是什么??为什么认为指数是2?我们只输入了系数...
DrStrangeLove 2012年

2
@DrStrangeLove:应该将输出读取为1 * x**2 + 1。他们将其2放在上面的行中,因为它是指数。从远处看。
Sven Marnach 2012年

14

假设要使用numpy,可以使用Rigorous定义在任意点上通过数值计算函数的导数 :

def d_fun(x):
    h = 1e-5 #in theory h is an infinitesimal
    return (fun(x+h)-fun(x))/h

您也可以使用对称导数以获得更好的结果:

def d_fun(x):
    h = 1e-5
    return (fun(x+h)-fun(x-h))/(2*h)

使用您的示例,完整的代码应类似于:

def fun(x):
    return x**2 + 1

def d_fun(x):
    h = 1e-5
    return (fun(x+h)-fun(x-h))/(2*h)

现在,您可以在以下位置找到数值的导数x=5

In [1]: d_fun(5)
Out[1]: 9.999999999621423

8

我会在堆上扔另一种方法...

scipy.interpolate的许多内插样条能够提供导数。因此,使用线性样条(k=1),样条的导数(使用derivative()方法)应等于正向差。我不确定,但是我相信使用三次样条导数将类似于居中差导数,因为它使用前后的值来构造三次样条。

from scipy.interpolate import InterpolatedUnivariateSpline

# Get a function that evaluates the linear spline at any x
f = InterpolatedUnivariateSpline(x, y, k=1)

# Get a function that evaluates the derivative of the linear spline at any x
dfdx = f.derivative()

# Evaluate the derivative dydx at each x location...
dydx = dfdx(x)

刚刚尝试过此操作,我一直从此函数AxisError中得到错误:轴-1超出了维度0数组的范围,并且我也没有在社区中看到对此的任何答案,有什么帮助吗?
阿扬·米特拉

将您的问题发布为新问题并在此处链接到该问题。可能需要提供一个导致您的错误发生的示例。我使用interp函数的错误通常是因为数据输入格式不正确-像重复值,错误的维数,数组之一意外为空,数据未针对x进行排序或排序时不是有效函数等。scipy可能错误地调用了numpy,但可能性很小。检查x.shape和y.shape。看看np.interp()是否有效-否则可能会提供更有用的错误。
flutefreak7 '19

5

为了计算梯度,机器学习社区使用Autograd:

有效地计算numpy代码的派生。

安装:

pip install autograd

这是一个例子:

import autograd.numpy as np
from autograd import grad

def fct(x):
    y = x**2+1
    return y

grad_fct = grad(fct)
print(grad_fct(1.0))

它还可以计算复杂函数(例如多元函数)的梯度。


嗨,可以通过提供步长来将此函数用于在数值上区分两列数据吗?谢谢
Ayan Mitra

3

根据您要求的精度水平,您可以使用简单的区分证明来自己解决:

>>> (((5 + 0.1) ** 2 + 1) - ((5) ** 2 + 1)) / 0.1
10.09999999999998
>>> (((5 + 0.01) ** 2 + 1) - ((5) ** 2 + 1)) / 0.01
10.009999999999764
>>> (((5 + 0.0000000001) ** 2 + 1) - ((5) ** 2 + 1)) / 0.0000000001
10.00000082740371

我们实际上不能接受渐变的极限,但是有点有趣。你一定要提防,因为

>>> (((5+0.0000000000000001)**2+1)-((5)**2+1))/0.0000000000000001
0.0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.