【问题标题】:Compute the Jacobian matrix in Python在 Python 中计算雅可比矩阵
【发布时间】:2018-03-29 09:53:44
【问题描述】:
import numpy as np


a = np.array([[1,2,3],
              [4,5,6],
              [7,8,9]])


b = np.array([[1,2,3]]).T

c = a.dot(b) #function

jacobian = a # as partial derivative of c w.r.t to b is a.

我正在阅读有关 jacobian 矩阵的内容,并试图构建一个,从我目前所阅读的内容来看,这个 python 代码应该被视为 jacobian。我理解对了吗?

【问题讨论】:

标签: python numpy derivative


【解决方案1】:

您可以使用哈佛 autograd 库 (link),其中 gradjacobian 将函数作为参数:

import autograd.numpy as np
from autograd import grad, jacobian

x = np.array([5,3], dtype=float)

def cost(x):
    return x[0]**2 / x[1] - np.log(x[1])

gradient_cost = grad(cost)
jacobian_cost = jacobian(cost)

gradient_cost(x)
jacobian_cost(np.array([x,x,x]))

否则,您可以使用可用于sympy 中的矩阵的jacobian 方法:

from sympy import sin, cos, Matrix
from sympy.abc import rho, phi

X = Matrix([rho*cos(phi), rho*sin(phi), rho**2])
Y = Matrix([rho, phi])

X.jacobian(Y)

此外,您可能也有兴趣查看此低级变体 (link)。 MATLAB 在其jacobian 函数here 上提供了很好的文档。

更新:请注意,autograd 库已被纳入 jax,它提供了用于计算正向和逆雅可比矩阵 (link) 的函数。

【讨论】:

    【解决方案2】:

    Jacobian 仅针对向量值函数定义。您不能使用填充了常量的数组来计算雅可比行列式;您必须知道基础函数及其偏导数,或它们的数值近似。当您考虑到常数(相对于某物)的(偏)导数为 0 时,这一点很明显。

    在 Python 中,您可以使用 SymPySymEngine 等符号数学模块来计算函数的雅可比行列式。下面是一个来自 Wikipedia 的示例的简单演示:

    使用SymEngine 模块:

    Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:40:30) [MSC v.1500 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    >>> import symengine
    >>>
    >>>
    >>> vars = symengine.symbols('x y') # Define x and y variables
    >>> f = symengine.sympify(['y*x**2', '5*x + sin(y)']) # Define function
    >>> J = symengine.zeros(len(f),len(vars)) # Initialise Jacobian matrix
    >>>
    >>> # Fill Jacobian matrix with entries
    ... for i, fi in enumerate(f):
    ...     for j, s in enumerate(vars):
    ...         J[i,j] = symengine.diff(fi, s)
    ...
    >>> print J
    [2*x*y, x**2]
    [5, cos(y)]
    >>>
    >>> print symengine.Matrix.det(J)
    2*x*y*cos(y) - 5*x**2
    

    【讨论】:

    • 如何得到最终输出?
    【解决方案3】:

    在python 3中,可以试试sympy包:

    import sympy as sym
    
    def Jacobian(v_str, f_list):
        vars = sym.symbols(v_str)
        f = sym.sympify(f_list)
        J = sym.zeros(len(f),len(vars))
        for i, fi in enumerate(f):
            for j, s in enumerate(vars):
                J[i,j] = sym.diff(fi, s)
        return J
    
    Jacobian('u1 u2', ['2*u1 + 3*u2','2*u1 - 3*u2'])
    

    给出:

    Matrix([[2,  3],[2, -3]])
    

    【讨论】:

    • 或者只是 Matrix(['2*u1 + 3*u2','2*u1 - 3*u2']).jacobian(['u1', 'u2']) 在 SymPy 中给出相同的结果。
    【解决方案4】:

    这是向量函数f(x) 的数学雅可比行列式的 Python 实现,假定返回一维 numpy 数组。

    import numpy as np
    
    def J(f, x, dx=1e-8):
        n = len(x)
        func = f(x)
        jac = np.zeros((n, n))
        for j in range(n):  # through columns to allow for vector addition
            Dxj = (abs(x[j])*dx if x[j] != 0 else dx)
            x_plus = [(xi if k != j else xi + Dxj) for k, xi in enumerate(x)]
            jac[:, j] = (f(x_plus) - func)/Dxj
        return jac
    

    建议使dx ~ 10-8

    【讨论】:

    • 什么是dx?它不应该是x的导数吗?这里怎么算数字?
    • 数值导数被计算为感兴趣的值和非常接近的值之间的函数变化率。这里数值导数的定义使用前向定义,或 f(x+dx)-f(x)/dx,其中 dx 非常小。显然,微积分就是在 dx 接近 0 时计算该函数的极限,但是可以通过非常低的 dx 获得该量的数值估计
    • 非方阵失败
    【解决方案5】:

    虽然autograd 是一个很好的库,但请务必查看它的upgraded version JAX,它有很好的文档记录(与 autograd 相比)。

    一个简单的例子:

    import jax.numpy as jnp
    from jax import jacfwd
    
    # Define some simple function.
    def sigmoid(x):
        return 0.5 * (jnp.tanh(x / 2) + 1)
    # Note that here, I want a derivative of a "vector" output function (inputs*a + b is a vector) wrt a input 
    # "vector" a at a0: Derivative of vector wrt another vector is a matrix: The Jacobian
    def simpleJ(a, b, inputs): #inputs is a matrix, a & b are vectors
        return sigmoid(jnp.dot(inputs, a) + b)
    
    inputs = jnp.array([[0.52, 1.12,  0.77],
                       [0.88, -1.08, 0.15],
                       [0.52, 0.06, -1.30],
                       [0.74, -2.49, 1.39]])
    
    b = jnp.array([0.2, 0.1, 0.3, 0.2])
    a0 = jnp.array([0.1,0.7,0.7])
    
    # Isolate the function: variables to be differentiated from the constant parameters
    f = lambda a: simpleJ(a, b, inputs) # Now f is just a function of variable to be differentiated
    
    J = jacfwd(f)
    # Till now I have only calculated the derivative, it still needs to be evaluated at a0.
    J(a0)
    

    【讨论】:

      【解决方案6】:

      如果您想一次找到多个点的雅可比行列式(例如,如果您的函数接受形状 (n, x) 并输出 (n, y)),这里有一个函数。这基本上是詹姆斯卡特的答案,但在很多方面。 dx 可能需要根据他的回答中的绝对值进行调整。

      def numerical_jacobian(f, xs, dx=1e-6):
        """
            f is a function that accepts input of shape (n_points, input_dim)
            and outputs (n_points, output_dim)
      
            return the jacobian as (n_points, output_dim, input_dim)
        """
        if len(xs.shape) == 1:
          xs = xs[np.newaxis, :]
          
        assert len(xs.shape) == 2
      
        ys = f(xs)
        
        x_dim = xs.shape[1]
        y_dim = ys.shape[1]
        
        jac = np.empty((xs.shape[0], y_dim, x_dim))
        
        for i in range(x_dim):
          x_try = xs + dx * e(x_dim, i + 1)
          jac[:, :, i] = (f(x_try) - ys) / dx
        
        return jac
      
      def e(n, i):
        ret = np.zeros(n)
        ret[i - 1] = 1.0
        return ret
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-07-24
        • 2018-02-07
        • 1970-01-01
        • 1970-01-01
        • 2020-07-14
        • 2023-04-02
        • 1970-01-01
        相关资源
        最近更新 更多