什么是雅可比矩阵(Jacobian Matrix)?

什么是雅可比矩阵(Jacobian Matrix)?插图

你有没有想过,谷歌地图是如何确定到达你所在位置的最短路线的?或者当你转动方向盘时,自动移动方向盘会对车辆的运动产生怎样的影响?那么,这一切都可以归结为雅可比矩阵。雅可比矩阵是矢量函数偏导数的矩阵。雅可比球面坐标变换是雅可比最常用的地方。它涉及微分中雅可比球面坐标变换的思想。在本文中,我们将讨论雅可比矩阵的数学概念、公式、行列式以及我们在日常生活中如何使用它。

什么是雅可比矩阵?

雅可比矩阵及其行列式是为变量数相同的有限个函数定义的,被称为 “雅可比”。它告诉我们在不同空间之间映射的函数中,一组变量的变化如何影响另一组变量。

在这种情况下,我们可以在每一行中找到关于变量的同一函数的第一次偏导数。矩阵可以是行列数相等的正方形矩阵,也可以是行列数不相等的矩形矩阵。

举例说明 在山路颠倒的山中跋涉时,通常会有一个方向和陡峭程度。无论你在山上的哪个位置,雅可比矩阵就像你的向导,告诉你攀登的陡度和方向。

什么是雅可比矩阵?

雅可比矩阵是一个由偏导数组成的矩阵,它显示了一个函数将输入向量转化为输出向量的过程。它解释了每个输出如何随每个输入变量而变化。对于函数 f:ℝⁿ → ℝᵐ,共有 m 个分量和 n 个变量,雅可比公式可表示为:

符号雅可比矩阵:Matrix([[2*x, -1], [2*y, 2*x]])

点 (2, 3) 的雅可比矩阵:Matrix([[4, -1], [6, 4]])

雅可比的行列式(符号):4*x**2 + 2*y

点 (2, 3) 的行列式:22

点 (2, 3) 处的雅可比数:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[[ 4.000001 -1. ]
[ 6. 4. ]]
[[ 4.000001 -1. ] [ 6. 4. ]]
[[ 4.000001 -1.      ]  
[ 6.        4.      ]]

在这里,雅可比公式将给出一个函数在某一点周围的局部线性近似值,并解释该函数是如何拉伸、旋转和变换空间的。

雅可比矩阵的数学基础

为了充分理解雅可比矩阵,我们将讨论不同的数学基础:

1. 矢量值函数与多元微积分

它基本上是指将点从一个空间映射到另一个空间的函数。这些函数具有与多个输入相对应的多个输出。这些函数是流体动力学等现实系统的基础结构。

雅可比比结合了线性代数和多变量微积分。标量导数告诉我们单变量函数的变化率。它还解释了以矩阵格式呈现的具有多个输入和输出的函数的变化率。

2. 符号与维度

雅可比矩阵的结构和格式解释了有关变换表示的重要信息。对于函数 f: ℝⁿ into ℝᵐ,其中“n”代表输入,“m”代表输出,雅可比矩阵就是一个“m”乘“n”的矩阵。雅可比矩阵的条目表示 Jᵢⱼ=∂fᵢ/∂xⱼ,第 i 个输出函数的表示随第 j 个输入变量的变化而变化。

因此,矩阵的维数会影响变换。从三维空间到二维空间,雅可比矩阵的行等于输出,列等于输入,从而形成 2*3 矩阵。

3. 几何解释

雅可比矩阵的函数行为也解释了代数定义的直观见解。下面的解释有助于我们确定雅可比矩阵如何从几何角度描述函数的局部行为。

雅可比矩阵的函数行为

  • 局部线性变换:雅可比函数给出了函数在点邻域的最线性近似值。它解释了输入点附近无限小的区域如何映射到输出点。
  • 切线近似:雅可比函数将切线向量从输入空间转换到输出空间,反之亦然。将其视为曲面时,它给出了这些曲面如何相互转向的局部描述。

4. 雅可比函数的可逆性

雅可比函数与可逆性之间的关系是必要的信息。它提供了在特定点上函数局部行为的洞察力。

雅可比函数的可逆性

  • |J| > 0:函数保留局部方向。
  • |J| < 0:局部方向被函数反转。
  • |J| = 0:在特定临界点失去可逆性

只要雅可比函数的行列式不等于零,它在邻域内就是可逆的。那么与该点重合,我们就有了反函数定理。但只要雅可比行列式变为零,输出域就会发生折叠、压缩或局部化。

雅可比的性质

现在,让我们来了解雅可比的性质。

  1. 链式法则:对于复合函数,可以通过雅可比乘法得到复合函数的雅可比。
  2. 方向导数:雅可比可以用来计算任意方向的方向导数。
  3. 线性近似:任意点附近函数的近似值为 f(x + Δx) ≈ f(x) + J(x) – Δx。

计算雅可比矩阵

现在,我们将看到计算雅可比矩阵和雅可比球面坐标变换的三种不同方法–分析推导法、数值逼近法和自动微分法。

雅可比矩阵的分析推导

这是一种经典方法,依靠直接计算偏导数得出雅可比矩阵,从而深入了解变换结构。它是通过系统微分每个分量函数与每个输入变量的关系来实现的。

让我们考虑这样一个例子:向量函数 f: ℝ → ℝᵐ 的分量为 f₁、f₂、…、fₘ,变量为 x₁、x₂、…、xₙ,在每个 j=1,2,….n 时,计算偏导数 ∂fi/∂xj。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
J(x) = [
∂f₁/∂x₁ ∂f₁/∂x₂ ... ∂f₁/∂xₙ
∂f₂/∂x₁ ∂f₂/∂x₂ ... ∂f₂/∂xₙ
... ... ... ...
∂fₘ/∂x₁ ∂fₘ/∂x₂ ... ∂fₘ/∂xₙ
]
Example: f(x,y) = (x²-y, 2xy), the partial derivatives evaluated are:
∂f₁/∂x = 2x
∂f₁/∂y = -1
∂f₂/∂x = 2y
∂f₂/∂y = 2x
And by this we can say that the Jacobian matrix observed is:
J(x,y) = [2x -1
2y 2x]
J(x) = [ ∂f₁/∂x₁ ∂f₁/∂x₂ ... ∂f₁/∂xₙ ∂f₂/∂x₁ ∂f₂/∂x₂ ... ∂f₂/∂xₙ ... ... ... ... ∂fₘ/∂x₁ ∂fₘ/∂x₂ ... ∂fₘ/∂xₙ ] Example: f(x,y) = (x²-y, 2xy), the partial derivatives evaluated are: ∂f₁/∂x = 2x ∂f₁/∂y = -1 ∂f₂/∂x = 2y ∂f₂/∂y = 2x And by this we can say that the Jacobian matrix observed is: J(x,y) = [2x -1 2y 2x]
J(x) = [
∂f₁/∂x₁  ∂f₁/∂x₂  ...  ∂f₁/∂xₙ
∂f₂/∂x₁  ∂f₂/∂x₂  ...  ∂f₂/∂xₙ
...      ...      ...  ...
∂fₘ/∂x₁  ∂fₘ/∂x₂  ...  ∂fₘ/∂xₙ
]

Example: f(x,y) = (x²-y, 2xy), the partial derivatives evaluated are:

∂f₁/∂x = 2x
∂f₁/∂y = -1
∂f₂/∂x = 2y
∂f₂/∂y = 2x

And by this we can say that the Jacobian matrix observed is:

J(x,y) = [2x  -1
2y  2x]

通过这种方法,我们可以看到精确的结果。然而,在同时处理多个变量或复杂函数时,情况会变得复杂,无法进行计算。

雅可比矩阵的数值逼近法

当分析推导过于繁琐或函数缺乏形式表达时,数值方法提供了实用的替代解决方案,即使用有限差分法计算偏导数。两种主要的有限差分法是

  1. 正向差分法:
∂fi/∂xⱼ ≈ [f(x₁,...,xⱼ+h,...,xₙ) - f(x₁,...,xⱼ,...,xₙ)]/h
  1. 中心差值精度更高
∂fi/∂xⱼ ≈ [f(x₁,...,xⱼ+h,...,xₙ) - f(x₁,...,xⱼ-h,...,xₙ)]/(2h)

这里,h = 小步,对于双精度来说,通常是 10-⁶ 的数量级。

关键在于选择合适的步长。步长过大会带来近似误差,而步长过小则会因浮点数限制而导致数值不稳定。使用自适应步长或理查德森外推法的先进技术可以进一步提高精度。

雅可比矩阵的自动微分

自动微分结合了分析精度和计算自动化,在列表中名列前茅。它与数值方法的不同之处在于,自动微分计算的是精确导数而不是近似导数,从而避免了离散化误差。自动微分的基本原理如下

  1. 应用链式法则:它系统地应用链式法则进行构成函数的基本运算。
  2. 计算图的表示:在已知导数的基本运算中,将函数分解为尖图。
  3. 正向和反向节点:正向模式将导数从输入传播到输出,而反向模式则将导数从输出传播回输入。

雅可比矩阵的自动微分

Source: Automatic Differentiation

这使得自动微分对于 TensorFlow、PyTorch、JAX 等现代软件框架来说非常容易使用且高效。他们更喜欢用它来计算机器学习中的雅可比矩阵和科学优化问题。

使用 Python 计算雅可比矩阵和行列式

让我们看看如何使用 Python 实现雅可比矩阵和雅可比球面坐标。我们将分别使用 SymPy 和 NumPy 进行符号计算和数值逼近。

步骤 1:设置环境

导入运行函数所需的路径。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import numpy as np import sympy as sp import matplotlib.pyplot as plt from matplotlib.patches import Ellipse
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

步骤 2:执行符号计算

使用 SymPy 编写符号计算函数。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def symbolic_jacobian():
x, y = sp.symbols('x y')
f1 = x**2 - y
f2 = 2*x*y
# Define the function vector
f = sp.Matrix([f1, f2])
X = sp.Matrix([x, y])
# Calculate the Jacobian matrix
J = f.jacobian(X)
print("Symbolic Jacobian matrix:")
print(J)
# Calculate the Jacobian at point (2, 3)
J_at_point = J.subs([(x, 2), (y, 3)])
print("\nJacobian at point (2, 3):")
print(J_at_point)
# Calculate the determinant
det_J = J.det()
print("\nDeterminant of Jacobian (symbolic):")
print(det_J)
print("\nDeterminant at point (2, 3):")
print(det_J.subs([(x, 2), (y, 3)]))
return J, det_J
def symbolic_jacobian(): x, y = sp.symbols('x y') f1 = x**2 - y f2 = 2*x*y # Define the function vector f = sp.Matrix([f1, f2]) X = sp.Matrix([x, y]) # Calculate the Jacobian matrix J = f.jacobian(X) print("Symbolic Jacobian matrix:") print(J) # Calculate the Jacobian at point (2, 3) J_at_point = J.subs([(x, 2), (y, 3)]) print("\nJacobian at point (2, 3):") print(J_at_point) # Calculate the determinant det_J = J.det() print("\nDeterminant of Jacobian (symbolic):") print(det_J) print("\nDeterminant at point (2, 3):") print(det_J.subs([(x, 2), (y, 3)])) return J, det_J
def symbolic_jacobian():
x, y = sp.symbols('x y')
f1 = x**2 - y
f2 = 2*x*y
# Define the function vector
f = sp.Matrix([f1, f2])
X = sp.Matrix([x, y])
# Calculate the Jacobian matrix
J = f.jacobian(X)
print("Symbolic Jacobian matrix:")
print(J)
# Calculate the Jacobian at point (2, 3)
J_at_point = J.subs([(x, 2), (y, 3)])
print("\nJacobian at point (2, 3):")
print(J_at_point)
# Calculate the determinant
det_J = J.det()
print("\nDeterminant of Jacobian (symbolic):")
print(det_J)
print("\nDeterminant at point (2, 3):")
print(det_J.subs([(x, 2), (y, 3)]))
return J, det_J

步骤 3:添加数值近似值

使用 NumPy 编写数值逼近函数。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def numerical_jacobian(func, x, epsilon=1e-6):
n = len(x) # Number of input variables
m = len(func(x)) # Number of output variables
jacobian = np.zeros((m, n))
for i in range(n):
x_plus = x.copy()
x_plus[i] += epsilon
jacobian[:, i] = (func(x_plus) - func(x)) / epsilon
return jacobian
def numerical_jacobian(func, x, epsilon=1e-6): n = len(x) # Number of input variables m = len(func(x)) # Number of output variables jacobian = np.zeros((m, n)) for i in range(n): x_plus = x.copy() x_plus[i] += epsilon jacobian[:, i] = (func(x_plus) - func(x)) / epsilon return jacobian
def numerical_jacobian(func, x, epsilon=1e-6):
n = len(x)  # Number of input variables
m = len(func(x))  # Number of output variables
jacobian = np.zeros((m, n))
for i in range(n):
x_plus = x.copy()
x_plus[i] += epsilon
jacobian[:, i] = (func(x_plus) - func(x)) / epsilon
return jacobian

步骤 4:编写执行函数

编写执行上述函数和可视化转换的主函数。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def f(x):
return np.array([x[0]**2 - x[1], 2*x[0]*x[1]])
# Visualize the transformation
def visualize_transformation():
# Create a grid of points
x = np.linspace(-3, 3, 20)
y = np.linspace(-3, 3, 20)
X, Y = np.meshgrid(x, y)
# Calculate transformed points
U = X**2 - Y
V = 2*X*Y
# Plot original and transformed grid
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# Original grid
ax1.set_title('Original Space')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.grid(True)
ax1.plot(X, Y, 'k.', markersize=2)
# Add a unit circle
circle = plt.Circle((0, 0), 1, fill=False, color='red', linewidth=2)
ax1.add_artist(circle)
ax1.set_xlim(-3, 3)
ax1.set_ylim(-3, 3)
ax1.set_aspect('equal')
# Transformed grid
ax2.set_title('Transformed Space')
ax2.set_xlabel('u')
ax2.set_ylabel('v')
ax2.grid(True)
ax2.plot(U, V, 'k.', markersize=2)
# Calculate the transformation of the unit circle
theta = np.linspace(0, 2*np.pi, 100)
x_circle = np.cos(theta)
y_circle = np.sin(theta)
u_circle = x_circle**2 - y_circle
v_circle = 2*x_circle*y_circle
ax2.plot(u_circle, v_circle, 'r-', linewidth=2)
# Show the local linear approximation at point (1, 0)
point = np.array([1, 0])
J = numerical_jacobian(f, point)
# Calculate how the Jacobian transforms a small circle at our point
scale = 0.5
transformed_points = []
for t in theta:
delta = scale * np.array([np.cos(t), np.sin(t)])
transformed_delta = J @ delta
transformed_points.append(transformed_delta)
transformed_points = np.array(transformed_points)
# Plot the approximation
base_point_transformed = f(point)
ax2.plot(base_point_transformed[0] + transformed_points[:, 0],
base_point_transformed[1] + transformed_points[:, 1],
'g-', linewidth=2, label='Linear Approximation')
ax2.legend()
plt.tight_layout()
plt.show()
# Execute the functions
symbolic_result = symbolic_jacobian()
point = np.array([2.0, 3.0])
numerical_result = numerical_jacobian(f, point)
print("\nNumerical Jacobian at point (2, 3):")
print(numerical_result)
# Visualize the transformation
visualize_transformation()
def f(x): return np.array([x[0]**2 - x[1], 2*x[0]*x[1]]) # Visualize the transformation def visualize_transformation(): # Create a grid of points x = np.linspace(-3, 3, 20) y = np.linspace(-3, 3, 20) X, Y = np.meshgrid(x, y) # Calculate transformed points U = X**2 - Y V = 2*X*Y # Plot original and transformed grid fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6)) # Original grid ax1.set_title('Original Space') ax1.set_xlabel('x') ax1.set_ylabel('y') ax1.grid(True) ax1.plot(X, Y, 'k.', markersize=2) # Add a unit circle circle = plt.Circle((0, 0), 1, fill=False, color='red', linewidth=2) ax1.add_artist(circle) ax1.set_xlim(-3, 3) ax1.set_ylim(-3, 3) ax1.set_aspect('equal') # Transformed grid ax2.set_title('Transformed Space') ax2.set_xlabel('u') ax2.set_ylabel('v') ax2.grid(True) ax2.plot(U, V, 'k.', markersize=2) # Calculate the transformation of the unit circle theta = np.linspace(0, 2*np.pi, 100) x_circle = np.cos(theta) y_circle = np.sin(theta) u_circle = x_circle**2 - y_circle v_circle = 2*x_circle*y_circle ax2.plot(u_circle, v_circle, 'r-', linewidth=2) # Show the local linear approximation at point (1, 0) point = np.array([1, 0]) J = numerical_jacobian(f, point) # Calculate how the Jacobian transforms a small circle at our point scale = 0.5 transformed_points = [] for t in theta: delta = scale * np.array([np.cos(t), np.sin(t)]) transformed_delta = J @ delta transformed_points.append(transformed_delta) transformed_points = np.array(transformed_points) # Plot the approximation base_point_transformed = f(point) ax2.plot(base_point_transformed[0] + transformed_points[:, 0], base_point_transformed[1] + transformed_points[:, 1], 'g-', linewidth=2, label='Linear Approximation') ax2.legend() plt.tight_layout() plt.show() # Execute the functions symbolic_result = symbolic_jacobian() point = np.array([2.0, 3.0]) numerical_result = numerical_jacobian(f, point) print("\nNumerical Jacobian at point (2, 3):") print(numerical_result) # Visualize the transformation visualize_transformation()
def f(x):
return np.array([x[0]**2 - x[1], 2*x[0]*x[1]])
# Visualize the transformation
def visualize_transformation():
# Create a grid of points
x = np.linspace(-3, 3, 20)
y = np.linspace(-3, 3, 20)
X, Y = np.meshgrid(x, y)
# Calculate transformed points
U = X**2 - Y
V = 2*X*Y
# Plot original and transformed grid
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# Original grid
ax1.set_title('Original Space')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.grid(True)
ax1.plot(X, Y, 'k.', markersize=2)
# Add a unit circle
circle = plt.Circle((0, 0), 1, fill=False, color='red', linewidth=2)
ax1.add_artist(circle)
ax1.set_xlim(-3, 3)
ax1.set_ylim(-3, 3)
ax1.set_aspect('equal')
# Transformed grid
ax2.set_title('Transformed Space')
ax2.set_xlabel('u')
ax2.set_ylabel('v')
ax2.grid(True)
ax2.plot(U, V, 'k.', markersize=2)
# Calculate the transformation of the unit circle
theta = np.linspace(0, 2*np.pi, 100)
x_circle = np.cos(theta)
y_circle = np.sin(theta)
u_circle = x_circle**2 - y_circle
v_circle = 2*x_circle*y_circle
ax2.plot(u_circle, v_circle, 'r-', linewidth=2)
# Show the local linear approximation at point (1, 0)
point = np.array([1, 0])
J = numerical_jacobian(f, point)
# Calculate how the Jacobian transforms a small circle at our point
scale = 0.5
transformed_points = []
for t in theta:
delta = scale * np.array([np.cos(t), np.sin(t)])
transformed_delta = J @ delta
transformed_points.append(transformed_delta)
transformed_points = np.array(transformed_points)
# Plot the approximation
base_point_transformed = f(point)
ax2.plot(base_point_transformed[0] + transformed_points[:, 0],
base_point_transformed[1] + transformed_points[:, 1],
'g-', linewidth=2, label='Linear Approximation')
ax2.legend()
plt.tight_layout()
plt.show()
# Execute the functions
symbolic_result = symbolic_jacobian()
point = np.array([2.0, 3.0])
numerical_result = numerical_jacobian(f, point)
print("\nNumerical Jacobian at point (2, 3):")
print(numerical_result)
# Visualize the transformation
visualize_transformation()

输出:

编写执行上述函数和可视化转换的主函数 编写执行上述函数和可视化转换的主函数

输出点评:

提出了非线性映射 f(x,y) = (x²-y, 2xy),并强调了雅可比特性。左图为原始空间,包含一个均匀网格和一个单位圆,右图为变换后的空间,其中圆变形为八字形。

雅可比矩阵是通过符号(Matrix([[2x, -1],[2y, 2*x]])和数值点(2,3)计算得出的。) 结果显示行列式等于 22。这意味着局部区域有很大的延伸。因此,这一分析提供了变换如何扭曲面积的数学视图。线性化(绿色曲线)代表了这种非线性映射的局部结构。

雅可比矩阵的应用

最新的 ML 框架包含自动微分工具,可为我们计算雅可比矩阵。这改变了复杂应用的游戏规则,例如

  1. 机械臂速度控制:在雅可比矩阵的帮助下,我们可以指出应用程序的角关节速度与末端执行器速度之间的关联,从而实现精确的运动路径。
  2. 动态系统稳定性分析:系统的稳定性将决定其与特征值运动的一致性,在特征值运动中可以找到平衡点。
  3. 蛇形机器人障碍导航:这些研究利用雅可比比将关节速度与头部速度联系起来,并在狭小空间内制定运动策略。
  4. 机械手运动规划:雅可比比计算达到所需末端执行器位置所需的关节轨迹,这对自动化中的取放操作至关重要。
  5. 机器人技术中的力矩变换:这意味着将末端执行器的力,如抓取力,转换为力敏感应用中所需的等效扭矩。

小结

微积分、微分几何和线性代数都是雅可比矩阵串联起来并应用于现实世界的数学学科。从先进的手术机器人到 GPS 定位,雅可比矩阵在提高技术响应速度方面发挥了巨大作用。这是数学如何既能描述我们的宇宙,又能帮助我们更有效、更高效地与之互动的一个例子。

评论留言