Numpy kullanarak türevi nasıl hesaplayabilirim?


96

Örneğin bir fonksiyonun türevini nasıl hesaplayabilirim?

y = x 2 +1

kullanarak numpy?

Diyelim ki, türevin değerini x = 5'de istiyorum ...


5
Sympy'yi kullanmanız gerekir: sympy.org/en/index.html Numpy, Python için sayısal bir hesaplama kitaplığıdır
prrao

Alternatif olarak, türevin sayısal değerini tahmin etmek için bir yöntem ister misiniz? Bunun için sonlu bir fark yöntemi kullanabilirsiniz, ancak bunların korkunç derecede gürültülü olma eğiliminde olduklarını unutmayın.
Henry Gomersall

Yanıtlar:


148

Dört seçeneğiniz var

  1. Sonlu Farklar
  2. Otomatik Türevler
  3. Sembolik Farklılaşma
  4. El ile türev hesaplayın.

Sonlu farklılıklar harici araçlar gerektirmez, ancak sayısal hataya eğilimlidir ve çok değişkenli bir durumdaysanız, biraz zaman alabilir.

Sorununuz yeterince basitse, sembolik farklılaştırma idealdir. Sembolik yöntemler bu günlerde oldukça sağlamlaşıyor. SymPy bunun için NumPy ile iyi bütünleşen mükemmel bir projedir. Otomatik sarma veya lambdify işlevlerine bakın veya Jensen'in benzer bir soru hakkındaki blog yayınına bakın .

Otomatik türevler çok iyidir, sayısal hatalara yatkın değildir, ancak bazı ek kitaplıklar gerektirir (bunun için google, birkaç iyi seçenek vardır). Bu en sağlam ama aynı zamanda en karmaşık / kurulumu zor olan seçenektir. Kendinizi numpysözdizimiyle sınırlamakta iyiyseniz , Theano iyi bir seçim olabilir.

İşte SymPy'yi kullanan bir örnek

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]: 2⋅x

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

Pardon, bu aptalca görünüyorsa, 3. Sembolik Farklılaşma ile 4. el ile farklılaştırma arasındaki farklar nelerdir?
DrStrangeLove

11
"Sembolik farklılaşma" dediğimde, sürecin bir bilgisayar tarafından işlendiğini ima etmeye niyetlendim. Prensipte 3 ve 4 sadece işi kimin yaptığına, bilgisayara veya programcıya göre farklılık gösterir. Tutarlılık, ölçeklenebilirlik ve tembellik nedeniyle 3 yerine 4 tercih edilir. 3 bir çözüm bulamazsa 4 gereklidir.
MRocklin

4
7. satırda, y wrt x'in türevini hesaplayan bir fonksiyon olan f'yi yaptık. 8'de bu türev fonksiyonunu hepsinin bir vektörüne uygularız ve tüm ikilerin vektörünü elde ederiz. Bunun nedeni, 6. satırda belirtildiği gibi, yprime = 2 * x.
MRocklin

Sadece tamlık uğruna, entegrasyonla farklılaştırma da yapabilirsiniz (bkz. Cauchy'nin integral formülü), örneğin içinde uygulanır mpmath(tam olarak ne yaptıklarından emin değilim).
DerWeh

Kendiniz uygulamadan numpy'de sonlu farklılıklar yapmanın kolay bir yolu var mı? Örneğin, önceden tanımlanmış noktalarda bir fonksiyonun gradyanını bulmak istiyorum.
Alex

43

Aklıma gelen en basit yol, numpy'nin gradyan fonksiyonunu kullanmak :

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

Bu şekilde, dydx, merkezi farklar kullanılarak hesaplanacak ve ileri farkları kullanan ve (n-1) boyut vektörünü döndürecek olan numpy.diff'in aksine, y ile aynı uzunluğa sahip olacaktır.


2
Ya dx sabit değilse?
weberc2

3
@ weberc2, bu durumda bir vektörü diğerine bölmelisiniz, ancak kenarları manuel olarak ileri ve geri türevlerle ayrı ayrı ele almalısınız.
Maytap

2
Ya da y'yi sabit bir dx ile enterpolasyon yapabilir ve ardından gradyanı hesaplayabilirsiniz.
IceArdor

@Sparkler Öneriniz için teşekkürler. Ben 2 küçük sorular sorabilir ise (i) neden geçmek yapmak dxiçin numpy.gradientyerine x? aşağıdaki gibi (ii) biz de senin son satırı yapabilir: dydx = numpy.gradient(y, numpy.gradient(x))?
user929304

2
V1.13'ten itibaren, ikinci argüman olarak bir dizi kullanılarak tekdüze olmayan aralık belirtilebilir. Bu sayfanın Örnekler bölümüne bakın .
Nathaniel Jones

28

NumPy, türevleri hesaplamak için genel işlevsellik sağlamaz. Polinomların basit ve özel durumunu ele alabilir ancak:

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

Türevi sayısal olarak hesaplamak istiyorsanız, uygulamaların büyük çoğunluğu için merkezi fark katsayılarını kullanmaktan kurtulabilirsiniz. Tek bir noktadaki türev için formül şöyle bir şey olacaktır:

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

Eğer xkarşılık gelen yfonksiyon değerleri dizisine sahip bir apsis dizisine sahipseniz, türevlerin yaklaşık değerlerini şu şekilde hesaplayabilirsiniz:

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

2
'Daha genel durumlar için sayısal türevleri hesaplamak kolaydır' - Farklı olmak için yalvarıyorum, genel durumlar için sayısal türevleri hesaplamak oldukça zor. Güzelce davranılan işlevleri seçtin.
Yüksek Performans Mark

>>> print p'den sonra 2 ne anlama geliyor? (2. satırda)
DrStrangeLove

@DrStrangeLove: Bu üs. Matematiksel gösterimi simüle etmek içindir.
Sven Marnach

@SvenMarnach bu maksimum üs mü? ya da ne?? Neden üssün 2 olduğunu düşünüyor? Sadece katsayıları
girdik

2
@DrStrangeLove: Çıktının olarak okunması gerekiyor 1 * x**2 + 1. 2Üstteki satıra koydular çünkü bu bir üs. Uzaktan bak.
Sven Marnach

15

numpyKullanmak istediğinizi varsayarsak , Titiz tanımını kullanarak herhangi bir noktada bir fonksiyonun türevini sayısal olarak hesaplayabilirsiniz :

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

Daha iyi sonuçlar için Simetrik türevi de kullanabilirsiniz :

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

Örneğinizi kullanırsanız, tam kod aşağıdaki gibi görünmelidir:

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

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

Şimdi türevi sayısal olarak şu adreste bulabilirsiniz x=5:

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

8

Yığın üzerine başka bir yöntem atacağım ...

scipy.interpolatebirçok enterpolasyonlu spline türevler sağlayabilir. Dolayısıyla, doğrusal bir spline ( k=1) kullanarak, spline'ın türevi ( derivative()yöntemi kullanarak ) bir ileri farka eşdeğer olmalıdır. Tam olarak emin değilim, ancak kübik spline'ı oluşturmak için önceki ve sonraki değerleri kullandığı için kübik spline türevi kullanmanın ortalanmış bir fark türevine benzer olacağına inanıyorum.

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)

Bunu biraz önce denedim, AxisError işlevinden hatalar almaya devam ediyorum: eksen -1, 0 boyut dizisi için sınırların dışında ve toplulukta da buna bir yanıt göremiyorum, herhangi bir yardım?
Ayan Mitra

Sorununuzu yeni bir soru olarak gönderin ve buraya bağlantı verin. Hatanızın oluşmasına neden olan bir örnek sağlamak muhtemelen gerekli olacaktır. İnterp fonksiyonlarında karşılaştığım hatalar genellikle verilerin iyi biçimlendirilmemesinden kaynaklanır - tekrarlanan değerler, yanlış sayıda boyut, dizilerden biri yanlışlıkla boş, veriler x'e göre sıralanmamış veya sıralandığında geçerli işlev vb. Scipy'nin numpy'yi yanlış çağırması olasıdır, ancak pek olası değildir. X.shape ve y.shape'i kontrol edin. Np.interp () çalışıp çalışmadığına bakın - çalışmazsa daha yararlı bir hata sağlayabilir.
flutefreak7

6

Gradyanları hesaplamak için makine öğrenimi topluluğu Autograd'ı kullanır:

" Numpy kodunun türevlerini verimli bir şekilde hesaplar. "

Yüklemek:

pip install autograd

İşte bir örnek:

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))

Ayrıca, karmaşık fonksiyonların gradyanlarını, örneğin çok değişkenli fonksiyonları da hesaplayabilir.


Merhaba, bu işlev adım uzunluğunu sağlayarak iki veri sütunu arasında sayısal olarak ayrım yapmak için kullanılabilir mi? teşekkürler
Ayan Mitra

3

İhtiyaç duyduğunuz hassasiyet düzeyine bağlı olarak, basit farklılaştırma kanıtını kullanarak bunu kendiniz çözebilirsiniz:

>>> (((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

aslında gradyan sınırını alamayız, ama biraz eğlencelidir. Yine de dikkatli olmalısın çünkü

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.