摘要: 实际问题中主要涉及的还是多变量的函数,单一变量导数的计算其实是多变量导数求偏导的一个特例。本文将单变量求导的实现扩展到多变量求偏导,输出的结果不仅是各个变量的导数,也是函数在当前点的梯度。
关键词: 多变量,偏导,梯度
前言
1 偏导数
假设有以下函数:
这也是为了便于介绍的,实际问题函数也比较复杂。这个函数已经有两个变量了。
使用Python实现如下:
def function_2(x):
"""一个多变量函数 y = x1^2 + 2*x2^2
x 可以为一个list或者tuple 元素要有两个以上
"""
return x[0]**2 + 2*x[1]**2
本文所需要的依赖包如下:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # 绘制3d图形
下面将这个函数可视化,看看是什么样子的。
绘图源码:
def plot_function_2():
"""函数function_2 3维可视化
"""
fig = plt.figure(figsize=(12, 8)) # 创建画板
ax = plt.axes(projection='3d') # 在当前画板上创建一个坐标轴
x1 = np.arange(-5, 5, 0.01) # 自变量1
x2 = np.arange(-5, 5, 0.01) # 自变量2
X1, X2 = np.meshgrid(x1, x2) # 将自变量坐标化
y = function_2([X1, X2]) # 计算对应左边的函数结果
ax.plot_surface(X1, X2, y, cmap='rainbow') # 绘制图形表面
ax.set_title("Fig. function $f(x_1, x_2)=x_1^2 + 2x_2^2$")
ax.set_xlabel("$x_1$")
ax.set_ylabel("$x_2$")
ax.set_zlabel("$f(x_1, x_2)$")
plt.show() # 显示
plot_function_2()
现在我们对目标函数中自变量x1和x2求偏导,需要注意的是当对x1求偏导的时候,我们把x2当作常数,对x2求导时也是如此把x1当作常数。其对应的导数如下:
实现对应的偏导数函数也比较简单如下:
def function_temp1(x):
"""f(x)对x1的偏导数
x x1的值
"""
return 2*x
def function_temp2(x):
"""f(x)对x2的偏导数
x x2的值
"""
return 4*x
2 偏导数Python实现
实现代码如下:
def numerical_diff(f, x):
"""
f为需要求导的函数
x为对应的变量
"""
h = 1e-10
diff = np.zeros_like(x.size)
for idx in range(x.size):
tmp_val = x[idx] # 获取
# f(x + h) 的计算
x[idx] = tmp_val + h # 对于的需要计算偏导数变量的值略加一点
fxh1 = f(x) # 计算
# f(x-h)的计算
x[idx] = tmp_val - h # 对于的需要计算偏导数变量的值略减一点
fxh2 = f(x)
diff[idx] = (fxh1 - fxh2)/(2*h)
x[idx] = tmp_val # 还原值
return diff
以点(2, 3)为例,比较自写求偏导的函数numerical_diff和标准函数计算的结果:
x = np.array([2., 3.])
print("numerical_diff result:\n")
print(numerical_diff(function_2, x))
print("standard result:\n")
print(np.array([function_temp1(x[0]), function_temp2(x[1])]))
"""
numerical_diff result:
[ 4.00000033 12.00000099]
standard result:
[ 4. 12.]
"""
从结果可以看出差别不是很大,基本上可以说是保持一致。
总结
numerical_diff函数的是有一定技巧的,我们需要注意的是在实际计算导数值时,与目标变量没有关系的部分,求导后都为0,与目标变量有关的部分会被进行差分求导的运算,补充一下求导是线性特征的,函数中的元素单独求导后再相加也是可以的。
硬核补充: numerical_diff函数输出的结果其实就是函数在某点的梯度。